Skip to content

Commit aa80e45

Browse files
authored
♻️ refactor: Logout UX, Improved State Teardown, & Remove Unused Code (danny-avila#5292)
* refactor: SearchBar and Nav components to streamline search functionality and improve state management * refactor: remove refresh conversations * chore: update useNewConvo calls to remove hardcoded default index * refactor: null check for submission in useSSE hook * refactor: remove useConversation hook and update useSearch to utilize useNewConvo * refactor: remove conversation and banner store files; consolidate state management into misc; improve typing of families and add messagesSiblingIdxFamily * refactor: more effectively clear all user/convo state without side effects on logout/delete user * refactor: replace useParams with useLocation in SearchBar to correctly load conversation * refactor: update SearchButtons to use button element and improve conversation ID handling * refactor: use named function for `newConversation` for better call stack tracing * refactor: enhance TermsAndConditionsModal to support array content and improve type definitions for terms of service * refactor: add SetConvoProvider and message invalidation when navigating from search results to prevent initial route rendering edge cases * refactor: rename getLocalStorageItems to localStorage and update imports for consistency * refactor: move clearLocalStorage function to utils and simplify localStorage clearing logic * refactor: migrate authentication mutations to a dedicated Auth data provider and update related tests
1 parent 24beda3 commit aa80e45

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+378
-434
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { createContext, useContext, useRef } from 'react';
2+
import type { MutableRefObject } from 'react';
3+
4+
type SetConvoContext = MutableRefObject<boolean>;
5+
6+
export const SetConvoContext = createContext<SetConvoContext>({} as SetConvoContext);
7+
8+
export const SetConvoProvider = ({ children }: { children: React.ReactNode }) => {
9+
const hasSetConversation = useRef<boolean>(false);
10+
11+
return <SetConvoContext.Provider value={hasSetConversation}>{children}</SetConvoContext.Provider>;
12+
};
13+
14+
export const useSetConvoContext = () => useContext(SetConvoContext);

client/src/Providers/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ export * from './AnnouncerContext';
1818
export * from './AgentsMapContext';
1919
export * from './CodeBlockContext';
2020
export * from './ToolCallsMapContext';
21+
export * from './SetConvoContext';

client/src/components/Auth/__tests__/Login.spec.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event';
33
import { getByTestId, render, waitFor } from 'test/layout-test-utils';
44
import * as mockDataProvider from 'librechat-data-provider/react-query';
55
import type { TStartupConfig } from 'librechat-data-provider';
6+
import * as authDataProvider from '~/data-provider/Auth/mutations';
67
import AuthLayout from '~/components/Auth/AuthLayout';
78
import Login from '~/components/Auth/Login';
89

@@ -61,7 +62,7 @@ const setup = ({
6162
},
6263
} = {}) => {
6364
const mockUseLoginUser = jest
64-
.spyOn(mockDataProvider, 'useLoginUserMutation')
65+
.spyOn(authDataProvider, 'useLoginUserMutation')
6566
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
6667
.mockReturnValue(useLoginUserReturnValue);
6768
const mockUseGetUserQuery = jest

client/src/components/Auth/__tests__/LoginForm.spec.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { render, getByTestId } from 'test/layout-test-utils';
22
import userEvent from '@testing-library/user-event';
33
import * as mockDataProvider from 'librechat-data-provider/react-query';
44
import type { TStartupConfig } from 'librechat-data-provider';
5+
import * as authDataProvider from '~/data-provider/Auth/mutations';
56
import Login from '../LoginForm';
67

78
jest.mock('librechat-data-provider/react-query');
@@ -66,7 +67,7 @@ const setup = ({
6667
},
6768
} = {}) => {
6869
const mockUseLoginUser = jest
69-
.spyOn(mockDataProvider, 'useLoginUserMutation')
70+
.spyOn(authDataProvider, 'useLoginUserMutation')
7071
//@ts-ignore - we don't need all parameters of the QueryObserverSuccessResult
7172
.mockReturnValue(useLoginUserReturnValue);
7273
const mockUseGetUserQuery = jest

client/src/components/Chat/Messages/SearchButtons.tsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,34 @@ export default function SearchButtons({ message }: { message: TMessage }) {
88
const localize = useLocalize();
99
const { searchQueryRes } = useSearchContext();
1010
const { navigateWithLastTools } = useNavigateToConvo();
11+
const conversationId = message.conversationId ?? '';
1112

12-
if (!message.conversationId) {
13+
if (!conversationId) {
1314
return null;
1415
}
1516

16-
const clickHandler = (event: React.MouseEvent<HTMLAnchorElement>) => {
17+
const clickHandler = (event: React.MouseEvent<HTMLButtonElement>) => {
1718
event.preventDefault();
1819

19-
const conversation = getConversationById(searchQueryRes?.data, message.conversationId);
20+
const conversation = getConversationById(searchQueryRes?.data, conversationId);
2021
if (!conversation) {
2122
return;
2223
}
2324

2425
document.title = message.title ?? '';
25-
navigateWithLastTools(conversation);
26+
navigateWithLastTools(conversation, true, true);
2627
};
2728

2829
return (
29-
<div className="visible mt-0 flex items-center justify-center gap-1 self-end text-gray-400 lg:justify-start">
30-
<a
31-
className="ml-0 flex cursor-pointer items-center gap-1.5 rounded-md p-1 text-xs hover:text-gray-900 hover:underline dark:text-gray-400/70 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400"
30+
<div className="visible mt-0 flex items-center justify-center gap-1 self-end text-text-secondary lg:justify-start">
31+
<button
32+
className="ml-0 flex cursor-pointer items-center gap-1.5 rounded-md p-1 text-xs hover:text-text-primary hover:underline"
3233
onClick={clickHandler}
3334
title={localize('com_ui_go_to_conversation')}
3435
>
3536
<Link className="icon-sm" />
3637
{message.title}
37-
</a>
38+
</button>
3839
</div>
3940
);
4041
}

client/src/components/Conversations/Convo.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Constants } from 'librechat-data-provider';
66
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
77
import type { MouseEvent, FocusEvent, KeyboardEvent } from 'react';
88
import type { TConversation } from 'librechat-data-provider';
9-
import { useConversations, useNavigateToConvo, useMediaQuery, useLocalize } from '~/hooks';
9+
import { useNavigateToConvo, useMediaQuery, useLocalize } from '~/hooks';
1010
import { useUpdateConversationMutation } from '~/data-provider';
1111
import EndpointIcon from '~/components/Endpoints/EndpointIcon';
1212
import { NotificationSeverity } from '~/common';
@@ -36,7 +36,6 @@ export default function Conversation({
3636
const activeConvos = useRecoilValue(store.allConversationsSelector);
3737
const { data: endpointsConfig } = useGetEndpointsQuery();
3838
const { navigateWithLastTools } = useNavigateToConvo();
39-
const { refreshConversations } = useConversations();
4039
const { showToast } = useToastContext();
4140
const { conversationId, title } = conversation;
4241
const inputRef = useRef<HTMLInputElement | null>(null);
@@ -97,7 +96,6 @@ export default function Conversation({
9796
updateConvoMutation.mutate(
9897
{ conversationId, title: titleInput ?? '' },
9998
{
100-
onSuccess: () => refreshConversations(),
10199
onError: () => {
102100
setTitleInput(title);
103101
showToast({
@@ -109,7 +107,7 @@ export default function Conversation({
109107
},
110108
);
111109
},
112-
[title, titleInput, conversationId, showToast, refreshConversations, updateConvoMutation],
110+
[title, titleInput, conversationId, showToast, updateConvoMutation],
113111
);
114112

115113
const handleKeyDown = useCallback(

client/src/components/Nav/MobileNav.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export default function MobileNav({
1414
}) {
1515
const localize = useLocalize();
1616
const queryClient = useQueryClient();
17-
const { newConversation } = useNewConvo(0);
17+
const { newConversation } = useNewConvo();
1818
const conversation = useRecoilValue(store.conversationByIndex(0));
1919
const { title = 'New Chat' } = conversation || {};
2020

client/src/components/Nav/Nav.tsx

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
import { useCallback, useEffect, useState, useMemo, memo } from 'react';
22
import { useRecoilValue } from 'recoil';
3-
import { useParams } from 'react-router-dom';
43
import { PermissionTypes, Permissions } from 'librechat-data-provider';
54
import type { ConversationListResponse } from 'librechat-data-provider';
65
import {
76
useLocalize,
87
useHasAccess,
98
useMediaQuery,
109
useAuthContext,
11-
useConversation,
1210
useLocalStorage,
1311
useNavScrolling,
14-
useConversations,
1512
} from '~/hooks';
1613
import { useConversationsInfiniteQuery } from '~/data-provider';
1714
import { Conversations } from '~/components/Conversations';
@@ -33,7 +30,6 @@ const Nav = ({
3330
setNavVisible: React.Dispatch<React.SetStateAction<boolean>>;
3431
}) => {
3532
const localize = useLocalize();
36-
const { conversationId } = useParams();
3733
const { isAuthenticated } = useAuthContext();
3834

3935
const [navWidth, setNavWidth] = useState('260px');
@@ -67,11 +63,9 @@ const Nav = ({
6763
}
6864
}, [isSmallScreen]);
6965

70-
const { newConversation } = useConversation();
7166
const [showLoading, setShowLoading] = useState(false);
7267
const isSearchEnabled = useRecoilValue(store.isSearchEnabled);
7368

74-
const { refreshConversations } = useConversations();
7569
const { pageNumber, searchQuery, setPageNumber, searchQueryRes } = useSearchContext();
7670
const [tags, setTags] = useState<string[]>([]);
7771
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } =
@@ -104,14 +98,6 @@ const Nav = ({
10498
[data, searchQuery, searchQueryRes?.data],
10599
);
106100

107-
const clearSearch = () => {
108-
setPageNumber(1);
109-
refreshConversations();
110-
if (conversationId == 'search') {
111-
newConversation();
112-
}
113-
};
114-
115101
const toggleNavVisible = () => {
116102
setNavVisible((prev: boolean) => {
117103
localStorage.setItem('navVisible', JSON.stringify(!prev));
@@ -174,7 +160,10 @@ const Nav = ({
174160
subHeaders={
175161
<>
176162
{isSearchEnabled === true && (
177-
<SearchBar clearSearch={clearSearch} isSmallScreen={isSmallScreen} />
163+
<SearchBar
164+
setPageNumber={setPageNumber}
165+
isSmallScreen={isSmallScreen}
166+
/>
178167
)}
179168
{hasAccessToBookmarks === true && (
180169
<>

client/src/components/Nav/SearchBar.tsx

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,39 @@
11
import debounce from 'lodash/debounce';
22
import { Search, X } from 'lucide-react';
33
import { useSetRecoilState } from 'recoil';
4+
import { useLocation } from 'react-router-dom';
45
import { QueryKeys } from 'librechat-data-provider';
56
import { useQueryClient } from '@tanstack/react-query';
67
import { forwardRef, useState, useCallback, useMemo, Ref } from 'react';
7-
import { useLocalize } from '~/hooks';
8+
import { useLocalize, useNewConvo } from '~/hooks';
89
import { cn } from '~/utils';
910
import store from '~/store';
1011

1112
type SearchBarProps = {
12-
clearSearch: () => void;
1313
isSmallScreen?: boolean;
14+
setPageNumber: React.Dispatch<React.SetStateAction<number>>;
1415
};
1516

1617
const SearchBar = forwardRef((props: SearchBarProps, ref: Ref<HTMLDivElement>) => {
17-
const { clearSearch, isSmallScreen } = props;
18+
const localize = useLocalize();
19+
const location = useLocation();
1820
const queryClient = useQueryClient();
21+
const { setPageNumber, isSmallScreen } = props;
22+
23+
const [text, setText] = useState('');
24+
const [showClearIcon, setShowClearIcon] = useState(false);
25+
26+
const { newConversation } = useNewConvo();
1927
const clearConvoState = store.useClearConvoState();
2028
const setSearchQuery = useSetRecoilState(store.searchQuery);
21-
const [showClearIcon, setShowClearIcon] = useState(false);
22-
const [text, setText] = useState('');
2329
const setIsSearching = useSetRecoilState(store.isSearching);
24-
const localize = useLocalize();
30+
31+
const clearSearch = useCallback(() => {
32+
setPageNumber(1);
33+
if (location.pathname.includes('/search')) {
34+
newConversation();
35+
}
36+
}, [newConversation, setPageNumber, location.pathname]);
2537

2638
const clearText = useCallback(() => {
2739
setShowClearIcon(false);

client/src/components/Nav/SettingsTabs/Data/ClearChats.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import React, { useState } from 'react';
22
import { useClearConversationsMutation } from 'librechat-data-provider/react-query';
33
import { Label, Button, OGDialog, OGDialogTrigger, Spinner } from '~/components';
4-
import { useConversation, useConversations, useLocalize } from '~/hooks';
4+
import { useLocalize, useNewConvo } from '~/hooks';
55
import OGDialogTemplate from '~/components/ui/OGDialogTemplate';
66

77
export const ClearChats = () => {
88
const localize = useLocalize();
99
const [open, setOpen] = useState(false);
10-
const { newConversation } = useConversation();
11-
const { refreshConversations } = useConversations();
10+
const { newConversation } = useNewConvo();
1211
const clearConvosMutation = useClearConversationsMutation();
1312

1413
const clearConvos = () => {
@@ -17,7 +16,6 @@ export const ClearChats = () => {
1716
{
1817
onSuccess: () => {
1918
newConversation();
20-
refreshConversations();
2119
},
2220
},
2321
);

0 commit comments

Comments
 (0)