fix: Sync-Indikator Listener-Cleanup und CSS-Zustände
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 <cursoragent@cursor.com>
This commit is contained in:
@@ -2172,6 +2172,12 @@ html.scheme-dark .themed-select-option.is-selected {
|
|||||||
100% { background-position: 0 0; }
|
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 {
|
.conn-status.warning {
|
||||||
background: rgba(251, 191, 36, 0.1);
|
background: rgba(251, 191, 36, 0.1);
|
||||||
color: #fbbf24;
|
color: #fbbf24;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default function LogbookDashboard({ onSelectLogbook, onLogout, onOpenProf
|
|||||||
const [online, setOnline] = useState(navigator.onLine)
|
const [online, setOnline] = useState(navigator.onLine)
|
||||||
const [username] = useState(localStorage.getItem('active_username') || 'Skipper')
|
const [username] = useState(localStorage.getItem('active_username') || 'Skipper')
|
||||||
|
|
||||||
const { pendingCount, showSpinner, showPendingWarning } = useSyncIndicator()
|
const { pendingCount, showSpinner, showPendingWarning, connStatusClassName } = useSyncIndicator()
|
||||||
|
|
||||||
// Listen to connectivity changes
|
// Listen to connectivity changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -271,7 +271,7 @@ export default function LogbookDashboard({ onSelectLogbook, onLogout, onOpenProf
|
|||||||
<div className="header-actions">
|
<div className="header-actions">
|
||||||
{/* Connection Indicator */}
|
{/* Connection Indicator */}
|
||||||
<div
|
<div
|
||||||
className={`conn-status ${online ? (pendingCount > 0 ? 'unsynced' : 'online') : 'offline'}`}
|
className={connStatusClassName(online)}
|
||||||
title={
|
title={
|
||||||
online
|
online
|
||||||
? showSpinner
|
? showSpinner
|
||||||
|
|||||||
@@ -129,7 +129,12 @@ export default function UserProfilePage({ onBack, onLogout }: UserProfilePagePro
|
|||||||
const [pendingRecoveryPhrase, setPendingRecoveryPhrase] = useState<string | null>(null)
|
const [pendingRecoveryPhrase, setPendingRecoveryPhrase] = useState<string | null>(null)
|
||||||
const [recoveryCopied, setRecoveryCopied] = useState(false)
|
const [recoveryCopied, setRecoveryCopied] = useState(false)
|
||||||
|
|
||||||
const { pendingCount: pendingSyncCount, showSpinner, showPendingWarning } = useSyncIndicator()
|
const {
|
||||||
|
pendingCount: pendingSyncCount,
|
||||||
|
showSpinner,
|
||||||
|
showPendingWarning,
|
||||||
|
connStatusClassName
|
||||||
|
} = useSyncIndicator()
|
||||||
|
|
||||||
const sharedLogbookCount = useLiveQuery(
|
const sharedLogbookCount = useLiveQuery(
|
||||||
() => db.logbooks.filter((lb) => lb.isShared === 1).count(),
|
() => db.logbooks.filter((lb) => lb.isShared === 1).count(),
|
||||||
@@ -528,7 +533,7 @@ export default function UserProfilePage({ onBack, onLogout }: UserProfilePagePro
|
|||||||
<h3>{t('profile.device_title')}</h3>
|
<h3>{t('profile.device_title')}</h3>
|
||||||
</div>
|
</div>
|
||||||
<p className="profile-section-desc">{t('profile.device_desc')}</p>
|
<p className="profile-section-desc">{t('profile.device_desc')}</p>
|
||||||
<div className={`profile-device-status conn-status ${online ? (pendingSyncCount > 0 ? 'warning' : 'online') : 'offline'}`}>
|
<div className={`profile-device-status ${connStatusClassName(online)}`}>
|
||||||
{online ? (
|
{online ? (
|
||||||
showSpinner ? (
|
showSpinner ? (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -3,6 +3,20 @@ import { useLiveQuery } from 'dexie-react-hooks'
|
|||||||
import { db } from '../services/db.js'
|
import { db } from '../services/db.js'
|
||||||
import { subscribeToSyncState } from '../services/sync.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). */
|
/** Sync queue depth and whether a sync pass is running (for header indicators). */
|
||||||
export function useSyncIndicator(logbookId?: string | null) {
|
export function useSyncIndicator(logbookId?: string | null) {
|
||||||
const [isSyncing, setIsSyncing] = useState(false)
|
const [isSyncing, setIsSyncing] = useState(false)
|
||||||
@@ -16,13 +30,19 @@ export function useSyncIndicator(logbookId?: string | null) {
|
|||||||
[logbookId]
|
[logbookId]
|
||||||
) ?? 0
|
) ?? 0
|
||||||
|
|
||||||
useEffect(() => subscribeToSyncState(setIsSyncing), [])
|
useEffect(() => {
|
||||||
|
return subscribeToSyncState(setIsSyncing)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const showSpinner = isSyncing
|
||||||
|
const showPendingWarning = pendingCount > 0 && !isSyncing
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isSyncing,
|
isSyncing,
|
||||||
pendingCount,
|
pendingCount,
|
||||||
/** Spin only while a sync pass is active — not for stale queue counts. */
|
showSpinner,
|
||||||
showSpinner: isSyncing,
|
showPendingWarning,
|
||||||
showPendingWarning: pendingCount > 0 && !isSyncing
|
connStatusClassName: (online: boolean) =>
|
||||||
|
syncConnStatusClassName(online, showSpinner, pendingCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user