/// /// declare let self: ServiceWorkerGlobalScope; // ── Install / Activate ──────────────────────────────────────────────────────── self.addEventListener('install', () => { self.skipWaiting(); }); self.addEventListener('activate', (event) => { event.waitUntil(self.clients.claim()); }); // ── Push notifications ──────────────────────────────────────────────────────── interface PushPayload { title: string; body: string; url?: string; icon?: string; badge?: string; } self.addEventListener('push', (event) => { if (!event.data) return; let payload: PushPayload; try { payload = event.data.json() as PushPayload; } catch { payload = { title: 'LibNovel', body: event.data.text() }; } const options: NotificationOptions = { body: payload.body, icon: payload.icon ?? '/icon-192.png', badge: payload.badge ?? '/favicon-32.png', data: { url: payload.url ?? '/' }, // Show notification even when the app is focused requireInteraction: false, }; event.waitUntil( self.registration.showNotification(payload.title, options) ); }); // ── Notification click ──────────────────────────────────────────────────────── self.addEventListener('notificationclick', (event) => { event.notification.close(); const url: string = (event.notification.data as { url?: string })?.url ?? '/'; event.waitUntil( self.clients .matchAll({ type: 'window', includeUncontrolled: true }) .then((clientList) => { // Focus existing window if it has the target URL already open for (const client of clientList) { if (client.url === url && 'focus' in client) { return client.focus(); } } // Otherwise open a new window if (self.clients.openWindow) { return self.clients.openWindow(url); } }) ); });