switching
This commit is contained in:
208
public/sw.js
208
public/sw.js
@ -1,50 +1,196 @@
|
||||
const CACHE_NAME = 'bitfielder-v1';
|
||||
const urlsToCache = [
|
||||
const CACHE_NAME = 'bitfielder-v2';
|
||||
const STATIC_CACHE = 'bitfielder-static-v2';
|
||||
const DYNAMIC_CACHE = 'bitfielder-dynamic-v2';
|
||||
|
||||
// Core app files that should always be cached
|
||||
const CORE_ASSETS = [
|
||||
'/',
|
||||
'/index.html',
|
||||
'/src/main.ts',
|
||||
'/src/FakeShader.ts',
|
||||
'/src/ShaderWorker.ts',
|
||||
'/src/Storage.ts',
|
||||
'/src/icons.ts',
|
||||
'/manifest.json'
|
||||
'/manifest.json',
|
||||
'/icon-192.png',
|
||||
'/icon-512.png'
|
||||
];
|
||||
|
||||
// Install event - cache resources
|
||||
// Assets that can be cached dynamically
|
||||
const DYNAMIC_ASSETS_PATTERNS = [
|
||||
/\/src\/.+\.(ts|tsx|js|jsx)$/,
|
||||
/\/src\/.+\.css$/,
|
||||
/fonts\.googleapis\.com/,
|
||||
/fonts\.gstatic\.com/
|
||||
];
|
||||
|
||||
// Install event - cache core resources
|
||||
self.addEventListener('install', event => {
|
||||
console.log('Service Worker installing...');
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME)
|
||||
.then(cache => {
|
||||
console.log('Opened cache');
|
||||
return cache.addAll(urlsToCache);
|
||||
Promise.all([
|
||||
caches.open(STATIC_CACHE).then(cache => {
|
||||
console.log('Caching core assets');
|
||||
return cache.addAll(CORE_ASSETS);
|
||||
}),
|
||||
caches.open(DYNAMIC_CACHE).then(cache => {
|
||||
console.log('Dynamic cache initialized');
|
||||
})
|
||||
]).then(() => {
|
||||
console.log('Service Worker installed successfully');
|
||||
return self.skipWaiting();
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Fetch event - serve from cache when offline
|
||||
self.addEventListener('fetch', event => {
|
||||
event.respondWith(
|
||||
caches.match(event.request)
|
||||
.then(response => {
|
||||
// Return cached version or fetch from network
|
||||
return response || fetch(event.request);
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// Activate event - clean up old caches
|
||||
// Activate event - clean up old caches and take control
|
||||
self.addEventListener('activate', event => {
|
||||
console.log('Service Worker activating...');
|
||||
event.waitUntil(
|
||||
caches.keys().then(cacheNames => {
|
||||
return Promise.all(
|
||||
cacheNames.map(cacheName => {
|
||||
if (cacheName !== CACHE_NAME) {
|
||||
return Promise.all([
|
||||
...cacheNames.map(cacheName => {
|
||||
if (cacheName !== STATIC_CACHE && cacheName !== DYNAMIC_CACHE) {
|
||||
console.log('Deleting old cache:', cacheName);
|
||||
return caches.delete(cacheName);
|
||||
}
|
||||
})
|
||||
);
|
||||
}),
|
||||
self.clients.claim()
|
||||
]);
|
||||
}).then(() => {
|
||||
console.log('Service Worker activated successfully');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Fetch event - sophisticated caching strategy
|
||||
self.addEventListener('fetch', event => {
|
||||
const { request } = event;
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Skip non-GET requests
|
||||
if (request.method !== 'GET') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip external APIs and chrome-extension
|
||||
if (!url.origin.includes(self.location.origin) && !url.hostname.includes('googleapis') && !url.hostname.includes('gstatic')) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.respondWith(handleFetch(request));
|
||||
});
|
||||
|
||||
async function handleFetch(request) {
|
||||
const url = new URL(request.url);
|
||||
|
||||
try {
|
||||
// Strategy 1: Core assets - Cache First
|
||||
if (CORE_ASSETS.some(asset => url.pathname === asset || url.pathname.endsWith(asset))) {
|
||||
return await cacheFirst(request, STATIC_CACHE);
|
||||
}
|
||||
|
||||
// Strategy 2: Dynamic assets - Stale While Revalidate
|
||||
if (DYNAMIC_ASSETS_PATTERNS.some(pattern => pattern.test(url.pathname))) {
|
||||
return await staleWhileRevalidate(request, DYNAMIC_CACHE);
|
||||
}
|
||||
|
||||
// Strategy 3: Fonts - Cache First with longer TTL
|
||||
if (url.hostname.includes('googleapis') || url.hostname.includes('gstatic')) {
|
||||
return await cacheFirst(request, DYNAMIC_CACHE);
|
||||
}
|
||||
|
||||
// Strategy 4: Everything else - Network First
|
||||
return await networkFirst(request, DYNAMIC_CACHE);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fetch error:', error);
|
||||
|
||||
// Fallback for navigation requests
|
||||
if (request.mode === 'navigate') {
|
||||
const cache = await caches.open(STATIC_CACHE);
|
||||
return await cache.match('/index.html') || new Response('App offline', { status: 503 });
|
||||
}
|
||||
|
||||
return new Response('Resource not available offline', { status: 503 });
|
||||
}
|
||||
}
|
||||
|
||||
// Cache First strategy
|
||||
async function cacheFirst(request, cacheName) {
|
||||
const cache = await caches.open(cacheName);
|
||||
const cached = await cache.match(request);
|
||||
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const response = await fetch(request);
|
||||
if (response.status === 200) {
|
||||
cache.put(request, response.clone());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// Network First strategy
|
||||
async function networkFirst(request, cacheName) {
|
||||
const cache = await caches.open(cacheName);
|
||||
|
||||
try {
|
||||
const response = await fetch(request);
|
||||
if (response.status === 200) {
|
||||
cache.put(request, response.clone());
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
const cached = await cache.match(request);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Stale While Revalidate strategy
|
||||
async function staleWhileRevalidate(request, cacheName) {
|
||||
const cache = await caches.open(cacheName);
|
||||
const cached = await cache.match(request);
|
||||
|
||||
// Always try to fetch and update cache in background
|
||||
const fetchPromise = fetch(request).then(response => {
|
||||
if (response.status === 200) {
|
||||
cache.put(request, response.clone());
|
||||
}
|
||||
return response;
|
||||
}).catch(() => null);
|
||||
|
||||
// Return cached version immediately if available, otherwise wait for network
|
||||
return cached || await fetchPromise;
|
||||
}
|
||||
|
||||
// Handle background sync for offline actions
|
||||
self.addEventListener('sync', event => {
|
||||
console.log('Background sync triggered:', event.tag);
|
||||
|
||||
if (event.tag === 'shader-save') {
|
||||
event.waitUntil(syncShaderData());
|
||||
}
|
||||
});
|
||||
|
||||
async function syncShaderData() {
|
||||
// This would handle syncing shader data when coming back online
|
||||
// For now, just log that sync is available
|
||||
console.log('Shader data sync available for future implementation');
|
||||
}
|
||||
|
||||
// Handle push notifications (for future features)
|
||||
self.addEventListener('push', event => {
|
||||
if (event.data) {
|
||||
const data = event.data.json();
|
||||
console.log('Push notification received:', data);
|
||||
|
||||
// Could be used for shader sharing notifications, etc.
|
||||
}
|
||||
});
|
||||
|
||||
// Provide offline status
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data && event.data.type === 'GET_VERSION') {
|
||||
event.ports[0].postMessage({ version: CACHE_NAME });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user