Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F3033776
D8903.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
8 KB
Referenced Files
None
Subscribers
None
D8903.diff
View Options
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
@@ -8,9 +8,11 @@
} from './thread-utils.js';
import { stringForUserExplicit } from './user-utils.js';
import { threadTypes } from '../types/thread-types-enum.js';
-import {
- type ThreadInfo,
- type RelativeMemberInfo,
+import type {
+ ThreadInfo,
+ RelativeMemberInfo,
+ ChatMentionCandidates,
+ ResolvedThreadInfo,
} from '../types/thread-types.js';
export type TypeaheadMatchedStrings = {
@@ -41,6 +43,10 @@
const chatMentionRegexString = `^(?<!\\\\)(@\\[\\[([0-9,|]+):(${validChatNameRegexString}?)(?<!\\\\)\\]\\])`;
const chatMentionRegex: RegExp = new RegExp(chatMentionRegexString);
+function encodeChatMentionText(text: string): string {
+ return text.replace(/]/g, '\\]');
+}
+
function decodeChatMentionText(text: string): string {
return text.replace(/\\]/g, ']');
}
@@ -80,6 +86,21 @@
);
}
+function getMentionTypeaheadChatSuggestions(
+ chatSearchIndex: SentencePrefixSearchIndex,
+ chatMentionCandidates: ChatMentionCandidates,
+ chatPrefix: string,
+): $ReadOnlyArray<ResolvedThreadInfo> {
+ const threadIDs = chatSearchIndex.getSearchResults(chatPrefix);
+ const result = [];
+ for (const threadID in chatMentionCandidates) {
+ if (chatPrefix.length === 0 || threadIDs.includes(threadID)) {
+ result.push(chatMentionCandidates[threadID]);
+ }
+ }
+ return result;
+}
+
function getNewTextAndSelection(
textBeforeAtSymbol: string,
entireText: string,
@@ -122,9 +143,11 @@
isUserMentioned,
extractUserMentionsFromText,
getMentionTypeaheadUserSuggestions,
+ getMentionTypeaheadChatSuggestions,
getNewTextAndSelection,
getTypeaheadRegexMatches,
getUserMentionsCandidates,
chatMentionRegex,
+ encodeChatMentionText,
decodeChatMentionText,
};
diff --git a/native/avatars/thread-avatar.react.js b/native/avatars/thread-avatar.react.js
--- a/native/avatars/thread-avatar.react.js
+++ b/native/avatars/thread-avatar.react.js
@@ -8,13 +8,17 @@
} from 'lib/shared/avatar-utils.js';
import { getSingleOtherUser } from 'lib/shared/thread-utils.js';
import { threadTypes } from 'lib/types/thread-types-enum.js';
-import { type RawThreadInfo, type ThreadInfo } from 'lib/types/thread-types.js';
+import type {
+ RawThreadInfo,
+ ThreadInfo,
+ ResolvedThreadInfo,
+} from 'lib/types/thread-types.js';
import Avatar from './avatar.react.js';
import { useSelector } from '../redux/redux-utils.js';
type Props = {
- +threadInfo: RawThreadInfo | ThreadInfo,
+ +threadInfo: RawThreadInfo | ThreadInfo | ResolvedThreadInfo,
+size: 'micro' | 'small' | 'large' | 'profile',
};
diff --git a/native/chat/chat-input-bar.react.js b/native/chat/chat-input-bar.react.js
--- a/native/chat/chat-input-bar.react.js
+++ b/native/chat/chat-input-bar.react.js
@@ -38,9 +38,11 @@
import { useEditMessage } from 'lib/shared/edit-messages-utils.js';
import {
getMentionTypeaheadUserSuggestions,
+ getMentionTypeaheadChatSuggestions,
getTypeaheadRegexMatches,
type Selection,
getUserMentionsCandidates,
+ encodeChatMentionText,
} from 'lib/shared/mention-utils.js';
import {
localIDPrefix,
@@ -58,6 +60,7 @@
checkIfDefaultMembersAreVoiced,
draftKeyFromThreadID,
useThreadChatMentionCandidates,
+ useThreadChatMentionSearchIndex,
} from 'lib/shared/thread-utils.js';
import { stringForUserExplicit } from 'lib/shared/user-utils.js';
import type { CalendarQuery } from 'lib/types/entry-types.js';
@@ -70,11 +73,13 @@
} from 'lib/types/message-types.js';
import type { Dispatch } from 'lib/types/redux-types.js';
import { threadPermissions } from 'lib/types/thread-permission-types.js';
-import {
- type ThreadInfo,
- type ClientThreadJoinRequest,
- type ThreadJoinPayload,
- type RelativeMemberInfo,
+import type {
+ ThreadInfo,
+ ResolvedThreadInfo,
+ ClientThreadJoinRequest,
+ ThreadJoinPayload,
+ RelativeMemberInfo,
+ ChatMentionCandidates,
} from 'lib/types/thread-types.js';
import { type UserInfos } from 'lib/types/user-types.js';
import {
@@ -91,6 +96,7 @@
} from './message-editing-context.react.js';
import type { RemoveEditMode } from './message-list-types.js';
import TypeaheadTooltip from './typeahead-tooltip.react.js';
+import ThreadAvatar from '../avatars/thread-avatar.react.js';
import UserAvatar from '../avatars/user-avatar.react.js';
import Button from '../components/button.react.js';
// eslint-disable-next-line import/extensions
@@ -171,6 +177,8 @@
+inputState: ?InputState,
+userSearchIndex: SentencePrefixSearchIndex,
+userMentionsCandidates: $ReadOnlyArray<RelativeMemberInfo>,
+ +chatMentionSearchIndex: SentencePrefixSearchIndex,
+ +chatMentionCandidates: ChatMentionCandidates,
+parentThreadInfo: ?ThreadInfo,
+editedMessagePreview: ?MessagePreviewResult,
+editedMessageInfo: ?MessageInfo,
@@ -498,20 +506,37 @@
}
typeaheadTooltipButton = ({ item, suggestionText, styles }) => {
+ let text = suggestionText;
+ let avatarComponent = null;
+ if (item.type === 'user') {
+ text = suggestionText;
+ avatarComponent = <UserAvatar size="small" userID={item.user.id} />;
+ } else if (item.type === 'chat') {
+ text = `@${item.chat.uiName}`;
+ avatarComponent = <ThreadAvatar size="small" threadInfo={item.chat} />;
+ }
return (
<>
- <UserAvatar size="small" userID={item.id} />
+ {avatarComponent}
<Text style={styles.buttonLabel} numberOfLines={1}>
- {suggestionText}
+ {text}
</Text>
</>
);
};
typeaheadTooltipSuggestionTextExtractor = (
- item: RelativeMemberInfo,
+ item:
+ | { type: 'user', user: RelativeMemberInfo }
+ | { type: 'chat', chat: ResolvedThreadInfo },
): string => {
- return `@${stringForUserExplicit(item)}`;
+ if (item.type === 'user') {
+ return `@${stringForUserExplicit(item.user)}`;
+ }
+ if (item.type === 'chat') {
+ return `@[[${item.chat.id}:${encodeChatMentionText(item.chat.uiName)}]]`;
+ }
+ return '';
};
render() {
@@ -580,13 +605,22 @@
this.props.viewerID,
typeaheadMatchedStrings.textPrefix,
);
+ const suggestedChats = getMentionTypeaheadChatSuggestions(
+ this.props.chatMentionSearchIndex,
+ this.props.chatMentionCandidates,
+ typeaheadMatchedStrings.textPrefix,
+ );
+ const suggestions = [
+ ...suggestedUsers.map(user => ({ type: 'user', user })),
+ ...suggestedChats.map(chat => ({ type: 'chat', chat })),
+ ];
- if (suggestedUsers.length > 0) {
+ if (suggestions.length > 0) {
typeaheadTooltip = (
<TypeaheadTooltip
text={this.state.text}
matchedStrings={typeaheadMatchedStrings}
- suggestions={suggestedUsers}
+ suggestions={suggestions}
focusAndUpdateTextAndSelection={this.focusAndUpdateTextAndSelection}
typeaheadButtonRenderer={this.typeaheadTooltipButton}
suggestionTextExtractor={
@@ -1272,6 +1306,10 @@
const userSearchIndex = useSelector(userStoreMentionSearchIndex);
+ const chatMentionSearchIndex = useThreadChatMentionSearchIndex(
+ props.threadInfo,
+ );
+
const { parentThreadID } = props.threadInfo;
const parentThreadInfo = useSelector(state =>
parentThreadID ? threadInfoSelector(state)[parentThreadID] : null,
@@ -1316,6 +1354,8 @@
inputState={inputState}
userSearchIndex={userSearchIndex}
userMentionsCandidates={userMentionsCandidates}
+ chatMentionSearchIndex={chatMentionSearchIndex}
+ chatMentionCandidates={chatMentionCandidates}
parentThreadInfo={parentThreadInfo}
editedMessagePreview={editedMessagePreview}
editedMessageInfo={editedMessageInfo}
diff --git a/native/utils/typeahead-utils.js b/native/utils/typeahead-utils.js
--- a/native/utils/typeahead-utils.js
+++ b/native/utils/typeahead-utils.js
@@ -1,11 +1,12 @@
// @flow
import { oldValidUsernameRegexString } from 'lib/shared/account-utils.js';
+import { validChatNameRegexString } from 'lib/shared/thread-utils.js';
// Native regex is a little bit different than web one as
// there are no named capturing groups yet on native.
const nativeMentionTypeaheadRegex: RegExp = new RegExp(
- `((^(.|\n)*\\s+)|^)@(${oldValidUsernameRegexString})?$`,
+ `((^(.|\n)*\\s+)|^)@(${oldValidUsernameRegexString}|${validChatNameRegexString})?$`,
);
export { nativeMentionTypeaheadRegex };
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Oct 22, 10:35 PM (21 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2344758
Default Alt Text
D8903.diff (8 KB)
Attached To
Mode
D8903: [native] Show chats in mention typeahead tooltip
Attached
Detach File
Event Timeline
Log In to Comment