diff --git a/lib/actions/message-actions.js b/lib/actions/message-actions.js --- a/lib/actions/message-actions.js +++ b/lib/actions/message-actions.js @@ -96,21 +96,20 @@ threadID: string, localID: string, text: string, + sidebarCreation?: boolean, ) => Promise) => - async (threadID, localID, text) => { + async (threadID, localID, text, sidebarCreation) => { let resultInfo; const getResultInfo = (passedResultInfo: CallServerEndpointResultInfo) => { resultInfo = passedResultInfo; }; - const response = await callServerEndpoint( - 'create_text_message', - { - threadID, - localID, - text, - }, - { getResultInfo }, - ); + let payload = { threadID, localID, text }; + if (sidebarCreation) { + payload = { ...payload, sidebarCreation }; + } + const response = await callServerEndpoint('create_text_message', payload, { + getResultInfo, + }); const resultInterface = resultInfo?.interface; invariant( resultInterface, @@ -137,19 +136,20 @@ threadID: string, localID: string, mediaMessageContents: $ReadOnlyArray, + sidebarCreation?: boolean, ) => Promise) => - async (threadID, localID, mediaMessageContents) => { + async (threadID, localID, mediaMessageContents, sidebarCreation) => { let resultInfo; const getResultInfo = (passedResultInfo: CallServerEndpointResultInfo) => { resultInfo = passedResultInfo; }; + let payload = { threadID, localID, mediaMessageContents }; + if (sidebarCreation) { + payload = { ...payload, sidebarCreation }; + } const response = await callServerEndpoint( 'create_multimedia_message', - { - threadID, - localID, - mediaMessageContents, - }, + payload, { getResultInfo }, ); const resultInterface = resultInfo?.interface; @@ -171,19 +171,20 @@ threadID: string, localID: string, mediaIDs: $ReadOnlyArray, + sidebarCreation?: boolean, ) => Promise) => - async (threadID, localID, mediaIDs) => { + async (threadID, localID, mediaIDs, sidebarCreation) => { let resultInfo; const getResultInfo = (passedResultInfo: CallServerEndpointResultInfo) => { resultInfo = passedResultInfo; }; + let payload = { threadID, localID, mediaIDs }; + if (sidebarCreation) { + payload = { ...payload, sidebarCreation }; + } const response = await callServerEndpoint( 'create_multimedia_message', - { - threadID, - localID, - mediaIDs, - }, + payload, { getResultInfo }, ); const resultInterface = resultInfo?.interface; diff --git a/lib/shared/thread-utils.js b/lib/shared/thread-utils.js --- a/lib/shared/thread-utils.js +++ b/lib/shared/thread-utils.js @@ -267,6 +267,10 @@ return !!threadID?.startsWith('pending'); } +function threadIsPendingSidebar(threadID: ?string): boolean { + return !!threadID?.startsWith('pending/sidebar/'); +} + function getSingleOtherUser( threadInfo: ThreadInfo | RawThreadInfo, viewerID: ?string, @@ -1518,6 +1522,7 @@ threadOtherMembers, threadIsGroupChat, threadIsPending, + threadIsPendingSidebar, getSingleOtherUser, getPendingThreadID, pendingThreadIDRegex, 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 @@ -39,6 +39,7 @@ import { createRealThreadFromPendingThread, threadIsPending, + threadIsPendingSidebar, } from 'lib/shared/thread-utils.js'; import type { CalendarQuery } from 'lib/types/entry-types.js'; import type { @@ -131,11 +132,13 @@ threadID: string, localID: string, mediaMessageContents: $ReadOnlyArray, + sidebarCreation?: boolean, ) => Promise, +sendTextMessage: ( threadID: string, localID: string, text: string, + sidebarCreation?: boolean, ) => Promise, +newThread: (request: ClientNewThreadRequest) => Promise, }; @@ -151,6 +154,12 @@ replyCallbacks: Array<(message: string) => void> = []; pendingThreadCreations = new Map>(); + // When the user sends a multimedia message that triggers the creation of a + // sidebar, the sidebar gets created right away, but the message needs to wait + // for the uploads to complete before sending. We use this Set to track the + // message localIDs that need sidebarCreation: true. + pendingSidebarCreationMessageLocalIDs = new Set(); + static getCompletedUploads(props: Props, state: State): CompletedUploads { const completedUploads = {}; for (const localMessageID in state.pendingUploads) { @@ -332,6 +341,8 @@ localID !== null && localID !== undefined, 'localID should be set', ); + const sidebarCreation = + this.pendingSidebarCreationMessageLocalIDs.has(localID); const mediaMessageContents = getMediaMessageServerDBContentsFromMedia( messageInfo.media, ); @@ -340,7 +351,9 @@ threadID, localID, mediaMessageContents, + sidebarCreation, ); + this.pendingSidebarCreationMessageLocalIDs.delete(localID); return { localID, serverID: result.id, @@ -390,6 +403,15 @@ ) => { this.sendCallbacks.forEach(callback => callback()); + const { localID } = messageInfo; + invariant( + localID !== null && localID !== undefined, + 'localID should be set', + ); + if (threadIsPendingSidebar(threadInfo.id)) { + this.pendingSidebarCreationMessageLocalIDs.add(localID); + } + if (!threadIsPending(threadInfo.id)) { this.props.dispatchActionPromise( sendTextMessageActionTypes, @@ -464,11 +486,15 @@ localID !== null && localID !== undefined, 'localID should be set', ); + const sidebarCreation = + this.pendingSidebarCreationMessageLocalIDs.has(localID); const result = await this.props.sendTextMessage( messageInfo.threadID, localID, messageInfo.text, + sidebarCreation, ); + this.pendingSidebarCreationMessageLocalIDs.delete(localID); return { localID, serverID: result.id, @@ -492,6 +518,10 @@ const localMessageID = `${localIDPrefix}${this.props.nextLocalID}`; this.startThreadCreation(threadInfo); + if (threadIsPendingSidebar(threadInfo.id)) { + this.pendingSidebarCreationMessageLocalIDs.add(localMessageID); + } + const uploadFileInputs = [], media = []; for (const selection of selections) { @@ -1073,6 +1103,10 @@ this.startThreadCreation(threadInfo); + if (threadIsPendingSidebar(threadInfo.id)) { + this.pendingSidebarCreationMessageLocalIDs.add(localMessageID); + } + const updateMedia = (media: $ReadOnlyArray): T[] => media.map(singleMedia => { let updatedMedia = singleMedia; 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 @@ -42,6 +42,7 @@ createRealThreadFromPendingThread, draftKeyFromThreadID, threadIsPending, + threadIsPendingSidebar, } from 'lib/shared/thread-utils.js'; import type { CalendarQuery } from 'lib/types/entry-types.js'; import type { @@ -114,11 +115,13 @@ threadID: string, localID: string, mediaIDs: $ReadOnlyArray, + sidebarCreation?: boolean, ) => Promise, +sendTextMessage: ( threadID: string, localID: string, text: string, + sidebarCreation?: boolean, ) => Promise, +newThread: (request: ClientNewThreadRequest) => Promise, +pushModal: PushModal, @@ -155,6 +158,12 @@ replyCallbacks: Array<(message: string) => void> = []; pendingThreadCreations = new Map>(); + // When the user sends a multimedia message that triggers the creation of a + // sidebar, the sidebar gets created right away, but the message needs to wait + // for the uploads to complete before sending. We use this Set to track the + // message localIDs that need sidebarCreation: true. + pendingSidebarCreationMessageLocalIDs = new Set(); + static reassignToRealizedThreads( state: { +[threadID: string]: T }, props: Props, @@ -407,6 +416,8 @@ localID !== null && localID !== undefined, 'localID should be set', ); + const sidebarCreation = + this.pendingSidebarCreationMessageLocalIDs.has(localID); const mediaIDs = []; for (const { id } of messageInfo.media) { mediaIDs.push(id); @@ -416,7 +427,9 @@ threadID, localID, mediaIDs, + sidebarCreation, ); + this.pendingSidebarCreationMessageLocalIDs.delete(localID); this.setState(prevState => { const newThreadID = this.getRealizedOrPendingThreadID(threadID); const prevUploads = prevState.pendingUploads[newThreadID]; @@ -989,6 +1002,15 @@ ) { this.props.sendCallbacks.forEach(callback => callback()); + const { localID } = messageInfo; + invariant( + localID !== null && localID !== undefined, + 'localID should be set', + ); + if (threadIsPendingSidebar(threadInfo.id)) { + this.pendingSidebarCreationMessageLocalIDs.add(localID); + } + if (!threadIsPending(threadInfo.id)) { this.props.dispatchActionPromise( sendTextMessageActionTypes, @@ -1043,11 +1065,15 @@ localID !== null && localID !== undefined, 'localID should be set', ); + const sidebarCreation = + this.pendingSidebarCreationMessageLocalIDs.has(localID); const result = await this.props.sendTextMessage( messageInfo.threadID, localID, messageInfo.text, + sidebarCreation, ); + this.pendingSidebarCreationMessageLocalIDs.delete(localID); return { localID, serverID: result.id, @@ -1069,6 +1095,10 @@ const localMessageID = `${localIDPrefix}${localID}`; this.startThreadCreation(threadInfo); + if (threadIsPendingSidebar(threadInfo.id)) { + this.pendingSidebarCreationMessageLocalIDs.add(localMessageID); + } + this.setState(prevState => { const newThreadID = this.getRealizedOrPendingThreadID(threadInfo.id); const currentPendingUploads = prevState.pendingUploads[newThreadID]; @@ -1196,6 +1226,10 @@ this.startThreadCreation(threadInfo); + if (threadIsPendingSidebar(threadInfo.id)) { + this.pendingSidebarCreationMessageLocalIDs.add(localMessageID); + } + const completed = InputStateContainer.completedMessageIDs(this.state); if (completed.has(localMessageID)) { this.sendMultimediaMessage(newRawMessageInfo);