diff --git a/components/ConnectionInfoModal.tsx b/components/ConnectionInfoModal.tsx index 09603c17..0d15afdd 100644 --- a/components/ConnectionInfoModal.tsx +++ b/components/ConnectionInfoModal.tsx @@ -1,3 +1,4 @@ +import { NWCClient, type Nip47Capability } from "@getalby/sdk/nwc"; import React from "react"; import { Modal, @@ -6,33 +7,57 @@ import { TouchableOpacity, View, } from "react-native"; -import { XIcon } from "~/components/Icons"; +import Toast from "react-native-toast-message"; +import Alert from "~/components/Alert"; +import { TriangleAlertIcon, XIcon } from "~/components/Icons"; +import { toastConfig } from "~/components/ToastConfig"; import { Text } from "~/components/ui/text"; -import { useAppStore } from "~/lib/state/appStore"; import { useThemeColor } from "~/lib/useThemeColor"; -import { cn } from "~/lib/utils"; +import { cn, getPubkeyFromNWCUrl } from "~/lib/utils"; type ConnectionInfoModalProps = { visible: boolean; onClose: () => void; + nostrWalletConnectUrl: string | undefined; + capabilities?: Nip47Capability[]; }; -function ConnectionInfoModal({ visible, onClose }: ConnectionInfoModalProps) { +function ConnectionInfoModal({ + visible, + onClose, + nostrWalletConnectUrl, + capabilities, +}: ConnectionInfoModalProps) { const { shadow } = useThemeColor("shadow"); - const selectedWalletId = useAppStore((store) => store.selectedWalletId); - const wallets = useAppStore((store) => store.wallets); - const capabilities = wallets[selectedWalletId].nwcCapabilities; - const nwcClient = useAppStore((store) => store.nwcClient); + const nwcInfo = React.useMemo( + () => + nostrWalletConnectUrl + ? NWCClient.parseWalletConnectUrl(nostrWalletConnectUrl) + : undefined, + [nostrWalletConnectUrl], + ); + const appPubkey = nostrWalletConnectUrl + ? getPubkeyFromNWCUrl(nostrWalletConnectUrl) + : undefined; + const insecureRelays = + nwcInfo?.relayUrls.filter((relayUrl) => !relayUrl.startsWith("wss://")) ?? + []; + const [relayStatuses, setRelayStatuses] = React.useState([]); React.useEffect(() => { - if (!nwcClient) { + if (!visible || !nwcInfo) { return; } + const pool = new NWCClient({ + relayUrls: nwcInfo.relayUrls, + walletPubkey: nwcInfo.walletPubkey, + }).pool; + let cancelled = false; (async () => { - const _relayStatuses = []; - for (const relayUrl of nwcClient.relayUrls) { + const _relayStatuses: boolean[] = []; + for (const relayUrl of nwcInfo.relayUrls) { try { - await nwcClient.pool.ensureRelay(relayUrl, { + await pool.ensureRelay(relayUrl, { connectionTimeout: 2000, }); _relayStatuses.push(true); @@ -40,9 +65,15 @@ function ConnectionInfoModal({ visible, onClose }: ConnectionInfoModalProps) { _relayStatuses.push(false); } } - setRelayStatuses(_relayStatuses); + if (!cancelled) { + setRelayStatuses(_relayStatuses); + } })(); - }, [nwcClient]); + return () => { + cancelled = true; + pool.destroy(); + }; + }, [visible, nwcInfo]); return ( Relays - {nwcClient?.relayUrls.map((relayUrl, index) => ( + {nwcInfo?.relayUrls.map((relayUrl, index) => ( ))} + {!!insecureRelays.length && ( + 1 ? "are" : "is" + } not using a secure (wss) connection.`} + icon={TriangleAlertIcon} + /> + )} + {!!nwcInfo?.lud16 && ( + + Lightning Address + + {nwcInfo.lud16} + + + )} + Capabilities @@ -129,19 +179,25 @@ function ConnectionInfoModal({ visible, onClose }: ConnectionInfoModalProps) { App Pubkey - {nwcClient?.publicKey} + {appPubkey} Wallet Pubkey - {nwcClient?.walletPubkey} + {nwcInfo?.walletPubkey} + ); } diff --git a/pages/settings/wallets/EditWallet.tsx b/pages/settings/wallets/EditWallet.tsx index 94f9550d..7c75dbbd 100644 --- a/pages/settings/wallets/EditWallet.tsx +++ b/pages/settings/wallets/EditWallet.tsx @@ -134,6 +134,8 @@ export function EditWallet() { setShowConnectionInfo(false)} + nostrWalletConnectUrl={wallets[walletId]?.nostrWalletConnectUrl} + capabilities={wallets[walletId]?.nwcCapabilities} /> { + if ( + nwcInfo.lud16 && + wallet.lightningAddress?.trim().toLowerCase() === + nwcInfo.lud16.trim().toLowerCase() + ) { + return true; + } + return ( + !!wallet.nostrWalletConnectUrl && + NWCClient.parseWalletConnectUrl(wallet.nostrWalletConnectUrl) + .walletPubkey === nwcInfo.walletPubkey + ); + }) + : false; const handleScanned = (data: string) => { return connect(data); @@ -70,13 +92,7 @@ export function SetupWallet() { setNostrWalletConnectUrl(nostrWalletConnectUrl); setCapabilities(capabilities); setName(nwcClient.lud16 || ""); - - Toast.show({ - type: "success", - text1: "Connection successful", - text2: "Please set your wallet name to finish", - position: "top", - }); + setShowConnectionInfo(true); setConnecting(false); return true; } catch (error) { @@ -88,6 +104,21 @@ export function SetupWallet() { [], ); + // Deferred to a post-render effect (rather than shown inline in connect()) + // so it fires after ConnectionInfoModal has mounted its own Toast host — + // otherwise this toast registers on the root Toast host and renders + // beneath the modal that opens in the same update. + React.useEffect(() => { + if (nostrWalletConnectUrl) { + Toast.show({ + type: "success", + text1: "Connection successful", + text2: "Review connection info and set your wallet name to finish", + position: "top", + }); + } + }, [nostrWalletConnectUrl]); + const addWallet = async () => { if (isLoading || !nostrWalletConnectUrl) { return; @@ -214,6 +245,20 @@ export function SetupWallet() { ) : ( + setShowConnectionInfo(false)} + nostrWalletConnectUrl={nostrWalletConnectUrl} + capabilities={capabilities} + /> + {existingWalletMatch && ( + + )} Wallet name @@ -230,7 +275,6 @@ export function SetupWallet() { onChangeText={setName} placeholder="Enter a name for your wallet" returnKeyType="done" - autoFocus /> {capabilities &&