Show a changelog in the update banner

changelog.json is a hand-maintained, newest-first list of user-visible
changes; it is part of the SW asset cache and the build hash, so even a
changelog-only edit rolls a new version.

When the update banner appears, the app fetches changelog.json twice:
the plain URL is answered cache-first by the old controlling worker
(the running build's copy) while a cache-busting query bypasses every
SW cache and hits the network (the waiting build's copy). Entries the
fresh copy has that the cached one lacks are exactly what the update
brings, and are listed under the banner message.
This commit is contained in:
Alexander Heldt
2026-07-12 17:29:32 +00:00
parent 2b4731185c
commit 094af3906d
6 changed files with 68 additions and 11 deletions
+35
View File
@@ -2149,9 +2149,44 @@
location.reload();
});
// What the waiting build changes compared to the running one. The plain
// URL is answered by the *old* controlling worker cache-first, i.e. the
// loaded build's changelog; the cache-busting query misses every SW cache
// and hits the network, i.e. the new build's changelog. Entries in the
// fresh copy that the cached one lacks are exactly "new since this build".
async function changelogDiff() {
const load = async (url) => {
try {
const res = await fetch(url);
if (!res.ok) return null;
const body = await res.json();
return Array.isArray(body) ? body : null;
} catch {
return null;
}
};
const current = await load("changelog.json");
const fresh = await load(`changelog.json?v=${Date.now()}`);
if (!fresh) return [];
const seen = new Set((current || []).map(e => e && e.text));
return fresh.filter(e => e && e.text && !seen.has(e.text));
}
function showUpdateBanner(worker) {
waitingWorker = worker;
updateBanner.hidden = false;
const list = document.getElementById("update-changelog");
list.hidden = true;
list.innerHTML = "";
changelogDiff().then(entries => {
if (entries.length === 0) return;
for (const e of entries.slice(0, 6)) {
const li = document.createElement("li");
li.textContent = e.text;
list.appendChild(li);
}
list.hidden = false;
});
}
updateReload.addEventListener("click", () => {