/* Service worker for the PolyMeter mobile app (mobile.html). * * Deliberately minimal and non-intrusive: it only manages its OWN app-shell URLs * (the page, manifest, icons). For every other request it does NOT call * respondWith(), so the rest of the site behaves exactly as if no SW existed. * * Strategy for the shell: network-first, fall back to cache. The page is a single * self-contained file that is version-stamped on deploy, so when the device is * online it always gets the freshest build; offline it still launches from cache. */ const CACHE = "polymeter-mobile-v2"; const SHELL = [ "/mobile.html", "/mobile-sessions.html", "/manifest.webmanifest", "/icon-192.png", "/icon-512.png", "/icon-180.png", ]; const SHELL_PATHS = new Set(SHELL); self.addEventListener("install", (e) => { self.skipWaiting(); e.waitUntil(caches.open(CACHE).then((c) => c.addAll(SHELL)).catch(() => {})); }); self.addEventListener("activate", (e) => { e.waitUntil( caches.keys() .then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))) .then(() => self.clients.claim()) ); }); self.addEventListener("fetch", (e) => { const req = e.request; if (req.method !== "GET") return; const url = new URL(req.url); if (url.origin !== self.location.origin) return; // Treat any navigation to /mobile.html (with or without ?standalone=1 etc.) as the shell. const path = url.pathname; if (!SHELL_PATHS.has(path)) return; // not ours — let the browser handle it e.respondWith( fetch(req) .then((res) => { if (res && res.ok) { const copy = res.clone(); caches.open(CACHE).then((c) => c.put(path, copy)); } return res; }) .catch(() => caches.match(path).then((hit) => hit || caches.match("/mobile.html"))) ); });