feat: integrate sync status indicator into highlights panel
- Updated HighlightsTab to accept syncStatus and syncErrorMessage props - Added SyncStatusIndicator component import and display in highlights panel - Enhanced BibleReaderApp with sync status tracking state (synced/syncing/pending/error) - Modified performSync function to update sync status based on result - Updated VersDetailsPanel to pass sync status props through to HighlightsTab - Sync status now visible to users in the Highlights tab with real-time updates Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -36,6 +36,8 @@ export function BibleReaderApp() {
|
|||||||
const [booksLoading, setBooksLoading] = useState(true)
|
const [booksLoading, setBooksLoading] = useState(true)
|
||||||
const [highlights, setHighlights] = useState<Map<string, BibleHighlight>>(new Map())
|
const [highlights, setHighlights] = useState<Map<string, BibleHighlight>>(new Map())
|
||||||
const syncManager = useRef<HighlightSyncManager | null>(null)
|
const syncManager = useRef<HighlightSyncManager | null>(null)
|
||||||
|
const [syncStatus, setSyncStatus] = useState<'synced' | 'syncing' | 'pending' | 'error'>('synced')
|
||||||
|
const [syncError, setSyncError] = useState<string | null>(null)
|
||||||
|
|
||||||
// Load books on mount or when locale changes
|
// Load books on mount or when locale changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -281,17 +283,19 @@ export function BibleReaderApp() {
|
|||||||
if (!syncManager.current) return
|
if (!syncManager.current) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const pending = await syncManager.current.getPendingSyncItems()
|
setSyncStatus('syncing')
|
||||||
if (pending.length === 0) return
|
const result = await syncManager.current.performSync()
|
||||||
|
|
||||||
await syncManager.current.markSyncing(pending.map(h => h.id))
|
if (result.errors > 0) {
|
||||||
|
setSyncStatus('error')
|
||||||
// TODO: POST to /api/highlights/bulk in Phase 2.1B
|
setSyncError(`Failed to sync ${result.errors} highlights`)
|
||||||
|
} else {
|
||||||
await syncManager.current.markSynced(pending.map(h => h.id))
|
setSyncStatus('synced')
|
||||||
|
setSyncError(null)
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Sync failed:', error)
|
setSyncStatus('error')
|
||||||
// Mark items with error status
|
setSyncError(error instanceof Error ? error.message : 'Unknown error')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,6 +374,8 @@ export function BibleReaderApp() {
|
|||||||
onHighlightVerse={handleHighlightVerse}
|
onHighlightVerse={handleHighlightVerse}
|
||||||
onChangeHighlightColor={handleChangeHighlightColor}
|
onChangeHighlightColor={handleChangeHighlightColor}
|
||||||
onRemoveHighlight={handleRemoveHighlight}
|
onRemoveHighlight={handleRemoveHighlight}
|
||||||
|
syncStatus={syncStatus}
|
||||||
|
syncErrorMessage={syncError || undefined}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Settings panel */}
|
{/* Settings panel */}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { Box, Button, Typography, Divider } from '@mui/material'
|
import { Box, Button, Typography, Divider } from '@mui/material'
|
||||||
import { BibleVerse, HighlightColor } from '@/types'
|
import { BibleVerse, HighlightColor } from '@/types'
|
||||||
|
import { SyncStatusIndicator } from './sync-status-indicator'
|
||||||
|
|
||||||
const HIGHLIGHT_COLORS: HighlightColor[] = ['yellow', 'orange', 'pink', 'blue']
|
const HIGHLIGHT_COLORS: HighlightColor[] = ['yellow', 'orange', 'pink', 'blue']
|
||||||
|
|
||||||
@@ -17,6 +18,8 @@ interface HighlightsTabProps {
|
|||||||
currentColor: HighlightColor | null
|
currentColor: HighlightColor | null
|
||||||
onToggleHighlight: () => void
|
onToggleHighlight: () => void
|
||||||
onColorChange: (color: HighlightColor) => void
|
onColorChange: (color: HighlightColor) => void
|
||||||
|
syncStatus?: 'synced' | 'syncing' | 'pending' | 'error'
|
||||||
|
syncErrorMessage?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function HighlightsTab({
|
export function HighlightsTab({
|
||||||
@@ -24,7 +27,9 @@ export function HighlightsTab({
|
|||||||
isHighlighted,
|
isHighlighted,
|
||||||
currentColor,
|
currentColor,
|
||||||
onToggleHighlight,
|
onToggleHighlight,
|
||||||
onColorChange
|
onColorChange,
|
||||||
|
syncStatus,
|
||||||
|
syncErrorMessage
|
||||||
}: HighlightsTabProps) {
|
}: HighlightsTabProps) {
|
||||||
if (!verse) return null
|
if (!verse) return null
|
||||||
|
|
||||||
@@ -80,6 +85,18 @@ export function HighlightsTab({
|
|||||||
|
|
||||||
<Divider sx={{ my: 2 }} />
|
<Divider sx={{ my: 2 }} />
|
||||||
|
|
||||||
|
{syncStatus && (
|
||||||
|
<Box sx={{ mt: 2 }}>
|
||||||
|
<Typography variant="subtitle2" sx={{ mb: 1 }}>
|
||||||
|
Sync Status
|
||||||
|
</Typography>
|
||||||
|
<SyncStatusIndicator
|
||||||
|
status={syncStatus}
|
||||||
|
errorMessage={syncErrorMessage}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
<Typography variant="body2" color="textSecondary">
|
<Typography variant="body2" color="textSecondary">
|
||||||
You can highlight the same verse multiple times with different colors.
|
You can highlight the same verse multiple times with different colors.
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ interface VersDetailsPanelProps {
|
|||||||
onHighlightVerse?: (color: HighlightColor) => void
|
onHighlightVerse?: (color: HighlightColor) => void
|
||||||
onChangeHighlightColor?: (color: HighlightColor) => void
|
onChangeHighlightColor?: (color: HighlightColor) => void
|
||||||
onRemoveHighlight?: () => void
|
onRemoveHighlight?: () => void
|
||||||
|
syncStatus?: 'synced' | 'syncing' | 'pending' | 'error'
|
||||||
|
syncErrorMessage?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export function VersDetailsPanel({
|
export function VersDetailsPanel({
|
||||||
@@ -31,6 +33,8 @@ export function VersDetailsPanel({
|
|||||||
onHighlightVerse,
|
onHighlightVerse,
|
||||||
onChangeHighlightColor,
|
onChangeHighlightColor,
|
||||||
onRemoveHighlight,
|
onRemoveHighlight,
|
||||||
|
syncStatus,
|
||||||
|
syncErrorMessage,
|
||||||
}: VersDetailsPanelProps) {
|
}: VersDetailsPanelProps) {
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
|
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
|
||||||
@@ -141,6 +145,8 @@ export function VersDetailsPanel({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onColorChange={(color) => onChangeHighlightColor?.(color)}
|
onColorChange={(color) => onChangeHighlightColor?.(color)}
|
||||||
|
syncStatus={syncStatus}
|
||||||
|
syncErrorMessage={syncErrorMessage}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user