// Copyright 2020-2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only /* eslint-disable max-classes-per-file */ import { Collection, Model } from 'backbone'; import { MessageModel } from '../models/messages'; type DeleteAttributesType = { targetSentTimestamp: number; serverTimestamp: number; fromId: string; }; export class DeleteModel extends Model {} let singleton: Deletes | undefined; export class Deletes extends Collection { static getSingleton(): Deletes { if (!singleton) { singleton = new Deletes(); } return singleton; } forMessage(message: MessageModel): Array { const matchingDeletes = this.filter(item => { return ( item.get('targetSentTimestamp') === message.get('sent_at') && item.get('fromId') === message.getContactId() ); }); if (matchingDeletes.length > 0) { window.log.info('Found early DOE for message'); this.remove(matchingDeletes); return matchingDeletes; } return []; } async onDelete(del: DeleteModel): Promise { try { // The conversation the deleted message was in; we have to find it in the database // to to figure that out. const targetConversation = await window.ConversationController.getConversationForTargetMessage( del.get('fromId'), del.get('targetSentTimestamp') ); if (!targetConversation) { window.log.info( 'No target conversation for DOE', del.get('fromId'), del.get('targetSentTimestamp') ); return; } // Do not await, since this can deadlock the queue targetConversation.queueJob('Deletes.onDelete', async () => { window.log.info('Handling DOE for', del.get('targetSentTimestamp')); const messages = await window.Signal.Data.getMessagesBySentAt( del.get('targetSentTimestamp'), { MessageCollection: window.Whisper.MessageCollection, } ); const targetMessage = messages.find( m => del.get('fromId') === m.getContactId() ); if (!targetMessage) { window.log.info( 'No message for DOE', del.get('fromId'), del.get('targetSentTimestamp') ); return; } const message = window.MessageController.register( targetMessage.id, targetMessage ); await window.Signal.Util.deleteForEveryone(message, del); this.remove(del); }); } catch (error) { window.log.error( 'Deletes.onDelete error:', error && error.stack ? error.stack : error ); } } }