diff --git a/app.json b/app.json index f6caee7..8e70276 100644 --- a/app.json +++ b/app.json @@ -3,7 +3,7 @@ "name": "Alby Go", "slug": "alby-mobile", "version": "1.7.2", - "scheme": ["lightning", "bitcoin", "alby"], + "scheme": ["lightning", "bitcoin", "alby", "nostr+walletconnect"], "orientation": "portrait", "icon": "./assets/icon.png", "userInterfaceStyle": "automatic", diff --git a/lib/link.ts b/lib/link.ts index aa5db1f..5dfcb05 100644 --- a/lib/link.ts +++ b/lib/link.ts @@ -2,7 +2,12 @@ import { router } from "expo-router"; import { BOLT11_REGEX } from "./constants"; import { lnurl as lnurlLib } from "./lnurl"; -const SUPPORTED_SCHEMES = ["lightning:", "bitcoin:", "alby:"]; +const SUPPORTED_SCHEMES = [ + "lightning:", + "bitcoin:", + "alby:", + "nostr+walletconnect:", +]; // Register exp scheme for testing during development // https://docs.expo.dev/guides/linking/#creating-urls @@ -22,6 +27,19 @@ export const handleLink = async (url: string) => { if (SUPPORTED_SCHEMES.indexOf(parsedUrl.protocol) > -1) { let { username, hostname, protocol, pathname, search } = parsedUrl; + if (parsedUrl.protocol === "nostr+walletconnect:") { + if (router.canDismiss()) { + router.dismissAll(); + } + console.info("Navigating to wallet setup"); + router.push({ + pathname: "/settings/wallets/setup", + params: { + nwcUrl: protocol + hostname + search, + }, + }); + return; + } if (parsedUrl.protocol === "exp:") { if (!parsedUrl.pathname) { diff --git a/pages/settings/Wallets.tsx b/pages/settings/Wallets.tsx index 9fcd21b..1901068 100644 --- a/pages/settings/Wallets.tsx +++ b/pages/settings/Wallets.tsx @@ -14,76 +14,71 @@ export function Wallets() { const selectedWalletId = useAppStore((store) => store.selectedWalletId); const wallets = useAppStore((store) => store.wallets); return ( - <> - - - - { - const active = item.index === selectedWalletId; + + + + { + const active = item.index === selectedWalletId; - return ( - { - if (item.index !== selectedWalletId) { - useAppStore.getState().setSelectedWalletId(item.index); - router.dismissAll(); - router.navigate("/"); - Toast.show({ - type: "success", - text1: `Switched wallet to ${item.item.name || DEFAULT_WALLET_NAME}`, - position: "top", - }); - } - }} - className={cn( - "flex flex-row items-center justify-between p-6 rounded-2xl border-2", - active ? "border-primary" : "border-transparent", - )} - > - - - - {item.item.name || DEFAULT_WALLET_NAME} - - - {active && ( - - - - - - )} - - ); - }} - /> - - - - + return ( + { + if (item.index !== selectedWalletId) { + useAppStore.getState().setSelectedWalletId(item.index); + router.dismissAll(); + router.navigate("/"); + Toast.show({ + type: "success", + text1: `Switched wallet to ${item.item.name || DEFAULT_WALLET_NAME}`, + position: "top", + }); + } + }} + className={cn( + "flex flex-row items-center justify-between p-6 rounded-2xl border-2", + active ? "border-primary" : "border-transparent", + )} + > + + + + {item.item.name || DEFAULT_WALLET_NAME} + + + {active && ( + + + + + + )} + + ); + }} + /> - + + + + ); } diff --git a/pages/settings/wallets/SetupWallet.tsx b/pages/settings/wallets/SetupWallet.tsx index 8b8b404..2210a53 100644 --- a/pages/settings/wallets/SetupWallet.tsx +++ b/pages/settings/wallets/SetupWallet.tsx @@ -1,7 +1,7 @@ import { nwc } from "@getalby/sdk"; import { Nip47Capability } from "@getalby/sdk/dist/NWCClient"; import * as Clipboard from "expo-clipboard"; -import { router } from "expo-router"; +import { router, useLocalSearchParams } from "expo-router"; import { useAppStore } from "lib/state/appStore"; import React from "react"; import { Pressable, TouchableOpacity, View } from "react-native"; @@ -28,6 +28,9 @@ import { REQUIRED_CAPABILITIES } from "~/lib/constants"; import { errorToast } from "~/lib/errorToast"; export function SetupWallet() { + const { nwcUrl: nwcUrlFromSchemeLink } = useLocalSearchParams<{ + nwcUrl: string; + }>(); const wallets = useAppStore((store) => store.wallets); const walletIdWithConnection = wallets.findIndex( (wallet) => wallet.nostrWalletConnectUrl, @@ -39,6 +42,7 @@ export function SetupWallet() { const [capabilities, setCapabilities] = React.useState(); const [name, setName] = React.useState(""); + const [startScanning, setStartScanning] = React.useState(false); const handleScanned = (data: string) => { return connect(data); @@ -56,47 +60,55 @@ export function SetupWallet() { connect(nostrWalletConnectUrl); } - async function connect(nostrWalletConnectUrl: string) { - try { - setConnecting(true); - // make sure connection is valid - const nwcClient = new nwc.NWCClient({ - nostrWalletConnectUrl, - }); - const info = await nwcClient.getInfo(); - const capabilities = [...info.methods] as Nip47Capability[]; - if (info.notifications?.length) { - capabilities.push("notifications"); - } - if ( - !REQUIRED_CAPABILITIES.every((capability) => - capabilities.includes(capability), - ) - ) { - const missing = REQUIRED_CAPABILITIES.filter( - (capability) => !capabilities.includes(capability), - ); - throw new Error(`Missing required capabilities: ${missing.join(", ")}`); - } + const connect = React.useCallback( + async (nostrWalletConnectUrl: string): Promise => { + try { + setConnecting(true); + // make sure connection is valid + const nwcClient = new nwc.NWCClient({ + nostrWalletConnectUrl, + }); + const info = await nwcClient.getInfo(); + const capabilities = [...info.methods] as Nip47Capability[]; + if (info.notifications?.length) { + capabilities.push("notifications"); + } + if ( + !REQUIRED_CAPABILITIES.every((capability) => + capabilities.includes(capability), + ) + ) { + const missing = REQUIRED_CAPABILITIES.filter( + (capability) => !capabilities.includes(capability), + ); + throw new Error( + `Missing required capabilities: ${missing.join(", ")}`, + ); + } - console.info("NWC connected", info); + console.info("NWC connected", info); - setNostrWalletConnectUrl(nostrWalletConnectUrl); - setCapabilities(capabilities); - setName(nwcClient.lud16 || ""); + setNostrWalletConnectUrl(nostrWalletConnectUrl); + setCapabilities(capabilities); + setName(nwcClient.lud16 || ""); - Toast.show({ - type: "success", - text1: "Connection successful", - text2: "Please set your wallet name to finish", - position: "top", - }); - } catch (error) { - console.error(error); - errorToast(error); - } - setConnecting(false); - } + Toast.show({ + type: "success", + text1: "Connection successful", + text2: "Please set your wallet name to finish", + position: "top", + }); + setConnecting(false); + return true; + } catch (error) { + console.error(error); + errorToast(error); + } + setConnecting(false); + return false; + }, + [], + ); const addWallet = () => { if (!nostrWalletConnectUrl) { @@ -122,6 +134,22 @@ export function SetupWallet() { router.replace("/"); }; + React.useEffect(() => { + if (nwcUrlFromSchemeLink) { + (async () => { + const result = await connect(nwcUrlFromSchemeLink); + // Delay the camera to show the error message + if (!result) { + setTimeout(() => { + setStartScanning(true); + }, 2000); + } + })(); + } else { + setStartScanning(true); + } + }, [connect, nwcUrlFromSchemeLink]); + return ( <> ) : !nostrWalletConnectUrl ? ( <> - +