fix(notifications): apply backend timestamps via scheduleNotifications API
Stop converting backend timestamps to HH:mm/recurring schedules and remove createSchedule/updateSchedule reconciliation. After a successful refresh payload, clear existing notifications and schedule exact timestamps via the plugin scheduleNotifications API (with back-compat clear fallback) to prevent drift.
This commit is contained in:
@@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
import { Capacitor } from "@capacitor/core";
|
import { Capacitor } from "@capacitor/core";
|
||||||
import type { PushNotificationSchema } from "@capacitor/push-notifications";
|
import type { PushNotificationSchema } from "@capacitor/push-notifications";
|
||||||
import { ScheduleKind } from "@timesafari/daily-notification-plugin";
|
|
||||||
import { DailyNotification } from "@/plugins/DailyNotificationPlugin";
|
import { DailyNotification } from "@/plugins/DailyNotificationPlugin";
|
||||||
import { REMINDER_ID_DAILY_REMINDER } from "./reminderIds";
|
import { REMINDER_ID_DAILY_REMINDER } from "./reminderIds";
|
||||||
import { configureNativeFetcherIfReady } from "./nativeFetcherConfig";
|
import { configureNativeFetcherIfReady } from "./nativeFetcherConfig";
|
||||||
@@ -577,52 +576,54 @@ export async function refreshNotifications(): Promise<void> {
|
|||||||
|
|
||||||
const nextNotifications = (data as { nextNotifications?: unknown })
|
const nextNotifications = (data as { nextNotifications?: unknown })
|
||||||
?.nextNotifications;
|
?.nextNotifications;
|
||||||
|
|
||||||
// Keep existing behavior: ensure background worker credentials are current.
|
|
||||||
await configureNativeFetcherIfReady();
|
|
||||||
|
|
||||||
if (!Array.isArray(nextNotifications)) {
|
if (!Array.isArray(nextNotifications)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Full replacement: clear prior scheduled notifications before applying backend schedule.
|
const timestamps = nextNotifications
|
||||||
// (Plugin API name is cancelAllNotifications; equivalent intent to "clearAllNotifications".)
|
.map((n) => (n as { timestamp?: unknown })?.timestamp)
|
||||||
await DailyNotification.cancelAllNotifications();
|
.filter((t): t is number => typeof t === "number" && Number.isFinite(t));
|
||||||
|
|
||||||
// Apply backend schedule to the native scheduler as recurring notifications.
|
if (timestamps.length === 0) {
|
||||||
// The plugin's schedule API is cron/clockTime-based (recurring), so we map
|
return;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dt = new Date(timestamp);
|
// Keep existing behavior: ensure background worker credentials are current.
|
||||||
const hh = dt.getHours().toString().padStart(2, "0");
|
await configureNativeFetcherIfReady();
|
||||||
const mm = dt.getMinutes().toString().padStart(2, "0");
|
|
||||||
const clockTime = `${hh}:${mm}`;
|
|
||||||
const id = `backend-${timestamp}`;
|
|
||||||
|
|
||||||
const existing = await DailyNotification.getSchedule(id);
|
// Backend is source of truth: apply exact timestamps (no HH:mm / recurring conversion).
|
||||||
if (existing) {
|
// Only clear after we have a valid API response payload.
|
||||||
await DailyNotification.updateSchedule(id, {
|
// eslint-disable-next-line no-console
|
||||||
clockTime,
|
console.log("[Notifications] Applying timestamps:", nextNotifications);
|
||||||
enabled: true,
|
|
||||||
});
|
const plugin = DailyNotification as unknown as {
|
||||||
} else {
|
clearAllNotifications?: () => Promise<void>;
|
||||||
// DailyNotification plugin typings currently omit `id` on CreateScheduleInput,
|
scheduleNotifications?: (options: {
|
||||||
// but runtime supports deterministic IDs for schedule reconciliation.
|
timestamps: number[];
|
||||||
await DailyNotification.createSchedule({
|
}) => Promise<void>;
|
||||||
id,
|
cancelAllNotifications?: () => Promise<void>;
|
||||||
kind: ScheduleKind.NOTIFY,
|
};
|
||||||
clockTime,
|
|
||||||
enabled: true,
|
if (typeof plugin.clearAllNotifications === "function") {
|
||||||
} as unknown as never);
|
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) {
|
} catch (err) {
|
||||||
logger.error("[NativeNotificationService] Refresh failed", err);
|
logger.error("[NativeNotificationService] Refresh failed", err);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user