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))
+        );
+      })
+    );
+  }
+});