PWA-Reload sauber: nur bei App-Oeffnen oder Dashboard-Klick, niemals bei Eingaben; version.txt im Build, SW v4, Carousel 4 Icons, Kalender ohne Komma
This commit is contained in:
@@ -2,46 +2,93 @@ import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import App from './App.jsx'
|
||||
|
||||
// ── Build-Zeit Check & Auto-Reload ───────────────────────────────────────────
|
||||
// ── Auto-Reload bei neuem Build ───────────────────────────────────────────────
|
||||
// Regeln:
|
||||
// - Neuer Build wird erkannt → nur Flag setzen (window.__newBuildAvailable)
|
||||
// - Reload passiert NUR:
|
||||
// a) beim Öffnen der App (App kommt aus Hintergrund in den Vordergrund)
|
||||
// b) beim Klick auf "Dashboard" (in App.jsx geprüft)
|
||||
// - NIEMALS während einer Eingabe oder bei ungespeicherten Feldern
|
||||
window.__newBuildAvailable = false;
|
||||
|
||||
const STORED_KEY = 'dd_build_time';
|
||||
|
||||
async function checkBuildTime({ reloadIfChanged = false } = {}) {
|
||||
// Läuft gerade eine Eingabe? (Fokus auf Eingabefeld)
|
||||
function isUserTyping() {
|
||||
const el = document.activeElement;
|
||||
if (!el) return false;
|
||||
const tag = el.tagName;
|
||||
return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || el.isContentEditable;
|
||||
}
|
||||
|
||||
// Stehen irgendwo ungespeicherte Eingaben in Textfeldern?
|
||||
function hasUnsavedInput() {
|
||||
const fields = document.querySelectorAll('input, textarea');
|
||||
for (const f of fields) {
|
||||
const isText = f.tagName === 'TEXTAREA' ||
|
||||
['text','search','url','email','number','password','tel'].includes(f.type);
|
||||
if (isText && f.value && f.value.trim() !== '') return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
async function getBuildTime() {
|
||||
try {
|
||||
const r = await fetch('/api/build-time', { cache: 'no-store' });
|
||||
const { buildTime } = await r.json();
|
||||
const stored = localStorage.getItem(STORED_KEY);
|
||||
if (!stored) {
|
||||
localStorage.setItem(STORED_KEY, buildTime);
|
||||
} else if (stored !== buildTime) {
|
||||
localStorage.setItem(STORED_KEY, buildTime);
|
||||
window.__newBuildAvailable = true;
|
||||
// Wenn explizit gewünscht (App-Fokus): sofort neu laden
|
||||
if (reloadIfChanged) window.location.reload();
|
||||
}
|
||||
} catch {}
|
||||
return buildTime;
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
// Beim Start prüfen
|
||||
checkBuildTime();
|
||||
// Alle 60s im Hintergrund
|
||||
setInterval(() => checkBuildTime(), 60_000);
|
||||
|
||||
// Wenn die App/der Tab wieder in den Vordergrund kommt → prüfen und ggf. sofort neu laden
|
||||
// Das deckt den Fall ab: PWA vom Homescreen öffnen
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
checkBuildTime({ reloadIfChanged: true });
|
||||
// Prüft die Build-Zeit und setzt nur das Flag (lädt nie von selbst)
|
||||
async function checkBuild() {
|
||||
const bt = await getBuildTime();
|
||||
if (!bt) return;
|
||||
const stored = localStorage.getItem(STORED_KEY);
|
||||
if (!stored) {
|
||||
localStorage.setItem(STORED_KEY, bt);
|
||||
} else if (stored !== bt) {
|
||||
localStorage.setItem(STORED_KEY, bt);
|
||||
window.__newBuildAvailable = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Sicherer Reload: nur wenn neuer Build da UND keine Eingabe aktiv/ungespeichert
|
||||
// Wird von App.jsx beim Dashboard-Klick aufgerufen. Gibt true zurück wenn neu geladen wird.
|
||||
window.__safeReloadIfNewBuild = function () {
|
||||
if (!window.__newBuildAvailable) return false;
|
||||
if (isUserTyping() || hasUnsavedInput()) return false;
|
||||
window.location.reload();
|
||||
return true;
|
||||
};
|
||||
|
||||
// Beim Start einmal prüfen (App-Öffnen Fall 1: Kaltstart)
|
||||
checkBuild();
|
||||
// Im Hintergrund alle 60s nur das Flag aktualisieren
|
||||
setInterval(() => checkBuild(), 60_000);
|
||||
|
||||
// App-Öffnen Fall 2: App kommt aus dem Hintergrund zurück in den Vordergrund
|
||||
document.addEventListener('visibilitychange', async () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
await checkBuild();
|
||||
// Nur laden wenn der User nicht gerade tippt und nichts ungespeichert ist
|
||||
if (!isUserTyping() && !hasUnsavedInput()) {
|
||||
window.__safeReloadIfNewBuild();
|
||||
}
|
||||
}
|
||||
});
|
||||
window.addEventListener('focus', () => {
|
||||
checkBuildTime({ reloadIfChanged: true });
|
||||
});
|
||||
|
||||
// ── Service Worker ────────────────────────────────────────────────────────────
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.addEventListener('message', (e) => {
|
||||
if (e.data?.type === 'SW_ACTIVATED') {
|
||||
window.__newBuildAvailable = true; // nur merken
|
||||
}
|
||||
});
|
||||
|
||||
navigator.serviceWorker.register('/sw.js').then(reg => {
|
||||
// Browser regelmäßig nach neuem SW suchen lassen
|
||||
setInterval(() => reg.update().catch(()=>{}), 60_000);
|
||||
if (reg.waiting) reg.waiting.postMessage('SKIP_WAITING');
|
||||
reg.addEventListener('updatefound', () => {
|
||||
const nw = reg.installing;
|
||||
@@ -49,9 +96,6 @@ if ('serviceWorker' in navigator) {
|
||||
if (nw.state === 'installed') nw.postMessage('SKIP_WAITING');
|
||||
});
|
||||
});
|
||||
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
||||
window.__newBuildAvailable = true;
|
||||
});
|
||||
}).catch(()=>{});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user