import { useEffect, useState } from 'react'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Check, Copy, Share2, Loader2 } from 'lucide-react'; import { useToast } from '@/hooks/useToast'; import { useTranslation } from '@/contexts/language'; import { generateQRCode } from '@/lib/wallet/qrCodeGenerator'; interface ShareProfileDialogProps { open: boolean; onOpenChange: (open: boolean) => void; /** The npub (or nprofile) to build the shareable URL from. */ npub: string; /** Display name, used in the share sheet + QR caption. */ displayName: string; /** Optional path (e.g. `/coach//`) appended to the canonical origin * instead of the default `/` — for sharing a specific Lab profile. */ path?: string; } /** * Share a skater's profile IRL or online. Skateboarding is a physical, local * community — a QR you can flash at a spot (or slap on a sticker) is worth more * than a raw npub. Falls back gracefully to copy-link when the native share * sheet isn't available. */ export function ShareProfileDialog({ open, onOpenChange, npub, displayName, path }: ShareProfileDialogProps) { const { t } = useTranslation(); const { toast } = useToast(); const [qr, setQr] = useState(null); const [copied, setCopied] = useState(false); // Always anchor to the canonical public site so shared links/QRs point to the // real domain — never the preview/sandbox iframe host that window.location // would give us inside the editor. const CANONICAL_ORIGIN = 'https://skate-spots.org'; const shareUrl = `${CANONICAL_ORIGIN}${path ?? `/${npub}`}`; useEffect(() => { if (!open) return; let active = true; setQr(null); // qrcode must be imported lazily (see generateQRCode) — a top-level import // crashes on its canvas dependency at module load. generateQRCode(shareUrl) .then((url) => { if (active) setQr(url); }) .catch((err) => { console.error('[ShareProfileDialog] QR generation failed:', err); if (active) setQr(null); }); return () => { active = false; }; }, [open, shareUrl]); const handleCopy = async () => { try { await navigator.clipboard.writeText(shareUrl); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch { toast({ title: t('walletUi.share.copyFailed'), description: t('walletUi.share.copyFailedDesc'), variant: 'destructive' }); } }; const canNativeShare = typeof navigator !== 'undefined' && !!navigator.share; const handleNativeShare = async () => { try { await navigator.share({ title: `${displayName} on SkateSpots`, text: `Check out ${displayName} on SkateSpots 🛹`, url: shareUrl, }); } catch { // user cancelled or unsupported — no-op } }; return ( {t('walletUi.share.title')} {t('walletUi.share.desc', { name: displayName })} {/* QR */}
{qr ? ( {t('walletUi.share.qrAlt')} ) : (
)}
{/* URL + copy */}
{canNativeShare && ( )}
); }