diff --git a/lib/shared/thread-actions-utils.js b/lib/shared/thread-actions-utils.js --- a/lib/shared/thread-actions-utils.js +++ b/lib/shared/thread-actions-utils.js @@ -20,10 +20,13 @@ import { threadTypes, assertThinThreadType, + assertThickThreadType, + threadTypeIsThick, } from '../types/thread-types-enum.js'; import type { ChangeThreadSettingsPayload, ClientNewThinThreadRequest, + NewThickThreadRequest, NewThreadResult, } from '../types/thread-types.js'; import type { DispatchActionPromise } from '../utils/redux-promise-utils.js'; @@ -51,6 +54,7 @@ +threadInfo: ThreadInfo, +dispatchActionPromise: DispatchActionPromise, +createNewThinThread: ClientNewThinThreadRequest => Promise, + +createNewThickThread: NewThickThreadRequest => Promise, +sourceMessageID: ?string, +viewerID: ?string, +handleError?: () => mixed, @@ -61,6 +65,7 @@ threadInfo, dispatchActionPromise, createNewThinThread, + createNewThickThread, sourceMessageID, viewerID, calendarQuery, @@ -69,44 +74,90 @@ return threadInfo.id; } + let newThreadID; + const otherMemberIDs = threadOtherMembers(threadInfo.members, viewerID).map( member => member.id, ); let resultPromise; - if (threadInfo.type !== threadTypes.SIDEBAR) { + if (threadInfo.type === threadTypes.SIDEBAR) { invariant( - otherMemberIDs.length > 0, - 'otherMemberIDs should not be empty for threads', + sourceMessageID, + 'sourceMessageID should be set when creating a sidebar', ); - // TODO add support for thickThreadTypes in ENG-8442 - const type = assertThinThreadType(pendingThreadType(otherMemberIDs.length)); invariant( - type !== 5, // Flow does not recognize that threadTypes.SIDEBAR is 5 - 'pendingThreadType should not return SIDEBAR', + threadInfo.parentThreadID, + 'parentThreadID should be set when creating a sidebar', ); resultPromise = createNewThinThread({ - type, + type: threadTypes.SIDEBAR, initialMemberIDs: otherMemberIDs, color: threadInfo.color, + sourceMessageID, + parentThreadID: threadInfo.parentThreadID, + name: threadInfo.name, calendarQuery, }); - } else { + void dispatchActionPromise(newThreadActionTypes, resultPromise); + const result = await resultPromise; + newThreadID = result.newThreadID; + } else if (threadInfo.type === threadTypes.THICK_SIDEBAR) { invariant( sourceMessageID, 'sourceMessageID should be set when creating a sidebar', ); - resultPromise = createNewThinThread({ - type: threadTypes.SIDEBAR, + invariant( + threadInfo.parentThreadID, + 'parentThreadID should be set when creating a sidebar', + ); + newThreadID = await createNewThickThread({ + type: threadTypes.THICK_SIDEBAR, initialMemberIDs: otherMemberIDs, color: threadInfo.color, sourceMessageID, parentThreadID: threadInfo.parentThreadID, name: threadInfo.name, - calendarQuery, }); + } else { + invariant( + otherMemberIDs.length > 0, + 'otherMemberIDs should not be empty for threads', + ); + if (threadTypeIsThick(threadInfo.type)) { + const type = assertThickThreadType( + pendingThreadType(otherMemberIDs.length), + ); + + invariant( + type !== 16, + // Flow does not recognize that threadTypes.THICK_SIDEBAR is 16 + 'pendingThreadType should not return THICK_SIDEBAR', + ); + newThreadID = await createNewThickThread({ + type, + initialMemberIDs: otherMemberIDs, + color: threadInfo.color, + }); + } else { + const type = assertThinThreadType( + pendingThreadType(otherMemberIDs.length), + ); + + invariant( + type !== 5, // Flow does not recognize that threadTypes.SIDEBAR is 5 + 'pendingThreadType should not return SIDEBAR', + ); + resultPromise = createNewThinThread({ + type, + initialMemberIDs: otherMemberIDs, + color: threadInfo.color, + calendarQuery, + }); + void dispatchActionPromise(newThreadActionTypes, resultPromise); + const result = await resultPromise; + newThreadID = result.newThreadID; + } } - void dispatchActionPromise(newThreadActionTypes, resultPromise); - const { newThreadID } = await resultPromise; return newThreadID; } diff --git a/lib/types/thread-types.js b/lib/types/thread-types.js --- a/lib/types/thread-types.js +++ b/lib/types/thread-types.js @@ -346,6 +346,7 @@ +type: 5, +sourceMessageID: string, ...BaseNewThreadRequest, + +parentThreadID: string, }>; export type ClientNewThinThreadRequest = $ReadOnly<{ 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 @@ -30,6 +30,7 @@ useBlobServiceUpload, } from 'lib/actions/upload-actions.js'; import commStaffCommunity from 'lib/facts/comm-staff-community.js'; +import { useNewThickThread } from 'lib/hooks/thread-hooks.js'; import type { CallSingleKeyserverEndpointOptions, CallSingleKeyserverEndpointResponse, @@ -97,6 +98,7 @@ import { type ClientNewThinThreadRequest, type NewThreadResult, + type NewThickThreadRequest, } from 'lib/types/thread-types.js'; import { getConfig } from 'lib/utils/config.js'; import { cloneError, getMessageForException } from 'lib/utils/errors.js'; @@ -171,6 +173,7 @@ +newThinThread: ( request: ClientNewThinThreadRequest, ) => Promise, + +newThickThread: (request: NewThickThreadRequest) => Promise, +textMessageCreationSideEffectsFunc: CreationSideEffectsFunc, }; type State = { @@ -464,6 +467,48 @@ ); }; + async processAndSendTextMessageDMOperation( + messageInfo: RawTextMessageInfo, + inputThreadInfo: ThreadInfo, + ) { + 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, + }, + }); + } + + async generateAndSendTextMessageAction( + messageInfo: RawTextMessageInfo, + threadInfo: ThreadInfo, + parentThreadInfo: ?ThreadInfo, + ) { + if (threadTypeIsThick(threadInfo.type)) { + void this.processAndSendTextMessageDMOperation(messageInfo, threadInfo); + return; + } + void this.props.dispatchActionPromise( + sendTextMessageActionTypes, + this.sendTextMessageAction(messageInfo, threadInfo, parentThreadInfo), + undefined, + messageInfo, + ); + } + sendTextMessage = async ( messageInfo: RawTextMessageInfo, inputThreadInfo: ThreadInfo, @@ -471,31 +516,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, @@ -506,23 +526,20 @@ } if (!threadIsPending(inputThreadInfo.id)) { - void this.props.dispatchActionPromise( - sendTextMessageActionTypes, - this.sendTextMessageAction( - messageInfo, - inputThreadInfo, - parentThreadInfo, - ), - undefined, + void this.generateAndSendTextMessageAction( messageInfo, + inputThreadInfo, + parentThreadInfo, ); return; } - this.props.dispatch({ - type: sendTextMessageActionTypes.started, - payload: messageInfo, - }); + if (!threadTypeIsThick(inputThreadInfo.type)) { + this.props.dispatch({ + type: sendTextMessageActionTypes.started, + payload: messageInfo, + }); + } let threadInfo = inputThreadInfo; const { viewerID } = this.props; @@ -549,11 +566,13 @@ const copy = cloneError(e); copy.localID = messageInfo.localID; copy.threadID = messageInfo.threadID; - this.props.dispatch({ - type: sendTextMessageActionTypes.failed, - payload: copy, - error: true, - }); + if (!threadTypeIsThick(inputThreadInfo.type)) { + this.props.dispatch({ + type: sendTextMessageActionTypes.failed, + payload: copy, + error: true, + }); + } return; } finally { this.pendingThreadCreations.delete(threadInfo.id); @@ -570,15 +589,10 @@ id: newThreadID, }; - void this.props.dispatchActionPromise( - sendTextMessageActionTypes, - this.sendTextMessageAction( - newMessageInfo, - newThreadInfo, - parentThreadInfo, - ), - undefined, + void this.generateAndSendTextMessageAction( newMessageInfo, + newThreadInfo, + parentThreadInfo, ); }; @@ -593,6 +607,7 @@ threadInfo, dispatchActionPromise: this.props.dispatchActionPromise, createNewThinThread: this.props.newThinThread, + createNewThickThread: this.props.newThickThread, sourceMessageID: threadInfo.sourceMessageID, viewerID: this.props.viewerID, calendarQuery, @@ -1787,6 +1802,7 @@ const callSendMultimediaMessage = useSendMultimediaMessage(); const callSendTextMessage = useSendTextMessage(); const callNewThinThread = useNewThinThread(); + const callNewThickThread = useNewThickThread(); const dispatchActionPromise = useDispatchActionPromise(); const dispatch = useDispatch(); const mediaReportsEnabled = useIsReportEnabled('mediaReports'); @@ -1810,6 +1826,7 @@ sendTextMessage={callSendTextMessage} processAndSendDMOperation={processAndSendDMOperation} newThinThread={callNewThinThread} + newThickThread={callNewThickThread} dispatchActionPromise={dispatchActionPromise} dispatch={dispatch} staffCanSee={staffCanSee} 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 @@ -40,6 +40,7 @@ } from 'lib/components/modal-provider.react.js'; import blobService from 'lib/facts/blob-service.js'; import commStaffCommunity from 'lib/facts/comm-staff-community.js'; +import { useNewThickThread } from 'lib/hooks/thread-hooks.js'; import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js'; import { getNextLocalUploadID } from 'lib/media/media-utils.js'; import { pendingToRealizedThreadIDsSelector } from 'lib/selectors/thread-selectors.js'; @@ -94,6 +95,7 @@ import { type ClientNewThinThreadRequest, type NewThreadResult, + type NewThickThreadRequest, } from 'lib/types/thread-types.js'; import { blobHashFromBlobServiceURI, @@ -165,6 +167,7 @@ +newThinThread: ( request: ClientNewThinThreadRequest, ) => Promise, + +newThickThread: (request: NewThickThreadRequest) => Promise, +pushModal: PushModal, +sendCallbacks: $ReadOnlyArray<() => mixed>, +registerSendCallback: (() => mixed) => void, @@ -597,6 +600,7 @@ threadInfo, dispatchActionPromise: this.props.dispatchActionPromise, createNewThinThread: this.props.newThinThread, + createNewThickThread: this.props.newThickThread, sourceMessageID: threadInfo.sourceMessageID, viewerID: this.props.viewerID, calendarQuery, @@ -1274,37 +1278,54 @@ ); } - async sendTextMessage( + async processAndSendTextMessageDMOperation( messageInfo: RawTextMessageInfo, inputThreadInfo: ThreadInfo, - parentThreadInfo: ?ThreadInfo, ) { - this.props.sendCallbacks.forEach(callback => callback()); + 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, + }, + }); + } - // 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, - }, - }); + async generateAndSendTextMessageAction( + messageInfo: RawTextMessageInfo, + threadInfo: ThreadInfo, + parentThreadInfo: ?ThreadInfo, + ) { + if (threadTypeIsThick(threadInfo.type)) { + void this.processAndSendTextMessageDMOperation(messageInfo, threadInfo); return; } + void this.props.dispatchActionPromise( + sendTextMessageActionTypes, + this.sendTextMessageAction(messageInfo, threadInfo, parentThreadInfo), + undefined, + messageInfo, + ); + } + + async sendTextMessage( + messageInfo: RawTextMessageInfo, + inputThreadInfo: ThreadInfo, + parentThreadInfo: ?ThreadInfo, + ) { + this.props.sendCallbacks.forEach(callback => callback()); const { localID } = messageInfo; invariant( @@ -1316,23 +1337,20 @@ } if (!threadIsPending(inputThreadInfo.id)) { - void this.props.dispatchActionPromise( - sendTextMessageActionTypes, - this.sendTextMessageAction( - messageInfo, - inputThreadInfo, - parentThreadInfo, - ), - undefined, + void this.generateAndSendTextMessageAction( messageInfo, + inputThreadInfo, + parentThreadInfo, ); return; } - this.props.dispatch({ - type: sendTextMessageActionTypes.started, - payload: messageInfo, - }); + if (!threadTypeIsThick(inputThreadInfo.type)) { + this.props.dispatch({ + type: sendTextMessageActionTypes.started, + payload: messageInfo, + }); + } let threadInfo = inputThreadInfo; const { viewerID } = this.props; @@ -1359,11 +1377,13 @@ const copy = cloneError(e); copy.localID = messageInfo.localID; copy.threadID = messageInfo.threadID; - this.props.dispatch({ - type: sendTextMessageActionTypes.failed, - payload: copy, - error: true, - }); + if (!threadTypeIsThick(inputThreadInfo.type)) { + this.props.dispatch({ + type: sendTextMessageActionTypes.failed, + payload: copy, + error: true, + }); + } return; } finally { this.pendingThreadCreations.delete(threadInfo.id); @@ -1380,15 +1400,10 @@ id: newThreadID, }; - void this.props.dispatchActionPromise( - sendTextMessageActionTypes, - this.sendTextMessageAction( - newMessageInfo, - newThreadInfo, - parentThreadInfo, - ), - undefined, + void this.generateAndSendTextMessageAction( newMessageInfo, + newThreadInfo, + parentThreadInfo, ); } @@ -1712,6 +1727,7 @@ const callSendMultimediaMessage = useLegacySendMultimediaMessage(); const callSendTextMessage = useSendTextMessage(); const callNewThinThread = useNewThinThread(); + const callNewThickThread = useNewThickThread(); const dispatch = useDispatch(); const dispatchActionPromise = useDispatchActionPromise(); const modalContext = useModalContext(); @@ -1751,6 +1767,7 @@ sendTextMessage={callSendTextMessage} processAndSendDMOperation={processAndSendDMOperation} newThinThread={callNewThinThread} + newThickThread={callNewThickThread} dispatch={dispatch} dispatchActionPromise={dispatchActionPromise} pushModal={modalContext.pushModal}