import React, { FC, useMemo, memo, useRef, } from '../../lib/teact/teact'; import { getDispatch, getGlobal, withGlobal } from '../../lib/teact/teactn'; import { ApiMessage, ApiUser, ApiChat } from '../../api/types'; import { selectUser, selectChatMessages, selectChat, selectCurrentTextSearch, } from '../../modules/selectors'; import { getMessageSummaryText, getChatTitle, getUserFullName, isChatChannel, } from '../../modules/helpers'; import renderText from '../common/helpers/renderText'; import useLang from '../../hooks/useLang'; import { orderBy } from '../../util/iteratees'; import { MEMO_EMPTY_ARRAY } from '../../util/memo'; import useKeyboardListNavigation from '../../hooks/useKeyboardListNavigation'; import useHistoryBack from '../../hooks/useHistoryBack'; import InfiniteScroll from '../ui/InfiniteScroll'; import ListItem from '../ui/ListItem'; import LastMessageMeta from '../common/LastMessageMeta'; import Avatar from '../common/Avatar'; import './RightSearch.scss'; export type OwnProps = { chatId: string; threadId: number; onClose: NoneToVoidFunction; isActive: boolean; }; type StateProps = { chat?: ApiChat; messagesById?: Record; query?: string; totalCount?: number; foundIds?: number[]; }; interface Result { message: ApiMessage; senderUser?: ApiUser; senderChat?: ApiChat; onClick: NoneToVoidFunction; } const RightSearch: FC = ({ chatId, threadId, onClose, isActive, chat, messagesById, query, totalCount, foundIds, }) => { const { searchTextMessagesLocal, focusMessage, } = getDispatch(); const lang = useLang(); const foundResults = useMemo(() => { if (!query || !foundIds || !foundIds.length || !messagesById) { return MEMO_EMPTY_ARRAY; } const results = foundIds.map((id) => { const message = messagesById[id]; if (!message) { return undefined; } const senderUser = message.senderId ? selectUser(getGlobal(), message.senderId) : undefined; let senderChat; if (chat && isChatChannel(chat)) { senderChat = chat; } else if (message.forwardInfo) { const { isChannelPost, fromChatId } = message.forwardInfo; senderChat = isChannelPost && fromChatId ? selectChat(getGlobal(), fromChatId) : undefined; } else { senderChat = message.senderId ? selectChat(getGlobal(), message.senderId) : undefined; } return { message, senderUser, senderChat, onClick: () => focusMessage({ chatId, threadId, messageId: id }), }; }).filter(Boolean) as Result[]; return orderBy(results, ({ message }) => message.date, 'desc'); }, [chatId, threadId, focusMessage, foundIds, chat, messagesById, query]); const renderSearchResult = ({ message, senderUser, senderChat, onClick, }: Result) => { const title = senderChat ? getChatTitle(lang, senderChat) : getUserFullName(senderUser); const text = getMessageSummaryText(lang, message); return (

{title && renderText(title)}

{renderText(text, ['emoji', 'highlight'], { highlight: query })}
); }; useHistoryBack(isActive, onClose); // eslint-disable-next-line no-null/no-null const containerRef = useRef(null); const handleKeyDown = useKeyboardListNavigation(containerRef, true, (index) => { const foundResult = foundResults?.[index === -1 ? 0 : index]; if (foundResult) { foundResult.onClick(); } }, '.ListItem-button', true); return (

{!query ? ( lang('lng_dlg_search_for_messages') ) : (totalCount === 0 || !foundResults.length) ? ( lang('lng_search_no_results') ) : totalCount === 1 ? ( '1 message found' ) : ( `${(foundResults.length && (totalCount || foundResults.length))} messages found` )}

{foundResults.map(renderSearchResult)}
); }; export default memo(withGlobal( (global, { chatId }): StateProps => { const chat = selectChat(global, chatId); const messagesById = chat && selectChatMessages(global, chat.id); if (!chat || !messagesById) { return {}; } const { query, results } = selectCurrentTextSearch(global) || {}; const { totalCount, foundIds } = results || {}; return { chat, messagesById, query, totalCount, foundIds, }; }, )(RightSearch));