2026-03-31 11:31:22 +02:00

162 lines
5.2 KiB
TypeScript

import type { FC } from '../../../lib/teact/teact';
import {
memo, useCallback, useMemo, useRef,
} from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
import type { ApiMessage } from '../../../api/types';
import type { StateProps } from './helpers/createMapStateToProps';
import { LoadMoreDirection } from '../../../types';
import { SLIDE_TRANSITION_DURATION } from '../../../config';
import { getIsDownloading, getMessageDocument } from '../../../global/helpers';
import { formatMonthAndYear, toYearMonth } from '../../../util/dates/oldDateFormat';
import { parseSearchResultKey } from '../../../util/keys/searchResultKey';
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
import { throttle } from '../../../util/schedulers';
import { createMapStateToProps } from './helpers/createMapStateToProps';
import { getSenderName } from './helpers/getSenderName';
import { useIntersectionObserver } from '../../../hooks/useIntersectionObserver';
import useOldLang from '../../../hooks/useOldLang';
import useAsyncRendering from '../../right/hooks/useAsyncRendering';
import Document from '../../common/Document';
import NothingFound from '../../common/NothingFound';
import InfiniteScroll from '../../ui/InfiniteScroll';
import Loading from '../../ui/Loading';
import Transition from '../../ui/Transition.tsx';
export type OwnProps = {
searchQuery?: string;
};
const CURRENT_TYPE = 'documents';
const INTERSECTION_THROTTLE = 500;
const runThrottled = throttle((cb) => cb(), 500, true);
const FileResults: FC<OwnProps & StateProps> = ({
searchQuery,
isLoading,
chatsById,
usersById,
globalMessagesByChatId,
foundIds,
activeDownloads,
shouldWarnAboutFiles,
}) => {
const {
searchMessagesGlobal,
focusMessage,
} = getActions();
const containerRef = useRef<HTMLDivElement>();
const lang = useOldLang();
const { observe: observeIntersectionForMedia } = useIntersectionObserver({
rootRef: containerRef,
throttleMs: INTERSECTION_THROTTLE,
});
const handleLoadMore = useCallback(({ direction }: { direction: LoadMoreDirection }) => {
if (direction === LoadMoreDirection.Backwards) {
runThrottled(() => {
searchMessagesGlobal({
type: CURRENT_TYPE,
});
});
}
// eslint-disable-next-line react-hooks-static-deps/exhaustive-deps -- `searchQuery` is required to prevent infinite message loading
}, [searchQuery]);
const foundMessages = useMemo(() => {
if (!foundIds || !globalMessagesByChatId) {
return MEMO_EMPTY_ARRAY;
}
return foundIds.map((id) => {
const [chatId, messageId] = parseSearchResultKey(id);
const message = globalMessagesByChatId[chatId]?.byId[messageId];
return message && getMessageDocument(message) ? message : undefined;
}).filter(Boolean);
}, [globalMessagesByChatId, foundIds]);
const handleMessageFocus = useCallback((message: ApiMessage) => {
focusMessage({ chatId: message.chatId, messageId: message.id });
}, [focusMessage]);
function renderList() {
return foundMessages.map((message, index) => {
const isFirst = index === 0;
const shouldDrawDateDivider = isFirst
|| toYearMonth(message.date) !== toYearMonth(foundMessages[index - 1].date);
return (
<>
{shouldDrawDateDivider && (
<p
className="section-heading"
dir={lang.isRtl ? 'rtl' : undefined}
key={message.date}
>
{formatMonthAndYear(lang, new Date(message.date * 1000))}
</p>
)}
<div
className="ListItem small-icon"
key={message.id}
>
<Document
document={getMessageDocument(message)!}
message={message}
datetime={message.date}
fileSize="small"
sender={getSenderName(lang, message, chatsById, usersById)}
className="scroll-item"
isDownloading={getIsDownloading(activeDownloads, message.content.document!)}
shouldWarnAboutFiles={shouldWarnAboutFiles}
observeIntersection={observeIntersectionForMedia}
onDateClick={handleMessageFocus}
/>
</div>
</>
);
});
}
const canRenderContents = useAsyncRendering([searchQuery], SLIDE_TRANSITION_DURATION) && !isLoading;
return (
<Transition
ref={containerRef}
slideClassName="LeftSearch--content"
name="fade"
activeKey={canRenderContents ? 1 : 0}
shouldCleanup
>
<InfiniteScroll
className="search-content documents-list custom-scroll"
items={canRenderContents ? foundMessages : undefined}
onLoadMore={handleLoadMore}
noFastList
>
{!canRenderContents && <Loading />}
{canRenderContents && (!foundIds || foundIds.length === 0) && (
<NothingFound
withSticker
text={lang('ChatList.Search.NoResults')}
description={lang('ChatList.Search.NoResultsDescription')}
/>
)}
{canRenderContents && foundIds && foundIds.length > 0 && renderList()}
</InfiniteScroll>
</Transition>
);
};
export default memo(withGlobal<OwnProps>(
createMapStateToProps(CURRENT_TYPE),
)(FileResults));