diff --git a/ts/test/types/Conversation_test.ts b/ts/test/types/Conversation_test.ts new file mode 100644 index 0000000000..f5aea6fc6a --- /dev/null +++ b/ts/test/types/Conversation_test.ts @@ -0,0 +1,102 @@ +import 'mocha'; +import { assert } from 'chai'; + +import * as Conversation from '../../types/Conversation'; +import { + IncomingMessage, + OutgoingMessage, + VerifiedChangeMessage, +} from '../../types/Message'; + +describe('Conversation', () => { + describe('createLastMessageUpdate', () => { + it('should reset last message if conversation has no messages', () => { + const input = { + currentLastMessageText: null, + currentTimestamp: null, + lastMessage: null, + lastMessageNotificationText: null, + }; + const expected = { + lastMessage: '', + timestamp: null, + }; + + const actual = Conversation.createLastMessageUpdate(input); + assert.deepEqual(actual, expected); + }); + + context('for regular message', () => { + it('should update last message text and timestamp', () => { + const input = { + currentLastMessageText: 'Existing message', + currentTimestamp: 555, + lastMessage: { + type: 'outgoing', + conversationId: 'foo', + sent_at: 666, + timestamp: 666, + } as OutgoingMessage, + lastMessageNotificationText: 'New outgoing message', + }; + const expected = { + lastMessage: 'New outgoing message', + timestamp: 666, + }; + + const actual = Conversation.createLastMessageUpdate(input); + assert.deepEqual(actual, expected); + }); + }); + context('for verified change message', () => { + it('should skip update', () => { + const input = { + currentLastMessageText: 'bingo', + currentTimestamp: 555, + lastMessage: { + type: 'verified-change', + conversationId: 'foo', + sent_at: 666, + timestamp: 666, + } as VerifiedChangeMessage, + lastMessageNotificationText: 'Verified Changed', + }; + const expected = { + lastMessage: 'bingo', + timestamp: 555, + }; + + const actual = Conversation.createLastMessageUpdate(input); + assert.deepEqual(actual, expected); + }); + }); + + context('for expired message', () => { + it('should update message but not timestamp (to prevent bump to top)', () => { + const input = { + currentLastMessageText: 'I am expired', + currentTimestamp: 555, + lastMessage: { + type: 'incoming', + conversationId: 'foo', + sent_at: 666, + timestamp: 666, + expirationTimerUpdate: { + expireTimer: 111, + fromSync: false, + source: '+12223334455', + }, + } as IncomingMessage, + lastMessageNotificationText: 'Last message before expired', + }; + const expected = { + lastMessage: 'Last message before expired', + timestamp: 555, + }; + + const actual = Conversation.createLastMessageUpdate(input); + assert.deepEqual(actual, expected); + }); + }); + }); +}); diff --git a/ts/types/Conversation.ts b/ts/types/Conversation.ts new file mode 100644 index 0000000000..e31b3b0a60 --- /dev/null +++ b/ts/types/Conversation.ts @@ -0,0 +1,45 @@ +import is from '@sindresorhus/is'; +import { Message } from './Message'; + + +interface ConversationLastMessageUpdate { + lastMessage: string | null; + timestamp: number | null; +} + +export const createLastMessageUpdate = ({ + currentLastMessageText, + currentTimestamp, + lastMessage, + lastMessageNotificationText, +}: { + currentLastMessageText: string | null, + currentTimestamp: number | null, + lastMessage: Message | null, + lastMessageNotificationText: string | null, +}): ConversationLastMessageUpdate => { + if (lastMessage === null) { + return { + lastMessage: '', + timestamp: null, + }; + } + + const { type } = lastMessage; + const isVerifiedChangeMessage = type === 'verified-change'; + const isExpiringMessage = is.object(lastMessage.expirationTimerUpdate); + const shouldUpdateTimestamp = !isVerifiedChangeMessage && !isExpiringMessage; + + const newTimestamp = shouldUpdateTimestamp ? + lastMessage.sent_at : + currentTimestamp; + + const shouldUpdateLastMessageText = !isVerifiedChangeMessage; + const newLastMessageText = shouldUpdateLastMessageText ? + lastMessageNotificationText : currentLastMessageText; + + return { + lastMessage: newLastMessageText, + timestamp: newTimestamp, + }; +};