diff --git a/lib/components/chat-mention-context.react.js b/lib/components/chat-mention-context.react.js new file mode 100644 --- /dev/null +++ b/lib/components/chat-mention-context.react.js @@ -0,0 +1,111 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; + +import genesis from '../facts/genesis.js'; +import SentencePrefixSearchIndex from '../shared/sentence-prefix-search-index.js'; +import { + useChatMentionCandidatesObjAndUtils, + useChatMentionSearchIndex, +} from '../shared/thread-utils.js'; +import type { + ChatMentionCandidates, + ChatMentionCandidatesObj, + ThreadInfo, +} from '../types/thread-types.js'; + +type Props = { + +children: React.Node, +}; +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 ChatMentionProvider(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 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(() => { + 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, + ]); +} + +export { + ChatMentionProvider, + useChatMentionContext, + useThreadChatMentionCandidates, +}; 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 @@ -1763,37 +1763,6 @@ }; } -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,10 +1821,11 @@ ]); } -function useChatMentionSearchIndex(): { +function useChatMentionSearchIndex( + chatMentionCandidatesObj: ChatMentionCandidatesObj, +): { +[id: string]: SentencePrefixSearchIndex, } { - const { chatMentionCandidatesObj } = useChatMentionCandidatesObjAndUtils(); return React.useMemo(() => { const result = {}; for (const communityThreadID in chatMentionCandidatesObj) { @@ -1880,22 +1850,6 @@ }, [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 +1915,7 @@ useRoleMemberCountsForCommunity, useRoleUserSurfacedPermissions, getThreadsToDeleteText, - useChatMentionCandidatesObj, - useThreadChatMentionCandidates, useUserProfileThreadInfo, - useThreadChatMentionSearchIndex, + useChatMentionCandidatesObjAndUtils, + useChatMentionSearchIndex, }; 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/components/chat-mention-context.react.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/components/chat-mention-context.react.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/components/chat-mention-context.react.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 @@ -3,8 +3,8 @@ import * as React from 'react'; import { View, StyleSheet } from 'react-native'; +import { useThreadChatMentionCandidates } from 'lib/components/chat-mention-context.react.js'; import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; -import { useThreadChatMentionCandidates } from 'lib/shared/thread-utils.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 @@ -3,12 +3,12 @@ import invariant from 'invariant'; import * as React from 'react'; +import { useThreadChatMentionCandidates } from 'lib/components/chat-mention-context.react.js'; import { ENSCacheContext } from 'lib/components/ens-cache-provider.react.js'; import { useLoggedInUserInfo } from 'lib/hooks/account-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 @@ -4,13 +4,11 @@ import * as React from 'react'; import Animated from 'react-native-reanimated'; +import { useThreadChatMentionCandidates } from 'lib/components/chat-mention-context.react.js'; import { useLoggedInUserInfo } from 'lib/hooks/account-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 { ChatMentionProvider } from 'lib/components/chat-mention-context.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/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 @@ -9,6 +9,10 @@ joinThread, newThreadActionTypes, } from 'lib/actions/thread-actions.js'; +import { + useChatMentionContext, + useThreadChatMentionCandidates, +} from 'lib/components/chat-mention-context.react.js'; import SWMansionIcon from 'lib/components/SWMansionIcon.react.js'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors.js'; import { threadInfoSelector } from 'lib/selectors/thread-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/components/chat-mention-context.react.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/components/chat-mention-context.react.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/components/chat-mention-context.react.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 @@ -3,10 +3,10 @@ import classNames from 'classnames'; import * as React from 'react'; +import { useThreadChatMentionCandidates } from 'lib/components/chat-mention-context.react.js'; import { useModalContext } from 'lib/components/modal-provider.react.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 @@ -3,10 +3,10 @@ import classNames from 'classnames'; import * as React from 'react'; +import { useThreadChatMentionCandidates } from 'lib/components/chat-mention-context.react.js'; import { useModalContext } from 'lib/components/modal-provider.react.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 @@ -5,12 +5,12 @@ import { useDispatch } from 'react-redux'; import { createSelector } from 'reselect'; +import { useThreadChatMentionCandidates } from 'lib/components/chat-mention-context.react.js'; import { ENSCacheContext } from 'lib/components/ens-cache-provider.react.js'; import { useLoggedInUserInfo } from 'lib/hooks/account-hooks.js'; import { createPendingSidebar, threadInHomeChatList, - useThreadChatMentionCandidates, } from 'lib/shared/thread-utils.js'; import type { ComposableMessageInfo,