diff --git a/lib/shared/mention-utils.js b/lib/shared/mention-utils.js --- a/lib/shared/mention-utils.js +++ b/lib/shared/mention-utils.js @@ -26,6 +26,16 @@ return new RegExp(`\\B@${username}\\b`, 'i').test(text); } +const mentionsExtractionRegex = new RegExp( + `\\B(@(${oldValidUsernameRegexString}))\\b`, + 'g', +); + +function extractMentionsFromText(text: string): string[] { + const iterator = text.matchAll(mentionsExtractionRegex); + return [...iterator].map(matches => matches[2]); +} + function getTypeaheadRegexMatches( text: string, selection: Selection, @@ -82,6 +92,7 @@ export { markdownMentionRegex, isMentioned, + extractMentionsFromText, getTypeaheadUserSuggestions, getNewTextAndSelection, getTypeaheadRegexMatches, 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 @@ -7,6 +7,7 @@ import tinycolor from 'tinycolor2'; import { type ParserRules } from './markdown.js'; +import { extractMentionsFromText } from './mention-utils.js'; import { getMessageTitle } from './message-utils.js'; import { relationshipBlockedInEitherDirection } from './relationship-utils.js'; import threadWatcher from './thread-watcher.js'; @@ -42,9 +43,10 @@ usersWithPersonalThreadSelector, } from '../selectors/user-selectors.js'; import type { CalendarQuery } from '../types/entry-types.js'; -import type { - RobotextMessageInfo, - ComposableMessageInfo, +import { + type RobotextMessageInfo, + type ComposableMessageInfo, + messageTypes, } from '../types/message-types.js'; import { userRelationshipStatus } from '../types/relationship-types.js'; import { @@ -462,8 +464,10 @@ const threadName = trimText(messageTitle, 30); const initialMembers = new Map(); - const { id: viewerID, username } = loggedInUserInfo; - initialMembers.set(viewerID, { id: viewerID, username }); + + const { id: viewerID, username: viewerUsername } = loggedInUserInfo; + initialMembers.set(viewerID, { id: viewerID, username: viewerUsername }); + if (userIsMember(parentThreadInfo, sourceMessageInfo.creator.id)) { const { id: sourceAuthorID, username: sourceAuthorUsername } = sourceMessageInfo.creator; @@ -477,6 +481,7 @@ }; initialMembers.set(sourceAuthorID, initialMemberUserInfo); } + const singleOtherUser = getSingleOtherUser(parentThreadInfo, viewerID); if (parentThreadType === threadTypes.PERSONAL && singleOtherUser) { const singleOtherUsername = parentThreadInfo.members.find( @@ -493,6 +498,24 @@ initialMembers.set(singleOtherUser, singleOtherUserInfo); } + if (sourceMessageInfo.type === messageTypes.TEXT) { + const memberMap = new Map(); + for (const member of parentThreadInfo.members) { + const { id, role, username } = member; + if (!role || !username) { + continue; + } + memberMap.set(username.toLowerCase(), { id, username }); + } + const mentions = extractMentionsFromText(sourceMessageInfo.text); + for (const mention of mentions) { + const userInfo = memberMap.get(mention.toLowerCase()); + if (userInfo) { + initialMembers.set(userInfo.id, userInfo); + } + } + } + return createPendingThread({ viewerID, threadType: threadTypes.SIDEBAR,