const sw = self as unknown as ServiceWorkerGlobalScope; interface ExtendableEvent extends Event { waitUntil(fn: Promise): void; } interface PushEvent extends ExtendableEvent { data: PushMessageData | null; } interface PushMessageData { json(): unknown; text(): string; } interface NotificationEvent extends ExtendableEvent { notification: Notification; } // ────────────────────────────────────────────────────────────────────────────── // Service Worker: Install & Activate // ────────────────────────────────────────────────────────────────────────────── sw.addEventListener('install', () => { sw.skipWaiting(); }); sw.addEventListener('activate', (event: Event) => { (event as ExtendableEvent).waitUntil(sw.clients.claim()); }); // ────────────────────────────────────────────────────────────────────────────── // Push Notification Handler // ────────────────────────────────────────────────────────────────────────────── sw.addEventListener('push', (event: Event) => { const pushEvent = event as PushEvent; if (!pushEvent.data) { return; } let data: { title?: string; body?: string; icon?: string; url?: string } = {}; try { data = pushEvent.data.json() as typeof data; } catch { data = { title: 'DST Collection', body: pushEvent.data.text() }; } const title = data.title ?? 'DST Collection'; const options: NotificationOptions = { body: data.body ?? '', icon: data.icon ?? '/assets/pwa-192x192.png', badge: '/assets/pwa-64x64.png', data: { url: data.url ?? '/admin/master/categories' }, tag: 'dst-notification', renotify: true, }; pushEvent.waitUntil(sw.registration.showNotification(title, options)); }); // ────────────────────────────────────────────────────────────────────────────── // Notification Click Handler // ────────────────────────────────────────────────────────────────────────────── sw.addEventListener('notificationclick', (event: Event) => { const notifEvent = event as NotificationEvent; notifEvent.notification.close(); const targetUrl: string = (notifEvent.notification.data?.url as string) ?? '/admin/dashboard'; notifEvent.waitUntil( sw.clients .matchAll({ type: 'window', includeUncontrolled: true }) .then((clientList) => { for (const client of clientList) { if ('focus' in client) { return (client as WindowClient) .navigate(targetUrl) .then((c) => c?.focus()); } } return sw.clients.openWindow(targetUrl); }), ); });