diff --git a/lib/hooks/input-state-container-hooks.js b/lib/hooks/input-state-container-hooks.js --- a/lib/hooks/input-state-container-hooks.js +++ b/lib/hooks/input-state-container-hooks.js @@ -69,7 +69,7 @@ : threadInfo.members; const recipientsIDs = recipients.map(recipient => recipient.id); - const messageIDs = await sendComposableDMOperation({ + const result = await sendComposableDMOperation({ type: dmOperationSpecificationTypes.OUTBOUND, op: { type: 'send_text_message', @@ -90,9 +90,9 @@ composableMessageID: localID, }); - if (messageIDs.length > 0) { + if (result.result === 'failure' && result.failedMessageIDs.length > 0) { const e: any = new Error('Failed to send message to all peers'); - e.failedOutboundP2PMessageIDs = messageIDs; + e.failedOutboundP2PMessageIDs = result.failedMessageIDs; throw e; } return { @@ -170,7 +170,7 @@ const messageID = uuid.v4(); const time = Date.now(); - const messageIDs = await sendComposableDMOperation({ + const result = await sendComposableDMOperation({ type: dmOperationSpecificationTypes.OUTBOUND, op: { type: 'send_multimedia_message', @@ -192,9 +192,9 @@ composableMessageID: localID, }); - if (messageIDs.length > 0) { + if (result.result === 'failure' && result.failedMessageIDs.length > 0) { const e: any = new Error('Failed to send message to all peers'); - e.failedOutboundP2PMessageIDs = messageIDs; + e.failedOutboundP2PMessageIDs = result.failedMessageIDs; throw e; } return { 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 @@ -20,7 +20,10 @@ import { useDispatchWithMetadata } from '../../hooks/ops-hooks.js'; import { mergeUpdatesWithMessageInfos } from '../../reducers/message-reducer.js'; import { getAllPeerUserIDAndDeviceIDs } from '../../selectors/user-selectors.js'; -import { usePeerToPeerCommunication } from '../../tunnelbroker/peer-to-peer-context.js'; +import { + usePeerToPeerCommunication, + type ProcessOutboundP2PMessagesResult, +} from '../../tunnelbroker/peer-to-peer-context.js'; import { processDMOpsActionType, queueDMOpsActionType, @@ -322,7 +325,7 @@ function useSendComposableDMOperation(): ( dmOperationSpecification: OutboundComposableDMOperationSpecification, -) => Promise<$ReadOnlyArray> { +) => Promise { const threadInfos = useSelector(state => state.threadStore.threadInfos); const { getDMOpsSendingPromise } = usePeerToPeerCommunication(); const dispatchWithMetadata = useDispatchWithMetadata(); @@ -333,7 +336,7 @@ return React.useCallback( async ( dmOperationSpecification: OutboundComposableDMOperationSpecification, - ): Promise<$ReadOnlyArray> => { + ): Promise => { const { promise, dmOpID } = getDMOpsSendingPromise(); const { op, composableMessageID, recipients } = dmOperationSpecification; diff --git a/lib/tunnelbroker/peer-to-peer-context.js b/lib/tunnelbroker/peer-to-peer-context.js --- a/lib/tunnelbroker/peer-to-peer-context.js +++ b/lib/tunnelbroker/peer-to-peer-context.js @@ -36,7 +36,7 @@ notificationsCreationData: ?NotificationsCreationData, ) => void, +getDMOpsSendingPromise: () => { - +promise: Promise<$ReadOnlyArray>, + +promise: Promise, +dmOpID: string, }, +broadcastEphemeralMessage: ( @@ -53,6 +53,13 @@ +children: React.Node, }; +export type ProcessOutboundP2PMessagesResult = + | { +result: 'success' } + | { + +result: 'failure', + +failedMessageIDs: $ReadOnlyArray, + }; + async function processOutboundP2PMessages( sendMessage: ( message: TunnelbrokerClientMessageToDevice, @@ -61,19 +68,25 @@ identityContext: IdentityClientContextType, peerOlmSessionsCreator: (userID: string, deviceID: string) => Promise, messageIDs: ?$ReadOnlyArray, -): Promise<$ReadOnlyArray> { +): Promise { let authMetadata; try { authMetadata = await identityContext.getAuthMetadata(); } catch (e) { - return []; + return { + result: 'failure', + failedMessageIDs: messageIDs ?? [], + }; } if ( !authMetadata.deviceID || !authMetadata.userID || !authMetadata.accessToken ) { - return []; + return { + result: 'failure', + failedMessageIDs: messageIDs ?? [], + }; } const { olmAPI, sqliteAPI } = getConfig(); @@ -194,9 +207,19 @@ ); await Promise.all(devicePromises); - // Returning messageIDs of failed messages. - const sentMessages = new Set(Object.keys(sentMessagesMap)); - return messageIDs?.filter(id => !sentMessages.has(id)) ?? []; + + const sentMessagesSet = new Set(Object.keys(sentMessagesMap)); + const failedMessageIDs = + messageIDs?.filter(id => !sentMessagesSet.has(id)) ?? []; + if (failedMessageIDs.length > 0) { + return { + result: 'failure', + failedMessageIDs, + }; + } + return { + result: 'success', + }; } const AUTOMATIC_RETRY_FREQUENCY = 30 * 1000; @@ -212,7 +235,7 @@ Map< string, { - +resolve: (messageIDs: $ReadOnlyArray) => mixed, + +resolve: (result: ProcessOutboundP2PMessagesResult) => mixed, +reject: Error => mixed, }, >, @@ -223,9 +246,11 @@ // connection) it will still resolve but with an empty array. const getDMOpsSendingPromise = React.useCallback(() => { const dmOpID = uuid.v4(); - const promise = new Promise<$ReadOnlyArray>((resolve, reject) => { - dmOpsSendingPromiseResolvers.current.set(dmOpID, { resolve, reject }); - }); + const promise = new Promise( + (resolve, reject) => { + dmOpsSendingPromiseResolvers.current.set(dmOpID, { resolve, reject }); + }, + ); return { promise, dmOpID }; }, []); @@ -260,7 +285,7 @@ do { const queueFront = processingQueue.current.shift(); try { - const [sentMessagesIDs] = await Promise.all([ + const [result] = await Promise.all([ processOutboundP2PMessages( sendMessageToDevice, identityContext, @@ -272,7 +297,7 @@ if (queueFront.dmOpID) { dmOpsSendingPromiseResolvers.current .get(queueFront.dmOpID) - ?.resolve?.(sentMessagesIDs); + ?.resolve?.(result); } } catch (e) { console.log(