153 lines
4.4 KiB
JavaScript
153 lines
4.4 KiB
JavaScript
// Service Worker version
|
|
const CACHE_VERSION = 'v1.0.0';
|
|
const CACHE_NAME = `game-timer-${CACHE_VERSION}`;
|
|
|
|
// Files to cache
|
|
const CACHE_FILES = [
|
|
'/',
|
|
'/index.html',
|
|
'/app.js',
|
|
'/audio.js',
|
|
'/styles.css',
|
|
'/manifest.json',
|
|
'/icons/android-chrome-192x192.png',
|
|
'/icons/android-chrome-512x512.png',
|
|
'/icons/apple-touch-icon.png',
|
|
'/icons/favicon-32x32.png',
|
|
'/icons/favicon-16x16.png'
|
|
];
|
|
|
|
// Install event - Cache files
|
|
self.addEventListener('install', event => {
|
|
console.log('[ServiceWorker] Install');
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME)
|
|
.then(cache => {
|
|
console.log('[ServiceWorker] Caching app shell');
|
|
return cache.addAll(CACHE_FILES);
|
|
})
|
|
.then(() => {
|
|
console.log('[ServiceWorker] Skip waiting on install');
|
|
return self.skipWaiting();
|
|
})
|
|
);
|
|
});
|
|
|
|
// Activate event - Clean old caches
|
|
self.addEventListener('activate', event => {
|
|
console.log('[ServiceWorker] Activate');
|
|
event.waitUntil(
|
|
caches.keys().then(keyList => {
|
|
return Promise.all(keyList.map(key => {
|
|
if (key !== CACHE_NAME) {
|
|
console.log('[ServiceWorker] Removing old cache', key);
|
|
return caches.delete(key);
|
|
}
|
|
}));
|
|
})
|
|
.then(() => {
|
|
console.log('[ServiceWorker] Claiming clients');
|
|
return self.clients.claim();
|
|
})
|
|
);
|
|
});
|
|
|
|
// Fetch event - Serve from cache, fallback to network
|
|
self.addEventListener('fetch', event => {
|
|
console.log('[ServiceWorker] Fetch', event.request.url);
|
|
|
|
// For navigation requests that include our deep link parameters,
|
|
// skip the cache and go straight to network
|
|
if (event.request.mode === 'navigate') {
|
|
const url = new URL(event.request.url);
|
|
|
|
// Check if request has action parameter or hash
|
|
if (url.searchParams.has('action') || url.hash.includes('action=')) {
|
|
console.log('[ServiceWorker] Processing deep link navigation');
|
|
return;
|
|
}
|
|
}
|
|
|
|
event.respondWith(
|
|
caches.match(event.request)
|
|
.then(response => {
|
|
return response || fetch(event.request)
|
|
.then(res => {
|
|
// Check if we should cache this response
|
|
if (shouldCacheResponse(event.request, res)) {
|
|
return caches.open(CACHE_NAME)
|
|
.then(cache => {
|
|
console.log('[ServiceWorker] Caching new resource:', event.request.url);
|
|
cache.put(event.request, res.clone());
|
|
return res;
|
|
});
|
|
}
|
|
return res;
|
|
});
|
|
})
|
|
.catch(error => {
|
|
console.log('[ServiceWorker] Fetch failed; returning offline page', error);
|
|
// You could return a custom offline page here
|
|
})
|
|
);
|
|
});
|
|
|
|
// Helper function to determine if a response should be cached
|
|
function shouldCacheResponse(request, response) {
|
|
// Only cache GET requests
|
|
if (request.method !== 'GET') return false;
|
|
|
|
// Don't cache errors
|
|
if (!response || response.status !== 200) return false;
|
|
|
|
// Check if URL should be cached
|
|
const url = new URL(request.url);
|
|
|
|
// Don't cache query parameters (except common ones for content)
|
|
if (url.search && !url.search.match(/\?(v|version|cache)=/)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Handle deep links from other apps (including Flic)
|
|
self.addEventListener('message', event => {
|
|
if (event.data && event.data.type === 'PROCESS_ACTION') {
|
|
const action = event.data.action;
|
|
console.log('[ServiceWorker] Received action message:', action);
|
|
|
|
// Broadcast the action to all clients
|
|
self.clients.matchAll().then(clients => {
|
|
clients.forEach(client => {
|
|
client.postMessage({
|
|
type: 'ACTION',
|
|
action: action
|
|
});
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
// This helps with navigation after app is installed
|
|
self.addEventListener('notificationclick', event => {
|
|
event.notification.close();
|
|
|
|
// This looks to see if the current is already open and focuses if it is
|
|
event.waitUntil(
|
|
self.clients.matchAll({
|
|
type: 'window'
|
|
})
|
|
.then(clientList => {
|
|
// Check if there is already a window/tab open with the target URL
|
|
for (const client of clientList) {
|
|
// If so, just focus it
|
|
if (client.url.startsWith(self.location.origin) && 'focus' in client) {
|
|
return client.focus();
|
|
}
|
|
}
|
|
// If not, open a new window/tab
|
|
if (self.clients.openWindow) {
|
|
return self.clients.openWindow('/');
|
|
}
|
|
})
|
|
);
|
|
}); |