From c76f5191361eb57a921df6cda095446132106f67 Mon Sep 17 00:00:00 2001 From: im-adithya Date: Tue, 21 Jan 2025 17:08:39 +0530 Subject: [PATCH] fix: keep Expo Go working --- app/_layout.tsx | 6 ++- context/Notification.tsx | 44 ++++++++++------- lib/constants.ts | 4 ++ lib/notifications.ts | 6 +-- lib/walletInfo.ts | 24 ++++++---- native/notifications/service.ts | 84 +++++++++++++++++++++++++++++++++ services/Notifications.ts | 81 ++++--------------------------- 7 files changed, 145 insertions(+), 104 deletions(-) create mode 100644 native/notifications/service.ts diff --git a/app/_layout.tsx b/app/_layout.tsx index c12d147..4da6a97 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -20,7 +20,7 @@ import { UserInactivityProvider } from "~/context/UserInactivity"; import "~/global.css"; import { useInfo } from "~/hooks/useInfo"; import { SessionProvider } from "~/hooks/useSession"; -import { NAV_THEME } from "~/lib/constants"; +import { IS_EXPO_GO, NAV_THEME } from "~/lib/constants"; import { isBiometricSupported } from "~/lib/isBiometricSupported"; import { useAppStore } from "~/lib/state/appStore"; import { useColorScheme } from "~/lib/useColorScheme"; @@ -96,7 +96,9 @@ export default function RootLayout() { await Promise.all([loadTheme(), loadFonts(), checkBiometricStatus()]); } finally { setResourcesLoaded(true); - await checkAndPromptForNotifications(); + if (!IS_EXPO_GO) { + await checkAndPromptForNotifications(); + } SplashScreen.hide(); } }; diff --git a/context/Notification.tsx b/context/Notification.tsx index 087ba09..8566652 100644 --- a/context/Notification.tsx +++ b/context/Notification.tsx @@ -1,38 +1,46 @@ -import * as ExpoNotifications from "expo-notifications"; import { useEffect, useRef } from "react"; +import { IS_EXPO_GO } from "~/lib/constants"; import { handleLink } from "~/lib/link"; import { useAppStore } from "~/lib/state/appStore"; -ExpoNotifications.setNotificationHandler({ - handleNotification: async () => { - return { - shouldShowAlert: true, - shouldPlaySound: true, - shouldSetBadge: false, - }; - }, -}); +let ExpoNotifications: any; + +if (!IS_EXPO_GO) { + ExpoNotifications = require("expo-notifications"); + + ExpoNotifications.setNotificationHandler({ + handleNotification: async () => { + return { + shouldShowAlert: true, + shouldPlaySound: true, + shouldSetBadge: false, + }; + }, + }); +} export const NotificationProvider = ({ children }: any) => { - const responseListener = useRef(); + const responseListener = useRef(); const isNotificationsEnabled = useAppStore( (store) => store.isNotificationsEnabled, ); useEffect(() => { - if (!isNotificationsEnabled) { + if (IS_EXPO_GO || !isNotificationsEnabled) { return; } // this is for iOS only as tapping the notifications // directly open the deep link on android responseListener.current = - ExpoNotifications.addNotificationResponseReceivedListener((response) => { - const deepLink = response.notification.request.content.data.deepLink; - if (deepLink) { - handleLink(deepLink); - } - }); + ExpoNotifications.addNotificationResponseReceivedListener( + (response: any) => { + const deepLink = response.notification.request.content.data.deepLink; + if (deepLink) { + handleLink(deepLink); + } + }, + ); return () => { responseListener.current && diff --git a/lib/constants.ts b/lib/constants.ts index 0fdcd0b..bc322ef 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -1,4 +1,5 @@ import { Nip47Capability } from "@getalby/sdk/dist/NWCClient"; +import Constants, { ExecutionEnvironment } from "expo-constants"; export const NAV_THEME = { light: { @@ -47,3 +48,6 @@ 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})/; + +export const IS_EXPO_GO = + Constants.executionEnvironment === ExecutionEnvironment.StoreClient; diff --git a/lib/notifications.ts b/lib/notifications.ts index e38fe2a..477fd14 100644 --- a/lib/notifications.ts +++ b/lib/notifications.ts @@ -1,6 +1,6 @@ import { nwc } from "@getalby/sdk"; import { Platform } from "react-native"; -import { NOSTR_API_URL } from "~/lib/constants"; +import { IS_EXPO_GO, NOSTR_API_URL } from "~/lib/constants"; import { errorToast } from "~/lib/errorToast"; import { computeSharedSecret } from "~/lib/sharedSecret"; import { useAppStore } from "~/lib/state/appStore"; @@ -11,7 +11,7 @@ export async function registerWalletNotifications( walletId: number, walletName?: string, ) { - if (!nwcUrl) { + if (IS_EXPO_GO || !nwcUrl) { return; } @@ -77,7 +77,7 @@ export async function registerWalletNotifications( } export async function deregisterWalletNotifications(pushId?: string) { - if (!pushId) { + if (IS_EXPO_GO || !pushId) { return; } try { diff --git a/lib/walletInfo.ts b/lib/walletInfo.ts index c84b72c..976d5c3 100644 --- a/lib/walletInfo.ts +++ b/lib/walletInfo.ts @@ -1,5 +1,5 @@ import { Platform } from "react-native"; -import { SUITE_NAME } from "~/lib/constants"; +import { IS_EXPO_GO, SUITE_NAME } from "~/lib/constants"; let UserDefaults: any; let SharedPreferences: any; @@ -7,11 +7,13 @@ let SharedPreferences: any; // this is done because accessing values stored from expo-secure-store // is quite difficult and we do not wish to complicate the notification // service extension (ios) or messaging service (android) -if (Platform.OS === "ios") { - UserDefaults = - require("@alevy97/react-native-userdefaults/src/ReactNativeUserDefaults.ios").default; -} else { - SharedPreferences = require("@getalby/expo-shared-preferences"); +if (!IS_EXPO_GO) { + if (Platform.OS === "ios") { + UserDefaults = + require("@alevy97/react-native-userdefaults/src/ReactNativeUserDefaults.ios").default; + } else { + SharedPreferences = require("@getalby/expo-shared-preferences"); + } } type WalletInfo = { @@ -28,9 +30,10 @@ export async function storeWalletInfo( publicKey: string, walletData: Partial, ) { - if (!publicKey) { + if (IS_EXPO_GO || !publicKey) { return; } + if (Platform.OS === "ios") { const groupDefaults = new UserDefaults(SUITE_NAME); const wallets = (await groupDefaults.get("wallets")) || {}; @@ -51,9 +54,10 @@ export async function storeWalletInfo( } export async function removeWalletInfo(publicKey: string, walletId: number) { - if (!publicKey) { + if (IS_EXPO_GO || !publicKey) { return; } + if (Platform.OS === "ios") { const groupDefaults = new UserDefaults(SUITE_NAME); const wallets = await groupDefaults.get("wallets"); @@ -85,6 +89,10 @@ export async function removeWalletInfo(publicKey: string, walletId: number) { } export async function removeAllInfo() { + if (IS_EXPO_GO) { + return; + } + if (Platform.OS === "ios") { const groupDefaults = new UserDefaults(SUITE_NAME); await groupDefaults.removeAll(); diff --git a/native/notifications/service.ts b/native/notifications/service.ts new file mode 100644 index 0000000..7226a11 --- /dev/null +++ b/native/notifications/service.ts @@ -0,0 +1,84 @@ +import Constants from "expo-constants"; +import * as Device from "expo-device"; +import * as ExpoNotifications from "expo-notifications"; +import { Platform } from "react-native"; +import Toast from "react-native-toast-message"; +import { errorToast } from "~/lib/errorToast"; +import { registerWalletNotifications } from "~/lib/notifications"; +import { useAppStore } from "~/lib/state/appStore"; + +export async function registerForPushNotificationsAsync(): Promise< + boolean | null +> { + if (Platform.OS === "android") { + ExpoNotifications.setNotificationChannelAsync("default", { + name: "default", + importance: ExpoNotifications.AndroidImportance.MAX, + vibrationPattern: [0, 250, 250, 250], + lightColor: "#FF231F7C", + enableLights: true, + enableVibrate: true, + }); + } + + if (!Device.isDevice) { + errorToast("Must use physical device for push notifications"); + return false; + } + + const { status: existingStatus } = + await ExpoNotifications.getPermissionsAsync(); + let finalStatus = existingStatus; + if (existingStatus !== "granted") { + const { status } = await ExpoNotifications.requestPermissionsAsync(); + finalStatus = status; + } + if (finalStatus === "undetermined") { + return null; + } + if (finalStatus === "denied") { + if (existingStatus === "denied") { + errorToast(new Error("Enable app notifications in device settings")); + } + return false; + } + const projectId = + Constants?.expoConfig?.extra?.eas?.projectId ?? + Constants?.easConfig?.projectId; + if (!projectId) { + errorToast(new Error("Project ID not found")); + } + try { + const pushToken = ( + await ExpoNotifications.getExpoPushTokenAsync({ + projectId, + }) + ).data; + + useAppStore.getState().setExpoPushToken(pushToken); + + const wallets = useAppStore.getState().wallets; + + for (let i = 0; i < wallets.length; i++) { + const wallet = wallets[i]; + if (!(wallet.nwcCapabilities || []).includes("notifications")) { + Toast.show({ + type: "info", + text1: `${wallet.name} does not have notifications capability`, + }); + continue; + } + await registerWalletNotifications( + wallet.nostrWalletConnectUrl ?? "", + i, + wallet.name, + ); + } + + return true; + } catch (error) { + errorToast(error); + } + + return false; +} diff --git a/services/Notifications.ts b/services/Notifications.ts index 7226a11..5e5df91 100644 --- a/services/Notifications.ts +++ b/services/Notifications.ts @@ -1,84 +1,19 @@ -import Constants from "expo-constants"; -import * as Device from "expo-device"; -import * as ExpoNotifications from "expo-notifications"; -import { Platform } from "react-native"; -import Toast from "react-native-toast-message"; +import { IS_EXPO_GO } from "~/lib/constants"; import { errorToast } from "~/lib/errorToast"; -import { registerWalletNotifications } from "~/lib/notifications"; -import { useAppStore } from "~/lib/state/appStore"; export async function registerForPushNotificationsAsync(): Promise< boolean | null > { - if (Platform.OS === "android") { - ExpoNotifications.setNotificationChannelAsync("default", { - name: "default", - importance: ExpoNotifications.AndroidImportance.MAX, - vibrationPattern: [0, 250, 250, 250], - lightColor: "#FF231F7C", - enableLights: true, - enableVibrate: true, - }); - } - - if (!Device.isDevice) { - errorToast("Must use physical device for push notifications"); - return false; - } - - const { status: existingStatus } = - await ExpoNotifications.getPermissionsAsync(); - let finalStatus = existingStatus; - if (existingStatus !== "granted") { - const { status } = await ExpoNotifications.requestPermissionsAsync(); - finalStatus = status; - } - if (finalStatus === "undetermined") { + if (IS_EXPO_GO) { + errorToast(new Error("Push notifications are disabled in Expo Go")); return null; } - if (finalStatus === "denied") { - if (existingStatus === "denied") { - errorToast(new Error("Enable app notifications in device settings")); - } - return false; - } - const projectId = - Constants?.expoConfig?.extra?.eas?.projectId ?? - Constants?.easConfig?.projectId; - if (!projectId) { - errorToast(new Error("Project ID not found")); - } - try { - const pushToken = ( - await ExpoNotifications.getExpoPushTokenAsync({ - projectId, - }) - ).data; - - useAppStore.getState().setExpoPushToken(pushToken); - - const wallets = useAppStore.getState().wallets; - - for (let i = 0; i < wallets.length; i++) { - const wallet = wallets[i]; - if (!(wallet.nwcCapabilities || []).includes("notifications")) { - Toast.show({ - type: "info", - text1: `${wallet.name} does not have notifications capability`, - }); - continue; - } - await registerWalletNotifications( - wallet.nostrWalletConnectUrl ?? "", - i, - wallet.name, - ); - } - return true; + try { + const nativePushNotifications = require("~/native/notifications/service"); + return await nativePushNotifications.registerForPushNotificationsAsync(); } catch (error) { - errorToast(error); + console.error("Error importing push notifications logic:", error); + return null; } - - return false; }