// Web Push service worker. // // Handles browser-delivered push events when the app tab is closed or in the // background. Tauri desktop does not install service workers; native OS // notifications are routed through the Tauri notification plugin instead // (see src/lib/osNotify.ts). // // Payload contract — server sends JSON of shape: // { title: string, body?: string, conversationId?: string, kind?: 'message' | 'call' } // Body is intentionally generic; message ciphertext is never included. self.addEventListener('install', (event) => { // Activate immediately so updates apply on next page load. event.waitUntil(self.skipWaiting()); }); self.addEventListener('activate', (event) => { event.waitUntil(self.clients.claim()); }); self.addEventListener('push', (event) => { let data = { title: 'Neue Nachricht', body: '' }; try { if (event.data) { data = { ...data, ...event.data.json() }; } } catch (_err) { /* malformed payload — fall back to defaults */ } const opts = { body: data.body || '', icon: '/favicon.svg', badge: '/favicon.svg', tag: data.conversationId || 'default', renotify: true, data: { conversationId: data.conversationId, kind: data.kind, }, }; event.waitUntil(self.registration.showNotification(data.title, opts)); }); self.addEventListener('notificationclick', (event) => { event.notification.close(); const conversationId = event.notification.data && event.notification.data.conversationId; const target = conversationId ? '/chats/' + conversationId : '/'; event.waitUntil( self.clients .matchAll({ type: 'window', includeUncontrolled: true }) .then((clientList) => { for (const client of clientList) { if ('focus' in client) { client.postMessage({ type: 'navigate', to: target }); return client.focus(); } } if (self.clients.openWindow) { return self.clients.openWindow(target); } return undefined; }), ); });