feat: implement ironclad offline strategy with pre-caching and root fallback
This commit is contained in:
53
public/sw.js
53
public/sw.js
@@ -1,5 +1,13 @@
|
||||
const CACHE_NAME = 'whisky-vault-v3'; // Increment version to apply new strategy
|
||||
const ASSETS_TO_CACHE = [
|
||||
const CACHE_NAME = 'whisky-vault-v4'; // Increment version for "Ironclad" strategy
|
||||
|
||||
// CONFIG: Core pages and assets to pre-cache immediately on install
|
||||
const CORE_PAGES = [
|
||||
'/', // Dashboard / Home
|
||||
'/tasting/new', // Critical: Add Tasting Screen
|
||||
'/scan', // Critical: Scan Screen
|
||||
];
|
||||
|
||||
const STATIC_ASSETS = [
|
||||
'/manifest.json',
|
||||
'/icon-192.png',
|
||||
'/icon-512.png',
|
||||
@@ -9,9 +17,12 @@ const ASSETS_TO_CACHE = [
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches.open(CACHE_NAME).then((cache) => {
|
||||
return cache.addAll(ASSETS_TO_CACHE);
|
||||
console.log('⚡ PWA: Pre-caching core pages and assets...');
|
||||
// Combine items to cache
|
||||
return cache.addAll([...CORE_PAGES, ...STATIC_ASSETS]);
|
||||
})
|
||||
);
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
@@ -63,7 +74,24 @@ self.addEventListener('fetch', (event) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. ASSETS & APP SHELL: Stale-While-Revalidate
|
||||
// 1. NEXT.JS DATA (RSC): Stale-While-Revalidate with empty JSON fallback
|
||||
if (url.pathname.startsWith('/_next/data/')) {
|
||||
event.respondWith(
|
||||
fetchWithTimeout(event.request, 2000)
|
||||
.catch(async () => {
|
||||
const cachedResponse = await caches.match(event.request);
|
||||
if (cachedResponse) return cachedResponse;
|
||||
|
||||
// Fallback to empty JSON to prevent "Application Error" screens
|
||||
return new Response(JSON.stringify({}), {
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. ASSETS & APP SHELL: Stale-While-Revalidate
|
||||
const isAsset = event.request.destination === 'style' ||
|
||||
event.request.destination === 'script' ||
|
||||
event.request.destination === 'worker' ||
|
||||
@@ -89,18 +117,21 @@ self.addEventListener('fetch', (event) => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. NAVIGATION: Network-First with 3s Timeout
|
||||
// 3. NAVIGATION: Ironclad Navigation Fallback
|
||||
if (event.request.mode === 'navigate') {
|
||||
event.respondWith(
|
||||
fetchWithTimeout(event.request, 3000)
|
||||
.catch(() => {
|
||||
console.log('[SW] Navigation network failure or timeout, falling back to cache');
|
||||
return caches.match(event.request);
|
||||
.catch(async () => {
|
||||
console.log('[SW] Navigation network failure, attempting cache fallback');
|
||||
const cachedResponse = await caches.match(event.request);
|
||||
if (cachedResponse) return cachedResponse;
|
||||
|
||||
// CRITICAL FALLBACK: Load the Root App Shell ('/')
|
||||
// This allows Next.js to bootstrap and handle the routing client-side
|
||||
console.warn('⚠️ PWA: Route not in cache, fallback to Root App Shell');
|
||||
return caches.match('/');
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Default: Network-Only or default fetch
|
||||
return;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user