@@ -106,6 +106,10 @@ self.addEventListener("push", (event) => {
106106 badge : notificationData . badge || "/logo.png" ,
107107 vibrate : [ 200 , 100 , 200 ] ,
108108 data : notificationData . data || { } ,
109+ requireInteraction : false ,
110+ silent : false ,
111+ tag : notificationData . tag || "default" ,
112+ renotify : true ,
109113 actions : notificationData . actions || [
110114 {
111115 action : "view" ,
@@ -119,7 +123,23 @@ self.addEventListener("push", (event) => {
119123 } ;
120124
121125 event . waitUntil (
122- self . registration . showNotification ( notificationData . title , options ) ,
126+ self . registration
127+ . showNotification ( notificationData . title , options )
128+ . then ( ( ) => {
129+ // Play notification sound by messaging all clients
130+ return self . clients . matchAll ( {
131+ type : "window" ,
132+ includeUncontrolled : true ,
133+ } ) ;
134+ } )
135+ . then ( ( clientList ) => {
136+ clientList . forEach ( ( client ) => {
137+ client . postMessage ( {
138+ type : "play-notification-sound" ,
139+ data : notificationData . data ,
140+ } ) ;
141+ } ) ;
142+ } ) ,
123143 ) ;
124144} ) ;
125145
@@ -209,7 +229,64 @@ self.addEventListener("message", (event) => {
209229
210230 if ( event . data && event . data . type === "SHOW_NOTIFICATION" ) {
211231 const { title, options } = event . data ;
212- event . waitUntil ( self . registration . showNotification ( title , options ) ) ;
232+ event . waitUntil (
233+ self . registration
234+ . showNotification ( title , options )
235+ . then ( ( ) => {
236+ // Notify clients to play sound
237+ return self . clients . matchAll ( {
238+ type : "window" ,
239+ includeUncontrolled : true ,
240+ } ) ;
241+ } )
242+ . then ( ( clientList ) => {
243+ clientList . forEach ( ( client ) => {
244+ client . postMessage ( {
245+ type : "play-notification-sound" ,
246+ data : options . data || { } ,
247+ } ) ;
248+ } ) ;
249+ } ) ,
250+ ) ;
251+ }
252+
253+ // Handle sync request from client
254+ if ( event . data && event . data . type === "SYNC_MESSAGES" ) {
255+ event . waitUntil (
256+ syncPendingMessages ( )
257+ . then ( ( ) => {
258+ // Notify client that sync is complete
259+ event . source . postMessage ( {
260+ type : "sync-complete" ,
261+ success : true ,
262+ } ) ;
263+ } )
264+ . catch ( ( error ) => {
265+ event . source . postMessage ( {
266+ type : "sync-complete" ,
267+ success : false ,
268+ error : error . message ,
269+ } ) ;
270+ } ) ,
271+ ) ;
272+ }
273+
274+ // Handle fetch new messages request
275+ if ( event . data && event . data . type === "FETCH_NEW_MESSAGES" ) {
276+ const { conversationId } = event . data ;
277+ event . waitUntil (
278+ fetchNewMessages ( conversationId )
279+ . then ( ( messages ) => {
280+ event . source . postMessage ( {
281+ type : "new-messages-fetched" ,
282+ conversationId,
283+ messages,
284+ } ) ;
285+ } )
286+ . catch ( ( error ) => {
287+ console . error ( "[Service Worker] Error fetching new messages:" , error ) ;
288+ } ) ,
289+ ) ;
213290 }
214291} ) ;
215292
@@ -219,21 +296,169 @@ self.addEventListener("sync", (event) => {
219296
220297 if ( event . tag === "sync-messages" ) {
221298 event . waitUntil (
222- // Sync pending messages
223- syncPendingMessages ( ) ,
299+ syncPendingMessages ( )
300+ . then ( ( ) => {
301+ console . log ( "[Service Worker] Messages synced successfully" ) ;
302+ // Notify all clients about successful sync
303+ return self . clients . matchAll ( { type : "window" } ) ;
304+ } )
305+ . then ( ( clientList ) => {
306+ clientList . forEach ( ( client ) => {
307+ client . postMessage ( {
308+ type : "messages-synced" ,
309+ success : true ,
310+ } ) ;
311+ } ) ;
312+ } )
313+ . catch ( ( error ) => {
314+ console . error ( "[Service Worker] Sync failed:" , error ) ;
315+ } ) ,
316+ ) ;
317+ }
318+
319+ if ( event . tag === "fetch-new-messages" ) {
320+ event . waitUntil (
321+ fetchAllNewMessages ( )
322+ . then ( ( ) => {
323+ // Notify clients about new messages
324+ return self . clients . matchAll ( { type : "window" } ) ;
325+ } )
326+ . then ( ( clientList ) => {
327+ clientList . forEach ( ( client ) => {
328+ client . postMessage ( {
329+ type : "new-messages-available" ,
330+ success : true ,
331+ } ) ;
332+ } ) ;
333+ } ) ,
224334 ) ;
225335 }
226336} ) ;
227337
338+ // Function to sync pending messages
228339async function syncPendingMessages ( ) {
229340 try {
230- // Retrieve pending messages from IndexedDB or cache
231- // Send them to the server
232- // This is a placeholder for future implementation
233- console . log ( "[Service Worker] Syncing pending messages..." ) ;
341+ // Open IndexedDB to get pending messages
342+ const db = await openDatabase ( ) ;
343+ const pendingMessages = await getPendingMessages ( db ) ;
344+
345+ if ( pendingMessages . length === 0 ) {
346+ console . log ( "[Service Worker] No pending messages to sync" ) ;
347+ return Promise . resolve ( ) ;
348+ }
349+
350+ console . log (
351+ `[Service Worker] Syncing ${ pendingMessages . length } pending messages...` ,
352+ ) ;
353+
354+ // Send each pending message
355+ const syncPromises = pendingMessages . map ( async ( msg ) => {
356+ try {
357+ const response = await fetch ( "/api/chat/send" , {
358+ method : "POST" ,
359+ headers : {
360+ "Content-Type" : "application/json" ,
361+ } ,
362+ credentials : "include" ,
363+ body : JSON . stringify ( msg . data ) ,
364+ } ) ;
365+
366+ if ( response . ok ) {
367+ // Remove from pending queue
368+ await removePendingMessage ( db , msg . id ) ;
369+ return { success : true , id : msg . id } ;
370+ } else {
371+ return { success : false , id : msg . id } ;
372+ }
373+ } catch ( error ) {
374+ console . error ( "[Service Worker] Failed to sync message:" , error ) ;
375+ return { success : false , id : msg . id , error } ;
376+ }
377+ } ) ;
378+
379+ await Promise . all ( syncPromises ) ;
234380 return Promise . resolve ( ) ;
235381 } catch ( error ) {
236382 console . error ( "[Service Worker] Sync failed:" , error ) ;
237383 return Promise . reject ( error ) ;
238384 }
239385}
386+
387+ // Function to fetch new messages for a conversation
388+ async function fetchNewMessages ( conversationId ) {
389+ try {
390+ const response = await fetch ( `/api/chat/messages/${ conversationId } ` , {
391+ credentials : "include" ,
392+ } ) ;
393+
394+ if ( response . ok ) {
395+ const messages = await response . json ( ) ;
396+ return messages ;
397+ } else {
398+ throw new Error ( "Failed to fetch messages" ) ;
399+ }
400+ } catch ( error ) {
401+ console . error ( "[Service Worker] Error fetching messages:" , error ) ;
402+ throw error ;
403+ }
404+ }
405+
406+ // Function to fetch all new messages when coming back online
407+ async function fetchAllNewMessages ( ) {
408+ try {
409+ const response = await fetch ( "/api/chat/conversations" , {
410+ credentials : "include" ,
411+ } ) ;
412+
413+ if ( response . ok ) {
414+ return await response . json ( ) ;
415+ } else {
416+ throw new Error ( "Failed to fetch conversations" ) ;
417+ }
418+ } catch ( error ) {
419+ console . error ( "[Service Worker] Error fetching all messages:" , error ) ;
420+ throw error ;
421+ }
422+ }
423+
424+ // IndexedDB helper functions
425+ function openDatabase ( ) {
426+ return new Promise ( ( resolve , reject ) => {
427+ const request = indexedDB . open ( "ChatDB" , 1 ) ;
428+
429+ request . onerror = ( ) => reject ( request . error ) ;
430+ request . onsuccess = ( ) => resolve ( request . result ) ;
431+
432+ request . onupgradeneeded = ( event ) => {
433+ const db = event . target . result ;
434+ if ( ! db . objectStoreNames . contains ( "pendingMessages" ) ) {
435+ db . createObjectStore ( "pendingMessages" , {
436+ keyPath : "id" ,
437+ autoIncrement : true ,
438+ } ) ;
439+ }
440+ } ;
441+ } ) ;
442+ }
443+
444+ function getPendingMessages ( db ) {
445+ return new Promise ( ( resolve , reject ) => {
446+ const transaction = db . transaction ( [ "pendingMessages" ] , "readonly" ) ;
447+ const store = transaction . objectStore ( "pendingMessages" ) ;
448+ const request = store . getAll ( ) ;
449+
450+ request . onsuccess = ( ) => resolve ( request . result ) ;
451+ request . onerror = ( ) => reject ( request . error ) ;
452+ } ) ;
453+ }
454+
455+ function removePendingMessage ( db , id ) {
456+ return new Promise ( ( resolve , reject ) => {
457+ const transaction = db . transaction ( [ "pendingMessages" ] , "readwrite" ) ;
458+ const store = transaction . objectStore ( "pendingMessages" ) ;
459+ const request = store . delete ( id ) ;
460+
461+ request . onsuccess = ( ) => resolve ( ) ;
462+ request . onerror = ( ) => reject ( request . error ) ;
463+ } ) ;
464+ }
0 commit comments