diff --git a/frontend/public/sw.js b/frontend/public/sw.js index 615a593..0f019ec 100644 --- a/frontend/public/sw.js +++ b/frontend/public/sw.js @@ -1,52 +1,44 @@ -// Service Worker – stellt sicher dass nach einem Deploy immer der neue Stand geladen wird -// index.html und API: immer vom Netzwerk (no-store) -// Assets (JS/CSS mit Hash): Network-first mit Cache-Fallback für Offline - -const CACHE_NAME = 'dickendock-v2'; +// Service Worker – index.html immer frisch vom Server, Assets gecacht +const CACHE_NAME = 'dickendock-v3'; self.addEventListener('install', () => { - self.skipWaiting(); // sofort aktivieren ohne auf Tab-Schließen zu warten + self.skipWaiting(); }); self.addEventListener('activate', e => { e.waitUntil( caches.keys() .then(keys => Promise.all(keys.map(k => caches.delete(k)))) - .then(() => self.clients.claim()) // alle offenen Tabs sofort übernehmen + .then(() => self.clients.claim()) ); }); self.addEventListener('fetch', e => { const url = new URL(e.request.url); - // API-Requests: nie cachen + // API: immer Netzwerk if (url.pathname.startsWith('/api/')) { e.respondWith(fetch(e.request)); return; } - // HTML-Navigation (index.html, /, /*, alle Routen): immer frisch vom Server - // Das ist der Kern: verhindert dass eine alte index.html aus dem HTTP-Cache kommt + // HTML-Navigation: immer frisch vom Server, nie aus Cache if (e.request.mode === 'navigate') { e.respondWith( - fetch(e.request, { - cache: 'no-store', // Browser-HTTP-Cache umgehen - headers: { 'Cache-Control': 'no-cache' } - }).catch(() => caches.match('/index.html')) // nur bei echtem Offline-Fall: Cache-Fallback + fetch(e.request, { cache: 'no-store' }) + .catch(() => caches.match('/index.html')) ); return; } - // Statische Assets (Vite baut mit Content-Hash im Dateinamen): - // Network-first, bei Fehler Cache-Fallback (für Offline-Nutzung) + // Statische Assets (JS/CSS mit Hash): Network-first, Cache als Fallback e.respondWith( fetch(e.request) - .then(response => { - if (response.ok) { - const clone = response.clone(); - caches.open(CACHE_NAME).then(c => c.put(e.request, clone)); + .then(res => { + if (res.ok) { + caches.open(CACHE_NAME).then(c => c.put(e.request, res.clone())); } - return response; + return res; }) .catch(() => caches.match(e.request)) ); diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 0fc7a50..dc2a61c 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -2383,7 +2383,10 @@ export default function App() { const [user,setUser]=useState(null); const [active,setActiveRaw]=useState('dashboard'); const setActive = (page) => { - import('./main.jsx').then(m => m.reloadIfNewBuild?.()).catch(()=>{}); + if (window.__newBuildAvailable) { + window.location.reload(); + return; // kein Rendern mehr, Reload läuft + } setActiveRaw(page); }; const [toastMsg,setToastMsg]=useState({msg:'',type:'success'}); diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 1f02f61..9c899fb 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -2,10 +2,11 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.jsx' -// ── Auto-Reload bei neuem Build ─────────────────────────────────────────────── -// Reload passiert beim nächsten Seitenwechsel (setActive) — nicht mitten in Eingaben +// ── Build-Zeit Check ────────────────────────────────────────────────────────── +// Globale Variable – synchron lesbar, kein dynamischer Import nötig +window.__newBuildAvailable = false; + const STORED_KEY = 'dd_build_time'; -let newBuildAvailable = false; async function checkBuildTime() { try { @@ -16,43 +17,32 @@ async function checkBuildTime() { localStorage.setItem(STORED_KEY, buildTime); } else if (stored !== buildTime) { localStorage.setItem(STORED_KEY, buildTime); - newBuildAvailable = true; + window.__newBuildAvailable = true; } } catch {} } -// Beim Start sofort prüfen (PWA-Launch: index.html könnte gecacht sein) +// Sofort beim Start prüfen, dann alle 60s checkBuildTime(); -// Danach alle 60s im Hintergrund weiter prüfen setInterval(checkBuildTime, 60_000); -// Wird von App.jsx bei jedem Seitenwechsel aufgerufen -export function reloadIfNewBuild() { - if (newBuildAvailable) window.location.reload(); -} - // ── Service Worker ──────────────────────────────────────────────────────────── if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js').then(reg => { - // Falls schon ein wartender SW vorhanden → sofort aktivieren - if (reg.waiting) { - reg.waiting.postMessage('SKIP_WAITING'); - } + if (reg.waiting) reg.waiting.postMessage('SKIP_WAITING'); reg.addEventListener('updatefound', () => { const nw = reg.installing; nw?.addEventListener('statechange', () => { if (nw.state === 'installed') { - // Neuer SW fertig installiert → sofort aktivieren nw.postMessage('SKIP_WAITING'); } }); }); - // Wenn der SW-Controller gewechselt hat (neuer SW aktiv): - // newBuildAvailable setzen — Reload passiert beim nächsten setActive + // Neuer SW aktiv → beim nächsten Seitenwechsel reloaden navigator.serviceWorker.addEventListener('controllerchange', () => { - newBuildAvailable = true; + window.__newBuildAvailable = true; }); }); }