fix(pwa): Endlos-Loop "Neue Version verfügbar" beseitigt
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m21s

Der SW rief bisher im Install-Handler self.skipWaiting() auf —
der neue SW übersprang damit die "waiting"-Phase und aktivierte
sofort. pwaStore.onUpdateFound feuerte trotzdem auf statechange=
"installed" + vorhandenem controller und setzte updateAvailable=
true. Ergebnis: Toast erschien, obwohl der SW bereits übernommen
hatte, und der Klick auf "Neu laden" löste durch das Timing einen
neuen Update-Zyklus aus → Endlosschleife, v.a. im Incognito-Mode
wo jede Session neu installiert.

Jetzt klassisches Pattern: SW wartet in "installed"-Zustand bis
der User den Toast bestätigt; pwaStore.reload() postet
SKIP_WAITING an den wartenden SW, lauscht auf controllerchange
und reloadet dann erst. Ohne diese Trennung ist der Toast
semantisch kaputt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-18 17:27:04 +02:00
parent 0ede62dc8a
commit 3d6f6393b3
2 changed files with 25 additions and 2 deletions

View File

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