/**
* Updates the loading screen progress bar and percent label.
* No-op once the app has faded the loader out.
* @param {number} done - Number of files refreshed so far.
* @param {number} total - Total number of files to refresh.
* @returns {void}
*/
function updateLoaderProgress(done, total) {
const percent = Math.round((done / total) * 100);
const label = document.querySelector('.page-loader-percent');
if (label) {
label.textContent = `${percent}%`;
}
const fill = document.querySelector('.page-loader-bar-fill');
if (fill) {
fill.style.width = `${percent}%`;
}
}
/**
* Fetches the asset manifest from pwa/manifest.php and revalidates each
* file. Requests pass through the service worker's network-first handler,
* so every response refreshes the offline fallback cache, covering .js,
* .css and .json files. Progress is reported to the loading screen.
* @returns {Promise<void>}
*/
async function refreshAssets() {
let files;
try {
const response = await fetch('./pwa/manifest.php');
if (!response.ok) return;
files = await response.json();
} catch (error) {
return;
}
for (let i = 0; i < files.length; i++) {
try {
// 'reload' bypasses the browser's heuristic HTTP cache and always
// revalidates against the network, so the service worker's offline
// cache is refreshed with the current file (keeping the same URL
// as cache key).
await fetch(files[i], { cache: 'reload' });
} catch (error) {
// Offline: the service worker serves the cached copy.
}
updateLoaderProgress(i + 1, files.length);
}
}
/**
* Registers the root service worker and handles cache invalidation.
* The browser re-checks sw.js on every load; when an updated worker
* takes over an already-controlled page, the page reloads once so the
* new code and assets are picked up behind the loading screen.
* The asset refresh is skipped when 'pwa-skip-refresh' is set to
* 'switching' in sessionStorage (done by user switching, which reloads
* into an unchanged app; index.php displays "Switching User..." for it).
* @returns {Promise<void>}
*/
export async function initPWA() {
if (!('serviceWorker' in navigator)) return;
const hadController = Boolean(navigator.serviceWorker.controller);
try {
const registration = await navigator.serviceWorker.register('./sw.js');
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (hadController) {
window.location.reload();
}
});
registration.update().catch((error) => console.error('PWA update check failed:', error));
await navigator.serviceWorker.ready;
if (sessionStorage.getItem('pwa-skip-refresh') === 'switching') {
sessionStorage.removeItem('pwa-skip-refresh');
return;
}
refreshAssets();
} catch (error) {
console.error('PWA registration failed:', error);
}
}