let bannerVisible = false; function showUpdateBanner() { if (bannerVisible) return; const banner = document.getElementById("updateBanner"); if (!banner) return; banner.hidden = false; bannerVisible = true; } async function applyUpdate() { const reg = await navigator.serviceWorker?.getRegistration(); if (reg?.waiting) { reg.waiting.postMessage({ type: "SKIP_WAITING" }); } else { await reg?.update(); location.reload(); } } export function wireAppUpdates() { if (!("serviceWorker" in navigator)) return; let refreshing = false; navigator.serviceWorker.addEventListener("controllerchange", () => { if (refreshing) return; refreshing = true; location.reload(); }); navigator.serviceWorker.addEventListener("message", (event) => { if (event.data?.type === "UPDATE_AVAILABLE") showUpdateBanner(); }); const button = document.getElementById("updateBannerBtn"); button?.addEventListener("click", () => applyUpdate()); const checkVersion = async () => { try { const reg = await navigator.serviceWorker.getRegistration(); await reg?.update(); const built = document.querySelector('meta[name="app-build"]')?.content; if (!built) return; const res = await fetch("/version.json", { cache: "no-store" }); if (!res.ok) return; const data = await res.json(); if (data.buildHash && data.buildHash !== built) { showUpdateBanner(); } } catch { /* ignore — offline or transient */ } }; window.addEventListener("focus", checkVersion); document.addEventListener("visibilitychange", () => { if (document.visibilityState === "visible") checkVersion(); }); checkVersion(); }