const VERSION = 'v4' const SHELL_CACHE = `crm-agent-shell-${VERSION}` const STATIC_CACHE = `crm-agent-static-${VERSION}` const API_CACHE = `crm-agent-api-${VERSION}` const OFFLINE_URL = '/agent-mobile-offline.html' const APP_SHELL = [ OFFLINE_URL, '/manifest.json', '/favicon.svg', '/icons/icon-192.png', '/icons/icon-512.png', ] const CACHEABLE_API_PREFIXES = [ '/api/agent/mobile/bootstrap', '/api/agent/mobile/dashboard', '/api/agent/mobile/call-queue', '/api/agent/mobile/leads', '/api/agent/mobile/follow-ups', ] const BLOCKED_PATHS = ['/storage/', '/uploads/', '/sanctum'] self.addEventListener('install', (event) => { event.waitUntil( caches.open(SHELL_CACHE) .then((cache) => cache.addAll(APP_SHELL)) .then(() => self.skipWaiting()) .catch(() => undefined) ) }) self.addEventListener('activate', (event) => { event.waitUntil( caches.keys() .then((keys) => Promise.all(keys .filter((key) => ![SHELL_CACHE, STATIC_CACHE, API_CACHE].includes(key)) .map((key) => caches.delete(key)) )) .then(() => self.clients.claim()) ) }) self.addEventListener('message', (event) => { if (event.data?.type === 'SKIP_WAITING') { self.skipWaiting() } }) function isBlocked(url) { return BLOCKED_PATHS.some((path) => url.pathname.startsWith(path)) || url.pathname.includes('recording') } function isMobileApi(url) { return CACHEABLE_API_PREFIXES.some((path) => url.pathname.startsWith(path)) } async function networkFirst(request, cacheName) { const cache = await caches.open(cacheName) try { const response = await fetch(request) if (response && response.ok) { cache.put(request, response.clone()) } return response } catch (error) { const cached = await cache.match(request) if (cached) return cached throw error } } async function staleWhileRevalidate(request, cacheName) { const cache = await caches.open(cacheName) const cached = await cache.match(request) const fetched = fetch(request).then((response) => { if (response && response.ok && response.type !== 'opaque') { cache.put(request, response.clone()) } return response }).catch(() => cached) return cached || fetched } self.addEventListener('fetch', (event) => { const request = event.request const url = new URL(request.url) if (request.method !== 'GET' || isBlocked(url)) return if (request.mode === 'navigate') { event.respondWith( fetch(request).catch(() => { if (url.pathname.startsWith('/agent-mobile')) { return caches.match(OFFLINE_URL) } return Response.error() }) ) return } if (isMobileApi(url)) { event.respondWith(networkFirst(request, API_CACHE)) return } if (['script', 'style', 'font', 'image', 'manifest'].includes(request.destination)) { event.respondWith(staleWhileRevalidate(request, STATIC_CACHE)) } })