fix: mobile indicator visibility, sequential pre-cache, and verified ready state

This commit is contained in:
2025-12-21 00:02:39 +01:00
parent 000f2582a3
commit 74a10b193c
2 changed files with 89 additions and 80 deletions

View File

@@ -10,7 +10,6 @@ export default function OfflineIndicator() {
useEffect(() => {
setIsOffline(!navigator.onLine);
// Check if bunker was already ready from previous session
const savedReady = localStorage.getItem('whisky_bunker_ready') === 'true';
setIsBunkerReady(savedReady);
@@ -22,7 +21,7 @@ export default function OfflineIndicator() {
setProgress(event.data.progress);
}
if (event.data?.type === 'PRECACHE_COMPLETE' || event.data?.type === 'BUNKER_STATUS') {
console.log('🛡️ PWA: Bunker is ready for offline use!');
console.log('🛡️ PWA: Bunker is ready!');
setIsBunkerReady(true);
localStorage.setItem('whisky_bunker_ready', 'true');
}
@@ -34,27 +33,10 @@ export default function OfflineIndicator() {
if ('serviceWorker' in navigator) {
navigator.serviceWorker.addEventListener('message', handleMessage);
// Proactive check
navigator.serviceWorker.ready.then((registration) => {
if (registration.active) {
registration.active.postMessage({ type: 'CHECK_BUNKER_STATUS' });
}
});
// Fallback: If after 20s we still think we are loading but SW is active, assume ready
const timer = setTimeout(() => {
const isSwActive = !!navigator.serviceWorker.controller;
if (!isBunkerReady && isSwActive) {
setIsBunkerReady(true);
localStorage.setItem('whisky_bunker_ready', 'true');
}
}, 20000);
return () => {
clearTimeout(timer);
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
navigator.serviceWorker.removeEventListener('message', handleMessage);
};
// Initial check: if already active, ask for status
if (navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage({ type: 'CHECK_BUNKER_STATUS' });
}
}
return () => {
@@ -64,41 +46,62 @@ export default function OfflineIndicator() {
navigator.serviceWorker.removeEventListener('message', handleMessage);
}
};
}, [isBunkerReady]);
}, []);
// 1. OFFLINE BAR (TOP)
if (isOffline) {
return (
<div className="fixed top-0 left-0 w-full bg-red-600 text-white text-[10px] font-black uppercase tracking-widest py-1 flex items-center justify-center gap-2 z-[9999] animate-pulse">
<WifiOff size={12} />
<div className="fixed top-0 left-0 w-full bg-red-600 text-white text-[11px] font-black uppercase tracking-[0.2em] py-2 flex items-center justify-center gap-2 z-[10001] shadow-xl animate-in slide-in-from-top duration-300">
<WifiOff size={14} className="animate-pulse" />
Offline-Modus: Bunker aktiv 🛡
</div>
);
}
// 2. READY STATUS (BOTTOM RIGHT)
if (isBunkerReady) {
return (
<div className="fixed bottom-20 right-4 z-[90] animate-in fade-in slide-in-from-right-4 duration-500">
<div className="bg-zinc-900/80 backdrop-blur-md border border-green-500/30 px-3 py-1.5 rounded-full flex items-center gap-2 shadow-lg shadow-green-500/10 group hover:bg-zinc-900 transition-colors cursor-help">
<ShieldCheck size={14} className="text-green-500" />
<span className="text-[9px] font-black uppercase tracking-widest text-zinc-300">
Bunker Aktiv
</span>
<div className="absolute bottom-full right-0 mb-2 w-48 p-2 bg-zinc-900 text-[10px] text-zinc-400 rounded-xl border border-white/10 opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none shadow-2xl">
Die App ist vollständig im "Bunker" gespeichert und funktioniert auch ohne Internet.
<div className="fixed bottom-24 right-4 z-[10000] animate-in fade-in slide-in-from-right-10 duration-700 pointer-events-auto">
<div className="bg-zinc-900/95 backdrop-blur-xl border border-green-500/40 px-4 py-2.5 rounded-2xl flex items-center gap-2.5 shadow-[0_20px_50px_rgba(0,0,0,0.5),0_0_20px_rgba(34,197,94,0.1)] group hover:scale-105 transition-all cursor-help ring-1 ring-white/10">
<div className="relative">
<ShieldCheck size={18} className="text-green-500" />
<div className="absolute -top-1 -right-1 w-2 h-2 bg-green-500 rounded-full blur-[2px] animate-pulse" />
</div>
<div className="flex flex-col">
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-white">Bunker Aktiv</span>
<span className="text-[8px] font-bold text-zinc-500 uppercase tracking-widest">Vollständig Offline fähig</span>
</div>
{/* Tooltip */}
<div className="absolute bottom-full right-0 mb-3 w-56 p-3 bg-zinc-950 text-[10px] text-zinc-400 rounded-2xl border border-white/10 opacity-0 group-hover:opacity-100 transition-all pointer-events-none shadow-2xl translate-y-2 group-hover:translate-y-0">
<p className="leading-relaxed">
<strong className="text-white block mb-1">Status: Gesichert</strong>
Alle wichtigen Bestandteile der App sind lokal gespeichert. Du kannst die App jederzeit im Funkloch nutzen.
</p>
</div>
</div>
</div>
);
}
// Show nothing if online and not ready yet (or show a loading state?)
// 3. LOADING STATUS (BOTTOM RIGHT)
return (
<div className="fixed bottom-20 right-4 z-[90] animate-in fade-in slide-in-from-right-4">
<div className="bg-zinc-900/80 backdrop-blur-md border border-amber-500/30 px-3 py-1.5 rounded-full flex items-center gap-2 shadow-lg shadow-amber-500/10">
<div className="w-1.5 h-1.5 bg-amber-500 rounded-full animate-pulse" />
<span className="text-[9px] font-black uppercase tracking-widest text-zinc-300">
Bunker wird geladen... {progress > 0 ? `${progress}%` : ''}
</span>
<div className="fixed bottom-24 right-4 z-[10000] animate-in fade-in slide-in-from-right-10 duration-500 pointer-events-auto">
<div className="bg-zinc-900/95 backdrop-blur-xl border border-amber-500/40 px-4 py-2.5 rounded-2xl flex items-center gap-3 shadow-[0_20px_50px_rgba(0,0,0,0.5),0_0_20px_rgba(245,158,11,0.1)] ring-1 ring-white/10">
<div className="relative w-5 h-5 flex items-center justify-center">
<div className="absolute inset-0 border-2 border-amber-500/20 rounded-full" />
<div
className="absolute inset-0 border-2 border-amber-500 rounded-full border-t-transparent animate-spin"
style={{ borderRightColor: 'transparent' }}
/>
<div className="w-1.5 h-1.5 bg-amber-500 rounded-full animate-pulse" />
</div>
<div className="flex flex-col">
<span className="text-[10px] font-black uppercase tracking-[0.2em] text-white">Bunker lädt...</span>
<span className="text-[10px] font-black text-amber-500 tabular-nums">
{progress > 0 ? `${progress}%` : 'Initialisiere'}
</span>
</div>
</div>
</div>
);