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}
+
+
+ );
+}
+
+// ── 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) => (
+ -
+ {i + 1}.
+ {step}
+
+ ))}
+
+
+ {/* URI input */}
+
+ );
+}
+
+// ── Wizard root ───────────────────────────────────────────────────────────────
+
+export function NWCWizard() {
+ const { nwcUri, clearNwcUri } = useLightningStore();
+ const [step, setStep] = useState<"choose" | "paste">("choose");
+ const [selectedWallet, setSelectedWallet] = useState(GENERIC);
+
+ if (nwcUri) {
+ return ;
+ }
+
+ if (step === "paste") {
+ return (
+ setStep("choose")}
+ onConnected={() => setStep("choose")}
+ />
+ );
+ }
+
+ return (
+
+
+ Choose your Lightning wallet to get a Nostr Wallet Connect (NWC) URI.
+
+
+ {WALLETS.map((w) => (
+ { setSelectedWallet(w); setStep("paste"); }}
+ />
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/shared/SettingsView.tsx b/src/components/shared/SettingsView.tsx
index 2cff8e1..7f35c9f 100644
--- a/src/components/shared/SettingsView.tsx
+++ b/src/components/shared/SettingsView.tsx
@@ -1,10 +1,9 @@
import { useState } from "react";
import { useUserStore } from "../../stores/user";
-import { useLightningStore } from "../../stores/lightning";
import { useMuteStore } from "../../stores/mute";
-import { isValidNwcUri } from "../../lib/lightning/nwc";
import { getNDK, getStoredRelayUrls, addRelay, removeRelay } from "../../lib/nostr";
import { useProfile } from "../../hooks/useProfile";
+import { NWCWizard } from "./NWCWizard";
function MutedRow({ pubkey, onUnmute }: { pubkey: string; onUnmute: () => void }) {
const profile = useProfile(pubkey);
@@ -162,70 +161,10 @@ function IdentitySection() {
}
function WalletSection() {
- const { nwcUri, setNwcUri, clearNwcUri } = useLightningStore();
- const [input, setInput] = useState("");
- const [error, setError] = useState(null);
- const [saving, setSaving] = useState(false);
-
- const handleSave = () => {
- const uri = input.trim();
- if (!uri) return;
- if (!isValidNwcUri(uri)) {
- setError("Invalid NWC URI. Must start with nostr+walletconnect://");
- return;
- }
- try {
- setSaving(true);
- setNwcUri(uri);
- setInput("");
- setError(null);
- } catch (err) {
- setError(String(err));
- } finally {
- setSaving(false);
- }
- };
-
return (
Lightning Wallet (NWC)
- {nwcUri ? (
-
-
- ⚡
- Wallet connected
-
-
-
Your NWC connection is active. You can zap notes and profiles.
-
- ) : (
-
- )}
+
);
}