299 lines
7.7 KiB
JavaScript
299 lines
7.7 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
|
|
|
|
const self = globalThis
|
|
const caches = globalThis.caches
|
|
const setInterval = globalThis.setInterval
|
|
const clearInterval = globalThis.clearInterval
|
|
|
|
let syncInProgress = false
|
|
let publishWorkerInterval = null
|
|
let notificationDetectorInterval = null
|
|
|
|
// Install event - cache resources
|
|
self.addEventListener('install', () => {
|
|
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')
|