You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			197 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			197 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			TypeScript
		
	
| import _ from 'lodash';
 | |
| import { getMessageById } from '../../data/data';
 | |
| import { SignalService } from '../../protobuf';
 | |
| import { PnServer } from '../../pushnotification';
 | |
| import { MessageController } from '../messages';
 | |
| import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage';
 | |
| import { EncryptionType, RawMessage } from '../types';
 | |
| import { UserUtils } from '../utils';
 | |
| 
 | |
| // tslint:disable-next-line no-unnecessary-class
 | |
| export class MessageSentHandler {
 | |
|   public static async handlePublicMessageSentSuccess(
 | |
|     sentMessage: OpenGroupVisibleMessage,
 | |
|     result: { serverId: number; serverTimestamp: number }
 | |
|   ) {
 | |
|     const { serverId, serverTimestamp } = result;
 | |
|     try {
 | |
|       const foundMessage = await MessageSentHandler.fetchHandleMessageSentData(sentMessage);
 | |
| 
 | |
|       if (!foundMessage) {
 | |
|         throw new Error(
 | |
|           'handlePublicMessageSentSuccess(): The message should be in memory for an openGroup message'
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       foundMessage.set({
 | |
|         serverTimestamp,
 | |
|         serverId,
 | |
|         isPublic: true,
 | |
|         sent: true,
 | |
|         sent_at: serverTimestamp, // we quote by sent_at, so we MUST sent_at: serverTimestamp
 | |
|         sync: true,
 | |
|         synced: true,
 | |
|         sentSync: true,
 | |
|       });
 | |
|       await foundMessage.commit();
 | |
|       foundMessage.getConversation()?.updateLastMessage();
 | |
|     } catch (e) {
 | |
|       window?.log?.error('Error setting public on message');
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   public static async handleMessageSentSuccess(
 | |
|     sentMessage: RawMessage,
 | |
|     wrappedEnvelope?: Uint8Array
 | |
|   ) {
 | |
|     // The wrappedEnvelope will be set only if the message is not one of OpenGroupV2Message type.
 | |
|     const fetchedMessage = await MessageSentHandler.fetchHandleMessageSentData(sentMessage);
 | |
|     if (!fetchedMessage) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let sentTo = fetchedMessage.get('sent_to') || [];
 | |
| 
 | |
|     let isOurDevice = false;
 | |
|     if (sentMessage.device) {
 | |
|       isOurDevice = UserUtils.isUsFromCache(sentMessage.device);
 | |
|     }
 | |
|     // FIXME this is not correct and will cause issues with syncing
 | |
|     // At this point the only way to check for medium
 | |
|     // group is by comparing the encryption type
 | |
|     const isClosedGroupMessage = sentMessage.encryption === EncryptionType.ClosedGroup;
 | |
| 
 | |
|     // We trigger a sync message only when the message is not to one of our devices, AND
 | |
|     // the message is not for an open group (there is no sync for opengroups, each device pulls all messages), AND
 | |
|     // if we did not sync or trigger a sync message for this specific message already
 | |
|     const shouldTriggerSyncMessage =
 | |
|       !isOurDevice &&
 | |
|       !isClosedGroupMessage &&
 | |
|       !fetchedMessage.get('synced') &&
 | |
|       !fetchedMessage.get('sentSync');
 | |
| 
 | |
|     // A message is synced if we triggered a sync message (sentSync)
 | |
|     // and the current message was sent to our device (so a sync message)
 | |
|     const shouldMarkMessageAsSynced = isOurDevice && fetchedMessage.get('sentSync');
 | |
| 
 | |
|     const contentDecoded = SignalService.Content.decode(sentMessage.plainTextBuffer);
 | |
|     const { dataMessage } = contentDecoded;
 | |
| 
 | |
|     /**
 | |
|      * We should hit the notify endpoint for push notification only if:
 | |
|      *  • It's a one-to-one chat or a closed group
 | |
|      *  • The message has either text or attachments
 | |
|      */
 | |
|     const hasBodyOrAttachments = Boolean(
 | |
|       dataMessage &&
 | |
|         (dataMessage.body || (dataMessage.attachments && dataMessage.attachments.length))
 | |
|     );
 | |
|     const shouldNotifyPushServer = hasBodyOrAttachments && !isOurDevice;
 | |
| 
 | |
|     if (shouldNotifyPushServer) {
 | |
|       // notify the push notification server if needed
 | |
|       if (!wrappedEnvelope) {
 | |
|         window?.log?.warn('Should send PN notify but no wrapped envelope set.');
 | |
|       } else {
 | |
|         // we do not really care about the retsult.
 | |
|         await PnServer.notify(wrappedEnvelope, sentMessage.device);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Handle the sync logic here
 | |
|     if (shouldTriggerSyncMessage) {
 | |
|       if (dataMessage) {
 | |
|         try {
 | |
|           await fetchedMessage.sendSyncMessage(
 | |
|             dataMessage as SignalService.DataMessage,
 | |
|             sentMessage.timestamp
 | |
|           );
 | |
|         } catch (e) {
 | |
|           window?.log?.warn('Got an error while trying to sendSyncMessage():', e);
 | |
|         }
 | |
|       }
 | |
|     } else if (shouldMarkMessageAsSynced) {
 | |
|       fetchedMessage.set({ synced: true });
 | |
|     }
 | |
| 
 | |
|     sentTo = _.union(sentTo, [sentMessage.device]);
 | |
| 
 | |
|     fetchedMessage.set({
 | |
|       sent_to: sentTo,
 | |
|       sent: true,
 | |
|       expirationStartTimestamp: Date.now(),
 | |
|       sent_at: sentMessage.timestamp,
 | |
|     });
 | |
| 
 | |
|     await fetchedMessage.commit();
 | |
|     fetchedMessage.getConversation()?.updateLastMessage();
 | |
|   }
 | |
| 
 | |
|   public static async handleMessageSentFailure(
 | |
|     sentMessage: RawMessage | OpenGroupVisibleMessage,
 | |
|     error: any
 | |
|   ) {
 | |
|     const fetchedMessage = await MessageSentHandler.fetchHandleMessageSentData(sentMessage);
 | |
|     if (!fetchedMessage) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (error instanceof Error) {
 | |
|       await fetchedMessage.saveErrors(error);
 | |
|     }
 | |
| 
 | |
|     if (!(sentMessage instanceof OpenGroupVisibleMessage)) {
 | |
|       const isOurDevice = UserUtils.isUsFromCache(sentMessage.device);
 | |
|       // if this message was for ourself, and it was not already synced,
 | |
|       // it means that we failed to sync it.
 | |
|       // so just remove the flag saying that we are currently sending the sync message
 | |
|       if (isOurDevice && !fetchedMessage.get('sync')) {
 | |
|         fetchedMessage.set({ sentSync: false });
 | |
|       }
 | |
| 
 | |
|       fetchedMessage.set({
 | |
|         expirationStartTimestamp: Date.now(),
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     // always mark the message as sent.
 | |
|     // the fact that we have errors on the sent is based on the saveErrors()
 | |
|     fetchedMessage.set({
 | |
|       sent: true,
 | |
|     });
 | |
| 
 | |
|     await fetchedMessage.commit();
 | |
|     await fetchedMessage.getConversation()?.updateLastMessage();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * This function tries to find a message by messageId by first looking on the MessageController.
 | |
|    * The MessageController holds all messages being in memory.
 | |
|    * Those are the messages sent recently, recieved recently, or the one shown to the user.
 | |
|    *
 | |
|    * If the app restarted, it's very likely those messages won't be on the memory anymore.
 | |
|    * In this case, this function will look for it in the database and return it.
 | |
|    * If the message is found on the db, it will also register it to the MessageController so our subsequent calls are quicker.
 | |
|    */
 | |
|   private static async fetchHandleMessageSentData(m: RawMessage | OpenGroupVisibleMessage) {
 | |
|     // if a message was sent and this message was sent after the last app restart,
 | |
|     // this message is still in memory in the MessageController
 | |
|     const msg = MessageController.getInstance().get(m.identifier);
 | |
| 
 | |
|     if (!msg || !msg.message) {
 | |
|       // otherwise, look for it in the database
 | |
|       // nobody is listening to this freshly fetched message .trigger calls
 | |
|       // so we can just update the fields on the database
 | |
|       const dbMessage = await getMessageById(m.identifier);
 | |
| 
 | |
|       if (!dbMessage) {
 | |
|         return null;
 | |
|       }
 | |
|       MessageController.getInstance().register(m.identifier, dbMessage);
 | |
|       return dbMessage;
 | |
|     }
 | |
| 
 | |
|     return msg.message;
 | |
|   }
 | |
| }
 |