diff --git a/lib/components/chat-mention-provider.react.js b/lib/components/chat-mention-provider.react.js new file mode 100644 --- /dev/null +++ b/lib/components/chat-mention-provider.react.js @@ -0,0 +1,227 @@ +// @flow + +import * as React from 'react'; + +import genesis from '../facts/genesis.js'; +import { threadInfoSelector } from '../selectors/thread-selectors.js'; +import SentencePrefixSearchIndex from '../shared/sentence-prefix-search-index.js'; +import { threadTypes } from '../types/thread-types-enum.js'; +import type { + ChatMentionCandidates, + ChatMentionCandidatesObj, + ResolvedThreadInfo, + ThreadInfo, +} from '../types/thread-types.js'; +import { useResolvedThreadInfosObj } from '../utils/entity-helpers.js'; +import { useSelector } from '../utils/redux-utils.js'; + +type Props = { + +children: React.Node, +}; +export type ChatMentionContextType = { + +getChatMentionSearchIndex: ( + threadInfo: ThreadInfo, + ) => SentencePrefixSearchIndex, + +communityThreadIDForGenesisThreads: { +[id: string]: string }, + +chatMentionCandidatesObj: ChatMentionCandidatesObj, +}; + +const emptySearchIndex = new SentencePrefixSearchIndex(); +const ChatMentionContext: React.Context = + React.createContext({ + getChatMentionSearchIndex: () => emptySearchIndex, + communityThreadIDForGenesisThreads: {}, + chatMentionCandidatesObj: {}, + }); + +function ChatMentionContextProvider(props: Props): React.Node { + const { children } = props; + + const { communityThreadIDForGenesisThreads, chatMentionCandidatesObj } = + useChatMentionCandidatesObjAndUtils(); + const searchIndices = useChatMentionSearchIndex(chatMentionCandidatesObj); + + const getChatMentionSearchIndex = React.useCallback( + (threadInfo: ThreadInfo) => { + if (threadInfo.community === genesis.id) { + return searchIndices[communityThreadIDForGenesisThreads[threadInfo.id]]; + } + return searchIndices[threadInfo.community ?? threadInfo.id]; + }, + [communityThreadIDForGenesisThreads, searchIndices], + ); + + const value = React.useMemo( + () => ({ + getChatMentionSearchIndex, + communityThreadIDForGenesisThreads, + chatMentionCandidatesObj, + }), + [ + getChatMentionSearchIndex, + communityThreadIDForGenesisThreads, + chatMentionCandidatesObj, + ], + ); + + return ( + + {children} + + ); +} + +function getChatMentionCandidates(threadInfos: { + +[id: string]: ResolvedThreadInfo, +}): { + chatMentionCandidatesObj: ChatMentionCandidatesObj, + communityThreadIDForGenesisThreads: { +[id: string]: string }, +} { + const result = {}; + const visitedGenesisThreads = new Set(); + const communityThreadIDForGenesisThreads = {}; + for (const currentThreadID in threadInfos) { + const currentThreadInfo = threadInfos[currentThreadID]; + const { community: currentThreadCommunity } = currentThreadInfo; + if (!currentThreadCommunity) { + if (!result[currentThreadID]) { + result[currentThreadID] = { [currentThreadID]: currentThreadInfo }; + } + continue; + } + if (!result[currentThreadCommunity]) { + result[currentThreadCommunity] = { + [currentThreadCommunity]: threadInfos[currentThreadCommunity], + }; + } + // Handle GENESIS community case: mentioning inside GENESIS should only + // show chats and threads inside the top level that is below GENESIS. + if (threadInfos[currentThreadCommunity].type === threadTypes.GENESIS) { + if (visitedGenesisThreads.has(currentThreadID)) { + continue; + } + const threadTraversePath = [currentThreadInfo]; + visitedGenesisThreads.add(currentThreadID); + let currentlySelectedThreadID = currentThreadInfo.parentThreadID; + while (currentlySelectedThreadID) { + const currentlySelectedThreadInfo = + threadInfos[currentlySelectedThreadID]; + if ( + visitedGenesisThreads.has(currentlySelectedThreadID) || + !currentlySelectedThreadInfo || + currentlySelectedThreadInfo.type === threadTypes.GENESIS + ) { + break; + } + threadTraversePath.push(currentlySelectedThreadInfo); + visitedGenesisThreads.add(currentlySelectedThreadID); + currentlySelectedThreadID = currentlySelectedThreadInfo.parentThreadID; + } + const lastThreadInTraversePath = + threadTraversePath[threadTraversePath.length - 1]; + let lastThreadInTraversePathParentID; + if (lastThreadInTraversePath.parentThreadID) { + lastThreadInTraversePathParentID = threadInfos[ + lastThreadInTraversePath.parentThreadID + ] + ? lastThreadInTraversePath.parentThreadID + : lastThreadInTraversePath.id; + } else { + lastThreadInTraversePathParentID = lastThreadInTraversePath.id; + } + if ( + threadInfos[lastThreadInTraversePathParentID].type === + threadTypes.GENESIS + ) { + if (!result[lastThreadInTraversePath.id]) { + result[lastThreadInTraversePath.id] = {}; + } + for (const threadInfo of threadTraversePath) { + result[lastThreadInTraversePath.id][threadInfo.id] = threadInfo; + communityThreadIDForGenesisThreads[threadInfo.id] = + lastThreadInTraversePath.id; + } + if ( + lastThreadInTraversePath.type !== threadTypes.PERSONAL && + lastThreadInTraversePath.type !== threadTypes.PRIVATE + ) { + result[genesis.id][lastThreadInTraversePath.id] = + lastThreadInTraversePath; + } + } else { + if ( + !communityThreadIDForGenesisThreads[lastThreadInTraversePathParentID] + ) { + result[lastThreadInTraversePathParentID] = {}; + communityThreadIDForGenesisThreads[lastThreadInTraversePathParentID] = + lastThreadInTraversePathParentID; + } + const lastThreadInTraversePathParentCommunityThreadID = + communityThreadIDForGenesisThreads[lastThreadInTraversePathParentID]; + for (const threadInfo of threadTraversePath) { + result[lastThreadInTraversePathParentCommunityThreadID][ + threadInfo.id + ] = threadInfo; + communityThreadIDForGenesisThreads[threadInfo.id] = + lastThreadInTraversePathParentCommunityThreadID; + } + } + continue; + } + result[currentThreadCommunity][currentThreadID] = currentThreadInfo; + } + return { + chatMentionCandidatesObj: result, + communityThreadIDForGenesisThreads, + }; +} + +function useChatMentionCandidatesObjAndUtils(): { + chatMentionCandidatesObj: ChatMentionCandidatesObj, + resolvedThreadInfos: ChatMentionCandidates, + communityThreadIDForGenesisThreads: { +[id: string]: string }, +} { + const threadInfos = useSelector(threadInfoSelector); + const resolvedThreadInfos = useResolvedThreadInfosObj(threadInfos); + const { chatMentionCandidatesObj, communityThreadIDForGenesisThreads } = + React.useMemo( + () => getChatMentionCandidates(resolvedThreadInfos), + [resolvedThreadInfos], + ); + return { + chatMentionCandidatesObj, + resolvedThreadInfos, + communityThreadIDForGenesisThreads, + }; +} + +function useChatMentionSearchIndex( + chatMentionCandidatesObj: ChatMentionCandidatesObj, +): { + +[id: string]: SentencePrefixSearchIndex, +} { + return React.useMemo(() => { + const result = {}; + for (const communityThreadID in chatMentionCandidatesObj) { + const searchIndex = new SentencePrefixSearchIndex(); + const searchIndexEntries = []; + for (const threadID in chatMentionCandidatesObj[communityThreadID]) { + searchIndexEntries.push({ + id: threadID, + uiName: chatMentionCandidatesObj[communityThreadID][threadID].uiName, + }); + } + // Sort the keys so that the order of the search result is consistent + searchIndexEntries.sort(({ uiName: uiNameA }, { uiName: uiNameB }) => + uiNameA.localeCompare(uiNameB), + ); + for (const { id, uiName } of searchIndexEntries) { + searchIndex.addEntry(id, uiName); + } + result[communityThreadID] = searchIndex; + } + return result; + }, [chatMentionCandidatesObj]); +} + +export { ChatMentionContextProvider, ChatMentionContext }; diff --git a/lib/hooks/chat-mention-hooks.js b/lib/hooks/chat-mention-hooks.js new file mode 100644 --- /dev/null +++ b/lib/hooks/chat-mention-hooks.js @@ -0,0 +1,47 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; + +import { + ChatMentionContext, + type ChatMentionContextType, +} from '../components/chat-mention-provider.react.js'; +import genesis from '../facts/genesis.js'; +import type { + ChatMentionCandidates, + ThreadInfo, +} from '../types/thread-types.js'; + +function useChatMentionContext(): ChatMentionContextType { + const context = React.useContext(ChatMentionContext); + invariant(context, 'ChatMentionContext not found'); + + return context; +} + +function useThreadChatMentionCandidates( + threadInfo: ThreadInfo, +): ChatMentionCandidates { + const { communityThreadIDForGenesisThreads, chatMentionCandidatesObj } = + useChatMentionContext(); + return React.useMemo(() => { + const communityID = + threadInfo.community === genesis.id + ? communityThreadIDForGenesisThreads[threadInfo.id] + : threadInfo.community ?? threadInfo.id; + const allChatsWithinCommunity = chatMentionCandidatesObj[communityID]; + if (!allChatsWithinCommunity) { + return {}; + } + const { [threadInfo.id]: _, ...result } = allChatsWithinCommunity; + return result; + }, [ + chatMentionCandidatesObj, + communityThreadIDForGenesisThreads, + threadInfo.community, + threadInfo.id, + ]); +} + +export { useThreadChatMentionCandidates, useChatMentionContext }; 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 @@ -45,7 +45,6 @@ usersWithPersonalThreadSelector, } from '../selectors/user-selectors.js'; import { getUserAvatarForThread } from '../shared/avatar-utils.js'; -import SentencePrefixSearchIndex from '../shared/sentence-prefix-search-index.js'; import type { CalendarQuery } from '../types/entry-types.js'; import { messageTypes } from '../types/message-types-enum.js'; import { @@ -79,9 +78,6 @@ type ClientNewThreadRequest, type NewThreadResult, type ChangeThreadSettingsPayload, - type ResolvedThreadInfo, - type ChatMentionCandidatesObj, - type ChatMentionCandidates, type UserProfileThreadInfo, } from '../types/thread-types.js'; import { updateTypes } from '../types/update-types-enum.js'; @@ -99,7 +95,6 @@ } from '../utils/action-utils.js'; import type { DispatchActionPromise } from '../utils/action-utils.js'; import type { GetENSNames } from '../utils/ens-helpers.js'; -import { useResolvedThreadInfosObj } from '../utils/entity-helpers.js'; import { ET, entityTextToRawString, @@ -1639,161 +1634,6 @@ } within this ${communityOrThreadNoun(threadInfo)}`; } -function getChatMentionCandidates(threadInfos: { - +[id: string]: ResolvedThreadInfo, -}): { - chatMentionCandidatesObj: ChatMentionCandidatesObj, - communityThreadIDForGenesisThreads: { +[id: string]: string }, -} { - const result = {}; - const visitedGenesisThreads = new Set(); - const communityThreadIDForGenesisThreads = {}; - for (const currentThreadID in threadInfos) { - const currentThreadInfo = threadInfos[currentThreadID]; - const { community: currentThreadCommunity } = currentThreadInfo; - if (!currentThreadCommunity) { - if (!result[currentThreadID]) { - result[currentThreadID] = { [currentThreadID]: currentThreadInfo }; - } - continue; - } - if (!result[currentThreadCommunity]) { - result[currentThreadCommunity] = { - [currentThreadCommunity]: threadInfos[currentThreadCommunity], - }; - } - // Handle GENESIS community case: mentioning inside GENESIS should only - // show chats and threads inside the top level that is below GENESIS. - if (threadInfos[currentThreadCommunity].type === threadTypes.GENESIS) { - if (visitedGenesisThreads.has(currentThreadID)) { - continue; - } - const threadTraversePath = [currentThreadInfo]; - visitedGenesisThreads.add(currentThreadID); - let currentlySelectedThreadID = currentThreadInfo.parentThreadID; - while (currentlySelectedThreadID) { - const currentlySelectedThreadInfo = - threadInfos[currentlySelectedThreadID]; - if ( - visitedGenesisThreads.has(currentlySelectedThreadID) || - !currentlySelectedThreadInfo || - currentlySelectedThreadInfo.type === threadTypes.GENESIS - ) { - break; - } - threadTraversePath.push(currentlySelectedThreadInfo); - visitedGenesisThreads.add(currentlySelectedThreadID); - currentlySelectedThreadID = currentlySelectedThreadInfo.parentThreadID; - } - const lastThreadInTraversePath = - threadTraversePath[threadTraversePath.length - 1]; - let lastThreadInTraversePathParentID; - if (lastThreadInTraversePath.parentThreadID) { - lastThreadInTraversePathParentID = threadInfos[ - lastThreadInTraversePath.parentThreadID - ] - ? lastThreadInTraversePath.parentThreadID - : lastThreadInTraversePath.id; - } else { - lastThreadInTraversePathParentID = lastThreadInTraversePath.id; - } - if ( - threadInfos[lastThreadInTraversePathParentID].type === - threadTypes.GENESIS - ) { - if (!result[lastThreadInTraversePath.id]) { - result[lastThreadInTraversePath.id] = {}; - } - for (const threadInfo of threadTraversePath) { - result[lastThreadInTraversePath.id][threadInfo.id] = threadInfo; - communityThreadIDForGenesisThreads[threadInfo.id] = - lastThreadInTraversePath.id; - } - if ( - lastThreadInTraversePath.type !== threadTypes.PERSONAL && - lastThreadInTraversePath.type !== threadTypes.PRIVATE - ) { - result[genesis.id][lastThreadInTraversePath.id] = - lastThreadInTraversePath; - } - } else { - if ( - !communityThreadIDForGenesisThreads[lastThreadInTraversePathParentID] - ) { - result[lastThreadInTraversePathParentID] = {}; - communityThreadIDForGenesisThreads[lastThreadInTraversePathParentID] = - lastThreadInTraversePathParentID; - } - const lastThreadInTraversePathParentCommunityThreadID = - communityThreadIDForGenesisThreads[lastThreadInTraversePathParentID]; - for (const threadInfo of threadTraversePath) { - result[lastThreadInTraversePathParentCommunityThreadID][ - threadInfo.id - ] = threadInfo; - communityThreadIDForGenesisThreads[threadInfo.id] = - lastThreadInTraversePathParentCommunityThreadID; - } - } - continue; - } - result[currentThreadCommunity][currentThreadID] = currentThreadInfo; - } - return { - chatMentionCandidatesObj: result, - communityThreadIDForGenesisThreads, - }; -} - -function useChatMentionCandidatesObjAndUtils(): { - chatMentionCandidatesObj: ChatMentionCandidatesObj, - resolvedThreadInfos: ChatMentionCandidates, - communityThreadIDForGenesisThreads: { +[id: string]: string }, -} { - const threadInfos = useSelector(threadInfoSelector); - const resolvedThreadInfos = useResolvedThreadInfosObj(threadInfos); - const { chatMentionCandidatesObj, communityThreadIDForGenesisThreads } = - React.useMemo( - () => getChatMentionCandidates(resolvedThreadInfos), - [resolvedThreadInfos], - ); - return { - chatMentionCandidatesObj, - resolvedThreadInfos, - communityThreadIDForGenesisThreads, - }; -} - -function useChatMentionCandidatesObj(): ChatMentionCandidatesObj { - const { chatMentionCandidatesObj } = useChatMentionCandidatesObjAndUtils(); - return chatMentionCandidatesObj; -} - -function useThreadChatMentionCandidates( - threadInfo: ThreadInfo, -): ChatMentionCandidates { - const { chatMentionCandidatesObj, communityThreadIDForGenesisThreads } = - useChatMentionCandidatesObjAndUtils(); - return React.useMemo(() => { - let communityID, - result = {}; - if (threadInfo.community === genesis.id) { - communityID = communityThreadIDForGenesisThreads[threadInfo.id]; - } else { - communityID = threadInfo.community ?? threadInfo.id; - } - if (chatMentionCandidatesObj[communityID]) { - result = { ...chatMentionCandidatesObj[communityID] }; - } - delete result[threadInfo.id]; - return result; - }, [ - chatMentionCandidatesObj, - communityThreadIDForGenesisThreads, - threadInfo.community, - threadInfo.id, - ]); -} - function useUserProfileThreadInfo(userInfo: ?UserInfo): ?UserProfileThreadInfo { const userID = userInfo?.id; const username = userInfo?.username; @@ -1852,50 +1692,6 @@ ]); } -function useChatMentionSearchIndex(): { - +[id: string]: SentencePrefixSearchIndex, -} { - const { chatMentionCandidatesObj } = useChatMentionCandidatesObjAndUtils(); - return React.useMemo(() => { - const result = {}; - for (const communityThreadID in chatMentionCandidatesObj) { - const searchIndex = new SentencePrefixSearchIndex(); - const searchIndexEntries = []; - for (const threadID in chatMentionCandidatesObj[communityThreadID]) { - searchIndexEntries.push({ - id: threadID, - uiName: chatMentionCandidatesObj[communityThreadID][threadID].uiName, - }); - } - // Sort the keys so that the order of the search result is consistent - searchIndexEntries.sort(({ uiName: uiNameA }, { uiName: uiNameB }) => - uiNameA.localeCompare(uiNameB), - ); - for (const { id, uiName } of searchIndexEntries) { - searchIndex.addEntry(id, uiName); - } - result[communityThreadID] = searchIndex; - } - return result; - }, [chatMentionCandidatesObj]); -} - -function useThreadChatMentionSearchIndex( - threadInfo: ThreadInfo, -): SentencePrefixSearchIndex { - const chatMentionCandidatesSearchIndex = useChatMentionSearchIndex(); - const { communityThreadIDForGenesisThreads } = - useChatMentionCandidatesObjAndUtils(); - if (threadInfo.community === genesis.id) { - return chatMentionCandidatesSearchIndex[ - communityThreadIDForGenesisThreads[threadInfo.id] - ]; - } - return chatMentionCandidatesSearchIndex[ - threadInfo.community ?? threadInfo.id - ]; -} - export { threadHasPermission, viewerIsMember, @@ -1961,8 +1757,5 @@ useRoleMemberCountsForCommunity, useRoleUserSurfacedPermissions, getThreadsToDeleteText, - useChatMentionCandidatesObj, - useThreadChatMentionCandidates, useUserProfileThreadInfo, - useThreadChatMentionSearchIndex, }; 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 @@ -31,6 +31,10 @@ joinThread, newThreadActionTypes, } from 'lib/actions/thread-actions.js'; +import { + useChatMentionContext, + useThreadChatMentionCandidates, +} from 'lib/hooks/chat-mention-hooks.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; import { userStoreMentionSearchIndex } from 'lib/selectors/user-selectors.js'; @@ -58,8 +62,6 @@ threadActualMembers, checkIfDefaultMembersAreVoiced, draftKeyFromThreadID, - useThreadChatMentionCandidates, - useThreadChatMentionSearchIndex, } from 'lib/shared/thread-utils.js'; import type { CalendarQuery } from 'lib/types/entry-types.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; @@ -1266,9 +1268,8 @@ const userSearchIndex = useSelector(userStoreMentionSearchIndex); - const chatMentionSearchIndex = useThreadChatMentionSearchIndex( - props.threadInfo, - ); + const { getChatMentionSearchIndex } = useChatMentionContext(); + const chatMentionSearchIndex = getChatMentionSearchIndex(props.threadInfo); const { parentThreadID } = props.threadInfo; const parentThreadInfo = useSelector(state => diff --git a/native/chat/message-list-types.js b/native/chat/message-list-types.js --- a/native/chat/message-list-types.js +++ b/native/chat/message-list-types.js @@ -4,7 +4,7 @@ import invariant from 'invariant'; import * as React from 'react'; -import { useThreadChatMentionCandidates } from 'lib/shared/thread-utils.js'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; import { type UserInfo } from 'lib/types/user-types.js'; diff --git a/native/chat/message-preview.react.js b/native/chat/message-preview.react.js --- a/native/chat/message-preview.react.js +++ b/native/chat/message-preview.react.js @@ -4,8 +4,8 @@ import * as React from 'react'; import { Text } from 'react-native'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import { useMessagePreview } from 'lib/shared/message-utils.js'; -import { useThreadChatMentionCandidates } from 'lib/shared/thread-utils.js'; import { type MessageInfo } from 'lib/types/message-types.js'; import { type ThreadInfo } from 'lib/types/thread-types.js'; diff --git a/native/chat/sidebar-input-bar-height-measurer.react.js b/native/chat/sidebar-input-bar-height-measurer.react.js --- a/native/chat/sidebar-input-bar-height-measurer.react.js +++ b/native/chat/sidebar-input-bar-height-measurer.react.js @@ -4,7 +4,7 @@ import { View, StyleSheet } from 'react-native'; import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; -import { useThreadChatMentionCandidates } from 'lib/shared/thread-utils.js'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import { DummyChatInputBar } from './chat-input-bar.react.js'; import { useMessageListScreenWidth } from './composed-message-width.js'; diff --git a/native/chat/sidebar-navigation.js b/native/chat/sidebar-navigation.js --- a/native/chat/sidebar-navigation.js +++ b/native/chat/sidebar-navigation.js @@ -5,10 +5,10 @@ import { ENSCacheContext } from 'lib/components/ens-cache-provider.react.js'; import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import { createPendingSidebar, createUnresolvedPendingSidebar, - useThreadChatMentionCandidates, } from 'lib/shared/thread-utils.js'; import type { ThreadInfo, diff --git a/native/chat/utils.js b/native/chat/utils.js --- a/native/chat/utils.js +++ b/native/chat/utils.js @@ -5,12 +5,10 @@ import Animated from 'react-native-reanimated'; import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import { colorIsDark } from 'lib/shared/color-utils.js'; import { messageKey } from 'lib/shared/message-utils.js'; -import { - viewerIsMember, - useThreadChatMentionCandidates, -} from 'lib/shared/thread-utils.js'; +import { viewerIsMember } from 'lib/shared/thread-utils.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; import { clusterEndHeight } from './chat-constants.js'; diff --git a/native/root.react.js b/native/root.react.js --- a/native/root.react.js +++ b/native/root.react.js @@ -19,6 +19,7 @@ import { Provider } from 'react-redux'; import { PersistGate as ReduxPersistGate } from 'redux-persist/es/integration/react.js'; +import { ChatMentionContextProvider } from 'lib/components/chat-mention-provider.react.js'; import { EditUserAvatarProvider } from 'lib/components/edit-user-avatar-provider.react.js'; import { ENSCacheProvider } from 'lib/components/ens-cache-provider.react.js'; import IntegrityHandler from 'lib/components/integrity-handler.react.js'; @@ -259,7 +260,9 @@ - + + + diff --git a/web/app.react.js b/web/app.react.js --- a/web/app.react.js +++ b/web/app.react.js @@ -15,6 +15,7 @@ fetchEntriesActionTypes, updateCalendarQueryActionTypes, } from 'lib/actions/entry-actions.js'; +import { ChatMentionContextProvider } from 'lib/components/chat-mention-provider.react.js'; import { EditUserAvatarProvider } from 'lib/components/edit-user-avatar-provider.react.js'; import { ModalProvider, @@ -193,14 +194,16 @@ - - - - - - - - {content} + + + + + + + + + {content} + diff --git a/web/chat/chat-input-bar.react.js b/web/chat/chat-input-bar.react.js --- a/web/chat/chat-input-bar.react.js +++ b/web/chat/chat-input-bar.react.js @@ -10,6 +10,10 @@ newThreadActionTypes, } from 'lib/actions/thread-actions.js'; import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; +import { + useChatMentionContext, + useThreadChatMentionCandidates, +} from 'lib/hooks/chat-mention-hooks.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import { threadInfoSelector } from 'lib/selectors/thread-selectors.js'; import { userStoreMentionSearchIndex } from 'lib/selectors/user-selectors.js'; @@ -28,8 +32,6 @@ threadFrozenDueToViewerBlock, threadActualMembers, checkIfDefaultMembersAreVoiced, - useThreadChatMentionCandidates, - useThreadChatMentionSearchIndex, } from 'lib/shared/thread-utils.js'; import type { CalendarQuery } from 'lib/types/entry-types.js'; import type { LoadingStatus } from 'lib/types/loading-types.js'; @@ -581,9 +583,8 @@ const dispatchActionPromise = useDispatchActionPromise(); const callJoinThread = useServerCall(joinThread); const userSearchIndex = useSelector(userStoreMentionSearchIndex); - const chatMentionSearchIndex = useThreadChatMentionSearchIndex( - props.threadInfo, - ); + const { getChatMentionSearchIndex } = useChatMentionContext(); + const chatMentionSearchIndex = getChatMentionSearchIndex(props.threadInfo); const { parentThreadID } = props.threadInfo; const parentThreadInfo = useSelector(state => diff --git a/web/chat/chat-message-list.react.js b/web/chat/chat-message-list.react.js --- a/web/chat/chat-message-list.react.js +++ b/web/chat/chat-message-list.react.js @@ -12,6 +12,7 @@ fetchMostRecentMessagesActionTypes, fetchMostRecentMessages, } from 'lib/actions/message-actions.js'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import { useOldestMessageServerID } from 'lib/hooks/message-hooks.js'; import { registerFetchKey } from 'lib/reducers/loading-reducer.js'; import { @@ -19,10 +20,7 @@ useMessageListData, } from 'lib/selectors/chat-selectors.js'; import { messageKey } from 'lib/shared/message-utils.js'; -import { - threadIsPending, - useThreadChatMentionCandidates, -} from 'lib/shared/thread-utils.js'; +import { threadIsPending } from 'lib/shared/thread-utils.js'; import type { FetchMessageInfosPayload } from 'lib/types/message-types.js'; import { threadTypes } from 'lib/types/thread-types-enum.js'; import { type ThreadInfo } from 'lib/types/thread-types.js'; diff --git a/web/chat/message-preview.react.js b/web/chat/message-preview.react.js --- a/web/chat/message-preview.react.js +++ b/web/chat/message-preview.react.js @@ -4,8 +4,8 @@ import invariant from 'invariant'; import * as React from 'react'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import { useMessagePreview } from 'lib/shared/message-utils.js'; -import { useThreadChatMentionCandidates } from 'lib/shared/thread-utils.js'; import { type MessageInfo } from 'lib/types/message-types.js'; import { type ThreadInfo } from 'lib/types/thread-types.js'; diff --git a/web/components/message-result.react.js b/web/components/message-result.react.js --- a/web/components/message-result.react.js +++ b/web/components/message-result.react.js @@ -3,9 +3,9 @@ import classNames from 'classnames'; import * as React from 'react'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import { useStringForUser } from 'lib/hooks/ens-cache.js'; import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js'; -import { useThreadChatMentionCandidates } from 'lib/shared/thread-utils.js'; import type { ThreadInfo } from 'lib/types/thread-types.js'; import { longAbsoluteDate } from 'lib/utils/date-utils.js'; diff --git a/web/modals/threads/sidebars/sidebar.react.js b/web/modals/threads/sidebars/sidebar.react.js --- a/web/modals/threads/sidebars/sidebar.react.js +++ b/web/modals/threads/sidebars/sidebar.react.js @@ -4,9 +4,9 @@ import * as React from 'react'; import { useModalContext } from 'lib/components/modal-provider.react.js'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import type { ChatThreadItem } from 'lib/selectors/chat-selectors.js'; import { useMessagePreview } from 'lib/shared/message-utils.js'; -import { useThreadChatMentionCandidates } from 'lib/shared/thread-utils.js'; import { shortAbsoluteDate } from 'lib/utils/date-utils.js'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; diff --git a/web/modals/threads/subchannels/subchannel.react.js b/web/modals/threads/subchannels/subchannel.react.js --- a/web/modals/threads/subchannels/subchannel.react.js +++ b/web/modals/threads/subchannels/subchannel.react.js @@ -4,9 +4,9 @@ import * as React from 'react'; import { useModalContext } from 'lib/components/modal-provider.react.js'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import { type ChatThreadItem } from 'lib/selectors/chat-selectors.js'; import { useMessagePreview } from 'lib/shared/message-utils.js'; -import { useThreadChatMentionCandidates } from 'lib/shared/thread-utils.js'; import { shortAbsoluteDate } from 'lib/utils/date-utils.js'; import { useResolvedThreadInfo } from 'lib/utils/entity-helpers.js'; diff --git a/web/selectors/thread-selectors.js b/web/selectors/thread-selectors.js --- a/web/selectors/thread-selectors.js +++ b/web/selectors/thread-selectors.js @@ -7,10 +7,10 @@ import { ENSCacheContext } from 'lib/components/ens-cache-provider.react.js'; import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; +import { useThreadChatMentionCandidates } from 'lib/hooks/chat-mention-hooks.js'; import { createPendingSidebar, threadInHomeChatList, - useThreadChatMentionCandidates, } from 'lib/shared/thread-utils.js'; import type { ComposableMessageInfo,