| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  | // Copyright 2021 Signal Messenger, LLC
 | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-26 14:15:33 -05:00
										 |  |  | import type { ConversationAttributesType } from '../model-types.d'; | 
					
						
							| 
									
										
										
										
											2021-06-17 10:15:10 -07:00
										 |  |  | import { hasErrors } from '../state/selectors/message'; | 
					
						
							| 
									
										
										
										
											2021-12-07 16:41:40 -06:00
										 |  |  | import { readReceiptsJobQueue } from '../jobs/readReceiptsJobQueue'; | 
					
						
							| 
									
										
										
										
											2021-07-23 17:02:36 -05:00
										 |  |  | import { readSyncJobQueue } from '../jobs/readSyncJobQueue'; | 
					
						
							| 
									
										
										
										
											2021-09-23 13:16:09 -05:00
										 |  |  | import { notificationService } from '../services/notifications'; | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  | import * as log from '../logging/log'; | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | export async function markConversationRead( | 
					
						
							|  |  |  |   conversationAttrs: ConversationAttributesType, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |   newestUnreadAt: number, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   options: { readAt?: number; sendReadReceipts: boolean } = { | 
					
						
							|  |  |  |     sendReadReceipts: true, | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-05-10 14:49:13 -04:00
										 |  |  | ): Promise<boolean> { | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   const { id: conversationId } = conversationAttrs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const [unreadMessages, unreadReactions] = await Promise.all([ | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     window.Signal.Data.getUnreadByConversationAndMarkRead({ | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       newestUnreadAt, | 
					
						
							|  |  |  |       readAt: options.readAt, | 
					
						
							|  |  |  |     }), | 
					
						
							|  |  |  |     window.Signal.Data.getUnreadReactionsAndMarkRead({ | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       conversationId, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |       newestUnreadAt, | 
					
						
							|  |  |  |     }), | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   ]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  |   log.info('markConversationRead', { | 
					
						
							| 
									
										
										
										
											2021-05-17 12:52:09 -04:00
										 |  |  |     conversationId, | 
					
						
							| 
									
										
										
										
											2021-12-08 11:52:46 -08:00
										 |  |  |     newestUnreadAt, | 
					
						
							| 
									
										
										
										
											2021-05-17 12:52:09 -04:00
										 |  |  |     unreadMessages: unreadMessages.length, | 
					
						
							|  |  |  |     unreadReactions: unreadReactions.length, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-10 14:49:13 -04:00
										 |  |  |   if (!unreadMessages.length && !unreadReactions.length) { | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-23 13:16:09 -05:00
										 |  |  |   notificationService.removeBy({ conversationId }); | 
					
						
							| 
									
										
										
										
											2021-05-17 12:52:09 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   const unreadReactionSyncData = new Map< | 
					
						
							|  |  |  |     string, | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |       messageId?: string; | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       senderUuid?: string; | 
					
						
							|  |  |  |       senderE164?: string; | 
					
						
							|  |  |  |       timestamp: number; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   >(); | 
					
						
							|  |  |  |   unreadReactions.forEach(reaction => { | 
					
						
							|  |  |  |     const targetKey = `${reaction.targetAuthorUuid}/${reaction.targetTimestamp}`; | 
					
						
							|  |  |  |     if (unreadReactionSyncData.has(targetKey)) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     unreadReactionSyncData.set(targetKey, { | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |       messageId: reaction.messageId, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       senderE164: undefined, | 
					
						
							|  |  |  |       senderUuid: reaction.targetAuthorUuid, | 
					
						
							|  |  |  |       timestamp: reaction.targetTimestamp, | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const allReadMessagesSync = unreadMessages.map(messageSyncData => { | 
					
						
							|  |  |  |     const message = window.MessageController.getById(messageSyncData.id); | 
					
						
							|  |  |  |     // we update the in-memory MessageModel with the fresh database call data
 | 
					
						
							|  |  |  |     if (message) { | 
					
						
							|  |  |  |       message.set(messageSyncData); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return { | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |       messageId: messageSyncData.id, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |       senderE164: messageSyncData.source, | 
					
						
							|  |  |  |       senderUuid: messageSyncData.sourceUuid, | 
					
						
							|  |  |  |       senderId: window.ConversationController.ensureContactIds({ | 
					
						
							|  |  |  |         e164: messageSyncData.source, | 
					
						
							|  |  |  |         uuid: messageSyncData.sourceUuid, | 
					
						
							|  |  |  |       }), | 
					
						
							|  |  |  |       timestamp: messageSyncData.sent_at, | 
					
						
							| 
									
										
										
										
											2021-06-17 10:15:10 -07:00
										 |  |  |       hasErrors: message ? hasErrors(message.attributes) : false, | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |     }; | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Some messages we're marking read are local notifications with no sender
 | 
					
						
							|  |  |  |   // If a message has errors, we don't want to send anything out about it.
 | 
					
						
							|  |  |  |   //   read syncs - let's wait for a client that really understands the message
 | 
					
						
							|  |  |  |   //      to mark it read. we'll mark our local error read locally, though.
 | 
					
						
							|  |  |  |   //   read receipts - here we can run into infinite loops, where each time the
 | 
					
						
							|  |  |  |   //      conversation is viewed, another error message shows up for the contact
 | 
					
						
							| 
									
										
										
										
											2021-05-10 14:49:13 -04:00
										 |  |  |   const unreadMessagesSyncData = allReadMessagesSync.filter( | 
					
						
							|  |  |  |     item => Boolean(item.senderId) && !item.hasErrors | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |   const readSyncs: Array<{ | 
					
						
							|  |  |  |     messageId?: string; | 
					
						
							|  |  |  |     senderE164?: string; | 
					
						
							|  |  |  |     senderUuid?: string; | 
					
						
							|  |  |  |     senderId?: string; | 
					
						
							|  |  |  |     timestamp: number; | 
					
						
							|  |  |  |     hasErrors?: string; | 
					
						
							| 
									
										
										
										
											2021-08-31 11:47:15 -05:00
										 |  |  |   }> = [...unreadMessagesSyncData, ...unreadReactionSyncData.values()]; | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   if (readSyncs.length && options.sendReadReceipts) { | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  |     log.info(`Sending ${readSyncs.length} read syncs`); | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |     // Because syncReadMessages sends to our other devices, and sendReadReceipts goes
 | 
					
						
							|  |  |  |     //   to a contact, we need accessKeys for both.
 | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |     if (window.ConversationController.areWePrimaryDevice()) { | 
					
						
							| 
									
										
										
										
											2021-09-17 14:27:53 -04:00
										 |  |  |       log.warn( | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |         'markConversationRead: We are primary device; not sending read syncs' | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2021-07-23 17:02:36 -05:00
										 |  |  |       readSyncJobQueue.add({ readSyncs }); | 
					
						
							| 
									
										
										
										
											2021-07-15 16:48:09 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-07 16:41:40 -06:00
										 |  |  |     await readReceiptsJobQueue.addIfAllowedByUser( | 
					
						
							|  |  |  |       window.storage, | 
					
						
							|  |  |  |       allReadMessagesSync | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-19 13:45:18 -07:00
										 |  |  |   window.Whisper.ExpiringMessagesListener.update(); | 
					
						
							|  |  |  |   window.Whisper.TapToViewMessagesListener.update(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-10 14:49:13 -04:00
										 |  |  |   return true; | 
					
						
							| 
									
										
										
										
											2021-05-06 18:15:25 -07:00
										 |  |  | } |