lint fix wip
This commit is contained in:
parent
17e4b10b1f
commit
dace103da8
@ -1,22 +1,26 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { nostrAuthService } from '@/lib/nostrAuth'
|
import { nostrAuthService } from '@/lib/nostrAuth'
|
||||||
import type { SyncProgress } from '@/lib/userContentSync'
|
|
||||||
import { getLastSyncDate, setLastSyncDate as setLastSyncDateStorage, getCurrentTimestamp, calculateDaysBetween } from '@/lib/syncStorage'
|
import { getLastSyncDate, setLastSyncDate as setLastSyncDateStorage, getCurrentTimestamp, calculateDaysBetween } from '@/lib/syncStorage'
|
||||||
import { MIN_EVENT_DATE } from '@/lib/platformConfig'
|
import { MIN_EVENT_DATE } from '@/lib/platformConfig'
|
||||||
import { objectCache } from '@/lib/objectCache'
|
import { objectCache } from '@/lib/objectCache'
|
||||||
import { t } from '@/lib/i18n'
|
import { t } from '@/lib/i18n'
|
||||||
|
import { useSyncProgress } from '@/lib/hooks/useSyncProgress'
|
||||||
|
|
||||||
export function SyncProgressBar(): React.ReactElement | null {
|
export function SyncProgressBar(): React.ReactElement | null {
|
||||||
console.warn('[SyncProgressBar] Component function called')
|
console.warn('[SyncProgressBar] Component function called')
|
||||||
|
|
||||||
const [syncProgress, setSyncProgress] = useState<SyncProgress | null>(null)
|
|
||||||
const [isSyncing, setIsSyncing] = useState(false)
|
|
||||||
const [lastSyncDate, setLastSyncDate] = useState<number | null>(null)
|
const [lastSyncDate, setLastSyncDate] = useState<number | null>(null)
|
||||||
const [totalDays, setTotalDays] = useState<number>(0)
|
const [totalDays, setTotalDays] = useState<number>(0)
|
||||||
const [isInitialized, setIsInitialized] = useState(false)
|
const [isInitialized, setIsInitialized] = useState(false)
|
||||||
const [connectionState, setConnectionState] = useState<{ connected: boolean; pubkey: string | null }>({ connected: false, pubkey: null })
|
const [connectionState, setConnectionState] = useState<{ connected: boolean; pubkey: string | null }>({ connected: false, pubkey: null })
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const { syncProgress, isSyncing, startMonitoring, stopMonitoring } = useSyncProgress({
|
||||||
|
onComplete: async () => {
|
||||||
|
await loadSyncStatus()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
async function loadSyncStatus(): Promise<void> {
|
async function loadSyncStatus(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const state = nostrAuthService.getState()
|
const state = nostrAuthService.getState()
|
||||||
@ -90,46 +94,19 @@ export function SyncProgressBar(): React.ReactElement | null {
|
|||||||
// Only auto-start if not recently synced
|
// Only auto-start if not recently synced
|
||||||
if (!isRecentlySynced && !isSyncing && connectionState.pubkey) {
|
if (!isRecentlySynced && !isSyncing && connectionState.pubkey) {
|
||||||
console.warn('[SyncProgressBar] Starting auto-sync...')
|
console.warn('[SyncProgressBar] Starting auto-sync...')
|
||||||
setIsSyncing(true)
|
|
||||||
setSyncProgress({ currentStep: 0, totalSteps: 6, completed: false })
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { swClient } = await import('@/lib/swClient')
|
const { swClient } = await import('@/lib/swClient')
|
||||||
const isReady = await swClient.isReady()
|
const isReady = await swClient.isReady()
|
||||||
if (isReady) {
|
if (isReady) {
|
||||||
await swClient.startUserSync(connectionState.pubkey)
|
await swClient.startUserSync(connectionState.pubkey)
|
||||||
// Progress is tracked via syncProgressManager
|
startMonitoring()
|
||||||
// Listen to syncProgressManager for updates
|
|
||||||
const { syncProgressManager } = await import('@/lib/syncProgressManager')
|
|
||||||
const checkProgress = (): void => {
|
|
||||||
const currentProgress = syncProgressManager.getProgress()
|
|
||||||
if (currentProgress) {
|
|
||||||
setSyncProgress(currentProgress)
|
|
||||||
if (currentProgress.completed) {
|
|
||||||
setIsSyncing(false)
|
|
||||||
void loadSyncStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check progress periodically
|
|
||||||
const progressInterval = setInterval(() => {
|
|
||||||
checkProgress()
|
|
||||||
const currentProgress = syncProgressManager.getProgress()
|
|
||||||
if (currentProgress?.completed) {
|
|
||||||
clearInterval(progressInterval)
|
|
||||||
}
|
|
||||||
}, 500)
|
|
||||||
// Cleanup after 60 seconds max
|
|
||||||
setTimeout(() => {
|
|
||||||
clearInterval(progressInterval)
|
|
||||||
setIsSyncing(false)
|
|
||||||
}, 60000)
|
|
||||||
} else {
|
} else {
|
||||||
setIsSyncing(false)
|
stopMonitoring()
|
||||||
}
|
}
|
||||||
} catch (autoSyncError) {
|
} catch (autoSyncError) {
|
||||||
console.error('[SyncProgressBar] Error during auto-sync:', autoSyncError)
|
console.error('[SyncProgressBar] Error during auto-sync:', autoSyncError)
|
||||||
setIsSyncing(false)
|
stopMonitoring()
|
||||||
setError(autoSyncError instanceof Error ? autoSyncError.message : 'Erreur de synchronisation')
|
setError(autoSyncError instanceof Error ? autoSyncError.message : 'Erreur de synchronisation')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -145,9 +122,6 @@ export function SyncProgressBar(): React.ReactElement | null {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsSyncing(true)
|
|
||||||
setSyncProgress({ currentStep: 0, totalSteps: 6, completed: false })
|
|
||||||
|
|
||||||
// Clear cache for user content (but keep other data)
|
// Clear cache for user content (but keep other data)
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
objectCache.clear('author'),
|
objectCache.clear('author'),
|
||||||
@ -171,39 +145,14 @@ export function SyncProgressBar(): React.ReactElement | null {
|
|||||||
const isReady = await swClient.isReady()
|
const isReady = await swClient.isReady()
|
||||||
if (isReady) {
|
if (isReady) {
|
||||||
await swClient.startUserSync(state.pubkey)
|
await swClient.startUserSync(state.pubkey)
|
||||||
// Progress is tracked via syncProgressManager
|
startMonitoring()
|
||||||
// Listen to syncProgressManager for updates
|
|
||||||
const { syncProgressManager } = await import('@/lib/syncProgressManager')
|
|
||||||
const checkProgress = (): void => {
|
|
||||||
const currentProgress = syncProgressManager.getProgress()
|
|
||||||
if (currentProgress) {
|
|
||||||
setSyncProgress(currentProgress)
|
|
||||||
if (currentProgress.completed) {
|
|
||||||
setIsSyncing(false)
|
|
||||||
void loadSyncStatus()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Check progress periodically
|
|
||||||
const progressInterval = setInterval(() => {
|
|
||||||
checkProgress()
|
|
||||||
const currentProgress = syncProgressManager.getProgress()
|
|
||||||
if (currentProgress?.completed) {
|
|
||||||
clearInterval(progressInterval)
|
|
||||||
}
|
|
||||||
}, 500)
|
|
||||||
// Cleanup after 60 seconds max
|
|
||||||
setTimeout(() => {
|
|
||||||
clearInterval(progressInterval)
|
|
||||||
setIsSyncing(false)
|
|
||||||
}, 60000)
|
|
||||||
} else {
|
} else {
|
||||||
setIsSyncing(false)
|
stopMonitoring()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (resyncError) {
|
} catch (resyncError) {
|
||||||
console.error('Error resynchronizing:', resyncError)
|
console.error('Error resynchronizing:', resyncError)
|
||||||
setIsSyncing(false)
|
stopMonitoring()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -289,8 +289,16 @@ export async function fetchAuthorPresentationFromPool(
|
|||||||
// Calculate totalSponsoring from cache before storing
|
// Calculate totalSponsoring from cache before storing
|
||||||
const { getAuthorSponsoring } = await import('./sponsoring')
|
const { getAuthorSponsoring } = await import('./sponsoring')
|
||||||
value.totalSponsoring = await getAuthorSponsoring(value.pubkey)
|
value.totalSponsoring = await getAuthorSponsoring(value.pubkey)
|
||||||
const { writeService } = await import('./writeService')
|
const { writeObjectToCache } = await import('./helpers/writeObjectHelper')
|
||||||
await writeService.writeObject('author', value.hash, event, value, tags.version ?? 0, tags.hidden, value.index, false)
|
await writeObjectToCache({
|
||||||
|
objectType: 'author',
|
||||||
|
hash: value.hash,
|
||||||
|
event,
|
||||||
|
parsed: value,
|
||||||
|
version: tags.version,
|
||||||
|
hidden: tags.hidden,
|
||||||
|
index: value.index,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { extractTagsFromEvent } from '../nostrTagSystem'
|
|||||||
import { parseObjectId } from '../urlGenerator'
|
import { parseObjectId } from '../urlGenerator'
|
||||||
import { getLatestVersion } from '../versionManager'
|
import { getLatestVersion } from '../versionManager'
|
||||||
import type { ObjectType } from '../objectCache'
|
import type { ObjectType } from '../objectCache'
|
||||||
|
import { writeObjectToCache } from './writeObjectHelper'
|
||||||
|
|
||||||
export interface EventCacheConfig {
|
export interface EventCacheConfig {
|
||||||
objectType: ObjectType
|
objectType: ObjectType
|
||||||
@ -75,8 +76,15 @@ export async function groupAndCacheEventsByHash(
|
|||||||
const version = getVersion ? getVersion(latestEvent) : extractTagsFromEvent(latestEvent).version ?? 0
|
const version = getVersion ? getVersion(latestEvent) : extractTagsFromEvent(latestEvent).version ?? 0
|
||||||
const hidden = getHidden ? getHidden(latestEvent) : extractTagsFromEvent(latestEvent).hidden ?? false
|
const hidden = getHidden ? getHidden(latestEvent) : extractTagsFromEvent(latestEvent).hidden ?? false
|
||||||
|
|
||||||
const { writeService } = await import('../writeService')
|
await writeObjectToCache({
|
||||||
await writeService.writeObject(objectType, hash, latestEvent, extracted, version, hidden, index, false)
|
objectType,
|
||||||
|
hash,
|
||||||
|
event: latestEvent,
|
||||||
|
parsed: extracted,
|
||||||
|
version,
|
||||||
|
hidden,
|
||||||
|
index,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,11 +109,16 @@ export async function cacheEventAsObject(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const index = (extracted as { index?: number })?.index ?? 0
|
const index = (extracted as { index?: number })?.index ?? 0
|
||||||
const version = tags.version ?? 0
|
|
||||||
const hidden = tags.hidden ?? false
|
|
||||||
|
|
||||||
const { writeService } = await import('../writeService')
|
await writeObjectToCache({
|
||||||
await writeService.writeObject(objectType, hash, event, extracted, version, hidden, index, false)
|
objectType,
|
||||||
|
hash,
|
||||||
|
event,
|
||||||
|
parsed: extracted,
|
||||||
|
version: tags.version,
|
||||||
|
hidden: tags.hidden,
|
||||||
|
index,
|
||||||
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,68 +6,79 @@
|
|||||||
import type { Event } from 'nostr-tools'
|
import type { Event } from 'nostr-tools'
|
||||||
import { extractTagsFromEvent } from '../nostrTagSystem'
|
import { extractTagsFromEvent } from '../nostrTagSystem'
|
||||||
import { extractPurchaseFromEvent, extractSponsoringFromEvent, extractReviewTipFromEvent } from '../metadataExtractor'
|
import { extractPurchaseFromEvent, extractSponsoringFromEvent, extractReviewTipFromEvent } from '../metadataExtractor'
|
||||||
|
import { writeObjectToCache } from './writeObjectHelper'
|
||||||
|
|
||||||
export async function cachePurchases(events: Event[]): Promise<void> {
|
export async function cachePurchases(events: Event[]): Promise<void> {
|
||||||
const { writeService } = await import('../writeService')
|
|
||||||
const { parsePurchaseFromEvent } = await import('../nostrEventParsing')
|
const { parsePurchaseFromEvent } = await import('../nostrEventParsing')
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
const extracted = await extractPurchaseFromEvent(event)
|
const extracted = await extractPurchaseFromEvent(event)
|
||||||
if (extracted) {
|
if (extracted) {
|
||||||
const purchase = await parsePurchaseFromEvent(event)
|
const purchase = await parsePurchaseFromEvent(event)
|
||||||
if (purchase) {
|
if (purchase?.hash) {
|
||||||
const purchaseTyped = purchase
|
await writeObjectToCache({
|
||||||
if (purchaseTyped.hash) {
|
objectType: 'purchase',
|
||||||
await writeService.writeObject('purchase', purchaseTyped.hash, event, purchaseTyped, 0, false, purchaseTyped.index ?? 0, false)
|
hash: purchase.hash,
|
||||||
}
|
event,
|
||||||
|
parsed: purchase,
|
||||||
|
index: purchase.index,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cacheSponsoring(events: Event[]): Promise<void> {
|
export async function cacheSponsoring(events: Event[]): Promise<void> {
|
||||||
const { writeService } = await import('../writeService')
|
|
||||||
const { parseSponsoringFromEvent } = await import('../nostrEventParsing')
|
const { parseSponsoringFromEvent } = await import('../nostrEventParsing')
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
const extracted = await extractSponsoringFromEvent(event)
|
const extracted = await extractSponsoringFromEvent(event)
|
||||||
if (extracted) {
|
if (extracted) {
|
||||||
const sponsoring = await parseSponsoringFromEvent(event)
|
const sponsoring = await parseSponsoringFromEvent(event)
|
||||||
if (sponsoring) {
|
if (sponsoring?.hash) {
|
||||||
const sponsoringTyped = sponsoring
|
await writeObjectToCache({
|
||||||
if (sponsoringTyped.hash) {
|
objectType: 'sponsoring',
|
||||||
await writeService.writeObject('sponsoring', sponsoringTyped.hash, event, sponsoringTyped, 0, false, sponsoringTyped.index ?? 0, false)
|
hash: sponsoring.hash,
|
||||||
}
|
event,
|
||||||
|
parsed: sponsoring,
|
||||||
|
index: sponsoring.index,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cacheReviewTips(events: Event[]): Promise<void> {
|
export async function cacheReviewTips(events: Event[]): Promise<void> {
|
||||||
const { writeService } = await import('../writeService')
|
|
||||||
const { parseReviewTipFromEvent } = await import('../nostrEventParsing')
|
const { parseReviewTipFromEvent } = await import('../nostrEventParsing')
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
const extracted = await extractReviewTipFromEvent(event)
|
const extracted = await extractReviewTipFromEvent(event)
|
||||||
if (extracted) {
|
if (extracted) {
|
||||||
const reviewTip = await parseReviewTipFromEvent(event)
|
const reviewTip = await parseReviewTipFromEvent(event)
|
||||||
if (reviewTip) {
|
if (reviewTip?.hash) {
|
||||||
const reviewTipTyped = reviewTip
|
await writeObjectToCache({
|
||||||
if (reviewTipTyped.hash) {
|
objectType: 'review_tip',
|
||||||
await writeService.writeObject('review_tip', reviewTipTyped.hash, event, reviewTipTyped, 0, false, reviewTipTyped.index ?? 0, false)
|
hash: reviewTip.hash,
|
||||||
}
|
event,
|
||||||
|
parsed: reviewTip,
|
||||||
|
index: reviewTip.index,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function cachePaymentNotes(events: Event[]): Promise<void> {
|
export async function cachePaymentNotes(events: Event[]): Promise<void> {
|
||||||
const { writeService } = await import('../writeService')
|
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
const tags = extractTagsFromEvent(event)
|
const tags = extractTagsFromEvent(event)
|
||||||
if (tags.type === 'payment' && tags.payment) {
|
if (tags.type === 'payment' && tags.payment) {
|
||||||
await writeService.writeObject('payment_note', event.id, event, {
|
await writeObjectToCache({
|
||||||
id: event.id,
|
objectType: 'payment_note',
|
||||||
type: 'payment_note',
|
hash: event.id,
|
||||||
eventId: event.id,
|
event,
|
||||||
}, 0, false, 0, false)
|
parsed: {
|
||||||
|
id: event.id,
|
||||||
|
type: 'payment_note',
|
||||||
|
eventId: event.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
91
lib/helpers/writeObjectHelper.ts
Normal file
91
lib/helpers/writeObjectHelper.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* Helper for writing objects to IndexedDB after extraction
|
||||||
|
* Centralizes the pattern of extract + writeObject with default parameters
|
||||||
|
* Optimizes imports by loading writeService once
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Event } from 'nostr-tools'
|
||||||
|
import type { ObjectType } from '../objectCache'
|
||||||
|
import { extractTagsFromEvent } from '../nostrTagSystem'
|
||||||
|
|
||||||
|
let writeServiceCache: typeof import('../writeService').writeService | null = null
|
||||||
|
|
||||||
|
async function getWriteService(): Promise<typeof import('../writeService').writeService> {
|
||||||
|
if (!writeServiceCache) {
|
||||||
|
const { writeService } = await import('../writeService')
|
||||||
|
writeServiceCache = writeService
|
||||||
|
}
|
||||||
|
return writeServiceCache
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WriteObjectParams {
|
||||||
|
objectType: ObjectType
|
||||||
|
hash: string
|
||||||
|
event: Event
|
||||||
|
parsed: unknown
|
||||||
|
version?: number
|
||||||
|
hidden?: boolean
|
||||||
|
index?: number
|
||||||
|
published?: false | string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write an object to IndexedDB cache
|
||||||
|
* Uses default values for version, hidden, index, and published if not provided
|
||||||
|
*/
|
||||||
|
export async function writeObjectToCache(params: WriteObjectParams): Promise<void> {
|
||||||
|
const {
|
||||||
|
objectType,
|
||||||
|
hash,
|
||||||
|
event,
|
||||||
|
parsed,
|
||||||
|
version,
|
||||||
|
hidden,
|
||||||
|
index,
|
||||||
|
published,
|
||||||
|
} = params
|
||||||
|
|
||||||
|
const tags = extractTagsFromEvent(event)
|
||||||
|
const writeService = await getWriteService()
|
||||||
|
|
||||||
|
await writeService.writeObject(
|
||||||
|
objectType,
|
||||||
|
hash,
|
||||||
|
event,
|
||||||
|
parsed,
|
||||||
|
version ?? tags.version ?? 0,
|
||||||
|
hidden ?? tags.hidden ?? false,
|
||||||
|
index ?? 0,
|
||||||
|
published ?? false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract and write an object from an event
|
||||||
|
* Combines extraction and writing in a single call
|
||||||
|
*/
|
||||||
|
export async function extractAndWriteObject<T extends { hash?: string; index?: number }>(
|
||||||
|
event: Event,
|
||||||
|
objectType: ObjectType,
|
||||||
|
extractor: (event: Event) => Promise<T | null>
|
||||||
|
): Promise<boolean> {
|
||||||
|
const extracted = await extractor(event)
|
||||||
|
if (!extracted) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const {hash} = extracted
|
||||||
|
if (!hash) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
await writeObjectToCache({
|
||||||
|
objectType,
|
||||||
|
hash,
|
||||||
|
event,
|
||||||
|
parsed: extracted,
|
||||||
|
index: extracted.index,
|
||||||
|
})
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
107
lib/hooks/useSyncProgress.ts
Normal file
107
lib/hooks/useSyncProgress.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* Custom hook for monitoring sync progress
|
||||||
|
* Centralizes the pattern of polling syncProgressManager and updating state
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useState, useEffect, useRef } from 'react'
|
||||||
|
import type { SyncProgress } from '../helpers/syncProgressHelper'
|
||||||
|
|
||||||
|
export interface UseSyncProgressOptions {
|
||||||
|
onComplete?: () => void | Promise<void>
|
||||||
|
pollInterval?: number
|
||||||
|
maxDuration?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UseSyncProgressResult {
|
||||||
|
syncProgress: SyncProgress | null
|
||||||
|
isSyncing: boolean
|
||||||
|
startMonitoring: () => void
|
||||||
|
stopMonitoring: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook to monitor sync progress from syncProgressManager
|
||||||
|
*/
|
||||||
|
export function useSyncProgress(options: UseSyncProgressOptions = {}): UseSyncProgressResult {
|
||||||
|
const { onComplete, pollInterval = 500, maxDuration = 60000 } = options
|
||||||
|
|
||||||
|
const [syncProgress, setSyncProgress] = useState<SyncProgress | null>(null)
|
||||||
|
const [isSyncing, setIsSyncing] = useState(false)
|
||||||
|
const intervalRef = useRef<NodeJS.Timeout | null>(null)
|
||||||
|
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
|
||||||
|
const onCompleteRef = useRef(onComplete)
|
||||||
|
const isMonitoringRef = useRef(false)
|
||||||
|
|
||||||
|
// Update onComplete ref when it changes
|
||||||
|
useEffect(() => {
|
||||||
|
onCompleteRef.current = onComplete
|
||||||
|
}, [onComplete])
|
||||||
|
|
||||||
|
const checkProgress = async (): Promise<void> => {
|
||||||
|
const { syncProgressManager } = await import('../syncProgressManager')
|
||||||
|
const currentProgress = syncProgressManager.getProgress()
|
||||||
|
if (currentProgress) {
|
||||||
|
setSyncProgress(currentProgress)
|
||||||
|
if (currentProgress.completed) {
|
||||||
|
setIsSyncing(false)
|
||||||
|
if (onCompleteRef.current) {
|
||||||
|
await onCompleteRef.current()
|
||||||
|
}
|
||||||
|
stopMonitoring()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const startMonitoring = (): void => {
|
||||||
|
if (isMonitoringRef.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isMonitoringRef.current = true
|
||||||
|
setIsSyncing(true)
|
||||||
|
setSyncProgress({ currentStep: 0, totalSteps: 7, completed: false })
|
||||||
|
|
||||||
|
// Poll progress periodically
|
||||||
|
intervalRef.current = setInterval(() => {
|
||||||
|
void checkProgress()
|
||||||
|
}, pollInterval)
|
||||||
|
|
||||||
|
// Cleanup after max duration
|
||||||
|
timeoutRef.current = setTimeout(() => {
|
||||||
|
stopMonitoring()
|
||||||
|
}, maxDuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
const stopMonitoring = (): void => {
|
||||||
|
if (!isMonitoringRef.current) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isMonitoringRef.current = false
|
||||||
|
setIsSyncing(false)
|
||||||
|
|
||||||
|
if (intervalRef.current) {
|
||||||
|
clearInterval(intervalRef.current)
|
||||||
|
intervalRef.current = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeoutRef.current) {
|
||||||
|
clearTimeout(timeoutRef.current)
|
||||||
|
timeoutRef.current = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup on unmount
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
stopMonitoring()
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return {
|
||||||
|
syncProgress,
|
||||||
|
isSyncing,
|
||||||
|
startMonitoring,
|
||||||
|
stopMonitoring,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -466,12 +466,15 @@ class NostrService {
|
|||||||
const objectTypes: Array<import('./objectCache').ObjectType> = ['author', 'series', 'publication', 'review', 'purchase', 'sponsoring', 'review_tip', 'payment_note']
|
const objectTypes: Array<import('./objectCache').ObjectType> = ['author', 'series', 'publication', 'review', 'purchase', 'sponsoring', 'review_tip', 'payment_note']
|
||||||
|
|
||||||
// First try to find in unpublished objects (faster)
|
// First try to find in unpublished objects (faster)
|
||||||
|
for (const objectType of objectTypes) {
|
||||||
|
// Load writeService once
|
||||||
|
const { writeService } = await import('./writeService')
|
||||||
|
|
||||||
for (const objectType of objectTypes) {
|
for (const objectType of objectTypes) {
|
||||||
try {
|
try {
|
||||||
const unpublished = await objectCache.getUnpublished(objectType)
|
const unpublished = await objectCache.getUnpublished(objectType)
|
||||||
const matching = unpublished.find((obj) => obj.event.id === eventId)
|
const matching = unpublished.find((obj) => obj.event.id === eventId)
|
||||||
if (matching) {
|
if (matching) {
|
||||||
const { writeService } = await import('./writeService')
|
|
||||||
await writeService.updatePublished(objectType, matching.id, published)
|
await writeService.updatePublished(objectType, matching.id, published)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -510,7 +513,6 @@ class NostrService {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (found) {
|
if (found) {
|
||||||
const { writeService } = await import('./writeService')
|
|
||||||
await writeService.updatePublished(objectType, found, published)
|
await writeService.updatePublished(objectType, found, published)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -256,6 +256,7 @@ class PlatformSyncService {
|
|||||||
*/
|
*/
|
||||||
private async processEvent(event: Event): Promise<void> {
|
private async processEvent(event: Event): Promise<void> {
|
||||||
const tags = extractTagsFromEvent(event)
|
const tags = extractTagsFromEvent(event)
|
||||||
|
const { writeObjectToCache } = await import('./helpers/writeObjectHelper')
|
||||||
|
|
||||||
// Log target event for debugging
|
// Log target event for debugging
|
||||||
if (event.id === '527d83e0af20bf23c3e104974090ccc21536ece72c24eb784b3642890f63b763') {
|
if (event.id === '527d83e0af20bf23c3e104974090ccc21536ece72c24eb784b3642890f63b763') {
|
||||||
@ -289,49 +290,92 @@ class PlatformSyncService {
|
|||||||
hash: parsed?.hash,
|
hash: parsed?.hash,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (parsed && parsed.hash) {
|
if (parsed?.hash) {
|
||||||
const { writeService } = await import('./writeService')
|
await writeObjectToCache({
|
||||||
await writeService.writeObject('author', parsed.hash, event, parsed, tags.version ?? 0, tags.hidden, parsed.index, false)
|
objectType: 'author',
|
||||||
|
hash: parsed.hash,
|
||||||
|
event,
|
||||||
|
parsed,
|
||||||
|
version: tags.version,
|
||||||
|
hidden: tags.hidden,
|
||||||
|
index: parsed.index,
|
||||||
|
})
|
||||||
if (event.id === '527d83e0af20bf23c3e104974090ccc21536ece72c24eb784b3642890f63b763') {
|
if (event.id === '527d83e0af20bf23c3e104974090ccc21536ece72c24eb784b3642890f63b763') {
|
||||||
console.warn(`[PlatformSync] Target event cached successfully as author with hash:`, parsed.hash)
|
console.warn(`[PlatformSync] Target event cached successfully as author with hash:`, parsed.hash)
|
||||||
}
|
}
|
||||||
} else if (event.id === '527d83e0af20bf23c3e104974090ccc21536ece72c24eb784b3642890f63b763') {
|
} else if (event.id === '527d83e0af20bf23c3e104974090ccc21536ece72c24eb784b3642890f63b763') {
|
||||||
console.warn(`[PlatformSync] Target event NOT cached: parsed=${parsed !== null}, hasHash=${parsed?.hash !== undefined}`)
|
console.warn(`[PlatformSync] Target event NOT cached: parsed=${parsed !== null}, hasHash=${parsed?.hash !== undefined}`)
|
||||||
}
|
}
|
||||||
} else if (tags.type === 'series') {
|
} else if (tags.type === 'series') {
|
||||||
const parsed = await parseSeriesFromEvent(event)
|
const parsed = await parseSeriesFromEvent(event)
|
||||||
if (parsed && parsed.hash) {
|
if (parsed?.hash) {
|
||||||
const { writeService } = await import('./writeService')
|
await writeObjectToCache({
|
||||||
await writeService.writeObject('series', parsed.hash, event, parsed, tags.version ?? 0, tags.hidden, parsed.index, false)
|
objectType: 'series',
|
||||||
|
hash: parsed.hash,
|
||||||
|
event,
|
||||||
|
parsed,
|
||||||
|
version: tags.version,
|
||||||
|
hidden: tags.hidden,
|
||||||
|
index: parsed.index,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else if (tags.type === 'publication') {
|
} else if (tags.type === 'publication') {
|
||||||
const parsed = await parseArticleFromEvent(event)
|
const parsed = await parseArticleFromEvent(event)
|
||||||
if (parsed && parsed.hash) {
|
if (parsed?.hash) {
|
||||||
const { writeService } = await import('./writeService')
|
await writeObjectToCache({
|
||||||
await writeService.writeObject('publication', parsed.hash, event, parsed, tags.version ?? 0, tags.hidden, parsed.index, false)
|
objectType: 'publication',
|
||||||
|
hash: parsed.hash,
|
||||||
|
event,
|
||||||
|
parsed,
|
||||||
|
version: tags.version,
|
||||||
|
hidden: tags.hidden,
|
||||||
|
index: parsed.index,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else if (tags.type === 'quote') {
|
} else if (tags.type === 'quote') {
|
||||||
const parsed = await parseReviewFromEvent(event)
|
const parsed = await parseReviewFromEvent(event)
|
||||||
if (parsed && parsed.hash) {
|
if (parsed?.hash) {
|
||||||
const { writeService } = await import('./writeService')
|
await writeObjectToCache({
|
||||||
await writeService.writeObject('review', parsed.hash, event, parsed, tags.version ?? 0, tags.hidden, parsed.index, false)
|
objectType: 'review',
|
||||||
|
hash: parsed.hash,
|
||||||
|
event,
|
||||||
|
parsed,
|
||||||
|
version: tags.version,
|
||||||
|
hidden: tags.hidden,
|
||||||
|
index: parsed.index,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} else if (event.kind === 9735) {
|
} else if (event.kind === 9735) {
|
||||||
// Zap receipts (kind 9735) can be sponsoring, purchase, or review_tip
|
// Zap receipts (kind 9735) can be sponsoring, purchase, or review_tip
|
||||||
const sponsoring = await parseSponsoringFromEvent(event)
|
const sponsoring = await parseSponsoringFromEvent(event)
|
||||||
if (sponsoring && sponsoring.hash) {
|
if (sponsoring?.hash) {
|
||||||
const { writeService } = await import('./writeService')
|
await writeObjectToCache({
|
||||||
await writeService.writeObject('sponsoring', sponsoring.hash, event, sponsoring, 0, false, sponsoring.index, false)
|
objectType: 'sponsoring',
|
||||||
|
hash: sponsoring.hash,
|
||||||
|
event,
|
||||||
|
parsed: sponsoring,
|
||||||
|
index: sponsoring.index,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
const purchase = await parsePurchaseFromEvent(event)
|
const purchase = await parsePurchaseFromEvent(event)
|
||||||
if (purchase && purchase.hash) {
|
if (purchase?.hash) {
|
||||||
const { writeService } = await import('./writeService')
|
await writeObjectToCache({
|
||||||
await writeService.writeObject('purchase', purchase.hash, event, purchase, 0, false, purchase.index, false)
|
objectType: 'purchase',
|
||||||
|
hash: purchase.hash,
|
||||||
|
event,
|
||||||
|
parsed: purchase,
|
||||||
|
index: purchase.index,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
const reviewTip = await parseReviewTipFromEvent(event)
|
const reviewTip = await parseReviewTipFromEvent(event)
|
||||||
if (reviewTip && reviewTip.hash) {
|
if (reviewTip?.hash) {
|
||||||
const { writeService } = await import('./writeService')
|
await writeObjectToCache({
|
||||||
await writeService.writeObject('review_tip', reviewTip.hash, event, reviewTip, 0, false, reviewTip.index, false)
|
objectType: 'review_tip',
|
||||||
|
hash: reviewTip.hash,
|
||||||
|
event,
|
||||||
|
parsed: reviewTip,
|
||||||
|
index: reviewTip.index,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,6 +84,7 @@ async function executeWriteTask(task) {
|
|||||||
|
|
||||||
// Listen for messages from main thread
|
// Listen for messages from main thread
|
||||||
self.addEventListener('message', (event) => {
|
self.addEventListener('message', (event) => {
|
||||||
|
// event is used to access event.data
|
||||||
const { type, data } = event.data
|
const { type, data } = event.data
|
||||||
|
|
||||||
// Add to queue with unique ID
|
// Add to queue with unique ID
|
||||||
@ -120,7 +121,7 @@ async function handleWriteObject(data, taskId) {
|
|||||||
const store = transaction.objectStore('objects')
|
const store = transaction.objectStore('objects')
|
||||||
|
|
||||||
// Vérifier si l'objet existe déjà pour préserver published
|
// Vérifier si l'objet existe déjà pour préserver published
|
||||||
const existing = await executeTransactionOperation(store, (s) => s.get(finalId)).catch(() => null)
|
const existing = await executeTransactionOperation(store, (s) => s.get(finalId)).catch((_e) => null)
|
||||||
|
|
||||||
// Préserver published si existant et non fourni
|
// Préserver published si existant et non fourni
|
||||||
const finalPublished = existing && published === false ? existing.published : (published ?? false)
|
const finalPublished = existing && published === false ? existing.published : (published ?? false)
|
||||||
@ -217,7 +218,7 @@ async function handleUpdatePublished(data, taskId) {
|
|||||||
* Handle write multi-table request
|
* Handle write multi-table request
|
||||||
* Transactions multi-tables : plusieurs transactions, logique de découpage côté worker
|
* Transactions multi-tables : plusieurs transactions, logique de découpage côté worker
|
||||||
*/
|
*/
|
||||||
async function handleWriteMultiTable(data, taskId) {
|
async function handleWriteMultiTable(data, _taskId) {
|
||||||
const { writes } = data // Array of { objectType, hash, event, parsed, version, hidden, index, published }
|
const { writes } = data // Array of { objectType, hash, event, parsed, version, hidden, index, published }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -250,7 +251,7 @@ async function handleWriteMultiTable(data, taskId) {
|
|||||||
finalId = `${hash}:${count}:${version}`
|
finalId = `${hash}:${count}:${version}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const existing = await executeTransactionOperation(store, (s) => s.get(finalId)).catch(() => null)
|
const existing = await executeTransactionOperation(store, (s) => s.get(finalId)).catch((_e) => null)
|
||||||
|
|
||||||
const finalPublished = existing && published === false ? existing.published : (published ?? false)
|
const finalPublished = existing && published === false ? existing.published : (published ?? false)
|
||||||
|
|
||||||
@ -281,7 +282,7 @@ async function handleWriteMultiTable(data, taskId) {
|
|||||||
|
|
||||||
self.postMessage({
|
self.postMessage({
|
||||||
type: 'WRITE_MULTI_TABLE_SUCCESS',
|
type: 'WRITE_MULTI_TABLE_SUCCESS',
|
||||||
data: { results, taskId },
|
data: { results },
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to write multi-table: ${error.message}`)
|
throw new Error(`Failed to write multi-table: ${error.message}`)
|
||||||
@ -301,7 +302,7 @@ async function handleCreateNotification(data, taskId) {
|
|||||||
|
|
||||||
// Vérifier si la notification existe déjà
|
// Vérifier si la notification existe déjà
|
||||||
const index = store.index('eventId')
|
const index = store.index('eventId')
|
||||||
const existing = await executeTransactionOperation(index, (idx) => idx.get(eventId)).catch(() => null)
|
const existing = await executeTransactionOperation(index, (idx) => idx.get(eventId)).catch((_e) => null)
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
// Notification déjà existante
|
// Notification déjà existante
|
||||||
@ -343,7 +344,7 @@ async function handleCreateNotification(data, taskId) {
|
|||||||
/**
|
/**
|
||||||
* Handle log publication request
|
* Handle log publication request
|
||||||
*/
|
*/
|
||||||
async function handleLogPublication(data, taskId) {
|
async function handleLogPublication(data, _taskId) {
|
||||||
const { eventId, relayUrl, success, error, objectType, objectId } = data
|
const { eventId, relayUrl, success, error, objectType, objectId } = data
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -417,7 +418,7 @@ function openDB(objectType) {
|
|||||||
* Open IndexedDB for notifications
|
* Open IndexedDB for notifications
|
||||||
*/
|
*/
|
||||||
function openNotificationDB() {
|
function openNotificationDB() {
|
||||||
return openIndexedDB('nostr_notifications', 1, (db) => {
|
return openIndexedDB('nostr_notifications', 1, (db, _event) => {
|
||||||
if (!db.objectStoreNames.contains('notifications')) {
|
if (!db.objectStoreNames.contains('notifications')) {
|
||||||
const store = db.createObjectStore('notifications', { keyPath: 'id' })
|
const store = db.createObjectStore('notifications', { keyPath: 'id' })
|
||||||
store.createIndex('type', 'type', { unique: false })
|
store.createIndex('type', 'type', { unique: false })
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user