154 lines
4.9 KiB
TypeScript
154 lines
4.9 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
import { nostrAuthService } from '@/lib/nostrAuth'
|
|
import { syncUserContentToCache, type SyncProgress } from '@/lib/userContentSync'
|
|
import { getLastSyncDate, getCurrentTimestamp, calculateDaysBetween } from '@/lib/syncStorage'
|
|
import { MIN_EVENT_DATE } from '@/lib/platformConfig'
|
|
import { t } from '@/lib/i18n'
|
|
|
|
export function SyncProgressBar(): React.ReactElement | null {
|
|
const [syncProgress, setSyncProgress] = useState<SyncProgress | null>(null)
|
|
const [isSyncing, setIsSyncing] = useState(false)
|
|
const [lastSyncDate, setLastSyncDate] = useState<number | null>(null)
|
|
const [totalDays, setTotalDays] = useState<number>(0)
|
|
|
|
async function loadSyncStatus(): Promise<void> {
|
|
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 (error) {
|
|
console.error('Error loading sync status:', error)
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
void loadSyncStatus()
|
|
}, [])
|
|
|
|
async function startSync(): Promise<void> {
|
|
try {
|
|
const state = nostrAuthService.getState()
|
|
if (!state.connected || !state.pubkey) {
|
|
return
|
|
}
|
|
|
|
setIsSyncing(true)
|
|
setSyncProgress({ currentStep: 0, totalSteps: 6, completed: false })
|
|
|
|
await syncUserContentToCache(state.pubkey, (progress) => {
|
|
setSyncProgress(progress)
|
|
if (progress.completed) {
|
|
setIsSyncing(false)
|
|
void loadSyncStatus()
|
|
}
|
|
})
|
|
} catch (error) {
|
|
console.error('Error starting sync:', error)
|
|
setIsSyncing(false)
|
|
}
|
|
}
|
|
|
|
// Don't show if not connected
|
|
const state = nostrAuthService.getState()
|
|
if (!state.connected || !state.pubkey) {
|
|
return null
|
|
}
|
|
|
|
// Check if sync is recently completed (within last hour)
|
|
const isRecentlySynced = lastSyncDate !== null && lastSyncDate >= getCurrentTimestamp() - 3600
|
|
|
|
const progressPercentage = syncProgress && syncProgress.totalSteps > 0
|
|
? Math.min(100, (syncProgress.currentStep / syncProgress.totalSteps) * 100)
|
|
: 0
|
|
|
|
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 (
|
|
<div className="bg-cyber-darker border border-neon-cyan/30 rounded-lg p-4 mt-6">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<h3 className="text-lg font-semibold text-neon-cyan">
|
|
{t('settings.sync.title')}
|
|
</h3>
|
|
{!isSyncing && (
|
|
<button
|
|
onClick={() => {
|
|
void startSync()
|
|
}}
|
|
className="px-3 py-1 text-xs bg-neon-cyan/20 hover:bg-neon-cyan/30 text-neon-cyan rounded border border-neon-cyan/50 hover:border-neon-cyan transition-colors"
|
|
>
|
|
{t('settings.sync.start')}
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{totalDays > 0 && (
|
|
<div className="mb-2">
|
|
<p className="text-sm text-cyber-accent">
|
|
{t('settings.sync.daysRange', {
|
|
startDate: formatDate(startDate),
|
|
endDate: formatDate(endDate),
|
|
days: totalDays,
|
|
})}
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{isSyncing && syncProgress && (
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between text-sm">
|
|
<span className="text-cyber-accent">
|
|
{t('settings.sync.progress', {
|
|
current: syncProgress.currentStep,
|
|
total: syncProgress.totalSteps,
|
|
})}
|
|
</span>
|
|
<span className="text-neon-cyan font-semibold">
|
|
{Math.round(progressPercentage)}%
|
|
</span>
|
|
</div>
|
|
<div className="w-full bg-cyber-dark rounded-full h-2 overflow-hidden">
|
|
<div
|
|
className="bg-neon-cyan h-full transition-all duration-300"
|
|
style={{ width: `${progressPercentage}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{!isSyncing && totalDays === 0 && isRecentlySynced && (
|
|
<p className="text-sm text-green-400">
|
|
{t('settings.sync.completed')}
|
|
</p>
|
|
)}
|
|
|
|
{!isSyncing && totalDays === 0 && !isRecentlySynced && (
|
|
<p className="text-sm text-cyber-accent">
|
|
{t('settings.sync.ready')}
|
|
</p>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|