From 1b4f4a674ac533c0b51260ba35ab91dd2cf9486d Mon Sep 17 00:00:00 2001 From: Ariel Costas Guerrero Date: Thu, 2 Apr 2026 12:38:10 +0200 Subject: Basic push notification system for service alerts Co-authored-by: Copilot --- src/frontend/app/utils/idb.ts | 83 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 src/frontend/app/utils/idb.ts (limited to 'src/frontend/app/utils') diff --git a/src/frontend/app/utils/idb.ts b/src/frontend/app/utils/idb.ts new file mode 100644 index 0000000..4d0aba7 --- /dev/null +++ b/src/frontend/app/utils/idb.ts @@ -0,0 +1,83 @@ +/** + * IndexedDB helpers for sharing data with the service worker. + * + * The service worker is a classic script and cannot import ES modules, so it + * contains its own equivalent implementation. Keep the schema (DB name, version, + * store names, and key shapes) in sync with the inline IDB code in pwa-worker.js. + * + * DB: "enmarcha-sw", version 1 + * Store "favorites" — { key: string, ids: string[] } + * Store "alertState" — { alertId: string, silenced: boolean, lastVersion: number } + */ + +const DB_NAME = "enmarcha-sw"; +const DB_VERSION = 1; + +function openDb(): Promise { + return new Promise((resolve, reject) => { + const req = indexedDB.open(DB_NAME, DB_VERSION); + + req.onupgradeneeded = () => { + const db = req.result; + if (!db.objectStoreNames.contains("favorites")) { + db.createObjectStore("favorites", { keyPath: "key" }); + } + if (!db.objectStoreNames.contains("alertState")) { + db.createObjectStore("alertState", { keyPath: "alertId" }); + } + }; + + req.onsuccess = () => resolve(req.result); + req.onerror = () => reject(req.error); + }); +} + +/** Persist a favourites list to IndexedDB so the service worker can read it. */ +export async function writeFavorites( + key: string, + ids: string[] +): Promise { + const db = await openDb(); + return new Promise((resolve, reject) => { + const tx = db.transaction("favorites", "readwrite"); + tx.objectStore("favorites").put({ key, ids }); + tx.oncomplete = () => { + db.close(); + resolve(); + }; + tx.onerror = () => reject(tx.error); + }); +} + +/** Read a favourites list from IndexedDB. */ +export async function readFavorites(key: string): Promise { + const db = await openDb(); + return new Promise((resolve, reject) => { + const tx = db.transaction("favorites", "readonly"); + const req = tx.objectStore("favorites").get(key); + req.onsuccess = () => { + db.close(); + resolve( + (req.result as { key: string; ids: string[] } | undefined)?.ids ?? [] + ); + }; + req.onerror = () => reject(req.error); + }); +} + +/** Persist per-alert notification state (silenced flag and last notified version). */ +export async function writeAlertState( + alertId: string, + state: { silenced: boolean; lastVersion: number } +): Promise { + const db = await openDb(); + return new Promise((resolve, reject) => { + const tx = db.transaction("alertState", "readwrite"); + tx.objectStore("alertState").put({ alertId, ...state }); + tx.oncomplete = () => { + db.close(); + resolve(); + }; + tx.onerror = () => reject(tx.error); + }); +} -- cgit v1.3