refactor: enhance push notification registration with improved error handling and retry logic

This commit is contained in:
Nawaz Dhandala
2026-02-18 08:39:29 +00:00
parent 92e247d168
commit c9eb72ba2c
4 changed files with 84 additions and 13 deletions

View File

@@ -78,9 +78,12 @@ export default class UserPushAPI extends BaseAPI<
});
if (existingDevice) {
// Mark as used and return a specific response indicating device was already registered
throw new BadDataException(
"This device is already registered for push notifications",
return Response.sendErrorResponse(
req,
res,
new BadDataException(
"This device is already registered for push notifications",
),
);
}

View File

@@ -20,11 +20,29 @@ export async function registerPushDevice(params: {
deviceName: Device.modelName || "Unknown Device",
projectId: params.projectId,
});
} catch (error: any) {
console.info(
`[PushNotifications] Device registered successfully for project ${params.projectId}`,
);
} catch (error: unknown) {
const status: number | undefined = (
error as { response?: { status?: number } }
)?.response?.status;
const message: string =
(error as { response?: { data?: { message?: string } } })?.response?.data
?.message || String(error);
// Treat "already registered" as success
if (error?.response?.status === 400) {
if (status === 400 && message.includes("already registered")) {
console.info(
`[PushNotifications] Device already registered for project ${params.projectId}`,
);
return;
}
// Log and re-throw other errors
console.error(
`[PushNotifications] Registration failed (status=${status}): ${message}`,
);
throw error;
}
}

View File

@@ -16,6 +16,9 @@ import { useAuth } from "./useAuth";
import { useProject } from "./useProject";
import { PUSH_TOKEN_KEY } from "./pushTokenUtils";
const RETRY_DELAY_MS: number = 5000;
const MAX_RETRIES: number = 3;
export function usePushNotifications(navigationRef: unknown): void {
const { isAuthenticated }: { isAuthenticated: boolean } = useAuth();
const { projectList }: { projectList: Array<{ _id: string }> } = useProject();
@@ -46,8 +49,31 @@ export function usePushNotifications(navigationRef: unknown): void {
let cancelled: boolean = false;
const register: () => Promise<void> = async (): Promise<void> => {
const token: string | null = await requestPermissionsAndGetToken();
let token: string | null = null;
let attempt: number = 0;
// Retry obtaining the push token
while (!token && attempt < MAX_RETRIES && !cancelled) {
token = await requestPermissionsAndGetToken();
if (!token && !cancelled) {
attempt++;
if (attempt < MAX_RETRIES) {
console.warn(
`[PushNotifications] Push token not available, retrying in ${RETRY_DELAY_MS}ms (attempt ${attempt}/${MAX_RETRIES})`,
);
await new Promise<void>((resolve: () => void): void => {
setTimeout(resolve, RETRY_DELAY_MS);
});
}
}
}
if (!token || cancelled) {
if (!token) {
console.warn(
"[PushNotifications] Could not obtain push token after all retries — device will not be registered",
);
}
return;
}
@@ -63,13 +89,21 @@ export function usePushNotifications(navigationRef: unknown): void {
deviceToken: token,
projectId: project._id,
});
} catch {
// Continue registering with other projects
} catch (error: unknown) {
console.warn(
`[PushNotifications] Failed to register device for project ${project._id}:`,
error,
);
}
}
};
register();
register().catch((error: unknown): void => {
console.error(
"[PushNotifications] Unexpected error during push registration:",
error,
);
});
return (): void => {
cancelled = true;

View File

@@ -80,6 +80,9 @@ export async function setupNotificationCategories(): Promise<void> {
export async function requestPermissionsAndGetToken(): Promise<string | null> {
if (!Device.isDevice) {
console.warn(
"[PushNotifications] Not a physical device — skipping push token registration",
);
return null;
}
@@ -92,6 +95,10 @@ export async function requestPermissionsAndGetToken(): Promise<string | null> {
}
if (finalStatus !== "granted") {
console.warn(
"[PushNotifications] Push notification permission not granted:",
finalStatus,
);
return null;
}
@@ -100,12 +107,21 @@ export async function requestPermissionsAndGetToken(): Promise<string | null> {
Constants.easConfig?.projectId;
if (!projectId) {
console.warn(
"[PushNotifications] EAS project ID not found — cannot register for push notifications",
);
return null;
}
const tokenData: ExpoPushToken = await Notifications.getExpoPushTokenAsync({
projectId,
});
try {
const tokenData: ExpoPushToken =
await Notifications.getExpoPushTokenAsync({
projectId,
});
return tokenData.data;
return tokenData.data;
} catch (error: unknown) {
console.error("[PushNotifications] Failed to get push token:", error);
return null;
}
}