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 @@ -1543,6 +1543,35 @@ return result; } +function patchThreadInfoToIncludeMentionedMembersOfParent( + threadInfo: ThreadInfo, + parentThreadInfo: ThreadInfo, + messageText: string, + viewerID: string, +): ThreadInfo { + const members: UserIDAndUsername[] = threadInfo.members + .map(({ id, username }) => (username ? { id, username } : null)) + .filter(Boolean); + const mentionedNewMembers = extractNewMentionedParentMembers( + messageText, + threadInfo, + parentThreadInfo, + ); + if (mentionedNewMembers.length === 0) { + return threadInfo; + } + members.push(...mentionedNewMembers); + return createPendingThread({ + viewerID, + threadType: threadTypes.SIDEBAR, + members, + parentThreadInfo, + threadColor: threadInfo.color, + name: threadInfo.name, + sourceMessageID: threadInfo.sourceMessageID, + }); +} + export { colorIsDark, generateRandomColor, @@ -1611,4 +1640,5 @@ threadMembersWithoutAddedAshoat, validChatNameRegex, chatNameMaxLength, + patchThreadInfoToIncludeMentionedMembersOfParent, }; 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 @@ -41,6 +41,7 @@ import { createRealThreadFromPendingThread, threadIsPending, + patchThreadInfoToIncludeMentionedMembersOfParent, } from 'lib/shared/thread-utils.js'; import type { CalendarQuery } from 'lib/types/entry-types.js'; import type { @@ -69,10 +70,11 @@ type MediaMissionReportCreationRequest, reportTypes, } from 'lib/types/report-types.js'; -import type { - ClientNewThreadRequest, - NewThreadResult, - ThreadInfo, +import { + type ClientNewThreadRequest, + type NewThreadResult, + type ThreadInfo, + threadTypes, } from 'lib/types/thread-types.js'; import { type DispatchActionPromise, @@ -389,15 +391,19 @@ sendTextMessage = async ( messageInfo: RawTextMessageInfo, - threadInfo: ThreadInfo, + inputThreadInfo: ThreadInfo, parentThreadInfo: ?ThreadInfo, ) => { this.sendCallbacks.forEach(callback => callback()); - if (!threadIsPending(threadInfo.id)) { + if (!threadIsPending(inputThreadInfo.id)) { this.props.dispatchActionPromise( sendTextMessageActionTypes, - this.sendTextMessageAction(messageInfo, threadInfo, parentThreadInfo), + this.sendTextMessageAction( + messageInfo, + inputThreadInfo, + parentThreadInfo, + ), undefined, messageInfo, ); @@ -409,6 +415,18 @@ payload: messageInfo, }); + let threadInfo = inputThreadInfo; + const { viewerID } = this.props; + if (viewerID && inputThreadInfo.type === threadTypes.SIDEBAR) { + invariant(parentThreadInfo, 'sidebar should have parent'); + threadInfo = patchThreadInfoToIncludeMentionedMembersOfParent( + inputThreadInfo, + parentThreadInfo, + messageInfo.text, + viewerID, + ); + } + let newThreadID = null; try { newThreadID = await this.startThreadCreation(threadInfo); @@ -447,9 +465,9 @@ ); }; - async startThreadCreation(threadInfo: ThreadInfo): Promise { + startThreadCreation(threadInfo: ThreadInfo): Promise { if (!threadIsPending(threadInfo.id)) { - return threadInfo.id; + return Promise.resolve(threadInfo.id); } let threadCreationPromise = this.pendingThreadCreations.get(threadInfo.id); if (!threadCreationPromise) { 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 @@ -44,6 +44,7 @@ createRealThreadFromPendingThread, draftKeyFromThreadID, threadIsPending, + patchThreadInfoToIncludeMentionedMembersOfParent, } from 'lib/shared/thread-utils.js'; import type { CalendarQuery } from 'lib/types/entry-types.js'; import type { @@ -65,10 +66,11 @@ import type { RawTextMessageInfo } from 'lib/types/messages/text.js'; import type { Dispatch } from 'lib/types/redux-types.js'; import { reportTypes } from 'lib/types/report-types.js'; -import type { - ClientNewThreadRequest, - NewThreadResult, - ThreadInfo, +import { + type ClientNewThreadRequest, + type NewThreadResult, + type ThreadInfo, + threadTypes, } from 'lib/types/thread-types.js'; import { type DispatchActionPromise, @@ -457,9 +459,9 @@ } } - async startThreadCreation(threadInfo: ThreadInfo): Promise { + startThreadCreation(threadInfo: ThreadInfo): Promise { if (!threadIsPending(threadInfo.id)) { - return threadInfo.id; + return Promise.resolve(threadInfo.id); } let threadCreationPromise = this.pendingThreadCreations.get(threadInfo.id); if (!threadCreationPromise) { @@ -989,15 +991,19 @@ async sendTextMessage( messageInfo: RawTextMessageInfo, - threadInfo: ThreadInfo, + inputThreadInfo: ThreadInfo, parentThreadInfo: ?ThreadInfo, ) { this.props.sendCallbacks.forEach(callback => callback()); - if (!threadIsPending(threadInfo.id)) { + if (!threadIsPending(inputThreadInfo.id)) { this.props.dispatchActionPromise( sendTextMessageActionTypes, - this.sendTextMessageAction(messageInfo, threadInfo, parentThreadInfo), + this.sendTextMessageAction( + messageInfo, + inputThreadInfo, + parentThreadInfo, + ), undefined, messageInfo, ); @@ -1009,6 +1015,18 @@ payload: messageInfo, }); + let threadInfo = inputThreadInfo; + const { viewerID } = this.props; + if (viewerID && inputThreadInfo.type === threadTypes.SIDEBAR) { + invariant(parentThreadInfo, 'sidebar should have parent'); + threadInfo = patchThreadInfoToIncludeMentionedMembersOfParent( + inputThreadInfo, + parentThreadInfo, + messageInfo.text, + viewerID, + ); + } + let newThreadID = null; try { newThreadID = await this.startThreadCreation(threadInfo);