From 2901c752ec5ed1270ae32a8b70a1ba60a2ca851a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Tue, 17 Sep 2024 19:41:02 +0200 Subject: [PATCH 01/11] fix: improve linking --- hooks/useHandleLinking.ts | 31 ++++++++++++++++--------------- pages/Wildcard.tsx | 3 ++- pages/send/LNURLPay.tsx | 2 +- pages/send/PaymentSuccess.tsx | 2 +- pages/send/Send.tsx | 2 ++ 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/hooks/useHandleLinking.ts b/hooks/useHandleLinking.ts index 5cd823b..9a89f3f 100644 --- a/hooks/useHandleLinking.ts +++ b/hooks/useHandleLinking.ts @@ -2,33 +2,34 @@ import * as Linking from "expo-linking"; import { router, useRootNavigationState } from "expo-router"; import React from "react"; -const SUPPORTED_SCHEMES = ["lightning:", "bitcoin:", "alby:"]; +const SUPPORTED_SCHEMES = ["lightning:", "bitcoin:", "alby:", "exp:"]; export function useHandleLinking() { const rootNavigationState = useRootNavigationState(); - let url = Linking.useURL(); - let hasNavigationState = !!rootNavigationState?.key; + const url = Linking.useURL(); + const hasNavigationState = !!rootNavigationState?.key; React.useEffect(() => { - if (!hasNavigationState) { + if (!hasNavigationState || !url) { return; } - console.log("Received linking URL", url); + + console.log("useHandleLinking", url); for (const scheme of SUPPORTED_SCHEMES) { - if (url?.startsWith(scheme)) { - console.log("Linking URL matched scheme", url, scheme); - if (url.startsWith(scheme + "//")) { - url = url.replace(scheme + "//", scheme); - } + if (url.startsWith(scheme)) { + let currentUrl = url.startsWith(scheme + "//") + ? url.replace(scheme + "//", scheme) + : url; + currentUrl = currentUrl.replace("exp:127.0.0.1:8081/--/", "lightning:"); + + console.log("navigating to send screen", currentUrl); - // TODO: it should not always navigate to send, - // but that's the only linking functionality supported right now - router.dismissAll(); - router.navigate({ + // Instead of dismissing all screens, we'll use replace to avoid navigation stack issues + router.replace({ pathname: "/send", params: { - url, + url: currentUrl, }, }); break; diff --git a/pages/Wildcard.tsx b/pages/Wildcard.tsx index 3cf5a18..d0f1f07 100644 --- a/pages/Wildcard.tsx +++ b/pages/Wildcard.tsx @@ -17,7 +17,8 @@ export function Wildcard() { }} /> - Loading {pathname} + Loading + {pathname} ); } diff --git a/pages/send/LNURLPay.tsx b/pages/send/LNURLPay.tsx index bd410a3..4db2ab7 100644 --- a/pages/send/LNURLPay.tsx +++ b/pages/send/LNURLPay.tsx @@ -85,7 +85,7 @@ export function LNURLPay() { To - {originalText} + {originalText.toLowerCase().replace("lightning:", "")} diff --git a/pages/send/PaymentSuccess.tsx b/pages/send/PaymentSuccess.tsx index bda1184..6eef670 100644 --- a/pages/send/PaymentSuccess.tsx +++ b/pages/send/PaymentSuccess.tsx @@ -36,7 +36,7 @@ export function PaymentSuccess() { Sent to - {originalText} + {originalText.toLowerCase().replace("lightning:", "")} } diff --git a/pages/send/Send.tsx b/pages/send/Send.tsx index 923ad40..0640249 100644 --- a/pages/send/Send.tsx +++ b/pages/send/Send.tsx @@ -53,6 +53,8 @@ export function Send() { loadPayment(keyboardText); } + // TODO: Add a property for the human readable version of the url + // and use it across different send / receive screens (e.g. without "lightning:") async function loadPayment(text: string) { if (!text) { errorToast(new Error("Your clipboard is empty.")); From ad6b0d135e9e0dd478292dd1b28a3f6ffacb67cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Tue, 17 Sep 2024 20:14:04 +0200 Subject: [PATCH 02/11] fix: improve linking --- components/DualCurrencyInput.tsx | 2 +- components/FocusableCamera.tsx | 1 - components/QRCodeScanner.tsx | 27 +++++++++++---------- pages/send/Send.tsx | 27 ++++++++++++++++++--- pages/settings/wallets/WalletConnection.tsx | 4 --- 5 files changed, 38 insertions(+), 23 deletions(-) diff --git a/components/DualCurrencyInput.tsx b/components/DualCurrencyInput.tsx index bebedc5..e28fd1d 100644 --- a/components/DualCurrencyInput.tsx +++ b/components/DualCurrencyInput.tsx @@ -85,6 +85,6 @@ export function DualCurrencyInput({ const styles = StyleSheet.create({ amountInput: { fontSize: 80, - height: 90, + height: 100, }, }); diff --git a/components/FocusableCamera.tsx b/components/FocusableCamera.tsx index 8407cbd..6721156 100644 --- a/components/FocusableCamera.tsx +++ b/components/FocusableCamera.tsx @@ -19,7 +19,6 @@ export function FocusableCamera({ onScanned }: FocusableCameraProps) { const handleBarCodeScanned = ({ data }: BarcodeScanningResult) => { onScanned(data); }; - return ( void; -}; + startScanning: boolean; +} -function QRCodeScanner({ onScanned }: QRCodeScannerProps) { - const [isScanning, setScanning] = React.useState(false); +function QRCodeScanner({ onScanned, startScanning = true }: QRCodeScannerProps) { + const [isScanning, setScanning] = React.useState(startScanning); const [isLoading, setLoading] = React.useState(false); const [permissionStatus, setPermissionStatus] = React.useState(PermissionStatus.UNDETERMINED); useEffect(() => { // Add some timeout to allow the screen transition to finish before // starting the camera to avoid stutters - setLoading(true); - window.setTimeout(async () => { - await scan(); - setLoading(false); - }, 200); - }, []); + if (startScanning) { + setLoading(true); + window.setTimeout(async () => { + await scan(); + setLoading(false); + }, 200); + } + }, [startScanning]); async function scan() { const { status } = await Camera.requestCameraPermissionsAsync(); @@ -59,9 +62,7 @@ function QRCodeScanner({ onScanned }: QRCodeScannerProps) { } {isScanning && ( - <> - - + )} } diff --git a/pages/send/Send.tsx b/pages/send/Send.tsx index 0640249..8c989c2 100644 --- a/pages/send/Send.tsx +++ b/pages/send/Send.tsx @@ -23,10 +23,22 @@ export function Send() { const [isLoading, setLoading] = React.useState(false); const [keyboardOpen, setKeyboardOpen] = React.useState(false); const [keyboardText, setKeyboardText] = React.useState(""); + const [startScanning, setStartScanning] = React.useState(false); + // Delay starting the QR scanner if url has no valid payment info useEffect(() => { if (url) { - loadPayment(url); + (async () => { + try { + const result = await loadPayment(url); + setStartScanning(!result); + } catch (error) { + console.error("failed to load payment", url, error); + errorToast(error); + } + })(); + } else { + setStartScanning(true); } }, [url]); @@ -55,10 +67,10 @@ export function Send() { // TODO: Add a property for the human readable version of the url // and use it across different send / receive screens (e.g. without "lightning:") - async function loadPayment(text: string) { + async function loadPayment(text: string): Promise { if (!text) { errorToast(new Error("Your clipboard is empty.")); - return; + return false; } console.log("loading payment", text); const originalText = text; @@ -108,6 +120,8 @@ export function Send() { }, }); } + + return true; } else { // Check if this is a valid invoice new Invoice({ @@ -118,12 +132,17 @@ export function Send() { pathname: "/send/confirm", params: { invoice: text, originalText }, }); + + return true; } } catch (error) { console.error("failed to load payment", originalText, error); errorToast(error); + } finally { setLoading(false); } + + return false; } return ( @@ -138,7 +157,7 @@ export function Send() { <> {!keyboardOpen && ( <> - +