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 @@ -80,7 +80,7 @@ import { unshimMessageInfos } from '../shared/unshim-utils.js'; import { updateSpecs } from '../shared/updates/update-specs.js'; import { recoveryFromReduxActionSources } from '../types/account-types.js'; -import { processDMOpsActionType, sendDMActionTypes } from '../types/dm-ops.js'; +import { processDMOpsActionType } from '../types/dm-ops.js'; import type { Media, Image } from '../types/media-types.js'; import { messageTypes } from '../types/message-types-enum.js'; import { @@ -1166,16 +1166,24 @@ action.type === sendTextMessageActionTypes.failed || action.type === sendMultimediaMessageActionTypes.failed ) { - const { localID } = action.payload; + const { localID, failedOutboundMessageIDs } = action.payload; + let newLocalMessageInfo = { + sendFailed: true, + }; + // Handling DMs + if (failedOutboundMessageIDs && failedOutboundMessageIDs.length > 0) { + newLocalMessageInfo = { + ...newLocalMessageInfo, + outboundP2PMessageIDs: failedOutboundMessageIDs, + }; + } const messageStoreOperations = [ { type: 'replace_local_message_info', payload: { id: localID, - localMessageInfo: { - sendFailed: true, - }, + localMessageInfo: newLocalMessageInfo, }, }, ]; @@ -1865,10 +1873,47 @@ outboundP2PMessages, messageIDWithoutAutoRetry, } = action.payload; - if (rawMessageInfos.length === 0 && updateInfos.length === 0) { + + if ( + rawMessageInfos.length === 0 && + updateInfos.length === 0 && + !messageIDWithoutAutoRetry + ) { return { messageStoreOperations: [], messageStore }; } + if ( + messageIDWithoutAutoRetry && + outboundP2PMessages && + outboundP2PMessages.length > 0 && + !messageStore.local[messageIDWithoutAutoRetry] + ) { + 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: [localOperation], + messageStore: processMessageStoreOperations(messageStore, [ + localOperation, + ]), + }; + } + const messagesResult = mergeUpdatesWithMessageInfos( rawMessageInfos, updateInfos, @@ -1883,93 +1928,13 @@ 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, - ]), - }; - } else if (action.type === sendDMActionTypes.success) { - const { messageID: sentMessageID, outboundP2PMessageIDs } = action.payload; - - const outboundP2PMessageIDsSet = new Set(outboundP2PMessageIDs); - const localOutboundP2PMessageIDs = - messageStore.local[sentMessageID]?.outboundP2PMessageIDs ?? []; - const remainingOutboundP2PMessageIDs = localOutboundP2PMessageIDs.filter( - id => !outboundP2PMessageIDsSet.has(id), - ); - - const messageStoreOperations: Array = []; - if (remainingOutboundP2PMessageIDs.length > 0) { - messageStoreOperations.push({ - type: 'replace_local_message_info', - payload: { - id: sentMessageID, - localMessageInfo: { - sendFailed: true, - outboundP2PMessageIDs: remainingOutboundP2PMessageIDs, - }, - }, - }); - } else { - messageStoreOperations.push({ - type: 'remove_local_message_infos', - payload: { - ids: [sentMessageID], - }, - }); - } - return { messageStoreOperations, messageStore: processMessageStoreOperations( - messageStore, + newMessageStore, messageStoreOperations, ), }; - } else if (action.type === sendDMActionTypes.started) { - const { messageID: sentMessageID } = action.payload; - const localOperation: ReplaceMessageStoreLocalMessageInfoOperation = { - type: 'replace_local_message_info', - payload: { - id: sentMessageID, - localMessageInfo: { - ...messageStore.local[sentMessageID], - sendFailed: false, - }, - }, - }; - - return { - messageStoreOperations: [localOperation], - messageStore: processMessageStoreOperations(messageStore, [ - 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 @@ -24,18 +24,14 @@ import { processDMOpsActionType, queueDMOpsActionType, - sendDMActionTypes, - type SendDMOpsSuccessPayload, + dmOperationValidator, } from '../../types/dm-ops.js'; -import { dmOperationValidator } from '../../types/dm-ops.js'; -import type { LocalMessageInfo } from '../../types/message-types.js'; import type { RawThreadInfo } from '../../types/minimally-encoded-thread-permissions-types.js'; import type { DispatchMetadata } from '../../types/redux-types.js'; import type { OutboundP2PMessage } from '../../types/sqlite-types.js'; import type { LegacyRawThreadInfo } from '../../types/thread-types.js'; import { updateTypes } from '../../types/update-types-enum.js'; import { extractUserIDsFromPayload } from '../../utils/conversion-utils.js'; -import { useDispatchActionPromise } from '../../utils/redux-promise-utils.js'; import { useSelector, useDispatch } from '../../utils/redux-utils.js'; import { messageSpecs } from '../messages/message-specs.js'; import { updateSpecs } from '../updates/update-specs.js'; @@ -43,6 +39,7 @@ function useProcessDMOperation(): ( dmOperationSpecification: DMOperationSpecification, dmOpID: ?string, + localMessageID: ?string, ) => Promise { const fetchMessage = useGetLatestMessageEdit(); const threadInfos = useSelector(state => state.threadStore.threadInfos); @@ -67,6 +64,7 @@ async ( dmOperationSpecification: DMOperationSpecification, dmOpID: ?string, + localMessageID: ?string, ) => { if (!viewerID) { console.log('ignored DMOperation because logged out'); @@ -102,15 +100,6 @@ dispatchMetadata = dmOperationSpecification.metadata; } - let messageIDWithoutAutoRetry: ?string = null; - if ( - dmOperationSpecification.type === - dmOperationSpecificationTypes.OUTBOUND && - !dmOpSpecs[dmOp.type].supportsAutoRetry - ) { - messageIDWithoutAutoRetry = dmOp.messageID; - } - if ( dmOperationSpecification.type === dmOperationSpecificationTypes.OUTBOUND && @@ -127,7 +116,8 @@ rawMessageInfos: [], updateInfos: [], outboundP2PMessages, - messageIDWithoutAutoRetry, + //TODO rename to something descriptive + messageIDWithoutAutoRetry: localMessageID, notificationsCreationData, }, }, @@ -285,7 +275,8 @@ rawMessageInfos, updateInfos, outboundP2PMessages, - messageIDWithoutAutoRetry, + //TODO rename to something descriptive + messageIDWithoutAutoRetry: localMessageID, notificationsCreationData, }, }, @@ -306,76 +297,26 @@ function useProcessAndSendDMOperation(): ( dmOperationSpecification: OutboundDMOperationSpecification, -) => Promise { + localMessageID?: string, +) => Promise<$ReadOnlyArray> { const processDMOps = useProcessDMOperation(); - const dispatchActionPromise = useDispatchActionPromise(); const { getDMOpsSendingPromise } = usePeerToPeerCommunication(); return React.useCallback( - async (dmOperationSpecification: OutboundDMOperationSpecification) => { - const { promise, dmOpID } = getDMOpsSendingPromise(); - await processDMOps(dmOperationSpecification, dmOpID); - - if ( - dmOperationSpecification.type === - dmOperationSpecificationTypes.OUTBOUND && - !dmOpSpecs[dmOperationSpecification.op.type].supportsAutoRetry && - dmOperationSpecification.op.messageID - ) { - const messageID: string = dmOperationSpecification.op.messageID; - - const sendingPromise: Promise = (async () => { - const outboundP2PMessageIDs = await promise; - return { - messageID, - outboundP2PMessageIDs, - }; - })(); - - void dispatchActionPromise( - sendDMActionTypes, - sendingPromise, - undefined, - { - messageID, - }, - ); - } - }, - [dispatchActionPromise, getDMOpsSendingPromise, processDMOps], - ); -} - -function useRetrySendDMOperation(): ( - messageID: string, - localMessageInfo: LocalMessageInfo, -) => Promise { - const { processOutboundMessages, getDMOpsSendingPromise } = - usePeerToPeerCommunication(); - const dispatchActionPromise = useDispatchActionPromise(); - - return React.useCallback( - async (messageID: string, localMessageInfo: LocalMessageInfo) => { + async ( + dmOperationSpecification: OutboundDMOperationSpecification, + localMessageID?: string, + ) => { + // Creates promise that will be resolved when + // Tunnelbroker will queue messages const { promise, dmOpID } = getDMOpsSendingPromise(); - processOutboundMessages(localMessageInfo.outboundP2PMessageIDs, dmOpID); - - const sendingPromise: Promise = (async () => { - const outboundP2PMessageIDs = await promise; - return { - messageID, - outboundP2PMessageIDs, - }; - })(); - void dispatchActionPromise(sendDMActionTypes, sendingPromise, undefined, { - messageID, - }); + // Processing DM Ops and generating messages to peers + await processDMOps(dmOperationSpecification, dmOpID, localMessageID); + // Returning promise, resolved after queuing messages + return promise; }, - [dispatchActionPromise, getDMOpsSendingPromise, processOutboundMessages], + [getDMOpsSendingPromise, processDMOps], ); } -export { - useProcessDMOperation, - useProcessAndSendDMOperation, - useRetrySendDMOperation, -}; +export { useProcessDMOperation, useProcessAndSendDMOperation }; 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 @@ -194,7 +194,9 @@ ); await Promise.all(devicePromises); - return Object.keys(sentMessagesMap); + // now we need to return failed messages + const sentMessages = new Set(Object.keys(sentMessagesMap)); + return messageIDs?.filter(id => !sentMessages.has(id)) ?? []; } const AUTOMATIC_RETRY_FREQUENCY = 30 * 1000; 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 @@ -442,18 +442,3 @@ }>, }, }; - -export type SendDMStartedPayload = { - +messageID: string, -}; - -export type SendDMOpsSuccessPayload = { - +messageID: string, - +outboundP2PMessageIDs: $ReadOnlyArray, -}; - -export const sendDMActionTypes = Object.freeze({ - started: 'SEND_DM_STARTED', - success: 'SEND_DM_SUCCESS', - failed: 'SEND_DM_FAILED', -}); diff --git a/lib/types/redux-types.js b/lib/types/redux-types.js --- a/lib/types/redux-types.js +++ b/lib/types/redux-types.js @@ -47,8 +47,6 @@ QueueDMOpsPayload, PruneDMOpsQueuePayload, ClearQueuedThreadDMOpsPayload, - SendDMStartedPayload, - SendDMOpsSuccessPayload, } from './dm-ops.js'; import type { DraftStore } from './draft-types.js'; import type { EnabledApps, SupportedApps } from './enabled-apps.js'; @@ -724,6 +722,7 @@ +payload: Error & { +localID: string, +threadID: string, + +failedOutboundMessageIDs?: $ReadOnlyArray, }, +loadingInfo?: LoadingInfo, } @@ -743,6 +742,7 @@ +payload: Error & { +localID: string, +threadID: string, + +failedOutboundMessageIDs?: $ReadOnlyArray, }, +loadingInfo?: LoadingInfo, } @@ -1580,22 +1580,6 @@ +type: 'PROCESS_DM_OPS', +payload: ProcessDMOpsPayload, } - | { - +type: 'SEND_DM_STARTED', - +payload: SendDMStartedPayload, - +loadingInfo: LoadingInfo, - } - | { - +type: 'SEND_DM_FAILED', - +error: true, - +payload: Error, - +loadingInfo: LoadingInfo, - } - | { - +type: 'SEND_DM_SUCCESS', - +payload: SendDMOpsSuccessPayload, - +loadingInfo: LoadingInfo, - } | { +type: 'INVALIDATE_TUNNELBROKER_DEVICE_TOKEN', +payload: { diff --git a/native/chat/failed-send.react.js b/native/chat/failed-send.react.js --- a/native/chat/failed-send.react.js +++ b/native/chat/failed-send.react.js @@ -5,16 +5,13 @@ import { Text, View } from 'react-native'; import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; -import { useRetrySendDMOperation } from 'lib/shared/dm-ops/process-dm-ops.js'; import { messageID } from 'lib/shared/message-utils.js'; import { messageTypes } from 'lib/types/message-types-enum.js'; import { type RawComposableMessageInfo, assertComposableRawMessage, - type LocalMessageInfo, } from 'lib/types/message-types.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; -import { threadTypeIsThick } from 'lib/types/thread-types-enum.js'; import { multimediaMessageSendFailed } from './multimedia-message-utils.js'; import textMessageSendFailed from './text-message-send-failed.js'; @@ -52,10 +49,6 @@ +styles: $ReadOnly, +inputState: ?InputState, +parentThreadInfo: ?ThreadInfo, - +retrySendDMOperation: ( - messageID: string, - localMessageInfo: LocalMessageInfo, - ) => Promise, }; class FailedSend extends React.PureComponent { retryingText = false; @@ -144,18 +137,6 @@ this.retryingMedia = true; } - if (threadTypeIsThick(this.props.item.threadInfo.type)) { - const failedMessageID = this.props.rawMessageInfo?.id; - invariant(failedMessageID, 'failedMessageID should be set for DMs'); - const localMessageInfo = this.props.item.localMessageInfo; - invariant( - localMessageInfo, - 'localMessageInfo should be set for failed message', - ); - void this.props.retrySendDMOperation(failedMessageID, localMessageInfo); - return; - } - const { inputState } = this.props; invariant( inputState, @@ -184,7 +165,6 @@ const parentThreadInfo = useSelector(state => parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, ); - const retrySendDMOperation = useRetrySendDMOperation(); return ( ); }); diff --git a/native/input/input-state-container.react.js b/native/input/input-state-container.react.js --- a/native/input/input-state-container.react.js +++ b/native/input/input-state-container.react.js @@ -97,6 +97,7 @@ import { type ClientNewThinThreadRequest, type NewThreadResult, + type RawThreadInfos, } from 'lib/types/thread-types.js'; import { getConfig } from 'lib/utils/config.js'; import { cloneError, getMessageForException } from 'lib/utils/errors.js'; @@ -167,11 +168,14 @@ +sendTextMessage: (input: SendTextMessageInput) => Promise, +processAndSendDMOperation: ( dmOperationSpecification: OutboundDMOperationSpecification, - ) => Promise, + localMessageID?: string, + ) => Promise<$ReadOnlyArray>, +newThinThread: ( request: ClientNewThinThreadRequest, ) => Promise, +textMessageCreationSideEffectsFunc: CreationSideEffectsFunc, + //TODO improve this + +threadInfos: RawThreadInfos, }; type State = { +pendingUploads: PendingMultimediaUploads, @@ -392,6 +396,51 @@ const mediaMessageContents = getMediaMessageServerDBContentsFromMedia( messageInfo.media, ); + const threadInfo = this.props.threadInfos[threadID]; + const messageID = uuid.v4(); + + if (threadInfo.thick) { + const messageIDs = await this.props.processAndSendDMOperation( + { + type: dmOperationSpecificationTypes.OUTBOUND, + op: { + type: 'send_media_message', + threadID, + creatorID: messageInfo.creatorID, + time: Date.now(), + messageID, + media: messageInfo.media, + }, + recipients: { + type: 'all_thread_members', + threadID: + threadInfo.type === thickThreadTypes.THICK_SIDEBAR && + threadInfo.parentThreadID + ? threadInfo.parentThreadID + : threadInfo.id, + }, + sendOnly: true, + }, + localID, + ); + if (messageIDs.length > 0) { + const copy: any = cloneError({}); + copy.localID = messageInfo.localID; + copy.threadID = messageInfo.threadID; + copy.messageIDs = messageIDs; + throw copy; + } + this.pendingSidebarCreationMessageLocalIDs.delete(localID); + return { + localID, + serverID: messageID, + threadID: threadID, + time: Date.now(), + //TODO + interface: 'socket', + }; + } + try { const result = await this.props.sendMultimediaMessage({ threadID, @@ -471,31 +520,6 @@ ) => { this.sendCallbacks.forEach(callback => callback()); - // TODO: this should be update according to thread creation logic - // (ENG-8567) - if (threadTypeIsThick(inputThreadInfo.type)) { - void this.props.processAndSendDMOperation({ - type: dmOperationSpecificationTypes.OUTBOUND, - op: { - type: 'send_text_message', - threadID: inputThreadInfo.id, - creatorID: messageInfo.creatorID, - time: Date.now(), - messageID: uuid.v4(), - text: messageInfo.text, - }, - recipients: { - type: 'all_thread_members', - threadID: - inputThreadInfo.type === thickThreadTypes.THICK_SIDEBAR && - inputThreadInfo.parentThreadID - ? inputThreadInfo.parentThreadID - : inputThreadInfo.id, - }, - }); - return; - } - const { localID } = messageInfo; invariant( localID !== null && localID !== undefined, @@ -620,6 +644,51 @@ ); const sidebarCreation = this.pendingSidebarCreationMessageLocalIDs.has(localID); + + const messageID = uuid.v4(); + + if (threadTypeIsThick(threadInfo.type)) { + const messageIDs = await this.props.processAndSendDMOperation( + { + type: dmOperationSpecificationTypes.OUTBOUND, + op: { + type: 'send_text_message', + threadID: threadInfo.id, + creatorID: messageInfo.creatorID, + time: Date.now(), + messageID, + text: messageInfo.text, + }, + recipients: { + type: 'all_thread_members', + threadID: + threadInfo.type === thickThreadTypes.THICK_SIDEBAR && + threadInfo.parentThreadID + ? threadInfo.parentThreadID + : threadInfo.id, + }, + sendOnly: true, + }, + localID, + ); + if (messageIDs.length > 0) { + const copy: any = cloneError({}); + copy.localID = messageInfo.localID; + copy.threadID = messageInfo.threadID; + copy.messageIDs = messageIDs; + throw copy; + } + this.pendingSidebarCreationMessageLocalIDs.delete(localID); + return { + localID, + serverID: messageID, + threadID: messageInfo.threadID, + time: Date.now(), + //TODO + interface: 'socket', + }; + } + const result = await this.props.sendTextMessage({ threadID: messageInfo.threadID, localID, @@ -1794,6 +1863,7 @@ const textMessageCreationSideEffectsFunc = useMessageCreationSideEffectsFunc(messageTypes.TEXT); const processAndSendDMOperation = useProcessAndSendDMOperation(); + const threads = useSelector(state => state.threadStore.threadInfos); return ( ); }); diff --git a/web/chat/failed-send.react.js b/web/chat/failed-send.react.js --- a/web/chat/failed-send.react.js +++ b/web/chat/failed-send.react.js @@ -5,16 +5,13 @@ import { type ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js'; import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; -import { useRetrySendDMOperation } from 'lib/shared/dm-ops/process-dm-ops.js'; import { messageID } from 'lib/shared/message-utils.js'; import { messageTypes } from 'lib/types/message-types-enum.js'; import { assertComposableMessageType, - type LocalMessageInfo, type RawComposableMessageInfo, } from 'lib/types/message-types.js'; import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js'; -import { threadTypeIsThick } from 'lib/types/thread-types-enum.js'; import css from './chat-message-list.css'; import multimediaMessageSendFailed from './multimedia-message-send-failed.js'; @@ -32,10 +29,6 @@ +rawMessageInfo: RawComposableMessageInfo, +inputState: ?InputState, +parentThreadInfo: ?ThreadInfo, - +retrySendDMOperation: ( - messageID: string, - localMessageInfo: LocalMessageInfo, - ) => Promise, }; class FailedSend extends React.PureComponent { retryingText = false; @@ -113,17 +106,7 @@ return; } this.retryingText = true; - if (threadTypeIsThick(this.props.threadInfo.type)) { - const failedMessageID = this.props.rawMessageInfo.id; - invariant(failedMessageID, 'failedMessageID should be set for DMs'); - const localMessageInfo = this.props.item.localMessageInfo; - invariant( - localMessageInfo, - 'localMessageInfo should be set for failed message', - ); - void this.props.retrySendDMOperation(failedMessageID, localMessageInfo); - return; - } + void inputState.sendTextMessage( { ...rawMessageInfo, @@ -167,7 +150,6 @@ const parentThreadInfo = useSelector(state => parentThreadID ? threadInfoSelector(state)[parentThreadID] : null, ); - const retrySendDMOperation = useRetrySendDMOperation(); return ( ); }); diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js --- a/web/input/input-state-container.react.js +++ b/web/input/input-state-container.react.js @@ -94,6 +94,7 @@ import { type ClientNewThinThreadRequest, type NewThreadResult, + type RawThreadInfos, } from 'lib/types/thread-types.js'; import { blobHashFromBlobServiceURI, @@ -161,7 +162,8 @@ +sendTextMessage: (input: SendTextMessageInput) => Promise, +processAndSendDMOperation: ( dmOperationSpecification: OutboundDMOperationSpecification, - ) => Promise, + localMessageID?: string, + ) => Promise<$ReadOnlyArray>, +newThinThread: ( request: ClientNewThinThreadRequest, ) => Promise, @@ -171,6 +173,8 @@ +unregisterSendCallback: (() => mixed) => void, +textMessageCreationSideEffectsFunc: CreationSideEffectsFunc, +identityContext: ?IdentityClientContextType, + //TODO improve this + +threadInfos: RawThreadInfos, }; type WritableState = { pendingUploads: { @@ -541,6 +545,52 @@ for (const { id } of messageInfo.media) { mediaIDs.push(id); } + + const messageID = uuid.v4(); + + const threadInfo = this.props.threadInfos[threadID]; + if (threadInfo.thick) { + const messageIDs = await this.props.processAndSendDMOperation( + { + type: dmOperationSpecificationTypes.OUTBOUND, + op: { + type: 'send_media_message', + threadID, + creatorID: messageInfo.creatorID, + time: Date.now(), + messageID, + media: messageInfo.media, + }, + recipients: { + type: 'all_thread_members', + threadID: + threadInfo.type === thickThreadTypes.THICK_SIDEBAR && + threadInfo.parentThreadID + ? threadInfo.parentThreadID + : threadInfo.id, + }, + sendOnly: true, + }, + localID, + ); + if (messageIDs.length > 0) { + const copy: any = cloneError({}); + copy.localID = messageInfo.localID; + copy.threadID = messageInfo.threadID; + copy.messageIDs = messageIDs; + throw copy; + } + this.pendingSidebarCreationMessageLocalIDs.delete(localID); + return { + localID, + serverID: messageID, + threadID: threadID, + time: Date.now(), + //TODO + interface: 'socket', + }; + } + try { const result = await this.props.sendMultimediaMessage({ threadID, @@ -1281,31 +1331,6 @@ ) { this.props.sendCallbacks.forEach(callback => callback()); - // TODO: this should be update according to thread creation logic - // (ENG-8567) - if (threadTypeIsThick(inputThreadInfo.type)) { - void this.props.processAndSendDMOperation({ - type: dmOperationSpecificationTypes.OUTBOUND, - op: { - type: 'send_text_message', - threadID: inputThreadInfo.id, - creatorID: messageInfo.creatorID, - time: Date.now(), - messageID: uuid.v4(), - text: messageInfo.text, - }, - recipients: { - type: 'all_thread_members', - threadID: - inputThreadInfo.type === thickThreadTypes.THICK_SIDEBAR && - inputThreadInfo.parentThreadID - ? inputThreadInfo.parentThreadID - : inputThreadInfo.id, - }, - }); - return; - } - const { localID } = messageInfo; invariant( localID !== null && localID !== undefined, @@ -1410,6 +1435,51 @@ ); const sidebarCreation = this.pendingSidebarCreationMessageLocalIDs.has(localID); + + const messageID = uuid.v4(); + + if (threadTypeIsThick(threadInfo.type)) { + const messageIDs = await this.props.processAndSendDMOperation( + { + type: dmOperationSpecificationTypes.OUTBOUND, + op: { + type: 'send_text_message', + threadID: threadInfo.id, + creatorID: messageInfo.creatorID, + time: Date.now(), + messageID, + text: messageInfo.text, + }, + recipients: { + type: 'all_thread_members', + threadID: + threadInfo.type === thickThreadTypes.THICK_SIDEBAR && + threadInfo.parentThreadID + ? threadInfo.parentThreadID + : threadInfo.id, + }, + sendOnly: true, + }, + localID, + ); + if (messageIDs.length > 0) { + const copy: any = cloneError({}); + copy.localID = messageInfo.localID; + copy.threadID = messageInfo.threadID; + copy.messageIDs = messageIDs; + throw copy; + } + this.pendingSidebarCreationMessageLocalIDs.delete(localID); + return { + localID, + serverID: messageID, + threadID: messageInfo.threadID, + time: Date.now(), + //TODO + interface: 'socket', + }; + } + const result = await this.props.sendTextMessage({ threadID: messageInfo.threadID, localID, @@ -1735,6 +1805,8 @@ const textMessageCreationSideEffectsFunc = useMessageCreationSideEffectsFunc(messageTypes.TEXT); + const threads = useSelector(state => state.threadStore.threadInfos); + return ( ); });