diff --git a/src/components/dev/NotificationDebugPanel.vue b/src/components/dev/NotificationDebugPanel.vue
index 63b40a30..2b2d61d1 100644
--- a/src/components/dev/NotificationDebugPanel.vue
+++ b/src/components/dev/NotificationDebugPanel.vue
@@ -88,25 +88,6 @@
Manually mints and uploads 100 delegated day JWTs. Requires an active
did:ethr identity and JWT authentication; Test Mode is not used.
-
- Local simulation only — calls the refresh API directly (no FCM push).
-
- Full pipeline — backend `/debug/send-wakeup` → FCM → WAKEUP_PING
- handler.
+ FCM delivery diagnostic only — backend `/debug/send-wakeup`. The app
+ no longer schedules api_* notifications from WAKEUP_PING.
@@ -177,70 +158,6 @@
-
-
-
Mock Timing Presets
-
-
- {{ preset.label }}
-
-
-
- Selected interval: {{ intervalLabel }}
-
-
-
-
-
-
Mock Refresh Controls
-
- Trigger Mock Refresh
-
-
-
-
-
-
Wakeup Ping Simulator
-
- Exercises the production push handler (not the refresh API shortcut
- above).
-
-
- Simulate WAKEUP_PING
-
-
-
-
-
-
Flood Test
-
- Run 20 Refreshes
-
-
-
@@ -358,14 +275,6 @@ type PendingInfo = {
wallClockSource?: string | null;
};
-const presets = [
- { label: "30 sec", ms: 30_000 },
- { label: "1 min", ms: 60_000 },
- { label: "5 min", ms: 5 * 60_000 },
- { label: "10 min", ms: 10 * 60_000 },
-];
-
-const intervalMs = ref
(60_000);
const busy = ref(false);
const pending = ref([]);
const pendingInspectorMessage = ref(null);
@@ -395,11 +304,6 @@ const truncatedFcmToken = computed(() => {
const eventLog = ref([]);
let unsubscribeEventLog: (() => void) | undefined;
-const intervalLabel = computed(() => {
- const preset = presets.find((p) => p.ms === intervalMs.value);
- return preset?.label ?? `${intervalMs.value}ms`;
-});
-
function formatIsoMs(ms: number | null | undefined): string {
if (ms == null || !Number.isFinite(ms)) {
return "";
@@ -423,27 +327,6 @@ async function refreshPending(): Promise {
pendingInspectorMessage.value = result.inspectorUnavailableMessage ?? null;
}
-async function onMockRefresh(): Promise {
- await withBusy(async () => {
- await NotificationDebugService.triggerMockRefresh(intervalMs.value);
- await refreshPending();
- });
-}
-
-async function onWakeupPing(): Promise {
- await withBusy(async () => {
- await NotificationDebugService.simulateWakeupPing();
- await refreshPending();
- });
-}
-
-async function onFloodTest(): Promise {
- await withBusy(async () => {
- await NotificationDebugService.runFloodTest(intervalMs.value);
- await refreshPending();
- });
-}
-
async function onClearNotifications(): Promise {
await withBusy(async () => {
await NotificationDebugService.clearNotifications();
@@ -514,20 +397,6 @@ async function onUploadAlertAuthorization(): Promise {
});
}
-async function onBackendRefresh(): Promise {
- await withBusy(async () => {
- await NotificationDebugService.triggerBackendRefresh();
- await refreshPending();
- });
-}
-
-async function onSimulateWakeupRefresh(): Promise {
- await withBusy(async () => {
- await NotificationDebugService.simulateWakeupViaRefresh();
- await refreshPending();
- });
-}
-
function formatRealWakeupStatusMessage(
result: Awaited<
ReturnType
diff --git a/src/composables/useNotifications.ts b/src/composables/useNotifications.ts
deleted file mode 100644
index 2f04ec59..00000000
--- a/src/composables/useNotifications.ts
+++ /dev/null
@@ -1,134 +0,0 @@
-/* eslint-disable @typescript-eslint/no-unused-vars */
-import { inject, onBeforeUnmount, onMounted } from "vue";
-import { NotificationIface } from "../constants/app";
-import { registerToken } from "@/services/notifications/NotificationService";
-import { refreshNotifications } from "@/services/notifications/NativeNotificationService";
-
-/**
- * Vue 3 composable for notifications
- * Provides a concise API for common notification patterns
- */
-
-export const NOTIFICATION_TIMEOUTS = {
- BRIEF: 1000, // Very brief toasts ("Sent..." messages)
- SHORT: 2000, // Short notifications (clipboard copies, quick confirmations)
- STANDARD: 3000, // Standard notifications (success messages, general info)
- LONG: 5000, // Longer notifications (errors, warnings, important info)
- VERY_LONG: 7000, // Very long notifications (complex operations)
- MODAL: -1, // Modal confirmations (no auto-dismiss)
-} as const;
-
-export function useNotifications() {
- // Inject the notify function from the app
- const notify =
- inject<(notification: NotificationIface, timeout?: number) => void>(
- "notify",
- );
-
- if (!notify) {
- throw new Error(
- "useNotifications must be used within a component that has $notify available",
- );
- }
-
- let refreshTimer: number | undefined = undefined;
- let refreshInFlight: Promise | null = null;
-
- async function refreshNotificationsDebounced(): Promise {
- if (refreshTimer != null) {
- window.clearTimeout(refreshTimer);
- }
- refreshTimer = window.setTimeout(() => {
- if (!refreshInFlight) {
- refreshInFlight = refreshNotifications().finally(() => {
- refreshInFlight = null;
- });
- }
- }, 300);
- }
-
- const onResume = () => {
- void refreshNotificationsDebounced();
- };
-
- onMounted(() => {
- void refreshNotificationsDebounced();
- document.addEventListener("resume", onResume);
- });
-
- onBeforeUnmount(() => {
- document.removeEventListener("resume", onResume);
- if (refreshTimer != null) {
- window.clearTimeout(refreshTimer);
- }
- });
-
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function success(_notification: NotificationIface, _timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function error(_notification: NotificationIface, _timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function warning(_notification: NotificationIface, _timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function info(_notification: NotificationIface, _timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function toast(_title: string, _text?: string, _timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function copied(_item: string, _timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function sent(_timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function confirm(
- _text: string,
- _onYes: () => Promise,
- _timeout?: number,
- ) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function confirmationSubmitted(_timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function genericError(_timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function genericSuccess(_timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function alreadyConfirmed(_timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function cannotConfirmIssuer(_timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function cannotConfirmHidden(_timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function notRegistered(_timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function notAGive(_timeout?: number) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function notificationOff(
- _title: string,
- _callback: (success: boolean) => Promise,
- _timeout?: number,
- ) {}
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- function downloadStarted(_format: string = "Dexie", _timeout?: number) {}
-
- return {
- success,
- error,
- warning,
- info,
- toast,
- copied,
- sent,
- confirm,
- confirmationSubmitted,
- genericError,
- genericSuccess,
- alreadyConfirmed,
- cannotConfirmIssuer,
- cannotConfirmHidden,
- notRegistered,
- notAGive,
- notificationOff,
- downloadStarted,
- /** POST FCM token to `/notifications/register` (same as startup native hook). */
- registerFcmToken: registerToken,
- refreshNotifications: refreshNotificationsDebounced,
- };
-}
diff --git a/src/libs/endorserServer.ts b/src/libs/endorserServer.ts
index 82bc1395..e259129b 100644
--- a/src/libs/endorserServer.ts
+++ b/src/libs/endorserServer.ts
@@ -23,7 +23,7 @@ import { LRUCache } from "lru-cache";
import * as R from "ramda";
import { DEFAULT_IMAGE_API_SERVER, NotificationIface } from "../constants/app";
-import { NOTIFICATION_TIMEOUTS } from "../composables/useNotifications";
+import { NOTIFICATION_TIMEOUTS } from "../utils/notificationUtils";
import { createNotifyHelpers } from "../utils/notify";
import { NOTIFY_PERSONAL_DATA_ERROR } from "../constants/notifications";
import { Contact } from "../db/tables/contacts";
diff --git a/src/services/notifications/NativeNotificationService.ts b/src/services/notifications/NativeNotificationService.ts
index 418802e0..9e02000b 100644
--- a/src/services/notifications/NativeNotificationService.ts
+++ b/src/services/notifications/NativeNotificationService.ts
@@ -13,30 +13,8 @@
import { Capacitor } from "@capacitor/core";
import type { PushNotificationSchema } from "@capacitor/push-notifications";
-import type {
- NotificationRefreshRequest,
- NotificationRefreshResponse,
-} from "@/interfaces/notifyApi";
import { DailyNotification } from "@/plugins/DailyNotificationPlugin";
-import { getOrCreateDeviceId } from "./deviceId";
import { REMINDER_ID_DAILY_REMINDER } from "./reminderIds";
-import { configureNativeFetcherIfReady } from "./nativeFetcherConfig";
-import {
- getNotificationApiBaseUrl,
- getTestMode,
-} from "./NotificationDebugConfig";
-import {
- logRefreshFailure,
- logRefreshStarted,
- logRefreshSuccess,
- logScheduleReplacement,
-} from "./notificationLog";
-import {
- getNotificationApiHeaders,
- logSkippingRefreshDueToMissingAuth,
- notificationApiFailureMessage,
- readNotificationApiBody,
-} from "./notificationApiAuth";
import { logNotification } from "./NotificationDebugEvents";
/**
@@ -563,200 +541,14 @@ export class NativeNotificationService implements NotificationServiceInterface {
}
}
-export type RefreshNotificationsResult = {
- ok: boolean;
- scheduledCount: number;
- status?: number;
- errorMessage?: string;
-};
-
/**
- * Re-applies native API fetcher credentials (JWT pool, active DID) so background
- * notification workers can run. No UI; safe from push handlers while backgrounded.
- */
-export async function refreshNotificationsWithDiagnostics(options?: {
- source?: string;
-}): Promise {
- const startedAt = performance.now();
- const source = options?.source;
- logRefreshStarted(source);
-
- if (!Capacitor.isNativePlatform()) {
- const errorMessage = "not a native platform";
- logRefreshFailure(startedAt, errorMessage, undefined, source);
- return {
- ok: false,
- scheduledCount: 0,
- errorMessage,
- };
- }
-
- try {
- const auth = await getNotificationApiHeaders("refresh");
- if (!auth.ok) {
- logSkippingRefreshDueToMissingAuth();
- logRefreshFailure(startedAt, auth.message, undefined, source);
- return {
- ok: false,
- scheduledCount: 0,
- errorMessage: auth.message,
- };
- }
-
- let deviceId: string | undefined;
- try {
- deviceId = await getOrCreateDeviceId();
- } catch (err) {
- logger.warn(
- "[NativeNotificationService] Could not obtain deviceId; skipping refresh",
- err,
- );
- }
- if (!deviceId) {
- // The service finds the device by deviceId or fcmToken and answers 400
- // without either, so there is no request worth sending.
- const errorMessage = "no deviceId (cannot identify this device)";
- logRefreshFailure(startedAt, errorMessage, undefined, source);
- return { ok: false, scheduledCount: 0, errorMessage };
- }
-
- const body: NotificationRefreshRequest = {
- deviceId,
- platform: Capacitor.getPlatform(),
- testMode: getTestMode(),
- };
- const baseUrl = getNotificationApiBaseUrl();
- const res = await fetch(`${baseUrl}/notifications/refresh`, {
- method: "POST",
- headers: auth.headers,
- body: JSON.stringify(body),
- });
-
- if (!res.ok) {
- const errorMessage = notificationApiFailureMessage(
- res.status,
- await readNotificationApiBody(res),
- );
- logger.warn("[NativeNotificationService] refreshNotifications failed", {
- status: res.status,
- statusText: res.statusText,
- errorMessage,
- });
- logRefreshFailure(startedAt, errorMessage, res.status, source);
- return {
- ok: false,
- scheduledCount: 0,
- status: res.status,
- errorMessage,
- };
- }
-
- const payload = (await res.json()) as NotificationRefreshResponse;
- const scheduledCount = Array.isArray(payload?.nextNotifications)
- ? payload.nextNotifications.length
- : 0;
- await applyNotificationRefreshPayload(payload);
- logRefreshSuccess(startedAt, scheduledCount, source);
- return { ok: true, scheduledCount };
- } catch (err) {
- logger.error("[NativeNotificationService] Refresh failed", err);
- const message = err instanceof Error ? err.message : String(err);
- logRefreshFailure(startedAt, message, undefined, source);
- return { ok: false, scheduledCount: 0, errorMessage: message };
- }
-}
-
-export async function refreshNotifications(): Promise {
- await refreshNotificationsWithDiagnostics();
-}
-
-export type NotificationRefreshPayload = {
- shouldNotify?: boolean;
- nextNotifications?: Array<{ timestamp?: number }>;
-};
-
-// `handleCapacitorPushNotificationReceived` and `applyNotificationRefreshPayload` are used by
-// DEV notification simulation tooling; they must stay production-safe because that tooling
-// exercises real flows. (`applyNotificationRefreshPayload` is also used by production refresh.)
-
-/**
- * Apply a "refresh notifications" payload by clearing and scheduling timestamps via the native plugin.
- *
- * This is the shared implementation used by:
- * - production refresh flow (`refreshNotifications` fetching from backend)
- * - dev-only debug flows (mock refresh with local payloads)
- *
- * Important: This function intentionally mirrors production behavior and does not introduce
- * any scheduling logic in UI layers.
- */
-export async function applyNotificationRefreshPayload(
- payload: unknown,
-): Promise {
- if (!Capacitor.isNativePlatform()) {
- return;
- }
-
- const data = payload as NotificationRefreshPayload;
- const nextNotifications = data?.nextNotifications;
- if (!Array.isArray(nextNotifications)) {
- return;
- }
-
- const timestamps = nextNotifications
- .map((n) => (n as { timestamp?: unknown })?.timestamp)
- .filter((t): t is number => typeof t === "number" && Number.isFinite(t));
-
- if (timestamps.length === 0) {
- logNotification("Schedule replacement skipped (no valid timestamps)");
- return;
- }
-
- // Keep existing behavior: ensure background worker credentials are current.
- await configureNativeFetcherIfReady();
-
- logScheduleReplacement(timestamps.length);
-
- if (typeof DailyNotification.clearApiNotifications !== "function") {
- logger.warn(
- "[NativeNotificationService] API notification clear unavailable (plugin clearApiNotifications missing); cannot replace schedule",
- );
- logNotification(
- "Schedule replacement aborted (API notification clear unavailable on plugin)",
- );
- return;
- }
-
- logNotification("Clearing API notifications before refresh");
- await DailyNotification.clearApiNotifications();
- logNotification("Cleared API notifications");
-
- if (typeof DailyNotification.scheduleApiNotifications !== "function") {
- logger.warn(
- "[NativeNotificationService] scheduleApiNotifications not available on plugin; cannot apply timestamps",
- );
- logNotification(
- "Schedule replacement aborted (scheduleApiNotifications unavailable)",
- );
- return;
- }
-
- await DailyNotification.scheduleApiNotifications({ timestamps });
- logNotification(
- `Schedule replacement applied (${timestamps.length} timestamp(s))`,
- );
-}
-
-/**
- * Silent FCM/APNs data push: refresh native notification pipeline when requested by backend.
+ * Capacitor push delivery hook. Legacy WAKEUP_PING → /notifications/refresh
+ * consumption was retired; AlertSearch uses visible FCM notifications and is
+ * not handled here. Keep logging for diagnostics without scheduling api_* work.
*/
export async function handleCapacitorPushNotificationReceived(
notification: PushNotificationSchema,
): Promise {
- if (notification.data?.type === "WAKEUP_PING") {
- logNotification("WAKEUP_PING handler — invoking refresh");
- await refreshNotificationsWithDiagnostics({ source: "WAKEUP_PING" });
- return;
- }
const type =
typeof notification.data?.type === "string"
? notification.data.type
diff --git a/src/services/notifications/NotificationDebugService.ts b/src/services/notifications/NotificationDebugService.ts
index 93c514de..f01229e7 100644
--- a/src/services/notifications/NotificationDebugService.ts
+++ b/src/services/notifications/NotificationDebugService.ts
@@ -1,14 +1,12 @@
/**
* DEV-only notification testing utilities.
*
- * IMPORTANT:
- * This service intentionally routes through the same production notification
- * orchestration paths used by refresh flows, wakeup pushes, and replacement.
- * Avoid adding duplicate scheduling logic here.
+ * Legacy WAKEUP_PING → /notifications/refresh → api_* tooling was removed in
+ * the Phase 2 retirement. Remaining helpers cover FCM registration, AlertSearch
+ * authorization upload, backend URL overrides, and pending-notification inspection.
*/
import { Capacitor } from "@capacitor/core";
-import type { PushNotificationSchema } from "@capacitor/push-notifications";
import type {
DebugSendWakeupRequest,
DebugSendWakeupResponse,
@@ -38,12 +36,6 @@ import {
notificationApiFailureMessage,
readNotificationApiBody,
} from "./notificationApiAuth";
-import {
- applyNotificationRefreshPayload,
- handleCapacitorPushNotificationReceived,
- refreshNotificationsWithDiagnostics,
- type NotificationRefreshPayload,
-} from "./NativeNotificationService";
import { truncateFcmTokenForLog } from "./notificationLog";
import { DailyNotification } from "@/plugins/DailyNotificationPlugin";
import { NotificationInspector } from "@/plugins/NotificationInspectorPlugin";
@@ -179,19 +171,10 @@ export const NotificationDebugService = {
return result;
},
- async triggerBackendRefresh(): Promise {
- await refreshNotificationsWithDiagnostics({ source: "debug panel" });
- },
-
- /** Local simulation: same API call as a WAKEUP_PING handler (no push payload). */
- async simulateWakeupViaRefresh(): Promise {
- logNotification("WAKEUP_PING simulation (local refresh API only)");
- await refreshNotificationsWithDiagnostics({
- source: "WAKEUP_PING simulation",
- });
- },
-
- /** Full pipeline: backend `/debug/send-wakeup` → FCM → native WAKEUP_PING handler. */
+ /**
+ * Backend `/debug/send-wakeup` → FCM only. App no longer consumes WAKEUP_PING
+ * for api_* scheduling; kept for FCM delivery diagnostics until backend Phase 3.
+ */
async sendRealWakeupPing(): Promise {
logNotification("Real WAKEUP_PING requested");
@@ -264,62 +247,6 @@ export const NotificationDebugService = {
}
},
- generateMockNotifications(
- intervalMs: number = 60_000,
- ): NotificationRefreshPayload {
- const now = Date.now();
- const future1 = now + intervalMs;
- const future2 = now + intervalMs * 2;
-
- return {
- shouldNotify: true,
- nextNotifications: [{ timestamp: future1 }, { timestamp: future2 }],
- };
- },
-
- async triggerMockRefresh(intervalMs?: number): Promise {
- logNotification("Mock refresh requested");
-
- const payload = this.generateMockNotifications(intervalMs);
- const timestamps = payload.nextNotifications?.map((n) => n.timestamp) ?? [];
- logNotification(`Mock payload generated (${timestamps.length} timestamps)`);
-
- if (!Capacitor.isNativePlatform()) {
- logNotification("Mock refresh skipped: not running on native platform");
- return;
- }
-
- await applyNotificationRefreshPayload(payload);
- logNotification("Mock refresh applied");
- },
-
- async simulateWakeupPing(): Promise {
- logNotification("Simulating WAKEUP_PING (production push handler)");
-
- if (!Capacitor.isNativePlatform()) {
- logNotification("WAKEUP_PING simulation skipped: not native platform");
- return;
- }
-
- const notification = {
- title: "WAKEUP_PING",
- body: "",
- id: "dev_wakeup_ping",
- data: { type: "WAKEUP_PING" },
- } as unknown as PushNotificationSchema;
-
- await handleCapacitorPushNotificationReceived(notification);
- },
-
- async runFloodTest(intervalMs?: number): Promise {
- logNotification("Flood test started (20 sequential refreshes)");
- for (let i = 0; i < 20; i++) {
- logNotification(`Flood iteration ${i + 1}/20`);
- await this.triggerMockRefresh(intervalMs);
- }
- logNotification("Flood test completed");
- },
-
async clearNotifications(): Promise {
logNotification("Clear notifications (debug panel)");
diff --git a/src/services/notifications/notificationLog.ts b/src/services/notifications/notificationLog.ts
index d8dc1298..6b7d2878 100644
--- a/src/services/notifications/notificationLog.ts
+++ b/src/services/notifications/notificationLog.ts
@@ -24,9 +24,6 @@ export function logPushNotificationReceived(notification: {
title: notification.title,
dataType: type,
});
- if (type === "WAKEUP_PING") {
- logNotification("WAKEUP_PING received — will trigger refresh");
- }
}
export function logPushNotificationActionPerformed(action: {
@@ -74,44 +71,6 @@ export function logTokenRegistrationFailure(
});
}
-export function logRefreshStarted(source?: string): void {
- logNotification(source ? `Refresh started (${source})` : "Refresh started");
-}
-
-function elapsedMsSince(startedAt: number): number {
- return performance.now() - startedAt;
-}
-
-export function logRefreshSuccess(
- startedAt: number,
- scheduledCount: number,
- source?: string,
-): void {
- const elapsedMs = Math.round(elapsedMsSince(startedAt));
- const message = source
- ? `Refresh completed (${source}) in ${elapsedMs}ms (scheduled ${scheduledCount})`
- : `Refresh completed in ${elapsedMs}ms (scheduled ${scheduledCount})`;
- logNotification(message);
-}
-
-export function logRefreshFailure(
- startedAt: number,
- errorMessage: string,
- status?: number,
- source?: string,
-): void {
- const statusPart = status != null ? ` HTTP ${status}` : "";
- const elapsedMs = Math.round(elapsedMsSince(startedAt));
- const message = source
- ? `Refresh failed (${source}) in ${elapsedMs}ms: ${errorMessage}${statusPart}`
- : `Refresh failed in ${elapsedMs}ms: ${errorMessage}${statusPart}`;
- logNotification(message);
-}
-
export function logNotificationClearing(method: string): void {
logNotification(`Clearing notifications via ${method}`);
}
-
-export function logScheduleReplacement(count: number): void {
- logNotification(`Schedule replacement: ${count} notification(s)`);
-}