2026-01-07 01:51:26 +01:00

294 lines
7.6 KiB
JavaScript

/**
* Service Worker for background synchronization
* Handles platform sync, user content sync, and publish worker
*/
const CACHE_NAME = 'zapwall-sync-v1'
const SYNC_INTERVAL_MS = 60000 // 1 minute
const REPUBLISH_INTERVAL_MS = 30000 // 30 seconds
let syncInProgress = false
let publishWorkerInterval = null
let notificationDetectorInterval = null
// Install event - cache resources
self.addEventListener('install', (event) => {
console.log('[SW] Service Worker installing')
self.skipWaiting() // Activate immediately
})
// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
console.log('[SW] Service Worker activating')
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
)
})
)
return self.clients.claim() // Take control of all pages immediately
})
// Message handler - communication with main thread
self.addEventListener('message', (event) => {
const { type, data } = event.data
switch (type) {
case 'START_PLATFORM_SYNC':
handleStartPlatformSync()
break
case 'STOP_PLATFORM_SYNC':
handleStopPlatformSync()
break
case 'START_USER_SYNC':
handleStartUserSync(data?.userPubkey)
break
case 'START_PUBLISH_WORKER':
handleStartPublishWorker()
break
case 'STOP_PUBLISH_WORKER':
handleStopPublishWorker()
break
case 'PUBLISH_EVENT':
handlePublishEvent(data?.event, data?.relays)
break
case 'START_NOTIFICATION_DETECTOR':
handleStartNotificationDetector(data?.userPubkey)
break
case 'STOP_NOTIFICATION_DETECTOR':
handleStopNotificationDetector()
break
case 'PING':
event.ports[0]?.postMessage({ type: 'PONG' })
break
default:
console.warn('[SW] Unknown message type:', type)
}
})
// Periodic sync event (if supported)
self.addEventListener('periodicsync', (event) => {
if (event.tag === 'platform-sync') {
event.waitUntil(handleStartPlatformSync())
} else if (event.tag === 'publish-worker') {
event.waitUntil(handlePublishWorker())
}
})
/**
* Start platform sync
*/
async function handleStartPlatformSync() {
if (syncInProgress) {
console.log('[SW] Platform sync already in progress')
return
}
syncInProgress = true
console.log('[SW] Starting platform sync')
try {
// Request sync from main thread (we need access to nostrService)
broadcastToClients({ type: 'SYNC_REQUEST', data: { syncType: 'platform' } })
// Set up periodic sync
if ('periodicSync' in self.registration) {
try {
await self.registration.periodicSync.register('platform-sync', {
minInterval: SYNC_INTERVAL_MS,
})
} catch (error) {
console.warn('[SW] Periodic sync not supported:', error)
}
}
} catch (error) {
console.error('[SW] Error starting platform sync:', error)
} finally {
syncInProgress = false
}
}
/**
* Stop platform sync
*/
function handleStopPlatformSync() {
console.log('[SW] Stopping platform sync')
if ('periodicSync' in self.registration) {
self.registration.periodicSync
.unregister('platform-sync')
.catch((error) => {
console.warn('[SW] Error unregistering periodic sync:', error)
})
}
}
/**
* Start user content sync
*/
async function handleStartUserSync(userPubkey) {
if (!userPubkey) {
console.warn('[SW] User sync requires pubkey')
return
}
console.log('[SW] Starting user content sync for:', userPubkey)
try {
// Request sync from main thread
broadcastToClients({
type: 'SYNC_REQUEST',
data: { syncType: 'user', userPubkey },
})
} catch (error) {
console.error('[SW] Error starting user sync:', error)
}
}
/**
* Start publish worker
*/
function handleStartPublishWorker() {
console.log('[SW] Starting publish worker')
// Clear existing interval
if (publishWorkerInterval) {
clearInterval(publishWorkerInterval)
}
// Process unpublished objects periodically
publishWorkerInterval = setInterval(() => {
handlePublishWorker()
}, REPUBLISH_INTERVAL_MS)
// Process immediately
handlePublishWorker()
}
/**
* Stop publish worker
*/
function handleStopPublishWorker() {
console.log('[SW] Stopping publish worker')
if (publishWorkerInterval) {
clearInterval(publishWorkerInterval)
publishWorkerInterval = null
}
}
/**
* Process unpublished objects
*/
async function handlePublishWorker() {
console.log('[SW] Processing unpublished objects')
try {
// Request publish worker execution from main thread
broadcastToClients({ type: 'PUBLISH_WORKER_REQUEST' })
} catch (error) {
console.error('[SW] Error in publish worker:', error)
}
}
/**
* Publish event to relays
*/
async function handlePublishEvent(event, relays) {
if (!event || !relays || relays.length === 0) {
console.warn('[SW] Invalid publish event data')
return
}
console.log('[SW] Publishing event:', event.id, 'to', relays.length, 'relays')
try {
// Request publish from main thread (we need access to nostrService)
broadcastToClients({
type: 'PUBLISH_REQUEST',
data: { event, relays },
})
} catch (error) {
console.error('[SW] Error publishing event:', error)
}
}
/**
* Broadcast message to all clients
*/
function broadcastToClients(message) {
self.clients.matchAll().then((clients) => {
clients.forEach((client) => {
client.postMessage(message)
})
})
}
// Background sync event (fallback for browsers without periodic sync)
self.addEventListener('sync', (event) => {
if (event.tag === 'platform-sync') {
event.waitUntil(handleStartPlatformSync())
} else if (event.tag === 'publish-worker') {
event.waitUntil(handlePublishWorker())
}
})
/**
* Start notification detector
* Service Worker directly populates notifications table
*/
function handleStartNotificationDetector(userPubkey) {
console.log('[SW] Starting notification detector for:', userPubkey)
// Clear existing interval
if (notificationDetectorInterval) {
clearInterval(notificationDetectorInterval)
}
// Service Worker directly scans IndexedDB and creates notifications
// This runs in the Service Worker context
notificationDetectorInterval = setInterval(() => {
void scanAndCreateNotifications(userPubkey)
}, 30000) // Every 30 seconds
// Process immediately
void scanAndCreateNotifications(userPubkey)
}
/**
* Scan IndexedDB and create notifications
* Service Worker détecte les changements de manière indépendante
* Il n'est pas dépendant du service d'écriture
*/
async function scanAndCreateNotifications(userPubkey) {
try {
// Le Service Worker détecte les changements indépendamment
// Il scanne IndexedDB directement pour détecter les nouveaux événements
// et crée les notifications via le Web Worker d'écriture
// Pour l'instant, on demande au thread principal de scanner
// car le Service Worker n'a pas accès direct à objectCache
// TODO: Implémenter la détection directe dans le Service Worker si possible
broadcastToClients({
type: 'NOTIFICATION_DETECT_REQUEST',
data: { userPubkey },
})
} catch (error) {
console.error('[SW] Error in notification detection:', error)
}
}
/**
* Stop notification detector
*/
function handleStopNotificationDetector() {
console.log('[SW] Stopping notification detector')
if (notificationDetectorInterval) {
clearInterval(notificationDetectorInterval)
notificationDetectorInterval = null
}
}
console.log('[SW] Service Worker loaded')