From b0a79541b6d87cabd3e8bbc4a5c03716b59715fa Mon Sep 17 00:00:00 2001 From: robin Date: Sat, 20 Dec 2025 23:51:16 +0100 Subject: [PATCH] feat: add Offline Mode Indicator (Bunker Status) --- public/sw.js | 20 ++++++++++ src/components/OfflineIndicator.tsx | 62 ++++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/public/sw.js b/public/sw.js index cdc6984..58fd5a8 100644 --- a/public/sw.js +++ b/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; diff --git a/src/components/OfflineIndicator.tsx b/src/components/OfflineIndicator.tsx index 7e6e14d..5951fc3 100644 --- a/src/components/OfflineIndicator.tsx +++ b/src/components/OfflineIndicator.tsx @@ -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 ( +
+ + Offline-Modus: Bunker aktiv 🛡️ +
+ ); + } + if (isBunkerReady) { + return ( +
+
+ + + Bunker Aktiv + +
+ Die App ist vollständig im "Bunker" gespeichert und funktioniert auch ohne Internet. +
+
+
+ ); + } + + // Show nothing if online and not ready yet (or show a loading state?) return ( -
- - Offline-Modus: Du siehst eine gespeicherte Version +
+
+
+ + Bunker wird geladen... + +
); }