diff --git a/src/services/notifications/NativeNotificationService.ts b/src/services/notifications/NativeNotificationService.ts index 841aafbd..a1694b59 100644 --- a/src/services/notifications/NativeNotificationService.ts +++ b/src/services/notifications/NativeNotificationService.ts @@ -13,7 +13,6 @@ import { Capacitor } from "@capacitor/core"; import type { PushNotificationSchema } from "@capacitor/push-notifications"; -import { ScheduleKind } from "@timesafari/daily-notification-plugin"; import { DailyNotification } from "@/plugins/DailyNotificationPlugin"; import { REMINDER_ID_DAILY_REMINDER } from "./reminderIds"; import { configureNativeFetcherIfReady } from "./nativeFetcherConfig"; @@ -577,52 +576,54 @@ export async function refreshNotifications(): Promise { const nextNotifications = (data as { nextNotifications?: unknown }) ?.nextNotifications; - - // Keep existing behavior: ensure background worker credentials are current. - await configureNativeFetcherIfReady(); - if (!Array.isArray(nextNotifications)) { return; } - // Full replacement: clear prior scheduled notifications before applying backend schedule. - // (Plugin API name is cancelAllNotifications; equivalent intent to "clearAllNotifications".) - await DailyNotification.cancelAllNotifications(); + const timestamps = nextNotifications + .map((n) => (n as { timestamp?: unknown })?.timestamp) + .filter((t): t is number => typeof t === "number" && Number.isFinite(t)); - // Apply backend schedule to the native scheduler as recurring notifications. - // The plugin's schedule API is cron/clockTime-based (recurring), so we map - // the backend timestamps to clockTime (HH:mm) schedules. - await Promise.all( - nextNotifications.map(async (n) => { - const timestamp = (n as { timestamp?: unknown })?.timestamp; - if (typeof timestamp !== "number" || !Number.isFinite(timestamp)) { - return; - } + if (timestamps.length === 0) { + return; + } - const dt = new Date(timestamp); - const hh = dt.getHours().toString().padStart(2, "0"); - const mm = dt.getMinutes().toString().padStart(2, "0"); - const clockTime = `${hh}:${mm}`; - const id = `backend-${timestamp}`; + // Keep existing behavior: ensure background worker credentials are current. + await configureNativeFetcherIfReady(); - const existing = await DailyNotification.getSchedule(id); - if (existing) { - await DailyNotification.updateSchedule(id, { - clockTime, - enabled: true, - }); - } else { - // DailyNotification plugin typings currently omit `id` on CreateScheduleInput, - // but runtime supports deterministic IDs for schedule reconciliation. - await DailyNotification.createSchedule({ - id, - kind: ScheduleKind.NOTIFY, - clockTime, - enabled: true, - } as unknown as never); - } - }), - ); + // Backend is source of truth: apply exact timestamps (no HH:mm / recurring conversion). + // Only clear after we have a valid API response payload. + // eslint-disable-next-line no-console + console.log("[Notifications] Applying timestamps:", nextNotifications); + + const plugin = DailyNotification as unknown as { + clearAllNotifications?: () => Promise; + scheduleNotifications?: (options: { + timestamps: number[]; + }) => Promise; + cancelAllNotifications?: () => Promise; + }; + + if (typeof plugin.clearAllNotifications === "function") { + await plugin.clearAllNotifications(); + } else if (typeof plugin.cancelAllNotifications === "function") { + // Back-compat: older builds expose cancelAllNotifications. + await plugin.cancelAllNotifications(); + } else { + logger.warn( + "[NativeNotificationService] No clearAllNotifications/cancelAllNotifications on plugin; cannot replace schedule", + ); + return; + } + + if (typeof plugin.scheduleNotifications !== "function") { + logger.warn( + "[NativeNotificationService] scheduleNotifications not available on plugin; cannot apply timestamps", + ); + return; + } + + await plugin.scheduleNotifications({ timestamps }); } catch (err) { logger.error("[NativeNotificationService] Refresh failed", err); }