From e42be002a6119f7449c2ee0901c9cbf3abfe6122 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Tue, 11 Jun 2024 11:02:03 +1000 Subject: [PATCH] feat: cleaned up accept/block/decline logic --- _locales/en/messages.json | 4 +- .../overlay/OverlayMessageRequest.tsx | 5 +- ts/interactions/conversationInteractions.ts | 6 +++ .../apis/snode_api/SnodeRequestTypes.ts | 11 ++++ ts/session/sending/MessageSender.ts | 54 +++++++++++-------- ts/session/sending/MessageSentHandler.ts | 3 +- ts/session/utils/String.ts | 3 +- ts/types/LocalizerKeys.ts | 1 - 8 files changed, 58 insertions(+), 29 deletions(-) diff --git a/_locales/en/messages.json b/_locales/en/messages.json index e7d08665cf5..745dea77789 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -572,7 +572,7 @@ "messageRequestAcceptedOursNoName": "You have accepted the message request", "declineRequestMessage": "Are you sure you want to decline this message request?", "deleteGroupRequest": "Are you sure you want to delete this group invite?", - "deleteGroupRequestAndBlock": "Are you sure you want to block $name$? Blocked users cannot send you message requests, group invites or call you.", + "deleteGroupRequestAndBlock": "Are you sure you want to block $name$?
Blocked users cannot send you message requests, group invites or call you.", "respondingToRequestWarning": "Sending a message to this user will automatically accept their message request and reveal your Session ID.", "respondingToGroupRequestWarning": "Sending a message to this group will automatically accept the group invite.", "userInvitedYouToGroup": "$name$ invited you to join $groupName$.", @@ -584,7 +584,7 @@ "mustBeApproved": "This conversation must be accepted to use this feature", "youHaveANewFriendRequest": "You have a new friend request", "clearAllConfirmationTitle": "Clear All Message Requests", - "clearAllConfirmationBody": "Are you sure you want to clear all message and group requests?", + "clearAllConfirmationBody": "Are you sure you want to clear all message requests and group invites?", "thereAreNoMessagesIn": "There are no messages in $name$.", "noMessagesInBlindedDisabledMsgRequests": "$name$ has message requests from Community conversations turned off, so you cannot send them a message.", "noMessagesInNoteToSelf": "You have no messages in $name$.", diff --git a/ts/components/leftpane/overlay/OverlayMessageRequest.tsx b/ts/components/leftpane/overlay/OverlayMessageRequest.tsx index 77e2cd33d2b..dd810027fcc 100644 --- a/ts/components/leftpane/overlay/OverlayMessageRequest.tsx +++ b/ts/components/leftpane/overlay/OverlayMessageRequest.tsx @@ -59,7 +59,7 @@ export const OverlayMessageRequest = () => { */ function handleClearAllRequestsClick() { const { i18n } = window; - const title = i18n('clearAllConfirmationTitle'); + const title = i18n('clearAll'); const message = i18n('clearAllConfirmationBody'); const onClose = dispatch(updateConfirmModal(null)); @@ -68,6 +68,9 @@ export const OverlayMessageRequest = () => { title, message, onClose, + okTheme: SessionButtonColor.Danger, + closeTheme: SessionButtonColor.Primary, + okText: window.i18n('clear'), onClickOk: async () => { window?.log?.info('Blocking all message requests'); if (!hasRequests) { diff --git a/ts/interactions/conversationInteractions.ts b/ts/interactions/conversationInteractions.ts index 4f52b41b29d..8dd2d0fdec8 100644 --- a/ts/interactions/conversationInteractions.ts +++ b/ts/interactions/conversationInteractions.ts @@ -197,6 +197,9 @@ export async function declineConversationWithoutConfirm({ window?.log?.info('No conversation to decline.'); return; } + window.log.debug( + `declineConversationWithoutConfirm of ${ed25519Str(conversationId)}, alsoBlock:${alsoBlock}, conversationIdOrigin:${conversationIdOrigin ? ed25519Str(conversationIdOrigin) : ''}` + ); // Note: do not set the active_at undefined as this would make that conversation not synced with the libsession wrapper await conversationToDecline.setIsApproved(false, false); @@ -288,7 +291,10 @@ export const declineConversationWithConfirm = ({ updateConfirmModal({ okText: window.i18n(okKey), cancelText: window.i18n('cancel'), + title: window.i18n(okKey), message, + okTheme: SessionButtonColor.Danger, + closeTheme: SessionButtonColor.Primary, onClickOk: async () => { await declineConversationWithoutConfirm({ conversationId, diff --git a/ts/session/apis/snode_api/SnodeRequestTypes.ts b/ts/session/apis/snode_api/SnodeRequestTypes.ts index 155a5ae7bae..edf978261d9 100644 --- a/ts/session/apis/snode_api/SnodeRequestTypes.ts +++ b/ts/session/apis/snode_api/SnodeRequestTypes.ts @@ -973,24 +973,35 @@ export class StoreUserMessageSubRequest extends SnodeAPISubRequest { public readonly dbMessageIdentifier: string | null; public readonly createdAtNetworkTimestamp: number; + public readonly plainTextBuffer: Uint8Array | null; + constructor( args: WithCreatedAtNetworkTimestamp & { ttlMs: number; encryptedData: Uint8Array; destination: PubkeyType; dbMessageIdentifier: string | null; + /** + * When we send a message to a 1o1 recipient, we then need to send the same message to our own swarm as a synced message. + * To forward that message, we need the original message data, which is the plainTextBuffer field here. + */ + plainTextBuffer: Uint8Array | null; } ) { super(); this.ttlMs = args.ttlMs; this.destination = args.destination; this.encryptedData = args.encryptedData; + this.plainTextBuffer = args.plainTextBuffer; this.dbMessageIdentifier = args.dbMessageIdentifier; this.createdAtNetworkTimestamp = args.createdAtNetworkTimestamp; if (isEmpty(this.encryptedData)) { throw new Error('this.encryptedData cannot be empty'); } + if (this.plainTextBuffer && !this.plainTextBuffer.length) { + throw new Error('this.plainTextBuffer can be either null or non-empty'); + } } public async buildAndSignParameters(): Promise<{ diff --git a/ts/session/sending/MessageSender.ts b/ts/session/sending/MessageSender.ts index b924c1f2dde..8a6daa96461 100644 --- a/ts/session/sending/MessageSender.ts +++ b/ts/session/sending/MessageSender.ts @@ -97,15 +97,29 @@ type PubkeyToRequestType = T extends Pub type StoreRequestsPerPubkey = Array>; +type EncryptedMessageDetails = Pick< + EncryptAndWrapMessageResults, + | 'namespace' + | 'encryptedAndWrappedData' + | 'identifier' + | 'ttl' + | 'networkTimestamp' + | 'plainTextBuffer' +>; + async function messageToRequest05({ destination, - encryptedAndWrapped: { namespace, encryptedAndWrappedData, identifier, ttl, networkTimestamp }, + encryptedAndWrapped: { + namespace, + encryptedAndWrappedData, + identifier, + ttl, + networkTimestamp, + plainTextBuffer, + }, }: { destination: PubkeyType; - encryptedAndWrapped: Pick< - EncryptAndWrapMessageResults, - 'namespace' | 'encryptedAndWrappedData' | 'identifier' | 'ttl' | 'networkTimestamp' - >; + encryptedAndWrapped: EncryptedMessageDetails; }): Promise { const shared05Arguments = { encryptedData: encryptedAndWrappedData, @@ -114,6 +128,7 @@ async function messageToRequest05({ destination, namespace, createdAtNetworkTimestamp: networkTimestamp, + plainTextBuffer, }; if (namespace === SnodeNamespaces.Default || namespace === SnodeNamespaces.LegacyClosedGroup) { return new StoreUserMessageSubRequest(shared05Arguments); @@ -175,10 +190,7 @@ async function messageToRequest({ encryptedAndWrapped, }: { destination: T; - encryptedAndWrapped: Pick< - EncryptAndWrapMessageResults, - 'namespace' | 'encryptedAndWrappedData' | 'identifier' | 'ttl' | 'networkTimestamp' - >; + encryptedAndWrapped: EncryptedMessageDetails; }): Promise> { if (PubKey.is03Pubkey(destination)) { const req = await messageToRequest03({ destination, encryptedAndWrapped }); @@ -200,12 +212,7 @@ async function messagesToRequests({ encryptedAndWrappedArr, }: { destination: T; - encryptedAndWrappedArr: Array< - Pick< - EncryptAndWrapMessageResults, - 'namespace' | 'encryptedAndWrappedData' | 'identifier' | 'ttl' | 'networkTimestamp' - > - >; + encryptedAndWrappedArr: Array; }): Promise>> { const subRequests: Array> = []; for (let index = 0; index < encryptedAndWrappedArr.length; index++) { @@ -260,6 +267,7 @@ async function sendSingleMessage({ // before we return from the await below. // and the isDuplicate messages relies on sent_at timestamp to be valid. const found = await Data.getMessageById(encryptedAndWrapped.identifier); + // make sure to not update the sent timestamp if this a currently syncing message if (found && !found.get('sentSync')) { found.set({ sent_at: encryptedAndWrapped.networkTimestamp }); @@ -497,10 +505,10 @@ type SharedEncryptAndWrap = { ttl: number; identifier: string; isSyncMessage: boolean; + plainTextBuffer: Uint8Array; }; type EncryptAndWrapMessage = { - plainTextBuffer: Uint8Array; destination: string; namespace: number; networkTimestamp: number; @@ -549,6 +557,7 @@ async function encryptForGroupV2( ttl, identifier, isSyncMessage: syncMessage, + plainTextBuffer, }; } @@ -594,6 +603,7 @@ async function encryptMessageAndWrap( ttl, identifier, isSyncMessage: syncMessage, + plainTextBuffer, }; } @@ -847,7 +857,6 @@ async function handleBatchResultWithSubRequests({ window.log.error('handleBatchResultWithSubRequests: invalid batch result '); return; } - const us = UserUtils.getOurPubKeyStrFromCache(); const seenHashes: Array = []; for (let index = 0; index < subRequests.length; index++) { @@ -878,11 +887,7 @@ async function handleBatchResultWithSubRequests({ // We need to store the hash of our synced message when for a 1o1. (as this is the one stored on our swarm) // For groups, we can just store that hash directly as the group's swarm is hosting all of the group messages - - if ( - subRequest.dbMessageIdentifier && - (subRequest.destination === us || isDestinationClosedGroup) - ) { + if (subRequest.dbMessageIdentifier) { // eslint-disable-next-line no-await-in-loop await MessageSentHandler.handleSwarmMessageSentSuccess( { @@ -891,7 +896,10 @@ async function handleBatchResultWithSubRequests({ ? SignalService.Envelope.Type.CLOSED_GROUP_MESSAGE : SignalService.Envelope.Type.SESSION_MESSAGE, identifier: subRequest.dbMessageIdentifier, - plainTextBuffer: null, + plainTextBuffer: + subRequest instanceof StoreUserMessageSubRequest + ? subRequest.plainTextBuffer + : null, }, subRequest.createdAtNetworkTimestamp, storedHash diff --git a/ts/session/sending/MessageSentHandler.ts b/ts/session/sending/MessageSentHandler.ts index d129989a022..7a32804394f 100644 --- a/ts/session/sending/MessageSentHandler.ts +++ b/ts/session/sending/MessageSentHandler.ts @@ -99,7 +99,8 @@ async function handleSwarmMessageSentSuccess( // 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 shouldMarkMessageAsSynced = + isOurDevice && fetchedMessage.get('sentSync') && isClosedGroupMessage; // Handle the sync logic here if (shouldTriggerSyncMessage && sentMessage && sentMessage.plainTextBuffer) { diff --git a/ts/session/utils/String.ts b/ts/session/utils/String.ts index 68713e5e5d0..68576fde6ec 100644 --- a/ts/session/utils/String.ts +++ b/ts/session/utils/String.ts @@ -72,4 +72,5 @@ export const sanitizeSessionUsername = (inputName: string) => { return validChars; }; -export const ed25519Str = (ed25519Key: string) => `(...${ed25519Key.substr(58)})`; +export const ed25519Str = (ed25519Key: string) => + `(...${ed25519Key.length > 58 ? ed25519Key.substr(58) : ed25519Key})`; diff --git a/ts/types/LocalizerKeys.ts b/ts/types/LocalizerKeys.ts index 369aa218ede..57da8ff1e51 100644 --- a/ts/types/LocalizerKeys.ts +++ b/ts/types/LocalizerKeys.ts @@ -71,7 +71,6 @@ export type LocalizerKeys = | 'clear' | 'clearAll' | 'clearAllConfirmationBody' - | 'clearAllConfirmationTitle' | 'clearAllData' | 'clearAllReactions' | 'clearDataSettingsTitle'