♾️ style: Infinite Scroll Nav and Sort Convos by Date/Usage (#1708)

* Style: Infinite Scroll and Group convos by date

* Style: Infinite Scroll and Group convos by date- Redesign NavBar

* Style: Infinite Scroll and Group convos by date- Redesign NavBar - Clean code

* Style: Infinite Scroll and Group convos by date- Redesign NavBar - Redesign NewChat Component

* Style: Infinite Scroll and Group convos by date- Redesign NavBar - Redesign NewChat Component

* Style: Infinite Scroll and Group convos by date- Redesign NavBar - Redesign NewChat Component

* Including OpenRouter and Mistral icon

* refactor(Conversations): cleanup use of utility functions and typing

* refactor(Nav/NewChat): use localStorage `lastConversationSetup` to determine the endpoint to use, as well as icons -> JSX components, remove use of `endpointSelected`

* refactor: remove use of `isFirstToday`

* refactor(Nav): remove use of `endpointSelected`, consolidate scrolling logic to its own hook `useNavScrolling`, remove use of recoil `conversation`

* refactor: Add spinner to bottom of list, throttle fetching, move query hooks to client workspace

* chore: sort by `updatedAt` field

* refactor: optimize conversation infinite query, use optimistic updates, add conversation helpers for managing pagination, remove unnecessary operations

* feat: gen_title route for generating the title for the conversation

* style(Convo): change hover bg-color

* refactor: memoize groupedConversations and return as array of tuples, correctly update convos pre/post message stream, only call genTitle if conversation is new, make `addConversation` dynamically either add/update depending if convo exists in pages already, reorganize type definitions

* style: rename Header NewChat Button -> HeaderNewChat, add NewChatIcon, closely match main Nav New Chat button to ChatGPT

* style(NewChat): add hover bg color

* style: cleanup comments, match ChatGPT nav styling, redesign search bar, make part of new chat sticky header, move Nav under same parent as outlet/mobilenav, remove legacy code, search only if searchQuery is not empty

* feat: add tests for conversation helpers and ensure no duplicate conversations are ever grouped

* style: hover bg-color

* feat: alt-click on convo item to open conversation in new tab

* chore: send error message when `gen_title` fails

---------

Co-authored-by: Walber Cardoso <walbercardoso@gmail.com>
This commit is contained in:
Danny Avila 2024-02-03 20:25:35 -05:00 committed by GitHub
parent 13b2d6e34a
commit 74459d6261
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
48 changed files with 1788 additions and 391 deletions

View file

@ -1,9 +1,11 @@
import { v4 } from 'uuid';
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
import { useEffect, useState } from 'react';
import {
/* @ts-ignore */
SSE,
QueryKeys,
EndpointURLs,
createPayload,
tPresetSchema,
@ -13,7 +15,15 @@ import {
removeNullishValues,
} from 'librechat-data-provider';
import { useGetUserBalance, useGetStartupConfig } from 'librechat-data-provider/react-query';
import type { TResPlugin, TMessage, TConversation, TSubmission } from 'librechat-data-provider';
import type {
TResPlugin,
TMessage,
TConversation,
TSubmission,
ConversationData,
} from 'librechat-data-provider';
import { addConversation, deleteConversation, updateConversation } from '~/utils';
import { useGenTitleMutation } from '~/data-provider';
import { useAuthContext } from './AuthContext';
import useChatHelpers from './useChatHelpers';
import useSetStorage from './useSetStorage';
@ -30,18 +40,14 @@ type TResData = {
export default function useSSE(submission: TSubmission | null, index = 0) {
const setStorage = useSetStorage();
const queryClient = useQueryClient();
const genTitle = useGenTitleMutation();
const { conversationId: paramId } = useParams();
const { token, isAuthenticated } = useAuthContext();
const [completed, setCompleted] = useState(new Set());
const {
addConvo,
setMessages,
setConversation,
setIsSubmitting,
resetLatestMessage,
invalidateConvos,
newConversation,
} = useChatHelpers(index, paramId);
const { setMessages, setConversation, setIsSubmitting, newConversation, resetLatestMessage } =
useChatHelpers(index, paramId);
const { data: startupConfig } = useGetStartupConfig();
const balanceQuery = useGetUserBalance({
@ -103,16 +109,21 @@ export default function useSSE(submission: TSubmission | null, index = 0) {
setMessages(messagesUpdate);
}
// refresh title
if (requestMessage?.parentMessageId == '00000000-0000-0000-0000-000000000000') {
setTimeout(() => {
invalidateConvos();
}, 2000);
const isNewConvo = conversation.conversationId !== submission.conversation.conversationId;
if (isNewConvo) {
queryClient.setQueryData<ConversationData>([QueryKeys.allConversations], (convoData) => {
if (!convoData) {
return convoData;
}
return deleteConversation(convoData, submission.conversation.conversationId as string);
});
}
// in case it takes too long.
// refresh title
if (isNewConvo && requestMessage?.parentMessageId == '00000000-0000-0000-0000-000000000000') {
setTimeout(() => {
invalidateConvos();
}, 5000);
genTitle.mutate({ conversationId: convoUpdate.conversationId as string });
}, 2500);
}
setConversation((prevState) => {
@ -164,9 +175,17 @@ export default function useSSE(submission: TSubmission | null, index = 0) {
setStorage(update);
return update;
});
if (message.parentMessageId == '00000000-0000-0000-0000-000000000000') {
addConvo(update);
}
queryClient.setQueryData<ConversationData>([QueryKeys.allConversations], (convoData) => {
if (!convoData) {
return convoData;
}
if (message.parentMessageId == '00000000-0000-0000-0000-000000000000') {
return addConversation(convoData, update);
} else {
return updateConversation(convoData, update);
}
});
resetLatestMessage();
};
@ -183,16 +202,21 @@ export default function useSSE(submission: TSubmission | null, index = 0) {
setMessages([...messages, requestMessage, responseMessage]);
}
// refresh title
if (requestMessage.parentMessageId == '00000000-0000-0000-0000-000000000000') {
setTimeout(() => {
invalidateConvos();
}, 1500);
const isNewConvo = conversation.conversationId !== submissionConvo.conversationId;
if (isNewConvo) {
queryClient.setQueryData<ConversationData>([QueryKeys.allConversations], (convoData) => {
if (!convoData) {
return convoData;
}
return deleteConversation(convoData, submissionConvo.conversationId as string);
});
}
// in case it takes too long.
// refresh title
if (isNewConvo && requestMessage.parentMessageId == '00000000-0000-0000-0000-000000000000') {
setTimeout(() => {
invalidateConvos();
}, 5000);
genTitle.mutate({ conversationId: conversation.conversationId as string });
}, 2500);
}
setConversation((prevState) => {