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)
}
}