diff --git a/public/sw.js b/public/sw.js index 883a853..86ac5c5 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,5 +1,5 @@ // PinchChat Service Worker — cache static assets for offline/instant load -const CACHE_NAME = 'pinchchat-v1'; +const CACHE_NAME = 'pinchchat-__SW_VERSION__'; // Cache static assets on install self.addEventListener('install', (event) => { diff --git a/src/main.tsx b/src/main.tsx index 86abb44..5e439c2 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -8,7 +8,24 @@ import './index.css' // Register service worker for PWA support (offline caching, installability) if ('serviceWorker' in navigator) { window.addEventListener('load', () => { - navigator.serviceWorker.register('/sw.js').catch(() => { + navigator.serviceWorker.register('/sw.js').then((reg) => { + // Check for updates periodically (every 30 min) + setInterval(() => reg.update().catch(() => {}), 30 * 60 * 1000); + + // When a new SW is installed and waiting, reload to activate it + reg.addEventListener('updatefound', () => { + const newWorker = reg.installing; + if (!newWorker) return; + newWorker.addEventListener('statechange', () => { + if (newWorker.state === 'installed' && navigator.serviceWorker.controller) { + // New version available — reload on next navigation + // The new SW called skipWaiting(), so it activates immediately + // Reload to pick up cached assets from the new cache version + window.location.reload(); + } + }); + }); + }).catch(() => { // SW registration failed — app works fine without it }); }); diff --git a/vite.config.ts b/vite.config.ts index 23774b6..dc20622 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,13 +1,33 @@ -import { defineConfig } from 'vite' +import { defineConfig, type Plugin } from 'vite' import react from '@vitejs/plugin-react' import tailwindcss from '@tailwindcss/vite' +import { readFileSync, writeFileSync, existsSync } from 'node:fs' +import { resolve, dirname } from 'node:path' +import { fileURLToPath } from 'node:url' import pkg from './package.json' with { type: 'json' } +const __dirname = dirname(fileURLToPath(import.meta.url)) + +/** Replace __SW_VERSION__ in sw.js with version+timestamp at build time */ +function swVersionPlugin(): Plugin { + const version = `${pkg.version}-${Date.now()}` + return { + name: 'sw-version', + closeBundle() { + const swPath = resolve(__dirname, 'dist', 'sw.js') + if (existsSync(swPath)) { + const content = readFileSync(swPath, 'utf-8') + writeFileSync(swPath, content.replace(/__SW_VERSION__/g, version)) + } + }, + } +} + export default defineConfig({ define: { __APP_VERSION__: JSON.stringify(pkg.version), }, - plugins: [react(), tailwindcss()], + plugins: [react(), tailwindcss(), swVersionPlugin()], build: { rollupOptions: { output: {