import type { FC } from '../../../lib/teact/teact'; import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import type { ApiChat, ApiMessage } from '../../../api/types'; import { LoadMoreDirection } from '../../../types'; import { selectTabState } from '../../../global/selectors'; import { parseSearchResultKey, type SearchResultKey } from '../../../util/keys/searchResultKey'; import { MEMO_EMPTY_ARRAY } from '../../../util/memo'; import { throttle } from '../../../util/schedulers'; import { renderMessageSummary } from '../../common/helpers/renderMessageText'; import useAppLayout from '../../../hooks/useAppLayout'; import useOldLang from '../../../hooks/useOldLang'; import NothingFound from '../../common/NothingFound'; import InfiniteScroll from '../../ui/InfiniteScroll'; import ChatMessage from './ChatMessage'; import DateSuggest from './DateSuggest'; import LeftSearchResultTopic from './LeftSearchResultTopic'; export type OwnProps = { searchQuery?: string; dateSearchQuery?: string; onReset: () => void; onSearchDateSelect: (value: Date) => void; }; type StateProps = { currentUserId?: string; foundIds?: SearchResultKey[]; globalMessagesByChatId?: Record }>; chatsById: Record; fetchingStatus?: { chats?: boolean; messages?: boolean }; foundTopicIds?: number[]; searchChatId?: string; }; const runThrottled = throttle((cb) => cb(), 500, true); const ChatMessageResults: FC = ({ searchQuery, dateSearchQuery, foundIds, globalMessagesByChatId, chatsById, fetchingStatus, foundTopicIds, searchChatId, onSearchDateSelect, onReset, }) => { const { searchMessagesGlobal, openThread } = getActions(); const lang = useOldLang(); const { isMobile } = useAppLayout(); const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => { if (direction === LoadMoreDirection.Backwards) { runThrottled(() => { searchMessagesGlobal({ type: 'text', }); }); } // eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- `searchQuery` is required to prevent infinite message loading }, [searchQuery]); const handleTopicClick = useCallback( (id: number) => { if (!searchChatId) return; openThread({ chatId: searchChatId, threadId: id, shouldReplaceHistory: true }); if (!isMobile) { onReset(); } }, [searchChatId, isMobile, onReset], ); const foundMessages = useMemo(() => { if (!foundIds || foundIds.length === 0) { return MEMO_EMPTY_ARRAY; } return foundIds .map((id) => { const [chatId, messageId] = parseSearchResultKey(id); return globalMessagesByChatId?.[chatId]?.byId[messageId]; }) .filter(Boolean) .sort((a, b) => b.date - a.date); }, [foundIds, globalMessagesByChatId]); function renderFoundMessage(message: ApiMessage) { const text = renderMessageSummary(lang, message); const chat = chatsById[message.chatId]; if (!text || !chat) { return undefined; } return ( ); } const nothingFound = fetchingStatus && !fetchingStatus.chats && !fetchingStatus.messages && !foundMessages.length && !foundTopicIds?.length; return (
{dateSearchQuery && (
)} {nothingFound && ( )} {Boolean(foundTopicIds?.length) && (

{lang('Topics')}

{foundTopicIds!.map((id) => { return ( ); })}
)} {Boolean(foundMessages.length) && (

{lang('SearchMessages')}

{foundMessages.map(renderFoundMessage)}
)}
); }; export default memo(withGlobal( (global): StateProps => { const { byId: chatsById } = global.chats; const { currentUserId, messages: { byChatId: globalMessagesByChatId } } = global; const { fetchingStatus, resultsByType, foundTopicIds, chatId: searchChatId, } = selectTabState(global).globalSearch; const { foundIds } = (resultsByType?.text) || {}; return { currentUserId, foundIds, globalMessagesByChatId, chatsById, fetchingStatus, foundTopicIds, searchChatId, }; }, )(ChatMessageResults));