'use strict'; // --------------------------------------------------------------------------- // Service Worker // --------------------------------------------------------------------------- if ('serviceWorker' in navigator) { window.addEventListener('load', () => { // Registered from '/sw.js' (root), not '/static/js/sw.js': a service // worker's default scope is its own script's directory, so registering // it from under /static/js/ would only ever let it control requests // under that path — page navigations to '/', '/books/' etc. would never // be intercepted, breaking offline entirely. navigator.serviceWorker.register('/sw.js', {scope: '/'}).catch(err => { console.warn('Service worker registration failed:', err); }); // Drop any leftover registration from the old, wrongly-scoped '/static/js/sw.js' // path so it doesn't keep running or fight the new root-scoped worker. navigator.serviceWorker.getRegistrations().then(regs => { regs.forEach(r => { if (r.scope.endsWith('/static/js/')) r.unregister(); }); }); }); // When a new SW activates it sends SW_ACTIVATED — reload to get fresh assets, // but never in the middle of something: not while a book is open, radio or a // podcast plays, or a text field has the focus — then as soon as that's over. let reloadPending = false; const busy = () => { if (currentBookId || isPlaying) return true; const host = window.DioraPodcastHost; if (host && host.playerState) { try { if (JSON.parse(host.playerState()).playing) return true; } catch (_) {} } const el = document.activeElement; return !!el && (el.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName)); }; const reloadWhenIdle = () => { if (busy()) setTimeout(reloadWhenIdle, 5000); else window.location.reload(); }; navigator.serviceWorker.addEventListener('message', e => { if (e.data?.type === 'SW_ACTIVATED' && !reloadPending) { reloadPending = true; reloadWhenIdle(); } }); }