/** * SasquatchProDialog — the "Upgrade to Sasquatch Pro" sheet. * * Shows the supporter tiers, the perk list, and handles Lightning payment * (auto via NWC/WebLN, or a QR fallback). Framed as supporting a free, * no-ads, community-funded app — Pro is the thank-you. */ import { useEffect, useState } from 'react'; import QRCode from 'qrcode'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import { useCurrentUser } from '@/hooks/useCurrentUser'; import { useSasquatchPro } from '@/hooks/useSasquatchPro'; import { usePurchasePro } from '@/hooks/usePurchasePro'; import { useToast } from '@/hooks/useToast'; import { PRO_TIERS, PRO_LIGHTNING_ADDRESS, type ProTier } from '@/lib/sasquatchPro'; import { Zap, Check, Loader2, Copy, Brain, Wand2, Rocket, BadgeCheck, } from 'lucide-react'; const PERKS: Array<{ icon: React.ComponentType<{ className?: string }>; text: string }> = [ { icon: Brain, text: 'Personal assistant — Sasquatch reads your own Stash, crews, spots & listings' }, { icon: Wand2, text: 'Action drafts: list gear, post a spot status, plan a trip — you confirm before anything publishes' }, { icon: Rocket, text: 'Answers use your personal context, not just public data' }, { icon: BadgeCheck, text: '⚡ Directly support SkateSpots & keep it free and ad-free' }, ]; export function SasquatchProDialog({ open, onOpenChange, }: { open: boolean; onOpenChange: (v: boolean) => void; }) { const { user } = useCurrentUser(); const { isPro, expiresAt } = useSasquatchPro(); const { toast } = useToast(); const purchase = usePurchasePro(); const [selected, setSelected] = useState( PRO_TIERS.find((t) => t.popular) ?? PRO_TIERS[1], ); const [qr, setQr] = useState(null); // Render a QR whenever we fall back to manual payment. useEffect(() => { let active = true; if (purchase.invoice) { QRCode.toDataURL(purchase.invoice.toUpperCase(), { width: 280, margin: 1 }) .then((url) => { if (active) setQr(url); }) .catch(() => setQr(null)); } else { setQr(null); } return () => { active = false; }; }, [purchase.invoice]); // Toast + close on success. useEffect(() => { if (purchase.state === 'paid') { toast({ title: 'Welcome to Sasquatch Pro! 🦧⚡', description: 'Your pass is active — a receipt just landed in your DMs. Thanks for keeping the streets free.' }); const t = setTimeout(() => { onOpenChange(false); purchase.reset(); }, 1400); return () => clearTimeout(t); } }, [purchase.state, toast, onOpenChange, purchase]); const copyInvoice = () => { if (purchase.invoice) { navigator.clipboard.writeText(purchase.invoice); toast({ title: 'Invoice copied' }); } }; const busy = purchase.state === 'preparing'; const manual = purchase.state === 'awaiting_payment' && !!purchase.invoice; return ( { onOpenChange(v); if (!v) purchase.reset(); }}> 🦧 Sasquatch Pro Supercharge your AI skate guide. 100% funds a free, no-ads community app. {isPro ? ( // ── Already Pro ─────────────────────────────────────────────────

You're a Pro supporter ⚡

{expiresAt && (

Active until {new Date(expiresAt * 1000).toLocaleDateString()}

)}
{PERKS.map((p) => (
{p.text}
))}

Want to extend? Pick a tier below to add more time.

purchase.purchase(selected)} extend />
) : manual ? ( // ── Manual QR payment ───────────────────────────────────────────

Scan to pay {selected.sats.toLocaleString()} sats for {selected.label} of Pro

{qr && Lightning invoice QR}

Paying to{' '} ⚡ {PRO_LIGHTNING_ADDRESS}

Tap "I've paid" after your wallet confirms — this records your pass to your Nostr identity.

) : ( // ── Purchase flow ───────────────────────────────────────────────
{PERKS.map((p) => (
{p.text}
))}
Free Sasquatch stays free — public spot/event/coach search + the full trick glossary. Pro adds the personal + action layer.
{!user && (

Log in first to upgrade.

)} {purchase.error && (

{purchase.error}

)} purchase.purchase(selected)} disabled={!user} />

Pays to ⚡ {PRO_LIGHTNING_ADDRESS} over Lightning. Bound to your npub.

)}
); } function TierPicker({ selected, onSelect }: { selected: ProTier; onSelect: (t: ProTier) => void }) { return (
{PRO_TIERS.map((t) => { const active = t.id === selected.id; return ( ); })}
); } function PayButton({ tier, busy, disabled, extend, onPay, }: { tier: ProTier; busy: boolean; disabled?: boolean; extend?: boolean; onPay: () => void; }) { return ( ); }