mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
refactor: enhance push notification registration with improved error handling and retry logic
This commit is contained in:
@@ -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",
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user