217 lines
8.4 KiB
TypeScript
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')
|
|
}
|
|
}
|