// Service Worker for ICE Facility Heatmap
const CACHE_NAME = 'ice-locator-v1';
const STATIC_CACHE = 'ice-locator-static-v1';
const DYNAMIC_CACHE = 'ice-locator-dynamic-v1';
// Assets to cache immediately
const STATIC_ASSETS = [
'/',
'/index.html',
'/favicon.svg',
'/manifest.json',
// Add critical CSS and JS files
'/src/main.tsx',
'/src/App.tsx',
'/src/index.css'
];
// Install event - cache static assets
self.addEventListener('install', (event) => {
console.log('Service Worker: Installing...');
event.waitUntil(
caches.open(STATIC_CACHE)
.then((cache) => {
console.log('Service Worker: Caching static assets');
return cache.addAll(STATIC_ASSETS);
})
.then(() => {
return self.skipWaiting();
})
.catch((error) => {
console.error('Service Worker: Failed to cache static assets', error);
})
);
});
// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
console.log('Service Worker: Activating...');
event.waitUntil(
caches.keys()
.then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== STATIC_CACHE && cacheName !== DYNAMIC_CACHE) {
console.log('Service Worker: Deleting old cache', cacheName);
return caches.delete(cacheName);
}
})
);
})
.then(() => {
return self.clients.claim();
})
);
});
// Fetch event - serve from cache, fallback to network
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Skip non-GET requests
if (request.method !== 'GET') {
return;
}
// Skip cross-origin requests
if (url.origin !== location.origin) {
return;
}
event.respondWith(
caches.match(request)
.then((cachedResponse) => {
// Return cached version if available
if (cachedResponse) {
console.log('Service Worker: Serving from cache', request.url);
return cachedResponse;
}
// Otherwise fetch from network
return fetch(request)
.then((response) => {
// Don't cache non-successful responses
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// Clone the response for caching
const responseToCache = response.clone();
// Cache the response
caches.open(DYNAMIC_CACHE)
.then((cache) => {
cache.put(request, responseToCache);
});
console.log('Service Worker: Fetching from network', request.url);
return response;
})
.catch((error) => {
console.error('Service Worker: Fetch failed', error);
// Return offline fallback for HTML pages
if (request.headers.get('accept').includes('text/html')) {
return caches.match('/index.html');
}
throw error;
});
})
);
});
// Background sync for offline data
self.addEventListener('sync', (event) => {
if (event.tag === 'background-sync') {
console.log('Service Worker: Background sync triggered');
event.waitUntil(
// Perform background sync operations here
Promise.resolve()
);
}
});
// Push notifications (if needed in the future)
self.addEventListener('push', (event) => {
if (event.data) {
const data = event.data.json();
const options = {
body: data.body,
icon: '/favicon.svg',
badge: '/favicon.svg',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: data.primaryKey
}
};
event.waitUntil(
self.registration.showNotification(data.title, options)
);
}
});
// Handle notification clicks
self.addEventListener('notificationclick', (event) => {
event.notification.close();
event.waitUntil(
clients.openWindow('/')
);
});