import { useState, useMemo } from 'react'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { Label } from '@/components/ui/label'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useAuthor } from '@/hooks/useAuthor'; import { useWallet } from '@/hooks/useWallet'; import { useCrewZap, splitAmount, type CrewZapResult } from '@/hooks/useCrewZap'; import { useToast } from '@/hooks/useToast'; import { genUserName } from '@/lib/genUserName'; import { Zap, Check, Copy, Loader2, X, Users, AlertCircle } from 'lucide-react'; import { cn } from '@/lib/utils'; const PRESETS = [210, 500, 1000, 2100, 5000]; interface ZapCrewDialogProps { crewName: string; /** Recipient pubkeys β€” typically founder + confirmed members. */ recipients: string[]; children: React.ReactNode; } function RecipientRow({ result }: { result: CrewZapResult }) { const author = useAuthor(result.pubkey); const meta = author.data?.metadata; const name = meta?.name ?? genUserName(result.pubkey); const { toast } = useToast(); const copyInvoice = async () => { if (!result.invoice) return; await navigator.clipboard.writeText(result.invoice); toast({ title: 'Invoice copied', description: 'Paste it into any Lightning wallet' }); }; return (
{name.slice(0, 2).toUpperCase()}

{name}

{result.amount.toLocaleString()} sats

); } function StatusPill({ result, onCopy }: { result: CrewZapResult; onCopy: () => void }) { switch (result.status) { case 'success': return Sent; case 'resolving': case 'paying': return {result.status === 'paying' ? 'Paying' : 'Resolving'}; case 'skipped': return Skipped; case 'failed': return result.invoice ? ( ) : ( Failed ); default: return Waiting; } } export function ZapCrewDialog({ crewName, recipients, children }: ZapCrewDialogProps) { const [open, setOpen] = useState(false); const { user } = useCurrentUser(); const { preferredMethod } = useWallet(); const { zapCrew, results, isRunning, reset } = useCrewZap(); const [amount, setAmount] = useState(1000); const [comment, setComment] = useState(''); // Don't include the current user as a recipient (no zapping yourself). const payable = useMemo( () => recipients.filter((pk) => pk !== user?.pubkey), [recipients, user?.pubkey] ); const numAmount = typeof amount === 'string' ? parseInt(amount, 10) || 0 : amount; const perHead = payable.length > 0 ? splitAmount(numAmount, payable.length) : []; const started = results.length > 0; const doneCount = results.filter((r) => r.status === 'success').length; const allDone = started && !isRunning; const handleZap = () => { if (numAmount <= 0 || payable.length === 0) return; zapCrew(payable, numAmount, comment || `Zapped the ${crewName} crew! 🀘`); }; const handleOpenChange = (o: boolean) => { setOpen(o); if (!o) { reset(); setAmount(1000); setComment(''); } }; if (!user) return <>{children}; return (
{children}
Zap the Crew Split a zap evenly across {payable.length} crew {payable.length === 1 ? 'member' : 'members'} with a Lightning address. {payable.length === 0 ? (
No other crew members to zap right now.
) : !started ? (
v && setAmount(parseInt(v, 10))} className="grid grid-cols-5 gap-1 mt-2" > {PRESETS.map((p) => ( {p >= 1000 ? `${p / 1000}k` : p} ))} setAmount(e.target.value)} placeholder="Custom total" className="mt-2 bg-gray-800 border-gray-700" />
{numAmount > 0 && (

β‰ˆ {perHead[0]?.toLocaleString()} sats each {perHead.length > 1 && perHead[0] !== perHead[perHead.length - 1] && ' (rounded)'}

)}