const CACHE = "tym-v1.0.1"; const ASSETS = [ "/", "/index.html", "/styles.css", "/app.js", "/manifest.json", "/version.json", "/icon.png", "/icon-192x192.png", "/icon-512x512.png", "/lib/api.js", "/lib/auth.js", "/lib/badge.js", "/lib/offlineQueue.js", "/lib/pwa.js", "/lib/ui.js", ]; self.addEventListener("install", (event) => { event.waitUntil( caches.open(CACHE).then((c) => c.addAll(ASSETS)).then(() => self.skipWaiting()) ); }); self.addEventListener("activate", (event) => { event.waitUntil( caches.keys().then((names) => Promise.all(names.filter((n) => n !== CACHE).map((n) => caches.delete(n))) ).then(() => self.clients.claim()) ); }); self.addEventListener("fetch", (event) => { const { request } = event; if (request.method !== "GET") return; const url = new URL(request.url); if (url.origin !== self.location.origin) return; if (url.pathname.startsWith("/api/")) { event.respondWith(fetch(request).catch(() => new Response(JSON.stringify({ offline: true }), { status: 503, headers: { "Content-Type": "application/json" }, }))); return; } if (request.mode === "navigate") { event.respondWith( fetch(request).catch(() => caches.match("/index.html")) ); return; } event.respondWith( caches.match(request, { ignoreSearch: true }).then((cached) => cached || fetch(request).then((resp) => { if (resp.ok) { const clone = resp.clone(); caches.open(CACHE).then((c) => c.put(request, clone)); } return resp; }) ) ); }); self.addEventListener("push", (event) => { if (!event.data) return; let data = {}; try { data = event.data.json(); } catch { data = { body: event.data.text() }; } const options = { body: data.body || "Med-Time!", icon: "/icon-192x192.png", badge: "/icon-72x72.png", silent: data.silent !== false, tag: data.tag || "med-reminder", renotify: true, data: { slot_id: data.slot_id }, actions: [ { action: "take", title: "Genommen ✓" }, { action: "snooze15", title: "+15 Min" }, { action: "snooze30", title: "+30 Min" }, ], }; event.waitUntil( self.registration.showNotification(data.title || "TakeYourMeds", options) ); }); async function apiPost(path, body) { const clients = await self.clients.matchAll({ type: "window" }); for (const client of clients) { client.postMessage({ type: "API_ACTION", path, body }); return; } } self.addEventListener("notificationclick", (event) => { event.notification.close(); const slotId = event.notification.data?.slot_id; const action = event.action; if (action === "take" && slotId) { event.waitUntil( self.clients.openWindow(`/?action=take&slot=${slotId}`) ); return; } if ((action === "snooze15" || action === "snooze30") && slotId) { const minutes = action === "snooze15" ? 15 : 30; event.waitUntil( self.clients.openWindow(`/?action=snooze&slot=${slotId}&minutes=${minutes}`) ); return; } event.waitUntil(self.clients.openWindow("/")); }); self.addEventListener("sync", (event) => { if (event.tag === "sync-logs") { event.waitUntil( self.clients.matchAll().then((list) => { list.forEach((c) => c.postMessage({ type: "SYNC_QUEUE" })); }) ); } });