Files
takeyourmeds/frontend/lib/updates.js
T
Frank Schwenk 7cec3030c1 feat: PWA update strategy with hashed assets and cache control
Move frontend sources to frontend/, build content-hashed assets at deploy,
network-first SW for shell/JS, immutable cache for /assets/, version.json
update banner, and Cache-Control middleware. Docs in docs/PWA-STRATEGY.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 08:51:12 +02:00

63 lines
1.7 KiB
JavaScript

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();
}