Mercurial
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 208:5d3e116dd745 | 209:3b47e82ac57e |
|---|---|
| 1 // Service Worker for MrJuneJune PWA | |
| 2 const CACHE_VERSION = 'v1'; | |
| 3 const CACHE_NAME = `mrjunejune-${CACHE_VERSION}`; | |
| 4 | |
| 5 // Files to cache immediately on install | |
| 6 const STATIC_CACHE = [ | |
| 7 '/', | |
| 8 '/base.css', | |
| 9 '/public/epi_all_colors.svg', | |
| 10 '/public/fonts/Roboto-Regular.ttf', | |
| 11 '/public/fonts/Roboto-Thin.ttf', | |
| 12 '/public/fonts/more-sugar.regular.otf', | |
| 13 '/public/fonts/more-sugar.thin.otf', | |
| 14 ]; | |
| 15 | |
| 16 // Install event - cache static assets | |
| 17 self.addEventListener('install', (event) => { | |
| 18 console.log('[SW] Installing service worker...'); | |
| 19 | |
| 20 event.waitUntil( | |
| 21 caches.open(CACHE_NAME).then((cache) => { | |
| 22 console.log('[SW] Caching static assets'); | |
| 23 return cache.addAll(STATIC_CACHE); | |
| 24 }).then(() => { | |
| 25 console.log('[SW] Skip waiting'); | |
| 26 return self.skipWaiting(); | |
| 27 }) | |
| 28 ); | |
| 29 }); | |
| 30 | |
| 31 // Activate event - clean up old caches | |
| 32 self.addEventListener('activate', (event) => { | |
| 33 console.log('[SW] Activating service worker...'); | |
| 34 | |
| 35 event.waitUntil( | |
| 36 caches.keys().then((cacheNames) => { | |
| 37 return Promise.all( | |
| 38 cacheNames.map((cacheName) => { | |
| 39 if (cacheName !== CACHE_NAME) { | |
| 40 console.log('[SW] Deleting old cache:', cacheName); | |
| 41 return caches.delete(cacheName); | |
| 42 } | |
| 43 }) | |
| 44 ); | |
| 45 }).then(() => { | |
| 46 console.log('[SW] Claiming clients'); | |
| 47 return self.clients.claim(); | |
| 48 }) | |
| 49 ); | |
| 50 }); | |
| 51 | |
| 52 // Fetch event - serve from cache, fallback to network | |
| 53 self.addEventListener('fetch', (event) => { | |
| 54 const { request } = event; | |
| 55 const url = new URL(request.url); | |
| 56 | |
| 57 // Skip non-GET requests | |
| 58 if (request.method !== 'GET') { | |
| 59 return; | |
| 60 } | |
| 61 | |
| 62 // Skip chrome-extension and other non-http(s) requests | |
| 63 if (!url.protocol.startsWith('http')) { | |
| 64 return; | |
| 65 } | |
| 66 | |
| 67 // Skip API calls and media uploads (always go to network) | |
| 68 if (url.pathname.startsWith('/api/')) { | |
| 69 return; | |
| 70 } | |
| 71 | |
| 72 event.respondWith( | |
| 73 caches.match(request).then((cachedResponse) => { | |
| 74 if (cachedResponse) { | |
| 75 console.log('[SW] Serving from cache:', url.pathname); | |
| 76 return cachedResponse; | |
| 77 } | |
| 78 | |
| 79 // Not in cache, fetch from network | |
| 80 return fetch(request).then((networkResponse) => { | |
| 81 // Only cache successful responses | |
| 82 if (!networkResponse || networkResponse.status !== 200 || networkResponse.type === 'error') { | |
| 83 return networkResponse; | |
| 84 } | |
| 85 | |
| 86 // Cache specific file types | |
| 87 const shouldCache = | |
| 88 url.pathname.endsWith('.css') || | |
| 89 url.pathname.endsWith('.js') || | |
| 90 url.pathname.endsWith('.svg') || | |
| 91 url.pathname.endsWith('.png') || | |
| 92 url.pathname.endsWith('.jpg') || | |
| 93 url.pathname.endsWith('.webp') || | |
| 94 url.pathname.endsWith('.woff') || | |
| 95 url.pathname.endsWith('.woff2') || | |
| 96 url.pathname.endsWith('.ttf') || | |
| 97 url.pathname.endsWith('.otf') || | |
| 98 url.pathname.startsWith('/blog/') || | |
| 99 url.pathname.startsWith('/notes/') || | |
| 100 url.pathname === '/'; | |
| 101 | |
| 102 if (shouldCache) { | |
| 103 const responseToCache = networkResponse.clone(); | |
| 104 caches.open(CACHE_NAME).then((cache) => { | |
| 105 console.log('[SW] Caching new resource:', url.pathname); | |
| 106 cache.put(request, responseToCache); | |
| 107 }); | |
| 108 } | |
| 109 | |
| 110 return networkResponse; | |
| 111 }).catch((error) => { | |
| 112 console.log('[SW] Fetch failed:', error); | |
| 113 | |
| 114 // Return offline page for HTML requests | |
| 115 if (request.headers.get('Accept').includes('text/html')) { | |
| 116 return caches.match('/offline.html'); | |
| 117 } | |
| 118 }); | |
| 119 }) | |
| 120 ); | |
| 121 }); | |
| 122 | |
| 123 // Handle messages from the client | |
| 124 self.addEventListener('message', (event) => { | |
| 125 if (event.data && event.data.type === 'SKIP_WAITING') { | |
| 126 self.skipWaiting(); | |
| 127 } | |
| 128 | |
| 129 if (event.data && event.data.type === 'CLEAR_CACHE') { | |
| 130 event.waitUntil( | |
| 131 caches.keys().then((cacheNames) => { | |
| 132 return Promise.all( | |
| 133 cacheNames.map((cacheName) => caches.delete(cacheName)) | |
| 134 ); | |
| 135 }) | |
| 136 ); | |
| 137 } | |
| 138 }); |