| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | // Copyright 2021 Signal Messenger, LLC
 | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  | import { z } from 'zod'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-25 16:29:49 -07:00
										 |  |  | import type { ReadonlyMessageAttributesType } from '../model-types.d'; | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  | import * as Errors from '../types/errors'; | 
					
						
							|  |  |  | import * as log from '../logging/log'; | 
					
						
							|  |  |  | import { GiftBadgeStates } from '../components/conversation/Message'; | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | import { ReadStatus } from '../messages/MessageReadStatus'; | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  | import { getMessageIdForLogging } from '../util/idForLogging'; | 
					
						
							|  |  |  | import { getMessageSentTimestamp } from '../util/getMessageSentTimestamp'; | 
					
						
							| 
									
										
										
										
											2022-08-23 13:38:13 -04:00
										 |  |  | import { isDownloaded } from '../types/Attachment'; | 
					
						
							| 
									
										
										
										
											2022-07-12 09:39:18 -07:00
										 |  |  | import { isIncoming } from '../state/selectors/message'; | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  | import { markViewed } from '../services/MessageUpdater'; | 
					
						
							| 
									
										
										
										
											2021-09-23 13:16:09 -05:00
										 |  |  | import { notificationService } from '../services/notifications'; | 
					
						
							| 
									
										
										
										
											2022-08-23 13:38:13 -04:00
										 |  |  | import { queueAttachmentDownloads } from '../util/queueAttachmentDownloads'; | 
					
						
							| 
									
										
										
										
											2023-04-10 20:54:43 -07:00
										 |  |  | import { queueUpdateMessage } from '../util/messageBatcher'; | 
					
						
							| 
									
										
										
										
											2024-04-15 20:11:48 -04:00
										 |  |  | import { AttachmentDownloadUrgency } from '../jobs/AttachmentDownloadManager'; | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  | import { isAciString } from '../util/isAciString'; | 
					
						
							| 
									
										
										
										
											2024-07-22 11:16:33 -07:00
										 |  |  | import { DataReader, DataWriter } from '../sql/Client'; | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const viewSyncTaskSchema = z.object({ | 
					
						
							|  |  |  |   type: z.literal('ViewSync').readonly(), | 
					
						
							|  |  |  |   senderAci: z.string().refine(isAciString), | 
					
						
							|  |  |  |   senderE164: z.string().optional(), | 
					
						
							|  |  |  |   senderId: z.string(), | 
					
						
							|  |  |  |   timestamp: z.number(), | 
					
						
							|  |  |  |   viewedAt: z.number(), | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export type ViewSyncTaskType = z.infer<typeof viewSyncTaskSchema>; | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-04 07:27:16 -08:00
										 |  |  | export type ViewSyncAttributesType = { | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |   envelopeId: string; | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  |   syncTaskId: string; | 
					
						
							|  |  |  |   viewSync: ViewSyncTaskType; | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-02 12:16:52 -04:00
										 |  |  | const viewSyncs = new Map<string, ViewSyncAttributesType>(); | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  | async function remove(sync: ViewSyncAttributesType): Promise<void> { | 
					
						
							| 
									
										
										
										
											2024-07-22 11:16:33 -07:00
										 |  |  |   await DataWriter.removeSyncTaskById(sync.syncTaskId); | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  | export async function forMessage( | 
					
						
							| 
									
										
										
										
											2024-07-25 16:29:49 -07:00
										 |  |  |   message: ReadonlyMessageAttributesType | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  | ): Promise<Array<ViewSyncAttributesType>> { | 
					
						
							| 
									
										
										
										
											2024-07-25 16:29:49 -07:00
										 |  |  |   const logId = `ViewSyncs.forMessage(${getMessageIdForLogging(message)})`; | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |   const sender = window.ConversationController.lookupOrCreate({ | 
					
						
							| 
									
										
										
										
											2024-07-25 16:29:49 -07:00
										 |  |  |     e164: message.source, | 
					
						
							|  |  |  |     serviceId: message.sourceServiceId, | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     reason: logId, | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2024-07-25 16:29:49 -07:00
										 |  |  |   const messageTimestamp = getMessageSentTimestamp(message, { | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     log, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const viewSyncValues = Array.from(viewSyncs.values()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   const matchingSyncs = viewSyncValues.filter(item => { | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  |     const { viewSync } = item; | 
					
						
							|  |  |  |     return ( | 
					
						
							|  |  |  |       viewSync.senderId === sender?.id && | 
					
						
							|  |  |  |       viewSync.timestamp === messageTimestamp | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (matchingSyncs.length > 0) { | 
					
						
							|  |  |  |     log.info( | 
					
						
							|  |  |  |       `${logId}: Found ${matchingSyncs.length} early view sync(s) for message ${messageTimestamp}` | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  |   await Promise.all( | 
					
						
							|  |  |  |     matchingSyncs.map(async sync => { | 
					
						
							|  |  |  |       await remove(sync); | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |   return matchingSyncs; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  | export async function onSync(sync: ViewSyncAttributesType): Promise<void> { | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  |   viewSyncs.set(sync.syncTaskId, sync); | 
					
						
							|  |  |  |   const { viewSync } = sync; | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  |   const logId = `ViewSyncs.onSync(timestamp=${viewSync.timestamp})`; | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |   try { | 
					
						
							| 
									
										
										
										
											2024-07-22 11:16:33 -07:00
										 |  |  |     const messages = await DataReader.getMessagesBySentAt(viewSync.timestamp); | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     const found = messages.find(item => { | 
					
						
							|  |  |  |       const sender = window.ConversationController.lookupOrCreate({ | 
					
						
							|  |  |  |         e164: item.source, | 
					
						
							|  |  |  |         serviceId: item.sourceServiceId, | 
					
						
							|  |  |  |         reason: logId, | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  |       return sender?.id === viewSync.senderId; | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     if (!found) { | 
					
						
							|  |  |  |       log.info( | 
					
						
							|  |  |  |         `${logId}: nothing found`, | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  |         viewSync.senderId, | 
					
						
							|  |  |  |         viewSync.senderE164, | 
					
						
							|  |  |  |         viewSync.senderAci | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |       ); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     notificationService.removeBy({ messageId: found.id }); | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-03 20:12:57 -04:00
										 |  |  |     const message = window.MessageCache.__DEPRECATED$register( | 
					
						
							|  |  |  |       found.id, | 
					
						
							|  |  |  |       found, | 
					
						
							|  |  |  |       'ViewSyncs.onSync' | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     let didChangeMessage = false; | 
					
						
							| 
									
										
										
										
											2022-08-23 13:38:13 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     if (message.get('readStatus') !== ReadStatus.Viewed) { | 
					
						
							|  |  |  |       didChangeMessage = true; | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  |       message.set(markViewed(message.attributes, viewSync.viewedAt)); | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |       const attachments = message.get('attachments'); | 
					
						
							|  |  |  |       if (!attachments?.every(isDownloaded)) { | 
					
						
							|  |  |  |         const updatedFields = await queueAttachmentDownloads( | 
					
						
							| 
									
										
										
										
											2024-04-15 20:11:48 -04:00
										 |  |  |           message.attributes, | 
					
						
							| 
									
										
										
										
											2024-09-03 18:00:51 -04:00
										 |  |  |           { urgency: AttachmentDownloadUrgency.STANDARD } | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |         ); | 
					
						
							|  |  |  |         if (updatedFields) { | 
					
						
							|  |  |  |           message.set(updatedFields); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-05-11 13:59:58 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-05-11 13:59:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     const giftBadge = message.get('giftBadge'); | 
					
						
							|  |  |  |     if (giftBadge) { | 
					
						
							|  |  |  |       didChangeMessage = true; | 
					
						
							|  |  |  |       message.set({ | 
					
						
							|  |  |  |         giftBadge: { | 
					
						
							|  |  |  |           ...giftBadge, | 
					
						
							|  |  |  |           state: isIncoming(message.attributes) | 
					
						
							|  |  |  |             ? GiftBadgeStates.Redeemed | 
					
						
							|  |  |  |             : GiftBadgeStates.Opened, | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-12-19 11:11:04 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |     if (didChangeMessage) { | 
					
						
							|  |  |  |       queueUpdateMessage(message.attributes); | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  |     await remove(sync); | 
					
						
							| 
									
										
										
										
											2023-08-21 16:08:27 -04:00
										 |  |  |   } catch (error) { | 
					
						
							|  |  |  |     log.error(`${logId} error:`, Errors.toLogFormat(error)); | 
					
						
							| 
									
										
										
										
											2024-06-17 12:24:39 -07:00
										 |  |  |     await remove(sync); | 
					
						
							| 
									
										
										
										
											2021-08-12 13:15:55 -05:00
										 |  |  |   } | 
					
						
							|  |  |  | } |