Fix PWA-Update: window global statt dynamic import, synchroner Reload bei Seitenwechsel
This commit is contained in:
@@ -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))
|
||||
);
|
||||
|
||||
@@ -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'});
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user