Add tap-to-update button on version footer to force-refresh cached assets
Phones stuck on stale service-worker caches (esp. iOS home-screen PWAs) could show the new version string while still running old app.js/css. Tapping the footer now unregisters the service worker, clears all caches, and reloads to pull the latest build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "shelem",
|
"name": "shelem",
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"description": "Shelem card game — multiplayer",
|
"description": "Shelem card game — multiplayer",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+22
-2
@@ -1700,20 +1700,40 @@ async function validateAuth() {
|
|||||||
updateAuthBar();
|
updateAuthBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the running app version in the lobby footer (served from /api/config)
|
// Show the running app version in the lobby footer (served from /api/config).
|
||||||
|
// Tapping it force-updates: clears the service-worker caches and reloads, so a
|
||||||
|
// phone stuck on stale cached assets can pull the latest build.
|
||||||
async function loadAppVersion() {
|
async function loadAppVersion() {
|
||||||
try {
|
try {
|
||||||
const d = await fetch('/api/config').then(r => r.json());
|
const d = await fetch('/api/config').then(r => r.json());
|
||||||
if (d.version) $('app-version').textContent = `v${d.version}`;
|
if (d.version) $('app-version').textContent = `v${d.version} · tap to update`;
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function forceUpdate() {
|
||||||
|
const el = $('app-version');
|
||||||
|
if (el) el.textContent = 'Updating…';
|
||||||
|
try {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
const regs = await navigator.serviceWorker.getRegistrations();
|
||||||
|
await Promise.all(regs.map(r => r.unregister()));
|
||||||
|
}
|
||||||
|
if (window.caches) {
|
||||||
|
const keys = await caches.keys();
|
||||||
|
await Promise.all(keys.map(k => caches.delete(k)));
|
||||||
|
}
|
||||||
|
} catch { /* ignore */ }
|
||||||
|
// SW gone and caches cleared — a plain reload now refetches everything fresh
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Boot ─────────────────────────────────────────────────────
|
// ─── Boot ─────────────────────────────────────────────────────
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
initLobby();
|
initLobby();
|
||||||
wireGameEvents();
|
wireGameEvents();
|
||||||
applyBarBottom();
|
applyBarBottom();
|
||||||
loadAppVersion();
|
loadAppVersion();
|
||||||
|
$('app-version').addEventListener('click', forceUpdate);
|
||||||
|
|
||||||
// Drop any stale/corrupted auth token before we use it to connect
|
// Drop any stale/corrupted auth token before we use it to connect
|
||||||
await validateAuth();
|
await validateAuth();
|
||||||
|
|||||||
@@ -1040,7 +1040,12 @@ input:focus { border-color: var(--gold); }
|
|||||||
font-size: .72rem;
|
font-size: .72rem;
|
||||||
color: rgba(255,255,255,.35);
|
color: rgba(255,255,255,.35);
|
||||||
letter-spacing: .5px;
|
letter-spacing: .5px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
}
|
}
|
||||||
|
.app-version:hover { color: rgba(255,255,255,.6); text-decoration: underline; }
|
||||||
|
.app-version:active { color: var(--gold); }
|
||||||
|
|
||||||
/* ── Round-by-round breakdown ───── */
|
/* ── Round-by-round breakdown ───── */
|
||||||
.round-breakdown-wrap { margin: 6px 0 2px; }
|
.round-breakdown-wrap { margin: 6px 0 2px; }
|
||||||
|
|||||||
Reference in New Issue
Block a user