From 814eeadd1f0cd6d6787175a97a7425c10a7081b2 Mon Sep 17 00:00:00 2001 From: elpatron Date: Sun, 31 May 2026 12:26:33 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Sync-Indikator=20Listener-Cleanup=20und?= =?UTF-8?q?=20CSS-Zust=C3=A4nde?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useSyncIndicator gibt die Unsubscribe-Funktion von subscribeToSyncState zurück. conn-status-Klassen berücksichtigen jetzt auch den aktiven Sync-Lauf (syncing) statt nur die Queue-Länge. Co-authored-by: Cursor --- client/src/App.css | 6 +++++ client/src/components/LogbookDashboard.tsx | 4 ++-- client/src/components/UserProfilePage.tsx | 9 +++++-- client/src/hooks/useSyncIndicator.ts | 28 ++++++++++++++++++---- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/client/src/App.css b/client/src/App.css index b2bc2ac..c9d2e56 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -2172,6 +2172,12 @@ html.scheme-dark .themed-select-option.is-selected { 100% { background-position: 0 0; } } +.conn-status.syncing { + background: rgba(59, 130, 246, 0.1); + color: #60a5fa; + border: 1px solid rgba(59, 130, 246, 0.25); +} + .conn-status.warning { background: rgba(251, 191, 36, 0.1); color: #fbbf24; diff --git a/client/src/components/LogbookDashboard.tsx b/client/src/components/LogbookDashboard.tsx index 43fa933..7bc0fe5 100644 --- a/client/src/components/LogbookDashboard.tsx +++ b/client/src/components/LogbookDashboard.tsx @@ -31,7 +31,7 @@ export default function LogbookDashboard({ onSelectLogbook, onLogout, onOpenProf const [online, setOnline] = useState(navigator.onLine) const [username] = useState(localStorage.getItem('active_username') || 'Skipper') - const { pendingCount, showSpinner, showPendingWarning } = useSyncIndicator() + const { pendingCount, showSpinner, showPendingWarning, connStatusClassName } = useSyncIndicator() // Listen to connectivity changes useEffect(() => { @@ -271,7 +271,7 @@ export default function LogbookDashboard({ onSelectLogbook, onLogout, onOpenProf
{/* Connection Indicator */}
0 ? 'unsynced' : 'online') : 'offline'}`} + className={connStatusClassName(online)} title={ online ? showSpinner diff --git a/client/src/components/UserProfilePage.tsx b/client/src/components/UserProfilePage.tsx index 3cc7317..3ee81d6 100644 --- a/client/src/components/UserProfilePage.tsx +++ b/client/src/components/UserProfilePage.tsx @@ -129,7 +129,12 @@ export default function UserProfilePage({ onBack, onLogout }: UserProfilePagePro const [pendingRecoveryPhrase, setPendingRecoveryPhrase] = useState(null) const [recoveryCopied, setRecoveryCopied] = useState(false) - const { pendingCount: pendingSyncCount, showSpinner, showPendingWarning } = useSyncIndicator() + const { + pendingCount: pendingSyncCount, + showSpinner, + showPendingWarning, + connStatusClassName + } = useSyncIndicator() const sharedLogbookCount = useLiveQuery( () => db.logbooks.filter((lb) => lb.isShared === 1).count(), @@ -528,7 +533,7 @@ export default function UserProfilePage({ onBack, onLogout }: UserProfilePagePro

{t('profile.device_title')}

{t('profile.device_desc')}

-
0 ? 'warning' : 'online') : 'offline'}`}> +
{online ? ( showSpinner ? ( <> diff --git a/client/src/hooks/useSyncIndicator.ts b/client/src/hooks/useSyncIndicator.ts index 0618a1b..3ce012c 100644 --- a/client/src/hooks/useSyncIndicator.ts +++ b/client/src/hooks/useSyncIndicator.ts @@ -3,6 +3,20 @@ import { useLiveQuery } from 'dexie-react-hooks' import { db } from '../services/db.js' import { subscribeToSyncState } from '../services/sync.js' +export type SyncConnStatusVariant = 'offline' | 'syncing' | 'pending' | 'online' + +/** Maps sync/online state to conn-status CSS modifier classes. */ +export function syncConnStatusClassName( + online: boolean, + showSpinner: boolean, + pendingCount: number +): string { + if (!online) return 'conn-status offline' + if (showSpinner) return 'conn-status syncing' + if (pendingCount > 0) return 'conn-status warning' + return 'conn-status online' +} + /** Sync queue depth and whether a sync pass is running (for header indicators). */ export function useSyncIndicator(logbookId?: string | null) { const [isSyncing, setIsSyncing] = useState(false) @@ -16,13 +30,19 @@ export function useSyncIndicator(logbookId?: string | null) { [logbookId] ) ?? 0 - useEffect(() => subscribeToSyncState(setIsSyncing), []) + useEffect(() => { + return subscribeToSyncState(setIsSyncing) + }, []) + + const showSpinner = isSyncing + const showPendingWarning = pendingCount > 0 && !isSyncing return { isSyncing, pendingCount, - /** Spin only while a sync pass is active — not for stale queue counts. */ - showSpinner: isSyncing, - showPendingWarning: pendingCount > 0 && !isSyncing + showSpinner, + showPendingWarning, + connStatusClassName: (online: boolean) => + syncConnStatusClassName(online, showSpinner, pendingCount) } }