From 255b6225c916db80e0275ac47786d70deb1c4809 Mon Sep 17 00:00:00 2001 From: William Grant Date: Mon, 3 Apr 2023 14:09:05 +0200 Subject: [PATCH] feat: block sending disappearing messages of the wrong type in different conversations, improved disappear after send or read logic, disable legacy sending for now --- ts/models/conversation.ts | 34 ++++++++++++++++--- ts/receiver/contentMessage.ts | 1 + .../messages/outgoing/ExpirableMessage.ts | 12 +++++++ .../ExpirationTimerUpdateMessage.ts | 8 ++--- .../outgoing/visibleMessage/VisibleMessage.ts | 20 +++-------- ts/session/sending/MessageSender.ts | 1 + ts/session/sending/MessageSentHandler.ts | 16 ++++++--- 7 files changed, 64 insertions(+), 28 deletions(-) diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index f91c711b7..f25da3254 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -96,7 +96,10 @@ import { import { sogsV3FetchPreviewAndSaveIt } from '../session/apis/open_group_api/sogsv3/sogsV3FetchFile'; import { Reaction } from '../types/Reaction'; import { Reactions } from '../util/reactions'; -import { DisappearingMessageConversationType } from '../util/expiringMessages'; +import { + DisappearingMessageConversationType, + DisappearingMessageType, +} from '../util/expiringMessages'; export class ConversationModel extends Backbone.Model { public updateLastMessage: () => any; @@ -558,12 +561,14 @@ export class ConversationModel extends Backbone.Model { if (this.isPublic() && !this.isOpenGroupV2()) { throw new Error('Only opengroupv2 are supported now'); } + // an OpenGroupV2 message is just a visible message const chatMessageParams: VisibleMessageParams = { body, identifier: id, timestamp: sentAt, attachments, + // TODO not supported in open groups expirationType, expireTimer, preview: preview ? [preview] : [], @@ -617,10 +622,11 @@ export class ConversationModel extends Backbone.Model { const destinationPubkey = new PubKey(destination); - // TODO check expiration types per different conversation setting - if (this.isPrivate()) { if (this.isMe()) { + if (!this.isDisappearingMode('deleteAfterSend')) { + return; + } chatMessageParams.syncTarget = this.id; const chatMessageMe = new VisibleMessage(chatMessageParams); @@ -639,17 +645,21 @@ export class ConversationModel extends Backbone.Model { expirationType, expireTimer, }); + // we need the return await so that errors are caught in the catch {} await getMessageQueue().sendToPubKey(destinationPubkey, groupInviteMessage); return; } - const chatMessagePrivate = new VisibleMessage(chatMessageParams); + const chatMessagePrivate = new VisibleMessage(chatMessageParams); await getMessageQueue().sendToPubKey(destinationPubkey, chatMessagePrivate); return; } if (this.isMediumGroup()) { + if (!this.isDisappearingMode('deleteAfterSend')) { + return; + } const chatMessageMediumGroup = new VisibleMessage(chatMessageParams); const closedGroupVisibleMessage = new ClosedGroupVisibleMessage({ chatMessage: chatMessageMediumGroup, @@ -2216,6 +2226,22 @@ export class ConversationModel extends Backbone.Model { return []; } + + private isDisappearingMode(mode: DisappearingMessageType) { + // TODO support legacy mode + const success = + this.get('expirationType') && + this.get('expirationType') !== 'off' && + mode === 'deleteAfterRead' + ? this.get('expirationType') === 'deleteAfterRead' + : this.get('expirationType') === 'deleteAfterSend'; + + if (!success) { + window.log.info(`WIP: This message should be disappear after ${mode}`, this); + } + + return success; + } } const throttledAllConversationsDispatch = debounce( diff --git a/ts/receiver/contentMessage.ts b/ts/receiver/contentMessage.ts index 00e2ca415..6083e10b4 100644 --- a/ts/receiver/contentMessage.ts +++ b/ts/receiver/contentMessage.ts @@ -789,6 +789,7 @@ export async function handleDataExtractionNotification( unread: 1, // 1 means unread expirationType: expirationType !== 'off' ? expirationType : undefined, expireTimer: convo.get('expireTimer') ? convo.get('expireTimer') : 0, + // TODO should this only be for delete after send? expirationStartTimestamp: setExpirationStartTimestamp(expirationType), }); convo.updateLastMessage(); diff --git a/ts/session/messages/outgoing/ExpirableMessage.ts b/ts/session/messages/outgoing/ExpirableMessage.ts index c79f404b7..cb9cb32ee 100644 --- a/ts/session/messages/outgoing/ExpirableMessage.ts +++ b/ts/session/messages/outgoing/ExpirableMessage.ts @@ -1,5 +1,6 @@ import { SignalService } from '../../../protobuf'; import { DisappearingMessageType } from '../../../util/expiringMessages'; +import { DURATION, TTL_DEFAULT } from '../../constants'; import { ContentMessage } from './ContentMessage'; import { MessageParams } from './Message'; @@ -31,4 +32,15 @@ export class ExpirableMessage extends ContentMessage { public getDisappearingMessageType(): DisappearingMessageType | undefined { return this.expirationType; } + + public ttl(): number { + switch (this.expirationType) { + case 'deleteAfterSend': + return this.expireTimer ? this.expireTimer * DURATION.SECONDS : TTL_DEFAULT.TTL_MAX; + case 'deleteAfterRead': + return TTL_DEFAULT.TTL_MAX; + default: + return TTL_DEFAULT.TTL_MAX; + } + } } diff --git a/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts b/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts index f4be6c8f3..ddcf3d3ab 100644 --- a/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts +++ b/ts/session/messages/outgoing/controlMessage/ExpirationTimerUpdateMessage.ts @@ -64,10 +64,10 @@ export class ExpirationTimerUpdateMessage extends DataMessage { data.syncTarget = this.syncTarget; } - // TODO Legacy support remove 2 weeks after the release - if (this.expireTimer) { - data.expireTimer = this.expireTimer; - } + // TODO should only happen in legacy mode and should be cancelled out once we have trigger the unix timestamp + // if (this.expireTimer) { + // data.expireTimer = this.expireTimer; + // } return data; } diff --git a/ts/session/messages/outgoing/visibleMessage/VisibleMessage.ts b/ts/session/messages/outgoing/visibleMessage/VisibleMessage.ts index 58758527f..5c21bd61e 100644 --- a/ts/session/messages/outgoing/visibleMessage/VisibleMessage.ts +++ b/ts/session/messages/outgoing/visibleMessage/VisibleMessage.ts @@ -3,7 +3,6 @@ import { isEmpty } from 'lodash'; import { SignalService } from '../../../../protobuf'; import { LokiProfile } from '../../../../types/Message'; import { Reaction } from '../../../../types/Reaction'; -import { DURATION, TTL_DEFAULT } from '../../../constants'; import { ExpirableMessage, ExpirableMessageParams } from '../ExpirableMessage'; interface AttachmentPointerCommon { @@ -123,9 +122,10 @@ export class VisibleMessage extends ExpirableMessage { dataMessage.attachments = this.attachments || []; - if (this.expireTimer) { - dataMessage.expireTimer = this.expireTimer; - } + // TODO should only happen in legacy mode and should be cancelled out once we have trigger the unix timestamp + // if (this.expireTimer) { + // dataMessage.expireTimer = this.expireTimer; + // } if (this.preview) { dataMessage.preview = this.preview; @@ -192,18 +192,6 @@ export class VisibleMessage extends ExpirableMessage { public isEqual(comparator: VisibleMessage): boolean { return this.identifier === comparator.identifier && this.timestamp === comparator.timestamp; } - - // TODO should this be on the Expirable message? Probably - public ttl(): number { - switch (this.expirationType) { - case 'deleteAfterSend': - return this.expireTimer ? this.expireTimer * DURATION.SECONDS : TTL_DEFAULT.TTL_MAX; - case 'deleteAfterRead': - return TTL_DEFAULT.TTL_MAX; - default: - return TTL_DEFAULT.TTL_MAX; - } - } } export function buildProfileForOutgoingMessage(params: { lokiProfile?: LokiProfile }) { diff --git a/ts/session/sending/MessageSender.ts b/ts/session/sending/MessageSender.ts index 6022704c1..4e4a803ee 100644 --- a/ts/session/sending/MessageSender.ts +++ b/ts/session/sending/MessageSender.ts @@ -111,6 +111,7 @@ export async function send( found.set({ sent_at: networkTimestamp }); await found.commit(); } + await MessageSender.sendMessageToSnode( recipient.key, data, diff --git a/ts/session/sending/MessageSentHandler.ts b/ts/session/sending/MessageSentHandler.ts index 1156f1ce6..5f61b4e89 100644 --- a/ts/session/sending/MessageSentHandler.ts +++ b/ts/session/sending/MessageSentHandler.ts @@ -71,7 +71,7 @@ async function handleMessageSentSuccess( !isClosedGroupMessage && !fetchedMessage.get('synced') && !fetchedMessage.get('sentSync') && - // TODO not 100% sure about this. Might need to change for synced expiries + // TODO not 100% on this. Handling syncing later !fetchedMessage.get('expirationType'); // A message is synced if we triggered a sync message (sentSync) @@ -132,15 +132,21 @@ async function handleMessageSentSuccess( sent_to: sentTo, sent: true, sent_at: effectiveTimestamp, + // TODO message status overrides this for some reason in the UI, message still disappears though + unread: fetchedMessage.get('expirationType') === 'deleteAfterRead' ? 1 : 0, }); - if (!shouldMarkMessageAsSynced) { + if ( + fetchedMessage.get('expirationType') === 'deleteAfterSend' && + Boolean(fetchedMessage.get('expirationStartTimestamp')) === false + ) { const expirationType = fetchedMessage.get('expirationType'); - if (expirationType && Boolean(fetchedMessage.get('expirationStartTimestamp')) === false) { + if (expirationType === 'deleteAfterSend') { + // TODO message timer start is a few seconds less than the amount due to it's position in the pipeline, not sure on a fix yet fetchedMessage.set({ expirationStartTimestamp: setExpirationStartTimestamp( expirationType, - expirationType === 'deleteAfterSend' ? effectiveTimestamp : undefined + fetchedMessage.get('sent_at') ), }); } @@ -179,6 +185,8 @@ async function handleMessageSentFailure( sent: true, }); + // We don't set the expirationStartTimestamp on a disappearing message here incase the user wishes to try and resend the message + await fetchedMessage.commit(); await fetchedMessage.getConversation()?.updateLastMessage(); }