PWA: synleg del-ikon og ekte autoupdate utan å avbryte aktiv bruk
- Bytt del-knappen frå tekstpila ↪ til ein innebygd Material-stil SVG (20×20). Den førre Unicode-pila vart teikna mykje mindre enn ℹ og ↻ av dei fleste fontar — no har alle tre ikona same visuelle vekt på tvers av nettlesarar og operativsystem. - Lagt til ein controllerchange-lyttar som utløyser ein omlasting *neste* gong appen vert lagt i bakgrunnen (visibilityState === 'hidden'), ikkje umiddelbart. Slik tek nye distribusjonar effekt utan brukarinngrep, men utan å rive vekk skjermen midt i ein tilfluktsromnavigering. registerType: 'autoUpdate' åleine sørgjer for at den nye SW-en skipWaiting + clientsClaim, men det reload-ar ikkje den allereie køyrande JavaScripten — denne lyttaren stettar det utan å bryte noregisseringsflyt. Påverkar ikkje fråkopla bruk: omlasting skjer berre etter at ein ny SW har vorte aktivert, og ein ny SW kan berre installerast medan nettverket er tilgjengeleg. Sjølve omlastinga løyser alle ressursar frå presjekken og fungerer fråkopla. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
948625b777
commit
c603e81b2a
3 changed files with 46 additions and 11 deletions
|
|
@ -25,7 +25,11 @@
|
||||||
<header id="status-bar" role="banner">
|
<header id="status-bar" role="banner">
|
||||||
<span id="status-text" aria-live="polite"></span>
|
<span id="status-text" aria-live="polite"></span>
|
||||||
<button id="about-btn" aria-label="About">ℹ</button>
|
<button id="about-btn" aria-label="About">ℹ</button>
|
||||||
<button id="share-btn" aria-label="Share shelter">↪</button>
|
<button id="share-btn" aria-label="Share shelter">
|
||||||
|
<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor" aria-hidden="true" focusable="false">
|
||||||
|
<path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92-1.31-2.92-2.92-2.92z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
<button id="refresh-btn" aria-label="Refresh data">↻</button>
|
<button id="refresh-btn" aria-label="Refresh data">↻</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,44 @@ import './styles/main.css';
|
||||||
import 'leaflet/dist/leaflet.css';
|
import 'leaflet/dist/leaflet.css';
|
||||||
import { initLocale } from './i18n/i18n';
|
import { initLocale } from './i18n/i18n';
|
||||||
import { init } from './app';
|
import { init } from './app';
|
||||||
import { setStatus } from './ui/status-bar';
|
|
||||||
import { t } from './i18n/i18n';
|
|
||||||
import { maybeShow as maybeShowIosInstallHint } from './ui/install-hint';
|
import { maybeShow as maybeShowIosInstallHint } from './ui/install-hint';
|
||||||
|
|
||||||
console.info(`[tilfluktsrom] build ${__BUILD_REVISION__}`);
|
console.info(`[tilfluktsrom] build ${__BUILD_REVISION__}`);
|
||||||
|
|
||||||
|
// Make `registerType: 'autoUpdate'` actually auto-update the running tab.
|
||||||
|
// vite-plugin-pwa's autoUpdate strategy makes the new service worker
|
||||||
|
// skipWaiting + clientsClaim, but the JS already loaded in the open tab is
|
||||||
|
// the *old* build until something triggers a navigation. Without this
|
||||||
|
// listener, a deploy is invisible until the user manually refreshes.
|
||||||
|
//
|
||||||
|
// We *defer* the reload until the user next backgrounds the app
|
||||||
|
// (visibilityState === 'hidden') instead of reloading immediately. This is
|
||||||
|
// an emergency app: a mid-task reload would lose the selected shelter,
|
||||||
|
// compass mode, and any in-flight UI state right when the user can least
|
||||||
|
// afford to be surprised. Deferring keeps the "auto" promise (they're on
|
||||||
|
// the new version next time they look at the screen) without interrupting
|
||||||
|
// active use.
|
||||||
|
//
|
||||||
|
// The `wasAlreadyControlled` guard avoids reloading on the very first SW
|
||||||
|
// install (when there was no previous controller — that's a fresh visit,
|
||||||
|
// not an update).
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
const wasAlreadyControlled = !!navigator.serviceWorker.controller;
|
||||||
|
let pendingReload = false;
|
||||||
|
|
||||||
|
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
||||||
|
if (!wasAlreadyControlled) return;
|
||||||
|
pendingReload = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('visibilitychange', () => {
|
||||||
|
if (pendingReload && document.visibilityState === 'hidden') {
|
||||||
|
pendingReload = false;
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
initLocale();
|
initLocale();
|
||||||
|
|
||||||
|
|
@ -25,14 +57,6 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
await navigator.storage.persist();
|
await navigator.storage.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen for service worker updates — flash a status message when a new
|
|
||||||
// version activates so the user knows they have fresh code/data.
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
|
||||||
setStatus(t('update_success'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await init();
|
await init();
|
||||||
|
|
||||||
// Shown only on first iOS Safari visit, once per device. Placed after init()
|
// Shown only on first iOS Safari visit, once per device. Placed after init()
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,13 @@ html, body {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#share-btn svg {
|
||||||
|
display: block;
|
||||||
|
/* `↻` and `ℹ` glyphs sit on a baseline; an SVG inside an inline-flex
|
||||||
|
container can otherwise pick up extra descender space and look
|
||||||
|
misaligned next to them. `display:block` removes that. */
|
||||||
|
}
|
||||||
|
|
||||||
/* --- Main content area (map or compass) --- */
|
/* --- Main content area (map or compass) --- */
|
||||||
#main-content {
|
#main-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue