From 4a859799229cf1f8bef4e0cee55ee5a9f4d07dfa Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 17 Jan 2026 22:17:12 +0000 Subject: [PATCH] refactor: use applesauce-wallet-connect and move wallet to header Replace custom NWC client implementation with applesauce-wallet-connect: - Install applesauce-wallet-connect for official NIP-47 support - Create nwc.ts service wrapper for WalletConnect singleton - Update NWCConnection type to match WalletConnectURI interface - Use service/relays/secret properties instead of custom names Move wallet display from user menu to header: - Create standalone WalletButton component - Add WalletButton to header next to UserMenu - Remove wallet UI from user menu dropdown - Show balance in header with yellow zap icon - Clicking wallet button opens connect dialog This provides better UX with wallet status visible in header and uses the official applesauce implementation for reliability. --- package-lock.json | 17 ++ package.json | 1 + src/components/ConnectWalletDialog.tsx | 22 +- src/components/WalletButton.tsx | 54 +++++ src/components/layouts/AppShell.tsx | 6 +- src/components/nostr/user-menu.tsx | 79 +------- src/services/nwc-client.ts | 269 ------------------------- src/services/nwc.ts | 43 ++++ src/types/app.ts | 6 +- 9 files changed, 139 insertions(+), 358 deletions(-) create mode 100644 src/components/WalletButton.tsx delete mode 100644 src/services/nwc-client.ts create mode 100644 src/services/nwc.ts diff --git a/package-lock.json b/package-lock.json index 61b8536..05e8e20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -42,6 +42,7 @@ "applesauce-relay": "^5.0.0", "applesauce-signers": "^5.0.0", "applesauce-wallet": "^5.0.0", + "applesauce-wallet-connect": "^5.0.1", "blossom-client-sdk": "^4.1.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -5747,6 +5748,22 @@ "url": "lightning:nostrudel@geyser.fund" } }, + "node_modules/applesauce-wallet-connect": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/applesauce-wallet-connect/-/applesauce-wallet-connect-5.0.1.tgz", + "integrity": "sha512-k/Gl2IIjfQelW4deN/0M9/I3uznUMZalGAP9/wPgwmAtUyaEHb8YJpOdxqLwCQ98vZMTAcwgK6hmXkAPqA6NTg==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.7.1", + "applesauce-common": "^5.0.0", + "applesauce-core": "^5.0.0", + "rxjs": "^7.8.1" + }, + "funding": { + "type": "lightning", + "url": "lightning:nostrudel@geyser.fund" + } + }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", diff --git a/package.json b/package.json index 697c812..160f261 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "applesauce-relay": "^5.0.0", "applesauce-signers": "^5.0.0", "applesauce-wallet": "^5.0.0", + "applesauce-wallet-connect": "^5.0.1", "blossom-client-sdk": "^4.1.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/src/components/ConnectWalletDialog.tsx b/src/components/ConnectWalletDialog.tsx index b916fe7..084bcb9 100644 --- a/src/components/ConnectWalletDialog.tsx +++ b/src/components/ConnectWalletDialog.tsx @@ -11,7 +11,7 @@ import { import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { useGrimoire } from "@/core/state"; -import { parseNWCUri, NWCClient } from "@/services/nwc-client"; +import { createWalletFromURI } from "@/services/nwc"; interface ConnectWalletDialogProps { open: boolean; @@ -53,27 +53,31 @@ export default function ConnectWalletDialog({ setError(null); try { - // Parse the connection URI - const connection = parseNWCUri(connectionString); - - // Create NWC client - const client = new NWCClient(connection); + // Create wallet instance from connection string + const wallet = createWalletFromURI(connectionString); // Test the connection by getting wallet info - const info = await client.getInfo(); + const info = await wallet.getInfo(); // Get initial balance let balance: number | undefined; try { - balance = await client.getBalance(); + const balanceResult = await wallet.getBalance(); + balance = balanceResult.balance; } catch (err) { console.warn("[NWC] Failed to get balance:", err); // Balance is optional, continue anyway } + // Get connection details from the wallet instance + const serialized = wallet.toJSON(); + // Save connection to state setNWCConnection({ - ...connection, + service: serialized.service, + relays: serialized.relays, + secret: serialized.secret, + lud16: serialized.lud16, balance, info: { alias: info.alias, diff --git a/src/components/WalletButton.tsx b/src/components/WalletButton.tsx new file mode 100644 index 0000000..06b82d3 --- /dev/null +++ b/src/components/WalletButton.tsx @@ -0,0 +1,54 @@ +import { useState } from "react"; +import { Wallet, Zap } from "lucide-react"; +import { useGrimoire } from "@/core/state"; +import { Button } from "@/components/ui/button"; +import ConnectWalletDialog from "@/components/ConnectWalletDialog"; + +export default function WalletButton() { + const { state } = useGrimoire(); + const nwcConnection = state.nwcConnection; + const [showConnectWallet, setShowConnectWallet] = useState(false); + + function formatBalance(millisats?: number): string { + if (millisats === undefined) return "—"; + const sats = Math.floor(millisats / 1000); + return sats.toLocaleString(); + } + + return ( + <> + + + {nwcConnection ? ( + + ) : ( + + )} + + ); +} diff --git a/src/components/layouts/AppShell.tsx b/src/components/layouts/AppShell.tsx index 3f82958..9b10dd0 100644 --- a/src/components/layouts/AppShell.tsx +++ b/src/components/layouts/AppShell.tsx @@ -10,6 +10,7 @@ import CommandLauncher from "../CommandLauncher"; import { GlobalAuthPrompt } from "../GlobalAuthPrompt"; import { SpellbookDropdown } from "../SpellbookDropdown"; import UserMenu from "../nostr/user-menu"; +import WalletButton from "../WalletButton"; import { AppShellContext } from "./AppShellContext"; interface AppShellProps { @@ -76,7 +77,10 @@ export function AppShell({ children, hideBottomBar = false }: AppShellProps) { - +
+ + +
{children} diff --git a/src/components/nostr/user-menu.tsx b/src/components/nostr/user-menu.tsx index 00fe4eb..4bb555e 100644 --- a/src/components/nostr/user-menu.tsx +++ b/src/components/nostr/user-menu.tsx @@ -1,4 +1,4 @@ -import { User, HardDrive, Palette, Wallet, Zap } from "lucide-react"; +import { User, HardDrive, Palette } from "lucide-react"; import accounts from "@/services/accounts"; import { useProfile } from "@/hooks/useProfile"; import { use$ } from "applesauce-react/hooks"; @@ -22,7 +22,6 @@ import Nip05 from "./nip05"; import { RelayLink } from "./RelayLink"; import SettingsDialog from "@/components/SettingsDialog"; import LoginDialog from "./LoginDialog"; -import ConnectWalletDialog from "@/components/ConnectWalletDialog"; import { useState } from "react"; import { useTheme } from "@/lib/themes"; @@ -57,13 +56,11 @@ function UserLabel({ pubkey }: { pubkey: string }) { export default function UserMenu() { const account = use$(accounts.active$); - const { state, addWindow, disconnectNWC } = useGrimoire(); + const { state, addWindow } = useGrimoire(); const relays = state.activeAccount?.relays; const blossomServers = state.activeAccount?.blossomServers; - const nwcConnection = state.nwcConnection; const [showSettings, setShowSettings] = useState(false); const [showLogin, setShowLogin] = useState(false); - const [showConnectWallet, setShowConnectWallet] = useState(false); const { themeId, setTheme, availableThemes } = useTheme(); function openProfile() { @@ -80,48 +77,10 @@ export default function UserMenu() { accounts.removeAccount(account); } - function handleDisconnectWallet() { - disconnectNWC(); - } - - function formatBalance(millisats?: number): string { - if (millisats === undefined) return "—"; - const sats = Math.floor(millisats / 1000); - return sats.toLocaleString(); - } - return ( <> - - - {/* Wallet Connection Button */} - {nwcConnection ? ( - - ) : ( - - )} -