diff --git a/package-lock.json b/package-lock.json
index 804b5f11..496b9457 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -51,7 +51,7 @@
"nostr-tools": "^2.13.0",
"nstart-modal": "^2.0.0",
"path-to-regexp": "^8.2.0",
- "qrcode.react": "^4.2.0",
+ "qr-code-styling": "^1.9.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^15.2.0",
@@ -8300,19 +8300,22 @@
"node": ">=6"
}
},
+ "node_modules/qr-code-styling": {
+ "version": "1.9.2",
+ "resolved": "https://registry.npmjs.org/qr-code-styling/-/qr-code-styling-1.9.2.tgz",
+ "integrity": "sha512-RgJaZJ1/RrXJ6N0j7a+pdw3zMBmzZU4VN2dtAZf8ZggCfRB5stEQ3IoDNGaNhYY3nnZKYlYSLl5YkfWN5dPutg==",
+ "dependencies": {
+ "qrcode-generator": "^1.4.4"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
"node_modules/qrcode-generator": {
"version": "1.4.4",
"resolved": "https://registry.npmjs.org/qrcode-generator/-/qrcode-generator-1.4.4.tgz",
"integrity": "sha512-HM7yY8O2ilqhmULxGMpcHSF1EhJJ9yBj8gvDEuZ6M+KGJ0YY2hKpnXvRD+hZPLrDVck3ExIGhmPtSdcjC+guuw=="
},
- "node_modules/qrcode.react": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz",
- "integrity": "sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==",
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
- }
- },
"node_modules/queue-microtask": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
diff --git a/package.json b/package.json
index e062d62c..e2276cad 100644
--- a/package.json
+++ b/package.json
@@ -61,7 +61,7 @@
"nostr-tools": "^2.13.0",
"nstart-modal": "^2.0.0",
"path-to-regexp": "^8.2.0",
- "qrcode.react": "^4.2.0",
+ "qr-code-styling": "^1.9.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^15.2.0",
diff --git a/src/components/AccountManager/NostrConnectionLogin.tsx b/src/components/AccountManager/NostrConnectionLogin.tsx
index a1f07f6f..c049e961 100644
--- a/src/components/AccountManager/NostrConnectionLogin.tsx
+++ b/src/components/AccountManager/NostrConnectionLogin.tsx
@@ -1,13 +1,13 @@
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
-import { useNostr } from '@/providers/NostrProvider'
-import { Loader, Copy, Check } from 'lucide-react'
-import { createNostrConnectURI, NostrConnectParams } from '@/providers/NostrProvider/nip46'
import { DEFAULT_NOSTRCONNECT_RELAY } from '@/constants'
+import { useNostr } from '@/providers/NostrProvider'
+import { createNostrConnectURI, NostrConnectParams } from '@/providers/NostrProvider/nip46'
+import { Check, Copy, Loader } from 'lucide-react'
import { generateSecretKey, getPublicKey } from 'nostr-tools'
-import { QRCodeSVG } from 'qrcode.react'
-import { useState, useEffect, useRef, useLayoutEffect } from 'react'
+import { useEffect, useLayoutEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
+import QrCode from '../QrCode'
export default function NostrConnectLogin({
back,
@@ -107,12 +107,7 @@ export default function NostrConnectLogin({
<>
-
+
{nostrConnectionErrMsg && (
{nostrConnectionErrMsg}
diff --git a/src/components/NpubQrCode/index.tsx b/src/components/NpubQrCode/index.tsx
new file mode 100644
index 00000000..f55c97d2
--- /dev/null
+++ b/src/components/NpubQrCode/index.tsx
@@ -0,0 +1,57 @@
+import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'
+import { Drawer, DrawerContent, DrawerTrigger } from '@/components/ui/drawer'
+import { useScreenSize } from '@/providers/ScreenSizeProvider'
+import { QrCodeIcon } from 'lucide-react'
+import { nip19 } from 'nostr-tools'
+import { useMemo } from 'react'
+import Nip05 from '../Nip05'
+import PubkeyCopy from '../PubkeyCopy'
+import QrCode from '../QrCode'
+import UserAvatar from '../UserAvatar'
+import Username from '../Username'
+
+export default function NpubQrCode({ pubkey }: { pubkey: string }) {
+ const { isSmallScreen } = useScreenSize()
+ const npub = useMemo(() => (pubkey ? nip19.npubEncode(pubkey) : ''), [pubkey])
+ if (!npub) return null
+
+ const trigger = (
+
+
+
+ )
+
+ const content = (
+
+ )
+
+ if (isSmallScreen) {
+ return (
+
+ {trigger}
+ {content}
+
+ )
+ }
+
+ return (
+
+ )
+}
diff --git a/src/components/QrCode/index.tsx b/src/components/QrCode/index.tsx
new file mode 100644
index 00000000..c8a87a21
--- /dev/null
+++ b/src/components/QrCode/index.tsx
@@ -0,0 +1,70 @@
+import { useTheme } from '@/providers/ThemeProvider'
+import QRCodeStyling from 'qr-code-styling'
+import { useEffect, useRef, useState } from 'react'
+
+export default function QrCode({ value, size = 180 }: { value: string; size?: number }) {
+ const { theme } = useTheme()
+ const ref = useRef
(null)
+ const [foregroundColor, setForegroundColor] = useState()
+ const [backgroundColor, setBackgroundColor] = useState()
+
+ useEffect(() => {
+ setTimeout(() => {
+ const fgColor = `hsl(${getColor('foreground')})`
+ const bgColor = `hsl(${getColor('background')})`
+ setForegroundColor(fgColor)
+ setBackgroundColor(bgColor)
+ }, 0)
+ }, [theme])
+
+ useEffect(() => {
+ setTimeout(() => {
+ const pixelRatio = window.devicePixelRatio || 2
+
+ const qrCode = new QRCodeStyling({
+ width: size * pixelRatio,
+ height: size * pixelRatio,
+ data: value,
+ dotsOptions: {
+ type: 'dots',
+ color: foregroundColor
+ },
+ cornersDotOptions: {
+ type: 'extra-rounded',
+ color: foregroundColor
+ },
+ cornersSquareOptions: {
+ type: 'extra-rounded',
+ color: foregroundColor
+ },
+ backgroundOptions: {
+ color: backgroundColor
+ }
+ })
+
+ if (ref.current) {
+ ref.current.innerHTML = ''
+ qrCode.append(ref.current)
+ const canvas = ref.current.querySelector('canvas')
+ if (canvas) {
+ canvas.style.width = `${size}px`
+ canvas.style.height = `${size}px`
+ canvas.style.maxWidth = '100%'
+ canvas.style.height = 'auto'
+ }
+ }
+ }, 0)
+
+ return () => {
+ if (ref.current) ref.current.innerHTML = ''
+ }
+ }, [value, size, foregroundColor, backgroundColor])
+
+ return
+}
+
+function getColor(name: string) {
+ if (typeof window !== 'undefined') {
+ return getComputedStyle(document.documentElement).getPropertyValue(`--${name}`).trim()
+ }
+}
diff --git a/src/components/QrCodePopover/index.tsx b/src/components/QrCodePopover/index.tsx
deleted file mode 100644
index a3886935..00000000
--- a/src/components/QrCodePopover/index.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Drawer, DrawerContent, DrawerTrigger } from '@/components/ui/drawer'
-import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
-import { useScreenSize } from '@/providers/ScreenSizeProvider'
-import { QrCode } from 'lucide-react'
-import { nip19 } from 'nostr-tools'
-import { QRCodeSVG } from 'qrcode.react'
-import { useMemo } from 'react'
-
-export default function QrCodePopover({ pubkey }: { pubkey: string }) {
- const { isSmallScreen } = useScreenSize()
- const npub = useMemo(() => (pubkey ? nip19.npubEncode(pubkey) : ''), [pubkey])
- if (!npub) return null
-
- if (isSmallScreen) {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- )
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
- )
-}
diff --git a/src/components/UserAvatar/index.tsx b/src/components/UserAvatar/index.tsx
index ad9c4f0e..a8658f45 100644
--- a/src/components/UserAvatar/index.tsx
+++ b/src/components/UserAvatar/index.tsx
@@ -12,6 +12,7 @@ import { useMemo } from 'react'
const UserAvatarSizeCnMap = {
large: 'w-24 h-24',
big: 'w-16 h-16',
+ semiBig: 'w-12 h-12',
normal: 'w-10 h-10',
medium: 'w-8 h-8',
small: 'w-7 h-7',
@@ -26,7 +27,7 @@ export default function UserAvatar({
}: {
userId: string
className?: string
- size?: 'large' | 'big' | 'normal' | 'medium' | 'small' | 'xSmall' | 'tiny'
+ size?: 'large' | 'big' | 'semiBig' | 'normal' | 'medium' | 'small' | 'xSmall' | 'tiny'
}) {
const { profile } = useFetchProfile(userId)
const defaultAvatar = useMemo(
diff --git a/src/pages/primary/MePage/index.tsx b/src/pages/primary/MePage/index.tsx
index 9f6cbb39..1a0ab01f 100644
--- a/src/pages/primary/MePage/index.tsx
+++ b/src/pages/primary/MePage/index.tsx
@@ -2,7 +2,7 @@ import AccountManager from '@/components/AccountManager'
import LoginDialog from '@/components/LoginDialog'
import LogoutDialog from '@/components/LogoutDialog'
import PubkeyCopy from '@/components/PubkeyCopy'
-import QrCodePopover from '@/components/QrCodePopover'
+import NpubQrCode from '@/components/NpubQrCode'
import { Button } from '@/components/ui/button'
import { Separator } from '@/components/ui/separator'
import { SimpleUserAvatar } from '@/components/UserAvatar'
@@ -53,7 +53,7 @@ const MePage = forwardRef((_, ref) => {
/>
diff --git a/src/pages/secondary/ProfilePage/index.tsx b/src/pages/secondary/ProfilePage/index.tsx
index 76ea8e4b..4bc35f9f 100644
--- a/src/pages/secondary/ProfilePage/index.tsx
+++ b/src/pages/secondary/ProfilePage/index.tsx
@@ -7,7 +7,7 @@ import ProfileBanner from '@/components/ProfileBanner'
import ProfileOptions from '@/components/ProfileOptions'
import ProfileZapButton from '@/components/ProfileZapButton'
import PubkeyCopy from '@/components/PubkeyCopy'
-import QrCodePopover from '@/components/QrCodePopover'
+import NpubQrCode from '@/components/NpubQrCode'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import { Skeleton } from '@/components/ui/skeleton'
@@ -157,7 +157,7 @@ const ProfilePage = forwardRef(({ id, index }: { id?: string; index?: number },
)}
Promise
}
@@ -62,21 +63,23 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
updateTheme()
}, [theme])
- const value = {
- themeSetting: themeSetting,
- setThemeSetting: async (themeSetting: TThemeSetting) => {
- storage.setThemeSetting(themeSetting)
- setThemeSetting(themeSetting)
- if (themeSetting === 'system') {
- setTheme(getSystemTheme())
- return
- }
- setTheme(themeSetting)
- }
- }
-
return (
-
+ {
+ storage.setThemeSetting(themeSetting)
+ setThemeSetting(themeSetting)
+ if (themeSetting === 'system') {
+ setTheme(getSystemTheme())
+ return
+ }
+ setTheme(themeSetting)
+ }
+ }}
+ >
{children}
)