import { useState, useEffect, useCallback } from 'react' import { nostrAuthService } from '@/lib/nostrAuth' import { getLastSyncDate, setLastSyncDate as setLastSyncDateStorage, getCurrentTimestamp, calculateDaysBetween } from '@/lib/syncStorage' import { MIN_EVENT_DATE } from '@/lib/platformConfig' import { objectCache } from '@/lib/objectCache' import { t } from '@/lib/i18n' import { useSyncProgress } from '@/lib/hooks/useSyncProgress' export function SyncProgressBar(): React.ReactElement | null { const [lastSyncDate, setLastSyncDate] = useState(null) const [totalDays, setTotalDays] = useState(0) const [isInitialized, setIsInitialized] = useState(false) const [connectionState, setConnectionState] = useState<{ connected: boolean; pubkey: string | null }>({ connected: false, pubkey: null }) const [error, setError] = useState(null) const loadSyncStatus = useCallback(async (): Promise => { try { const state = nostrAuthService.getState() if (!state.connected || !state.pubkey) { return } const storedLastSyncDate = await getLastSyncDate() const currentTimestamp = getCurrentTimestamp() const days = calculateDaysBetween(storedLastSyncDate, currentTimestamp) setLastSyncDate(storedLastSyncDate) setTotalDays(days) } catch (loadError) { console.error('Error loading sync status:', loadError) } }, []) const { syncProgress, isSyncing, startMonitoring, stopMonitoring } = useSyncProgress({ onComplete: loadSyncStatus, }) useEffect(() => { // Check connection state const checkConnection = (): void => { const state = nostrAuthService.getState() console.warn('[SyncProgressBar] Initial connection check:', { connected: state.connected, pubkey: state.pubkey }) setConnectionState({ connected: state.connected ?? false, pubkey: state.pubkey ?? null }) setIsInitialized(true) } // Initial check checkConnection() // Listen to connection changes const unsubscribe = nostrAuthService.subscribe((state) => { console.warn('[SyncProgressBar] Connection state changed:', { connected: state.connected, pubkey: state.pubkey }) setConnectionState({ connected: state.connected ?? false, pubkey: state.pubkey ?? null }) }) return () => { unsubscribe() } }, []) useEffect(() => { console.warn('[SyncProgressBar] Effect triggered:', { isInitialized, connected: connectionState.connected, pubkey: connectionState.pubkey, isSyncing }) if (!isInitialized) { console.warn('[SyncProgressBar] Not initialized yet') return } if (!connectionState.connected) { console.warn('[SyncProgressBar] Not connected') return } if (!connectionState.pubkey) { console.warn('[SyncProgressBar] No pubkey') return } void runAutoSyncCheck({ connection: { connected: connectionState.connected, pubkey: connectionState.pubkey }, isSyncing, loadSyncStatus, startMonitoring, stopMonitoring, setError, }) }, [isInitialized, connectionState.connected, connectionState.pubkey, isSyncing, loadSyncStatus, startMonitoring, stopMonitoring]) async function resynchronize(): Promise { try { const state = nostrAuthService.getState() if (!state.connected || !state.pubkey) { return } // Clear cache for user content (but keep other data) await Promise.all([ objectCache.clear('author'), objectCache.clear('series'), objectCache.clear('publication'), objectCache.clear('review'), objectCache.clear('purchase'), objectCache.clear('sponsoring'), objectCache.clear('review_tip'), ]) // Reset last sync date to force full resync await setLastSyncDateStorage(MIN_EVENT_DATE) // Reload sync status await loadSyncStatus() // Start full resynchronization via Service Worker if (state.pubkey !== null) { const { swClient } = await import('@/lib/swClient') const isReady = await swClient.isReady() if (isReady) { await swClient.startUserSync(state.pubkey) startMonitoring() } else { stopMonitoring() } } } catch (resyncError) { console.error('Error resynchronizing:', resyncError) stopMonitoring() } } // Don't show if not initialized or not connected if (!isInitialized || !connectionState.connected || !connectionState.pubkey) { return null } // Check if sync is recently completed (within last hour) const isRecentlySynced = lastSyncDate !== null && lastSyncDate >= getCurrentTimestamp() - 3600 const progressPercentage = computeProgressPercentage(syncProgress) const formatDate = (timestamp: number): string => { const date = new Date(timestamp * 1000) const locale = typeof window !== 'undefined' ? navigator.language : 'fr-FR' return date.toLocaleDateString(locale, { day: '2-digit', month: '2-digit', year: 'numeric' }) } const getStartDate = (): number => { if (lastSyncDate !== null) { return lastSyncDate } return MIN_EVENT_DATE } const startDate = getStartDate() const endDate = getCurrentTimestamp() return (
{ setError(null) }} />

{t('settings.sync.title')}

{ void resynchronize() }} />
) } function computeProgressPercentage(syncProgress: ReturnType['syncProgress']): number { if (!syncProgress || syncProgress.totalSteps <= 0) { return 0 } return Math.min(100, (syncProgress.currentStep / syncProgress.totalSteps) * 100) } function SyncErrorBanner(params: { error: string | null; onDismiss: () => void }): React.ReactElement | null { if (!params.error) { return null } return (
{params.error}
) } function SyncResyncButton(params: { isSyncing: boolean; onClick: () => void }): React.ReactElement | null { if (params.isSyncing) { return null } return ( ) } function SyncDateRange(params: { totalDays: number; startDate: string; endDate: string }): React.ReactElement | null { if (params.totalDays <= 0) { return null } return (

{t('settings.sync.daysRange', { startDate: params.startDate, endDate: params.endDate, days: params.totalDays, })}

) } function SyncProgressSection(params: { isSyncing: boolean syncProgress: ReturnType['syncProgress'] progressPercentage: number }): React.ReactElement | null { if (!params.isSyncing || !params.syncProgress) { return null } return (
{t('settings.sync.progress', { current: params.syncProgress.currentStep, total: params.syncProgress.totalSteps, })} {Math.round(params.progressPercentage)}%
) } function SyncStatusMessage(params: { isSyncing: boolean; totalDays: number; isRecentlySynced: boolean }): React.ReactElement | null { if (params.isSyncing || params.totalDays !== 0) { return null } if (params.isRecentlySynced) { return

{t('settings.sync.completed')}

} return

{t('settings.sync.ready')}

} async function runAutoSyncCheck(params: { connection: { connected: boolean; pubkey: string | null } isSyncing: boolean loadSyncStatus: () => Promise startMonitoring: () => void stopMonitoring: () => void setError: (value: string | null) => void }): Promise { console.warn('[SyncProgressBar] Starting sync check...') await params.loadSyncStatus() const shouldStart = await shouldAutoStartSync({ isSyncing: params.isSyncing, pubkey: params.connection.pubkey, }) if (!shouldStart || !params.connection.pubkey) { console.warn('[SyncProgressBar] Skipping auto-sync:', { shouldStart, isSyncing: params.isSyncing, hasPubkey: Boolean(params.connection.pubkey) }) return } console.warn('[SyncProgressBar] Starting auto-sync...') await startAutoSync({ pubkey: params.connection.pubkey, startMonitoring: params.startMonitoring, stopMonitoring: params.stopMonitoring, setError: params.setError, }) } async function shouldAutoStartSync(params: { isSyncing: boolean; pubkey: string | null }): Promise { if (params.isSyncing || !params.pubkey) { return false } const storedLastSyncDate = await getLastSyncDate() const currentTimestamp = getCurrentTimestamp() const isRecentlySynced = storedLastSyncDate >= currentTimestamp - 3600 console.warn('[SyncProgressBar] Sync status:', { storedLastSyncDate, currentTimestamp, isRecentlySynced }) return !isRecentlySynced } async function startAutoSync(params: { pubkey: string startMonitoring: () => void stopMonitoring: () => void setError: (value: string | null) => void }): Promise { try { const { swClient } = await import('@/lib/swClient') const isReady = await swClient.isReady() if (!isReady) { params.stopMonitoring() return } await swClient.startUserSync(params.pubkey) params.startMonitoring() } catch (autoSyncError) { console.error('[SyncProgressBar] Error during auto-sync:', autoSyncError) params.stopMonitoring() params.setError(autoSyncError instanceof Error ? autoSyncError.message : 'Erreur de synchronisation') } }