diff --git a/static/pwa.js b/static/pwa.js index 3214f4d..9fba113 100644 --- a/static/pwa.js +++ b/static/pwa.js @@ -4,7 +4,8 @@ const Pwa = (() => { const DISMISS_KEY = "pwa-hint-dismissed"; let deferredPrompt = null; let refreshInstallHint = null; - let hintBound = false; + let dismissListenerBound = false; + let initialized = false; function isStandalone() { return window.matchMedia("(display-mode: standalone)").matches @@ -44,40 +45,42 @@ const Pwa = (() => { } function hideHint(hint, { persist = true } = {}) { + if (!hint) return; if (persist) { try { localStorage.setItem(DISMISS_KEY, "1"); } catch { - /* private mode with storage blocked – still hide for this session */ + /* storage blocked – still hide for this session */ } } hint.hidden = true; hint.classList.add("is-dismissed"); + hint.style.setProperty("display", "none", "important"); } function showHint(hint) { + if (!hint) return; hint.hidden = false; hint.classList.remove("is-dismissed"); + hint.style.removeProperty("display"); } - function bindHintActions(hint) { - if (hintBound) return; - hintBound = true; + function bindDismissListener() { + if (dismissListenerBound) return; + dismissListenerBound = true; - hint.addEventListener("click", (event) => { - if (event.target.closest(".pwa-hint-dismiss")) { - event.preventDefault(); - hideHint(hint); - } - }); + document.addEventListener("click", (event) => { + if (!event.target.closest("#pwa-hint-dismiss, .pwa-hint-dismiss")) return; + event.preventDefault(); + event.stopPropagation(); + hideHint(document.getElementById("pwa-install-hint")); + }, true); } function setupInstallHint() { const hint = document.getElementById("pwa-install-hint"); if (!hint) return null; - bindHintActions(hint); - if (isStandalone()) { hideHint(hint, { persist: false }); return null; @@ -89,9 +92,10 @@ const Pwa = (() => { const body = document.getElementById("pwa-hint-body"); const installBtn = document.getElementById("pwa-install-btn"); - const dismissBtn = hint.querySelector(".pwa-hint-dismiss"); + const dismissBtn = document.getElementById("pwa-hint-dismiss"); const updateHintText = () => { + if (typeof t !== "function") return; const title = hint.querySelector("[data-i18n='pwa.hintTitle']"); if (title) title.textContent = t("pwa.hintTitle"); if (body && !deferredPrompt) body.textContent = t(platformHintKey()); @@ -129,6 +133,8 @@ const Pwa = (() => { } async function init() { + if (initialized) return; + initialized = true; refreshInstallHint = setupInstallHint(); await registerServiceWorker(); } @@ -137,6 +143,8 @@ const Pwa = (() => { refreshInstallHint?.(); } + bindDismissListener(); + return { init, refreshHint }; })(); diff --git a/static/sw.js b/static/sw.js index 62a01a9..16292b7 100644 --- a/static/sw.js +++ b/static/sw.js @@ -1,4 +1,4 @@ -const CACHE = "if-viewer-static-v5"; +const CACHE = "if-viewer-static-v6"; const ASSETS = [ "/static/style.css", "/static/favicon.svg", @@ -10,6 +10,11 @@ const ASSETS = [ "/static/locales/de.json", ]; +const NETWORK_FIRST = new Set([ + "/static/pwa.js", + "/static/style.css", +]); + self.addEventListener("install", (event) => { event.waitUntil( caches.open(CACHE) @@ -33,6 +38,21 @@ self.addEventListener("fetch", (event) => { if (url.pathname.includes("/api/")) return; if (!url.pathname.startsWith("/static/")) return; + if (NETWORK_FIRST.has(url.pathname)) { + event.respondWith( + fetch(event.request) + .then((response) => { + if (response.ok) { + const copy = response.clone(); + caches.open(CACHE).then((cache) => cache.put(event.request, copy)); + } + return response; + }) + .catch(() => caches.match(event.request)) + ); + return; + } + event.respondWith( caches.match(event.request).then((cached) => cached || fetch(event.request)) ); diff --git a/templates/_pwa_head.html b/templates/_pwa_head.html index 7242a7b..a92d8b9 100644 --- a/templates/_pwa_head.html +++ b/templates/_pwa_head.html @@ -3,4 +3,4 @@ - + diff --git a/templates/_pwa_hint.html b/templates/_pwa_hint.html index a8095f8..2f37d19 100644 --- a/templates/_pwa_hint.html +++ b/templates/_pwa_hint.html @@ -5,6 +5,6 @@
- +