diff --git a/lib/reducers/message-reducer.js b/lib/reducers/message-reducer.js --- a/lib/reducers/message-reducer.js +++ b/lib/reducers/message-reducer.js @@ -59,6 +59,7 @@ messageStoreOpsHandlers, type MessageStoreOperation, type ReplaceMessageOperation, + type ReplaceMessageStoreLocalMessageInfoOperation, } from '../ops/message-store-ops.js'; import { pendingToRealizedThreadIDsSelector } from '../selectors/thread-selectors.js'; import { @@ -1801,7 +1802,12 @@ messageStore: processedMessageStore, }; } else if (action.type === processDMOpsActionType) { - const { rawMessageInfos, updateInfos } = action.payload; + const { + rawMessageInfos, + updateInfos, + outboundP2PMessages, + messageIDWithoutAutoRetry, + } = action.payload; if (rawMessageInfos.length === 0 && updateInfos.length === 0) { return { messageStoreOperations: [], messageStore }; } @@ -1812,12 +1818,44 @@ {}, ); - return mergeNewMessages( - messageStore, - messagesResult.rawMessageInfos, - messagesResult.truncationStatuses, - newThreadInfos, - ); + const { messageStoreOperations, messageStore: newMessageStore } = + mergeNewMessages( + messageStore, + messagesResult.rawMessageInfos, + messagesResult.truncationStatuses, + newThreadInfos, + ); + + if ( + !messageIDWithoutAutoRetry || + !outboundP2PMessages || + outboundP2PMessages.length === 0 + ) { + return { messageStoreOperations, messageStore: newMessageStore }; + } + + const newMessageID: string = messageIDWithoutAutoRetry; + + // Messages to other peers that can be retried from UI, + // we need to track statuses of each one. + const outboundP2PMessageIDs = outboundP2PMessages.map(msg => msg.messageID); + const localOperation: ReplaceMessageStoreLocalMessageInfoOperation = { + type: 'replace_local_message_info', + payload: { + id: newMessageID, + localMessageInfo: { + sendFailed: false, + outboundP2PMessageIDs, + }, + }, + }; + + return { + messageStoreOperations: [...messageStoreOperations, localOperation], + messageStore: processMessageStoreOperations(newMessageStore, [ + localOperation, + ]), + }; } return { messageStoreOperations: [], messageStore }; } diff --git a/lib/shared/dm-ops/process-dm-ops.js b/lib/shared/dm-ops/process-dm-ops.js --- a/lib/shared/dm-ops/process-dm-ops.js +++ b/lib/shared/dm-ops/process-dm-ops.js @@ -19,6 +19,7 @@ import { mergeUpdatesWithMessageInfos } from '../../reducers/message-reducer.js'; import { getAllPeerUserIDAndDeviceIDs } from '../../selectors/user-selectors.js'; import { + dmOperationTypes, processDMOpsActionType, queueDMOpsActionType, } from '../../types/dm-ops.js'; @@ -161,6 +162,16 @@ }); } + let messageIDWithoutAutoRetry: ?string = null; + if ( + dmOperationSpecification.type === + dmOperationSpecificationTypes.OUTBOUND && + !dmOperationSpecification.supportsAutoRetry && + dmOperationSpecification.op.type === dmOperationTypes.SEND_TEXT_MESSAGE + ) { + messageIDWithoutAutoRetry = dmOperationSpecification.op.messageID; + } + dispatchWithMetadata( { type: processDMOpsActionType, @@ -168,6 +179,7 @@ rawMessageInfos, updateInfos, outboundP2PMessages, + messageIDWithoutAutoRetry, }, }, metadata, diff --git a/lib/types/dm-ops.js b/lib/types/dm-ops.js --- a/lib/types/dm-ops.js +++ b/lib/types/dm-ops.js @@ -352,6 +352,10 @@ +rawMessageInfos: $ReadOnlyArray, +updateInfos: $ReadOnlyArray, +outboundP2PMessages: ?$ReadOnlyArray, + // For messages that could be retried from UI, we need to bind DM `messageID` + // with `outboundP2PMessages` to keep track of whether all P2P messages + // were queued on Tunnelbroker. + +messageIDWithoutAutoRetry: ?string, }; export const queueDMOpsActionType = 'QUEUE_DM_OPS';