// Offline app shell for the installed PWA. Only the player's own files are cached: // feeds must stay fresh, and audio (range requests, often hundreds of MB) and the // /fips/ gateway always go straight to the network. const CACHE = 'podsteadr-player-v1'; const SHELL = ['./', 'manifest.webmanifest', 'icons/icon.svg', 'icons/icon-192.png', 'icons/icon-512.png']; self.addEventListener('install', (event) => { event.waitUntil(caches.open(CACHE).then((cache) => cache.addAll(SHELL)).then(() => self.skipWaiting())); }); self.addEventListener('activate', (event) => { event.waitUntil( caches .keys() .then((keys) => Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))) .then(() => self.clients.claim()), ); }); self.addEventListener('fetch', (event) => { const { request } = event; const url = new URL(request.url); const scope = new URL(self.registration.scope); if ( request.method !== 'GET' || url.origin !== scope.origin || url.pathname.startsWith(`${scope.pathname}fips/`) || request.headers.has('range') ) { return; } // Page loads: network first so a deploy shows up immediately; cached shell when offline. if (request.mode === 'navigate') { event.respondWith( fetch(request) .then((res) => { const copy = res.clone(); caches.open(CACHE).then((cache) => cache.put('./', copy)); return res; }) .catch(() => caches.match('./')), ); return; } // Hashed build assets and icons never change under the same URL: cache first. event.respondWith( caches.match(request).then( (hit) => hit || fetch(request).then((res) => { if (res.ok) { const copy = res.clone(); caches.open(CACHE).then((cache) => cache.put(request, copy)); } return res; }), ), ); });