diff --git a/ts/session/disappearing_messages/index.ts b/ts/session/disappearing_messages/index.ts index 8c54de7ca..f74783417 100644 --- a/ts/session/disappearing_messages/index.ts +++ b/ts/session/disappearing_messages/index.ts @@ -422,6 +422,34 @@ export async function checkForExpireUpdateInContentMessage( return expireUpdate; } +/** + * Checks if an outgoing message is meant to disappear and if so trigger the timer + */ +export function checkForExpiringOutgoingMessage(message: MessageModel, location?: string) { + const convo = message.getConversation(); + const expireTimer = message.getExpireTimer(); + const expirationType = message.getExpirationType(); + + if ( + convo && + expirationType && + expireTimer > 0 && + Boolean(message.getExpirationStartTimestamp()) === false + ) { + const expirationMode = changeToDisappearingConversationMode(convo, expirationType, expireTimer); + + if (expirationMode !== 'off') { + message.set({ + expirationStartTimestamp: setExpirationStartTimestamp( + expirationMode, + message.get('sent_at'), + location + ), + }); + } + } +} + // TODO legacy messages support will be removed in a future release export function getMessageReadyToDisappear( conversationModel: ConversationModel, diff --git a/ts/session/sending/MessageSentHandler.ts b/ts/session/sending/MessageSentHandler.ts index 504e4a96c..6de155f42 100644 --- a/ts/session/sending/MessageSentHandler.ts +++ b/ts/session/sending/MessageSentHandler.ts @@ -2,10 +2,7 @@ import _ from 'lodash'; import { Data } from '../../data/data'; import { SignalService } from '../../protobuf'; import { PnServer } from '../apis/push_notification_api'; -import { - changeToDisappearingConversationMode, - setExpirationStartTimestamp, -} from '../disappearing_messages'; +import { checkForExpiringOutgoingMessage } from '../disappearing_messages'; import { OpenGroupVisibleMessage } from '../messages/outgoing/visibleMessage/OpenGroupVisibleMessage'; import { RawMessage } from '../types'; import { UserUtils } from '../utils'; @@ -130,29 +127,7 @@ async function handleMessageSentSuccess( sent_at: effectiveTimestamp, }); - const convo = fetchedMessage.getConversation(); - const expireTimer = fetchedMessage.getExpireTimer(); - const expirationType = fetchedMessage.getExpirationType(); - - if ( - convo && - expirationType && - expireTimer > 0 && - Boolean(fetchedMessage.getExpirationStartTimestamp()) === false - ) { - const expirationMode = changeToDisappearingConversationMode(convo, expirationType, expireTimer); - - // NOTE starting disappearing messages timer for all outbound messages - if (expirationMode !== 'off') { - fetchedMessage.set({ - expirationStartTimestamp: setExpirationStartTimestamp( - expirationMode, - fetchedMessage.get('sent_at'), - 'handleMessageSentSuccess' - ), - }); - } - } + checkForExpiringOutgoingMessage(fetchedMessage, 'handleMessageSentSuccess'); await fetchedMessage.commit(); fetchedMessage.getConversation()?.updateLastMessage(); diff --git a/ts/test/session/unit/disappearing_messages/DisappearingMessage_test.ts b/ts/test/session/unit/disappearing_messages/DisappearingMessage_test.ts index 36e267ad8..5bc1b6984 100644 --- a/ts/test/session/unit/disappearing_messages/DisappearingMessage_test.ts +++ b/ts/test/session/unit/disappearing_messages/DisappearingMessage_test.ts @@ -8,6 +8,7 @@ import { changeToDisappearingConversationMode, changeToDisappearingMessageType, checkForExpireUpdateInContentMessage, + checkForExpiringOutgoingMessage, setExpirationStartTimestamp, } from '../../../../session/disappearing_messages'; import { @@ -22,6 +23,7 @@ import { generateDisappearingVisibleMessage, generateFakeExpirationTimerUpdate, generateFakeIncomingPrivateMessage, + generateFakeOutgoingPrivateMessage, generateVisibleMessage, } from '../../../test-utils/utils'; @@ -469,6 +471,91 @@ describe('DisappearingMessage', () => { }); }); + describe('checkForExpiringInOutgoingMessage', () => { + it('if the message is supposed to disappear then the expirationStartTimestamp should be set to the sent_at value', async () => { + const conversation = new ConversationModel({ + ...conversationArgs, + id: ourNumber, + } as any); + const message = generateFakeOutgoingPrivateMessage(conversation.get('id')); + message.set({ + expirationType: 'deleteAfterRead', + expireTimer: 300, + sent_at: GetNetworkTime.getNowWithNetworkOffset(), + }); + Sinon.stub(message, 'getConversation').returns(conversation); + + checkForExpiringOutgoingMessage(message, 'unit tests'); + + expect(message.getExpirationStartTimestamp(), 'it should be defined').to.not.be.undefined; + expect( + isValidUnixTimestamp(message.getExpirationStartTimestamp()), + 'it should be a valid unix timestamp' + ).to.be.true; + expect(message.getExpirationStartTimestamp(), 'it should equal the sent_at value').to.equal( + message.get('sent_at') + ); + }); + it('if there is no expireTimer then the expirationStartTimestamp should be undefined', async () => { + const conversation = new ConversationModel({ + ...conversationArgs, + id: ourNumber, + } as any); + const message = generateFakeOutgoingPrivateMessage(conversation.get('id')); + message.set({ + expirationType: 'deleteAfterRead', + sent_at: GetNetworkTime.getNowWithNetworkOffset(), + }); + Sinon.stub(message, 'getConversation').returns(conversation); + + checkForExpiringOutgoingMessage(message, 'unit tests'); + + expect(message.getExpirationStartTimestamp(), 'it should be undefined').to.be.undefined; + }); + it('if there is no expirationType then the expirationStartTimestamp should be undefined', async () => { + const conversation = new ConversationModel({ + ...conversationArgs, + id: ourNumber, + } as any); + const message = generateFakeOutgoingPrivateMessage(conversation.get('id')); + message.set({ + expireTimer: 300, + sent_at: GetNetworkTime.getNowWithNetworkOffset(), + }); + Sinon.stub(message, 'getConversation').returns(conversation); + + checkForExpiringOutgoingMessage(message, 'unit tests'); + + expect(message.getExpirationStartTimestamp(), 'it should be undefined').to.be.undefined; + }); + it('if expirationStartTimestamp is already defined then it should not have changed', async () => { + const now = GetNetworkTime.getNowWithNetworkOffset(); + const conversation = new ConversationModel({ + ...conversationArgs, + id: ourNumber, + } as any); + const message = generateFakeOutgoingPrivateMessage(conversation.get('id')); + message.set({ + expirationType: 'deleteAfterRead', + expireTimer: 300, + sent_at: now, + expirationStartTimestamp: now + 10000, + }); + Sinon.stub(message, 'getConversation').returns(conversation); + + checkForExpiringOutgoingMessage(message, 'unit tests'); + + expect(message.getExpirationStartTimestamp(), 'it should be defined').to.not.be.undefined; + expect( + isValidUnixTimestamp(message.getExpirationStartTimestamp()), + 'it should be a valid unix timestamp' + ).to.be.true; + expect(message.getExpirationStartTimestamp(), 'it should equal its original value').to.equal( + now + 10000 + ); + }); + }); + describe('conversation.ts', () => { describe('updateExpireTimer', () => { it('if the coversation is public it should return false', async () => { diff --git a/ts/test/test-utils/utils/message.ts b/ts/test/test-utils/utils/message.ts index a446149ea..5b1f2e14f 100644 --- a/ts/test/test-utils/utils/message.ts +++ b/ts/test/test-utils/utils/message.ts @@ -107,6 +107,15 @@ export function generateFakeIncomingPrivateMessage(): MessageModel { }); } +export function generateFakeOutgoingPrivateMessage(pubkey?: string): MessageModel { + const convoId = pubkey || TestUtils.generateFakePubKeyStr(); + return new MessageModel({ + conversationId: convoId, + source: convoId, + type: 'outgoing', + }); +} + export function generateFakeIncomingOpenGroupMessageV4({ id, reactions,