Mercurial
diff mrjunejune/src/public/sw.js @ 209:3b47e82ac57e
[MrJuneJune] PWA updates.
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Sun, 15 Feb 2026 15:43:26 -0800 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mrjunejune/src/public/sw.js Sun Feb 15 15:43:26 2026 -0800 @@ -0,0 +1,138 @@ +// Service Worker for MrJuneJune PWA +const CACHE_VERSION = 'v1'; +const CACHE_NAME = `mrjunejune-${CACHE_VERSION}`; + +// Files to cache immediately on install +const STATIC_CACHE = [ + '/', + '/base.css', + '/public/epi_all_colors.svg', + '/public/fonts/Roboto-Regular.ttf', + '/public/fonts/Roboto-Thin.ttf', + '/public/fonts/more-sugar.regular.otf', + '/public/fonts/more-sugar.thin.otf', +]; + +// Install event - cache static assets +self.addEventListener('install', (event) => { + console.log('[SW] Installing service worker...'); + + event.waitUntil( + caches.open(CACHE_NAME).then((cache) => { + console.log('[SW] Caching static assets'); + return cache.addAll(STATIC_CACHE); + }).then(() => { + console.log('[SW] Skip waiting'); + return self.skipWaiting(); + }) + ); +}); + +// Activate event - clean up old caches +self.addEventListener('activate', (event) => { + console.log('[SW] Activating service worker...'); + + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames.map((cacheName) => { + if (cacheName !== CACHE_NAME) { + console.log('[SW] Deleting old cache:', cacheName); + return caches.delete(cacheName); + } + }) + ); + }).then(() => { + console.log('[SW] Claiming clients'); + 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 chrome-extension and other non-http(s) requests + if (!url.protocol.startsWith('http')) { + return; + } + + // Skip API calls and media uploads (always go to network) + if (url.pathname.startsWith('/api/')) { + return; + } + + event.respondWith( + caches.match(request).then((cachedResponse) => { + if (cachedResponse) { + console.log('[SW] Serving from cache:', url.pathname); + return cachedResponse; + } + + // Not in cache, fetch from network + return fetch(request).then((networkResponse) => { + // Only cache successful responses + if (!networkResponse || networkResponse.status !== 200 || networkResponse.type === 'error') { + return networkResponse; + } + + // Cache specific file types + const shouldCache = + url.pathname.endsWith('.css') || + url.pathname.endsWith('.js') || + url.pathname.endsWith('.svg') || + url.pathname.endsWith('.png') || + url.pathname.endsWith('.jpg') || + url.pathname.endsWith('.webp') || + url.pathname.endsWith('.woff') || + url.pathname.endsWith('.woff2') || + url.pathname.endsWith('.ttf') || + url.pathname.endsWith('.otf') || + url.pathname.startsWith('/blog/') || + url.pathname.startsWith('/notes/') || + url.pathname === '/'; + + if (shouldCache) { + const responseToCache = networkResponse.clone(); + caches.open(CACHE_NAME).then((cache) => { + console.log('[SW] Caching new resource:', url.pathname); + cache.put(request, responseToCache); + }); + } + + return networkResponse; + }).catch((error) => { + console.log('[SW] Fetch failed:', error); + + // Return offline page for HTML requests + if (request.headers.get('Accept').includes('text/html')) { + return caches.match('/offline.html'); + } + }); + }) + ); +}); + +// Handle messages from the client +self.addEventListener('message', (event) => { + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } + + if (event.data && event.data.type === 'CLEAR_CACHE') { + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames.map((cacheName) => caches.delete(cacheName)) + ); + }) + ); + } +});