This commit is contained in:
Jamie Brunton committed 2026-01-01 17:59:18 +00:00
1 parent 0fa7971cb9
commit 3b445597f0
25 files changed
+757 -101

No files matched your search

+62 -5
View File
@@ -101,6 +101,47 @@ export async function clearPushToken(userId: string): Promise<void> {
}
}
/**
* Get Expo push token with retry logic for Android.
* On Android, Firebase may not be initialized immediately on app start,
* so we retry a few times with increasing delays.
*/
async function getExpoPushTokenWithRetry(
projectId: string,
maxRetries = 5
): Promise<string | null> {
console.log(`[Push] Getting token for project: ${projectId}, platform: ${Platform.OS}`);
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
console.log(`[Push] Attempt ${attempt + 1}/${maxRetries} to get push token...`);
const tokenData = await Notifications.getExpoPushTokenAsync({ projectId });
console.log(`[Push] Successfully got token: ${tokenData.data}`);
return tokenData.data;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.log(`[Push] Attempt ${attempt + 1} failed: ${errorMessage}`);
// Retry for any Firebase-related errors on Android
const isFirebaseError =
error instanceof Error &&
(error.message.includes("Firebase") ||
error.message.includes("FCM") ||
error.message.includes("Google Play"));
if (Platform.OS === "android" && attempt < maxRetries - 1) {
const delay = (attempt + 1) * 1000;
console.log(`[Push] Retrying in ${delay}ms...`);
await new Promise((resolve) => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
return null;
}
/**
* Check if current device needs to register for push notifications.
* This handles the case where a user onboarded on one device (e.g., iOS)
@@ -110,32 +151,44 @@ export async function clearPushToken(userId: string): Promise<void> {
* Returns true if a new token was registered.
*/
export async function checkAndRegisterPushToken(userId: string): Promise<boolean> {
console.log(`[Push] checkAndRegisterPushToken called for user: ${userId}`);
if (Platform.OS === "web") {
console.log("[Push] Skipping - web platform");
return false;
}
try {
// First check if permissions are already granted
const { status } = await Notifications.getPermissionsAsync();
console.log(`[Push] Current permission status: ${status}`);
// If permissions not granted, we need to prompt the user
// But only do this if we don't already have a token for this device
if (status !== "granted") {
// Request permissions
console.log("[Push] Requesting permissions...");
const { status: newStatus } = await Notifications.requestPermissionsAsync();
console.log(`[Push] New permission status: ${newStatus}`);
if (newStatus !== "granted") {
console.log("[Push] Permission denied");
return false;
}
}
// Get the current device's push token
const projectId = Constants.expoConfig?.extra?.eas?.projectId;
console.log(`[Push] Project ID: ${projectId}`);
if (!projectId) {
console.log("[Push] No project ID configured");
return false;
}
const tokenData = await Notifications.getExpoPushTokenAsync({ projectId });
const currentToken = tokenData.data;
const currentToken = await getExpoPushTokenWithRetry(projectId);
console.log(`[Push] Current token: ${currentToken}`);
if (!currentToken) {
console.log("[Push] Failed to get token");
return false;
}
// Fetch the stored token from the database
const { data: profile } = await supabase
@@ -144,16 +197,20 @@ export async function checkAndRegisterPushToken(userId: string): Promise<boolean
.eq("id", userId)
.single();
console.log(`[Push] Stored token: ${profile?.push_token}`);
// If the stored token is different (or null), save the new one
if (profile?.push_token !== currentToken) {
console.log("[Push] Token changed, saving new token...");
await savePushToken(userId, currentToken);
console.log("Push token updated for new device");
console.log("[Push] Push token updated for new device");
return true;
}
console.log("[Push] Token unchanged");
return false;
} catch (error) {
console.error("Failed to check/register push token:", error);
console.error("[Push] Failed to check/register push token:", error);
captureError(error as Error, { context: "push_token_check" });
return false;
}