diff --git a/src/lib/client/pwa.svelte.ts b/src/lib/client/pwa.svelte.ts index bc125ac..e84d991 100644 --- a/src/lib/client/pwa.svelte.ts +++ b/src/lib/client/pwa.svelte.ts @@ -41,7 +41,22 @@ class PwaStore { reload(): void { this.updateAvailable = false; - location.reload(); + const waiting = this.registration?.waiting; + if (!waiting) { + // Kein wartender SW — entweder war es nur eine Toast-Anzeige, oder + // der SW ist schon aktiv. In beiden Fällen reicht ein Reload. + location.reload(); + return; + } + // Klassisches Pattern: User-Klick → SKIP_WAITING an den wartenden + // SW → controllerchange feuert, wenn der neue SW übernimmt → dann + // reloaden wir die Seite, damit sie unter dem neuen SW läuft. + navigator.serviceWorker.addEventListener( + 'controllerchange', + () => location.reload(), + { once: true } + ); + waiting.postMessage({ type: 'SKIP_WAITING' }); } dismiss(): void { diff --git a/src/service-worker.ts b/src/service-worker.ts index 0bbc7e4..648acc0 100644 --- a/src/service-worker.ts +++ b/src/service-worker.ts @@ -20,7 +20,12 @@ self.addEventListener('install', (event) => { (async () => { const cache = await caches.open(SHELL_CACHE); await cache.addAll(SHELL_ASSETS); - await self.skipWaiting(); + // Kein self.skipWaiting() hier — der Client (pwaStore) fragt den + // User via UpdateToast, ob der neue SW sofort übernehmen soll, und + // schickt dann eine SKIP_WAITING-Message. Ohne diese Trennung + // würde pwaStore beim Install-Event fälschlich "Neue Version" + // zeigen (weil statechange='installed' + controller=alter SW), und + // der neue SW würde einen Tick später ungefragt übernehmen. })() ); }); @@ -91,6 +96,9 @@ self.addEventListener('message', (event) => { event.waitUntil(runSync(false)); } else if (data.type === 'sync-check') { event.waitUntil(runSync(true)); + } else if (data.type === 'SKIP_WAITING') { + // Wird vom pwaStore nach User-Klick auf "Neu laden" geschickt. + void self.skipWaiting(); } });