// TaskFlow Service Worker for Push Notifications const CACHE_NAME = 'taskflow-v1'; // Install event - cache essential assets self.addEventListener('install', (event) => { console.log('[SW] Installing service worker...'); self.skipWaiting(); }); // Activate event - clean up old caches self.addEventListener('activate', (event) => { console.log('[SW] Activating service worker...'); event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames .filter((name) => name !== CACHE_NAME) .map((name) => caches.delete(name)) ); }) ); self.clients.claim(); }); // Push event - handle incoming push notifications self.addEventListener('push', (event) => { console.log('[SW] Push received:', event); let data = { title: 'TaskFlow', body: 'You have a new notification', icon: '/icon-256.png', badge: '/icon-128.png', tag: 'taskflow-notification', data: {} }; if (event.data) { try { const payload = event.data.json(); data = { title: payload.title || data.title, body: payload.body || data.body, icon: payload.icon || data.icon, badge: payload.badge || data.badge, tag: payload.tag || data.tag, data: payload.data || {} }; } catch (e) { // If not JSON, use text data.body = event.data.text(); } } const options = { body: data.body, icon: data.icon, badge: data.badge, tag: data.tag, data: data.data, vibrate: [100, 50, 100], requireInteraction: false, actions: [ { action: 'open', title: 'Open TaskFlow' }, { action: 'dismiss', title: 'Dismiss' } ] }; event.waitUntil( self.registration.showNotification(data.title, options) ); }); // Notification click event - handle user interaction self.addEventListener('notificationclick', (event) => { console.log('[SW] Notification clicked:', event); event.notification.close(); if (event.action === 'dismiss') { return; } // Default action or 'open' action - open the app const urlToOpen = event.notification.data?.url || '/'; event.waitUntil( clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => { // Check if app is already open for (const client of clientList) { if (client.url.includes(self.location.origin) && 'focus' in client) { client.focus(); if (event.notification.data?.url) { client.navigate(event.notification.data.url); } return; } } // Open new window if not already open if (clients.openWindow) { return clients.openWindow(urlToOpen); } }) ); }); // Notification close event self.addEventListener('notificationclose', (event) => { console.log('[SW] Notification closed:', event); }); // Handle push subscription change self.addEventListener('pushsubscriptionchange', (event) => { console.log('[SW] Push subscription changed:', event); event.waitUntil( self.registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: event.oldSubscription?.options?.applicationServerKey }).then((subscription) => { // Send new subscription to server return fetch('/api/push/resubscribe', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ oldEndpoint: event.oldSubscription?.endpoint, newSubscription: subscription.toJSON() }) }); }) ); });