mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-04-04 06:47:19 +02:00
* fix: search result announcements * chore: address Copilot comments * chore: no nested ternary statements allowed * chore: memoize results announcement
118 lines
3.7 KiB
TypeScript
118 lines
3.7 KiB
TypeScript
import { useEffect, useMemo } from 'react';
|
|
import { useRecoilValue } from 'recoil';
|
|
import { Spinner, useToastContext } from '@librechat/client';
|
|
import MinimalMessagesWrapper from '~/components/Chat/Messages/MinimalMessages';
|
|
import { useNavScrolling, useLocalize, useAuthContext } from '~/hooks';
|
|
import SearchMessage from '~/components/Chat/Messages/SearchMessage';
|
|
import { useMessagesInfiniteQuery } from '~/data-provider';
|
|
import { useFileMapContext } from '~/Providers';
|
|
import store from '~/store';
|
|
|
|
export default function Search() {
|
|
const localize = useLocalize();
|
|
const fileMap = useFileMapContext();
|
|
const { showToast } = useToastContext();
|
|
const { isAuthenticated } = useAuthContext();
|
|
const search = useRecoilValue(store.search);
|
|
const searchQuery = search.debouncedQuery;
|
|
|
|
const {
|
|
data: searchMessages,
|
|
isLoading,
|
|
isError,
|
|
fetchNextPage,
|
|
isFetchingNextPage,
|
|
hasNextPage: _hasNextPage,
|
|
} = useMessagesInfiniteQuery(
|
|
{
|
|
search: searchQuery || undefined,
|
|
},
|
|
{
|
|
enabled: isAuthenticated && !!searchQuery,
|
|
staleTime: 30000,
|
|
cacheTime: 300000,
|
|
},
|
|
);
|
|
|
|
const { containerRef } = useNavScrolling({
|
|
nextCursor: searchMessages?.pages[searchMessages.pages.length - 1]?.nextCursor,
|
|
setShowLoading: () => ({}),
|
|
fetchNextPage: fetchNextPage,
|
|
isFetchingNext: isFetchingNextPage,
|
|
});
|
|
|
|
const messages = useMemo(() => {
|
|
const msgs =
|
|
searchMessages?.pages.flatMap((page) =>
|
|
page.messages.map((message) => {
|
|
if (!message.files || !fileMap) {
|
|
return message;
|
|
}
|
|
return {
|
|
...message,
|
|
files: message.files.map((file) => fileMap[file.file_id ?? ''] ?? file),
|
|
};
|
|
}),
|
|
) || [];
|
|
|
|
return msgs.length === 0 ? null : msgs;
|
|
}, [fileMap, searchMessages?.pages]);
|
|
|
|
useEffect(() => {
|
|
if (isError && searchQuery) {
|
|
showToast({ message: 'An error occurred during search', status: 'error' });
|
|
}
|
|
}, [isError, searchQuery, showToast]);
|
|
|
|
const resultsCount = messages?.length ?? 0;
|
|
const resultsAnnouncement = useMemo(() => {
|
|
if (resultsCount === 0) {
|
|
return localize('com_ui_nothing_found');
|
|
}
|
|
if (resultsCount === 1) {
|
|
return localize('com_ui_result_found', { count: resultsCount });
|
|
}
|
|
return localize('com_ui_results_found', { count: resultsCount });
|
|
}, [resultsCount, localize]);
|
|
|
|
const isSearchLoading = search.isTyping || isLoading || isFetchingNextPage;
|
|
|
|
if (isSearchLoading) {
|
|
return (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<Spinner className="text-text-primary" />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!searchQuery) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<MinimalMessagesWrapper ref={containerRef} className="relative flex h-full pt-4">
|
|
<div className="sr-only" role="alert" aria-atomic="true">
|
|
{resultsAnnouncement}
|
|
</div>
|
|
{(messages && messages.length === 0) || messages == null ? (
|
|
<div className="absolute inset-0 flex items-center justify-center">
|
|
<div className="rounded-lg bg-white p-6 text-lg text-gray-500 dark:border-gray-800/50 dark:bg-gray-800 dark:text-gray-300">
|
|
{localize('com_ui_nothing_found')}
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<>
|
|
{messages.map((msg) => (
|
|
<SearchMessage key={msg.messageId} message={msg} />
|
|
))}
|
|
{isFetchingNextPage && (
|
|
<div className="flex justify-center py-4">
|
|
<Spinner className="text-text-primary" />
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
<div className="absolute bottom-0 left-0 right-0 h-[5%] bg-gradient-to-t from-gray-50 to-transparent dark:from-gray-800" />
|
|
</MinimalMessagesWrapper>
|
|
);
|
|
}
|