From 1691d499309d769089ab2a065a60ae49a64d958a Mon Sep 17 00:00:00 2001 From: SkateSpots Date: Mon, 5 Oct 2026 19:40:38 +0700 Subject: [PATCH] feat(supporters): add silent-payments donation card (BIP-353 name + QR) --- src/i18n/en.ts | 5 +++ src/i18n/es.ts | 5 +++ src/pages/SupportersPage.tsx | 72 +++++++++++++++++++++++++++++++++++- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 435fe7b..0e7df0d 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -6500,6 +6500,11 @@ walletUi: { sourceAngor: 'Angor', supportersTotals: '{{count}} supporters · {{angor}} sats via Angor · {{zap}} sats zapped', quietNote: 'Plus everyone who gave quietly — thank you. ❤️', + silentTitle: 'Private on-chain (silent payments)', + silentDesc: 'One name, a fresh address for every payment — on-chain via silent payments, or Lightning from the same record. Nothing to reuse, nothing to link.', + silentCopyName: 'Copy name', + silentCopied: 'Copied', + silentWallets: 'Needs a wallet that supports payment names / silent payments: Sparrow, Cake, Dana, Phoenix, BlueWallet, Wasabi, Nunchuk or BitBox02.', tiersNote: 'Tiers: Supporter under 100k sats · Patron 100k–1M · Legend 1M+', tierEmpty: 'No one yet — be the first.', joinSupporters: 'Join {{count}} supporters', diff --git a/src/i18n/es.ts b/src/i18n/es.ts index 4de33e4..39b37b8 100644 --- a/src/i18n/es.ts +++ b/src/i18n/es.ts @@ -6500,6 +6500,11 @@ walletUi: { sourceAngor: 'Angor', supportersTotals: '{{count}} patrocinadores · {{angor}} sats vía Angor · {{zap}} sats zapeados', quietNote: 'Y a todos los que aportaron en silencio — gracias. ❤️', + silentTitle: 'On-chain privado (silent payments)', + silentDesc: 'Un nombre, una dirección nueva en cada pago — on-chain con silent payments, o Lightning desde el mismo registro. Sin reutilizar direcciones, nada que enlazar.', + silentCopyName: 'Copiar nombre', + silentCopied: 'Copiado', + silentWallets: 'Requiere una cartera compatible con nombres de pago / silent payments: Sparrow, Cake, Dana, Phoenix, BlueWallet, Wasabi, Nunchuk o BitBox02.', tiersNote: 'Niveles: Colaborador menos de 100k sats · Patrón 100k–1M · Leyenda 1M+', tierEmpty: 'Nadie todavía — sé el primero.', joinSupporters: 'Únete a {{count}} patrocinadores', diff --git a/src/pages/SupportersPage.tsx b/src/pages/SupportersPage.tsx index 3af8a77..a676129 100644 --- a/src/pages/SupportersPage.tsx +++ b/src/pages/SupportersPage.tsx @@ -1,8 +1,9 @@ -import { useMemo, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { useSeoMeta } from '@unhead/react'; import { Link } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; -import { ExternalLink, Zap, Heart, Users, Handshake, Building2, Star, ShieldCheck, Server, Globe, Bot, Code2, Briefcase, Megaphone, TrendingUp } from 'lucide-react'; +import QRCode from 'qrcode'; +import { ExternalLink, Zap, Heart, Users, Handshake, Building2, Star, ShieldCheck, Server, Globe, Bot, Code2, Briefcase, Megaphone, TrendingUp, Check, Copy } from 'lucide-react'; import { SupportSkateSpots } from '@/components/SupportSkateSpots'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { BACKERS, BACKERS_TOTAL_SATS, tierOf } from '@/lib/backers'; @@ -15,6 +16,13 @@ import { useTranslation } from '@/contexts/language'; const ANGOR_PROJECT_URL = 'https://angor.io/project/angor1qtunna00uqyx52rr0nvqa059z2gknhrmn7urd6z'; +// Silent-payments "payment name" (BIP-353) → BIP-321 URI with a silent payment +// address (sp) + a BOLT 12 Lightning offer (lno). Resolved from +// skatespots.user._bitcoin-payment.silentpayments.net (2026-10-05). If the name +// is re-pointed, update SILENT_URI from the DNS TXT record. +const SILENT_NAME = 'skatespots@silentpayments.net'; +const SILENT_URI = 'bitcoin:?sp=sp1qq2xlxmvh5tpkjw460cfjf5ndz87fc45k0fm3glyalqxq4m436u8nyqa745dyh55pz4vchhjzam5tmlvvlp6whhkqmdk3e7xvfp4y80jhvq40lyl3&lno=lno1pgqppmsrse80qf0aara4slvcjxrvu6j2rp5ftmjy4yntlsmsutpkvkt6878s8y79jwe96rpahf567zyt0lhe26sxpsace8n9u5l359n254e639mqqgpas2cxvhc6ftcr6wrh6nv36mz53qhsfvwrxvgf8mnvq4tsg0z2plqqxvv8geddvc60pys4ja0ajvhhhka2zzfdsnmkvygu872fa0awyqvv8rrpg6k0kg8zpxh8hy3hm9pr35hfa73s92mk38xfqfgfz98v0w34c4z8un6ljxlfjcu69m3de0nqarvh8k8kqqey5kgfmvyfhrg3wltewtj8xn35aevy96dws40hygx7xpfu2k0ty8rm4mv262df8f9q3z2strn4cmh8svss'; + // Only count support received from this point (the project's public launch era). // Zaps to the platform before this are ignored. NOTE: as of 2026-10-05 every // platform zap is already ≥ 2026-01, so this doesn't currently trim the list — @@ -117,6 +125,63 @@ const PAYS_FOR = [ { icon: Megaphone, key: 'walletUi.supporters.payMarketing', desc: 'walletUi.supporters.payMarketingDesc' }, ] as const; +// ─── Silent-payments donation card (BIP-353 name → BIP-321 URI) ────────────── + +function SilentPaymentsCard({ className }: { className?: string }) { + const { t } = useTranslation(); + const [qr, setQr] = useState(null); + const [copied, setCopied] = useState(false); + + useEffect(() => { + let active = true; + QRCode.toDataURL(SILENT_URI, { width: 200, margin: 1 }) + .then((url) => { if (active) setQr(url); }) + .catch(() => { if (active) setQr(null); }); + return () => { active = false; }; + }, []); + + const copy = async () => { + try { + await navigator.clipboard.writeText(SILENT_NAME); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + } catch { /* clipboard blocked */ } + }; + + return ( +
+
+ 🕵️ +
+

+ {t('walletUi.supporters.silentTitle')} +

+

{t('walletUi.supporters.silentDesc')}

+
+
+ +
+ {qr && ( + {SILENT_NAME} + )} +
+

{SILENT_NAME}

+ +

+ {t('walletUi.supporters.silentWallets')} +

+
+
+
+ ); +} + // ─── Section header ─────────────────────────────────────────────────────────── function SectionHeader({ @@ -577,6 +642,9 @@ export default function SupportersPage() { {/* Lightning rail — the in-app way to chip in (zap publicly or pay quietly). */} + + {/* Silent payments — private on-chain (and Lightning) via a BIP-353 name. */} + {/* ── 2. What your support pays for (words only) ── */}