From 47a98cc802350d43edcaa05cd1a32c94ace34180 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Tue, 5 Nov 2024 15:52:49 +0530 Subject: [PATCH 01/97] fix: check for ln address and bolt11 before navigating to send --- hooks/__tests__/useHandleLinking.ts | 35 +++++++++++++++-------- lib/constants.ts | 2 ++ lib/link.ts | 44 +++++++++++++++++++++-------- 3 files changed, 59 insertions(+), 22 deletions(-) diff --git a/hooks/__tests__/useHandleLinking.ts b/hooks/__tests__/useHandleLinking.ts index 6319176..f957fbe 100644 --- a/hooks/__tests__/useHandleLinking.ts +++ b/hooks/__tests__/useHandleLinking.ts @@ -33,38 +33,51 @@ jest.mock("../../lib/lnurl", () => { const testVectors: Record = { // Lightning Addresses "lightning:hello@getalby.com": { - url: "lightning:hello@getalby.com", + url: "hello@getalby.com", path: "/send", }, "lightning://hello@getalby.com": { - url: "lightning:hello@getalby.com", + url: "hello@getalby.com", path: "/send", }, "LIGHTNING://hello@getalby.com": { - url: "lightning:hello@getalby.com", + url: "hello@getalby.com", path: "/send", }, "LIGHTNING:hello@getalby.com": { - url: "lightning:hello@getalby.com", + url: "hello@getalby.com", path: "/send", }, // Lightning invoices - "lightning:lnbc1": { url: "lightning:lnbc1", path: "/send" }, - "lightning://lnbc1": { url: "lightning:lnbc1", path: "/send" }, + "lightning:lnbc123": { + url: "lnbc123", + path: "/send", + }, + "lightning://lnbc123": { + url: "lnbc123", + path: "/send", + }, // BIP21 - "bitcoin:bitcoinaddress?lightning=invoice": { - url: "bitcoin:bitcoinaddress?lightning=invoice", + "bitcoin:bitcoinaddress?lightning=lnbc123": { + url: "lnbc123", path: "/send", }, - "BITCOIN:bitcoinaddress?lightning=invoice": { - url: "bitcoin:bitcoinaddress?lightning=invoice", + "BITCOIN:bitcoinaddress?lightning=lnbc123": { + url: "lnbc123", path: "/send", }, // LNURL-withdraw - "lightning:lnurlw123": { url: "lightning:lnurlw123", path: "/withdraw" }, + "lightning:lnurlw123": { + url: "lnurlw123", + path: "/withdraw", + }, + "lightning://lnurlw123": { + url: "lnurlw123", + path: "/withdraw", + }, }; describe("handleLink", () => { diff --git a/lib/constants.ts b/lib/constants.ts index f9674ee..47d735a 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -40,3 +40,5 @@ export const REQUIRED_CAPABILITIES: Nip47Capability[] = [ export const SATS_REGEX = /^\d*$/; export const FIAT_REGEX = /^\d*(\.\d{0,2})?$/; + +export const BOLT11_REGEX = /.*?((lnbcrt|lntb|lnbc)([0-9]{1,}[a-z0-9]+){1})/; diff --git a/lib/link.ts b/lib/link.ts index 7a498c6..aa5db1f 100644 --- a/lib/link.ts +++ b/lib/link.ts @@ -1,5 +1,6 @@ import { router } from "expo-router"; -import { lnurl } from "./lnurl"; +import { BOLT11_REGEX } from "./constants"; +import { lnurl as lnurlLib } from "./lnurl"; const SUPPORTED_SCHEMES = ["lightning:", "bitcoin:", "alby:"]; @@ -44,27 +45,48 @@ export const handleLink = async (url: string) => { console.info("Navigating to", fullUrl); - const lnurlValue = lnurl.findLnurl(fullUrl); - if (lnurlValue) { - const lnurlDetails = await lnurl.getDetails(lnurlValue); + const schemePattern = new RegExp( + `^(${SUPPORTED_SCHEMES.map((s) => s.replace(":", "")).join("|")}):`, + ); + const trimmedUrl = fullUrl.replace(schemePattern, ""); + + // Check for LNURLs + const lnurl = lnurlLib.findLnurl(trimmedUrl); + if (lnurl) { + const lnurlDetails = await lnurlLib.getDetails(lnurl); if (lnurlDetails.tag === "withdrawRequest") { router.push({ pathname: "/withdraw", params: { - url: fullUrl, + url: lnurl, + }, + }); + return; + } + + if (lnurlDetails.tag === "payRequest") { + router.push({ + pathname: "/send", + params: { + url: lnurl, }, }); return; } } - router.push({ - pathname: "/send", - params: { - url: fullUrl, - }, - }); + // Check for BOLT-11 invoices (including BIP-21 unified QRs) + const bolt11Match = trimmedUrl.match(BOLT11_REGEX); + if (bolt11Match) { + const bolt11 = bolt11Match[1]; + router.push({ + pathname: "/send", + params: { + url: bolt11, + }, + }); + } } else { // Redirect the user to the home screen // if no match was found From 5e01251492a7019891ba0ed4cd32254881320b1e Mon Sep 17 00:00:00 2001 From: im-adithya Date: Tue, 5 Nov 2024 16:23:29 +0530 Subject: [PATCH 02/97] fix: allow users to withdraw when ln address is not set --- pages/receive/Receive.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pages/receive/Receive.tsx b/pages/receive/Receive.tsx index 0157b61..969c882 100644 --- a/pages/receive/Receive.tsx +++ b/pages/receive/Receive.tsx @@ -192,15 +192,25 @@ export function Receive() { - + + )} From 9637b9ea4cd0660a00de185749ba66c39435849d Mon Sep 17 00:00:00 2001 From: im-adithya Date: Tue, 5 Nov 2024 17:08:50 +0530 Subject: [PATCH 03/97] chore: handle min max values in lnurl withdraw --- components/DualCurrencyInput.tsx | 11 +++++++- pages/withdraw/Withdraw.tsx | 45 +++++++++++++++++++++++--------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/components/DualCurrencyInput.tsx b/components/DualCurrencyInput.tsx index 5a1d898..27b8a13 100644 --- a/components/DualCurrencyInput.tsx +++ b/components/DualCurrencyInput.tsx @@ -8,6 +8,7 @@ import { SATS_REGEX, } from "~/lib/constants"; import { useAppStore } from "~/lib/state/appStore"; +import { cn } from "~/lib/utils"; import { RefreshCw } from "./Icons"; import { Input } from "./ui/input"; import { Text } from "./ui/text"; @@ -17,6 +18,8 @@ type DualCurrencyInputProps = { setAmount(amount: string): void; autoFocus?: boolean; readOnly?: boolean; + max?: number; + min?: number; }; export function DualCurrencyInput({ @@ -24,6 +27,8 @@ export function DualCurrencyInput({ setAmount, autoFocus = false, readOnly = false, + max, + min, }: DualCurrencyInputProps) { const getFiatAmount = useGetFiatAmount(); const getSatsAmount = useGetSatsAmount(); @@ -61,7 +66,11 @@ export function DualCurrencyInput({ return ( max) || (min && Number(amount) < min)) && + "text-red-500", + )} placeholder="0" keyboardType={inputMode === "sats" ? "number-pad" : "decimal-pad"} value={inputMode === "sats" ? amount : fiatAmount} diff --git a/pages/withdraw/Withdraw.tsx b/pages/withdraw/Withdraw.tsx index 2de74f6..a04a315 100644 --- a/pages/withdraw/Withdraw.tsx +++ b/pages/withdraw/Withdraw.tsx @@ -10,6 +10,12 @@ import Loading from "~/components/Loading"; import QRCodeScanner from "~/components/QRCodeScanner"; import Screen from "~/components/Screen"; import { Button } from "~/components/ui/button"; +import { + Card, + CardDescription, + CardHeader, + CardTitle, +} from "~/components/ui/card"; import { Text } from "~/components/ui/text"; import { useGetFiatAmount } from "~/hooks/useGetFiatAmount"; import { errorToast } from "~/lib/errorToast"; @@ -109,17 +115,6 @@ export function Withdraw() { return; } - if (Number(valueSat) < lnurlDetails.minWithdrawable / 1000) { - throw new Error( - `Amount below minimum limit of ${lnurlDetails.minWithdrawable} sats`, - ); - } - if (Number(valueSat) > lnurlDetails.maxWithdrawable / 1000) { - throw new Error( - `Amount exceeds maximum limit of ${lnurlDetails.maxWithdrawable} sats.`, - ); - } - setLoadingConfirm(true); const nwcClient = useAppStore.getState().nwcClient; @@ -222,6 +217,8 @@ export function Withdraw() { @@ -235,11 +232,35 @@ export function Withdraw() { )} + {lnurlDetails.minWithdrawable !== + lnurlDetails.maxWithdrawable && ( + + + Withdraw Limit + + Enter an amount between{" "} + + {Math.floor(lnurlDetails.minWithdrawable / 1000)}{" "} + sats + {" "} + and{" "} + + {Math.floor(lnurlDetails.maxWithdrawable / 1000)}{" "} + sats + + + + + )} - - ); -} diff --git a/pages/settings/Notifications.tsx b/pages/settings/Notifications.tsx new file mode 100644 index 0000000..0a589c3 --- /dev/null +++ b/pages/settings/Notifications.tsx @@ -0,0 +1,37 @@ +import React from "react"; +import { Text, View } from "react-native"; +import Screen from "~/components/Screen"; +import { Label } from "~/components/ui/label"; +import { Switch } from "~/components/ui/switch"; +import { useAppStore } from "~/lib/state/appStore"; +import { registerForPushNotificationsAsync } from "~/services/Notifications"; + +export function Notifications() { + // TODO: If this is enabled, register notifications on new wallets being added + const isEnabled = useAppStore((store) => store.isNotificationsEnabled); + + return ( + + + + + + { + useAppStore.getState().setNotificationsEnabled(checked); + if (checked) { + await registerForPushNotificationsAsync(); + } else { + // TODO: de-register all wallets on nostr api + } + }} + nativeID="security" + /> + + + + ); +} diff --git a/pages/settings/Settings.tsx b/pages/settings/Settings.tsx index 33eaea6..3e01eee 100644 --- a/pages/settings/Settings.tsx +++ b/pages/settings/Settings.tsx @@ -62,6 +62,15 @@ export function Settings() { + + + + + Notifications + + + + @@ -107,15 +116,6 @@ export function Settings() { Onboarding - { - router.push("/notifications"); - }} - > - - Notifications - { diff --git a/services/Notifications.ts b/services/Notifications.ts new file mode 100644 index 0000000..7f3cf82 --- /dev/null +++ b/services/Notifications.ts @@ -0,0 +1,110 @@ +import Constants from "expo-constants"; +import { Platform } from "react-native"; + +import { nwc } from "@getalby/sdk"; +import * as Device from "expo-device"; +import * as ExpoNotifications from "expo-notifications"; +import { NOSTR_API_URL } from "~/lib/constants"; +import { errorToast } from "~/lib/errorToast"; +import { useAppStore } from "~/lib/state/appStore"; + +// TODO: add background notification handling for android + +export async function registerForPushNotificationsAsync() { + if (Platform.OS === "android") { + ExpoNotifications.setNotificationChannelAsync("default", { + name: "default", + importance: ExpoNotifications.AndroidImportance.MAX, + vibrationPattern: [0, 250, 250, 250], + lightColor: "#FF231F7C", + }); + } + + if (Device.isDevice) { + const { status: existingStatus } = + await ExpoNotifications.getPermissionsAsync(); + let finalStatus = existingStatus; + if (existingStatus !== "granted") { + const { status } = await ExpoNotifications.requestPermissionsAsync(); + finalStatus = status; + } + if (finalStatus !== "granted") { + errorToast( + new Error( + "Permission not granted to get push token for push notification", + ), + ); + return; + } + const projectId = + Constants?.expoConfig?.extra?.eas?.projectId ?? + Constants?.easConfig?.projectId; + if (!projectId) { + errorToast(new Error("Project ID not found")); + } + try { + const pushTokenString = ( + await ExpoNotifications.getExpoPushTokenAsync({ + projectId, + }) + ).data; + + // FIXME: This would trigger the same notification + // per wallet if they belong to the same node + const wallets = useAppStore.getState().wallets; + + for (const wallet of wallets) { + const nwcUrl = wallet.nostrWalletConnectUrl; + if (!nwcUrl) { + continue; + } + const nwcClient = new nwc.NWCClient({ + nostrWalletConnectUrl: wallet.nostrWalletConnectUrl, + }); + + const body = { + pushToken: pushTokenString, + relayUrl: nwcClient.relayUrl, + connectionPubkey: nwcClient.publicKey, + walletPubkey: nwcClient.walletPubkey, + }; + + try { + const response = await fetch( + `${NOSTR_API_URL}/nip47/notifications/go`, + { + method: "POST", + headers: { + Accept: "application/json", + "Accept-encoding": "gzip, deflate", + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }, + ); + + if (response.ok) { + const responseData = await response.json(); + useAppStore.getState().updateWallet( + { + pushId: responseData.subscriptionId, + }, + wallet.nostrWalletConnectUrl, + ); + // TODO: Send a DELETE call to nostr api on deleting the wallet + } else { + throw new Error(`Error: ${response.status} ${response.statusText}`); + } + } catch (error) { + errorToast(error); + } + } + + return; + } catch (error) { + errorToast(error); + } + } else { + errorToast("Must use physical device for push notifications"); + } +} diff --git a/yarn.lock b/yarn.lock index 98ed439..62db63e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1199,10 +1199,10 @@ mv "~2" safe-json-stringify "~1" -"@expo/cli@0.18.30": - version "0.18.30" - resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.18.30.tgz#0cb4829aa11e98ae350a5c15958b9816e9a1d2f0" - integrity sha512-V90TUJh9Ly8stYo8nwqIqNWCsYjE28GlVFWEhAFCUOp99foiQr8HSTpiiX5GIrprcPoWmlGoY+J5fQA29R4lFg== +"@expo/cli@0.18.31": + version "0.18.31" + resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.18.31.tgz#d07b7f1b2d10d146ec8b732ce1353b90912c56bd" + integrity sha512-v9llw9fT3Uv+TCM6Xllo54t672CuYtinEQZ2LPJ2EJsCwuTc4Cd2gXQaouuIVD21VoeGQnr5JtJuWbF97sBKzQ== dependencies: "@babel/runtime" "^7.20.0" "@expo/code-signing-certificates" "0.0.5" @@ -1290,10 +1290,10 @@ node-forge "^1.2.1" nullthrows "^1.1.1" -"@expo/config-plugins@8.0.10": - version "8.0.10" - resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-8.0.10.tgz#5cda076f38bc04675cb42d8acdd23d6e460a62de" - integrity sha512-KG1fnSKRmsudPU9BWkl59PyE0byrE2HTnqbOrgwr2FAhqh7tfr9nRs6A9oLS/ntpGzmFxccTEcsV0L4apsuxxg== +"@expo/config-plugins@8.0.11": + version "8.0.11" + resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-8.0.11.tgz#b814395a910f4c8b7cc95d9719dccb6ca53ea4c5" + integrity sha512-oALE1HwnLFthrobAcC9ocnR9KXLzfWEjgIe4CPe+rDsfC6GDs8dGYCXfRFoCEzoLN4TGYs9RdZ8r0KoCcNrm2A== dependencies: "@expo/config-types" "^51.0.3" "@expo/json-file" "~8.3.0" @@ -1563,23 +1563,6 @@ base64-js "^1.2.3" xmlbuilder "^14.0.0" -"@expo/prebuild-config@7.0.6": - version "7.0.6" - resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-7.0.6.tgz#b9c2c36ee564244da8073ce7bea22ebe57743615" - integrity sha512-Hts+iGBaG6OQ+N8IEMMgwQElzJeSTb7iUJ26xADEHkaexsucAK+V52dM8M4ceicvbZR9q8M+ebJEGj0MCNA3dQ== - dependencies: - "@expo/config" "~9.0.0-beta.0" - "@expo/config-plugins" "~8.0.0-beta.0" - "@expo/config-types" "^51.0.0-unreleased" - "@expo/image-utils" "^0.5.0" - "@expo/json-file" "^8.3.0" - "@react-native/normalize-colors" "0.74.84" - debug "^4.3.1" - fs-extra "^9.0.0" - resolve-from "^5.0.0" - semver "^7.6.0" - xml2js "0.6.0" - "@expo/prebuild-config@7.0.9": version "7.0.9" resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-7.0.9.tgz#7abd489e18ed6514a0c9cd214eb34c0d5efda799" @@ -2543,11 +2526,6 @@ hermes-parser "0.19.1" nullthrows "^1.1.1" -"@react-native/normalize-colors@0.74.84": - version "0.74.84" - resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.74.84.tgz#4764d59775c17a6ed193509cb01ae2f42dd5c045" - integrity sha512-Y5W6x8cC5RuakUcTVUFNAIhUZ/tYpuqHZlRBoAuakrTwVuoNHXfQki8lj1KsYU7rW6e3VWgdEx33AfOQpdNp6A== - "@react-native/normalize-colors@0.74.85": version "0.74.85" resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.74.85.tgz#62bcb9ab1b10b822ca0278fdfdf23d3b18e125da" @@ -5374,10 +5352,10 @@ expo-notifications@~0.28.19: expo-constants "~16.0.0" fs-extra "^9.1.0" -expo-router@^3.5.23: - version "3.5.23" - resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-3.5.23.tgz#da038e28c64cb69f19d046d7c651c389c5207a3e" - integrity sha512-Re2kYcxov67hWrcjuu0+3ovsLxYn79PuX6hgtYN20MgigY5ttX79KOIBEVGTO3F3y9dxSrGHyy5Z14BcO+usGQ== +expo-router@^3.5.24: + version "3.5.24" + resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-3.5.24.tgz#ac834b66c023151a3f919c456805cdd7377a677f" + integrity sha512-wFi+PIUrOntF5cgg0PgBMlkxEZlWedIv5dWnPFEzN6Tr3A3bpsqdDLgOEIwvwd+pxn5DLzykTmg9EkQ1pPGspw== dependencies: "@expo/metro-runtime" "3.2.3" "@expo/server" "^0.4.0" @@ -5385,7 +5363,7 @@ expo-router@^3.5.23: "@react-navigation/bottom-tabs" "~6.5.7" "@react-navigation/native" "~6.1.6" "@react-navigation/native-stack" "~6.9.12" - expo-splash-screen "0.27.5" + expo-splash-screen "0.27.7" react-native-helmet-async "2.0.4" schema-utils "^4.0.1" @@ -5394,27 +5372,34 @@ expo-secure-store@^13.0.2: resolved "https://registry.yarnpkg.com/expo-secure-store/-/expo-secure-store-13.0.2.tgz#ba8f6076fc38062a28bb2ce5edab9cd28ef88598" integrity sha512-3QYgoneo8p8yeeBPBiAfokNNc2xq6+n8+Ob4fAlErEcf4H7Y72LH+K/dx0nQyWau2ZKZUXBxyyfuHFyVKrEVLg== -expo-splash-screen@0.27.5: - version "0.27.5" - resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.27.5.tgz#bcc1ebb4e761e19a1f2112469f3d424a36fb1e2c" - integrity sha512-9rdZuLkFCfgJBxrheUsOEOIW6Rp+9NVlpSE0hgXQwbTCLTncf00IHSE8/L2NbFyeDLNjof1yZBppaV7tXHRUzA== +expo-splash-screen@0.27.7: + version "0.27.7" + resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.27.7.tgz#52171be54d8c008880d928e802819d767fbd3c12" + integrity sha512-s+eGcG185878nixlrjhhLD6UDYrvoqBUaBkIEozBVWFg3pkdsKpONPiUAco4XR3h7I/9ODq4quN28RJLFO+s0Q== dependencies: - "@expo/prebuild-config" "7.0.6" + "@expo/prebuild-config" "7.0.9" expo-status-bar@~1.12.1: version "1.12.1" resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-1.12.1.tgz#52ce594aab5064a0511d14375364d718ab78aa66" integrity sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA== -expo@~51.0.34: - version "51.0.38" - resolved "https://registry.yarnpkg.com/expo/-/expo-51.0.38.tgz#e4127b230454a34a507cfb9f1a2e4b3855cb0579" - integrity sha512-/B9npFkOPmv6WMIhdjQXEY0Z9k/67UZIVkodW8JxGIXwKUZAGHL+z1R5hTtWimpIrvVhyHUFU3f8uhfEKYhHNQ== +expo-task-manager@^11.8.2: + version "11.8.2" + resolved "https://registry.yarnpkg.com/expo-task-manager/-/expo-task-manager-11.8.2.tgz#1090a445565ca65ed99991166ddda38575b3dc8c" + integrity sha512-Uhy3ol5gYeZOyeRFddYjLI1B2DGRH1gjp/YC8Hpn5p5MVENviySoKNF+wd98rRvOAokzrzElyDBHSTfX+C3tpg== + dependencies: + unimodules-app-loader "~4.6.0" + +expo@~51.0.39: + version "51.0.39" + resolved "https://registry.yarnpkg.com/expo/-/expo-51.0.39.tgz#d9efab081a91a0d3e925b0e4648722b13a8fceae" + integrity sha512-Cs/9xopyzJrpXWbyVUZnr37rprdFJorRgfSp6cdBfvbjxZeKnw2MEu7wJwV/s626i5lZTPGjZPHUF9uQvt51cg== dependencies: "@babel/runtime" "^7.20.0" - "@expo/cli" "0.18.30" + "@expo/cli" "0.18.31" "@expo/config" "9.0.4" - "@expo/config-plugins" "8.0.10" + "@expo/config-plugins" "8.0.11" "@expo/metro-config" "0.18.11" "@expo/vector-icons" "^14.0.3" babel-preset-expo "~11.0.15" @@ -10597,6 +10582,11 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== +unimodules-app-loader@~4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/unimodules-app-loader/-/unimodules-app-loader-4.6.0.tgz#8836040b3acbf605fc4c2c6f6feb6dd9084ea0d4" + integrity sha512-FRNIlx7sLBDVPG117JnEBhnzZkTIgZTEwYW2rzrY9HdvLBTpRN+k0dxY50U/CAhFHW3zMD0OP5JAlnSQRhx5HA== + unique-filename@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" From 184548afc2f24cc0f6675d3d15c0d96349173d01 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 11 Nov 2024 21:35:07 +0530 Subject: [PATCH 08/97] feat: add notification service extension --- .gitignore | 1 + app.json | 7 +++ app/_layout.tsx | 39 ++++++------ assets/NotificationService.m | 117 +++++++++++++++++++++++++++++++++++ context/Notification.tsx | 54 +--------------- lib/constants.ts | 2 + lib/sharedSecret.ts | 8 +++ package.json | 3 + services/Notifications.ts | 20 +++++- yarn.lock | 57 ++++++++++++++++- 10 files changed, 236 insertions(+), 72 deletions(-) create mode 100644 assets/NotificationService.m create mode 100644 lib/sharedSecret.ts diff --git a/.gitignore b/.gitignore index 142d300..5ee8d59 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ yarn-error.* *.tsbuildinfo ios +android diff --git a/app.json b/app.json index 17170e2..d934605 100644 --- a/app.json +++ b/app.json @@ -14,6 +14,13 @@ }, "assetBundlePatterns": ["**/*"], "plugins": [ + [ + "expo-notification-service-extension-plugin", + { + "mode": "production", + "iosNSEFilePath": "./assets/NotificationService.m" + } + ], [ "expo-local-authentication", { diff --git a/app/_layout.tsx b/app/_layout.tsx index e683cee..66ac1e4 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -12,6 +12,7 @@ import Toast from "react-native-toast-message"; import PolyfillCrypto from "react-native-webview-crypto"; import { SWRConfig } from "swr"; import { toastConfig } from "~/components/ToastConfig"; +import { NotificationProvider } from "~/context/Notification"; import { UserInactivityProvider } from "~/context/UserInactivity"; import "~/global.css"; import { useInfo } from "~/hooks/useInfo"; @@ -110,24 +111,26 @@ export default function RootLayout() { return ( - - - - - - - - - - - - - + + + + + + + + + + + + + + + ); } diff --git a/assets/NotificationService.m b/assets/NotificationService.m new file mode 100644 index 0000000..ea71271 --- /dev/null +++ b/assets/NotificationService.m @@ -0,0 +1,117 @@ +#import "NotificationService.h" +#import + +@interface NotificationService () + +@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); +@property (nonatomic, strong) UNNotificationRequest *receivedRequest; +@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; + +@end + +@implementation NotificationService + +// Helper function to convert hex string to NSData +NSData* dataFromHexString(NSString *hexString) { + NSMutableData *data = [NSMutableData data]; + int idx; + for (idx = 0; idx+2 <= hexString.length; idx+=2) { + NSRange range = NSMakeRange(idx, 2); + NSString *hexByte = [hexString substringWithRange:range]; + unsigned int byte; + if ([[NSScanner scannerWithString:hexByte] scanHexInt:&byte]) { + [data appendBytes:&byte length:1]; + } else { + return nil; // invalid hex string + } + } + return data; +} + +- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { + self.receivedRequest = request; + self.contentHandler = contentHandler; + self.bestAttemptContent = [request.content mutableCopy]; + + NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.getalby.mobile.nse"]; + NSDictionary *walletsDict = [sharedDefaults objectForKey:@"wallets"]; + + NSString *appPubkey = request.content.userInfo[@"body"][@"appPubkey"]; + if (!appPubkey) { + return; + } + + NSDictionary *walletInfo = walletsDict[appPubkey]; + if (!walletInfo) { + return; + } + + NSString *sharedSecretString = walletInfo[@"sharedSecret"]; + NSString *walletName = walletInfo[@"name"]; + if (!sharedSecretString) { + return; + } + + NSData *sharedSecretData = dataFromHexString(sharedSecretString); + if (!sharedSecretData || sharedSecretData.length != kCCKeySizeAES256) { + return; + } + + NSString *encryptedContent = request.content.userInfo[@"body"][@"content"]; + NSArray *parts = [encryptedContent componentsSeparatedByString:@"?iv="]; + if (parts.count < 2) { + return; + } + + NSString *ciphertextBase64 = parts[0]; + NSString *ivBase64 = parts[1]; + + NSData *ciphertextData = [[NSData alloc] initWithBase64EncodedString:ciphertextBase64 options:0]; + NSData *ivData = [[NSData alloc] initWithBase64EncodedString:ivBase64 options:0]; + + if (!ciphertextData || !ivData || ivData.length != kCCBlockSizeAES128) { + return; + } + + // Prepare for decryption + size_t decryptedDataLength = ciphertextData.length + kCCBlockSizeAES128; + NSMutableData *plaintextData = [NSMutableData dataWithLength:decryptedDataLength]; + + size_t numBytesDecrypted = 0; + CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, sharedSecretData.bytes, sharedSecretData.length, ivData.bytes, ciphertextData.bytes, ciphertextData.length, plaintextData.mutableBytes, decryptedDataLength, &numBytesDecrypted); + + if (cryptStatus == kCCSuccess) { + plaintextData.length = numBytesDecrypted; + + NSError *jsonError = nil; + NSDictionary *parsedContent = [NSJSONSerialization JSONObjectWithData:plaintextData options:0 error:&jsonError]; + + if (!parsedContent || jsonError) { + return; + } + + NSString *notificationType = parsedContent[@"notification_type"]; + if (![notificationType isEqualToString:@"payment_received"]) { + return; + } + + NSDictionary *notificationDict = parsedContent[@"notification"]; + NSNumber *amountNumber = notificationDict[@"amount"]; + if (!amountNumber) { + return; + } + + double amountInSats = [amountNumber doubleValue] / 1000.0; + self.bestAttemptContent.title = walletName; + self.bestAttemptContent.body = [NSString stringWithFormat:@"You just received %.0f sats ⚡️", amountInSats]; + } + + self.contentHandler(self.bestAttemptContent); +} + +- (void)serviceExtensionTimeWillExpire { + self.bestAttemptContent.body = @"expired noitification"; + self.contentHandler(self.bestAttemptContent); +} + +@end diff --git a/context/Notification.tsx b/context/Notification.tsx index db5f133..34c88ab 100644 --- a/context/Notification.tsx +++ b/context/Notification.tsx @@ -1,61 +1,13 @@ import { useEffect, useRef } from "react"; -import { Nip47Notification } from "@getalby/sdk/dist/NWCClient"; import * as ExpoNotifications from "expo-notifications"; import { useAppStore } from "~/lib/state/appStore"; ExpoNotifications.setNotificationHandler({ - handleNotification: async (notification) => { - console.info("🔔 handleNotification", { - data: notification.request.content.data, - }); - - if (!notification.request.content.data.isLocal) { - console.info("🏠️ Local notification", notification.request.content); - - const encryptedData = notification.request.content.data.content; - const nwcClient = useAppStore.getState().nwcClient!; - - // TODO: Get the correct keys to decrypt - - try { - console.info("🔴", encryptedData, nwcClient?.secret); - const decryptedContent = await nwcClient.decrypt( - nwcClient?.walletPubkey!, - encryptedData, - ); - console.info("🔓️ decrypted data", decryptedContent); - const nip47Notification = JSON.parse( - decryptedContent, - ) as Nip47Notification; - console.info("decrypted", nip47Notification); - - if (nip47Notification.notification_type === "payment_received") { - ExpoNotifications.scheduleNotificationAsync({ - content: { - title: `You just received ${Math.floor(nip47Notification.notification.amount / 1000)} sats`, - body: nip47Notification.notification.description, - data: { - ...notification.request.content.data, - isLocal: true, - }, - }, - trigger: null, - }); - } - } catch (e) { - console.error("Failed to parse decrypted event content", e); - return { - shouldShowAlert: false, - shouldPlaySound: false, - shouldSetBadge: false, - }; - } - } - + handleNotification: async () => { return { - shouldShowAlert: !!notification.request.content.data.isLocal, - shouldPlaySound: false, + shouldShowAlert: true, + shouldPlaySound: true, shouldSetBadge: false, }; }, diff --git a/lib/constants.ts b/lib/constants.ts index 83ec22a..b341f6c 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -19,6 +19,8 @@ export const NAV_THEME = { }, }; +export const SUITE_NAME = "group.com.getalby.mobile.nse"; + export const BACKGROUND_NOTIFICATION_TASK = "BACKGROUND-NOTIFICATION-TASK"; export const INACTIVITY_THRESHOLD = 5 * 60 * 1000; diff --git a/lib/sharedSecret.ts b/lib/sharedSecret.ts new file mode 100644 index 0000000..280be09 --- /dev/null +++ b/lib/sharedSecret.ts @@ -0,0 +1,8 @@ +import { secp256k1 } from "@noble/curves/secp256k1"; +import { Buffer } from "buffer"; + +export function computeSharedSecret(pub: string, sk: string): string { + const sharedSecret = secp256k1.getSharedSecret(sk, "02" + pub); + const normalizedKey = sharedSecret.slice(1); + return Buffer.from(normalizedKey).toString("hex"); +} diff --git a/package.json b/package.json index 82b0266..78fd79f 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,10 @@ "test:ci": "jest" }, "dependencies": { + "@alevy97/react-native-userdefaults": "^0.2.2", "@getalby/lightning-tools": "^5.0.3", "@getalby/sdk": "^3.7.1", + "@noble/curves": "^1.6.0", "@react-native-async-storage/async-storage": "1.23.1", "@rn-primitives/dialog": "^1.0.3", "@rn-primitives/portal": "^1.0.3", @@ -46,6 +48,7 @@ "expo-linear-gradient": "~13.0.2", "expo-linking": "~6.3.1", "expo-local-authentication": "~14.0.1", + "expo-notification-service-extension-plugin": "^1.0.1", "expo-notifications": "~0.28.19", "expo-router": "^3.5.24", "expo-secure-store": "^13.0.2", diff --git a/services/Notifications.ts b/services/Notifications.ts index 7f3cf82..4fea4a4 100644 --- a/services/Notifications.ts +++ b/services/Notifications.ts @@ -1,11 +1,13 @@ import Constants from "expo-constants"; import { Platform } from "react-native"; +import UserDefaults from "@alevy97/react-native-userdefaults/src/ReactNativeUserDefaults.ios"; import { nwc } from "@getalby/sdk"; import * as Device from "expo-device"; import * as ExpoNotifications from "expo-notifications"; -import { NOSTR_API_URL } from "~/lib/constants"; +import { NOSTR_API_URL, SUITE_NAME } from "~/lib/constants"; import { errorToast } from "~/lib/errorToast"; +import { computeSharedSecret } from "~/lib/sharedSecret"; import { useAppStore } from "~/lib/state/appStore"; // TODO: add background notification handling for android @@ -52,6 +54,10 @@ export async function registerForPushNotificationsAsync() { // FIXME: This would trigger the same notification // per wallet if they belong to the same node const wallets = useAppStore.getState().wallets; + let groupDefaults; + if (Platform.OS === "ios") { + groupDefaults = new UserDefaults(SUITE_NAME); + } for (const wallet of wallets) { const nwcUrl = wallet.nostrWalletConnectUrl; @@ -95,6 +101,18 @@ export async function registerForPushNotificationsAsync() { } else { throw new Error(`Error: ${response.status} ${response.statusText}`); } + + if (groupDefaults) { + let wallets = (await groupDefaults.get("wallets")) || {}; + wallets[nwcClient.publicKey] = { + name: wallet.name, + sharedSecret: computeSharedSecret( + nwcClient.walletPubkey, + nwcClient.secret ?? "", + ), + }; + groupDefaults.set("wallets", wallets); + } } catch (error) { errorToast(error); } diff --git a/yarn.lock b/yarn.lock index 62db63e..80dabb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,11 @@ resolved "https://registry.yarnpkg.com/@0no-co/graphql.web/-/graphql.web-1.0.7.tgz#c7a762c887b3482a79ffa68f63de5e96059a62e4" integrity sha512-E3Qku4mTzdrlwVWGPxklDnME5ANrEGetvYw4i2GCRlppWXXE4QD66j7pwb8HelZwS6LnqEChhrSOGCXpbiu6MQ== +"@alevy97/react-native-userdefaults@^0.2.2": + version "0.2.2" + resolved "https://registry.yarnpkg.com/@alevy97/react-native-userdefaults/-/react-native-userdefaults-0.2.2.tgz#7e289d7f5f00fd9238941e022e5db5caad244cf8" + integrity sha512-ugcKr8SEi5SsQtxSxI0gMgg6S6vl2mTUU09fxcTKxnGVHzU3zs3WcbWUfFcbeH2XyjMIrqUc97beVpZLTg3PWQ== + "@alloc/quick-lru@^5.2.0": version "5.2.0" resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" @@ -1465,6 +1470,23 @@ dotenv-expand "~11.0.6" getenv "^1.0.0" +"@expo/image-utils@^0.3.22": + version "0.3.23" + resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.3.23.tgz#f14fd7e1f5ff6f8e4911a41e27dd274470665c3f" + integrity sha512-nhUVvW0TrRE4jtWzHQl8TR4ox7kcmrc2I0itaeJGjxF5A54uk7avgA0wRt7jP1rdvqQo1Ke1lXyLYREdhN9tPw== + dependencies: + "@expo/spawn-async" "1.5.0" + chalk "^4.0.0" + fs-extra "9.0.0" + getenv "^1.0.0" + jimp-compact "0.16.1" + mime "^2.4.4" + node-fetch "^2.6.0" + parse-png "^2.1.0" + resolve-from "^5.0.0" + semver "7.3.2" + tempy "0.3.0" + "@expo/image-utils@^0.5.0": version "0.5.1" resolved "https://registry.yarnpkg.com/@expo/image-utils/-/image-utils-0.5.1.tgz#06fade141facebcd8431355923d30f3839309942" @@ -1608,6 +1630,13 @@ debug "^4.3.4" source-map-support "~0.5.21" +"@expo/spawn-async@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.5.0.tgz#799827edd8c10ef07eb1a2ff9dcfe081d596a395" + integrity sha512-LB7jWkqrHo+5fJHNrLAFdimuSXQ2MQ4lA7SQW5bf/HbsXuV2VrT/jN/M8f/KoWt0uJMGN4k/j7Opx4AvOOxSew== + dependencies: + cross-spawn "^6.0.5" + "@expo/spawn-async@^1.5.0", "@expo/spawn-async@^1.7.2": version "1.7.2" resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.7.2.tgz#fcfe66c3e387245e72154b1a7eae8cada6a47f58" @@ -1990,11 +2019,23 @@ dependencies: "@noble/hashes" "1.3.1" +"@noble/curves@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.6.0.tgz#be5296ebcd5a1730fccea4786d420f87abfeb40b" + integrity sha512-TlaHRXDehJuRNR9TfZDNQ45mMEd5dwUwmicsafcIX4SsNiqnCHKjE/1alYPd/lDRVhxdhUAlv8uEhMCI5zjIJQ== + dependencies: + "@noble/hashes" "1.5.0" + "@noble/hashes@1.3.1": version "1.3.1" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== +"@noble/hashes@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.5.0.tgz#abadc5ca20332db2b1b2aa3e496e9af1213570b0" + integrity sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA== + "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": version "1.3.3" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" @@ -4291,7 +4332,7 @@ cross-fetch@^3.1.5: dependencies: node-fetch "^2.6.12" -cross-spawn@^6.0.0: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -5338,6 +5379,13 @@ expo-modules-core@1.12.26: dependencies: invariant "^2.2.4" +expo-notification-service-extension-plugin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/expo-notification-service-extension-plugin/-/expo-notification-service-extension-plugin-1.0.1.tgz#1f3bd59c131f23a2b6ddb7808c67be8e0951ec57" + integrity sha512-sCXJ674b4wvmzmu6INGgInl1ghlmo0B3yBhDiDzitHGVfrZBCa6g0vVfntHPMbSme8kZ9E3kK0wT9LEjC2xVTQ== + dependencies: + "@expo/image-utils" "^0.3.22" + expo-notifications@~0.28.19: version "0.28.19" resolved "https://registry.yarnpkg.com/expo-notifications/-/expo-notifications-0.28.19.tgz#d8f858b887aacfc8ac5e0a59b00a9c88ded54469" @@ -7897,7 +7945,7 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.4.1: +mime@^2.4.1, mime@^2.4.4: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -9514,6 +9562,11 @@ selfsigned@^2.4.1: "@types/node-forge" "^1.3.0" node-forge "^1" +semver@7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" From f4bf55d685692223df580169bc021d28884dc3d4 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Tue, 12 Nov 2024 19:06:33 +0530 Subject: [PATCH 09/97] feat: add messaging service for android notifications --- .gitignore | 4 +- MessagingService.kt | 144 ++++++++++++++++++++++++++++++++++++++ app.json | 3 +- app/_layout.tsx | 23 +----- package.json | 2 +- services/Notifications.ts | 29 ++++++-- yarn.lock | 17 ++--- 7 files changed, 181 insertions(+), 41 deletions(-) create mode 100644 MessagingService.kt diff --git a/.gitignore b/.gitignore index 5ee8d59..d8f729d 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,6 @@ yarn-error.* *.tsbuildinfo ios -android +android + +google-services.json diff --git a/MessagingService.kt b/MessagingService.kt new file mode 100644 index 0000000..1530d3a --- /dev/null +++ b/MessagingService.kt @@ -0,0 +1,144 @@ +package com.getalby.mobile + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.os.Build +import android.util.Base64 +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import com.google.firebase.messaging.FirebaseMessagingService +import com.google.firebase.messaging.RemoteMessage +import org.json.JSONObject +import java.nio.charset.Charset +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +class MessagingService : FirebaseMessagingService() { + + override fun onMessageReceived(remoteMessage: RemoteMessage) { + val notificationId = System.currentTimeMillis().toInt() + + val notificationManager = NotificationManagerCompat.from(this) + + if (remoteMessage.data.isEmpty()) { + return + } + + if (!remoteMessage.data.containsKey("body")) { + return + } + + var encryptedContent = "" + var appPubkey = "" + val body = remoteMessage.data["body"] ?: return + + if (body.isEmpty()) { + return + } + + try { + val jsonBody = JSONObject(body) + encryptedContent = jsonBody.optString("content", "") + appPubkey = jsonBody.optString("appPubkey", "") + } catch (e: Exception) { + return + } + + if (encryptedContent.isEmpty()) { + return + } + + val sharedSecret = getSharedSecretFromPreferences(this, appPubkey) + val walletName = getWalletNameFromPreferences(this, appPubkey) ?: "Alby Go" + + if (sharedSecret.isNullOrEmpty()) { + return + } + + val sharedSecretBytes = hexStringToByteArray(sharedSecret) + val decryptedContent = decrypt(encryptedContent, sharedSecretBytes) + + if (decryptedContent == null) { + return + } + + val amount = try { + val json = JSONObject(decryptedContent) + val notification = json.getJSONObject("notification") + notification.getInt("amount") / 1000 + } catch (e: Exception) { + return + } + + val notificationText = "You have received $amount sats ⚡️" + + // TODO: check if these are the right channel ids corressponding to expo code + // Create a notification channel for Android O and above + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val channel = NotificationChannel( + "default", + "default", + NotificationManager.IMPORTANCE_HIGH + ) + val manager = getSystemService(NotificationManager::class.java) + manager.createNotificationChannel(channel) + } + + // Build the notification + val notificationBuilder = NotificationCompat.Builder(this, "default") + .setSmallIcon(R.mipmap.ic_launcher) + .setContentTitle(walletName) + .setContentText(notificationText) + .setAutoCancel(true) + notificationManager.notify(notificationId, notificationBuilder.build()) + } + + private fun getSharedSecretFromPreferences(context: Context, key: String): String? { + val sharedPreferences = context.getSharedPreferences(context.packageName + ".settings", Context.MODE_PRIVATE) + return sharedPreferences.getString("${key}_shared_secret", null) + } + + private fun getWalletNameFromPreferences(context: Context, key: String): String? { + val sharedPreferences = context.getSharedPreferences(context.packageName + ".settings", Context.MODE_PRIVATE) + return sharedPreferences.getString("${key}_name", null) + } + + // Function to decrypt the content + private fun decrypt(content: String, key: ByteArray): String? { + val parts = content.split("?iv=") + if (parts.size < 2) { + return null + } + + val ciphertext = Base64.decode(parts[0], Base64.DEFAULT) + val iv = Base64.decode(parts[1], Base64.DEFAULT) + + return try { + val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding") + val secretKey = SecretKeySpec(key, "AES") + val ivParams = IvParameterSpec(iv) + cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParams) + + val plaintext = cipher.doFinal(ciphertext) + String(plaintext, Charset.forName("UTF-8")) + } catch (e: Exception) { + e.printStackTrace() + null + } + } + + // Helper function to convert hex string to byte array + private fun hexStringToByteArray(s: String): ByteArray { + val len = s.length + val data = ByteArray(len / 2) + var i = 0 + while (i < len) { + data[i / 2] = ((Character.digit(s[i], 16) shl 4) + + Character.digit(s[i + 1], 16)).toByte() + i += 2 + } + return data + } +} diff --git a/app.json b/app.json index d934605..72cc0ec 100644 --- a/app.json +++ b/app.json @@ -65,7 +65,8 @@ "foregroundImage": "./assets/adaptive-icon.png", "backgroundImage": "./assets/adaptive-icon-bg.png" }, - "permissions": ["android.permission.CAMERA"] + "permissions": ["android.permission.CAMERA"], + "googleServicesFile": "./google-services.json" }, "extra": { "eas": { diff --git a/app/_layout.tsx b/app/_layout.tsx index 66ac1e4..35fa219 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,10 +1,8 @@ import { Theme, ThemeProvider } from "@react-navigation/native"; import { PortalHost } from "@rn-primitives/portal"; import * as Font from "expo-font"; -import * as Notifications from "expo-notifications"; import { Slot, SplashScreen } from "expo-router"; import { StatusBar } from "expo-status-bar"; -import * as TaskManager from "expo-task-manager"; import { swrConfiguration } from "lib/swr"; import * as React from "react"; import { SafeAreaView } from "react-native"; @@ -17,7 +15,7 @@ import { UserInactivityProvider } from "~/context/UserInactivity"; import "~/global.css"; import { useInfo } from "~/hooks/useInfo"; import { SessionProvider } from "~/hooks/useSession"; -import { BACKGROUND_NOTIFICATION_TASK, NAV_THEME } from "~/lib/constants"; +import { NAV_THEME } from "~/lib/constants"; import { isBiometricSupported } from "~/lib/isBiometricSupported"; import { useAppStore } from "~/lib/state/appStore"; import { useColorScheme } from "~/lib/useColorScheme"; @@ -36,25 +34,6 @@ export { ErrorBoundary, } from "expo-router"; -// FIXME: only use this in android (?) -TaskManager.defineTask( - BACKGROUND_NOTIFICATION_TASK, - ({ data }: { data: Record }) => { - console.info("Received a notification in the background!", data?.body); - // Do something with the notification data - }, -); - -Notifications.registerTaskAsync(BACKGROUND_NOTIFICATION_TASK) - .then(() => { - console.info( - `Notifications.registerTaskAsync success: ${BACKGROUND_NOTIFICATION_TASK}`, - ); - }) - .catch((reason) => { - console.info(`Notifications registerTaskAsync failed: ${reason}`); - }); - // Prevent the splash screen from auto-hiding before getting the color scheme. SplashScreen.preventAutoHideAsync(); diff --git a/package.json b/package.json index 78fd79f..c246f7d 100644 --- a/package.json +++ b/package.json @@ -52,8 +52,8 @@ "expo-notifications": "~0.28.19", "expo-router": "^3.5.24", "expo-secure-store": "^13.0.2", + "expo-shared-preferences": "^0.4.0", "expo-status-bar": "~1.12.1", - "expo-task-manager": "^11.8.2", "lottie-react-native": "6.7.0", "lucide-react-native": "^0.376.0", "nativewind": "^4.0.1", diff --git a/services/Notifications.ts b/services/Notifications.ts index 4fea4a4..c367d4c 100644 --- a/services/Notifications.ts +++ b/services/Notifications.ts @@ -1,15 +1,21 @@ -import Constants from "expo-constants"; -import { Platform } from "react-native"; - -import UserDefaults from "@alevy97/react-native-userdefaults/src/ReactNativeUserDefaults.ios"; import { nwc } from "@getalby/sdk"; +import Constants from "expo-constants"; import * as Device from "expo-device"; import * as ExpoNotifications from "expo-notifications"; +import * as SharedPreferences from "expo-shared-preferences"; +import { Platform } from "react-native"; import { NOSTR_API_URL, SUITE_NAME } from "~/lib/constants"; import { errorToast } from "~/lib/errorToast"; import { computeSharedSecret } from "~/lib/sharedSecret"; import { useAppStore } from "~/lib/state/appStore"; +let UserDefaults: any; + +if (Platform.OS === "ios") { + UserDefaults = + require("@alevy97/react-native-userdefaults/src/ReactNativeUserDefaults.ios").default; +} + // TODO: add background notification handling for android export async function registerForPushNotificationsAsync() { @@ -102,6 +108,9 @@ export async function registerForPushNotificationsAsync() { throw new Error(`Error: ${response.status} ${response.statusText}`); } + // TODO: also update these defaults when wallet name is updated + // TODO: remove these group defaults when wallet is removed + // or when notifications are removed maybe? if (groupDefaults) { let wallets = (await groupDefaults.get("wallets")) || {}; wallets[nwcClient.publicKey] = { @@ -112,6 +121,18 @@ export async function registerForPushNotificationsAsync() { ), }; groupDefaults.set("wallets", wallets); + } else { + // TODO: json stringify and add similar to iOS + const sharedSecretKey = `${nwcClient.publicKey}_shared_secret`; + const nameKey = `${nwcClient.publicKey}_name`; + SharedPreferences.setItemAsync(nameKey, wallet.name ?? ""); + SharedPreferences.setItemAsync( + sharedSecretKey, + computeSharedSecret( + nwcClient.walletPubkey, + nwcClient.secret ?? "", + ), + ); } } catch (error) { errorToast(error); diff --git a/yarn.lock b/yarn.lock index 80dabb1..ce74522 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5420,6 +5420,11 @@ expo-secure-store@^13.0.2: resolved "https://registry.yarnpkg.com/expo-secure-store/-/expo-secure-store-13.0.2.tgz#ba8f6076fc38062a28bb2ce5edab9cd28ef88598" integrity sha512-3QYgoneo8p8yeeBPBiAfokNNc2xq6+n8+Ob4fAlErEcf4H7Y72LH+K/dx0nQyWau2ZKZUXBxyyfuHFyVKrEVLg== +expo-shared-preferences@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/expo-shared-preferences/-/expo-shared-preferences-0.4.0.tgz#948549e378606cdf994162d2476be2fafeb3671b" + integrity sha512-gsyITslV7FwT4HrnEKmcnisZbPuyjEg+1KqDfxsvFOc9gVl2/LHlvZ/KbzY38MKGiCfxqx+gr48aUypNrLjYxA== + expo-splash-screen@0.27.7: version "0.27.7" resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.27.7.tgz#52171be54d8c008880d928e802819d767fbd3c12" @@ -5432,13 +5437,6 @@ expo-status-bar@~1.12.1: resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-1.12.1.tgz#52ce594aab5064a0511d14375364d718ab78aa66" integrity sha512-/t3xdbS8KB0prj5KG5w7z+wZPFlPtkgs95BsmrP/E7Q0xHXTcDcQ6Cu2FkFuRM+PKTb17cJDnLkawyS5vDLxMA== -expo-task-manager@^11.8.2: - version "11.8.2" - resolved "https://registry.yarnpkg.com/expo-task-manager/-/expo-task-manager-11.8.2.tgz#1090a445565ca65ed99991166ddda38575b3dc8c" - integrity sha512-Uhy3ol5gYeZOyeRFddYjLI1B2DGRH1gjp/YC8Hpn5p5MVENviySoKNF+wd98rRvOAokzrzElyDBHSTfX+C3tpg== - dependencies: - unimodules-app-loader "~4.6.0" - expo@~51.0.39: version "51.0.39" resolved "https://registry.yarnpkg.com/expo/-/expo-51.0.39.tgz#d9efab081a91a0d3e925b0e4648722b13a8fceae" @@ -10635,11 +10633,6 @@ unicode-property-aliases-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== -unimodules-app-loader@~4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/unimodules-app-loader/-/unimodules-app-loader-4.6.0.tgz#8836040b3acbf605fc4c2c6f6feb6dd9084ea0d4" - integrity sha512-FRNIlx7sLBDVPG117JnEBhnzZkTIgZTEwYW2rzrY9HdvLBTpRN+k0dxY50U/CAhFHW3zMD0OP5JAlnSQRhx5HA== - unique-filename@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" From ba47b3804fa529c5bdfbc0ef9ea0905cb5718319 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Tue, 12 Nov 2024 19:23:31 +0530 Subject: [PATCH 10/97] chore: changes --- MessagingService.kt => assets/MessagingService.kt | 5 +++++ assets/NotificationService.m | 14 ++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) rename MessagingService.kt => assets/MessagingService.kt (97%) diff --git a/MessagingService.kt b/assets/MessagingService.kt similarity index 97% rename from MessagingService.kt rename to assets/MessagingService.kt index 1530d3a..c82b6a4 100644 --- a/MessagingService.kt +++ b/assets/MessagingService.kt @@ -50,6 +50,10 @@ class MessagingService : FirebaseMessagingService() { return } + if (appPubkey.isEmpty()) { + return + } + val sharedSecret = getSharedSecretFromPreferences(this, appPubkey) val walletName = getWalletNameFromPreferences(this, appPubkey) ?: "Alby Go" @@ -64,6 +68,7 @@ class MessagingService : FirebaseMessagingService() { return } + // TODO: remove if notification type is not payment_received val amount = try { val json = JSONObject(decryptedContent) val notification = json.getJSONObject("notification") diff --git a/assets/NotificationService.m b/assets/NotificationService.m index ea71271..24a4bde 100644 --- a/assets/NotificationService.m +++ b/assets/NotificationService.m @@ -32,15 +32,22 @@ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withConte self.receivedRequest = request; self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; - - NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.getalby.mobile.nse"]; - NSDictionary *walletsDict = [sharedDefaults objectForKey:@"wallets"]; + + // TODO: check if userinfo / body are empty NSString *appPubkey = request.content.userInfo[@"body"][@"appPubkey"]; if (!appPubkey) { return; } + NSString *encryptedContent = request.content.userInfo[@"body"][@"content"]; + if (!encryptedContent) { + return; + } + + NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.getalby.mobile.nse"]; + NSDictionary *walletsDict = [sharedDefaults objectForKey:@"wallets"]; + NSDictionary *walletInfo = walletsDict[appPubkey]; if (!walletInfo) { return; @@ -57,7 +64,6 @@ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withConte return; } - NSString *encryptedContent = request.content.userInfo[@"body"][@"content"]; NSArray *parts = [encryptedContent componentsSeparatedByString:@"?iv="]; if (parts.count < 2) { return; From 814959e9555cc7906d46f9beaefff29b8a019539 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 13 Nov 2024 00:21:31 +0530 Subject: [PATCH 11/97] chore: add message service config plugin --- RELEASE.md | 2 +- app.config.js | 88 +++++++++++++++++++++++ app.json | 78 -------------------- package.json | 1 + pages/settings/Notifications.tsx | 2 +- plugins/withMessageServicePlugin.js | 106 ++++++++++++++++++++++++++++ yarn.lock | 21 ++++++ 7 files changed, 218 insertions(+), 80 deletions(-) create mode 100644 app.config.js delete mode 100644 app.json create mode 100644 plugins/withMessageServicePlugin.js diff --git a/RELEASE.md b/RELEASE.md index b48b913..306c446 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -2,7 +2,7 @@ 1. Update version in -- `app.json` +- `app.config.js` - `package.json` 2. Create a git tag and push it (a new draft release will be created) diff --git a/app.config.js b/app.config.js new file mode 100644 index 0000000..eb47a4f --- /dev/null +++ b/app.config.js @@ -0,0 +1,88 @@ +import withMessagingServicePlugin from "./plugins/withMessageServicePlugin"; + +export default ({ config }) => { + return { + ...config, + name: "Alby Go", + slug: "alby-mobile", + version: "1.7.1", + scheme: ["bitcoin", "lightning", "alby"], + orientation: "portrait", + icon: "./assets/icon.png", + userInterfaceStyle: "automatic", + splash: { + image: "./assets/splash.png", + resizeMode: "cover", + backgroundColor: "#0F0C40", + }, + assetBundlePatterns: ["**/*"], + plugins: [ + [ + withMessagingServicePlugin, + { + androidFMSFilePath: "./assets/MessagingService.kt", + }, + ], + [ + "expo-notification-service-extension-plugin", + { + mode: "production", + iosNSEFilePath: "./assets/NotificationService.m", + }, + ], + [ + "expo-local-authentication", + { + faceIDPermission: "Allow Alby Go to use Face ID.", + }, + ], + [ + "expo-camera", + { + cameraPermission: + "Allow Alby Go to use the camera to scan wallet connection and payment QR codes", + recordAudioAndroid: false, + }, + ], + [ + "expo-font", + { + fonts: [ + "./assets/fonts/OpenRunde-Regular.otf", + "./assets/fonts/OpenRunde-Medium.otf", + "./assets/fonts/OpenRunde-Semibold.otf", + "./assets/fonts/OpenRunde-Bold.otf", + ], + }, + ], + "expo-router", + "expo-secure-store", + ], + ios: { + supportsTablet: true, + bundleIdentifier: "com.getalby.mobile", + config: { + usesNonExemptEncryption: false, + }, + infoPlist: { + UIBackgroundModes: ["remote-notification", "processing"], + }, + }, + android: { + package: "com.getalby.mobile", + icon: "./assets/icon.png", + adaptiveIcon: { + foregroundImage: "./assets/adaptive-icon.png", + backgroundImage: "./assets/adaptive-icon-bg.png", + }, + permissions: ["android.permission.CAMERA"], + googleServicesFile: "./google-services.json", + }, + extra: { + eas: { + projectId: "294965ec-3a67-4994-8794-5cc1117ef155", + }, + }, + owner: "roland_alby", + }; +}; diff --git a/app.json b/app.json deleted file mode 100644 index 72cc0ec..0000000 --- a/app.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "expo": { - "name": "Alby Go", - "slug": "alby-mobile", - "version": "1.7.1", - "scheme": ["bitcoin", "lightning", "alby"], - "orientation": "portrait", - "icon": "./assets/icon.png", - "userInterfaceStyle": "automatic", - "splash": { - "image": "./assets/splash.png", - "resizeMode": "cover", - "backgroundColor": "#0F0C40" - }, - "assetBundlePatterns": ["**/*"], - "plugins": [ - [ - "expo-notification-service-extension-plugin", - { - "mode": "production", - "iosNSEFilePath": "./assets/NotificationService.m" - } - ], - [ - "expo-local-authentication", - { - "faceIDPermission": "Allow Alby Go to use Face ID." - } - ], - [ - "expo-camera", - { - "cameraPermission": "Allow Alby Go to use the camera to scan wallet connection and payment QR codes", - "recordAudioAndroid": false - } - ], - [ - "expo-font", - { - "fonts": [ - "./assets/fonts/OpenRunde-Regular.otf", - "./assets/fonts/OpenRunde-Medium.otf", - "./assets/fonts/OpenRunde-Semibold.otf", - "./assets/fonts/OpenRunde-Bold.otf" - ] - } - ], - "expo-router", - "expo-secure-store" - ], - "ios": { - "supportsTablet": true, - "bundleIdentifier": "com.getalby.mobile", - "config": { - "usesNonExemptEncryption": false - }, - "infoPlist": { - "UIBackgroundModes": ["remote-notification", "processing"] - } - }, - "android": { - "package": "com.getalby.mobile", - "icon": "./assets/icon.png", - "adaptiveIcon": { - "foregroundImage": "./assets/adaptive-icon.png", - "backgroundImage": "./assets/adaptive-icon-bg.png" - }, - "permissions": ["android.permission.CAMERA"], - "googleServicesFile": "./google-services.json" - }, - "extra": { - "eas": { - "projectId": "294965ec-3a67-4994-8794-5cc1117ef155" - } - }, - "owner": "roland_alby" - } -} diff --git a/package.json b/package.json index c246f7d..7440502 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "devDependencies": { "@babel/core": "^7.20.0", "@babel/preset-typescript": "^7.24.7", + "@expo/config-plugins": "^8.0.10", "@testing-library/react-hooks": "^8.0.1", "@testing-library/react-native": "^12.7.2", "@types/crypto-js": "^4.2.2", diff --git a/pages/settings/Notifications.tsx b/pages/settings/Notifications.tsx index 0a589c3..fa1a591 100644 --- a/pages/settings/Notifications.tsx +++ b/pages/settings/Notifications.tsx @@ -21,12 +21,12 @@ export function Notifications() { { - useAppStore.getState().setNotificationsEnabled(checked); if (checked) { await registerForPushNotificationsAsync(); } else { // TODO: de-register all wallets on nostr api } + useAppStore.getState().setNotificationsEnabled(checked); }} nativeID="security" /> diff --git a/plugins/withMessageServicePlugin.js b/plugins/withMessageServicePlugin.js new file mode 100644 index 0000000..c6223d5 --- /dev/null +++ b/plugins/withMessageServicePlugin.js @@ -0,0 +1,106 @@ +const { + withAndroidManifest, + withAppBuildGradle, + withDangerousMod, +} = require("@expo/config-plugins"); +const fs = require("fs"); +const path = require("path"); + +module.exports = function withMessagingServicePlugin(config, props = {}) { + config = withMessagingService(config, props); + config = withAndroidManifest(config, modifyAndroidManifest); + config = withAppBuildGradle(config, modifyAppBuildGradle); + return config; +}; + +function getPackageName(config) { + return config.android && config.android.package + ? config.android.package + : null; +} + +function withMessagingService(config, props) { + return withDangerousMod(config, [ + "android", + async (config) => { + const projectRoot = config.modRequest.projectRoot; + const srcFilePath = path.resolve(projectRoot, props.androidFMSFilePath); + + const packageName = getPackageName(config); + if (!packageName) { + throw new Error("Android package name not found in app config."); + } + + const packagePath = packageName.replace(/\./g, path.sep); + + const destDir = path.join( + projectRoot, + "android", + "app", + "src", + "main", + "java", + packagePath, + ); + const destFilePath = path.join(destDir, "MessagingService.kt"); + + fs.mkdirSync(destDir, { recursive: true }); + fs.copyFileSync(srcFilePath, destFilePath); + + return config; + }, + ]); +} + +function modifyAndroidManifest(config) { + const androidManifest = config.modResults; + + const application = androidManifest.manifest.application?.[0]; + if (!application) { + throw new Error("Could not find in AndroidManifest.xml"); + } + + if (!application.service) { + application.service = []; + } + + const serviceExists = application.service.some( + (service) => service.$["android:name"] === ".MessagingService", + ); + + if (!serviceExists) { + application.service.push({ + $: { + "android:name": ".MessagingService", + "android:exported": "false", + }, + "intent-filter": [ + { + action: [ + { + $: { + "android:name": "com.google.firebase.MESSAGING_EVENT", + }, + }, + ], + }, + ], + }); + } + + return config; +} + +function modifyAppBuildGradle(config) { + const buildGradle = config.modResults.contents; + const dependency = `implementation("com.google.firebase:firebase-messaging:23.2.1")`; + + if (!buildGradle.includes(dependency)) { + const pattern = /dependencies\s?{/; + config.modResults.contents = buildGradle.replace(pattern, (match) => { + return `${match}\n ${dependency}`; + }); + } + + return config; +} diff --git a/yarn.lock b/yarn.lock index ce74522..cd0efc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1316,6 +1316,27 @@ xcode "^3.0.1" xml2js "0.6.0" +"@expo/config-plugins@^8.0.10": + version "8.0.10" + resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-8.0.10.tgz#5cda076f38bc04675cb42d8acdd23d6e460a62de" + integrity sha512-KG1fnSKRmsudPU9BWkl59PyE0byrE2HTnqbOrgwr2FAhqh7tfr9nRs6A9oLS/ntpGzmFxccTEcsV0L4apsuxxg== + dependencies: + "@expo/config-types" "^51.0.3" + "@expo/json-file" "~8.3.0" + "@expo/plist" "^0.1.0" + "@expo/sdk-runtime-versions" "^1.0.0" + chalk "^4.1.2" + debug "^4.3.1" + find-up "~5.0.0" + getenv "^1.0.0" + glob "7.1.6" + resolve-from "^5.0.0" + semver "^7.5.4" + slash "^3.0.0" + slugify "^1.6.6" + xcode "^3.0.1" + xml2js "0.6.0" + "@expo/config-plugins@~8.0.0": version "8.0.5" resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-8.0.5.tgz#fc165e59786e399dd4694aae2a7cd716ab8a496c" From 979dfb24666c6610288b2d7b45851f779fbbf2fa Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 13 Nov 2024 01:08:51 +0530 Subject: [PATCH 12/97] chore: use our own modified package --- package.json | 2 +- services/Notifications.ts | 2 +- yarn.lock | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 7440502..1b2d4ea 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "dependencies": { "@alevy97/react-native-userdefaults": "^0.2.2", + "@getalby/expo-shared-preferences": "^0.0.1", "@getalby/lightning-tools": "^5.0.3", "@getalby/sdk": "^3.7.1", "@noble/curves": "^1.6.0", @@ -52,7 +53,6 @@ "expo-notifications": "~0.28.19", "expo-router": "^3.5.24", "expo-secure-store": "^13.0.2", - "expo-shared-preferences": "^0.4.0", "expo-status-bar": "~1.12.1", "lottie-react-native": "6.7.0", "lucide-react-native": "^0.376.0", diff --git a/services/Notifications.ts b/services/Notifications.ts index c367d4c..1d1d1d4 100644 --- a/services/Notifications.ts +++ b/services/Notifications.ts @@ -1,8 +1,8 @@ +import * as SharedPreferences from "@getalby/expo-shared-preferences"; import { nwc } from "@getalby/sdk"; import Constants from "expo-constants"; import * as Device from "expo-device"; import * as ExpoNotifications from "expo-notifications"; -import * as SharedPreferences from "expo-shared-preferences"; import { Platform } from "react-native"; import { NOSTR_API_URL, SUITE_NAME } from "~/lib/constants"; import { errorToast } from "~/lib/errorToast"; diff --git a/yarn.lock b/yarn.lock index cd0efc5..95f3683 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1682,6 +1682,11 @@ find-up "^5.0.0" js-yaml "^4.1.0" +"@getalby/expo-shared-preferences@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@getalby/expo-shared-preferences/-/expo-shared-preferences-0.0.1.tgz#91be9e8f6b138ee4aa014896fb9f5ce25063db30" + integrity sha512-9TW1m7rLEVttF5rUW3iLNYLFy0kLNlA5HU3x7jcUfVzixuA6CC/NZgLdLSItTR1HG5Ttw4FB/6U5TBvVIkn16Q== + "@getalby/lightning-tools@^5.0.3": version "5.0.3" resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-5.0.3.tgz#4cc6ef1253a30fb4913af89b842645e0c04994bf" @@ -5441,11 +5446,6 @@ expo-secure-store@^13.0.2: resolved "https://registry.yarnpkg.com/expo-secure-store/-/expo-secure-store-13.0.2.tgz#ba8f6076fc38062a28bb2ce5edab9cd28ef88598" integrity sha512-3QYgoneo8p8yeeBPBiAfokNNc2xq6+n8+Ob4fAlErEcf4H7Y72LH+K/dx0nQyWau2ZKZUXBxyyfuHFyVKrEVLg== -expo-shared-preferences@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/expo-shared-preferences/-/expo-shared-preferences-0.4.0.tgz#948549e378606cdf994162d2476be2fafeb3671b" - integrity sha512-gsyITslV7FwT4HrnEKmcnisZbPuyjEg+1KqDfxsvFOc9gVl2/LHlvZ/KbzY38MKGiCfxqx+gr48aUypNrLjYxA== - expo-splash-screen@0.27.7: version "0.27.7" resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.27.7.tgz#52171be54d8c008880d928e802819d767fbd3c12" From 4957141828750a9957f9ebd430ba6a35d29da4fa Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 13 Nov 2024 14:05:34 +0530 Subject: [PATCH 13/97] chore: import shared preferences only in android --- services/Notifications.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services/Notifications.ts b/services/Notifications.ts index 1d1d1d4..7eb341c 100644 --- a/services/Notifications.ts +++ b/services/Notifications.ts @@ -1,4 +1,3 @@ -import * as SharedPreferences from "@getalby/expo-shared-preferences"; import { nwc } from "@getalby/sdk"; import Constants from "expo-constants"; import * as Device from "expo-device"; @@ -10,10 +9,13 @@ import { computeSharedSecret } from "~/lib/sharedSecret"; import { useAppStore } from "~/lib/state/appStore"; let UserDefaults: any; +let SharedPreferences: any; if (Platform.OS === "ios") { UserDefaults = require("@alevy97/react-native-userdefaults/src/ReactNativeUserDefaults.ios").default; +} else { + SharedPreferences = require("@getalby/expo-shared-preferences"); } // TODO: add background notification handling for android @@ -79,6 +81,7 @@ export async function registerForPushNotificationsAsync() { relayUrl: nwcClient.relayUrl, connectionPubkey: nwcClient.publicKey, walletPubkey: nwcClient.walletPubkey, + isIOS: Platform.OS === "ios", }; try { From 9b4a596668aa52d3d4ec70c5e258ae9f1c180784 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 13 Nov 2024 15:53:53 +0530 Subject: [PATCH 14/97] chore: add google services json to env --- app.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.config.js b/app.config.js index eb47a4f..27e029c 100644 --- a/app.config.js +++ b/app.config.js @@ -76,7 +76,7 @@ export default ({ config }) => { backgroundImage: "./assets/adaptive-icon-bg.png", }, permissions: ["android.permission.CAMERA"], - googleServicesFile: "./google-services.json", + googleServicesFile: process.env.GOOGLE_SERVICES_JSON, }, extra: { eas: { From 6a5ccc466244198562df710a0f3556eb31cecb21 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Thu, 14 Nov 2024 17:20:45 +0530 Subject: [PATCH 15/97] chore: further changes --- assets/MessagingService.kt | 119 +++++++++++++----------- assets/NotificationService.m | 82 ++++++++++------ lib/notifications.ts | 90 ++++++++++++++++++ lib/state/appStore.ts | 28 +++--- lib/storeWalletInfo.ts | 44 +++++++++ pages/settings/Notifications.tsx | 13 ++- pages/settings/Settings.tsx | 7 +- pages/settings/wallets/EditWallet.tsx | 6 +- pages/settings/wallets/RenameWallet.tsx | 7 +- pages/settings/wallets/SetupWallet.tsx | 14 ++- services/Notifications.ts | 116 ++++------------------- 11 files changed, 328 insertions(+), 198 deletions(-) create mode 100644 lib/notifications.ts create mode 100644 lib/storeWalletInfo.ts diff --git a/assets/MessagingService.kt b/assets/MessagingService.kt index c82b6a4..ffc7335 100644 --- a/assets/MessagingService.kt +++ b/assets/MessagingService.kt @@ -3,7 +3,9 @@ package com.getalby.mobile import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context +import android.graphics.Color import android.os.Build +// import android.os.PowerManager import android.util.Base64 import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat @@ -17,100 +19,94 @@ import javax.crypto.spec.SecretKeySpec class MessagingService : FirebaseMessagingService() { - override fun onMessageReceived(remoteMessage: RemoteMessage) { - val notificationId = System.currentTimeMillis().toInt() - - val notificationManager = NotificationManagerCompat.from(this) + data class WalletInfo( + val name: String, + val sharedSecret: String + ) + + private fun getWalletInfo(context: Context, key: String): WalletInfo? { + val sharedPreferences = context.getSharedPreferences("${context.packageName}.settings", Context.MODE_PRIVATE) + val walletsString = sharedPreferences.getString("wallets", null) ?: return null + return try { + val walletsJson = JSONObject(walletsString) + val walletJson = walletsJson.optJSONObject(key) ?: return null + WalletInfo( + name = walletJson.optString("name", "Alby Go"), + sharedSecret = walletJson.optString("sharedSecret", "") + ) + } catch (e: Exception) { + e.printStackTrace() + null + } + } + override fun onMessageReceived(remoteMessage: RemoteMessage) { if (remoteMessage.data.isEmpty()) { return } - if (!remoteMessage.data.containsKey("body")) { - return - } - - var encryptedContent = "" - var appPubkey = "" - val body = remoteMessage.data["body"] ?: return - - if (body.isEmpty()) { - return - } + val data = remoteMessage.data + val body = data["body"] ?: return - try { - val jsonBody = JSONObject(body) - encryptedContent = jsonBody.optString("content", "") - appPubkey = jsonBody.optString("appPubkey", "") + val jsonBody = try { + JSONObject(body) } catch (e: Exception) { return } - if (encryptedContent.isEmpty()) { - return - } + val encryptedContent = jsonBody.optString("content", "") + val appPubkey = jsonBody.optString("appPubkey", "") - if (appPubkey.isEmpty()) { + if (encryptedContent.isEmpty() || appPubkey.isEmpty()) { return } - val sharedSecret = getSharedSecretFromPreferences(this, appPubkey) - val walletName = getWalletNameFromPreferences(this, appPubkey) ?: "Alby Go" - - if (sharedSecret.isNullOrEmpty()) { - return + val walletInfo = getWalletInfo(this, appPubkey) ?: return + if (walletInfo.sharedSecret.isEmpty()) { + return } + val sharedSecretBytes = hexStringToByteArray(walletInfo.sharedSecret) + val walletName = walletInfo.name - val sharedSecretBytes = hexStringToByteArray(sharedSecret) - val decryptedContent = decrypt(encryptedContent, sharedSecretBytes) + val decryptedContent = decrypt(encryptedContent, sharedSecretBytes) ?: return - if (decryptedContent == null) { + val json = try { + JSONObject(decryptedContent) + } catch (e: Exception) { return } - // TODO: remove if notification type is not payment_received - val amount = try { - val json = JSONObject(decryptedContent) - val notification = json.getJSONObject("notification") - notification.getInt("amount") / 1000 - } catch (e: Exception) { + val notificationType = json.optString("notification_type", "") + if (notificationType != "payment_received") { return } - val notificationText = "You have received $amount sats ⚡️" + val notification = json.optJSONObject("notification") ?: return + val amount = notification.optInt("amount", 0) / 1000 - // TODO: check if these are the right channel ids corressponding to expo code - // Create a notification channel for Android O and above - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val channel = NotificationChannel( - "default", - "default", - NotificationManager.IMPORTANCE_HIGH - ) - val manager = getSystemService(NotificationManager::class.java) - manager.createNotificationChannel(channel) - } + val notificationText = "You have received $amount sats ⚡️" - // Build the notification val notificationBuilder = NotificationCompat.Builder(this, "default") .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle(walletName) .setContentText(notificationText) .setAutoCancel(true) + val notificationManager = NotificationManagerCompat.from(this) + val notificationId = System.currentTimeMillis().toInt() notificationManager.notify(notificationId, notificationBuilder.build()) + // wakeApp() } private fun getSharedSecretFromPreferences(context: Context, key: String): String? { - val sharedPreferences = context.getSharedPreferences(context.packageName + ".settings", Context.MODE_PRIVATE) + val sharedPreferences = context.getSharedPreferences("${context.packageName}.settings", Context.MODE_PRIVATE) return sharedPreferences.getString("${key}_shared_secret", null) } private fun getWalletNameFromPreferences(context: Context, key: String): String? { - val sharedPreferences = context.getSharedPreferences(context.packageName + ".settings", Context.MODE_PRIVATE) + val sharedPreferences = context.getSharedPreferences("${context.packageName}.settings", Context.MODE_PRIVATE) return sharedPreferences.getString("${key}_name", null) } - // Function to decrypt the content private fun decrypt(content: String, key: ByteArray): String? { val parts = content.split("?iv=") if (parts.size < 2) { @@ -129,12 +125,10 @@ class MessagingService : FirebaseMessagingService() { val plaintext = cipher.doFinal(ciphertext) String(plaintext, Charset.forName("UTF-8")) } catch (e: Exception) { - e.printStackTrace() null } } - // Helper function to convert hex string to byte array private fun hexStringToByteArray(s: String): ByteArray { val len = s.length val data = ByteArray(len / 2) @@ -146,4 +140,19 @@ class MessagingService : FirebaseMessagingService() { } return data } + + // private fun wakeApp() { + // @Suppress("DEPRECATION") + // val pm = applicationContext.getSystemService(POWER_SERVICE) as PowerManager + // val screenIsOn = pm.isInteractive + // if (!screenIsOn) { + // val wakeLockTag = packageName + "WAKELOCK" + // val wakeLock = pm.newWakeLock( + // PowerManager.FULL_WAKE_LOCK or + // PowerManager.ACQUIRE_CAUSES_WAKEUP or + // PowerManager.ON_AFTER_RELEASE, wakeLockTag + // ) + // wakeLock.acquire() + // } + // } } diff --git a/assets/NotificationService.m b/assets/NotificationService.m index 24a4bde..9762ddc 100644 --- a/assets/NotificationService.m +++ b/assets/NotificationService.m @@ -11,7 +11,6 @@ @interface NotificationService () @implementation NotificationService -// Helper function to convert hex string to NSData NSData* dataFromHexString(NSString *hexString) { NSMutableData *data = [NSMutableData data]; int idx; @@ -22,7 +21,7 @@ @implementation NotificationService if ([[NSScanner scannerWithString:hexByte] scanHexInt:&byte]) { [data appendBytes:&byte length:1]; } else { - return nil; // invalid hex string + return nil; } } return data; @@ -33,39 +32,63 @@ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withConte self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; - // TODO: check if userinfo / body are empty + NSDictionary *userInfo = request.content.userInfo; + if (!userInfo) { + self.contentHandler(nil); + return; + } + + NSDictionary *bodyDict = userInfo[@"body"]; + if (!bodyDict) { + self.contentHandler(nil); + return; + } - NSString *appPubkey = request.content.userInfo[@"body"][@"appPubkey"]; + NSString *appPubkey = bodyDict[@"appPubkey"]; if (!appPubkey) { + self.contentHandler(nil); return; } - NSString *encryptedContent = request.content.userInfo[@"body"][@"content"]; + NSString *encryptedContent = bodyDict[@"content"]; if (!encryptedContent) { + self.contentHandler(nil); return; } NSUserDefaults *sharedDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.getalby.mobile.nse"]; NSDictionary *walletsDict = [sharedDefaults objectForKey:@"wallets"]; + if (!walletsDict) { + self.contentHandler(nil); + return; + } NSDictionary *walletInfo = walletsDict[appPubkey]; if (!walletInfo) { + self.contentHandler(nil); return; } NSString *sharedSecretString = walletInfo[@"sharedSecret"]; NSString *walletName = walletInfo[@"name"]; + if (!walletName) { + walletName = @"Alby Go"; + } + if (!sharedSecretString) { + self.contentHandler(nil); return; } NSData *sharedSecretData = dataFromHexString(sharedSecretString); if (!sharedSecretData || sharedSecretData.length != kCCKeySizeAES256) { + self.contentHandler(nil); return; } NSArray *parts = [encryptedContent componentsSeparatedByString:@"?iv="]; if (parts.count < 2) { + self.contentHandler(nil); return; } @@ -76,47 +99,52 @@ - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withConte NSData *ivData = [[NSData alloc] initWithBase64EncodedString:ivBase64 options:0]; if (!ciphertextData || !ivData || ivData.length != kCCBlockSizeAES128) { + self.contentHandler(nil); return; } - - // Prepare for decryption + size_t decryptedDataLength = ciphertextData.length + kCCBlockSizeAES128; NSMutableData *plaintextData = [NSMutableData dataWithLength:decryptedDataLength]; size_t numBytesDecrypted = 0; CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, sharedSecretData.bytes, sharedSecretData.length, ivData.bytes, ciphertextData.bytes, ciphertextData.length, plaintextData.mutableBytes, decryptedDataLength, &numBytesDecrypted); - if (cryptStatus == kCCSuccess) { - plaintextData.length = numBytesDecrypted; + if (cryptStatus != kCCSuccess) { + self.contentHandler(nil); + return; + } - NSError *jsonError = nil; - NSDictionary *parsedContent = [NSJSONSerialization JSONObjectWithData:plaintextData options:0 error:&jsonError]; + plaintextData.length = numBytesDecrypted; - if (!parsedContent || jsonError) { - return; - } + NSError *jsonError = nil; + NSDictionary *parsedContent = [NSJSONSerialization JSONObjectWithData:plaintextData options:0 error:&jsonError]; - NSString *notificationType = parsedContent[@"notification_type"]; - if (![notificationType isEqualToString:@"payment_received"]) { - return; - } + if (!parsedContent || jsonError) { + self.contentHandler(nil); + return; + } - NSDictionary *notificationDict = parsedContent[@"notification"]; - NSNumber *amountNumber = notificationDict[@"amount"]; - if (!amountNumber) { - return; - } + NSString *notificationType = parsedContent[@"notification_type"]; + if (![notificationType isEqualToString:@"payment_received"]) { + self.contentHandler(nil); + return; + } - double amountInSats = [amountNumber doubleValue] / 1000.0; - self.bestAttemptContent.title = walletName; - self.bestAttemptContent.body = [NSString stringWithFormat:@"You just received %.0f sats ⚡️", amountInSats]; + NSDictionary *notificationDict = parsedContent[@"notification"]; + NSNumber *amountNumber = notificationDict[@"amount"]; + if (!amountNumber) { + self.contentHandler(nil); + return; } + double amountInSats = [amountNumber doubleValue] / 1000.0; + self.bestAttemptContent.title = walletName; + self.bestAttemptContent.body = [NSString stringWithFormat:@"You just received %.0f sats ⚡️", amountInSats]; self.contentHandler(self.bestAttemptContent); } - (void)serviceExtensionTimeWillExpire { - self.bestAttemptContent.body = @"expired noitification"; + self.bestAttemptContent.body = @"expired notification"; self.contentHandler(self.bestAttemptContent); } diff --git a/lib/notifications.ts b/lib/notifications.ts new file mode 100644 index 0000000..6a21be3 --- /dev/null +++ b/lib/notifications.ts @@ -0,0 +1,90 @@ +import { nwc } from "@getalby/sdk"; +import { Platform } from "react-native"; +import { NOSTR_API_URL } from "~/lib/constants"; +import { errorToast } from "~/lib/errorToast"; +import { computeSharedSecret } from "~/lib/sharedSecret"; +import { useAppStore } from "~/lib/state/appStore"; +import { storeWalletInfo } from "~/lib/storeWalletInfo"; + +export async function registerWalletNotifications( + nwcUrl: string, + walletId: number, + walletName?: string, +) { + if (!nwcUrl) { + return; + } + + const nwcClient = new nwc.NWCClient({ + nostrWalletConnectUrl: nwcUrl, + }); + + const pushToken = useAppStore.getState().expoPushToken; + if (!pushToken) { + errorToast(new Error("Push token is not set")); + return; + } + + const body = { + pushToken, + relayUrl: nwcClient.relayUrl, + connectionPubkey: nwcClient.publicKey, + walletPubkey: nwcClient.walletPubkey, + isIOS: Platform.OS === "ios", + }; + + try { + const response = await fetch(`${NOSTR_API_URL}/nip47/notifications/push`, { + method: "POST", + headers: { + Accept: "application/json", + "Accept-encoding": "gzip, deflate", + "Content-Type": "application/json", + }, + body: JSON.stringify(body), + }); + + if (response.ok) { + const responseData = await response.json(); + useAppStore.getState().updateWallet(walletId, { + pushId: responseData.subscriptionId, + }); + } else { + new Error(`Error: ${response.status} ${response.statusText}`); + } + + const walletData = { + name: walletName ?? "", + sharedSecret: computeSharedSecret( + nwcClient.walletPubkey, + nwcClient.secret ?? "", + ), + }; + + try { + await storeWalletInfo(nwcClient.publicKey, walletData); + } catch (storageError) { + errorToast(new Error("Failed to save wallet data")); + } + } catch (error) { + errorToast(error); + } +} + +export async function deregisterWalletNotifications(pushId?: string) { + if (!pushId) { + return; + } + try { + const response = await fetch(`${NOSTR_API_URL}/subscriptions/${pushId}`, { + method: "DELETE", + }); + if (!response.ok) { + errorToast( + new Error("Failed to deregister push notification subscription"), + ); + } + } catch (error) { + errorToast(error); + } +} diff --git a/lib/state/appStore.ts b/lib/state/appStore.ts index 5d14bf8..27629a3 100644 --- a/lib/state/appStore.ts +++ b/lib/state/appStore.ts @@ -14,11 +14,13 @@ interface AppState { readonly isNotificationsEnabled: boolean; readonly isOnboarded: boolean; readonly theme: Theme; + readonly expoPushToken: string; setUnlocked: (unlocked: boolean) => void; setTheme: (theme: Theme) => void; setOnboarded: (isOnboarded: boolean) => void; + setExpoPushToken: (expoPushToken: string) => void; setNWCClient: (nwcClient: NWCClient | undefined) => void; - updateWallet(wallet: Partial, nostrWalletConnectUrl?: string): void; + updateWallet(walletId: number, walletUpdate: Partial): void; updateCurrentWallet(wallet: Partial): void; removeCurrentWallet(): void; setFiatCurrency(fiatCurrency: string): void; @@ -37,6 +39,7 @@ const addressBookEntryKeyPrefix = "addressBookEntry"; const selectedWalletIdKey = "selectedWalletId"; const fiatCurrencyKey = "fiatCurrency"; const hasOnboardedKey = "hasOnboarded"; +const expoPushTokenKey = "expoPushToken"; const lastAlbyPaymentKey = "lastAlbyPayment"; const themeKey = "theme"; const isSecurityEnabledKey = "isSecurityEnabled"; @@ -93,17 +96,11 @@ function loadAddressBookEntries(): AddressBookEntry[] { } export const useAppStore = create()((set, get) => { - const updateWallet = ( - walletUpdate: Partial, - nostrWalletConnectUrl: string, - ) => { - const wallets = [...get().wallets]; - const walletId = wallets.findIndex( - (wallet) => wallet.nostrWalletConnectUrl === nostrWalletConnectUrl, - ); + const updateWallet = (walletId: number, walletUpdate: Partial) => { if (walletId < 0) { return; } + const wallets = [...get().wallets]; const wallet: Wallet = { ...(wallets[walletId] || {}), ...walletUpdate, @@ -133,7 +130,6 @@ export const useAppStore = create()((set, get) => { }); }; - // TODO: de-register push notification subscripiton using pushId const removeCurrentWallet = () => { const wallets = [...get().wallets]; if (wallets.length <= 1) { @@ -190,6 +186,7 @@ export const useAppStore = create()((set, get) => { theme, isOnboarded: secureStorage.getItem(hasOnboardedKey) === "true", selectedWalletId: initialSelectedWalletId, + expoPushToken: "", updateWallet, updateCurrentWallet, removeCurrentWallet, @@ -208,6 +205,10 @@ export const useAppStore = create()((set, get) => { } set({ isOnboarded }); }, + setExpoPushToken: (expoPushToken) => { + secureStorage.setItem(expoPushTokenKey, expoPushToken); + set({ expoPushToken }); + }, setNWCClient: (nwcClient) => set({ nwcClient }), setSecurityEnabled: (isEnabled) => { secureStorage.setItem(isSecurityEnabledKey, isEnabled.toString()); @@ -263,7 +264,6 @@ export const useAppStore = create()((set, get) => { updateLastAlbyPayment: () => { secureStorage.setItem(lastAlbyPaymentKey, new Date().toString()); }, - // TODO: de-register push notification subscripitons using pushId reset() { // clear wallets for (let i = 0; i < get().wallets.length; i++) { @@ -292,6 +292,12 @@ export const useAppStore = create()((set, get) => { // set to initial wallet status secureStorage.setItem(selectedWalletIdKey, "0"); + // clear notifications enabled status + secureStorage.removeItem(isNotificationsEnabledKey); + + // clear expo push notifications token + secureStorage.removeItem(expoPushTokenKey); + set({ nwcClient: undefined, fiatCurrency: undefined, diff --git a/lib/storeWalletInfo.ts b/lib/storeWalletInfo.ts new file mode 100644 index 0000000..501969b --- /dev/null +++ b/lib/storeWalletInfo.ts @@ -0,0 +1,44 @@ +import { Platform } from "react-native"; +import { SUITE_NAME } from "~/lib/constants"; + +let UserDefaults: any; +let SharedPreferences: any; + +if (Platform.OS === "ios") { + UserDefaults = + require("@alevy97/react-native-userdefaults/src/ReactNativeUserDefaults.ios").default; +} else { + SharedPreferences = require("@getalby/expo-shared-preferences"); +} + +type WalletInfo = { + name: string; + sharedSecret: string; +}; + +export async function storeWalletInfo( + publicKey: string, + walletData: Partial, +) { + if (!publicKey) { + return; + } + if (Platform.OS === "ios") { + const groupDefaults = new UserDefaults(SUITE_NAME); + let wallets = (await groupDefaults.get("wallets")) || []; + wallets[publicKey] = { + ...(wallets[publicKey] || {}), + ...walletData, + }; + await groupDefaults.set("wallets", wallets); + } else { + let wallets = []; + const walletsString = await SharedPreferences.getItemAsync("wallets"); + wallets = walletsString ? JSON.parse(walletsString) : []; + wallets[publicKey] = { + ...(wallets[publicKey] || {}), + ...walletData, + }; + await SharedPreferences.setItemAsync("wallets", JSON.stringify(wallets)); + } +} diff --git a/pages/settings/Notifications.tsx b/pages/settings/Notifications.tsx index fa1a591..703fddf 100644 --- a/pages/settings/Notifications.tsx +++ b/pages/settings/Notifications.tsx @@ -3,11 +3,12 @@ import { Text, View } from "react-native"; import Screen from "~/components/Screen"; import { Label } from "~/components/ui/label"; import { Switch } from "~/components/ui/switch"; +import { deregisterWalletNotifications } from "~/lib/notifications"; import { useAppStore } from "~/lib/state/appStore"; import { registerForPushNotificationsAsync } from "~/services/Notifications"; export function Notifications() { - // TODO: If this is enabled, register notifications on new wallets being added + const [isLoading, setLoading] = React.useState(false); const isEnabled = useAppStore((store) => store.isNotificationsEnabled); return ( @@ -19,14 +20,20 @@ export function Notifications() { Allow Go to send notifications { + setLoading(true); if (checked) { - await registerForPushNotificationsAsync(); + checked = await registerForPushNotificationsAsync(); } else { - // TODO: de-register all wallets on nostr api + const wallets = useAppStore.getState().wallets; + for (const wallet of wallets) { + await deregisterWalletNotifications(wallet.pushId); + } } useAppStore.getState().setNotificationsEnabled(checked); + setLoading(false); }} nativeID="security" /> diff --git a/pages/settings/Settings.tsx b/pages/settings/Settings.tsx index 3e01eee..a62b51d 100644 --- a/pages/settings/Settings.tsx +++ b/pages/settings/Settings.tsx @@ -19,10 +19,12 @@ import Screen from "~/components/Screen"; import { Text } from "~/components/ui/text"; import { useSession } from "~/hooks/useSession"; import { DEFAULT_CURRENCY, DEFAULT_WALLET_NAME } from "~/lib/constants"; +import { deregisterWalletNotifications } from "~/lib/notifications"; import { useAppStore } from "~/lib/state/appStore"; import { useColorScheme } from "~/lib/useColorScheme"; export function Settings() { + const wallets = useAppStore((store) => store.wallets); const wallet = useAppStore((store) => store.wallets[store.selectedWalletId]); const [developerCounter, setDeveloperCounter] = React.useState(0); const [developerMode, setDeveloperMode] = React.useState(__DEV__); @@ -129,7 +131,10 @@ export function Settings() { }, { text: "Confirm", - onPress: () => { + onPress: async () => { + for (const wallet of wallets) { + await deregisterWalletNotifications(wallet.pushId); + } router.dismissAll(); useAppStore.getState().reset(); }, diff --git a/pages/settings/wallets/EditWallet.tsx b/pages/settings/wallets/EditWallet.tsx index 26628e9..274eafc 100644 --- a/pages/settings/wallets/EditWallet.tsx +++ b/pages/settings/wallets/EditWallet.tsx @@ -14,6 +14,7 @@ import { } from "~/components/ui/card"; import { Text } from "~/components/ui/text"; import { DEFAULT_WALLET_NAME } from "~/lib/constants"; +import { deregisterWalletNotifications } from "~/lib/notifications"; import { useAppStore } from "~/lib/state/appStore"; export function EditWallet() { @@ -117,7 +118,10 @@ export function EditWallet() { }, { text: "Confirm", - onPress: () => { + onPress: async () => { + await deregisterWalletNotifications( + wallets[selectedWalletId].pushId, + ); useAppStore.getState().removeCurrentWallet(); if (wallets.length !== 1) { router.back(); diff --git a/pages/settings/wallets/RenameWallet.tsx b/pages/settings/wallets/RenameWallet.tsx index 10abd27..07a751e 100644 --- a/pages/settings/wallets/RenameWallet.tsx +++ b/pages/settings/wallets/RenameWallet.tsx @@ -9,10 +9,12 @@ import { Input } from "~/components/ui/input"; import { Text } from "~/components/ui/text"; import { DEFAULT_WALLET_NAME } from "~/lib/constants"; import { useAppStore } from "~/lib/state/appStore"; +import { storeWalletInfo } from "~/lib/storeWalletInfo"; export function RenameWallet() { const selectedWalletId = useAppStore((store) => store.selectedWalletId); const wallets = useAppStore((store) => store.wallets); + const nwcClient = useAppStore((store) => store.nwcClient); const [walletName, setWalletName] = React.useState( wallets[selectedWalletId].name || "", ); @@ -34,10 +36,13 @@ export function RenameWallet() { diff --git a/pages/send/AddressBook.tsx b/pages/send/AddressBook.tsx index b81a656..c3f60e2 100644 --- a/pages/send/AddressBook.tsx +++ b/pages/send/AddressBook.tsx @@ -1,5 +1,5 @@ import { Link, router } from "expo-router"; -import { ScrollView, TouchableOpacity, View } from "react-native"; +import { Pressable, ScrollView, TouchableOpacity, View } from "react-native"; import { Trash2 } from "~/components/Icons"; import Screen from "~/components/Screen"; import { Button } from "~/components/ui/button"; @@ -21,7 +21,7 @@ export function AddressBook() { {addressBookEntries.length > 0 ? ( addressBookEntries.map((addressBookEntry, index) => ( - { router.dismissAll(); @@ -62,7 +62,7 @@ export function AddressBook() { - + )) ) : ( No entries yet. From fce4ebc328e4185f935aeaf62917550faf251a4b Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 4 Dec 2024 12:13:50 +0530 Subject: [PATCH 39/97] chore: update version to 1.7.2 --- app.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index 657a53b..eb6438b 100644 --- a/app.json +++ b/app.json @@ -2,7 +2,7 @@ "expo": { "name": "Alby Go", "slug": "alby-mobile", - "version": "1.7.1", + "version": "1.7.2", "scheme": ["lightning", "bitcoin", "alby"], "orientation": "portrait", "icon": "./assets/icon.png", diff --git a/package.json b/package.json index 0dbe795..5f856a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "alby-go", - "version": "1.7.1", + "version": "1.7.2", "main": "./index.js", "scripts": { "start": "expo start", From 21d144cb21a71f469b1188bc03b3266078208309 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Fri, 6 Dec 2024 15:52:22 +0700 Subject: [PATCH 40/97] chore: update dependencies --- components/Screen.tsx | 4 +- lib/applyGlobalPolyfills.ts | 1 + package.json | 13 +-- yarn.lock | 199 ++++++++++++++++++++++++------------ 4 files changed, 146 insertions(+), 71 deletions(-) diff --git a/components/Screen.tsx b/components/Screen.tsx index 5a2bc9e..a478d78 100644 --- a/components/Screen.tsx +++ b/components/Screen.tsx @@ -1,10 +1,10 @@ -import { HeaderButtonProps } from "@react-navigation/native-stack/src/types"; +import { NativeStackHeaderRightProps } from "@react-navigation/native-stack/src/types"; import { Stack } from "expo-router"; import { StackAnimationTypes } from "react-native-screens"; type ScreenProps = { title: string; - right?: (props: HeaderButtonProps) => React.ReactNode; + right?: (props: NativeStackHeaderRightProps) => React.ReactNode; animation?: StackAnimationTypes; }; diff --git a/lib/applyGlobalPolyfills.ts b/lib/applyGlobalPolyfills.ts index 4301560..773ff29 100644 --- a/lib/applyGlobalPolyfills.ts +++ b/lib/applyGlobalPolyfills.ts @@ -1,3 +1,4 @@ +import "message-port-polyfill"; import "react-native-get-random-values"; import "react-native-url-polyfill/auto"; const TextEncodingPolyfill = require("text-encoding"); diff --git a/package.json b/package.json index b896bf6..dfbfcb3 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,8 @@ "test:ci": "jest" }, "dependencies": { - "@getalby/lightning-tools": "^5.0.3", - "@getalby/sdk": "^3.7.1", + "@getalby/lightning-tools": "^5.1.0", + "@getalby/sdk": "^3.8.1", "@react-native-async-storage/async-storage": "1.23.1", "@rn-primitives/dialog": "^1.0.3", "@rn-primitives/portal": "^1.0.3", @@ -37,19 +37,20 @@ "clsx": "^2.1.1", "crypto-js": "^4.2.0", "dayjs": "^1.11.10", - "expo": "~52.0.11", - "expo-camera": "~16.0.7", + "expo": "~52.0.14", + "expo-camera": "~16.0.8", "expo-clipboard": "~7.0.0", "expo-constants": "~17.0.3", "expo-font": "~13.0.1", "expo-linear-gradient": "~14.0.1", "expo-linking": "~7.0.3", "expo-local-authentication": "~15.0.1", - "expo-router": "~4.0.9", + "expo-router": "~4.0.11", "expo-secure-store": "~14.0.0", "expo-status-bar": "~2.0.0", "lottie-react-native": "7.1.0", "lucide-react-native": "^0.376.0", + "message-port-polyfill": "^0.2.0", "nativewind": "^4.0.1", "react": "18.3.1", "react-dom": "18.3.1", @@ -62,7 +63,7 @@ "react-native-svg": "15.8.0", "react-native-toast-message": "^2.2.0", "react-native-url-polyfill": "^2.0.0", - "react-native-webview": "13.12.2", + "react-native-webview": "^13.12.2", "react-native-webview-crypto": "^0.0.26", "swr": "^2.2.5", "tailwind-merge": "^2.3.0", diff --git a/yarn.lock b/yarn.lock index a50a15b..5e48209 100644 --- a/yarn.lock +++ b/yarn.lock @@ -41,7 +41,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.2.tgz#278b6b13664557de95b8f35b90d96785850bb56e" integrity sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg== -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0", "@babel/core@^7.23.9", "@babel/core@^7.25.2": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.20.0", "@babel/core@^7.23.9", "@babel/core@^7.25.2", "@babel/core@^7.26.0": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== @@ -876,10 +876,10 @@ dependencies: uuid "^8.0.0" -"@expo/cli@0.21.8": - version "0.21.8" - resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.21.8.tgz#f80a8c89b7fb1a8ab2ce5e5ae8b6dec89ad6aa3d" - integrity sha512-gU+NlL/XS9r7LEfLhjDDKuv3jEtOh+rVnk/k7Lp8WrUwaMCoEGfmQpSqLXetFCCC4UFXSaj1cdMGoy2UBw4rew== +"@expo/cli@0.22.0": + version "0.22.0" + resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.22.0.tgz#6f6bf7da60e9edfc5968bb3cd7e4907e0c213a41" + integrity sha512-89yOeyA8ObTxxR8izv80foYITXPtsyU0Q+LTalD8PytISRtmWqgbJ2b8go2kPrJJRJ8urUKIIo6k6ShjMurRxw== dependencies: "@0no-co/graphql.web" "^1.0.8" "@babel/runtime" "^7.20.0" @@ -981,6 +981,26 @@ xcode "^3.0.1" xml2js "0.6.0" +"@expo/config-plugins@~9.0.11": + version "9.0.11" + resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-9.0.11.tgz#7aec3663a783706ded3b6c7f3859221e17c9122a" + integrity sha512-zufuPQWkeEpXfMWFx2lWStoN43p6cO13p8n2KMIEK6jJMC/kkfldYyl8gYtEEYAL1nFfOf/W2pIXXPQ2sggnSw== + dependencies: + "@expo/config-types" "^52.0.0" + "@expo/json-file" "~9.0.0" + "@expo/plist" "^0.2.0" + "@expo/sdk-runtime-versions" "^1.0.0" + chalk "^4.1.2" + debug "^4.3.5" + getenv "^1.0.0" + glob "^10.4.2" + resolve-from "^5.0.0" + semver "^7.5.4" + slash "^3.0.0" + slugify "^1.6.6" + xcode "^3.0.1" + xml2js "0.6.0" + "@expo/config-types@^52.0.0": version "52.0.1" resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-52.0.1.tgz#327af1b72a3a9d4556f41e083e0e284dd8198b96" @@ -1034,10 +1054,10 @@ dotenv-expand "~11.0.6" getenv "^1.0.0" -"@expo/fingerprint@0.11.2": - version "0.11.2" - resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.11.2.tgz#94de978e94e2e2773c6a5554d53d04f7a7c710d2" - integrity sha512-WPibADqymGSKkNNnrGfw4dRipz7F8DwMSv7zb6T9oTGtdRiObrUpGmtBXmvo6z9MqWkNRprEJNxPjvkkvMvwhQ== +"@expo/fingerprint@0.11.3": + version "0.11.3" + resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.11.3.tgz#e370ae8f83e0642f752b058e2102e984a0a5bc98" + integrity sha512-9lgXmcIePvZ7Wef63XtvuN3HfCUevF4E4tQPdEbH9/dUWwpOvvwQ3KT4OJ9jdh8JJ3nTdO9eDQ/8k8xr1aQ5Kg== dependencies: "@expo/spawn-async" "^1.7.2" arg "^5.0.2" @@ -1075,7 +1095,31 @@ json5 "^2.2.3" write-file-atomic "^2.3.0" -"@expo/metro-config@0.19.4", "@expo/metro-config@~0.19.0": +"@expo/metro-config@0.19.5": + version "0.19.5" + resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.19.5.tgz#121817ac85cfc055686daafb8c85afb4f5d6a2d0" + integrity sha512-wl5lVgXq4FN4kBJHNyU5U9J5hH8S8rYXrp/pgbwA+J/smQfiElYKMYomTGbHUb4LQ0VnmlX6/kI4x/zJk+mq7w== + dependencies: + "@babel/core" "^7.20.0" + "@babel/generator" "^7.20.5" + "@babel/parser" "^7.20.0" + "@babel/types" "^7.20.0" + "@expo/config" "~10.0.4" + "@expo/env" "~0.4.0" + "@expo/json-file" "~9.0.0" + "@expo/spawn-async" "^1.7.2" + chalk "^4.1.0" + debug "^4.3.2" + fs-extra "^9.1.0" + getenv "^1.0.0" + glob "^10.4.2" + jsc-safe-url "^0.2.4" + lightningcss "~1.27.0" + minimatch "^3.0.4" + postcss "~8.4.32" + resolve-from "^5.0.0" + +"@expo/metro-config@~0.19.0": version "0.19.4" resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.19.4.tgz#940b6fad7809a92a8ffdb1bbe87aa805f5822c6b" integrity sha512-2SWwYN8MZvMIRawWEr+1RBYncitPwu2VMACRYig+wBycJ9fsPb6BMVmBYi+3MHDUlJHNy/Bqfw++jn1eqBFETQ== @@ -1208,18 +1252,18 @@ find-up "^5.0.0" js-yaml "^4.1.0" -"@getalby/lightning-tools@^5.0.3": +"@getalby/lightning-tools@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-5.1.0.tgz#85fae6c6501e59e3fc1ae836e365a35df00bab41" integrity sha512-KhLnKu6hboNYxVmmegRi06ufW3J0o/5gZ5vMe4e94tKC1o8glN/fDn1fTL4hRIqgZjfzvDPPmV1A3YUkNtQttA== -"@getalby/sdk@^3.7.1": - version "3.7.1" - resolved "https://registry.yarnpkg.com/@getalby/sdk/-/sdk-3.7.1.tgz#b4560bfbfad8cac243b18cfdefccbbd643473574" - integrity sha512-fn/JrnH7NvD4Hu9REXQ8TLQVPN/BYnv0QcCO7L5M6gQg2Clndoj7JHn7CY/fX5BQq7d9jvfujeNrXgBJkEklnw== +"@getalby/sdk@^3.8.1": + version "3.8.1" + resolved "https://registry.yarnpkg.com/@getalby/sdk/-/sdk-3.8.1.tgz#24a8229d694c13b76fdff687df4145464ed6cf08" + integrity sha512-YjeiD3hId8DxuSm2eU9XPzuSb4ijF4tqmy1v42AWbjTsGXFERIt2NHqiJ4CnxvGPrmu9scxLwmKi3bJw8ln6NA== dependencies: - eventemitter3 "^5.0.1" - nostr-tools "^1.17.0" + emittery "^1.0.3" + nostr-tools "2.9.4" "@humanwhocodes/config-array@^0.13.0": version "0.13.0" @@ -1512,12 +1556,19 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@noble/ciphers@0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-0.2.0.tgz#a12cda60f3cf1ab5d7c77068c3711d2366649ed7" - integrity sha512-6YBxJDAapHSdd3bLDv6x2wRPwq4QFMUaB3HvljNBUTThDd12eSm7/3F+2lnfzx2jvM+S6Nsy0jEt9QbPqSwqRw== +"@noble/ciphers@^0.5.1": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-0.5.3.tgz#48b536311587125e0d0c1535f73ec8375cd76b23" + integrity sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w== -"@noble/curves@1.1.0", "@noble/curves@~1.1.0": +"@noble/curves@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + +"@noble/curves@~1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== @@ -1529,6 +1580,11 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": version "1.3.3" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" @@ -2862,10 +2918,10 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" -babel-preset-expo@~12.0.2: - version "12.0.2" - resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-12.0.2.tgz#1a5f2017332871eb976c3faf8f5f586f37b5f257" - integrity sha512-WLApoPw4sOnwwJY+tzp270ndUNfq6xXcZEQUjEQJr8YyDd6uacz7/4iyt2Wl4wEQTabm9DYIZ3GVuNkZzL0M1g== +babel-preset-expo@~12.0.3: + version "12.0.3" + resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-12.0.3.tgz#2ad62fe007517704841788cfea38b333e307663f" + integrity sha512-1695e8y3U/HjifKx33vcNnFMSUSXwPWwhFxRlL6NRx2TENN6gySH82gPOWgxcra6gi+EJgXx52xG3PcqTjwW6w== dependencies: "@babel/plugin-proposal-decorators" "^7.12.9" "@babel/plugin-transform-export-namespace-from" "^7.22.11" @@ -3874,6 +3930,11 @@ emittery@^0.13.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== +emittery@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/emittery/-/emittery-1.0.3.tgz#c9d2a9c689870f15251bb13b31c67715c26d69ac" + integrity sha512-tJdCJitoy2lrC2ldJcqN4vkqJ00lT+tOWNT1hBJjO/3FDMJa5TTIiYGCKGkn/WfCyOzUMObeohbVTj00fhiLiA== + emoji-regex@^10.3.0: version "10.4.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.4.0.tgz#03553afea80b3975749cfcb36f776ca268e413d4" @@ -4413,10 +4474,10 @@ expo-asset@~11.0.1: invariant "^2.2.4" md5-file "^3.2.3" -expo-camera@~16.0.7: - version "16.0.7" - resolved "https://registry.yarnpkg.com/expo-camera/-/expo-camera-16.0.7.tgz#45e73a86c63106639cf1ff611a365d79e9d5842e" - integrity sha512-F4msGjXMa2WJ6Gest9HVA8i0txwfuwNSbGtlbgcTr5givNWfxIwx5DuW+i7w+Xstv2YqSJL3qFAAIZgHfygHTQ== +expo-camera@~16.0.8: + version "16.0.8" + resolved "https://registry.yarnpkg.com/expo-camera/-/expo-camera-16.0.8.tgz#bdef407c70525e2373cdaf9e45dbe0c8dc92f54e" + integrity sha512-QxuHsJot1M/0vCr9wV41OeEdHYp33VkvtmWv7XV2rXL/m8PJBm3m9o1ls5iL5s3hXlAC3tBCsfnt2m7ohfNdWQ== dependencies: invariant "^2.2.4" @@ -4472,10 +4533,10 @@ expo-local-authentication@~15.0.1: dependencies: invariant "^2.2.4" -expo-modules-autolinking@2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-2.0.2.tgz#cf946317e22e120dfe97440f225c8033c6bced42" - integrity sha512-n3jC7VoJLfOLGk8NWhEAvM5zSjbLh1kMUSo76nJupx5/vASxDdzihppYebrKrNXPHq5mcw8Jr+r7YB+8xHx7QQ== +expo-modules-autolinking@2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-2.0.3.tgz#c0de0129bedf1b6f9aa36093e435d00509f27fcd" + integrity sha512-Q/ALJ54eS7Cr7cmbP+unEDTkHFQivQerWWrqZxuXOrSFYGCYU22+/xAZXaJOpZwseOVsP74zSkoRY/wBimVs7w== dependencies: "@expo/spawn-async" "^1.7.2" chalk "^4.1.0" @@ -4486,17 +4547,17 @@ expo-modules-autolinking@2.0.2: require-from-string "^2.0.2" resolve-from "^5.0.0" -expo-modules-core@2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-2.0.6.tgz#4c419904613103dabdace0057ddb49b3f64f744a" - integrity sha512-IsFDn8TqhmnxNUWxkhyVqJ07x/vLlaUN1f2R4eYaP9NFoSWb0c2bTf99a03NGxnfuQ9G7Jrzu+VafSHzCKUxxQ== +expo-modules-core@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-2.1.1.tgz#970af4cfd70c8aa6fc0096dd0a6578aa003a479f" + integrity sha512-yQzYCLR2mre4BNMXuqkeJ0oSNgmGEMI6BcmIzeNZbC2NFEjiaDpKvlV9bclYCtyVhUEVNbJcEPYMr6c1Y4eR4w== dependencies: invariant "^2.2.4" -expo-router@~4.0.9: - version "4.0.9" - resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-4.0.9.tgz#37fe346761f2c7aa6dfc6d7625669d4aefda155d" - integrity sha512-bZupRd2nUWolihwhW2kqTTAVyhMaHJbtEFn49bOHtrfl0gkIHld+IecUIh+eJW6QTAcTOHCu5gVHLoJeM0mwjA== +expo-router@~4.0.11: + version "4.0.11" + resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-4.0.11.tgz#324364534fc7837a99a7894c611b3cd75e4470c5" + integrity sha512-2Qrd/fk98kC+CTg1umbuUaBaGkpdGStPpkSR99SoAjX6KWC1WhNMCv0hGFn7cRmSNOWQzgIfLGLERhRY1o4myw== dependencies: "@expo/metro-runtime" "4.0.0" "@expo/server" "^0.5.0" @@ -4522,26 +4583,26 @@ expo-status-bar@~2.0.0: resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-2.0.0.tgz#dd99adc2ace12a24c92718cd0f97b93347103393" integrity sha512-vxxdpvpNDMTEc5uTiIrbTvySKKUsOACmfl8OZuUdjNle05oGqwtq3v5YObwym/njSByjoyuZX8UpXBZnxvarwQ== -expo@~52.0.11: - version "52.0.11" - resolved "https://registry.yarnpkg.com/expo/-/expo-52.0.11.tgz#2c45faa2ac5061b916ea76c0146560c1bef02ef5" - integrity sha512-flUffjURDVufsMpoHrgsp+QDvR6xG/hjeMbzeSyFUcPP64uh3Av1/EJ4uUXhmHYV6/8YbHMwEgUbmko85vBtKQ== +expo@~52.0.14: + version "52.0.14" + resolved "https://registry.yarnpkg.com/expo/-/expo-52.0.14.tgz#e99a921dc5caff4b5b68fa96069ba7c3e64c4d17" + integrity sha512-omGLh6cmaFZPcvTuMZY9Pi0aBOseeJI7MjJAAbi2rFssNVJJme44jM0CJ4Hq3p/6XqSFlZMqnuAfsAAzm/c/Rg== dependencies: "@babel/runtime" "^7.20.0" - "@expo/cli" "0.21.8" + "@expo/cli" "0.22.0" "@expo/config" "~10.0.5" - "@expo/config-plugins" "~9.0.10" - "@expo/fingerprint" "0.11.2" - "@expo/metro-config" "0.19.4" + "@expo/config-plugins" "~9.0.11" + "@expo/fingerprint" "0.11.3" + "@expo/metro-config" "0.19.5" "@expo/vector-icons" "^14.0.0" - babel-preset-expo "~12.0.2" + babel-preset-expo "~12.0.3" expo-asset "~11.0.1" expo-constants "~17.0.3" expo-file-system "~18.0.4" expo-font "~13.0.1" expo-keep-awake "~14.0.1" - expo-modules-autolinking "2.0.2" - expo-modules-core "2.0.6" + expo-modules-autolinking "2.0.3" + expo-modules-core "2.1.1" fbemitter "^3.0.0" web-streams-polyfill "^3.3.2" whatwg-url-without-unicode "8.0.0-3" @@ -6624,6 +6685,11 @@ merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== +message-port-polyfill@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/message-port-polyfill/-/message-port-polyfill-0.2.0.tgz#fcffa2b15d1b414ff4ab88976bea05211253b5a7" + integrity sha512-bDET4Uvtw8p92Knys7X/2wj5QlX9OfuPxA0EVsYhkKYbqiE6tHdFR6k7NPShRx/ZAR/hVh4bo355iwHMSbE14A== + metro-babel-transformer@0.81.0: version "0.81.0" resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.81.0.tgz#cf468eafea52e4d8a77844eb7257f8a76e9d9d94" @@ -7067,17 +7133,24 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -nostr-tools@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.17.0.tgz#b6f62e32fedfd9e68ec0a7ce57f74c44fc768e8c" - integrity sha512-LZmR8GEWKZeElbFV5Xte75dOeE9EFUW/QLI1Ncn3JKn0kFddDKEfBbFN8Mu4TMs+L4HR/WTPha2l+PPuRnJcMw== +nostr-tools@2.9.4: + version "2.9.4" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-2.9.4.tgz#ec0e1faa95bf9e5fee30b36c842a270135f40183" + integrity sha512-Powumwkp+EWbdK1T8IsEX4daTLQhtWJvitfZ6OP2BdU1jJZvNlUp3SQB541UYw4uc9jgLbxZW6EZSdZoSfIygQ== dependencies: - "@noble/ciphers" "0.2.0" - "@noble/curves" "1.1.0" + "@noble/ciphers" "^0.5.1" + "@noble/curves" "1.2.0" "@noble/hashes" "1.3.1" "@scure/base" "1.1.1" "@scure/bip32" "1.3.1" "@scure/bip39" "1.2.1" + optionalDependencies: + nostr-wasm v0.1.0 + +nostr-wasm@v0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/nostr-wasm/-/nostr-wasm-0.1.0.tgz#17af486745feb2b7dd29503fdd81613a24058d94" + integrity sha512-78BTryCLcLYv96ONU8Ws3Q1JzjlAt+43pWQhIl86xZmWeegYCNLPml7yQ+gG3vR6V5h4XGj+TxO+SS5dsThQIA== npm-package-arg@^11.0.0: version "11.0.3" @@ -7898,10 +7971,10 @@ react-native-webview-crypto@^0.0.26: fast-base64-encode "^1.0.0" webview-crypto "^0.1.13" -react-native-webview@13.12.2: - version "13.12.2" - resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.12.2.tgz#b379b28c725632efabb42a2cf8387bd03cf34f33" - integrity sha512-OpRcEhf1IEushREax6rrKTeqGrHZ9OmryhZLBLQQU4PwjqVsq55iC8OdYSD61/F628f9rURn9THyxEZjrknpQQ== +react-native-webview@^13.12.2: + version "13.12.4" + resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.12.4.tgz#1a563a7fbd6bf53d688388d46708f273ed0ebb94" + integrity sha512-8lWeYPVWeOj0ya9ZpDesOQPRgczuN3ogQHlhS21sNXndd4kvfPG+WjlRdrvxYgj//udpwmzcWzagwLnEp60Aqg== dependencies: escape-string-regexp "^4.0.0" invariant "2.2.4" From 2048b07d00b10f83297c4c9155c4e2fbd52553b3 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Fri, 6 Dec 2024 15:04:47 +0530 Subject: [PATCH 41/97] chore: change splash background color --- app.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.json b/app.json index 486bc65..f6caee7 100644 --- a/app.json +++ b/app.json @@ -10,7 +10,7 @@ "splash": { "image": "./assets/splash.png", "resizeMode": "cover", - "backgroundColor": "#0F0C40" + "backgroundColor": "#0B0930" }, "newArchEnabled": true, "assetBundlePatterns": ["**/*"], From 590431bd5b83f696feec39e79419a02cc26dddaa Mon Sep 17 00:00:00 2001 From: im-adithya Date: Fri, 6 Dec 2024 16:05:43 +0530 Subject: [PATCH 42/97] chore: remove crypto polyfill --- app/_layout.tsx | 2 -- package.json | 3 --- pages/Transaction.tsx | 5 ++--- yarn.lock | 44 +------------------------------------------ 4 files changed, 3 insertions(+), 51 deletions(-) diff --git a/app/_layout.tsx b/app/_layout.tsx index 7e7bca3..219db1a 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -12,7 +12,6 @@ import { swrConfiguration } from "lib/swr"; import * as React from "react"; import { SafeAreaView } from "react-native"; import Toast from "react-native-toast-message"; -import PolyfillCrypto from "react-native-webview-crypto"; import { SWRConfig } from "swr"; import { toastConfig } from "~/components/ToastConfig"; import { UserInactivityProvider } from "~/context/UserInactivity"; @@ -96,7 +95,6 @@ export default function RootLayout() { - diff --git a/package.json b/package.json index caf25ba..779b10e 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,6 @@ "buffer": "^6.0.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", - "crypto-js": "^4.2.0", "dayjs": "^1.11.10", "expo": "~52.0.14", "expo-camera": "~16.0.8", @@ -64,7 +63,6 @@ "react-native-toast-message": "^2.2.0", "react-native-url-polyfill": "^2.0.0", "react-native-webview": "^13.12.2", - "react-native-webview-crypto": "^0.0.26", "swr": "^2.2.5", "tailwind-merge": "^2.3.0", "text-encoding": "^0.7.0", @@ -75,7 +73,6 @@ "@babel/preset-typescript": "^7.24.7", "@testing-library/react-hooks": "^8.0.1", "@testing-library/react-native": "^12.7.2", - "@types/crypto-js": "^4.2.2", "@types/jest": "^29.5.13", "@types/react": "~18.3.12", "eslint": "^8.57.0", diff --git a/pages/Transaction.tsx b/pages/Transaction.tsx index 845f130..89f96c2 100644 --- a/pages/Transaction.tsx +++ b/pages/Transaction.tsx @@ -1,6 +1,5 @@ import { Nip47Transaction } from "@getalby/sdk/dist/NWCClient"; -import Hex from "crypto-js/enc-hex"; -import Utf8 from "crypto-js/enc-utf8"; +import { hexToBytes } from "@noble/hashes/utils"; import dayjs from "dayjs"; import * as Clipboard from "expo-clipboard"; import { useLocalSearchParams } from "expo-router"; @@ -49,7 +48,7 @@ export function Transaction() { )?.find((record) => record.type === 7629169); if (tlvRecord) { parsedBoostagram = JSON.parse( - Utf8.stringify(Hex.parse(tlvRecord.value)), + new TextDecoder().decode(hexToBytes(tlvRecord.value)), ); } } catch (e) { diff --git a/yarn.lock b/yarn.lock index 5e48209..4ba85b0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2240,11 +2240,6 @@ resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.6.0.tgz#eac397f28bf1d6ae0ae081363eca2f425bedf0d5" integrity sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA== -"@types/crypto-js@^4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/crypto-js/-/crypto-js-4.2.2.tgz#771c4a768d94eb5922cc202a3009558204df0cea" - integrity sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ== - "@types/graceful-fs@^4.1.3": version "4.1.9" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4" @@ -3570,11 +3565,6 @@ crypt@0.0.2: resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" integrity sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow== -crypto-js@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.2.0.tgz#4d931639ecdfd12ff80e8186dba6af2c2e856631" - integrity sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q== - crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -3950,11 +3940,6 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -encode-utf8@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda" - integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw== - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -4617,11 +4602,6 @@ fast-base64-decode@^1.0.0: resolved "https://registry.yarnpkg.com/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz#b434a0dd7d92b12b43f26819300d2dafb83ee418" integrity sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q== -fast-base64-encode@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-base64-encode/-/fast-base64-encode-1.0.0.tgz#883945eb67e139dbf5a877bcca57a89e6824c7d4" - integrity sha512-z2XCzVK4fde2cuTEHu2QGkLD6BPtJNKJPn0Z7oINvmhq/quUuIIVPYKUdN0gYeZqOyurjJjBH/bUzK5gafyHvw== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -6558,11 +6538,6 @@ lodash.throttle@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ== -lodash@4.17.3: - version "4.17.3" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.3.tgz#557ed7d2a9438cac5fd5a43043ca60cb455e01f7" - integrity sha512-H+sg4+uBLOBrw9833P6gCURJjV+puWPbxM8S3H4ORlhVCmQpF5yCE50bc4Exaqm9U5Nhjw83Okq1azyb1U7mxw== - lodash@^4.17.19, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -7962,15 +7937,6 @@ react-native-url-polyfill@^2.0.0: dependencies: whatwg-url-without-unicode "8.0.0-3" -react-native-webview-crypto@^0.0.26: - version "0.0.26" - resolved "https://registry.yarnpkg.com/react-native-webview-crypto/-/react-native-webview-crypto-0.0.26.tgz#bce360876ed3367e677cb5cfa88564ca892ba72f" - integrity sha512-RshjQDik60LOhh7Q+SKIsXjnCgCIBZqZOB+v4MWa7l+0uAEyeZyMkWKL0xJRzWfTQ9vnLu4nHyn6qjEEi0uHnA== - dependencies: - encode-utf8 "^1.0.2" - fast-base64-encode "^1.0.0" - webview-crypto "^0.1.13" - react-native-webview@^13.12.2: version "13.12.4" resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.12.4.tgz#1a563a7fbd6bf53d688388d46708f273ed0ebb94" @@ -8493,7 +8459,7 @@ send@^0.19.0: range-parser "~1.2.1" statuses "2.0.1" -serialize-error@2.1.0, serialize-error@^2.1.0: +serialize-error@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a" integrity sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw== @@ -9624,14 +9590,6 @@ webidl-conversions@^7.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -webview-crypto@^0.1.13: - version "0.1.13" - resolved "https://registry.yarnpkg.com/webview-crypto/-/webview-crypto-0.1.13.tgz#290243e1d3b45712b358d2f8a69952de63c06ee2" - integrity sha512-8nRkNvvYchoFi32tooLX6qZzG4iCoxOBGsamZnZ1BnN4Nl6cATiIOUzwWDjUpfMu8Mvf+t3Dn0p9cSLrnZfwtg== - dependencies: - lodash "4.17.3" - serialize-error "2.1.0" - whatwg-encoding@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" From 5859f8157ab4ab5a6f9cdedb1dd94c71f0af44ef Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Fri, 6 Dec 2024 18:12:58 +0700 Subject: [PATCH 43/97] feat: show failed and pending transactions --- hooks/useTransactions.ts | 22 +++++++-- pages/Transaction.tsx | 88 ++++++++++++++++++++++++----------- pages/Transactions.tsx | 55 ++++++++++++++++------ pages/send/ConfirmPayment.tsx | 32 ++++++++++++- 4 files changed, 149 insertions(+), 48 deletions(-) diff --git a/hooks/useTransactions.ts b/hooks/useTransactions.ts index 993d17e..2fa3705 100644 --- a/hooks/useTransactions.ts +++ b/hooks/useTransactions.ts @@ -1,3 +1,7 @@ +import { + Nip47ListTransactionsRequest, + Nip47Transaction, +} from "@getalby/sdk/dist/NWCClient"; import { useAppStore } from "lib/state/appStore"; import useSWR from "swr"; import { TRANSACTIONS_PAGE_SIZE } from "~/lib/constants"; @@ -15,11 +19,19 @@ const fetcher = async (...args: FetchArgs) => { const page = +(transactionsUrl.searchParams.get("page") as string); try { - const transactions = await nwcClient.listTransactions({ - limit: TRANSACTIONS_PAGE_SIZE, - offset: (page - 1) * TRANSACTIONS_PAGE_SIZE, - }); - return transactions; + const transactions = await nwcClient.listTransactions( + { + limit: TRANSACTIONS_PAGE_SIZE, + offset: (page - 1) * TRANSACTIONS_PAGE_SIZE, + unpaid_outgoing: true, + } as Nip47ListTransactionsRequest /* TODO: remove cast once unpaid_outgoing or similar is part of spec/js-sdk */, + ); + return transactions as { + // TODO: undo when JS SDK includes state property + transactions: (Nip47Transaction & { + state: "settled" | "pending" | "failed"; + })[]; + }; } catch (error) { errorToast(error); throw error; diff --git a/pages/Transaction.tsx b/pages/Transaction.tsx index 845f130..ec9ddb0 100644 --- a/pages/Transaction.tsx +++ b/pages/Transaction.tsx @@ -7,7 +7,7 @@ import { useLocalSearchParams } from "expo-router"; import React from "react"; import { ScrollView, TouchableOpacity, View } from "react-native"; import Toast from "react-native-toast-message"; -import { MoveDownLeft, MoveUpRight } from "~/components/Icons"; +import { MoveDownLeft, MoveUpRight, X } from "~/components/Icons"; import Screen from "~/components/Screen"; import { Text } from "~/components/ui/text"; import { useGetFiatAmount } from "~/hooks/useGetFiatAmount"; @@ -38,7 +38,10 @@ export function Transaction() { const { transactionJSON } = useLocalSearchParams() as unknown as { transactionJSON: string; }; - const transaction: Nip47Transaction = JSON.parse(transactionJSON); + // TODO: undo when JS SDK includes state property + const transaction: Nip47Transaction & { + state: "settled" | "pending" | "failed"; + } = JSON.parse(transactionJSON); const getFiatAmount = useGetFiatAmount(); const boostagram = React.useMemo(() => { @@ -64,18 +67,43 @@ export function Transaction() { - {transaction.type === "incoming" && ( - + {transaction.state !== "failed" && ( + <> + {transaction.type === "incoming" && ( + + )} + {transaction.type === "outgoing" && ( + + )} + )} - {transaction.type === "outgoing" && ( - + {transaction.state === "failed" && ( + )} - - {transaction.type === "incoming" ? "Received" : "Sent"} + + {transaction.type === "incoming" + ? "Received" + : transaction.state === "failed" + ? "Failed" + : transaction.state === "pending" + ? "Sending" + : "Sent"} @@ -104,7 +132,9 @@ export function Transaction() { } + {transaction.state === "settled" && + transaction.type === "outgoing" && ( + + )} - - + {transaction.state === "settled" && ( + + )} diff --git a/pages/Transactions.tsx b/pages/Transactions.tsx index ee16cb9..af13f41 100644 --- a/pages/Transactions.tsx +++ b/pages/Transactions.tsx @@ -26,7 +26,11 @@ export function Transactions() { const [loadingNextPage, setLoadingNextPage] = React.useState(false); const [transactionsLoaded, setTransactionsLoaded] = React.useState(false); const [allTransactions, setAllTransactions] = React.useState< - Nip47Transaction[] + //Nip47Transaction[] + // TODO: undo when JS SDK includes state property + (Nip47Transaction & { + state: "settled" | "pending" | "failed"; + })[] >([]); const [refreshingTransactions, setRefreshingTransactions] = React.useState(false); @@ -116,26 +120,47 @@ export function Transactions() { }) } > - + - {transaction.type === "incoming" && ( - + {transaction.state !== "failed" && ( + <> + {transaction.type === "incoming" && ( + + )} + {transaction.type === "outgoing" && ( + + )} + )} - {transaction.type === "outgoing" && ( - + {transaction.state === "failed" && ( + )} - - {transaction.description - ? transaction.description - : transaction.type === "incoming" + + + {transaction.type === "incoming" ? "Received" - : "Sent"} - - - {dayjs.unix(transaction.settled_at).fromNow()} - + : transaction.state === "failed" + ? "Failed" + : transaction.state === "pending" + ? "Sending" + : "Sent"} + + + {dayjs + .unix(transaction.settled_at || transaction.created_at) + .fromNow()} + + + {transaction.description && ( + {transaction.description} + )} + {!transactions?.transactions.some( + (transaction) => transaction.state === "pending", + ) && ( + + + + + + + One or more pending payments + + + + Please check your transaction list before paying to ensure you + do not make a payment twice. + + + + + )} diff --git a/yarn.lock b/yarn.lock index 4ba85b0..a8705e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1252,10 +1252,10 @@ find-up "^5.0.0" js-yaml "^4.1.0" -"@getalby/lightning-tools@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-5.1.0.tgz#85fae6c6501e59e3fc1ae836e365a35df00bab41" - integrity sha512-KhLnKu6hboNYxVmmegRi06ufW3J0o/5gZ5vMe4e94tKC1o8glN/fDn1fTL4hRIqgZjfzvDPPmV1A3YUkNtQttA== +"@getalby/lightning-tools@^5.1.1": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-5.1.1.tgz#51125b2c58ef9372ae9efa93d0808d2205914b91" + integrity sha512-qiGWY7AMnQXywNlpEUTm/2u7Qx0C0qV0i3vlAV5ip8xV2quo4hkesHuAh6dBg/p3VC7t1fa9YUe9677hvQ3fVA== "@getalby/sdk@^3.8.1": version "3.8.1" From 113bf7f2aa44ed9b48d9d4e8f30a713137811ed6 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Sat, 7 Dec 2024 17:03:28 +0700 Subject: [PATCH 46/97] fix: remove set lightning address on receive page (confusing for new users) --- pages/receive/Receive.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pages/receive/Receive.tsx b/pages/receive/Receive.tsx index 969c882..af048b4 100644 --- a/pages/receive/Receive.tsx +++ b/pages/receive/Receive.tsx @@ -1,6 +1,6 @@ import { Nip47Transaction } from "@getalby/sdk/dist/NWCClient"; import * as Clipboard from "expo-clipboard"; -import { Link, router } from "expo-router"; +import { router } from "expo-router"; import React from "react"; import { Image, Share, TouchableOpacity, View } from "react-native"; import Toast from "react-native-toast-message"; @@ -179,7 +179,8 @@ export function Receive() { {!enterCustomAmount && !invoice && !lightningAddress && ( <> - + {/* TODO: re-add when we have a way to create a lightning address for new users */} + {/* Receive quickly with a Lightning Address @@ -190,7 +191,7 @@ export function Receive() { - + */} diff --git a/pages/Transaction.tsx b/pages/Transaction.tsx index 4b4cf66..9bad76f 100644 --- a/pages/Transaction.tsx +++ b/pages/Transaction.tsx @@ -6,7 +6,7 @@ import { useLocalSearchParams } from "expo-router"; import React from "react"; import { ScrollView, TouchableOpacity, View } from "react-native"; import Toast from "react-native-toast-message"; -import { MoveDownLeft, MoveUpRight, X } from "~/components/Icons"; +import { MoveDownIcon, MoveUpIcon, XIcon } from "~/components/Icons"; import Screen from "~/components/Screen"; import { Text } from "~/components/ui/text"; import { useGetFiatAmount } from "~/hooks/useGetFiatAmount"; @@ -72,19 +72,19 @@ export function Transaction() { {transaction.state !== "failed" && ( <> {transaction.type === "incoming" && ( - )} {transaction.type === "outgoing" && ( - + )} )} {transaction.state === "failed" && ( - + )} - + )} /> @@ -126,15 +126,19 @@ export function Transactions() { {transaction.state !== "failed" && ( <> {transaction.type === "incoming" && ( - + )} {transaction.type === "outgoing" && ( - + )} )} {transaction.state === "failed" && ( - + )} diff --git a/pages/receive/Receive.tsx b/pages/receive/Receive.tsx index af048b4..00d4385 100644 --- a/pages/receive/Receive.tsx +++ b/pages/receive/Receive.tsx @@ -6,7 +6,7 @@ import { Image, Share, TouchableOpacity, View } from "react-native"; import Toast from "react-native-toast-message"; import DismissableKeyboardView from "~/components/DismissableKeyboardView"; import { DualCurrencyInput } from "~/components/DualCurrencyInput"; -import { ArchiveRestore, Copy, Share2, ZapIcon } from "~/components/Icons"; +import { CopyIcon, ExportIcon, ShareIcon, ZapIcon } from "~/components/Icons"; import Loading from "~/components/Loading"; import QRCode from "~/components/QRCode"; import Screen from "~/components/Screen"; @@ -209,7 +209,7 @@ export function Receive() { router.push("/withdraw"); }} > - + Withdraw @@ -267,7 +267,7 @@ export function Receive() { variant="secondary" className="flex-1 flex flex-col gap-2" > - + Share {!enterCustomAmount && invoice && ( @@ -276,7 +276,7 @@ export function Receive() { onPress={copy} className="flex-1 flex flex-col gap-2" > - + Copy )} @@ -298,7 +298,7 @@ export function Receive() { router.push("/withdraw"); }} > - + Withdraw )} diff --git a/pages/send/AddressBook.tsx b/pages/send/AddressBook.tsx index c3f60e2..48b1f9b 100644 --- a/pages/send/AddressBook.tsx +++ b/pages/send/AddressBook.tsx @@ -1,6 +1,6 @@ import { Link, router } from "expo-router"; import { Pressable, ScrollView, TouchableOpacity, View } from "react-native"; -import { Trash2 } from "~/components/Icons"; +import { TrashIcon } from "~/components/Icons"; import Screen from "~/components/Screen"; import { Button } from "~/components/ui/button"; import { @@ -58,7 +58,7 @@ export function AddressBook() { useAppStore.getState().removeAddressBookEntry(index); }} > - + diff --git a/pages/send/Send.tsx b/pages/send/Send.tsx index 0111e1d..36bf59c 100644 --- a/pages/send/Send.tsx +++ b/pages/send/Send.tsx @@ -5,11 +5,7 @@ import { lnurl } from "lib/lnurl"; import React from "react"; import { View } from "react-native"; import DismissableKeyboardView from "~/components/DismissableKeyboardView"; -import { - BookUser, - ClipboardPaste, - Keyboard as KeyboardIcon, -} from "~/components/Icons"; +import { BookUserIcon, EditIcon, PasteIcon } from "~/components/Icons"; import Loading from "~/components/Loading"; import QRCodeScanner from "~/components/QRCodeScanner"; import Screen from "~/components/Screen"; @@ -194,7 +190,7 @@ export function Send() { variant="secondary" className="flex flex-col gap-2 flex-1" > - + Manual diff --git a/pages/settings/Security.tsx b/pages/settings/Security.tsx index 593d0fa..b305c0e 100644 --- a/pages/settings/Security.tsx +++ b/pages/settings/Security.tsx @@ -1,6 +1,6 @@ import React from "react"; import { Text, View } from "react-native"; -import { TriangleAlert } from "~/components/Icons"; +import { TriangleAlertIcon } from "~/components/Icons"; import Loading from "~/components/Loading"; import Screen from "~/components/Screen"; import { @@ -40,7 +40,11 @@ export function Security() { - + Setup Device Security diff --git a/pages/settings/Settings.tsx b/pages/settings/Settings.tsx index 151543e..29e29d4 100644 --- a/pages/settings/Settings.tsx +++ b/pages/settings/Settings.tsx @@ -1,13 +1,13 @@ import { Link, router } from "expo-router"; import { Alert, TouchableOpacity, View } from "react-native"; import { - Bitcoin, - Egg, - Fingerprint, - LogOut, - Palette, - Power, - Wallet2, + AtomIcon, + BitcoinIcon, + FingerprintIcon, + ResetIcon, + SignOutIcon, + ThemeIcon, + WalletIcon, } from "~/components/Icons"; import Constants from "expo-constants"; @@ -35,7 +35,7 @@ export function Settings() { - + Wallets @@ -51,7 +51,7 @@ export function Settings() { - + Fiat Currency @@ -63,7 +63,7 @@ export function Settings() { - + Security @@ -74,7 +74,7 @@ export function Settings() { className="flex flex-row gap-4" onPress={toggleColorScheme} > - + Theme ({colorScheme.charAt(0).toUpperCase() + colorScheme.substring(1)}) @@ -93,7 +93,7 @@ export function Settings() { signOut(); }} > - + Sign out - + Open Onboarding - + Reset Wallet diff --git a/pages/settings/Wallets.tsx b/pages/settings/Wallets.tsx index 9fcd21b..063b4bb 100644 --- a/pages/settings/Wallets.tsx +++ b/pages/settings/Wallets.tsx @@ -1,6 +1,6 @@ import { Link, router } from "expo-router"; import { FlatList, TouchableOpacity, View } from "react-native"; -import { Settings2, Wallet2 } from "~/components/Icons"; +import { SettingsIcon, WalletIcon } from "~/components/Icons"; import { Button } from "~/components/ui/button"; import Toast from "react-native-toast-message"; @@ -44,7 +44,7 @@ export function Wallets() { )} > - + - + )} diff --git a/pages/settings/wallets/EditWallet.tsx b/pages/settings/wallets/EditWallet.tsx index b56abf9..8b17624 100644 --- a/pages/settings/wallets/EditWallet.tsx +++ b/pages/settings/wallets/EditWallet.tsx @@ -5,10 +5,10 @@ import Toast from "react-native-toast-message"; import { Nip47Capability } from "@getalby/sdk/dist/NWCClient"; import * as Clipboard from "expo-clipboard"; import { - ArchiveRestore, - Trash2, - TriangleAlert, - Wallet2, + ExportIcon, + TrashIcon, + TriangleAlertIcon, + WalletIcon, ZapIcon, } from "~/components/Icons"; import Screen from "~/components/Screen"; @@ -34,7 +34,7 @@ export function EditWallet() { ) < 0 && ( - + Your wallet does not support {capability} @@ -46,7 +46,7 @@ export function EditWallet() { - + Wallet Name @@ -108,7 +108,7 @@ export function EditWallet() { > - + Export Wallet @@ -144,7 +144,7 @@ export function EditWallet() { > - + Delete Wallet diff --git a/pages/settings/wallets/SetupWallet.tsx b/pages/settings/wallets/SetupWallet.tsx index 8b8b404..97518e1 100644 --- a/pages/settings/wallets/SetupWallet.tsx +++ b/pages/settings/wallets/SetupWallet.tsx @@ -7,7 +7,7 @@ import React from "react"; import { Pressable, TouchableOpacity, View } from "react-native"; import Toast from "react-native-toast-message"; import DismissableKeyboardView from "~/components/DismissableKeyboardView"; -import { ClipboardPaste, HelpCircle, X } from "~/components/Icons"; +import { HelpCircleIcon, PasteIcon, XIcon } from "~/components/Icons"; import Loading from "~/components/Loading"; import QRCodeScanner from "~/components/QRCodeScanner"; import Screen from "~/components/Screen"; @@ -136,13 +136,13 @@ export function SetupWallet() { router.replace("/"); }} > - + ) : ( - + @@ -189,7 +189,7 @@ export function SetupWallet() { variant="secondary" className="flex-1 flex flex-col gap-2" > - + Paste diff --git a/pages/withdraw/Withdraw.tsx b/pages/withdraw/Withdraw.tsx index 2de74f6..6aeebaa 100644 --- a/pages/withdraw/Withdraw.tsx +++ b/pages/withdraw/Withdraw.tsx @@ -5,7 +5,7 @@ import React, { useEffect } from "react"; import { View } from "react-native"; import DismissableKeyboardView from "~/components/DismissableKeyboardView"; import { DualCurrencyInput } from "~/components/DualCurrencyInput"; -import { ClipboardPaste } from "~/components/Icons"; +import { PasteIcon } from "~/components/Icons"; import Loading from "~/components/Loading"; import QRCodeScanner from "~/components/QRCodeScanner"; import Screen from "~/components/Screen"; @@ -177,7 +177,7 @@ export function Withdraw() { variant="secondary" className="flex flex-col gap-2 flex-1" > - + Paste diff --git a/yarn.lock b/yarn.lock index db3f00d..19ce1ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1633,6 +1633,11 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== +"@popicons/react-native@^0.0.16": + version "0.0.16" + resolved "https://registry.yarnpkg.com/@popicons/react-native/-/react-native-0.0.16.tgz#a16d3592709fef87c389822df9a96029d1d71910" + integrity sha512-eO+Tf9ovQVx6hxjmTby4nU3t1pVzvigxb5mO1BtCNl52Z9PkSOid+9pDsu9LWmfJBV6h7KP+H9CDyM10nBDd5A== + "@radix-ui/primitive@1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2" From 70963f5fe53e35ef5cacdcfad2f942af6af76a6d Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 16 Dec 2024 15:44:39 +0530 Subject: [PATCH 55/97] chore: use updated design --- pages/send/LNURLPay.tsx | 61 +++++++++++++++++-------------------- pages/withdraw/Withdraw.tsx | 37 +++++++++++++++++----- 2 files changed, 58 insertions(+), 40 deletions(-) diff --git a/pages/send/LNURLPay.tsx b/pages/send/LNURLPay.tsx index 8ae7fc0..6944deb 100644 --- a/pages/send/LNURLPay.tsx +++ b/pages/send/LNURLPay.tsx @@ -3,21 +3,15 @@ import React, { useEffect } from "react"; import { View } from "react-native"; import DismissableKeyboardView from "~/components/DismissableKeyboardView"; import { DualCurrencyInput } from "~/components/DualCurrencyInput"; -import { AlertCircle } from "~/components/Icons"; import Loading from "~/components/Loading"; import { Receiver } from "~/components/Receiver"; import Screen from "~/components/Screen"; import { Button } from "~/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardTitle, -} from "~/components/ui/card"; import { Input } from "~/components/ui/input"; import { Text } from "~/components/ui/text"; import { errorToast } from "~/lib/errorToast"; import { LNURLPayServiceResponse, lnurl } from "~/lib/lnurl"; +import { cn } from "~/lib/utils"; export function LNURLPay() { const { @@ -31,10 +25,17 @@ export function LNURLPay() { }; const lnurlDetails: LNURLPayServiceResponse = JSON.parse(lnurlDetailsJSON); const [isLoading, setLoading] = React.useState(false); - const [amount, setAmount] = React.useState(amountParam ?? 0); + const [amount, setAmount] = React.useState(amountParam ?? ""); const [comment, setComment] = React.useState(""); const [isAmountReadOnly, setAmountReadOnly] = React.useState(false); + const isAmountInvalid = React.useMemo(() => { + const min = Math.floor(lnurlDetails.minSendable / 1000); + const max = Math.floor(lnurlDetails.maxSendable / 1000); + + return Number(amount) < min || Number(amount) > max; + }, [amount, lnurlDetails.minSendable, lnurlDetails.maxSendable]); + useEffect(() => { // Handle fixed amount LNURLs if (lnurlDetails.minSendable === lnurlDetails.maxSendable) { @@ -86,6 +87,24 @@ export function LNURLPay() { min={Math.floor(lnurlDetails.minSendable / 1000)} max={Math.floor(lnurlDetails.maxSendable / 1000)} /> + + + Between{" "} + {new Intl.NumberFormat().format( + Math.floor(lnurlDetails.minSendable / 1000), + )} + {" and "} + {new Intl.NumberFormat().format( + Math.floor(lnurlDetails.maxSendable / 1000), + )}{" "} + sats + + Comment @@ -101,35 +120,11 @@ export function LNURLPay() { - {lnurlDetails.minSendable !== lnurlDetails.maxSendable && ( - - - - - Sending Limit - - Enter an amount between{" "} - - {Math.floor(lnurlDetails.minSendable / 1000)} sats - {" "} - and{" "} - - {Math.floor(lnurlDetails.maxSendable / 1000)} sats - - - - - - )} - + 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..8a83b65 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 } = 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 (nwcUrl) { + (async () => { + const result = await connect(nwcUrl); + // Delay the camera to show the error message + if (!result) { + setTimeout(() => { + setStartScanning(true); + }, 2000); + } + })(); + } else { + setStartScanning(true); + } + }, [connect, nwcUrl]); + return ( <> ) : !nostrWalletConnectUrl ? ( <> - + From 3cf4d9b937fd41c0f07e6f391dd159f4df486d61 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 18 Dec 2024 20:20:39 +0530 Subject: [PATCH 62/97] fix: use expo recommended safearea --- app/_layout.tsx | 4 ++-- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/_layout.tsx b/app/_layout.tsx index 219db1a..ac227bf 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -10,7 +10,7 @@ import { Slot, SplashScreen } from "expo-router"; import { StatusBar } from "expo-status-bar"; import { swrConfiguration } from "lib/swr"; import * as React from "react"; -import { SafeAreaView } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; import Toast from "react-native-toast-message"; import { SWRConfig } from "swr"; import { toastConfig } from "~/components/ToastConfig"; @@ -95,7 +95,7 @@ export default function RootLayout() { - + diff --git a/package.json b/package.json index 97d8845..4779d2a 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "react-native-get-random-values": "^1.9.0", "react-native-qrcode-svg": "^6.3.1", "react-native-reanimated": "~3.16.1", - "react-native-safe-area-context": "4.12.0", + "react-native-safe-area-context": "5.0.0", "react-native-screens": "~4.1.0", "react-native-svg": "15.8.0", "react-native-toast-message": "^2.2.0", diff --git a/yarn.lock b/yarn.lock index db3f00d..d539a40 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7903,10 +7903,10 @@ react-native-reanimated@~3.16.1: convert-source-map "^2.0.0" invariant "^2.2.4" -react-native-safe-area-context@4.12.0: - version "4.12.0" - resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.12.0.tgz#17868522a55bbc6757418c94a1b4abdda6b045d9" - integrity sha512-ukk5PxcF4p3yu6qMZcmeiZgowhb5AsKRnil54YFUUAXVIS7PJcMHGGC+q44fCiBg44/1AJk5njGMez1m9H0BVQ== +react-native-safe-area-context@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.0.0.tgz#0f28f3b406d4466c6afdaaa615198d12741e88b5" + integrity sha512-4K4TvEbRsTDYuSSJZfMNKuJNn1+qgrSkOBwRoreiHcuqy1egrHpkhPhoN1Zg1+b3BxcVXlKXtMIf4eVaG/DPJw== react-native-screens@~4.1.0: version "4.1.0" From 88b40b7b13d98e8a18dfac7642447cd81fdfe3e1 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 18 Dec 2024 21:38:34 +0530 Subject: [PATCH 63/97] chore: revert background class --- app/_layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/_layout.tsx b/app/_layout.tsx index ac227bf..30db87f 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -95,7 +95,7 @@ export default function RootLayout() { - + From 427d4127be123932783c29c0a05aa233853ceaef Mon Sep 17 00:00:00 2001 From: im-adithya Date: Wed, 18 Dec 2024 21:53:55 +0530 Subject: [PATCH 64/97] chore: change nwcUrl to nwcUrlFromSchemeLink --- pages/settings/wallets/SetupWallet.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pages/settings/wallets/SetupWallet.tsx b/pages/settings/wallets/SetupWallet.tsx index 8a83b65..2210a53 100644 --- a/pages/settings/wallets/SetupWallet.tsx +++ b/pages/settings/wallets/SetupWallet.tsx @@ -28,7 +28,7 @@ import { REQUIRED_CAPABILITIES } from "~/lib/constants"; import { errorToast } from "~/lib/errorToast"; export function SetupWallet() { - const { nwcUrl } = useLocalSearchParams<{ + const { nwcUrl: nwcUrlFromSchemeLink } = useLocalSearchParams<{ nwcUrl: string; }>(); const wallets = useAppStore((store) => store.wallets); @@ -135,9 +135,9 @@ export function SetupWallet() { }; React.useEffect(() => { - if (nwcUrl) { + if (nwcUrlFromSchemeLink) { (async () => { - const result = await connect(nwcUrl); + const result = await connect(nwcUrlFromSchemeLink); // Delay the camera to show the error message if (!result) { setTimeout(() => { @@ -148,7 +148,7 @@ export function SetupWallet() { } else { setStartScanning(true); } - }, [connect, nwcUrl]); + }, [connect, nwcUrlFromSchemeLink]); return ( <> From b1a19ec68e513eec122ba8c093060c5c31800bb8 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Thu, 19 Dec 2024 12:49:29 +0530 Subject: [PATCH 65/97] chore: change card in wallet settings --- pages/settings/wallets/EditWallet.tsx | 38 ++++++++++++++------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/pages/settings/wallets/EditWallet.tsx b/pages/settings/wallets/EditWallet.tsx index b56abf9..f6d4f52 100644 --- a/pages/settings/wallets/EditWallet.tsx +++ b/pages/settings/wallets/EditWallet.tsx @@ -1,8 +1,7 @@ import { Link, router } from "expo-router"; -import { Alert, Pressable, Text, View } from "react-native"; +import { Alert, Pressable, View } from "react-native"; import Toast from "react-native-toast-message"; -import { Nip47Capability } from "@getalby/sdk/dist/NWCClient"; import * as Clipboard from "expo-clipboard"; import { ArchiveRestore, @@ -18,7 +17,7 @@ import { CardDescription, CardTitle, } from "~/components/ui/card"; -import { DEFAULT_WALLET_NAME } from "~/lib/constants"; +import { DEFAULT_WALLET_NAME, REQUIRED_CAPABILITIES } from "~/lib/constants"; import { useAppStore } from "~/lib/state/appStore"; export function EditWallet() { @@ -27,21 +26,24 @@ export function EditWallet() { return ( - {(["notifications", "list_transactions"] as Nip47Capability[]).map( - (capability) => - (wallets[selectedWalletId].nwcCapabilities || []).indexOf( - capability, - ) < 0 && ( - - - - - Your wallet does not support {capability} - - - - ), - )} + {/* TODO: Do not allow notifications to be toggled without notifications capability */} + + + + + This wallet might not work as expected + + Missing capabilities:  + {REQUIRED_CAPABILITIES.filter( + (capability) => + !(wallets[selectedWalletId].nwcCapabilities || []).includes( + capability, + ), + ).join(", ")} + + + + From 7cc220b31ad56213be3944c0a0faf3ca338145b7 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 23 Dec 2024 22:51:09 +0530 Subject: [PATCH 66/97] fix: pending payment warning --- pages/send/ConfirmPayment.tsx | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/pages/send/ConfirmPayment.tsx b/pages/send/ConfirmPayment.tsx index d8d4c9e..808e62d 100644 --- a/pages/send/ConfirmPayment.tsx +++ b/pages/send/ConfirmPayment.tsx @@ -2,7 +2,7 @@ import { Invoice } from "@getalby/lightning-tools"; import { Link, router, useLocalSearchParams } from "expo-router"; import React from "react"; -import { View } from "react-native"; +import { Pressable, View } from "react-native"; import { TriangleAlert, ZapIcon } from "~/components/Icons"; import Loading from "~/components/Loading"; import { Receiver } from "~/components/Receiver"; @@ -119,19 +119,21 @@ export function ConfirmPayment() { {transactions?.transactions.some( (transaction) => transaction.state === "pending", ) && ( - - - - - - One or more pending payments - - Please check your transaction list before paying to ensure - you do not make a payment twice. - - - - + + + + + + + One or more pending payments + + Please check your transaction list before paying to ensure + you do not make a payment twice. + + + + + )} + diff --git a/pages/receive/Receive.tsx b/pages/receive/Receive.tsx index 00d4385..08b0b37 100644 --- a/pages/receive/Receive.tsx +++ b/pages/receive/Receive.tsx @@ -6,7 +6,7 @@ import { Image, Share, TouchableOpacity, View } from "react-native"; import Toast from "react-native-toast-message"; import DismissableKeyboardView from "~/components/DismissableKeyboardView"; import { DualCurrencyInput } from "~/components/DualCurrencyInput"; -import { CopyIcon, ExportIcon, ShareIcon, ZapIcon } from "~/components/Icons"; +import { CopyIcon, ShareIcon, WithdrawIcon, ZapIcon } from "~/components/Icons"; import Loading from "~/components/Loading"; import QRCode from "~/components/QRCode"; import Screen from "~/components/Screen"; @@ -209,7 +209,7 @@ export function Receive() { router.push("/withdraw"); }} > - + Withdraw @@ -298,7 +298,7 @@ export function Receive() { router.push("/withdraw"); }} > - + Withdraw )} diff --git a/pages/settings/Settings.tsx b/pages/settings/Settings.tsx index 29e29d4..b9c36b0 100644 --- a/pages/settings/Settings.tsx +++ b/pages/settings/Settings.tsx @@ -1,9 +1,9 @@ import { Link, router } from "expo-router"; import { Alert, TouchableOpacity, View } from "react-native"; import { - AtomIcon, BitcoinIcon, FingerprintIcon, + OnboardingIcon, ResetIcon, SignOutIcon, ThemeIcon, @@ -103,7 +103,7 @@ export function Settings() { useAppStore.getState().setOnboarded(false); }} > - + Open Onboarding diff --git a/yarn.lock b/yarn.lock index 6fa3074..3ed274e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1633,10 +1633,10 @@ resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== -"@popicons/react-native@^0.0.16": - version "0.0.16" - resolved "https://registry.yarnpkg.com/@popicons/react-native/-/react-native-0.0.16.tgz#a16d3592709fef87c389822df9a96029d1d71910" - integrity sha512-eO+Tf9ovQVx6hxjmTby4nU3t1pVzvigxb5mO1BtCNl52Z9PkSOid+9pDsu9LWmfJBV6h7KP+H9CDyM10nBDd5A== +"@popicons/react-native@^0.0.20": + version "0.0.20" + resolved "https://registry.yarnpkg.com/@popicons/react-native/-/react-native-0.0.20.tgz#0bb9d84f63a8057e918c86a8f809e101b95eb35b" + integrity sha512-oBTQ6DNBA4NGEnODr/IhGujcDVZPgKB0uXNYEkrg9znsCYN/CZC1CTyoKnQzQSnmsZj/7+JLFd0TpUD7v1DCug== "@radix-ui/primitive@1.1.0": version "1.1.0" @@ -6590,11 +6590,6 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lucide-react-native@^0.376.0: - version "0.376.0" - resolved "https://registry.yarnpkg.com/lucide-react-native/-/lucide-react-native-0.376.0.tgz#e66640581eb290911956e64cf11bc62d791f64b8" - integrity sha512-Mo+w+cCkVpy8AeMQk9si8oLomaCsOsPi4WHR9oFm9YHz3I9nFvVdiMIUNvuWLSL5dPD1i+AVzn0ahpNFviEPlQ== - make-dir@^2.0.0, make-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" From ca81ae86c4456b20e79635516c1f9f594d631896 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Tue, 24 Dec 2024 19:07:26 +0530 Subject: [PATCH 74/97] chore: change skeleton height --- pages/Home.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/Home.tsx b/pages/Home.tsx index dee7ebb..237636f 100644 --- a/pages/Home.tsx +++ b/pages/Home.tsx @@ -127,7 +127,7 @@ export function Home() { ) : ( - + )} @@ -142,7 +142,7 @@ export function Home() { ) + " sats"} ) : ( - + )} From d3c5d20fe95e39e38f2ef028cb1b39f08efb9625 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Fri, 27 Dec 2024 09:54:01 +0530 Subject: [PATCH 75/97] feat: simplify wallet renaming flow --- components/Icons.tsx | 2 +- lib/state/appStore.ts | 37 ++++++++++++--------- pages/Home.tsx | 6 ++-- pages/settings/Wallets.tsx | 28 ++++++++-------- pages/settings/wallets/EditWallet.tsx | 27 +++++++-------- pages/settings/wallets/LightningAddress.tsx | 13 ++++---- pages/settings/wallets/RenameWallet.tsx | 16 +++++---- 7 files changed, 67 insertions(+), 62 deletions(-) diff --git a/components/Icons.tsx b/components/Icons.tsx index 67f34a2..e26c711 100644 --- a/components/Icons.tsx +++ b/components/Icons.tsx @@ -16,7 +16,7 @@ import { PopiconsClipboardTextSolid as PasteIcon, PopiconsReloadLine as RefreshIcon, PopiconsReloadSolid as ResetIcon, - PopiconsSettingsMinimalLine as SettingsIcon, + PopiconsSettingsMinimalSolid as SettingsIcon, PopiconsShareSolid as ShareIcon, PopiconsLogoutSolid as SignOutIcon, PopiconsLoopSolid as SwapIcon, diff --git a/lib/state/appStore.ts b/lib/state/appStore.ts index a5fa0db..e56455a 100644 --- a/lib/state/appStore.ts +++ b/lib/state/appStore.ts @@ -16,9 +16,10 @@ interface AppState { setUnlocked: (unlocked: boolean) => void; setTheme: (theme: Theme) => void; setOnboarded: (isOnboarded: boolean) => void; + getNWCClient: (walletId: number) => NWCClient | undefined; setNWCClient: (nwcClient: NWCClient | undefined) => void; - updateCurrentWallet(wallet: Partial): void; - removeCurrentWallet(): void; + updateWallet(wallet: Partial, walletId?: number): void; + removeWallet(walletId?: number): void; setFiatCurrency(fiatCurrency: string): void; setSelectedWalletId(walletId: number): void; setSecurityEnabled(securityEnabled: boolean): void; @@ -89,25 +90,22 @@ function loadAddressBookEntries(): AddressBookEntry[] { } export const useAppStore = create()((set, get) => { - const updateCurrentWallet = (walletUpdate: Partial) => { - const selectedWalletId = get().selectedWalletId; + const updateWallet = (walletUpdate: Partial, walletId?: number) => { + walletId = walletId ?? get().selectedWalletId; const wallets = [...get().wallets]; const wallet: Wallet = { - ...(wallets[selectedWalletId] || {}), + ...(wallets[walletId] || {}), ...walletUpdate, }; - secureStorage.setItem( - getWalletKey(selectedWalletId), - JSON.stringify(wallet), - ); - wallets[selectedWalletId] = wallet; + secureStorage.setItem(getWalletKey(walletId), JSON.stringify(wallet)); + wallets[walletId] = wallet; set({ wallets, }); }; - const removeCurrentWallet = () => { + const removeWallet = (walletId?: number) => { const wallets = [...get().wallets]; if (wallets.length <= 1) { // set to initial wallet status @@ -123,9 +121,10 @@ export const useAppStore = create()((set, get) => { return; } const selectedWalletId = get().selectedWalletId; + walletId = walletId ?? selectedWalletId; // move existing wallets down one - for (let i = selectedWalletId; i < wallets.length - 1; i++) { + for (let i = walletId; i < wallets.length - 1; i++) { const nextWallet = secureStorage.getItem(getWalletKey(i + 1)); if (!nextWallet) { throw new Error("Next wallet not found"); @@ -135,9 +134,14 @@ export const useAppStore = create()((set, get) => { secureStorage.removeItem(getWalletKey(wallets.length - 1)); - get().setSelectedWalletId(0); + if (walletId === selectedWalletId) { + get().setSelectedWalletId(0); + } else if (walletId < selectedWalletId) { + get().setSelectedWalletId(selectedWalletId - 1); + } + set({ - wallets: wallets.filter((_, i) => i !== selectedWalletId), + wallets: wallets.filter((_, i) => i !== walletId), }); }; @@ -181,9 +185,10 @@ export const useAppStore = create()((set, get) => { theme, isOnboarded: secureStorage.getItem(hasOnboardedKey) === "true", selectedWalletId: initialSelectedWalletId, - updateCurrentWallet, - removeCurrentWallet, + updateWallet, + removeWallet, removeAddressBookEntry, + getNWCClient, setUnlocked: (unlocked) => { set({ unlocked }); }, diff --git a/pages/Home.tsx b/pages/Home.tsx index ca4366c..b81d322 100644 --- a/pages/Home.tsx +++ b/pages/Home.tsx @@ -71,7 +71,7 @@ export function Home() { right={() => ( - + )} @@ -93,7 +93,7 @@ export function Home() { onPress={switchBalanceState} className="w-full flex flex-col items-center justify-center gap-4" > - {wallets.length && ( + {wallets.length > 1 && ( { router.push("/settings/wallets"); @@ -149,7 +149,7 @@ export function Home() { - + - {active && ( - - - - - - )} + + + + + ); }} diff --git a/pages/settings/wallets/EditWallet.tsx b/pages/settings/wallets/EditWallet.tsx index c725f0b..999cc3e 100644 --- a/pages/settings/wallets/EditWallet.tsx +++ b/pages/settings/wallets/EditWallet.tsx @@ -1,8 +1,7 @@ -import { Link, router } from "expo-router"; +import * as Clipboard from "expo-clipboard"; +import { Link, router, useLocalSearchParams } from "expo-router"; import { Pressable, Alert as RNAlert, View } from "react-native"; import Toast from "react-native-toast-message"; - -import * as Clipboard from "expo-clipboard"; import Alert from "~/components/Alert"; import { ExportIcon, @@ -22,29 +21,30 @@ import { DEFAULT_WALLET_NAME, REQUIRED_CAPABILITIES } from "~/lib/constants"; import { useAppStore } from "~/lib/state/appStore"; export function EditWallet() { - const selectedWalletId = useAppStore((store) => store.selectedWalletId); + const { id } = useLocalSearchParams() as { id: string }; const wallets = useAppStore((store) => store.wallets); + + let walletId = parseInt(id); + return ( {/* TODO: Do not allow notifications to be toggled without notifications capability */} {!REQUIRED_CAPABILITIES.every((capability) => - (wallets[selectedWalletId].nwcCapabilities || []).includes(capability), + (wallets[walletId]?.nwcCapabilities || []).includes(capability), ) && ( - !(wallets[selectedWalletId].nwcCapabilities || []).includes( - capability, - ), + !(wallets[walletId]?.nwcCapabilities || []).includes(capability), ).join(", ")}`} icon={TriangleAlertIcon} className="mb-0" /> )} - + @@ -52,17 +52,14 @@ export function EditWallet() { Wallet Name - {wallets[selectedWalletId].name || DEFAULT_WALLET_NAME} + {wallets[walletId]?.name || DEFAULT_WALLET_NAME} - + @@ -134,7 +131,7 @@ export function EditWallet() { { text: "Confirm", onPress: () => { - useAppStore.getState().removeCurrentWallet(); + useAppStore.getState().removeWallet(walletId); if (wallets.length !== 1) { router.back(); } diff --git a/pages/settings/wallets/LightningAddress.tsx b/pages/settings/wallets/LightningAddress.tsx index 360e57a..3832af2 100644 --- a/pages/settings/wallets/LightningAddress.tsx +++ b/pages/settings/wallets/LightningAddress.tsx @@ -1,5 +1,5 @@ import { LightningAddress } from "@getalby/lightning-tools"; -import { router } from "expo-router"; +import { router, useLocalSearchParams } from "expo-router"; import React from "react"; import { View } from "react-native"; import Toast from "react-native-toast-message"; @@ -13,19 +13,20 @@ import { errorToast } from "~/lib/errorToast"; import { useAppStore } from "~/lib/state/appStore"; export function SetLightningAddress() { - const selectedWalletId = useAppStore((store) => store.selectedWalletId); + const { id } = useLocalSearchParams() as { id: string }; + const walletId = parseInt(id); const wallets = useAppStore((store) => store.wallets); const [lightningAddress, setLightningAddress] = React.useState(""); React.useEffect(() => { - setLightningAddress(wallets[selectedWalletId].lightningAddress || ""); - }, [wallets, selectedWalletId]); + setLightningAddress(wallets[walletId].lightningAddress || ""); + }, [wallets, walletId]); const [isLoading, setLoading] = React.useState(false); const updateLightningAddress = async () => { setLoading(true); try { if (lightningAddress) { - const nwcClient = useAppStore.getState().nwcClient; + const nwcClient = useAppStore.getState().getNWCClient(walletId); if (!nwcClient) { throw new Error("NWC client not connected"); } @@ -53,7 +54,7 @@ export function SetLightningAddress() { } } - useAppStore.getState().updateCurrentWallet({ lightningAddress }); + useAppStore.getState().updateWallet({ lightningAddress }, walletId); Toast.show({ type: "success", text1: "Lightning address updated", diff --git a/pages/settings/wallets/RenameWallet.tsx b/pages/settings/wallets/RenameWallet.tsx index 10abd27..663a106 100644 --- a/pages/settings/wallets/RenameWallet.tsx +++ b/pages/settings/wallets/RenameWallet.tsx @@ -1,4 +1,4 @@ -import { router } from "expo-router"; +import { router, useLocalSearchParams } from "expo-router"; import React from "react"; import { View } from "react-native"; import Toast from "react-native-toast-message"; @@ -11,10 +11,11 @@ import { DEFAULT_WALLET_NAME } from "~/lib/constants"; import { useAppStore } from "~/lib/state/appStore"; export function RenameWallet() { - const selectedWalletId = useAppStore((store) => store.selectedWalletId); + const { id } = useLocalSearchParams() as { id: string }; + const walletId = parseInt(id); const wallets = useAppStore((store) => store.wallets); const [walletName, setWalletName] = React.useState( - wallets[selectedWalletId].name || "", + wallets[walletId].name || "", ); return ( @@ -35,9 +36,12 @@ export function RenameWallet() { + + } /> - - - - + ); } From d04975002e703091ebeb01c0b140be37f8c937b2 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Fri, 27 Dec 2024 16:49:11 +0530 Subject: [PATCH 84/97] fix: use replace to return to / --- pages/settings/wallets/SetupWallet.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pages/settings/wallets/SetupWallet.tsx b/pages/settings/wallets/SetupWallet.tsx index b490cd4..4bad391 100644 --- a/pages/settings/wallets/SetupWallet.tsx +++ b/pages/settings/wallets/SetupWallet.tsx @@ -128,6 +128,7 @@ export function SetupWallet() { if (router.canDismiss()) { router.dismissAll(); } + router.replace("/"); }; React.useEffect(() => { @@ -160,6 +161,7 @@ export function SetupWallet() { if (router.canDismiss()) { router.dismissAll(); } + router.replace("/"); }} > From 8eea8ef447dae5d985a96a0bc52dab327995b3f0 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Fri, 27 Dec 2024 20:55:52 +0530 Subject: [PATCH 85/97] chore: update deps to match expo doctor --- package.json | 15 +- yarn.lock | 400 +++++++++++++++++++++++++-------------------------- 2 files changed, 203 insertions(+), 212 deletions(-) diff --git a/package.json b/package.json index 402050f..f0188a3 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "ios": "expo run:ios", "eas:build:ios:preview": "eas build --profile preview --platform ios", "eas:build:android:preview": "eas build --profile preview --platform android", + "build": "eas build --non-interactive --platform android --local --profile preview --output=./app-release.apk", "eas:build:android": "eas build --platform android", "eas:build:ios": "eas build --platform ios", "eas:submit:ios": "eas submit -p ios --latest", @@ -37,15 +38,15 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "dayjs": "^1.11.10", - "expo": "~52.0.14", - "expo-camera": "~16.0.8", + "expo": "~52.0.20", + "expo-camera": "~16.0.10", "expo-clipboard": "~7.0.0", "expo-constants": "~17.0.3", - "expo-font": "~13.0.1", + "expo-font": "~13.0.2", "expo-linear-gradient": "~14.0.1", "expo-linking": "~7.0.3", "expo-local-authentication": "~15.0.1", - "expo-router": "~4.0.11", + "expo-router": "~4.0.15", "expo-secure-store": "~14.0.0", "expo-status-bar": "~2.0.0", "lottie-react-native": "7.1.0", @@ -53,16 +54,16 @@ "nativewind": "^4.0.1", "react": "18.3.1", "react-dom": "18.3.1", - "react-native": "0.76.3", + "react-native": "0.76.5", "react-native-get-random-values": "^1.9.0", "react-native-qrcode-svg": "^6.3.1", "react-native-reanimated": "~3.16.1", "react-native-safe-area-context": "5.0.0", - "react-native-screens": "~4.1.0", + "react-native-screens": "~4.4.0", "react-native-svg": "15.8.0", "react-native-toast-message": "^2.2.0", "react-native-url-polyfill": "^2.0.0", - "react-native-webview": "^13.12.2", + "react-native-webview": "^13.12.5", "swr": "^2.2.5", "tailwind-merge": "^2.3.0", "text-encoding": "^0.7.0", diff --git a/yarn.lock b/yarn.lock index 3ed274e..2dc9598 100644 --- a/yarn.lock +++ b/yarn.lock @@ -876,10 +876,10 @@ dependencies: uuid "^8.0.0" -"@expo/cli@0.22.0": - version "0.22.0" - resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.22.0.tgz#6f6bf7da60e9edfc5968bb3cd7e4907e0c213a41" - integrity sha512-89yOeyA8ObTxxR8izv80foYITXPtsyU0Q+LTalD8PytISRtmWqgbJ2b8go2kPrJJRJ8urUKIIo6k6ShjMurRxw== +"@expo/cli@0.22.7": + version "0.22.7" + resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.22.7.tgz#c6501e6ff5162eacba9d027c0e09c91b8db32cee" + integrity sha512-aNrUPVFPdIX42Q6UM6qygrN4DUqnXMDS1CnkTfNFVIZWRiJ1TUA05Zk6aF35M674CKd/c/dWHFjmbgjsyN/hEA== dependencies: "@0no-co/graphql.web" "^1.0.8" "@babel/runtime" "^7.20.0" @@ -890,15 +890,15 @@ "@expo/env" "~0.4.0" "@expo/image-utils" "^0.6.0" "@expo/json-file" "^9.0.0" - "@expo/metro-config" "~0.19.0" + "@expo/metro-config" "~0.19.8" "@expo/osascript" "^2.0.31" "@expo/package-manager" "^1.5.0" "@expo/plist" "^0.2.0" - "@expo/prebuild-config" "^8.0.17" + "@expo/prebuild-config" "^8.0.23" "@expo/rudder-sdk-node" "^1.1.1" "@expo/spawn-async" "^1.7.2" "@expo/xcpretty" "^4.3.0" - "@react-native/dev-middleware" "0.76.3" + "@react-native/dev-middleware" "0.76.5" "@urql/core" "^5.0.6" "@urql/exchange-retry" "^1.3.0" accepts "^1.3.8" @@ -981,10 +981,10 @@ xcode "^3.0.1" xml2js "0.6.0" -"@expo/config-plugins@~9.0.11": - version "9.0.11" - resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-9.0.11.tgz#7aec3663a783706ded3b6c7f3859221e17c9122a" - integrity sha512-zufuPQWkeEpXfMWFx2lWStoN43p6cO13p8n2KMIEK6jJMC/kkfldYyl8gYtEEYAL1nFfOf/W2pIXXPQ2sggnSw== +"@expo/config-plugins@~9.0.12": + version "9.0.12" + resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-9.0.12.tgz#f122b2dca22e135eadf6e73442da3ced0ce8aa0a" + integrity sha512-/Ko/NM+GzvJyRkq8PITm8ms0KY5v0wmN1OQFYRMkcJqOi3PjlhndW+G6bHpJI9mkQXBaUnHwAiGLqIC3+MQ5Wg== dependencies: "@expo/config-types" "^52.0.0" "@expo/json-file" "~9.0.0" @@ -1006,7 +1006,7 @@ resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-52.0.1.tgz#327af1b72a3a9d4556f41e083e0e284dd8198b96" integrity sha512-vD8ZetyKV7U29lR6+NJohYeoLYTH+eNYXJeNiSOrWCz0witJYY11meMmEnpEaVbN89EfC6uauSUOa6wihtbyPQ== -"@expo/config@~10.0.4", "@expo/config@~10.0.5": +"@expo/config@~10.0.4": version "10.0.5" resolved "https://registry.yarnpkg.com/@expo/config/-/config-10.0.5.tgz#2de75e3f5d46a55f9f5140b73e0913265e6a41c6" integrity sha512-wq48h3HlAPq5v/gMprarAiVY1aEXNBVJ+Em0vrHcYFO8UyxzR6oIao2E4Ed3VWHqhTzPXkMPH4hKCKlzFVBFwQ== @@ -1025,6 +1025,25 @@ slugify "^1.3.4" sucrase "3.35.0" +"@expo/config@~10.0.6": + version "10.0.6" + resolved "https://registry.yarnpkg.com/@expo/config/-/config-10.0.6.tgz#85830491bc8cce2af3f19276922a13f5578d2aa8" + integrity sha512-xXkfPElrtxznkOZxFASJ7OPa6E9IHSjcZwj5BQ6XUF2dz5M7AFa2h5sXM8AalSaDU5tEBSgoUOjTh5957TlR8g== + dependencies: + "@babel/code-frame" "~7.10.4" + "@expo/config-plugins" "~9.0.10" + "@expo/config-types" "^52.0.0" + "@expo/json-file" "^9.0.0" + deepmerge "^4.3.1" + getenv "^1.0.0" + glob "^10.4.2" + require-from-string "^2.0.2" + resolve-from "^5.0.0" + resolve-workspace-root "^2.0.0" + semver "^7.6.0" + slugify "^1.3.4" + sucrase "3.35.0" + "@expo/devcert@^1.1.2": version "1.1.4" resolved "https://registry.yarnpkg.com/@expo/devcert/-/devcert-1.1.4.tgz#d98807802a541847cc42791a606bfdc26e641277" @@ -1054,10 +1073,10 @@ dotenv-expand "~11.0.6" getenv "^1.0.0" -"@expo/fingerprint@0.11.3": - version "0.11.3" - resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.11.3.tgz#e370ae8f83e0642f752b058e2102e984a0a5bc98" - integrity sha512-9lgXmcIePvZ7Wef63XtvuN3HfCUevF4E4tQPdEbH9/dUWwpOvvwQ3KT4OJ9jdh8JJ3nTdO9eDQ/8k8xr1aQ5Kg== +"@expo/fingerprint@0.11.6": + version "0.11.6" + resolved "https://registry.yarnpkg.com/@expo/fingerprint/-/fingerprint-0.11.6.tgz#7e01d436c1610c7dc1fc6898b2d90adaa19a39a0" + integrity sha512-hlVIfMEJYZIqIFMjeGRN5GhK/h6vJ3M4QVc1ZD8F0Bh7gMeI+jZkEyZdL5XT29jergQrksP638e2qFwgrGTw/w== dependencies: "@expo/spawn-async" "^1.7.2" arg "^5.0.2" @@ -1095,34 +1114,10 @@ json5 "^2.2.3" write-file-atomic "^2.3.0" -"@expo/metro-config@0.19.5": - version "0.19.5" - resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.19.5.tgz#121817ac85cfc055686daafb8c85afb4f5d6a2d0" - integrity sha512-wl5lVgXq4FN4kBJHNyU5U9J5hH8S8rYXrp/pgbwA+J/smQfiElYKMYomTGbHUb4LQ0VnmlX6/kI4x/zJk+mq7w== - dependencies: - "@babel/core" "^7.20.0" - "@babel/generator" "^7.20.5" - "@babel/parser" "^7.20.0" - "@babel/types" "^7.20.0" - "@expo/config" "~10.0.4" - "@expo/env" "~0.4.0" - "@expo/json-file" "~9.0.0" - "@expo/spawn-async" "^1.7.2" - chalk "^4.1.0" - debug "^4.3.2" - fs-extra "^9.1.0" - getenv "^1.0.0" - glob "^10.4.2" - jsc-safe-url "^0.2.4" - lightningcss "~1.27.0" - minimatch "^3.0.4" - postcss "~8.4.32" - resolve-from "^5.0.0" - -"@expo/metro-config@~0.19.0": - version "0.19.4" - resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.19.4.tgz#940b6fad7809a92a8ffdb1bbe87aa805f5822c6b" - integrity sha512-2SWwYN8MZvMIRawWEr+1RBYncitPwu2VMACRYig+wBycJ9fsPb6BMVmBYi+3MHDUlJHNy/Bqfw++jn1eqBFETQ== +"@expo/metro-config@0.19.8", "@expo/metro-config@~0.19.8": + version "0.19.8" + resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.19.8.tgz#f1ea552b6fa5217093fe364ff5ca78a7e261a28b" + integrity sha512-dVAOetouQYuOTEJ2zR0OTLNPOH6zPkeEt5fY53TK0Wxi1QmtsmH6vEWg05U4zkSJ6f1aXmQ0Za77R8QxuukESA== dependencies: "@babel/core" "^7.20.0" "@babel/generator" "^7.20.5" @@ -1183,17 +1178,17 @@ base64-js "^1.2.3" xmlbuilder "^14.0.0" -"@expo/prebuild-config@^8.0.17": - version "8.0.20" - resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-8.0.20.tgz#ff135bca966425e22e70141002d150dcaef9b7dc" - integrity sha512-2N2R3qP12Jitmi8V0UG/5s6Se2Fq9RKIdlOTrVA5TzJeHkhCcvQRaRUlojwqjlYh4B3cByLjhTXyWoxM6+wpXQ== +"@expo/prebuild-config@^8.0.23": + version "8.0.23" + resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-8.0.23.tgz#2ec6d5464f35d308bdb94ba75b7e6aba0ebb507d" + integrity sha512-Zf01kFiN2PISmLb0DhIAJh76v3J2oYUKSjiAtGZLOH0HUz59by/qdyU4mGHWdeyRdCCrLUA21Rct2MBykvRMsg== dependencies: "@expo/config" "~10.0.4" "@expo/config-plugins" "~9.0.10" "@expo/config-types" "^52.0.0" "@expo/image-utils" "^0.6.0" "@expo/json-file" "^9.0.0" - "@react-native/normalize-colors" "0.76.3" + "@react-native/normalize-colors" "0.76.5" debug "^4.3.1" fs-extra "^9.0.0" resolve-from "^5.0.0" @@ -1806,22 +1801,22 @@ dependencies: merge-options "^3.0.4" -"@react-native/assets-registry@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.76.3.tgz#c31e91b6f60fed7b0bfc1af617e3a61218d1ec08" - integrity sha512-7Fnc3lzCFFpnoyL1egua6d/qUp0KiIpeSLbfOMln4nI2g2BMzyFHdPjJnpLV2NehmS0omOOkrfRqK5u1F/MXzA== +"@react-native/assets-registry@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.76.5.tgz#3343338813aa6354df9fec52af50d0b5f7f3d013" + integrity sha512-MN5dasWo37MirVcKWuysRkRr4BjNc81SXwUtJYstwbn8oEkfnwR9DaqdDTo/hHOnTdhafffLIa2xOOHcjDIGEw== -"@react-native/babel-plugin-codegen@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.3.tgz#331f1afb4fe744b135979f0d0db62b1cd07cb5bf" - integrity sha512-mZ7jmIIg4bUnxCqY3yTOkoHvvzsDyrZgfnIKiTGm5QACrsIGa5eT3pMFpMm2OpxGXRDrTMsYdPXE2rCyDX52VQ== +"@react-native/babel-plugin-codegen@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.76.5.tgz#a7c32274351e51db9c0a7849ce8059940448c087" + integrity sha512-xe7HSQGop4bnOLMaXt0aU+rIatMNEQbz242SDl8V9vx5oOTI0VbZV9yLy6yBc6poUlYbcboF20YVjoRsxX4yww== dependencies: - "@react-native/codegen" "0.76.3" + "@react-native/codegen" "0.76.5" -"@react-native/babel-preset@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.76.3.tgz#f1a839b0b2ced0399148ada5e1e152136109b940" - integrity sha512-zi2nPlQf9q2fmfPyzwWEj6DU96v8ziWtEfG7CTAX2PG/Vjfsr94vn/wWrCdhBVvLRQ6Kvd/MFAuDYpxmQwIiVQ== +"@react-native/babel-preset@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.76.5.tgz#794ca17e1107e46712153f296a4930de2048e20e" + integrity sha512-1Nu5Um4EogOdppBLI4pfupkteTjWfmI0hqW8ezWTg7Bezw0FtBj8yS8UYVd3wTnDFT9A5mA2VNoNUqomJnvj2A== dependencies: "@babel/core" "^7.25.2" "@babel/plugin-proposal-export-default-from" "^7.24.7" @@ -1864,15 +1859,15 @@ "@babel/plugin-transform-typescript" "^7.25.2" "@babel/plugin-transform-unicode-regex" "^7.24.7" "@babel/template" "^7.25.0" - "@react-native/babel-plugin-codegen" "0.76.3" + "@react-native/babel-plugin-codegen" "0.76.5" babel-plugin-syntax-hermes-parser "^0.25.1" babel-plugin-transform-flow-enums "^0.0.2" react-refresh "^0.14.0" -"@react-native/codegen@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.76.3.tgz#a94664601bb60797dd1042972bffdd1b2bfe008c" - integrity sha512-oJCH/jbYeGmFJql8/y76gqWCCd74pyug41yzYAjREso1Z7xL88JhDyKMvxEnfhSdMOZYVl479N80xFiXPy3ZYA== +"@react-native/codegen@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.76.5.tgz#4d89ec14a023d6946dbc44537c39b03bd006db7b" + integrity sha512-FoZ9VRQ5MpgtDAnVo1rT9nNRfjnWpE40o1GeJSDlpUMttd36bVXvsDm8W/NhX8BKTWXSX+CPQJsRcvN1UPYGKg== dependencies: "@babel/parser" "^7.25.3" glob "^7.1.1" @@ -1883,13 +1878,13 @@ nullthrows "^1.1.1" yargs "^17.6.2" -"@react-native/community-cli-plugin@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.3.tgz#64e1209959103c4ef60a5edb7d7db94329f35ed5" - integrity sha512-vgsLixHS24jR0d0QqPykBWFaC+V8x9cM3cs4oYXw3W199jgBNGP9MWcUJLazD2vzrT/lUTVBVg0rBeB+4XR6fg== +"@react-native/community-cli-plugin@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.76.5.tgz#e701a9f99565504a2510d1b54713b1c5dd0f1bb4" + integrity sha512-3MKMnlU0cZOWlMhz5UG6WqACJiWUrE3XwBEumzbMmZw3Iw3h+fIsn+7kLLE5EhzqLt0hg5Y4cgYFi4kOaNgq+g== dependencies: - "@react-native/dev-middleware" "0.76.3" - "@react-native/metro-babel-transformer" "0.76.3" + "@react-native/dev-middleware" "0.76.5" + "@react-native/metro-babel-transformer" "0.76.5" chalk "^4.0.0" execa "^5.1.1" invariant "^2.2.4" @@ -1900,18 +1895,18 @@ readline "^1.3.0" semver "^7.1.3" -"@react-native/debugger-frontend@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/debugger-frontend/-/debugger-frontend-0.76.3.tgz#531e616f6dad159a58117efc69cec20422d15b0d" - integrity sha512-pMHQ3NpPB28RxXciSvm2yD+uDx3pkhzfuWkc7VFgOduyzPSIr0zotUiOJzsAtrj8++bPbOsAraCeQhCqoOTWQw== +"@react-native/debugger-frontend@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/debugger-frontend/-/debugger-frontend-0.76.5.tgz#0e89940543fb5029506690b83f12547d0bf42cc4" + integrity sha512-5gtsLfBaSoa9WP8ToDb/8NnDBLZjv4sybQQj7rDKytKOdsXm3Pr2y4D7x7GQQtP1ZQRqzU0X0OZrhRz9xNnOqA== -"@react-native/dev-middleware@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/dev-middleware/-/dev-middleware-0.76.3.tgz#52edc76c88e0c2c436eb989551b827bf69f2a56f" - integrity sha512-b+2IpW40z1/S5Jo5JKrWPmucYU/PzeGyGBZZ/SJvmRnBDaP3txb9yIqNZAII1EWsKNhedh8vyRO5PSuJ9Juqzw== +"@react-native/dev-middleware@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/dev-middleware/-/dev-middleware-0.76.5.tgz#10d02fcc6c3c9d24f6dc147c2ef95d6fa6bd3787" + integrity sha512-f8eimsxpkvMgJia7POKoUu9uqjGF6KgkxX4zqr/a6eoR1qdEAWUd6PonSAqtag3PAqvEaJpB99gLH2ZJI1nDGg== dependencies: "@isaacs/ttlcache" "^1.4.1" - "@react-native/debugger-frontend" "0.76.3" + "@react-native/debugger-frontend" "0.76.5" chrome-launcher "^0.15.2" chromium-edge-launcher "^0.2.0" connect "^3.6.5" @@ -1922,92 +1917,92 @@ serve-static "^1.13.1" ws "^6.2.3" -"@react-native/gradle-plugin@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.76.3.tgz#f9cd1c17433811349fff1cab48393b6e9dd7bfe1" - integrity sha512-t0aYZ8ND7+yc+yIm6Yp52bInneYpki6RSIFZ9/LMUzgMKvEB62ptt/7sfho9QkKHCNxE1DJSWIqLIGi/iHHkyg== +"@react-native/gradle-plugin@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.76.5.tgz#90d55ec3a99c609358db97b2d7444b28fdc35bc0" + integrity sha512-7KSyD0g0KhbngITduC8OABn0MAlJfwjIdze7nA4Oe1q3R7qmAv+wQzW+UEXvPah8m1WqFjYTkQwz/4mK3XrQGw== -"@react-native/js-polyfills@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.76.3.tgz#9ac9838b814cf6623797c5118fa4542eb46f410e" - integrity sha512-pubJFArMMrdZiytH+W95KngcSQs+LsxOBsVHkwgMnpBfRUxXPMK4fudtBwWvhnwN76Oe+WhxSq7vOS5XgoPhmw== +"@react-native/js-polyfills@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.76.5.tgz#8f35696d96f804de589cd38382c4f0ffbbc248d5" + integrity sha512-ggM8tcKTcaqyKQcXMIvcB0vVfqr9ZRhWVxWIdiFO1mPvJyS6n+a+lLGkgQAyO8pfH0R1qw6K9D0nqbbDo865WQ== -"@react-native/metro-babel-transformer@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.3.tgz#9accefdb77c509329f7272f4ab0676b11d870257" - integrity sha512-b2zQPXmW7avw/7zewc9nzMULPIAjsTwN03hskhxHUJH5pzUf7pIklB3FrgYPZrRhJgzHiNl3tOPu7vqiKzBYPg== +"@react-native/metro-babel-transformer@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.76.5.tgz#ed5a05fff34c47af43eba4069e50be7c486f77bd" + integrity sha512-Cm9G5Sg5BDty3/MKa3vbCAJtT3YHhlEaPlQALLykju7qBS+pHZV9bE9hocfyyvc5N/osTIGWxG5YOfqTeMu1oQ== dependencies: "@babel/core" "^7.25.2" - "@react-native/babel-preset" "0.76.3" + "@react-native/babel-preset" "0.76.5" hermes-parser "0.23.1" nullthrows "^1.1.1" -"@react-native/normalize-colors@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.76.3.tgz#8d4de4a8671385c53b2d202ef0137632abcf747d" - integrity sha512-Yrpmrh4IDEupUUM/dqVxhAN8QW1VEUR3Qrk2lzJC1jB2s46hDe0hrMP2vs12YJqlzshteOthjwXQlY0TgIzgbg== +"@react-native/normalize-colors@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.76.5.tgz#a33560736311aefcf1d3cb594597befe81a9a53c" + integrity sha512-6QRLEok1r55gLqj+94mEWUENuU5A6wsr2OoXpyq/CgQ7THWowbHtru/kRGRr6o3AQXrVnZheR60JNgFcpNYIug== -"@react-native/virtualized-lists@0.76.3": - version "0.76.3" - resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.76.3.tgz#9865f9e3770c101476564dc2436018f82adfb4b3" - integrity sha512-wTGv9pVh3vAOWb29xFm+J9VRe9dUcUcb9FyaMLT/Hxa88W4wqa5ZMe1V9UvrrBiA1G5DKjv8/1ZcDsJhyugVKA== +"@react-native/virtualized-lists@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.76.5.tgz#394c2d48687db30c79278d183fda8a129a2d24d3" + integrity sha512-M/fW1fTwxrHbcx0OiVOIxzG6rKC0j9cR9Csf80o77y1Xry0yrNPpAlf8D1ev3LvHsiAUiRNFlauoPtodrs2J1A== dependencies: invariant "^2.2.4" nullthrows "^1.1.1" -"@react-navigation/bottom-tabs@^7.0.0": - version "7.0.13" - resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.0.13.tgz#c50e766d4017acab372a755c7d39560c26da3dcc" - integrity sha512-kPTJQoUguVvtVEfXlPd2SsnQtFcvFsAfe2CK5QPyBPTjg7ibmGx7FRYQipmNT1EmL57oBlbHZ3PcDj6rrF+Qfg== +"@react-navigation/bottom-tabs@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.2.0.tgz#5b336b823226647a263b4fe743655462796b6aaf" + integrity sha512-1LxjgnbPyFINyf9Qr5d1YE0pYhuJayg5TCIIFQmbcX4PRhX7FKUXV7cX8OzrKXEdZi/UE/VNXugtozPAR9zgvA== dependencies: - "@react-navigation/elements" "^2.1.7" + "@react-navigation/elements" "^2.2.5" color "^4.2.3" -"@react-navigation/core@^7.1.1": - version "7.1.1" - resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.1.1.tgz#c8c6d2e21f3f7a190e2667898670788627cf3f65" - integrity sha512-i1E1c4xCR4v2Qwry0XtlT3zG9LNjI/Q9YP98hFzK1YDzFmsFjyYCquYU5seV8GakJorQ8vivorGeQeWjlLGW3w== +"@react-navigation/core@^7.3.1": + version "7.3.1" + resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.3.1.tgz#c6d4857fa2dd321d12ca87e200478c38c420f157" + integrity sha512-S3KCGvNsoqVk8ErAtQI2EAhg9185lahF5OY01ofrrD4Ij/uk3QEHHjoGQhR5l5DXSCSKr1JbMQA7MEKMsBiWZA== dependencies: - "@react-navigation/routers" "^7.1.0" + "@react-navigation/routers" "^7.1.2" escape-string-regexp "^4.0.0" - nanoid "3.3.7" + nanoid "3.3.8" query-string "^7.1.3" react-is "^18.2.0" use-latest-callback "^0.2.1" use-sync-external-store "^1.2.2" -"@react-navigation/elements@^2.1.7": - version "2.1.7" - resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.1.7.tgz#7fef2780ef5056d622456dc364958bcd814b58df" - integrity sha512-MFeImIuJrE2D45SNptQitoNUeCmyq8Yt+a7HKvDP4W0g8j9UcF5LCWPae7FjWKbSq8G60o5XTN8MZImaik4vaw== +"@react-navigation/elements@^2.2.5": + version "2.2.5" + resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.2.5.tgz#0e2ca76e2003e96b417a3d7c2829bf1afd69193f" + integrity sha512-sDhE+W14P7MNWLMxXg1MEVXwkLUpMZJGflE6nQNzLmolJQIHgcia0Mrm8uRa3bQovhxYu1UzEojLZ+caoZt7Fg== dependencies: color "^4.2.3" -"@react-navigation/native-stack@^7.0.0": - version "7.1.9" - resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.1.9.tgz#24959d66082fb21419b22bd7ca41878df4182a20" - integrity sha512-hPLuzr1coGpyQT9UkWZeZ/4zxyiaFaTEzAPq51B49FXdqeeUUJ2VXll5uyLTg6fkPV+kOIx8N8hhNTFyntxlMQ== +"@react-navigation/native-stack@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@react-navigation/native-stack/-/native-stack-7.2.0.tgz#8aa489f88d662b3543a931b9cb934bb2e09a4893" + integrity sha512-mw7Nq9qQrGsmJmCTwIIWB7yY/3tWYXvQswx+HJScGAadIjemvytJXm1fcl3+YZ9T9Ym0aERcVe5kDs+ny3X4vA== dependencies: - "@react-navigation/elements" "^2.1.7" + "@react-navigation/elements" "^2.2.5" warn-once "^0.1.1" -"@react-navigation/native@^7.0.0": - version "7.0.8" - resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.0.8.tgz#a194935654185b8b08c6fd7b42e6855618223a63" - integrity sha512-L1FYVCBqR5SWBabQbinfIJdJvv41pk/JG+PHANjxXj+WTWrZ75GKjTjmn5a7be94Dwhl5v8DCyD5T4SOHJq6UA== +"@react-navigation/native@^7.0.14": + version "7.0.14" + resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.0.14.tgz#b3ee2879038dcf0523d26516af88d3adc549ce5e" + integrity sha512-Gi6lLw4VOGSWAhmUdJOMauOKGK51/YA1CprjXm91sNfgERWvznqEMw8QmUQx9SEqYfi0LfZhbzpMst09SJ00lw== dependencies: - "@react-navigation/core" "^7.1.1" + "@react-navigation/core" "^7.3.1" escape-string-regexp "^4.0.0" fast-deep-equal "^3.1.3" - nanoid "3.3.7" + nanoid "3.3.8" use-latest-callback "^0.2.1" -"@react-navigation/routers@^7.1.0": - version "7.1.0" - resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-7.1.0.tgz#a70da606676320250d82d98666c7509b0048db0d" - integrity sha512-ln9/U4vbUKno8l5GcB0dj0QMXFfRDhTfL7WOGJnPEPHAq99PnlyrsDXpqKGOTH9qtLzrfSjR1QQomnQ9i/tGEg== +"@react-navigation/routers@^7.1.2": + version "7.1.2" + resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-7.1.2.tgz#647a63e383673de0c4fc10c64a17f551d5da0a17" + integrity sha512-emdEjpVDK8zbiu2GChC8oYIAub9i/OpNuQJekVsbyFCBz4/TzaBzms38Q53YaNhdIFNmiYLfHv/Y1Ub7KYfm3w== dependencies: - nanoid "3.3.7" + nanoid "3.3.8" "@remix-run/node@^2.12.0": version "2.15.0" @@ -2918,10 +2913,10 @@ babel-preset-current-node-syntax@^1.0.0: "@babel/plugin-syntax-private-property-in-object" "^7.14.5" "@babel/plugin-syntax-top-level-await" "^7.14.5" -babel-preset-expo@~12.0.3: - version "12.0.3" - resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-12.0.3.tgz#2ad62fe007517704841788cfea38b333e307663f" - integrity sha512-1695e8y3U/HjifKx33vcNnFMSUSXwPWwhFxRlL6NRx2TENN6gySH82gPOWgxcra6gi+EJgXx52xG3PcqTjwW6w== +babel-preset-expo@~12.0.4: + version "12.0.4" + resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-12.0.4.tgz#ec965530d866c8905aac1fa478562cb08ab32a55" + integrity sha512-SAzAwqpyjA+/OFrU95OOioj6oTeCv4+rRfrNmBTy5S/gJswrZKBSPJioFudIaJBy43W+BL7HA5AspBIF6tO/aA== dependencies: "@babel/plugin-proposal-decorators" "^7.12.9" "@babel/plugin-transform-export-namespace-from" "^7.22.11" @@ -2929,7 +2924,7 @@ babel-preset-expo@~12.0.3: "@babel/plugin-transform-parameters" "^7.22.15" "@babel/preset-react" "^7.22.15" "@babel/preset-typescript" "^7.23.0" - "@react-native/babel-preset" "0.76.3" + "@react-native/babel-preset" "0.76.5" babel-plugin-react-native-web "~0.19.13" react-refresh "^0.14.2" @@ -4464,10 +4459,10 @@ expo-asset@~11.0.1: invariant "^2.2.4" md5-file "^3.2.3" -expo-camera@~16.0.8: - version "16.0.8" - resolved "https://registry.yarnpkg.com/expo-camera/-/expo-camera-16.0.8.tgz#bdef407c70525e2373cdaf9e45dbe0c8dc92f54e" - integrity sha512-QxuHsJot1M/0vCr9wV41OeEdHYp33VkvtmWv7XV2rXL/m8PJBm3m9o1ls5iL5s3hXlAC3tBCsfnt2m7ohfNdWQ== +expo-camera@~16.0.10: + version "16.0.10" + resolved "https://registry.yarnpkg.com/expo-camera/-/expo-camera-16.0.10.tgz#d3ee17f18984d33425046c83a78e06a533654ddb" + integrity sha512-CKvC9dhBuR7VkuwXsBya+Ok2h97z9t7EefkP5sZNdPXZYab8K/zxcisGJZG6/mjCVqzHIftvCb3GxJtuRrDIBQ== dependencies: invariant "^2.2.4" @@ -4484,17 +4479,17 @@ expo-constants@~17.0.0, expo-constants@~17.0.3: "@expo/config" "~10.0.4" "@expo/env" "~0.4.0" -expo-file-system@~18.0.4: - version "18.0.4" - resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-18.0.4.tgz#eecf8dc0b3b545e9ac5cd00352665afe2d57732f" - integrity sha512-aAWEDwnu0XHOBYvQ9Q0+QIa+483vYJaC4IDsXyWQ73Rtsg273NZh5kYowY+cAocvoSmA99G6htrLBn11ax2bTQ== +expo-file-system@~18.0.6: + version "18.0.6" + resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-18.0.6.tgz#43f7718530d0e2aa1f49bca7ccb721007acabf2c" + integrity sha512-gGEwIJCXV3/wpIJ/wRyhmieLOSAY7HeFFjb+wEfHs04aE63JYR+rXXV4b7rBpEh1ZgNV9U91zfet/iQG7J8HBQ== dependencies: web-streams-polyfill "^3.3.2" -expo-font@~13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-13.0.1.tgz#3a7eed7a4238a352fc74a425fd8c020c5f122382" - integrity sha512-8JE47B+6cLeKWr5ql8gU6YsPHjhrz1vMrTqYMm72No/8iW8Sb/uL4Oc0dpmbjq3hLLXBY0xPBQOgU7FQ6Y04Vg== +expo-font@~13.0.2: + version "13.0.2" + resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-13.0.2.tgz#efd3e91714c040a0cf38db91920bce2e0331ff6e" + integrity sha512-H9FaXM7ZW5+EfV38w80BgJG3H17kB7CuVXwHoiszIYyoPfWz9bWesFe4QwNZjTq3pzKes28sSd8irFuflIrSIA== dependencies: fontfaceobserver "^2.1.0" @@ -4523,10 +4518,10 @@ expo-local-authentication@~15.0.1: dependencies: invariant "^2.2.4" -expo-modules-autolinking@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-2.0.3.tgz#c0de0129bedf1b6f9aa36093e435d00509f27fcd" - integrity sha512-Q/ALJ54eS7Cr7cmbP+unEDTkHFQivQerWWrqZxuXOrSFYGCYU22+/xAZXaJOpZwseOVsP74zSkoRY/wBimVs7w== +expo-modules-autolinking@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-2.0.4.tgz#28fcd12fb0d066a2933cca3bf3b597da0f6b2f2a" + integrity sha512-e0p+19NhmD50U7s7BV7kWIypWmTNC9n/VlJKlXS05hM/zX7pe6JKmXyb+BFnXJq3SLBalLCUY0tu2gEUF3XeVg== dependencies: "@expo/spawn-async" "^1.7.2" chalk "^4.1.0" @@ -4537,24 +4532,24 @@ expo-modules-autolinking@2.0.3: require-from-string "^2.0.2" resolve-from "^5.0.0" -expo-modules-core@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-2.1.1.tgz#970af4cfd70c8aa6fc0096dd0a6578aa003a479f" - integrity sha512-yQzYCLR2mre4BNMXuqkeJ0oSNgmGEMI6BcmIzeNZbC2NFEjiaDpKvlV9bclYCtyVhUEVNbJcEPYMr6c1Y4eR4w== +expo-modules-core@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-2.1.2.tgz#258be4fbd162b69eb4ad2789131ac2dc7e85fc08" + integrity sha512-0OhMU5S8zf9c/CRh1MwiXfOInI9wzz6yiIh5RuR/9J7N6xHRum68hInsPbaSc1UQpo08ZZLM4MPsbpoNRUoqIg== dependencies: invariant "^2.2.4" -expo-router@~4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-4.0.11.tgz#324364534fc7837a99a7894c611b3cd75e4470c5" - integrity sha512-2Qrd/fk98kC+CTg1umbuUaBaGkpdGStPpkSR99SoAjX6KWC1WhNMCv0hGFn7cRmSNOWQzgIfLGLERhRY1o4myw== +expo-router@~4.0.15: + version "4.0.15" + resolved "https://registry.yarnpkg.com/expo-router/-/expo-router-4.0.15.tgz#bdc00b90bd60ab5ccb35ae51f31dcbc96c179949" + integrity sha512-5MDy7iVzgi8lheRunsR4lTKEKTNqukC3uYSWhY370Nakdd+E/Woz+Vw1M67/KrnvefTV5hF97bNUUMzY+fyojw== dependencies: "@expo/metro-runtime" "4.0.0" "@expo/server" "^0.5.0" "@radix-ui/react-slot" "1.0.1" - "@react-navigation/bottom-tabs" "^7.0.0" - "@react-navigation/native" "^7.0.0" - "@react-navigation/native-stack" "^7.0.0" + "@react-navigation/bottom-tabs" "^7.2.0" + "@react-navigation/native" "^7.0.14" + "@react-navigation/native-stack" "^7.2.0" client-only "^0.0.1" react-helmet-async "^1.3.0" react-native-helmet-async "2.0.4" @@ -4573,26 +4568,26 @@ expo-status-bar@~2.0.0: resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-2.0.0.tgz#dd99adc2ace12a24c92718cd0f97b93347103393" integrity sha512-vxxdpvpNDMTEc5uTiIrbTvySKKUsOACmfl8OZuUdjNle05oGqwtq3v5YObwym/njSByjoyuZX8UpXBZnxvarwQ== -expo@~52.0.14: - version "52.0.14" - resolved "https://registry.yarnpkg.com/expo/-/expo-52.0.14.tgz#e99a921dc5caff4b5b68fa96069ba7c3e64c4d17" - integrity sha512-omGLh6cmaFZPcvTuMZY9Pi0aBOseeJI7MjJAAbi2rFssNVJJme44jM0CJ4Hq3p/6XqSFlZMqnuAfsAAzm/c/Rg== +expo@~52.0.20: + version "52.0.23" + resolved "https://registry.yarnpkg.com/expo/-/expo-52.0.23.tgz#68b79da2206afdce9c68f45f519f812bab9a2cfe" + integrity sha512-DR36Vkpz/ZLPci4fxDBG/pLk26nGK63vcZ+X4RZJfNBzi14DXZ939loP8YzWGV78Qp23qdPINczpo2727tqLxg== dependencies: "@babel/runtime" "^7.20.0" - "@expo/cli" "0.22.0" - "@expo/config" "~10.0.5" - "@expo/config-plugins" "~9.0.11" - "@expo/fingerprint" "0.11.3" - "@expo/metro-config" "0.19.5" + "@expo/cli" "0.22.7" + "@expo/config" "~10.0.6" + "@expo/config-plugins" "~9.0.12" + "@expo/fingerprint" "0.11.6" + "@expo/metro-config" "0.19.8" "@expo/vector-icons" "^14.0.0" - babel-preset-expo "~12.0.3" + babel-preset-expo "~12.0.4" expo-asset "~11.0.1" expo-constants "~17.0.3" - expo-file-system "~18.0.4" - expo-font "~13.0.1" + expo-file-system "~18.0.6" + expo-font "~13.0.2" expo-keep-awake "~14.0.1" - expo-modules-autolinking "2.0.3" - expo-modules-core "2.1.1" + expo-modules-autolinking "2.0.4" + expo-modules-core "2.1.2" fbemitter "^3.0.0" web-streams-polyfill "^3.3.2" whatwg-url-without-unicode "8.0.0-3" @@ -7020,12 +7015,7 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" -nanoid@3.3.7: - version "3.3.7" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" - integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== - -nanoid@^3.3.7: +nanoid@3.3.8, nanoid@^3.3.7: version "3.3.8" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== @@ -7908,10 +7898,10 @@ react-native-safe-area-context@5.0.0: resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.0.0.tgz#0f28f3b406d4466c6afdaaa615198d12741e88b5" integrity sha512-4K4TvEbRsTDYuSSJZfMNKuJNn1+qgrSkOBwRoreiHcuqy1egrHpkhPhoN1Zg1+b3BxcVXlKXtMIf4eVaG/DPJw== -react-native-screens@~4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.1.0.tgz#3f703a1bb4fd65fd7963e616ddc373a97809c254" - integrity sha512-tCBwe7fRMpoi/nIgZxE86N8b2SH8d5PlfGaQO8lgqlXqIyvwqm3u1HJCaA0tsacPyzhW7vVtRfQyq9e1j0S2gA== +react-native-screens@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.4.0.tgz#3fcbcdf1bbb1be2736b10d43edc3d4e69c37b5aa" + integrity sha512-c7zc7Zwjty6/pGyuuvh9gK3YBYqHPOxrhXfG1lF4gHlojQSmIx2piNbNaV+Uykj+RDTmFXK0e/hA+fucw/Qozg== dependencies: react-freeze "^1.0.0" warn-once "^0.1.0" @@ -7937,27 +7927,27 @@ react-native-url-polyfill@^2.0.0: dependencies: whatwg-url-without-unicode "8.0.0-3" -react-native-webview@^13.12.2: - version "13.12.4" - resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.12.4.tgz#1a563a7fbd6bf53d688388d46708f273ed0ebb94" - integrity sha512-8lWeYPVWeOj0ya9ZpDesOQPRgczuN3ogQHlhS21sNXndd4kvfPG+WjlRdrvxYgj//udpwmzcWzagwLnEp60Aqg== +react-native-webview@^13.12.5: + version "13.12.5" + resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.12.5.tgz#ed9eec1eda234d7cf18d329859b9bdebf7e258b6" + integrity sha512-INOKPom4dFyzkbxbkuQNfeRG9/iYnyRDzrDkJeyvSWgJAW2IDdJkWFJBS2v0RxIL4gqLgHkiIZDOfiLaNnw83Q== dependencies: escape-string-regexp "^4.0.0" invariant "2.2.4" -react-native@0.76.3: - version "0.76.3" - resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.76.3.tgz#18b79949c58932e9fd8d04b205e5c0a46bc46d8f" - integrity sha512-0TUhgmlouRNf6yuDIIAdbQl0g1VsONgCMsLs7Et64hjj5VLMCA7np+4dMrZvGZ3wRNqzgeyT9oWJsUm49AcwSQ== +react-native@0.76.5: + version "0.76.5" + resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.76.5.tgz#3ce43d3c31f46cfd98e56ef2dfc70866c04ad185" + integrity sha512-op2p2kB+lqMF1D7AdX4+wvaR0OPFbvWYs+VBE7bwsb99Cn9xISrLRLAgFflZedQsa5HvnOGrULhtnmItbIKVVw== dependencies: "@jest/create-cache-key-function" "^29.6.3" - "@react-native/assets-registry" "0.76.3" - "@react-native/codegen" "0.76.3" - "@react-native/community-cli-plugin" "0.76.3" - "@react-native/gradle-plugin" "0.76.3" - "@react-native/js-polyfills" "0.76.3" - "@react-native/normalize-colors" "0.76.3" - "@react-native/virtualized-lists" "0.76.3" + "@react-native/assets-registry" "0.76.5" + "@react-native/codegen" "0.76.5" + "@react-native/community-cli-plugin" "0.76.5" + "@react-native/gradle-plugin" "0.76.5" + "@react-native/js-polyfills" "0.76.5" + "@react-native/normalize-colors" "0.76.5" + "@react-native/virtualized-lists" "0.76.5" abort-controller "^3.0.0" anser "^1.4.9" ansi-regex "^5.0.0" From 0ca86fad3dd48143423855de4a49527fe5cdb654 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Fri, 27 Dec 2024 20:57:19 +0530 Subject: [PATCH 86/97] fix: align wallet name and balance --- pages/Home.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pages/Home.tsx b/pages/Home.tsx index ea926ca..7e260d8 100644 --- a/pages/Home.tsx +++ b/pages/Home.tsx @@ -91,6 +91,7 @@ export function Home() { > {wallets.length > 1 && ( { router.push("/settings/wallets"); }} @@ -98,7 +99,7 @@ export function Home() { {wallets[selectedWalletId].name || DEFAULT_WALLET_NAME} @@ -117,9 +118,11 @@ export function Home() { getFiatAmount(Math.floor(balance.balance / 1000))} {balanceDisplayMode === "hidden" && "****"} - - {balanceDisplayMode === "sats" && "sats"} - + {balanceDisplayMode === "sats" && ( + + sats + + )} ) : ( From 117358af4ddea39e71efd27c1b00d2db04845641 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 30 Dec 2024 13:46:20 +0530 Subject: [PATCH 87/97] fix: get theme from device --- app.json | 6 ++++-- app/_layout.tsx | 8 ++++++-- lib/state/appStore.ts | 6 +++--- package.json | 1 + yarn.lock | 13 +++++++++++++ 5 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app.json b/app.json index 5d8fd3b..8fa1a88 100644 --- a/app.json +++ b/app.json @@ -50,7 +50,8 @@ }, "infoPlist": { "LSMinimumSystemVersion": "12.0" - } + }, + "userInterfaceStyle": "automatic" }, "android": { "package": "com.getalby.mobile", @@ -60,7 +61,8 @@ "backgroundImage": "./assets/adaptive-icon-bg.png", "monochromeImage": "./assets/monochromatic.png" }, - "permissions": ["android.permission.CAMERA"] + "permissions": ["android.permission.CAMERA"], + "userInterfaceStyle": "automatic" }, "extra": { "eas": { diff --git a/app/_layout.tsx b/app/_layout.tsx index e34d644..2b10fed 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -69,10 +69,14 @@ export default function RootLayout() { const loadTheme = React.useCallback((): Promise => { return new Promise((resolve) => { const theme = useAppStore.getState().theme; - setColorScheme(theme); + if (theme) { + setColorScheme(theme); + } else { + useAppStore.getState().setTheme(isDarkColorScheme ? "dark" : "light"); + } resolve(); }); - }, [setColorScheme]); + }, [isDarkColorScheme, setColorScheme]); React.useEffect(() => { const init = async () => { diff --git a/lib/state/appStore.ts b/lib/state/appStore.ts index ce227e0..1fee427 100644 --- a/lib/state/appStore.ts +++ b/lib/state/appStore.ts @@ -12,7 +12,7 @@ interface AppState { readonly addressBookEntries: AddressBookEntry[]; readonly isSecurityEnabled: boolean; readonly isOnboarded: boolean; - readonly theme: Theme; + readonly theme?: Theme; readonly balanceDisplayMode: BalanceDisplayMode; setUnlocked: (unlocked: boolean) => void; setTheme: (theme: Theme) => void; @@ -45,7 +45,7 @@ const isSecurityEnabledKey = "isSecurityEnabled"; export const lastActiveTimeKey = "lastActiveTime"; export type BalanceDisplayMode = "sats" | "fiat" | "hidden"; -export type Theme = "system" | "light" | "dark"; +export type Theme = "light" | "dark"; type Wallet = { name?: string; @@ -176,7 +176,7 @@ export const useAppStore = create()((set, get) => { const isSecurityEnabled = secureStorage.getItem(isSecurityEnabledKey) === "true"; - const theme = (secureStorage.getItem(themeKey) as Theme) || "system"; + const theme = (secureStorage.getItem(themeKey) as Theme) || null; const balanceDisplayMode = (secureStorage.getItem(balanceDisplayModeKey) as BalanceDisplayMode) || "sats"; diff --git a/package.json b/package.json index 402050f..fa0ff6c 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "expo-router": "~4.0.11", "expo-secure-store": "~14.0.0", "expo-status-bar": "~2.0.0", + "expo-system-ui": "~4.0.6", "lottie-react-native": "7.1.0", "message-port-polyfill": "^0.2.0", "nativewind": "^4.0.1", diff --git a/yarn.lock b/yarn.lock index 3ed274e..e962198 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1947,6 +1947,11 @@ resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.76.3.tgz#8d4de4a8671385c53b2d202ef0137632abcf747d" integrity sha512-Yrpmrh4IDEupUUM/dqVxhAN8QW1VEUR3Qrk2lzJC1jB2s46hDe0hrMP2vs12YJqlzshteOthjwXQlY0TgIzgbg== +"@react-native/normalize-colors@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.76.5.tgz#a33560736311aefcf1d3cb594597befe81a9a53c" + integrity sha512-6QRLEok1r55gLqj+94mEWUENuU5A6wsr2OoXpyq/CgQ7THWowbHtru/kRGRr6o3AQXrVnZheR60JNgFcpNYIug== + "@react-native/virtualized-lists@0.76.3": version "0.76.3" resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.76.3.tgz#9865f9e3770c101476564dc2436018f82adfb4b3" @@ -4573,6 +4578,14 @@ expo-status-bar@~2.0.0: resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-2.0.0.tgz#dd99adc2ace12a24c92718cd0f97b93347103393" integrity sha512-vxxdpvpNDMTEc5uTiIrbTvySKKUsOACmfl8OZuUdjNle05oGqwtq3v5YObwym/njSByjoyuZX8UpXBZnxvarwQ== +expo-system-ui@~4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/expo-system-ui/-/expo-system-ui-4.0.6.tgz#23d172e346dbe478f31e36019c2570242e7c0762" + integrity sha512-JWmw0aaNIB8YxA6bXgH6nClyledZaAG5VNzoRvmXT4+j3MY4waAHSSSdVV71bUgjchT/2KOAcibZ/EeosJONug== + dependencies: + "@react-native/normalize-colors" "0.76.5" + debug "^4.3.2" + expo@~52.0.14: version "52.0.14" resolved "https://registry.yarnpkg.com/expo/-/expo-52.0.14.tgz#e99a921dc5caff4b5b68fa96069ba7c3e64c4d17" From 2dd48822f19d54b26088fdf0fc8570cc3fb934b0 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 30 Dec 2024 16:05:54 +0530 Subject: [PATCH 88/97] fix: use splash screen config plugin --- app.json | 13 ++++++++----- app/_layout.tsx | 5 +++-- package.json | 1 + yarn.lock | 29 +++++++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/app.json b/app.json index 5d8fd3b..80a816b 100644 --- a/app.json +++ b/app.json @@ -7,14 +7,17 @@ "orientation": "portrait", "icon": "./assets/icon.png", "userInterfaceStyle": "automatic", - "splash": { - "image": "./assets/splash.png", - "resizeMode": "cover", - "backgroundColor": "#0B0930" - }, "newArchEnabled": true, "assetBundlePatterns": ["**/*"], "plugins": [ + [ + "expo-splash-screen", + { + "backgroundColor": "#0B0930", + "image": "./assets/icon.png", + "imageWidth": "150" + } + ], [ "expo-local-authentication", { diff --git a/app/_layout.tsx b/app/_layout.tsx index e34d644..528fceb 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -6,7 +6,8 @@ import { } from "@react-navigation/native"; import { PortalHost } from "@rn-primitives/portal"; import * as Font from "expo-font"; -import { Slot, SplashScreen } from "expo-router"; +import { Slot } from "expo-router"; +import * as SplashScreen from "expo-splash-screen"; import { StatusBar } from "expo-status-bar"; import { swrConfiguration } from "lib/swr"; import * as React from "react"; @@ -80,7 +81,7 @@ export default function RootLayout() { await Promise.all([loadTheme(), loadFonts(), checkBiometricStatus()]); } finally { setResourcesLoaded(true); - SplashScreen.hideAsync(); + SplashScreen.hide(); } }; diff --git a/package.json b/package.json index 402050f..207330e 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "expo-local-authentication": "~15.0.1", "expo-router": "~4.0.11", "expo-secure-store": "~14.0.0", + "expo-splash-screen": "^0.29.18", "expo-status-bar": "~2.0.0", "lottie-react-native": "7.1.0", "message-port-polyfill": "^0.2.0", diff --git a/yarn.lock b/yarn.lock index 3ed274e..072f278 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1200,6 +1200,23 @@ semver "^7.6.0" xml2js "0.6.0" +"@expo/prebuild-config@^8.0.23": + version "8.0.23" + resolved "https://registry.yarnpkg.com/@expo/prebuild-config/-/prebuild-config-8.0.23.tgz#2ec6d5464f35d308bdb94ba75b7e6aba0ebb507d" + integrity sha512-Zf01kFiN2PISmLb0DhIAJh76v3J2oYUKSjiAtGZLOH0HUz59by/qdyU4mGHWdeyRdCCrLUA21Rct2MBykvRMsg== + dependencies: + "@expo/config" "~10.0.4" + "@expo/config-plugins" "~9.0.10" + "@expo/config-types" "^52.0.0" + "@expo/image-utils" "^0.6.0" + "@expo/json-file" "^9.0.0" + "@react-native/normalize-colors" "0.76.5" + debug "^4.3.1" + fs-extra "^9.0.0" + resolve-from "^5.0.0" + semver "^7.6.0" + xml2js "0.6.0" + "@expo/rudder-sdk-node@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@expo/rudder-sdk-node/-/rudder-sdk-node-1.1.1.tgz#6aa575f346833eb6290282118766d4919c808c6a" @@ -1947,6 +1964,11 @@ resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.76.3.tgz#8d4de4a8671385c53b2d202ef0137632abcf747d" integrity sha512-Yrpmrh4IDEupUUM/dqVxhAN8QW1VEUR3Qrk2lzJC1jB2s46hDe0hrMP2vs12YJqlzshteOthjwXQlY0TgIzgbg== +"@react-native/normalize-colors@0.76.5": + version "0.76.5" + resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.76.5.tgz#a33560736311aefcf1d3cb594597befe81a9a53c" + integrity sha512-6QRLEok1r55gLqj+94mEWUENuU5A6wsr2OoXpyq/CgQ7THWowbHtru/kRGRr6o3AQXrVnZheR60JNgFcpNYIug== + "@react-native/virtualized-lists@0.76.3": version "0.76.3" resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.76.3.tgz#9865f9e3770c101476564dc2436018f82adfb4b3" @@ -4568,6 +4590,13 @@ expo-secure-store@~14.0.0: resolved "https://registry.yarnpkg.com/expo-secure-store/-/expo-secure-store-14.0.0.tgz#cf6eb7f73e619f8907d5a073e2f438927b5fc2ab" integrity sha512-VyhtRFXP+7hQmHhKlHIOWid1Q/IRpM7Uif32tZHLZHvQ6FNz2cUkr26XWGvCa7btYbrR6OL++FBFZYjbIcRZTw== +expo-splash-screen@^0.29.18: + version "0.29.18" + resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.29.18.tgz#96ccce3d5a03389a9061743903b0a77c22a16796" + integrity sha512-bTBY+LF6YtYen2j60yGNh2SX/tG4UXZAyBCMMriOSiZZ7LSCs3ARyEufaSiWk+ckWShTeMqItOnaAN/CAF8MJA== + dependencies: + "@expo/prebuild-config" "^8.0.23" + expo-status-bar@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-2.0.0.tgz#dd99adc2ace12a24c92718cd0f97b93347103393" From 951f7d42371ab68467d00fc5c457170660dea32b Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 30 Dec 2024 19:20:44 +0530 Subject: [PATCH 89/97] chore: remove splash png --- assets/splash.png | Bin 15004 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/splash.png diff --git a/assets/splash.png b/assets/splash.png deleted file mode 100644 index ae04ebf2613732fe8dc3327d554d31269fe8abe3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15004 zcmeI3Wl$Vl^XLb6f(H+aySqbx;O_1OCpaN!u;A|Q?kpZ81ef6M9yGXfH*ejK|9U?> zx9VSYtDgR_H9O}_pYC6GpEEmW=bN(P2Q*|tWB>rrWTYij0RRpb0FajuVIXJB1UKFR z0JNd9yt*Xh>+AnlLh+95uf6Mzlw_cnmlrxpavBQChld9mau6d81wA#*$;8K}r>FM< z40nrmcXxNJj7$&9_J5{rxLIh}7^t3B95`55Ihd$6H#P)$`Sg`JU)CJ1=4@|nZjO$Q zxY#*VrCF;}Rn(M~dAYb(mX{}g{W?E8o1K{v5foygqyIf>CBj3`LPwdKoikLcrzj`S z$4+BnXeccyDJd?lDbK$C!)&?Rbf(oPDNyFmpFbxjC(e$JIT7;GB21&Bqk{tj2D*CU zA|mqQ%(c}ur6ncv9mc=FhPql>A#U%LBv}p*4*Guls4TAt4-Ip7aor!b{8^-%AG zVIeg+<$2ZdqlKlpiRmXZ0c(9eK`z>;h=|=mi|u}MIcXUa4era!%e~#*qJqMfJPkV| z{`lCq*y!kp&ys=8;-SI8UjqUhZ0*Jx42t8G(nDqaoy4}cwmUmIn!#WnZ*MOT4=puy z3mu-9HAmZz*1hFA^_gk~F$!y|tKFsAO*!grmO?$<-HioL@nZZ(&xM~o>_66` zp<(joKd=D*CnF-U{|7w_UtysB+C%m_FQxp?_2B-C=fA=G7tepAqwz1EfARcR0{^w~ z-|YF{!PDvO7fu(s&FeePJXEwIzUwQOD3)+^c?>paa)?7rdZb4}6GxS$NcuG>)Jl{O zGU(4@!+*v5@kYpMc?^%koBM2jV*lwOYvT4k`^@D(YvBLz;qN!h?oYT?%TlYT*^Jk$O*xVNGkM*?D#4;8uL*|#P_S26fS!mQmQeGa>^v_oyF3hVS+6)>lz}!j zAjv{S%B&FYuvaI8(A@|hD$PR$Cvcp9t9taEB|)n03kPC%A^F!;wT-f|Efy+pc8Zi5 z1sA}Yvzdoy9eY(-yNvTuX=q~b?|rD!;Y}Tv2HKQp%rww#R~+f_E;LgS$B;6owC2FG zv%T^zePp*yS6_22+522gG31>5D47#6y9~;|wXaX^YkX+7T&~%C(TZuwSVBs5f4FPt zdEr4BC*my7EpKXCBxap=PFAohlQun=LMc010{OK<3A7(H)cW1Jj8N zsgkj`<1E(|K?2@xU)7mRlJNbl`+}rrZ>rgT`Q{6a?KNC;#Y_jcckUmCL8Zp)p$oR^ zw|vs4_p?4q!+m#4)85>dBv)iA>W2o%ciRjGgqFLv-S#y7d}}Y%{4&em&br|Bp`7|7 zu;KT-C-<{|yO(xXFRCgf(&*Ud!Yqq?c;JQmi*8^ZJmGXk!EX11i2L_NYilm9 z`RcirDNX(;r4p_p#H8l{UAWL%F@)~3{6f&N&!Q+XD;irg_uTC~<^<=!1HM?r#E5I7*x?#ALT`fK(1LzxwF_I;2J=Do(^&JK{v6#XDCkE5@&e0F38P7hO z+C*G=S7bBg)pk>XDL2GXffF+)53qR$8Fa5~*;mlfkBLEgd|5LT()$9NTvCXp{6W+x_azF-H&!HK6jDq+@Z5=lA^Pao!3=-I1) zmwbC)G%9D8mJbDZmbo~B1@mg3ZwNgn$=atlh$V-)Marx%>R1V0qX7Ss&v)7ji3-U^ zBd$Xi3GjYb$`jKil1n5>1CsMpXpID1TZ5 zQEwSEVJ**JrkFy)Iaplmzs;K|FU#Ce6bcV)B>AEcyI}hs)_cpCtKsjo7{=%6PayUpOm+#HS!O~wwqriz>_G##V- zUsKpD$xz0IDD~$pVg{KL`{_>!3k&0!F1Y|eiP1E#G9+lrr)}p0+hXOL`gAN+RqgIx z01fEIrqH>wj6K&&snk|*2Wieo$VS>TUi~hG7;oGdqhPHiO#=Mg~5iBi%S>} z-Wp<{_F(TLITq>f_zWHMeI*mg9GOXKykbbp=@DmA@EIlL$t+dZP!EG9F1cimj~yZZ zdlWYnJQL$I)>YPP~}LTzn`OD%SR?;Uv!93qb8s6I#uF)T_}VVHtcAhzjv zKnm>5tcbMAOntZ+j*O6U#aq=IsBVj_&vkT63Gz}|%sTH?5)(gtxo({y91`QEB9nju z^ygB7$0!R&${t4Iae8wL%2|r$%KFM7xi@TwM_&;7QA0)jNXdn`#I`hvFpd%iFg)DG z2zb>`5iB{N-%{_9!=+SvqjFFUvCM`!W9mYtbegJ$F64>qLy8BtXchRozXA*-emriZ z1H}TZ=IqHMYJoho$-NxH(fP;3O#yx!C}x}h@OBKpxA?P2+LYVS*ep&$*C!GIJ|Di6 zr9y(VKwu+^8Oe?xyOX7!<=Ymb;JC>*M>RM9r2{CG2#D&pW9|zG9x5qzJO@TnmX`HT zv|Ze(G}S)|j)vc*s4iz+)>$V)k~RM)qk6)*WXei{V^@x|^YA+RSF!l z`^~Kjv21K($gWMUS4C*9_6i+b$Q7p-T}PK!0`V@rZu|I%o9CeZvSKLS>s#;zxw9No zI2quXH?8cAsb4UXyqa69oZTWiAL~z!2>30wpOsJt(xPvK!u>R?yIpfT{@$E|Au1Z~ zABH0dE1@om!+?iVM(=9c-VtA3T5t+hQ!lzn^j_R^O9aogj@eSP`XZwjRc zO+58`wcn-StEufM6+#dh@RaiK`wO}jGEdPChPLC4&t3zm8hcrEo?lq43yN;Yj}$nsX&60{~9B3Gg&O!kc5xs}y0F_6dSXaEYlM zvYbL;odcG`Rz6*9{ge)eArx?~`?#6a795FekKN}k*7^Pe+9?;AXM9QB4?VYc8c8>5 zWK{wK={=Bvw3gr|P!7lX+eR~4or0E&oyg_1q;;8B?2$_VejBqfR5+l!K_;vC%4~xF z=8WrVo$RWYitZ01oHCP~87^e%Dvq}PVTh70Skpj(5bEaouJvB`K$}P`Ky7>q zfLyOWvC(SZ;uY8yjdq&%#73UT#c-VwT0|j7XFS86+{OR^mJdO-ER4_X&qFDP1=JOpmv+B3^)5DU- zJt*<{DM$tgKj<*n!L`NPoN$2XM9bnaIeq6J69-a)p%JDpTFIn8`x#{lkytR>NY{%* zOT$bRUb7*@qpho-LE!o}$_t6H(w2Dp%&)f%m4Ray6RUF8C<`cMJa*3Vv1$=ag;;t#G>f6EEC>Z!;T_Th&Fs=fb!lMu^ zLLW?R;uhL?RO~#2BMY^97e>r43Y?S0mmo1^{v1;KUXi(VWLcx``e8FLJw5#*(1XW8 z6NjqIobgyDEvm?kl1TxlKe>jc3u!qrn-5@}{6^y5LW)o>qYzp#QKGZ(jf$!B#r~u& zQF?o1%b7qhMF!cAUOsc2#?Na|)p67S0pJl5Uv!YElS|h~)Y4h#Zp{>eEXcWcpJ&%` z6Qs)p$>b}2C0^oE#-`*Ud7^;;*3%FhzlgTbThLd(dCZrd!u8%IeRWPau14ZuE7H6O z&4`q>23WZ}8J2~}td7bTi6WfQ-&PdT6gVKcG;KF?DFvrd7-5%Q2hTRlwAVli+!Sh= zODg`o-5B}J((n>_l51)3tRC_ZbDl(Mc$a0-T&=gj;w;x}sM8_f^eLLOJi!MUmYJs{Mn#bUsp&(iBr-n_S5#Ht=;#ULeDP=m1w7|u*VGtTsF6X2%yT4- zLwUTIus{PvPa}D}O)IYY2_3oF=73y~)i)wX7Brd~MqeLRsizC}0OGmDh3c;m-j<#p z<~k55qI7>&_1+w6r^=v`+F-1Oz20=P2g;XCdUP>icNO_Mz1U9Tfr%n5F#*;ptS@p? z{0>aX#|88R!+xYR9az)n7JkJ^S5*4C-`Na5o|f-mw0^q?psS`_H<(cEglbXN!xa#($CECxhc>2CHl= z?2!pnlnm{qamO&4?KXdh#K%t~XtL!UE_8 zI^|AjvNPf3!wrMK(y@(9B}Ps<@x-coNtYHB4t6FlKdGPs>1n4E8#Gz_np)RrQ%p7=MTWNXP<%|eXM@Cu{JXp*KT~C0= zriq57Bav$Z)$|c5oV-O{WyWT;T*LOYB7aZ8l9~fb#?&wM@!pL(;ps}q@nWqr1%n)| z#~D$lnE<=j9lU_3Vh-yA_0}8NOK2AR**%QeO2|e!AD=QvTD+E29QlGx?1HB{ptPL7 z`W{-7y8&gc+w)Pm2+0zBh-`CzAOr)F*!LB!#uv|AOl1vicQKaw+IpK2@i~K#NZ(7I zdG#}`)7eGci#U&j$r`fic(wV=#m`H4@vv$paR-qgd^y%n7k&Xc>}eG%?q|y9Jh2Vty;8hiZU6gG+2vY*#{3t7HP`ke;^Trp$ko~5dH0mVsjt+ zf=?+iZ>V-yL z$8P}``C2T@eX&hl(9J~HThi##X)en&>5f|B#E36y9#xNotKHWe{q)FU?|aUU*dmL6 zVdmRb#0sFVxFb;en+ZsJMhG6q`A8wsEuFoGRfq{W0r@-VJk?1g@-42poM3l6Jb50w4KV6nb`D^8t^ud2fb}se!D>No$*)wCTd8hHCfQXO#GX;WoRTp zmQ__$au=3_G%Wwi8}zpZ4#MugZ@OdWMGmUjO9We~Q&mR^PIbLEyUV`kuff|As;R+d zuKpmcI0cv#Wstwi`II0{R!I&%TSHYlF!e7Y23@nYUO{cp3tiB+_7^A2#$=*$a*^&# zmJFGp=cMdW$Vw^X<{1lxYvLn<_?twVGR=7UwZ36XkT4f3lc3j7<3;Fig#2(>;X?43 zvy*b*8#5u7s9E!aYh}i$I_mS}*GDDypio=dhZ`R-{e;?LIW$c~<6I#c>;x^z82}02 zX9UT8G4%$#ZJK-HFnKK<%6Crl+Ki{rJl*EZ>Wvz&pU%oz4&aftt-nNqr4T0@9OGl# z9bgsld+rXgL^;z2$g^OpV}IsPqJl`|4SrTJA_|1B2tpFPMiXRJ0TWk*yS+Pzj$xru zDwU8bzbA ztOJF!2qC2BHEForBdGF;vtMkU5M$9mepPf7luD4wDVC}{)_4uo3}gmJNDgX4xL zx=?(L-#~G_R76gmLc+_CkpRv`G7nD$iy{YQ4$zb)p@Q0O=GtlbZG&nb>>}e%h=a0Z zt$z0hr*caPs`W4`m!e9@$a4hEC?jHt7c>2+?Su}(bv2P!Jwqg;zhZUsGl$x@P_i|0 zY$s7~Q5tr%%|zCwu!%gPg+&5Q?@c7^%qhe!)J_Mu%sVUoV#eLP2;ne}#-+%nUiT;B zG?)3#^6X3nAO;s~-xf0q$wQs;I*&j~k3=svGJnc6)+u_yNzra`Ug;S4HX^?|OOI&l z&``g4czA7anE?uyd9zI|y6UiF$c-U>bv#CCvCJ@8ZTCgD%)w01hEdp?Rw7aE-Y(HZv~&xHaS7g>320l(5fOsyF=&B%>}UANIYo3zE&C9 z;Rbq7@FBbNUlV_2#WZ+;;mIwU*l^RZ31UT`23^f?OR&Cm-Bo4-Th0|^X4-ltvD8$>d*Lx(g;cszgPOI|gVlMpN8!51KdUD0T(1b%(fwR6igzj&;aS5Ske^v}Il ze>rlDHdvYRxe0?Q!efTSW{~2t>wVdb0}ZWCf7@Jsc_$gw4E*D1Yie6#6^5VA8< zmyIxl@CrLPv5=uv(}c0f+>c34TvARnLeCFC#*1pgDm*R-CZJAbxZLQj;*P0T*@&L8 zKN2ySQGkWyz;vUI_RzcjW?CA~tUY3m_e@f^?5$5R`gDKI^mV_#w{UL!H2}F%)VWSbGtS#(5xE!l@jELy_-SGmJHD7_`-C#&Tn)y0^BloZy)9 z&cD}t*v;QCdCRG)(B01}r9kpP;)j)E?$ln?E-Oj_W3TXAY>TxJ!~t#n9q48uf&y9H z=RaG`e|@&!of;&oYs%*7m1kyaeLrT1t-iaafHE7N3JP5PXu&1zUnO>+;F8gn%~^$}`~_6}XrT zHWRPqybTkUGTa`Xx6d7GK95=Zi_avRUcV(d9$k1yCW^ZgnK#`Ey%}U~CTZwesIm&9 znkcDGu`AOnnjdMS-yH&@<}Vyr=jZ6jFBb`Qg>lB@>zpgSOUy!t_4q7-qOjiK}=T zew>R3t~eO&AYl_A?3dOAl@riVW-hvz#Sk=@F|0B;00SD2EspmJ=^!joT)~)ha(hk@ zyX15MY&FvXfV}CU-NMeiB@)#lQd8*nX>CFdR9xkw*QfCNL}AGjIzNq~Z_-(I=O}u5 zP(%-3_@t-+QFX&t;ehAaqpwz#!QyRgK{}V>=%6+ z_pC$G14Lny&2v`UWD_S&pzkM#k?$>@ZS@iaua{!N*Ium_9Yu!GVgSeXp)NWPVZWVa zHJg^6KK)}0=Vvdnl0OTd1%ll(JF%2;VSrB;J?xrPr!-@Q!TY>QYIoE+HuQyX%4_3> zIjI1!wI!77>k+F?F^_3P;TQd4zcbO0!bMevL!;?~OllED@YWOvi_u`zAiT2LnTUO3 zv0d2^|FnQ!H(!ZWgW8cKZfMb#sUTWG46%|h`9Zqess?N~_34UBC-v5W+{kP^m=_Vf z$i9rM=0#%eOE|I^5EeS2v+F>WRF&p-Dy@?v| zUr5t&n-MF2CRROE2&OE$iA~@dR}9ZUVxwse=m76FnEOt^0vH_0+_oU?i99O9DwPAI zpVK(vyt61qA$THEEa~-zOK-eTtWj$)V zEWzf%PE4HE&lvG(JrB)1#FG*1^ij|h>Z!@07d9XoDQse$NjV`FEv1I$rQ?Crxi%1m zgZW63=R%pqS)%hbAP~T)*kybcPxqzyVCTu0)RBm^N#W&B&KApX#T-}6ol~Tx004FO zbED;f3Y3eVc~!OaWX{Bj&u0bNl6uK+7Y?gNKtt{^MLe2+J-nFx!07gSU)F^ZE2Dy> z&_hTE-?K5Rw~!O@U3SkP3ALG=?f?%ch9|Y$u%w;{DoKdb9w`o&V2@7xzxjM(28SJL$yU-oC`UpV@9yQG+(Oy_Gx&8y9EPUk*Ym$@R3ZaRuUj z0oC!Ypq65t^g5k*`EYJGVM1^K$E6X$hiOEhZ<6q)e$;ZH1&^{ZKe8L7OY>B8qL}Z+ zOAobc4|Fbg14f4y+EC<8nZjzKt+yD}ti211OEX!Hb zV?AhpIB79vA*~8!{NdZ?+XO?|Y{ar#vD6=CQ*F*T>{H`YSda`Pu4KRb;%%vigPeEm zS=a8GaOPVW`)J_RJW4U<>nb||E0$0heH zEs4de&h3(x@&AgGfUlHPql_Yhg(7{K%f@GP<8`xjBqze4)#;TQ+;K+_~KGl$!qdLKmFx0Wcs=DvYaK4 z9{j}aG>1DU(PPJlwnq$S_3TxW+7M8PrOEUIp8yD}nPo}mgXWQuMP9oHDJ;sg^_N=* z2g;=V#&wyqc1(U}K8wNtDak+AGq-Yl`lnHp3Kp?ad^vQHyT!4E`X(CIAh<$QAnAoRaqz%8D z)B2%rWQZ{5`q3DOr{f^L6_Pd{6uu27#8A>xh!A6}tgh1K*5>Di`vqHATJboe;1Gp> z&Z&{1USIp2wRj43N2CkSpR~J$8>wv7+rJ@MNZ{2gZJK(W6)>U0mmvr>uqx<4>nPJY zqKX1}#Ghim&v~dV6fvD%n7*e;B(;80czA1?8n;mfrYL2Z4$TaS)CWr#8DA>L=a=Y| zlL5d>{xN$Gb)K{EocS!Rfs&+|O5zsx7e{1L#dMv}TSD}Ls?=kyZ%i=YcxK1PDwpW> z-%#E$MTE1kK)2DJW8!MA_=)keZotEJ#a^Oy`l{j`7DJD3KhOT>c!x)|f$@!URvESg zE~~0H538Ts&Ce-*IMi}$H_a0!i$eosFg8vr_Oo0(Py5>rO7%L=cBzKrV-(g8gmYEe z(o-!eQi0na2J5HH%f17^puaP5^An|vPt=(qDpfmZd9T#@JR#ZK&ci=3V;qN=%a_!; z@~%=9@)X)!lqPz_=);uTb|_KjP~L66oQR9l5M%sj+`83ELrOG1lLf}-#1Cx-j7@I= zfMBC$Xo@I4n?S@MN9NlFH*xEg9VK1+BFH%^Fq=y<;T-1ypG`oghica-u$@kk+>feEs&o3a|2q(zB#RxvJuep`GM z{V5;cy0;>xLr~C}luC^^c>6aZ0Gzp*?T+fJ8sbmo##GQ{Sqtz^7FzF&yUyCE$^w9A zby-Z(d-Hoc?T6EMvUJycBeHawr$IwWh6y?sC;(8yWnw{Ly@$rlNke*5h=X3Yb!1$B z5wfB)f<-6=bh|9h>ZI4la(YdmuJ#7)6Tp0SEDG*2&*Qn6!IH2FYRv+W@mxxcw9S^eoT=7#? zEHU%pnk(LuJtpzoE`iGE?H_}T-<5Q~-z%Eb(-Dt+;(~=3CC=!RD?yP^xUsiszGzb) z&)|z}Sx1G%zzq9#&qdz=3fQRoOTt`xtgoRf&3?VZP?^csOY@91{9_HC7g3p zftrSC`)Re_t^j9>Ag;!=vGA{N{zVaq!=n~|NtHhWcNgmt|}fvocX$3$qC6^LzL&WZKt~h0DLDKPANNMJhJmGu`h^YiQFC%*?IZ~9E#ig1`4|& z>_ z9XdQ;|72G*cvKs=>q7ytNV9$>YEBzL74^vK!&=zZ>a-)(uKg$EKr8_$*-%CItp{Ed zNw;Eal7y~>evz^D=q!RiDpIZM15^?E|d`>)RWkr=;4fx%1+2CE;5z1>5Zr5Z$=QU!ZDnIScrRFLu6_$qfw+K3%C3>1~A zn)98?%oP-=DzFJ90kQ)6WJ@Y%7Hed@wN2PvS>#(;SQ{tRsz6AUH!R(05BUkjW%kxB zAFG$x6d6S>l^aylzTaM9hBR+G>!>v*9p_t794j|yQv5kecV{sOr)sd=CC&^5x@*NPh<%N+mH6|GK8(qfz4Sl5NB#uiy`%Lrj1TV#6(@votQS2`X#?UG2yl}d0v`jO*IknIJ(BL?r= z=w31mNvOm^H3VoiDg{)i_r4*<1wW7d48Y{Sp)*}H|Ac#-31v+em!2RmG;FBS^dCR+mI?^md?QFT&=Ono5(HbU|GmtU7>iH0)Lmh2dWX!ga7~l From eca2d15ff0e896e6e66b4920d221299e6f554f4f Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 30 Dec 2024 14:21:27 +0530 Subject: [PATCH 90/97] fix: screen header right buttons --- pages/Home.tsx | 12 +++++++----- pages/Transactions.tsx | 2 +- pages/send/PaymentSuccess.tsx | 1 + pages/settings/wallets/SetupWallet.tsx | 8 ++++---- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/pages/Home.tsx b/pages/Home.tsx index ea926ca..4a32b44 100644 --- a/pages/Home.tsx +++ b/pages/Home.tsx @@ -65,11 +65,13 @@ export function Home() { ( - - - - - + { + router.push("/settings"); + }} + > + + )} /> diff --git a/pages/Transactions.tsx b/pages/Transactions.tsx index 0f3ab2f..b241eb6 100644 --- a/pages/Transactions.tsx +++ b/pages/Transactions.tsx @@ -75,7 +75,7 @@ export function Transactions() { animation="slide_from_bottom" right={() => ( { + onPressIn={() => { router.back(); }} > diff --git a/pages/send/PaymentSuccess.tsx b/pages/send/PaymentSuccess.tsx index 3be99ce..c11a127 100644 --- a/pages/send/PaymentSuccess.tsx +++ b/pages/send/PaymentSuccess.tsx @@ -1,6 +1,7 @@ import { openURL } from "expo-linking"; import { router, useLocalSearchParams } from "expo-router"; import { LNURLPaymentSuccessAction } from "lib/lnurl"; +import React from "react"; import { ScrollView, View } from "react-native"; import { Tick } from "~/animations/Tick"; import { Receiver } from "~/components/Receiver"; diff --git a/pages/settings/wallets/SetupWallet.tsx b/pages/settings/wallets/SetupWallet.tsx index 4bad391..315dbdb 100644 --- a/pages/settings/wallets/SetupWallet.tsx +++ b/pages/settings/wallets/SetupWallet.tsx @@ -4,7 +4,7 @@ import * as Clipboard from "expo-clipboard"; import { router, useLocalSearchParams } from "expo-router"; import { useAppStore } from "lib/state/appStore"; import React from "react"; -import { Pressable, TouchableOpacity, View } from "react-native"; +import { TouchableOpacity, View } from "react-native"; import Toast from "react-native-toast-message"; import Alert from "~/components/Alert"; import DismissableKeyboardView from "~/components/DismissableKeyboardView"; @@ -153,8 +153,8 @@ export function SetupWallet() { title="Setup Wallet Connection" right={() => walletIdWithConnection !== -1 ? ( - { + { useAppStore .getState() .setSelectedWalletId(walletIdWithConnection); @@ -165,7 +165,7 @@ export function SetupWallet() { }} > - + ) : ( From f068acbc13dae88599cd6ee7bb0ebc2b9a2e6e7d Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 30 Dec 2024 19:37:49 +0530 Subject: [PATCH 91/97] chore: add touchable opacity to show transactions button --- pages/Home.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/pages/Home.tsx b/pages/Home.tsx index 4a32b44..5715d2a 100644 --- a/pages/Home.tsx +++ b/pages/Home.tsx @@ -147,12 +147,14 @@ export function Home() { - - + + + + From 8de482f80cf2ff379bece014e0903b88b786ee16 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 30 Dec 2024 19:51:25 +0530 Subject: [PATCH 92/97] chore: remove build script --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 6a6d6db..0118238 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "ios": "expo run:ios", "eas:build:ios:preview": "eas build --profile preview --platform ios", "eas:build:android:preview": "eas build --profile preview --platform android", - "build": "eas build --non-interactive --platform android --local --profile preview --output=./app-release.apk", "eas:build:android": "eas build --platform android", "eas:build:ios": "eas build --platform ios", "eas:submit:ios": "eas submit -p ios --latest", From 1181045eb9a78534536ca56bc33ad809239e6e7d Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 30 Dec 2024 20:03:06 +0530 Subject: [PATCH 93/97] chore: remove logo from qr codes --- pages/receive/Receive.tsx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/pages/receive/Receive.tsx b/pages/receive/Receive.tsx index 08b0b37..cbe2303 100644 --- a/pages/receive/Receive.tsx +++ b/pages/receive/Receive.tsx @@ -2,7 +2,7 @@ import { Nip47Transaction } from "@getalby/sdk/dist/NWCClient"; import * as Clipboard from "expo-clipboard"; import { router } from "expo-router"; import React from "react"; -import { Image, Share, TouchableOpacity, View } from "react-native"; +import { Share, TouchableOpacity, View } from "react-native"; import Toast from "react-native-toast-message"; import DismissableKeyboardView from "~/components/DismissableKeyboardView"; import { DualCurrencyInput } from "~/components/DualCurrencyInput"; @@ -218,16 +218,7 @@ export function Receive() { {!enterCustomAmount && (invoice.length || lightningAddress) && ( <> - - - - - - + {invoice ? ( From 796ba6ef41364157e46494506fc2552114cb19df Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 30 Dec 2024 20:29:19 +0530 Subject: [PATCH 94/97] chore: add biometric and fingerprint permissions for android --- app.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app.json b/app.json index be28d4c..cbe258c 100644 --- a/app.json +++ b/app.json @@ -64,7 +64,11 @@ "backgroundImage": "./assets/adaptive-icon-bg.png", "monochromeImage": "./assets/monochromatic.png" }, - "permissions": ["android.permission.CAMERA"], + "permissions": [ + "android.permission.CAMERA", + "android.permission.USE_BIOMETRIC", + "android.permission.USE_FINGERPRINT" + ], "userInterfaceStyle": "automatic" }, "extra": { From 09bf128bab284791773b59b6254fb01a28764341 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 30 Dec 2024 20:52:50 +0530 Subject: [PATCH 95/97] chore: remove high error correction level in QRs --- components/QRCode.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/QRCode.tsx b/components/QRCode.tsx index 2cb58c3..fb118d6 100644 --- a/components/QRCode.tsx +++ b/components/QRCode.tsx @@ -12,7 +12,7 @@ function QRCode({ value }: { value: string }) { style={{ borderRadius: 28, elevation: 2 }} > - + ); From 60d1a27cc97fac0493c35185fe4f11f0e295499e Mon Sep 17 00:00:00 2001 From: im-adithya Date: Mon, 30 Dec 2024 20:54:14 +0530 Subject: [PATCH 96/97] chore: bump version to 1.8.1 --- app.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app.json b/app.json index cbe258c..7b73ebd 100644 --- a/app.json +++ b/app.json @@ -2,7 +2,7 @@ "expo": { "name": "Alby Go", "slug": "alby-mobile", - "version": "1.8.0", + "version": "1.8.1", "scheme": ["lightning", "bitcoin", "alby", "nostr+walletconnect"], "orientation": "portrait", "icon": "./assets/icon.png", diff --git a/package.json b/package.json index 0118238..d736de3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "alby-go", - "version": "1.8.0", + "version": "1.8.1", "main": "./index.js", "scripts": { "start": "expo start", From ee84610551b4a1a3e66184e66e3bdc94612e7eae Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Mon, 6 Jan 2025 12:53:44 +0700 Subject: [PATCH 97/97] feat: pay 0-amount invoices --- app/(app)/send/0-amount.js | 5 +++ package.json | 2 +- pages/send/ConfirmPayment.tsx | 19 ++++---- pages/send/Send.tsx | 15 ++++++- pages/send/ZeroAmount.tsx | 83 +++++++++++++++++++++++++++++++++++ yarn.lock | 8 ++-- 6 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 app/(app)/send/0-amount.js create mode 100644 pages/send/ZeroAmount.tsx diff --git a/app/(app)/send/0-amount.js b/app/(app)/send/0-amount.js new file mode 100644 index 0000000..3b28dc7 --- /dev/null +++ b/app/(app)/send/0-amount.js @@ -0,0 +1,5 @@ +import { ZeroAmount } from "../../../pages/send/ZeroAmount"; + +export default function Page() { + return ; +} diff --git a/package.json b/package.json index d736de3..2ebec16 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "test:ci": "jest" }, "dependencies": { - "@getalby/lightning-tools": "^5.1.1", + "@getalby/lightning-tools": "^5.1.2", "@getalby/sdk": "^3.8.2", "@popicons/react-native": "^0.0.20", "@react-native-async-storage/async-storage": "1.23.1", diff --git a/pages/send/ConfirmPayment.tsx b/pages/send/ConfirmPayment.tsx index 25230fc..836704b 100644 --- a/pages/send/ConfirmPayment.tsx +++ b/pages/send/ConfirmPayment.tsx @@ -18,17 +18,22 @@ import { useAppStore } from "~/lib/state/appStore"; export function ConfirmPayment() { const { data: transactions } = useTransactions(); - const { invoice, originalText, comment, successAction } = + const { invoice, originalText, comment, successAction, amount } = useLocalSearchParams() as { invoice: string; originalText: string; comment: string; successAction: string; + amount?: string; }; const getFiatAmount = useGetFiatAmount(); const [isLoading, setLoading] = React.useState(false); const wallets = useAppStore((store) => store.wallets); const selectedWalletId = useAppStore((store) => store.selectedWalletId); + const decodedInvoice = new Invoice({ + pr: invoice, + }); + const amountToPaySats = amount ? +amount : decodedInvoice.satoshi; async function pay() { setLoading(true); @@ -39,6 +44,7 @@ export function ConfirmPayment() { } const response = await nwcClient.payInvoice({ invoice, + amount: amount ? amountToPaySats * 1000 : undefined, }); console.info("payInvoice Response", response); @@ -54,7 +60,7 @@ export function ConfirmPayment() { preimage: response.preimage, originalText, invoice, - amount: decodedInvoice.satoshi, + amount: amountToPaySats, successAction, }, }); @@ -65,9 +71,6 @@ export function ConfirmPayment() { setLoading(false); } - const decodedInvoice = new Invoice({ - pr: invoice, - }); return ( <> @@ -75,9 +78,7 @@ export function ConfirmPayment() { - {new Intl.NumberFormat().format( - Math.ceil(decodedInvoice.satoshi), - )} + {new Intl.NumberFormat().format(Math.ceil(amountToPaySats))} sats @@ -85,7 +86,7 @@ export function ConfirmPayment() { {getFiatAmount && ( - {getFiatAmount(decodedInvoice.satoshi)} + {getFiatAmount(amountToPaySats)} )} diff --git a/pages/send/Send.tsx b/pages/send/Send.tsx index 36bf59c..c83b48a 100644 --- a/pages/send/Send.tsx +++ b/pages/send/Send.tsx @@ -132,7 +132,20 @@ export function Send() { return true; } else { // Check if this is a valid invoice - new Invoice({ pr: text }); + const invoice = new Invoice({ pr: text }); + + if (invoice.satoshi === 0) { + router.replace({ + pathname: "/send/0-amount", + params: { + invoice: text, + originalText, + comment: invoice.description, + }, + }); + return true; + } + router.replace({ pathname: "/send/confirm", params: { invoice: text, originalText }, diff --git a/pages/send/ZeroAmount.tsx b/pages/send/ZeroAmount.tsx new file mode 100644 index 0000000..e7a03e7 --- /dev/null +++ b/pages/send/ZeroAmount.tsx @@ -0,0 +1,83 @@ +import { router, useLocalSearchParams } from "expo-router"; +import React from "react"; +import { View } from "react-native"; +import DismissableKeyboardView from "~/components/DismissableKeyboardView"; +import { DualCurrencyInput } from "~/components/DualCurrencyInput"; +import Loading from "~/components/Loading"; +import { Receiver } from "~/components/Receiver"; +import Screen from "~/components/Screen"; +import { Button } from "~/components/ui/button"; +import { Text } from "~/components/ui/text"; +import { errorToast } from "~/lib/errorToast"; + +export function ZeroAmount() { + const { invoice, originalText, comment } = + useLocalSearchParams() as unknown as { + invoice: string; + originalText: string; + comment: string; + }; + const [isLoading, setLoading] = React.useState(false); + const [amount, setAmount] = React.useState(""); + + async function submit() { + setLoading(true); + try { + router.push({ + pathname: "/send/confirm", + params: { + invoice, + originalText, + amount, + }, + }); + } catch (error) { + console.error(error); + errorToast(error); + } + setLoading(false); + } + + return ( + <> + + + + + + + + + {comment && ( + + + Comment + + + {comment} + + + )} + + + + + + + + + ); +} diff --git a/yarn.lock b/yarn.lock index f417716..39ed398 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1247,10 +1247,10 @@ find-up "^5.0.0" js-yaml "^4.1.0" -"@getalby/lightning-tools@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-5.1.1.tgz#51125b2c58ef9372ae9efa93d0808d2205914b91" - integrity sha512-qiGWY7AMnQXywNlpEUTm/2u7Qx0C0qV0i3vlAV5ip8xV2quo4hkesHuAh6dBg/p3VC7t1fa9YUe9677hvQ3fVA== +"@getalby/lightning-tools@^5.1.2": + version "5.1.2" + resolved "https://registry.yarnpkg.com/@getalby/lightning-tools/-/lightning-tools-5.1.2.tgz#8a018e98d5c13097dd98d93192cf5e4e455f4c20" + integrity sha512-BwGm8eGbPh59BVa1gI5yJMantBl/Fdps6X4p1ZACnmxz9vDINX8/3aFoOnDlF7yyA2boXWCsReVQSr26Q2yjiQ== "@getalby/sdk@^3.8.2": version "3.8.2"