feat: add Offline Mode Indicator (Bunker Status)
This commit is contained in:
20
public/sw.js
20
public/sw.js
@@ -43,10 +43,19 @@ self.addEventListener('install', (event) => {
|
||||
});
|
||||
await Promise.all(promises);
|
||||
console.log('✅ PWA: Bunker build finished');
|
||||
|
||||
// Signal to clients that pre-caching is complete
|
||||
broadcast({ type: 'PRECACHE_COMPLETE', version: CACHE_NAME });
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Helper: Broadcast to all clients
|
||||
async function broadcast(message) {
|
||||
const clients = await self.clients.matchAll();
|
||||
clients.forEach(client => client.postMessage(message));
|
||||
}
|
||||
|
||||
// Activate: Alte Bunker räumen
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
@@ -64,6 +73,17 @@ self.addEventListener('activate', (event) => {
|
||||
self.clients.claim();
|
||||
});
|
||||
|
||||
// Communication: Listen for status checks
|
||||
self.addEventListener('message', (event) => {
|
||||
if (event.data?.type === 'CHECK_BUNKER_STATUS') {
|
||||
event.source.postMessage({
|
||||
type: 'BUNKER_STATUS',
|
||||
isReady: true,
|
||||
version: CACHE_NAME
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
// Nur GET-Requests cachen
|
||||
if (event.request.method !== 'GET') return;
|
||||
|
||||
@@ -1,32 +1,84 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { WifiOff } from 'lucide-react';
|
||||
import { WifiOff, ShieldCheck } from 'lucide-react';
|
||||
|
||||
export default function OfflineIndicator() {
|
||||
const [isOffline, setIsOffline] = useState(false);
|
||||
const [isBunkerReady, setIsBunkerReady] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setIsOffline(!navigator.onLine);
|
||||
// Check if bunker was already ready from previous session
|
||||
const savedReady = localStorage.getItem('whisky_bunker_ready') === 'true';
|
||||
setIsBunkerReady(savedReady);
|
||||
|
||||
const handleOnline = () => setIsOffline(false);
|
||||
const handleOffline = () => setIsOffline(true);
|
||||
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.data?.type === 'PRECACHE_COMPLETE' || event.data?.type === 'BUNKER_STATUS') {
|
||||
console.log('🛡️ PWA: Bunker is ready for offline use!');
|
||||
setIsBunkerReady(true);
|
||||
localStorage.setItem('whisky_bunker_ready', 'true');
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.addEventListener('message', handleMessage);
|
||||
|
||||
// Proactively check status if SW is already active
|
||||
if (navigator.serviceWorker.controller) {
|
||||
navigator.serviceWorker.controller.postMessage({ type: 'CHECK_BUNKER_STATUS' });
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.removeEventListener('message', handleMessage);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
if (!isOffline) return null;
|
||||
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} />
|
||||
Offline-Modus: Bunker aktiv 🛡️
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Show nothing if online and not ready yet (or show a loading state?)
|
||||
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} />
|
||||
Offline-Modus: Du siehst eine gespeicherte Version
|
||||
<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...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user