Fix: doppelte SW-Registrierung entfernt, Reload bei App-Fokus; Schnellzugriff gleichgross; Kalender ohne Komma

This commit is contained in:
2026-06-02 21:15:22 +02:00
parent fe899f5287
commit 9f8891395a
4 changed files with 24 additions and 26 deletions

View File

@@ -17,14 +17,5 @@
<body> <body>
<div id="root"></div> <div id="root"></div>
<script type="module" src="/src/main.jsx"></script> <script type="module" src="/src/main.jsx"></script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(r => console.log('SW registriert:', r.scope))
.catch(e => console.warn('SW Fehler:', e));
});
}
</script>
</body> </body>
</html> </html>

View File

@@ -459,7 +459,7 @@ function QuickLinksCarousel({ links }) {
}; };
if (pageCount <= 1) return ( if (pageCount <= 1) return (
<div style={{ display:'grid', gridTemplateColumns:`repeat(${perRow}, 1fr)`, gap, justifyItems:'center' }}> <div style={{ display:'grid', gridTemplateColumns:`repeat(${perRow}, 1fr)`, gap }}>
{links.map(l => <QuickLinkIcon key={l.id} l={l}/>)} {links.map(l => <QuickLinkIcon key={l.id} l={l}/>)}
</div> </div>
); );
@@ -476,7 +476,6 @@ function QuickLinksCarousel({ links }) {
display:'grid', display:'grid',
gridTemplateColumns:`repeat(${perRow}, 1fr)`, gridTemplateColumns:`repeat(${perRow}, 1fr)`,
gridTemplateRows:'repeat(2, auto)', gridTemplateRows:'repeat(2, auto)',
justifyItems:'center',
gap, flexShrink:0, scrollSnapAlign:'start', gap, flexShrink:0, scrollSnapAlign:'start',
width:'100%', minWidth:'100%', width:'100%', minWidth:'100%',
}}> }}>
@@ -507,7 +506,7 @@ function QuickLinkIcon({ l }) {
style={{ display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:3, style={{ display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', gap:3,
padding:'4px 2px', background:'rgba(255,255,255,0.04)', padding:'4px 2px', background:'rgba(255,255,255,0.04)',
border:'1px solid rgba(255,255,255,0.08)', borderRadius:10, border:'1px solid rgba(255,255,255,0.08)', borderRadius:10,
textDecoration:'none', height:48, cursor:'pointer' }}> textDecoration:'none', height:48, width:'100%', boxSizing:'border-box', cursor:'pointer' }}>
{l.icon && l.icon.startsWith('http') {l.icon && l.icon.startsWith('http')
? <img src={l.icon} alt="" style={{width:16,height:16,objectFit:'contain'}} onError={e=>e.target.style.display='none'}/> ? <img src={l.icon} alt="" style={{width:16,height:16,objectFit:'contain'}} onError={e=>e.target.style.display='none'}/>
: <span style={{ fontSize:16 }}>{l.icon}</span>} : <span style={{ fontSize:16 }}>{l.icon}</span>}
@@ -2381,7 +2380,7 @@ export default function App() {
const [user,setUser]=useState(null); const [user,setUser]=useState(null);
const [active,setActiveRaw]=useState('dashboard'); const [active,setActiveRaw]=useState('dashboard');
const setActive = (page) => { const setActive = (page) => {
import('./main.jsx').then(m => m.reloadIfNewBuild?.()).catch(()=>{}); if (window.__newBuildAvailable) { window.location.reload(); return; }
setActiveRaw(page); setActiveRaw(page);
}; };
const [toastMsg,setToastMsg]=useState({msg:'',type:'success'}); const [toastMsg,setToastMsg]=useState({msg:'',type:'success'});

View File

@@ -172,7 +172,7 @@ export default function CalendarWidget() {
<span style={{ color:'rgba(255,255,255,0.35)', fontFamily:'monospace', fontSize:9, flexShrink:0 }}> <span style={{ color:'rgba(255,255,255,0.35)', fontFamily:'monospace', fontSize:9, flexShrink:0 }}>
{nextEvent.allDay {nextEvent.allDay
? nextEvent.dtstart.toLocaleDateString('de-DE',{day:'numeric',month:'short'}) ? nextEvent.dtstart.toLocaleDateString('de-DE',{day:'numeric',month:'short'})
: nextEvent.dtstart.toLocaleString('de-DE',{day:'2-digit',month:'2-digit',hour:'2-digit',minute:'2-digit'}) : `${nextEvent.dtstart.toLocaleDateString('de-DE',{day:'2-digit',month:'2-digit'})} ${nextEvent.dtstart.toLocaleTimeString('de-DE',{hour:'2-digit',minute:'2-digit'})}`
} }
</span> </span>
</div> </div>

View File

@@ -2,13 +2,12 @@ import React from 'react'
import ReactDOM from 'react-dom/client' import ReactDOM from 'react-dom/client'
import App from './App.jsx' import App from './App.jsx'
// ── Build-Zeit Check ────────────────────────────────────────────────────────── // ── Build-Zeit Check & Auto-Reload ───────────────────────────────────────────
// Globale Variable synchron lesbar, kein dynamischer Import nötig
window.__newBuildAvailable = false; window.__newBuildAvailable = false;
const STORED_KEY = 'dd_build_time'; const STORED_KEY = 'dd_build_time';
async function checkBuildTime() { async function checkBuildTime({ reloadIfChanged = false } = {}) {
try { try {
const r = await fetch('/api/build-time', { cache: 'no-store' }); const r = await fetch('/api/build-time', { cache: 'no-store' });
const { buildTime } = await r.json(); const { buildTime } = await r.json();
@@ -18,33 +17,42 @@ async function checkBuildTime() {
} else if (stored !== buildTime) { } else if (stored !== buildTime) {
localStorage.setItem(STORED_KEY, buildTime); localStorage.setItem(STORED_KEY, buildTime);
window.__newBuildAvailable = true; window.__newBuildAvailable = true;
// Wenn explizit gewünscht (App-Fokus): sofort neu laden
if (reloadIfChanged) window.location.reload();
} }
} catch {} } catch {}
} }
// Sofort beim Start prüfen, dann alle 60s // Beim Start prüfen
checkBuildTime(); checkBuildTime();
setInterval(checkBuildTime, 60_000); // 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 });
}
});
window.addEventListener('focus', () => {
checkBuildTime({ reloadIfChanged: true });
});
// ── Service Worker ──────────────────────────────────────────────────────────── // ── Service Worker ────────────────────────────────────────────────────────────
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(reg => { navigator.serviceWorker.register('/sw.js').then(reg => {
if (reg.waiting) reg.waiting.postMessage('SKIP_WAITING'); if (reg.waiting) reg.waiting.postMessage('SKIP_WAITING');
reg.addEventListener('updatefound', () => { reg.addEventListener('updatefound', () => {
const nw = reg.installing; const nw = reg.installing;
nw?.addEventListener('statechange', () => { nw?.addEventListener('statechange', () => {
if (nw.state === 'installed') { if (nw.state === 'installed') nw.postMessage('SKIP_WAITING');
nw.postMessage('SKIP_WAITING');
}
}); });
}); });
// Neuer SW aktiv → beim nächsten Seitenwechsel reloaden
navigator.serviceWorker.addEventListener('controllerchange', () => { navigator.serviceWorker.addEventListener('controllerchange', () => {
window.__newBuildAvailable = true; window.__newBuildAvailable = true;
}); });
}); }).catch(()=>{});
} }
ReactDOM.createRoot(document.getElementById('root')).render( ReactDOM.createRoot(document.getElementById('root')).render(