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 { @@ -1813,7 +1814,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 }; } @@ -1824,12 +1830,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 @@ -161,6 +161,15 @@ }); } + let messageIDWithoutAutoRetry: ?string = null; + if ( + dmOperationSpecification.type === + dmOperationSpecificationTypes.OUTBOUND && + !dmOpSpecs[dmOperationSpecification.op.type].supportsAutoRetry + ) { + messageIDWithoutAutoRetry = dmOperationSpecification.op.messageID; + } + dispatchWithMetadata( { type: processDMOpsActionType, @@ -168,6 +177,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';