view 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 source

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