import { useParams } from 'react-router-dom'; import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useCallback, useEffect, useState, useMemo, memo } from 'react'; import type { ConversationListResponse } from 'librechat-data-provider'; import { useMediaQuery, useAuthContext, useConversation, useLocalStorage, useNavScrolling, useConversations, } from '~/hooks'; import { useSearchInfiniteQuery, useConversationsInfiniteQuery } from '~/data-provider'; import { TooltipProvider, Tooltip } from '~/components/ui'; import { Conversations } from '~/components/Conversations'; import { Spinner } from '~/components/svg'; import SearchBar from './SearchBar'; import NavToggle from './NavToggle'; import NavLinks from './NavLinks'; import NewChat from './NewChat'; import { cn } from '~/utils'; import store from '~/store'; const Nav = ({ navVisible, setNavVisible }) => { const { conversationId } = useParams(); const { isAuthenticated } = useAuthContext(); const [navWidth, setNavWidth] = useState('260px'); const [isHovering, setIsHovering] = useState(false); const isSmallScreen = useMediaQuery('(max-width: 768px)'); const [newUser, setNewUser] = useLocalStorage('newUser', true); const [isToggleHovering, setIsToggleHovering] = useState(false); const handleMouseEnter = useCallback(() => { setIsHovering(true); }, []); const handleMouseLeave = useCallback(() => { setIsHovering(false); }, []); useEffect(() => { if (isSmallScreen) { setNavWidth('320px'); } else { setNavWidth('260px'); } }, [isSmallScreen]); const [pageNumber, setPageNumber] = useState(1); const [showLoading, setShowLoading] = useState(false); const searchQuery = useRecoilValue(store.searchQuery); const isSearchEnabled = useRecoilValue(store.isSearchEnabled); const { newConversation, searchPlaceholderConversation } = useConversation(); const { refreshConversations } = useConversations(); const setSearchResultMessages = useSetRecoilState(store.searchResultMessages); const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useConversationsInfiniteQuery( { pageNumber: pageNumber.toString() }, { enabled: isAuthenticated }, ); const searchQueryRes = useSearchInfiniteQuery( { pageNumber: pageNumber.toString(), searchQuery: searchQuery }, { enabled: isAuthenticated && !!searchQuery.length }, ); const { containerRef, moveToTop } = useNavScrolling({ setShowLoading, hasNextPage: searchQuery ? searchQueryRes.hasNextPage : hasNextPage, fetchNextPage: searchQuery ? searchQueryRes.fetchNextPage : fetchNextPage, isFetchingNextPage: searchQuery ? searchQueryRes.isFetchingNextPage : isFetchingNextPage, }); const conversations = useMemo( () => (searchQuery ? searchQueryRes?.data : data)?.pages.flatMap((page) => page.conversations) || [], [data, searchQuery, searchQueryRes?.data], ); const onSearchSuccess = useCallback(({ data }: { data: ConversationListResponse }) => { const res = data; searchPlaceholderConversation(); setSearchResultMessages(res.messages); /* disabled due recoil methods not recognized as state setters */ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // Empty dependency array useEffect(() => { //we use isInitialLoading here instead of isLoading because query is disabled by default if (searchQueryRes.data) { onSearchSuccess({ data: searchQueryRes.data.pages[0] }); } }, [searchQueryRes.data, searchQueryRes.isInitialLoading, onSearchSuccess]); const clearSearch = () => { setPageNumber(1); refreshConversations(); if (conversationId == 'search') { newConversation(); } }; const toggleNavVisible = () => { setNavVisible((prev: boolean) => { localStorage.setItem('navVisible', JSON.stringify(!prev)); return !prev; }); if (newUser) { setNewUser(false); } }; const itemToggleNav = () => { if (isSmallScreen) { toggleNavVisible(); } }; return (
); }; export default memo(Nav);