Skip to content

Commit

Permalink
fix: keep Expo Go working
Browse files Browse the repository at this point in the history
  • Loading branch information
im-adithya committed Jan 21, 2025
1 parent c982b5e commit c76f519
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 104 deletions.
6 changes: 4 additions & 2 deletions app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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();
}
};
Expand Down
44 changes: 26 additions & 18 deletions context/Notification.tsx
Original file line number Diff line number Diff line change
@@ -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<ExpoNotifications.Subscription>();
const responseListener = useRef<any>();
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 &&
Expand Down
4 changes: 4 additions & 0 deletions lib/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Nip47Capability } from "@getalby/sdk/dist/NWCClient";
import Constants, { ExecutionEnvironment } from "expo-constants";

export const NAV_THEME = {
light: {
Expand Down Expand Up @@ -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;
6 changes: 3 additions & 3 deletions lib/notifications.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -11,7 +11,7 @@ export async function registerWalletNotifications(
walletId: number,
walletName?: string,
) {
if (!nwcUrl) {
if (IS_EXPO_GO || !nwcUrl) {
return;
}

Expand Down Expand Up @@ -77,7 +77,7 @@ export async function registerWalletNotifications(
}

export async function deregisterWalletNotifications(pushId?: string) {
if (!pushId) {
if (IS_EXPO_GO || !pushId) {
return;
}
try {
Expand Down
24 changes: 16 additions & 8 deletions lib/walletInfo.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
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;

// 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 = {
Expand All @@ -28,9 +30,10 @@ export async function storeWalletInfo(
publicKey: string,
walletData: Partial<WalletInfo>,
) {
if (!publicKey) {
if (IS_EXPO_GO || !publicKey) {
return;
}

if (Platform.OS === "ios") {
const groupDefaults = new UserDefaults(SUITE_NAME);
const wallets = (await groupDefaults.get("wallets")) || {};
Expand All @@ -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");
Expand Down Expand Up @@ -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();
Expand Down
84 changes: 84 additions & 0 deletions native/notifications/service.ts
Original file line number Diff line number Diff line change
@@ -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;
}
81 changes: 8 additions & 73 deletions services/Notifications.ts
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit c76f519

Please sign in to comment.