10 changed files with 168 additions and 91 deletions
@ -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 @@ |
|||||||
|
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 @@ |
|||||||
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