// Falah PWA Service Worker const CACHE = "falah-cache-v1"; const IMMUTABLE_CACHE = "falah-immutable-v1"; // Assets to pre-cache on install const PRECACHE_ASSETS = [ "/mobile/manifest.json", "/mobile/icons/icon-192x192.png", "/mobile/icons/icon-512x512.png", "/mobile/apple-touch-icon.png", "/mobile/offline" ]; // Install: pre-cache core assets self.addEventListener("install", (event) => { event.waitUntil( caches.open(IMMUTABLE_CACHE).then((cache) => cache.addAll(PRECACHE_ASSETS)) ); self.skipWaiting(); }); // Activate: clean old caches self.addEventListener("activate", (event) => { event.waitUntil( caches.keys().then((keys) => Promise.all( keys .filter((k) => k !== CACHE && k !== IMMUTABLE_CACHE) .map((k) => caches.delete(k)) ) ) ); self.clients.claim(); }); // Fetch: network-first for pages, cache-first for static assets self.addEventListener("fetch", (event) => { const { request } = event; const url = new URL(request.url); // Only handle same-origin /mobile requests if (!url.pathname.startsWith("/mobile")) return; // API calls — never cache if (url.pathname.startsWith("/mobile/api/")) return; // Static assets (icons, fonts, images) — cache-first if ( url.pathname.match(/\.(png|jpg|jpeg|gif|svg|ico|webp|woff2?|css)$/) ) { event.respondWith(cacheFirst(request)); return; } // Navigation & pages — network-first with fallback if (request.mode === "navigate") { event.respondWith(networkFirst(request)); return; } // Everything else — network-only event.respondWith(fetch(request).catch(() => caches.match(request))); }); async function networkFirst(request) { try { const response = await fetch(request); if (response.ok) { const cache = await caches.open(CACHE); cache.put(request, response.clone()); } return response; } catch { const cached = await caches.match(request); if (cached) return cached; // Offline fallback page return caches.match("/mobile/offline"); } } async function cacheFirst(request) { const cached = await caches.match(request); if (cached) return cached; try { return await fetch(request); } catch { return new Response("", { status: 408 }); } }