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 @@ -38,7 +38,9 @@ outboundMessageIDs: ?$ReadOnlyArray, dmOpID: ?string, ) => void, - +sendDMOperation: (op: OutboundDMOperationSpecification) => Promise, + +sendDMOperation: ( + op: OutboundDMOperationSpecification, + ) => Promise<$ReadOnlyArray>, +broadcastEphemeralMessage: ( contentPayload: string, recipients: $ReadOnlyArray<{ +userID: string, +deviceID: string }>, @@ -61,14 +63,19 @@ identityContext: IdentityClientContextType, peerOlmSessionsCreator: (userID: string, deviceID: string) => Promise, messageIDs: ?$ReadOnlyArray, -): Promise { - const authMetadata = await identityContext.getAuthMetadata(); +): Promise<$ReadOnlyArray> { + let authMetadata; + try { + authMetadata = await identityContext.getAuthMetadata(); + } catch (e) { + return []; + } if ( !authMetadata.deviceID || !authMetadata.userID || !authMetadata.accessToken ) { - return; + return []; } const { olmAPI, sqliteAPI } = getConfig(); @@ -90,31 +97,38 @@ } } + const sentMessagesMap: { [messageID: string]: boolean } = {}; + const sendMessageToPeer = async ( message: OutboundP2PMessage, ): Promise => { if (!authMetadata.deviceID || !authMetadata.userID) { return; } - const encryptedMessage: EncryptedMessage = { - type: peerToPeerMessageTypes.ENCRYPTED_MESSAGE, - senderInfo: { - deviceID: authMetadata.deviceID, - userID: authMetadata.userID, - }, - encryptedData: JSON.parse(message.ciphertext), - }; - await sendMessage( - { - deviceID: message.deviceID, - payload: JSON.stringify(encryptedMessage), - }, - message.messageID, - ); - await sqliteAPI.markOutboundP2PMessageAsSent( - message.messageID, - message.deviceID, - ); + try { + const encryptedMessage: EncryptedMessage = { + type: peerToPeerMessageTypes.ENCRYPTED_MESSAGE, + senderInfo: { + deviceID: authMetadata.deviceID, + userID: authMetadata.userID, + }, + encryptedData: JSON.parse(message.ciphertext), + }; + await sendMessage( + { + deviceID: message.deviceID, + payload: JSON.stringify(encryptedMessage), + }, + message.messageID, + ); + await sqliteAPI.markOutboundP2PMessageAsSent( + message.messageID, + message.deviceID, + ); + sentMessagesMap[message.messageID] = true; + } catch (e) { + console.error(e); + } }; for (const peerDeviceID in devicesMap) { @@ -157,9 +171,14 @@ } } else if (message.status === outboundP2PMessageStatuses.encrypted) { await sendMessageToPeer(message); + } else if (message.status === outboundP2PMessageStatuses.sent) { + // Handle edge-case when message was sent, but it wasn't updated + // in the message store. + sentMessagesMap[message.messageID] = true; } } } + return Object.keys(sentMessagesMap); } const AUTOMATIC_RETRY_FREQUENCY = 30 * 1000; @@ -173,15 +192,24 @@ const dispatch = useDispatch(); const dmOpsSendingPromiseResolvers = React.useRef< - Map mixed, +reject: Error => mixed }>, + Map< + string, + { + +resolve: ($ReadOnlyArray) => mixed, + +reject: Error => mixed, + }, + >, >(new Map()); const allPeerUserIDAndDeviceIDs = useSelector(getAllPeerUserIDAndDeviceIDs); const currentUserInfo = useSelector(state => state.currentUserInfo); + // This returns a promise that will be resolved with arrays of successfully + // sent messages, so in case of failing all messages (e.g. no internet + // connection) it will still resolve but with an empty array. const sendDMOperation = React.useCallback( async (op: OutboundDMOperationSpecification) => { const dmOpID = uuid.v4(); - const promise = new Promise((resolve, reject) => { + const promise = new Promise<$ReadOnlyArray>((resolve, reject) => { dmOpsSendingPromiseResolvers.current.set(dmOpID, { resolve, reject }); }); @@ -223,7 +251,7 @@ do { const queueFront = processingQueue.current.shift(); try { - await processOutboundP2PMessages( + const sentMessagesIDs = await processOutboundP2PMessages( sendMessageToDevice, identityContext, peerOlmSessionsCreator, @@ -232,7 +260,7 @@ if (queueFront.dmOpID) { dmOpsSendingPromiseResolvers.current .get(queueFront.dmOpID) - ?.resolve?.(); + ?.resolve?.(sentMessagesIDs); } } catch (e) { console.log(