import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog' import { Drawer, DrawerContent, DrawerHeader, DrawerOverlay, DrawerTitle } from '@/components/ui/drawer' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { useNostr } from '@/providers/NostrProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider' import { useZap } from '@/providers/ZapProvider' import lightning from '@/services/lightning.service' import stuffStatsService from '@/services/stuff-stats.service' import { Loader } from 'lucide-react' import { NostrEvent } from 'nostr-tools' import { Dispatch, SetStateAction, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' import UserAvatar from '../UserAvatar' import Username from '../Username' export default function ZapDialog({ open, setOpen, pubkey, event, defaultAmount, defaultComment }: { open: boolean setOpen: Dispatch> pubkey: string event?: NostrEvent defaultAmount?: number defaultComment?: string }) { const { t } = useTranslation() const { isSmallScreen } = useScreenSize() const drawerContentRef = useRef(null) useEffect(() => { const handleResize = () => { if (drawerContentRef.current) { drawerContentRef.current.style.setProperty('bottom', `env(safe-area-inset-bottom)`) } } if (window.visualViewport) { window.visualViewport.addEventListener('resize', handleResize) handleResize() // Initial call in case the keyboard is already open } return () => { if (window.visualViewport) { window.visualViewport.removeEventListener('resize', handleResize) } } }, []) if (isSmallScreen) { return ( setOpen(false)} /> e.preventDefault()} ref={drawerContentRef} className="flex flex-col gap-4 px-4 mb-4" >
{t('Zap to')}
) } return ( e.preventDefault()}>
{t('Zap to')}
) } function ZapDialogContent({ setOpen, recipient, event, defaultAmount, defaultComment }: { open: boolean setOpen: Dispatch> recipient: string event?: NostrEvent defaultAmount?: number defaultComment?: string }) { const { t, i18n } = useTranslation() const { pubkey } = useNostr() const { defaultZapSats, defaultZapComment } = useZap() const [sats, setSats] = useState(defaultAmount ?? defaultZapSats) const [comment, setComment] = useState(defaultComment ?? defaultZapComment) const isSelfZap = useMemo(() => pubkey === recipient, [pubkey, recipient]) const [zapping, setZapping] = useState(false) const presetAmounts = useMemo(() => { if (i18n.language.startsWith('zh')) { return [ { display: '21', val: 21 }, { display: '66', val: 66 }, { display: '210', val: 210 }, { display: '666', val: 666 }, { display: '1k', val: 1000 }, { display: '2.1k', val: 2100 }, { display: '6.6k', val: 6666 }, { display: '10k', val: 10000 }, { display: '21k', val: 21000 }, { display: '66k', val: 66666 }, { display: '100k', val: 100000 }, { display: '210k', val: 210000 } ] } return [ { display: '21', val: 21 }, { display: '42', val: 42 }, { display: '210', val: 210 }, { display: '420', val: 420 }, { display: '1k', val: 1000 }, { display: '2.1k', val: 2100 }, { display: '4.2k', val: 4200 }, { display: '10k', val: 10000 }, { display: '21k', val: 21000 }, { display: '42k', val: 42000 }, { display: '100k', val: 100000 }, { display: '210k', val: 210000 } ] }, [i18n.language]) const handleZap = async () => { try { if (!pubkey) { throw new Error('You need to be logged in to zap') } setZapping(true) const zapResult = await lightning.zap(pubkey, event ?? recipient, sats, comment, () => setOpen(false) ) // user canceled if (!zapResult) { return } if (event) { stuffStatsService.addZap(pubkey, event.id, zapResult.invoice, sats, comment) } } catch (error) { toast.error(`${t('Zap failed')}: ${(error as Error).message}`) } finally { setZapping(false) } } return ( <> {/* Sats slider or input */}
{ setSats((pre) => { if (e.target.value === '') { return 0 } let num = parseInt(e.target.value, 10) if (isNaN(num) || num < 0) { num = pre } return num }) }} onFocus={(e) => { requestAnimationFrame(() => { const val = e.target.value e.target.setSelectionRange(val.length, val.length) }) }} className="bg-transparent text-center w-full p-0 focus-visible:outline-none text-6xl font-bold" />
{/* Self-zap easter egg warning */} {isSelfZap && (
{t('selfZapWarning')}
)} {/* Preset sats buttons */}
{presetAmounts.map(({ display, val }) => ( ))}
{/* Comment input */}
setComment(e.target.value)} />
) }