const CACHE_NAME = 'listify-shell-v3'; const SHELL_ASSETS = ['/', '/index.html', '/manifest.webmanifest', '/pwa-icon.svg']; self.addEventListener('install', (event) => { event.waitUntil( caches .open(CACHE_NAME) .then((cache) => cache.addAll(SHELL_ASSETS)) .then(() => self.skipWaiting()), ); }); self.addEventListener('activate', (event) => { event.waitUntil( caches .keys() .then((keys) => Promise.all(keys.filter((key) => key !== CACHE_NAME).map((key) => caches.delete(key))), ) .then(() => self.clients.claim()), ); }); self.addEventListener('fetch', (event) => { const request = event.request; const url = new URL(request.url); if (url.origin !== self.location.origin || url.pathname.startsWith('/api/')) { return; } if (request.mode === 'navigate') { event.respondWith( fetch(request) .then((response) => { const responseClone = response.clone(); caches.open(CACHE_NAME).then((cache) => cache.put('/index.html', responseClone)); return response; }) .catch(() => caches.match('/index.html')), ); return; } event.respondWith( caches.match(request).then((cached) => { if (cached) { return cached; } return fetch(request).then((response) => { if (request.method === 'GET' && response.ok) { const responseClone = response.clone(); caches.open(CACHE_NAME).then((cache) => cache.put(request, responseClone)); } return response; }); }), ); }); self.addEventListener('push', (event) => { let payload = {}; try { payload = event.data ? event.data.json() : {}; } catch { payload = {}; } const title = payload.title || 'Listify Tasks'; const options = { body: payload.body || 'Du hast offene Tasks.', tag: payload.tag || 'listify-task-digest', renotify: true, timestamp: Date.now(), vibrate: [120, 80, 120], icon: '/pwa-icon.svg', badge: '/pwa-icon.svg', data: { url: payload.url || '/tasks', }, }; event.waitUntil(self.registration.showNotification(title, options)); }); self.addEventListener('notificationclick', (event) => { event.notification.close(); const targetUrl = event.notification.data?.url || '/tasks'; const absoluteUrl = new URL(targetUrl, self.location.origin).href; event.waitUntil( self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clients) => { for (const client of clients) { if (client.url === absoluteUrl && 'focus' in client) { return client.focus(); } } return self.clients.openWindow(absoluteUrl); }), ); });