2026-01-13 14:49:19 +01:00

217 lines
8.4 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react'
import { nostrAuthService } from '@/lib/nostrAuth'
import { MIN_EVENT_DATE } from '@/lib/platformConfig'
import { objectCache } from '@/lib/objectCache'
import { calculateDaysBetween, getCurrentTimestamp, getLastSyncDate, setLastSyncDate as setLastSyncDateStorage } from '@/lib/syncStorage'
import { useSyncProgress } from '@/lib/hooks/useSyncProgress'
import type { SyncProgress, SyncProgressBarController } from './types'
type ConnectionState = { isInitialized: boolean; connected: boolean; pubkey: string | null }
export function useSyncProgressBarController(): SyncProgressBarController | null {
const connection = useNostrConnectionState()
const [error, setError] = useState<string | null>(null)
const syncStatus = useSyncStatus()
const { syncProgress, isSyncing, startMonitoring, stopMonitoring } = useSyncProgress({ onComplete: syncStatus.loadSyncStatus })
useAutoSyncEffect({ connection, isSyncing, loadSyncStatus: syncStatus.loadSyncStatus, startMonitoring, stopMonitoring, setError })
if (!connection.isInitialized || !connection.connected || !connection.pubkey) {
return null
}
const progressPercentage = computeProgressPercentage(syncProgress)
const isRecentlySynced = isRecentlySyncedFromLastSyncDate(syncStatus.lastSyncDate)
const startDate = getSyncStartDate(syncStatus.lastSyncDate)
const endDate = getCurrentTimestamp()
return {
error,
dismissError: () => setError(null),
isSyncing,
syncProgress,
progressPercentage,
totalDays: syncStatus.totalDays,
startDateLabel: formatSyncDate(startDate),
endDateLabel: formatSyncDate(endDate),
isRecentlySynced,
onResyncClick: () => {
void resynchronizeUserContent({ startMonitoring, stopMonitoring, loadSyncStatus: syncStatus.loadSyncStatus, setError })
},
}
}
function useNostrConnectionState(): ConnectionState {
const initial = nostrAuthService.getState()
const [state, setState] = useState<{ connected: boolean; pubkey: string | null }>(() => ({
connected: initial.connected ?? false,
pubkey: initial.pubkey ?? null,
}))
const [isInitialized] = useState(true)
useEffect(() => {
const unsubscribe = nostrAuthService.subscribe((next) => setState({ connected: next.connected ?? false, pubkey: next.pubkey ?? null }))
return () => unsubscribe()
}, [])
return { isInitialized, connected: state.connected, pubkey: state.pubkey }
}
function useSyncStatus(): { lastSyncDate: number | null; totalDays: number; loadSyncStatus: () => Promise<void> } {
const [lastSyncDate, setLastSyncDate] = useState<number | null>(null)
const [totalDays, setTotalDays] = useState<number>(0)
const loadSyncStatus = useCallback(async (): Promise<void> => {
try {
const state = nostrAuthService.getState()
if (!state.connected || !state.pubkey) {
return
}
const storedLastSyncDate = await getLastSyncDate()
const days = calculateDaysBetween(storedLastSyncDate, getCurrentTimestamp())
setLastSyncDate(storedLastSyncDate)
setTotalDays(days)
} catch (loadError) {
console.error('[SyncProgressBar] Error loading sync status:', loadError)
}
}, [])
return { lastSyncDate, totalDays, loadSyncStatus }
}
function useAutoSyncEffect(params: {
connection: ConnectionState
isSyncing: boolean
loadSyncStatus: () => Promise<void>
startMonitoring: () => void
stopMonitoring: () => void
setError: (value: string | null) => void
}): void {
useEffect(() => {
if (!params.connection.isInitialized || !params.connection.connected || !params.connection.pubkey) {
return
}
void runAutoSyncCheck({
connection: { connected: true, pubkey: params.connection.pubkey },
isSyncing: params.isSyncing,
loadSyncStatus: params.loadSyncStatus,
startMonitoring: params.startMonitoring,
stopMonitoring: params.stopMonitoring,
setError: params.setError,
})
}, [params.connection.connected, params.connection.isInitialized, params.connection.pubkey, params.isSyncing, params.loadSyncStatus, params.setError, params.startMonitoring, params.stopMonitoring])
}
function formatSyncDate(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' })
}
function getSyncStartDate(lastSyncDate: number | null): number {
return lastSyncDate ?? MIN_EVENT_DATE
}
function isRecentlySyncedFromLastSyncDate(lastSyncDate: number | null): boolean {
return lastSyncDate !== null && lastSyncDate >= getCurrentTimestamp() - 3600
}
async function resynchronizeUserContent(params: {
startMonitoring: () => void
stopMonitoring: () => void
loadSyncStatus: () => Promise<void>
setError: (value: string | null) => void
}): Promise<void> {
try {
const state = nostrAuthService.getState()
if (!state.connected || !state.pubkey) {
return
}
await clearUserContentCache()
await setLastSyncDateStorage(MIN_EVENT_DATE)
await params.loadSyncStatus()
await startUserSyncOrStop({ pubkey: state.pubkey, startMonitoring: params.startMonitoring, stopMonitoring: params.stopMonitoring })
} catch (resyncError) {
console.error('[SyncProgressBar] Error resynchronizing:', resyncError)
params.stopMonitoring()
params.setError(resyncError instanceof Error ? resyncError.message : 'Erreur de synchronisation')
}
}
async function clearUserContentCache(): Promise<void> {
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'),
])
}
async function startUserSyncOrStop(params: { pubkey: string; startMonitoring: () => void; stopMonitoring: () => void }): Promise<void> {
const { swClient } = await import('@/lib/swClient')
const isReady = await swClient.isReady()
if (!isReady) {
params.stopMonitoring()
return
}
await swClient.startUserSync(params.pubkey)
params.startMonitoring()
}
function computeProgressPercentage(syncProgress: SyncProgress): number {
if (!syncProgress || syncProgress.totalSteps <= 0) {
return 0
}
return Math.min(100, (syncProgress.currentStep / syncProgress.totalSteps) * 100)
}
async function runAutoSyncCheck(params: {
connection: { connected: boolean; pubkey: string | null }
isSyncing: boolean
loadSyncStatus: () => Promise<void>
startMonitoring: () => void
stopMonitoring: () => void
setError: (value: string | null) => void
}): Promise<void> {
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<boolean> {
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<void> {
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')
}
}