diff --git a/src/components/shared/NWCWizard.tsx b/src/components/shared/NWCWizard.tsx new file mode 100644 index 0000000..de3c10d --- /dev/null +++ b/src/components/shared/NWCWizard.tsx @@ -0,0 +1,296 @@ +import { useState } from "react"; +import { useLightningStore } from "../../stores/lightning"; +import { isValidNwcUri, parseNwcUri } from "../../lib/lightning/nwc"; + +// ── Wallet catalogue ───────────────────────────────────────────────────────── + +interface WalletDef { + id: string; + name: string; + tagline: string; + setupUrl: string; + steps: string[]; + detect: (relay: string) => boolean; +} + +const WALLETS: WalletDef[] = [ + { + id: "alby-hub", + name: "Alby Hub", + tagline: "Self-hosted · full control", + setupUrl: "https://albyhub.com", + steps: [ + "Open your Alby Hub dashboard", + 'Go to Connections → "Add connection"', + "Copy the Nostr Wallet Connect URI", + "Paste it below", + ], + detect: (r) => r.includes("albyhub") || (r.includes("getalby") && r.includes("hub")), + }, + { + id: "alby", + name: "Alby Extension", + tagline: "Browser extension", + setupUrl: "https://getalby.com/apps/new", + steps: [ + "Go to getalby.com/apps/new", + "Create a new app connection", + "Copy the Nostr Wallet Connect URI", + "Paste it below", + ], + detect: (r) => r.includes("getalby") && !r.includes("albyhub"), + }, + { + id: "mutiny", + name: "Mutiny", + tagline: "Web-based wallet", + setupUrl: "https://app.mutinywallet.com/#/settings/connections", + steps: [ + "Open Mutiny Wallet", + "Go to Settings → Nostr Wallet Connect", + "Create a new connection", + "Copy the URI and paste it below", + ], + detect: (r) => r.includes("mutiny"), + }, + { + id: "phoenix", + name: "Phoenix", + tagline: "Mobile · self-custodial", + setupUrl: "https://phoenix.acinq.co", + steps: [ + "Open Phoenix on your phone", + "Go to Settings → Nostr Wallet Connect", + "Tap 'Link a new wallet'", + "Copy the URI and paste it below", + ], + detect: (r) => r.includes("phoenix") || r.includes("acinq"), + }, +]; + +const GENERIC: WalletDef = { + id: "generic", + name: "Other wallet", + tagline: "", + setupUrl: "", + steps: [ + "Open your Lightning wallet", + "Find the Nostr Wallet Connect section", + "Generate a new NWC connection URI", + "Paste it below", + ], + detect: () => true, +}; + +function detectWallet(nwcUri: string): WalletDef { + try { + const { relayUrl } = parseNwcUri(nwcUri); + return WALLETS.find((w) => w.detect(relayUrl)) ?? GENERIC; + } catch { + return GENERIC; + } +} + +// ── Connected state ─────────────────────────────────────────────────────────── + +function ConnectedState({ nwcUri, onDisconnect }: { nwcUri: string; onDisconnect: () => void }) { + const wallet = detectWallet(nwcUri); + let relay = ""; + try { + relay = parseNwcUri(nwcUri).relayUrl; + const u = new URL(relay); + relay = u.hostname; + } catch { /* keep raw */ } + + return ( +
+
+ +
+ {wallet.name} connected + {relay && via {relay}} +
+ +
+

+ Your NWC connection is active. You can zap notes and profiles. +

+
+ ); +} + +// ── Wallet card ─────────────────────────────────────────────────────────────── + +function WalletCard({ wallet, onSelect }: { wallet: WalletDef; onSelect: () => void }) { + return ( +
+
{wallet.name}
+
{wallet.tagline}
+
+ {wallet.setupUrl && ( + e.stopPropagation()} + > + open ↗ + + )} + +
+
+ ); +} + +// ── Paste step ──────────────────────────────────────────────────────────────── + +function PasteStep({ + wallet, + onBack, + onConnected, +}: { + wallet: WalletDef; + onBack: () => void; + onConnected: () => void; +}) { + const { setNwcUri } = useLightningStore(); + const [input, setInput] = useState(""); + const [error, setError] = useState(null); + + const valid = isValidNwcUri(input.trim()); + let parsedRelay = ""; + if (valid) { + try { + parsedRelay = new URL(parseNwcUri(input.trim()).relayUrl).hostname; + } catch { /* keep raw */ } + } + + const formatError = + input.trim() && !valid + ? input.trim().startsWith("nostr+walletconnect://") + ? "URI is incomplete — missing relay or secret parameter" + : "Should start with nostr+walletconnect://" + : null; + + const handleConnect = () => { + const uri = input.trim(); + if (!isValidNwcUri(uri)) { + setError("Invalid NWC URI — check and try again"); + return; + } + try { + setNwcUri(uri); + onConnected(); + } catch (err) { + setError(String(err)); + } + }; + + return ( +
+ + + {/* Step-by-step instructions */} +
    + {wallet.steps.map((step, i) => ( +
  1. + {i + 1}. + {step} +
  2. + ))} +
+ + {/* URI input */} +