Files
dickendock/frontend/src/main.jsx

102 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
// ── Auto-Reload bei neuem Build ───────────────────────────────────────────────
// __BUILD_TIME__ wird von Vite zur Build-Zeit aus version.txt gebacken.
// /api/build-time liefert denselben Wert vom Server.
// Wenn sie abweichen → neues Build wurde deployed → Reload.
// Kein localStorage, keine Race-Condition.
const MY_BUILD_TIME = __BUILD_TIME__;
window.__newBuildAvailable = false;
function isUserTyping() {
const el = document.activeElement;
if (!el) return false;
const tag = el.tagName;
return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || el.isContentEditable;
}
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();
return buildTime;
} catch { return null; }
}
// Prüft ob Server-Build != Bundle-Build → Flag setzen
// reloadNow: sofort laden wenn kein Input aktiv (für App-Fokus)
async function checkBuild({ reloadNow = false } = {}) {
const serverBt = await getBuildTime();
if (!serverBt) return;
if (serverBt !== MY_BUILD_TIME) {
window.__newBuildAvailable = true;
if (reloadNow && !isUserTyping() && !hasUnsavedInput()) {
window.location.reload();
}
}
}
// Sicherer Reload bei Seitenwechsel (aufgerufen aus App.jsx setActive)
window.__safeReloadIfNewBuild = function () {
if (!window.__newBuildAvailable) return false;
if (isUserTyping() || hasUnsavedInput()) return false;
window.location.reload();
return true;
};
// Beim Start prüfen nach 3s sofort reloaden wenn neue Version (App hat Zeit zu rendern)
checkBuild().then(() => {
if (window.__newBuildAvailable && !isUserTyping() && !hasUnsavedInput()) {
setTimeout(() => {
if (!isUserTyping() && !hasUnsavedInput()) window.location.reload();
}, 3000);
}
});
// Im Hintergrund alle 60s
setInterval(() => checkBuild(), 60_000);
// App kommt in den Vordergrund → prüfen und bei Bedarf sofort laden
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
checkBuild({ reloadNow: true });
}
});
// ── Service Worker ────────────────────────────────────────────────────────────
if ('serviceWorker' in navigator) {
navigator.serviceWorker.addEventListener('message', (e) => {
// SW hat neue Version aktiviert → sofort reloaden
if (e.data?.type === 'SW_ACTIVATED') {
window.__newBuildAvailable = true;
if (!isUserTyping() && !hasUnsavedInput()) window.location.reload();
}
});
navigator.serviceWorker.register('/sw.js').then(reg => {
setInterval(() => reg.update().catch(()=>{}), 60_000);
if (reg.waiting) reg.waiting.postMessage('SKIP_WAITING');
reg.addEventListener('updatefound', () => {
const nw = reg.installing;
nw?.addEventListener('statechange', () => {
if (nw.state === 'installed') nw.postMessage('SKIP_WAITING');
});
});
}).catch(()=>{});
}
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode><App /></React.StrictMode>
)