story-research-zapwall/lib/swSyncHandler.ts
2026-01-10 09:41:57 +01:00

186 lines
6.5 KiB
TypeScript

/**
* Service Worker sync handler - executes sync operations in main thread
* Listens to requests from Service Worker and executes them
*/
import { platformSyncService } from './platformSync'
import { syncUserContentToCache } from './userContentSync'
import { publishWorker } from './publishWorker'
import { nostrService } from './nostr'
import { swClient } from './swClient'
import type { Event } from 'nostr-tools'
class ServiceWorkerSyncHandler {
private initialized = false
/**
* Initialize the sync handler
* Sets up message listeners from Service Worker
*/
async initialize(): Promise<void> {
if (this.initialized) {
return
}
if (typeof window === 'undefined') {
return
}
try {
await swClient.register()
this.registerMessageHandlers()
this.initialized = true
console.warn('[SWSyncHandler] Initialized')
} catch (error) {
console.error('[SWSyncHandler] Initialization failed:', error)
}
}
private registerMessageHandlers(): void {
swClient.onMessage('SYNC_REQUEST', (data: unknown) => {
void this.handleSyncRequestMessage(data)
})
swClient.onMessage('PUBLISH_WORKER_REQUEST', () => {
void this.handlePublishWorkerRequest()
})
swClient.onMessage('PUBLISH_REQUEST', (data: unknown) => {
void this.handlePublishRequestMessage(data)
})
swClient.onMessage('NOTIFICATION_DETECT_REQUEST', (data: unknown) => {
void this.handleNotificationDetectRequestMessage(data)
})
}
private async handleSyncRequestMessage(data: unknown): Promise<void> {
const syncData = data as { syncType: string; userPubkey?: string }
if (syncData.syncType === 'platform') {
await this.handlePlatformSyncRequest()
return
}
if (syncData.syncType === 'user' && syncData.userPubkey) {
await this.handleUserSyncRequest(syncData.userPubkey)
}
}
private async handlePublishRequestMessage(data: unknown): Promise<void> {
const publishData = data as { event: Event; relays: string[] }
await this.handlePublishRequest(publishData.event, publishData.relays)
}
private async handleNotificationDetectRequestMessage(data: unknown): Promise<void> {
const detectData = data as { userPubkey: string }
await this.handleNotificationDetectRequest(detectData.userPubkey)
}
/**
* Handle platform sync request from Service Worker
*/
private async handlePlatformSyncRequest(): Promise<void> {
try {
console.warn('[SWSyncHandler] Executing platform sync request')
await platformSyncService.startSync()
} catch (error) {
console.error('[SWSyncHandler] Error in platform sync:', error)
}
}
/**
* Handle user sync request from Service Worker
*/
private async handleUserSyncRequest(userPubkey: string): Promise<void> {
try {
console.warn('[SWSyncHandler] Executing user sync request for:', userPubkey)
await syncUserContentToCache(userPubkey)
} catch (error) {
console.error('[SWSyncHandler] Error in user sync:', error)
}
}
/**
* Handle publish worker request from Service Worker
*/
private async handlePublishWorkerRequest(): Promise<void> {
try {
console.warn('[SWSyncHandler] Executing publish worker request')
// Trigger publish worker processing
// The publishWorker will process unpublished objects
await publishWorker['processUnpublished']()
} catch (error) {
console.error('[SWSyncHandler] Error in publish worker:', error)
}
}
/**
* Handle notification detection request from Service Worker
* Le Service Worker détecte les changements indépendamment
* Ce handler exécute la détection dans le thread principal et crée les notifications via writeService
*/
private async handleNotificationDetectRequest(userPubkey: string): Promise<void> {
try {
console.warn('[SWSyncHandler] Executing notification detection request for:', userPubkey)
const { notificationDetector } = await import('./notificationDetector')
// Scanner IndexedDB pour détecter les nouveaux événements
await notificationDetector.scan()
// Les notifications sont créées via notificationDetector qui utilise notificationService
// qui utilise maintenant writeService pour écrire via Web Worker
} catch (error) {
console.error('[SWSyncHandler] Error in notification detection:', error)
}
}
/**
* Handle publish request from Service Worker
* Uses websocketService to route events to Service Worker
*/
private async handlePublishRequest(event: Event, relays: string[]): Promise<void> {
try {
console.warn('[SWSyncHandler] Executing publish request for event:', event.id)
const { websocketService } = await import('./websocketService')
const { publishLog } = await import('./publishLog')
// Publish to specified relays via websocketService (routes to Service Worker)
const statuses = await websocketService.publishEvent(event, relays)
const successfulRelays = logPublishStatuses({ publishLog, eventId: event.id, relays, statuses })
// Update published status in IndexedDB
await updatePublishedStatusUnsafe(event.id, successfulRelays.length > 0 ? successfulRelays : false)
} catch (error) {
console.error('[SWSyncHandler] Error in publish request:', error)
}
}
}
export const swSyncHandler = new ServiceWorkerSyncHandler()
function logPublishStatuses(params: {
publishLog: { logPublication: (params: { eventId: string; relayUrl: string; success: boolean; error?: string }) => Promise<void> }
eventId: string
relays: string[]
statuses: Array<{ success: boolean; error?: string }>
}): string[] {
const successfulRelays: string[] = []
params.statuses.forEach((status, index) => {
const relayUrl = params.relays[index]
if (!relayUrl) {
return
}
if (status.success) {
successfulRelays.push(relayUrl)
void params.publishLog.logPublication({ eventId: params.eventId, relayUrl, success: true })
return
}
const errorMessage = status.error ?? 'Unknown error'
console.error(`[SWSyncHandler] Relay ${relayUrl} failed:`, errorMessage)
void params.publishLog.logPublication({ eventId: params.eventId, relayUrl, success: false, error: errorMessage })
})
return successfulRelays
}
async function updatePublishedStatusUnsafe(eventId: string, published: false | string[]): Promise<void> {
const service = nostrService as unknown as { updatePublishedStatus: (eventId: string, published: false | string[]) => Promise<void> }
await service.updatePublishedStatus(eventId, published)
}