const CACHE_NAME = 'agency-cache-v4'; const ASSETS = [ '/', '/index.html', '/manifest.json', '/icon-192.png', '/icon-512.png' ]; // Force immediate update to the latest SW self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => { return cache.addAll(ASSETS); }).then(() => self.skipWaiting()) ); }); // Clean up old caches and take control of all clients immediately self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames.map((cacheName) => { if (cacheName !== CACHE_NAME) { console.log('Deleting old cache:', cacheName); return caches.delete(cacheName); } }) ); }).then(() => self.clients.claim()) ); }); self.addEventListener('fetch', (event) => { // Only intercept http/https requests if (!event.request.url.startsWith('http')) return; event.respondWith( caches.match(event.request).then((cachedResponse) => { if (cachedResponse) { return cachedResponse; } return fetch(event.request).catch((error) => { // If network fetch fails and it's a navigation request, return index.html if (event.request.mode === 'navigate') { return caches.match('/index.html'); } // For assets, return a failure response instead of throwing. // Re-throwing (or returning a rejected promise) causes the browser to show // the "unexpected error" interception UI. console.warn('Fetch failed for:', event.request.url, error); return new Response('Network error occurred', { status: 503, statusText: 'Service Unavailable', headers: new Headers({ 'Content-Type': 'text/plain' }) }); }); }) ); }); self.addEventListener('push', (event) => { let data = { title: 'Notification', body: 'New update from Agency' }; try { if (event.data) { data = event.data.json(); } } catch (e) { console.error('Error parsing push data:', e); } const options = { body: data.body, icon: '/icon-192.png', badge: '/icon-192.png', vibrate: [100, 50, 100], data: { dateOfArrival: Date.now(), primaryKey: '1', taskId: data.task_id, runId: data.run_id } }; event.waitUntil( self.registration.showNotification(data.title, options) ); }); self.addEventListener('notificationclick', (event) => { event.notification.close(); const taskId = event.notification.data.taskId; const runId = event.notification.data.runId; let url = '/'; if (taskId && runId) { url = `/?taskId=${taskId}&runId=${runId}`; } event.waitUntil( clients.matchAll({ type: 'window', includeUncontrolled: true }).then((windowClients) => { // Check if there is already a window open and focus it, or open a new one for (let client of windowClients) { if ('focus' in client) { // Navigate the existing client to the new URL if it's the same app return client.navigate(url).then(c => c.focus()); } } if (clients.openWindow) { return clients.openWindow(url); } }) ); });