10 changed files with 168 additions and 91 deletions
@ -0,0 +1,57 @@
@@ -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 = ( |
||||
<div className="bg-muted rounded-full h-5 w-5 flex flex-col items-center justify-center text-muted-foreground hover:text-foreground"> |
||||
<QrCodeIcon size={14} /> |
||||
</div> |
||||
) |
||||
|
||||
const content = ( |
||||
<div className="w-full flex flex-col items-center gap-4 p-8"> |
||||
<div className="flex items-center w-full gap-2 pointer-events-none px-1"> |
||||
<UserAvatar size="semiBig" userId={pubkey} /> |
||||
<div className="flex-1 w-0"> |
||||
<Username userId={pubkey} className="text-xl font-semibold truncate" /> |
||||
<Nip05 pubkey={pubkey} /> |
||||
</div> |
||||
</div> |
||||
<QrCode size={512} value={`nostr:${npub}`} /> |
||||
<div className="flex flex-col items-center"> |
||||
<PubkeyCopy pubkey={pubkey} /> |
||||
</div> |
||||
</div> |
||||
) |
||||
|
||||
if (isSmallScreen) { |
||||
return ( |
||||
<Drawer> |
||||
<DrawerTrigger>{trigger}</DrawerTrigger> |
||||
<DrawerContent>{content}</DrawerContent> |
||||
</Drawer> |
||||
) |
||||
} |
||||
|
||||
return ( |
||||
<Dialog> |
||||
<DialogTrigger>{trigger}</DialogTrigger> |
||||
<DialogContent className="w-80 p-0 m-0" onOpenAutoFocus={(e) => e.preventDefault()}> |
||||
{content} |
||||
</DialogContent> |
||||
</Dialog> |
||||
) |
||||
} |
||||
@ -0,0 +1,70 @@
@@ -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<HTMLDivElement>(null) |
||||
const [foregroundColor, setForegroundColor] = useState<string | undefined>() |
||||
const [backgroundColor, setBackgroundColor] = useState<string | undefined>() |
||||
|
||||
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 <div ref={ref} /> |
||||
} |
||||
|
||||
function getColor(name: string) { |
||||
if (typeof window !== 'undefined') { |
||||
return getComputedStyle(document.documentElement).getPropertyValue(`--${name}`).trim() |
||||
} |
||||
} |
||||
@ -1,52 +0,0 @@
@@ -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 ( |
||||
<Drawer> |
||||
<DrawerTrigger> |
||||
<div className="bg-muted rounded-full h-5 w-5 flex flex-col items-center justify-center text-muted-foreground hover:text-foreground"> |
||||
<QrCode size={14} /> |
||||
</div> |
||||
</DrawerTrigger> |
||||
<DrawerContent className="h-1/2"> |
||||
<div className="flex justify-center items-center h-full"> |
||||
<QRCodeSVG |
||||
size={300} |
||||
value={`nostr:${npub}`} |
||||
bgColor="hsl(var(--background))" |
||||
fgColor="hsl(var(--foreground))" |
||||
/> |
||||
</div> |
||||
</DrawerContent> |
||||
</Drawer> |
||||
) |
||||
} |
||||
|
||||
return ( |
||||
<Popover> |
||||
<PopoverTrigger> |
||||
<div className="bg-muted rounded-full h-5 w-5 flex flex-col items-center justify-center text-muted-foreground hover:text-foreground"> |
||||
<QrCode size={14} /> |
||||
</div> |
||||
</PopoverTrigger> |
||||
<PopoverContent className="w-fit h-fit"> |
||||
<QRCodeSVG |
||||
value={`nostr:${npub}`} |
||||
bgColor="hsl(var(--background))" |
||||
fgColor="hsl(var(--foreground))" |
||||
/> |
||||
</PopoverContent> |
||||
</Popover> |
||||
) |
||||
} |
||||
Loading…
Reference in new issue