class PwaStore { updateAvailable = $state(false); private registration: ServiceWorkerRegistration | null = null; private pollTimer: ReturnType | null = null; async init(): Promise { if (typeof navigator === 'undefined' || !('serviceWorker' in navigator)) return; try { this.registration = await navigator.serviceWorker.ready; } catch { return; } if (!this.registration) return; // Wenn beim Mount schon ein neuer SW installiert und aktiv wartet, // zeigen wir den Toast direkt an. if (this.registration.waiting) { this.updateAvailable = true; } this.registration.addEventListener('updatefound', () => this.onUpdateFound()); // Alle 30 Minuten aktiv nach Updates fragen, damit der User sie auch // mitbekommt, wenn er die Seite lange offen lässt ohne zu navigieren. this.pollTimer = setInterval(() => { void this.registration?.update().catch(() => {}); }, 30 * 60_000); } private onUpdateFound(): void { const installing = this.registration?.installing; if (!installing) return; installing.addEventListener('statechange', () => { // 'installed' UND ein laufender controller = Update für bestehenden Tab. // (Ohne controller wäre das die erste Installation, kein Update.) if (installing.state === 'installed' && navigator.serviceWorker.controller) { this.updateAvailable = true; } }); } reload(): void { this.updateAvailable = false; location.reload(); } dismiss(): void { this.updateAvailable = false; } } export const pwaStore = new PwaStore();