mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-21 21:50:49 +02:00
🧵 fix: Prevent Unnecessary Re-renders when Loading Chats (#5189)
* chore: typing * chore: typing * fix: enhance message scrolling logic to handle empty messages tree and ref checks * fix: optimize message selection logic with useCallback for better performance * chore: typing * refactor: optimize icon rendering * refactor: further optimize chat props * fix: remove unnecessary console log in useQueryParams cleanup * refactor: add queryClient to reset message data on new conversation initiation * refactor: update data-testid attributes for consistency and improve code readability * refactor: integrate queryClient to reset message data on new conversation initiation
This commit is contained in:
parent
7987e04a2c
commit
b01c744eb8
20 changed files with 184 additions and 88 deletions
|
@ -1,8 +1,9 @@
|
|||
import { memo } from 'react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useGetMessagesByConvoId } from 'librechat-data-provider/react-query';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import type { ChatFormValues } from '~/common';
|
||||
import { ChatContext, AddedChatContext, useFileMapContext, ChatFormProvider } from '~/Providers';
|
||||
import { useChatHelpers, useAddedResponse, useSSE } from '~/hooks';
|
||||
|
@ -24,10 +25,13 @@ function ChatView({ index = 0 }: { index?: number }) {
|
|||
const fileMap = useFileMapContext();
|
||||
|
||||
const { data: messagesTree = null, isLoading } = useGetMessagesByConvoId(conversationId ?? '', {
|
||||
select: (data) => {
|
||||
const dataTree = buildTree({ messages: data, fileMap });
|
||||
return dataTree?.length === 0 ? null : dataTree ?? null;
|
||||
},
|
||||
select: useCallback(
|
||||
(data: TMessage[]) => {
|
||||
const dataTree = buildTree({ messages: data, fileMap });
|
||||
return dataTree?.length === 0 ? null : dataTree ?? null;
|
||||
},
|
||||
[fileMap],
|
||||
),
|
||||
enabled: !!fileMap,
|
||||
});
|
||||
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { QueryKeys, Constants } from 'librechat-data-provider';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import { useMediaQuery, useLocalize } from '~/hooks';
|
||||
import { NewChatIcon } from '~/components/svg';
|
||||
import { useChatContext } from '~/Providers';
|
||||
import { useMediaQuery, useLocalize } from '~/hooks';
|
||||
|
||||
export default function HeaderNewChat() {
|
||||
const { newConversation } = useChatContext();
|
||||
const queryClient = useQueryClient();
|
||||
const { conversation, newConversation } = useChatContext();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 768px)');
|
||||
const localize = useLocalize();
|
||||
if (isSmallScreen) {
|
||||
|
@ -12,10 +16,16 @@ export default function HeaderNewChat() {
|
|||
return (
|
||||
<button
|
||||
data-testid="wide-header-new-chat-button"
|
||||
aria-label={localize("com_ui_new_chat")}
|
||||
aria-label={localize('com_ui_new_chat')}
|
||||
type="button"
|
||||
className="btn btn-neutral btn-small border-token-border-medium relative ml-2 flex hidden h-9 w-9 items-center justify-center whitespace-nowrap rounded-lg rounded-lg border focus:border-black-500 dark:focus:border-white-500 md:flex"
|
||||
onClick={() => newConversation()}
|
||||
className="btn btn-neutral btn-small border-token-border-medium focus:border-black-500 dark:focus:border-white-500 relative ml-2 flex h-9 w-9 items-center justify-center whitespace-nowrap rounded-lg border md:flex"
|
||||
onClick={() => {
|
||||
queryClient.setQueryData<TMessage[]>(
|
||||
[QueryKeys.messages, conversation?.conversationId ?? Constants.NEW_CONVO],
|
||||
[],
|
||||
);
|
||||
newConversation();
|
||||
}}
|
||||
>
|
||||
<div className="flex w-full items-center justify-center gap-2">
|
||||
<NewChatIcon />
|
||||
|
|
|
@ -1,26 +1,23 @@
|
|||
import React, { useMemo, memo } from 'react';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { TMessage, TPreset, Assistant, Agent } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import type { Assistant, Agent, TMessage } from 'librechat-data-provider';
|
||||
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
|
||||
import { getEndpointField, getIconEndpoint } from '~/utils';
|
||||
import Icon from '~/components/Endpoints/Icon';
|
||||
|
||||
const MessageIcon = memo(
|
||||
(
|
||||
props: Pick<TMessageProps, 'message' | 'conversation'> & {
|
||||
assistant?: Assistant;
|
||||
agent?: Agent;
|
||||
},
|
||||
) => {
|
||||
(props: {
|
||||
iconData?: TMessage & { modelLabel?: string };
|
||||
assistant?: Assistant;
|
||||
agent?: Agent;
|
||||
}) => {
|
||||
const { data: endpointsConfig } = useGetEndpointsQuery();
|
||||
const { message, conversation, assistant, agent } = props;
|
||||
const { iconData, assistant, agent } = props;
|
||||
|
||||
const assistantName = useMemo(() => assistant?.name ?? '', [assistant]);
|
||||
const assistantAvatar = useMemo(() => assistant?.metadata?.avatar ?? '', [assistant]);
|
||||
const agentName = useMemo(() => props.agent?.name ?? '', [props.agent]);
|
||||
const agentAvatar = useMemo(() => props.agent?.avatar?.filepath ?? '', [props.agent]);
|
||||
const isCreatedByUser = useMemo(() => message?.isCreatedByUser ?? false, [message]);
|
||||
|
||||
let avatarURL = '';
|
||||
|
||||
|
@ -30,21 +27,10 @@ const MessageIcon = memo(
|
|||
avatarURL = agentAvatar;
|
||||
}
|
||||
|
||||
const messageSettings = useMemo(
|
||||
() => ({
|
||||
...(conversation ?? {}),
|
||||
...({
|
||||
...(message ?? {}),
|
||||
iconURL: message?.iconURL ?? '',
|
||||
} as TMessage),
|
||||
}),
|
||||
[conversation, message],
|
||||
);
|
||||
|
||||
const iconURL = messageSettings.iconURL;
|
||||
const iconURL = iconData?.iconURL;
|
||||
const endpoint = useMemo(
|
||||
() => getIconEndpoint({ endpointsConfig, iconURL, endpoint: messageSettings.endpoint }),
|
||||
[endpointsConfig, iconURL, messageSettings.endpoint],
|
||||
() => getIconEndpoint({ endpointsConfig, iconURL, endpoint: iconData?.endpoint }),
|
||||
[endpointsConfig, iconURL, iconData?.endpoint],
|
||||
);
|
||||
|
||||
const endpointIconURL = useMemo(
|
||||
|
@ -52,10 +38,11 @@ const MessageIcon = memo(
|
|||
[endpointsConfig, endpoint],
|
||||
);
|
||||
|
||||
if (isCreatedByUser !== true && iconURL != null && iconURL.includes('http')) {
|
||||
if (iconData?.isCreatedByUser !== true && iconURL != null && iconURL.includes('http')) {
|
||||
return (
|
||||
<ConvoIconURL
|
||||
preset={messageSettings as typeof messageSettings & TPreset}
|
||||
iconURL={iconURL}
|
||||
modelLabel={iconData?.modelLabel}
|
||||
context="message"
|
||||
assistantAvatar={assistantAvatar}
|
||||
agentAvatar={agentAvatar}
|
||||
|
@ -68,10 +55,10 @@ const MessageIcon = memo(
|
|||
|
||||
return (
|
||||
<Icon
|
||||
isCreatedByUser={isCreatedByUser}
|
||||
isCreatedByUser={iconData?.isCreatedByUser ?? false}
|
||||
endpoint={endpoint}
|
||||
iconURL={avatarURL || endpointIconURL}
|
||||
model={message?.model ?? conversation?.model}
|
||||
model={iconData?.model}
|
||||
assistantName={assistantName}
|
||||
agentName={agentName}
|
||||
size={28.8}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import type { TMessageContentParts } from 'librechat-data-provider';
|
||||
import type { TMessage, TMessageContentParts } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import Icon from '~/components/Chat/Messages/MessageIcon';
|
||||
import MessageIcon from '~/components/Chat/Messages/MessageIcon';
|
||||
import { useMessageHelpers, useLocalize } from '~/hooks';
|
||||
import ContentParts from './Content/ContentParts';
|
||||
import SiblingSwitch from './SiblingSwitch';
|
||||
|
@ -35,6 +36,26 @@ export default function Message(props: TMessageProps) {
|
|||
const fontSize = useRecoilValue(store.fontSize);
|
||||
const { children, messageId = null, isCreatedByUser } = message ?? {};
|
||||
|
||||
const iconData = useMemo(
|
||||
() =>
|
||||
({
|
||||
endpoint: conversation?.endpoint,
|
||||
model: conversation?.model ?? message?.model,
|
||||
iconURL: conversation?.iconURL ?? message?.iconURL ?? '',
|
||||
modelLabel: conversation?.chatGptLabel ?? conversation?.modelLabel,
|
||||
isCreatedByUser: message?.isCreatedByUser,
|
||||
} as TMessage & { modelLabel?: string }),
|
||||
[
|
||||
conversation?.chatGptLabel,
|
||||
conversation?.modelLabel,
|
||||
conversation?.endpoint,
|
||||
conversation?.iconURL,
|
||||
conversation?.model,
|
||||
message?.model,
|
||||
message?.iconURL,
|
||||
message?.isCreatedByUser,
|
||||
],
|
||||
);
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
|
@ -62,12 +83,7 @@ export default function Message(props: TMessageProps) {
|
|||
<div>
|
||||
<div className="pt-0.5">
|
||||
<div className="shadow-stroke flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
|
||||
<Icon
|
||||
message={message}
|
||||
conversation={conversation}
|
||||
assistant={assistant}
|
||||
agent={agent}
|
||||
/>
|
||||
<MessageIcon iconData={iconData} assistant={assistant} agent={agent} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -56,7 +56,7 @@ export default function MessagesView({
|
|||
</div>
|
||||
) : (
|
||||
<>
|
||||
{Header && Header}
|
||||
{Header != null && Header}
|
||||
<div ref={screenshotTargetRef}>
|
||||
<MultiMessage
|
||||
key={conversationId} // avoid internal state mixture
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useAuthContext, useLocalize } from '~/hooks';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import type { TMessageProps } from '~/common';
|
||||
import MinimalHoverButtons from '~/components/Chat/Messages/MinimalHoverButtons';
|
||||
import Icon from '~/components/Chat/Messages/MessageIcon';
|
||||
|
@ -15,6 +17,17 @@ export default function Message({ message }: Pick<TMessageProps, 'message'>) {
|
|||
const { user } = useAuthContext();
|
||||
const localize = useLocalize();
|
||||
|
||||
const iconData = useMemo(
|
||||
() =>
|
||||
({
|
||||
endpoint: message?.endpoint,
|
||||
model: message?.model,
|
||||
iconURL: message?.iconURL ?? '',
|
||||
isCreatedByUser: message?.isCreatedByUser,
|
||||
} as TMessage & { modelLabel?: string }),
|
||||
[message?.model, message?.iconURL, message?.endpoint, message?.isCreatedByUser],
|
||||
);
|
||||
|
||||
if (!message) {
|
||||
return null;
|
||||
}
|
||||
|
@ -27,7 +40,7 @@ export default function Message({ message }: Pick<TMessageProps, 'message'>) {
|
|||
? (user?.name ?? '') || (user?.username ?? '')
|
||||
: localize('com_user_message');
|
||||
} else {
|
||||
messageLabel = message.sender || '';
|
||||
messageLabel = message.sender ?? '';
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -39,7 +52,7 @@ export default function Message({ message }: Pick<TMessageProps, 'message'>) {
|
|||
<div>
|
||||
<div className="pt-0.5">
|
||||
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
|
||||
<Icon message={message} />
|
||||
<Icon iconData={iconData} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,7 +6,7 @@ import MessageContent from '~/components/Chat/Messages/Content/MessageContent';
|
|||
import PlaceholderRow from '~/components/Chat/Messages/ui/PlaceholderRow';
|
||||
import SiblingSwitch from '~/components/Chat/Messages/SiblingSwitch';
|
||||
import HoverButtons from '~/components/Chat/Messages/HoverButtons';
|
||||
import Icon from '~/components/Chat/Messages/MessageIcon';
|
||||
import MessageIcon from '~/components/Chat/Messages/MessageIcon';
|
||||
import { Plugin } from '~/components/Messages/Content';
|
||||
import SubRow from '~/components/Chat/Messages/SubRow';
|
||||
import { MessageContext } from '~/Providers';
|
||||
|
@ -66,6 +66,27 @@ const MessageRender = memo(
|
|||
[hasNoChildren, msg?.depth, latestMessage?.depth],
|
||||
);
|
||||
|
||||
const iconData = useMemo(
|
||||
() =>
|
||||
({
|
||||
endpoint: conversation?.endpoint,
|
||||
model: conversation?.model ?? msg?.model,
|
||||
iconURL: conversation?.iconURL ?? msg?.iconURL ?? '',
|
||||
modelLabel: conversation?.chatGptLabel ?? conversation?.modelLabel,
|
||||
isCreatedByUser: msg?.isCreatedByUser,
|
||||
} as TMessage & { modelLabel?: string }),
|
||||
[
|
||||
conversation?.chatGptLabel,
|
||||
conversation?.modelLabel,
|
||||
conversation?.endpoint,
|
||||
conversation?.iconURL,
|
||||
conversation?.model,
|
||||
msg?.model,
|
||||
msg?.iconURL,
|
||||
msg?.isCreatedByUser,
|
||||
],
|
||||
);
|
||||
|
||||
if (!msg) {
|
||||
return null;
|
||||
}
|
||||
|
@ -125,7 +146,7 @@ const MessageRender = memo(
|
|||
<div>
|
||||
<div className="pt-0.5">
|
||||
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
|
||||
<Icon message={msg} conversation={conversation} assistant={assistant} />
|
||||
<MessageIcon iconData={iconData} assistant={assistant} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -52,7 +52,8 @@ export default function ConvoIcon({
|
|||
<>
|
||||
{iconURL && iconURL.includes('http') ? (
|
||||
<ConvoIconURL
|
||||
preset={conversation}
|
||||
iconURL={iconURL}
|
||||
modelLabel={conversation?.chatGptLabel ?? conversation?.modelLabel ?? ''}
|
||||
endpointIconURL={endpointIconURL}
|
||||
assistantAvatar={avatar}
|
||||
assistantName={name}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import React, { memo } from 'react';
|
||||
import type { TPreset } from 'librechat-data-provider';
|
||||
import type { IconMapProps } from '~/common';
|
||||
import { icons } from '~/components/Chat/Menus/Endpoints/Icons';
|
||||
|
||||
interface ConvoIconURLProps {
|
||||
preset: TPreset | null;
|
||||
iconURL?: string;
|
||||
modelLabel?: string;
|
||||
endpointIconURL?: string;
|
||||
assistantName?: string;
|
||||
agentName?: string;
|
||||
|
@ -29,7 +29,8 @@ const styleImageMap = {
|
|||
};
|
||||
|
||||
const ConvoIconURL: React.FC<ConvoIconURLProps> = ({
|
||||
preset,
|
||||
iconURL = '',
|
||||
modelLabel = '',
|
||||
endpointIconURL,
|
||||
assistantAvatar,
|
||||
assistantName,
|
||||
|
@ -37,7 +38,6 @@ const ConvoIconURL: React.FC<ConvoIconURLProps> = ({
|
|||
agentName,
|
||||
context,
|
||||
}) => {
|
||||
const { iconURL = '' } = preset ?? {};
|
||||
let Icon: (
|
||||
props: IconMapProps & {
|
||||
context?: string;
|
||||
|
@ -57,7 +57,7 @@ const ConvoIconURL: React.FC<ConvoIconURLProps> = ({
|
|||
>
|
||||
<img
|
||||
src={iconURL}
|
||||
alt={preset?.chatGptLabel ?? preset?.modelLabel ?? ''}
|
||||
alt={modelLabel}
|
||||
style={styleImageMap[context ?? 'default'] ?? styleImageMap.default}
|
||||
className="object-cover"
|
||||
/>
|
||||
|
|
|
@ -42,10 +42,8 @@ export default function EndpointIcon({
|
|||
if (iconURL && (iconURL.includes('http') || iconURL.startsWith('/images/'))) {
|
||||
return (
|
||||
<ConvoIconURL
|
||||
preset={{
|
||||
...(conversation as TPreset),
|
||||
iconURL,
|
||||
}}
|
||||
iconURL={iconURL}
|
||||
modelLabel={conversation?.chatGptLabel ?? conversation?.modelLabel ?? ''}
|
||||
context={context}
|
||||
endpointIconURL={endpointIconURL}
|
||||
assistantAvatar={assistantAvatar}
|
||||
|
|
|
@ -6,7 +6,7 @@ import ContentParts from '~/components/Chat/Messages/Content/ContentParts';
|
|||
import PlaceholderRow from '~/components/Chat/Messages/ui/PlaceholderRow';
|
||||
import SiblingSwitch from '~/components/Chat/Messages/SiblingSwitch';
|
||||
import HoverButtons from '~/components/Chat/Messages/HoverButtons';
|
||||
import Icon from '~/components/Chat/Messages/MessageIcon';
|
||||
import MessageIcon from '~/components/Chat/Messages/MessageIcon';
|
||||
import SubRow from '~/components/Chat/Messages/SubRow';
|
||||
import { useMessageActions } from '~/hooks';
|
||||
import { cn, logger } from '~/utils';
|
||||
|
@ -65,6 +65,27 @@ const ContentRender = memo(
|
|||
[msg?.children, msg?.depth, latestMessage?.depth],
|
||||
);
|
||||
|
||||
const iconData = useMemo(
|
||||
() =>
|
||||
({
|
||||
endpoint: conversation?.endpoint,
|
||||
model: conversation?.model ?? msg?.model,
|
||||
iconURL: conversation?.iconURL ?? msg?.iconURL ?? '',
|
||||
modelLabel: conversation?.chatGptLabel ?? conversation?.modelLabel,
|
||||
isCreatedByUser: msg?.isCreatedByUser,
|
||||
} as TMessage & { modelLabel?: string }),
|
||||
[
|
||||
conversation?.chatGptLabel,
|
||||
conversation?.modelLabel,
|
||||
conversation?.endpoint,
|
||||
conversation?.iconURL,
|
||||
conversation?.model,
|
||||
msg?.model,
|
||||
msg?.iconURL,
|
||||
msg?.isCreatedByUser,
|
||||
],
|
||||
);
|
||||
|
||||
if (!msg) {
|
||||
return null;
|
||||
}
|
||||
|
@ -109,12 +130,7 @@ const ContentRender = memo(
|
|||
<div>
|
||||
<div className="pt-0.5">
|
||||
<div className="flex h-6 w-6 items-center justify-center overflow-hidden rounded-full">
|
||||
<Icon
|
||||
message={msg}
|
||||
conversation={conversation}
|
||||
assistant={assistant}
|
||||
agent={agent}
|
||||
/>
|
||||
<MessageIcon iconData={iconData} assistant={assistant} agent={agent} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { QueryKeys, Constants } from 'librechat-data-provider';
|
||||
import type { TMessage } from 'librechat-data-provider';
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import { useLocalize, useNewConvo } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
@ -10,6 +13,7 @@ export default function MobileNav({
|
|||
setNavVisible: Dispatch<SetStateAction<boolean>>;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const queryClient = useQueryClient();
|
||||
const { newConversation } = useNewConvo(0);
|
||||
const conversation = useRecoilValue(store.conversationByIndex(0));
|
||||
const { title = 'New Chat' } = conversation || {};
|
||||
|
@ -46,13 +50,19 @@ export default function MobileNav({
|
|||
</svg>
|
||||
</button>
|
||||
<h1 className="flex-1 overflow-hidden text-ellipsis whitespace-nowrap text-center text-sm font-normal">
|
||||
{title || localize('com_ui_new_chat')}
|
||||
{title ?? localize('com_ui_new_chat')}
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={localize('com_ui_new_chat')}
|
||||
className="m-1 inline-flex size-10 items-center justify-center rounded-full hover:bg-surface-hover"
|
||||
onClick={() => newConversation()}
|
||||
onClick={() => {
|
||||
queryClient.setQueryData<TMessage[]>(
|
||||
[QueryKeys.messages, conversation?.conversationId ?? Constants.NEW_CONVO],
|
||||
[],
|
||||
);
|
||||
newConversation();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { Search } from 'lucide-react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { QueryKeys, Constants } from 'librechat-data-provider';
|
||||
import { useGetEndpointsQuery } from 'librechat-data-provider/react-query';
|
||||
import type { TConversation } from 'librechat-data-provider';
|
||||
import type { TConversation, TMessage } from 'librechat-data-provider';
|
||||
import { getEndpointField, getIconEndpoint, getIconKey } from '~/utils';
|
||||
import { icons } from '~/components/Chat/Menus/Endpoints/Icons';
|
||||
import ConvoIconURL from '~/components/Endpoints/ConvoIconURL';
|
||||
|
@ -35,7 +37,12 @@ const NewChatButtonIcon = ({ conversation }: { conversation: TConversation | nul
|
|||
return (
|
||||
<div className="h-7 w-7 flex-shrink-0">
|
||||
{iconURL && iconURL.includes('http') ? (
|
||||
<ConvoIconURL preset={conversation} endpointIconURL={iconURL} context="nav" />
|
||||
<ConvoIconURL
|
||||
iconURL={iconURL}
|
||||
modelLabel={conversation?.chatGptLabel ?? conversation?.modelLabel ?? ''}
|
||||
endpointIconURL={iconURL}
|
||||
context="nav"
|
||||
/>
|
||||
) : (
|
||||
<div className="shadow-stroke relative flex h-full items-center justify-center rounded-full bg-white text-black">
|
||||
{endpoint && Icon != null && (
|
||||
|
@ -65,6 +72,7 @@ export default function NewChat({
|
|||
subHeaders?: React.ReactNode;
|
||||
isSmallScreen: boolean;
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
/** Note: this component needs an explicit index passed if using more than one */
|
||||
const { newConversation: newConvo } = useNewConvo(index);
|
||||
const navigate = useNavigate();
|
||||
|
@ -75,6 +83,10 @@ export default function NewChat({
|
|||
const clickHandler = (event: React.MouseEvent<HTMLAnchorElement>) => {
|
||||
if (event.button === 0 && !(event.ctrlKey || event.metaKey)) {
|
||||
event.preventDefault();
|
||||
queryClient.setQueryData<TMessage[]>(
|
||||
[QueryKeys.messages, conversation?.conversationId ?? Constants.NEW_CONVO],
|
||||
[],
|
||||
);
|
||||
newConvo();
|
||||
navigate('/c/new');
|
||||
toggleNav();
|
||||
|
@ -87,7 +99,7 @@ export default function NewChat({
|
|||
<a
|
||||
href="/"
|
||||
tabIndex={0}
|
||||
data-testid="nav-new-chat"
|
||||
data-testid="nav-new-chat-button"
|
||||
onClick={clickHandler}
|
||||
className={cn(
|
||||
'group flex h-10 items-center gap-2 rounded-lg px-2 font-medium transition-colors duration-200 hover:bg-surface-hover',
|
||||
|
|
|
@ -42,7 +42,7 @@ export default function Message(props: TMessageProps) {
|
|||
if (isCreatedByUser) {
|
||||
messageLabel = 'anonymous';
|
||||
} else {
|
||||
messageLabel = message.sender || '';
|
||||
messageLabel = message.sender ?? '';
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -30,14 +30,15 @@ export default function MessageIcon(
|
|||
[conversation, message],
|
||||
);
|
||||
|
||||
const iconURL = messageSettings?.iconURL;
|
||||
let endpoint = messageSettings?.endpoint;
|
||||
const iconURL = messageSettings.iconURL ?? '';
|
||||
let endpoint = messageSettings.endpoint;
|
||||
endpoint = getIconEndpoint({ endpointsConfig: undefined, iconURL, endpoint });
|
||||
|
||||
if (!message?.isCreatedByUser && iconURL && iconURL.includes('http')) {
|
||||
if (message?.isCreatedByUser !== true && iconURL && iconURL.includes('http')) {
|
||||
return (
|
||||
<ConvoIconURL
|
||||
preset={messageSettings as typeof messageSettings & TPreset}
|
||||
iconURL={iconURL}
|
||||
modelLabel={messageSettings.chatGptLabel ?? messageSettings.modelLabel ?? ''}
|
||||
context="message"
|
||||
assistantAvatar={assistantAvatar}
|
||||
assistantName={assistantName}
|
||||
|
@ -47,7 +48,7 @@ export default function MessageIcon(
|
|||
);
|
||||
}
|
||||
|
||||
if (message?.isCreatedByUser) {
|
||||
if (message?.isCreatedByUser === true) {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
|
@ -67,7 +68,7 @@ export default function MessageIcon(
|
|||
<MessageEndpointIcon
|
||||
{...messageSettings}
|
||||
endpoint={endpoint}
|
||||
iconURL={!assistant ? undefined : assistantAvatar}
|
||||
iconURL={assistant == null ? undefined : assistantAvatar}
|
||||
model={message?.model ?? conversation?.model}
|
||||
assistantName={assistantName}
|
||||
agentName={agentName}
|
||||
|
|
|
@ -116,7 +116,7 @@ export default function useChatHelpers(index = 0, paramId?: string) {
|
|||
|
||||
const handleRegenerate = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
const parentMessageId = latestMessage?.parentMessageId;
|
||||
const parentMessageId = latestMessage?.parentMessageId ?? '';
|
||||
if (!parentMessageId) {
|
||||
console.error('Failed to regenerate the message: parentMessageId not found.');
|
||||
return;
|
||||
|
|
|
@ -207,7 +207,6 @@ export default function useQueryParams({
|
|||
|
||||
return () => {
|
||||
clearInterval(intervalId);
|
||||
console.log('Cleanup: `useQueryParams` interval cleared');
|
||||
};
|
||||
}, [searchParams, methods, textAreaRef, newQueryConvo, newConversation]);
|
||||
}
|
||||
|
|
|
@ -72,22 +72,30 @@ export default function useMessageScrolling(messagesTree?: TMessage[] | null) {
|
|||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!messagesTree) {
|
||||
if (!messagesTree || messagesTree.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSubmitting && scrollToBottom && !abortScroll) {
|
||||
if (!messagesEndRef.current || !scrollableRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSubmitting && scrollToBottom && abortScroll !== true) {
|
||||
scrollToBottom();
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (abortScroll) {
|
||||
if (abortScroll === true) {
|
||||
scrollToBottom && scrollToBottom.cancel();
|
||||
}
|
||||
};
|
||||
}, [isSubmitting, messagesTree, scrollToBottom, abortScroll]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!messagesEndRef.current || !scrollableRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (scrollToBottom && autoScroll && conversationId !== Constants.NEW_CONVO) {
|
||||
scrollToBottom();
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ const showStopButtonByIndex = atomFamily<boolean, string | number>({
|
|||
default: false,
|
||||
});
|
||||
|
||||
const abortScrollFamily = atomFamily({
|
||||
const abortScrollFamily = atomFamily<boolean, string | number>({
|
||||
key: 'abortScrollByIndex',
|
||||
default: false,
|
||||
effects: [
|
||||
|
|
|
@ -78,7 +78,7 @@ test.describe('Messaging suite', () => {
|
|||
expect(currentUrl).toBe(initialUrl);
|
||||
|
||||
//cleanup the conversation
|
||||
await page.getByTestId('new-chat-button').click();
|
||||
await page.getByTestId('nav-new-chat-button').click();
|
||||
expect(page.url()).toBe(initialUrl);
|
||||
|
||||
// Click on the first conversation
|
||||
|
@ -158,7 +158,7 @@ test.describe('Messaging suite', () => {
|
|||
const currentUrl = page.url();
|
||||
const conversationId = currentUrl.split(basePath).pop() ?? '';
|
||||
expect(isUUID(conversationId)).toBeTruthy();
|
||||
await page.getByTestId('new-chat-button').click();
|
||||
await page.getByTestId('nav-new-chat-button').click();
|
||||
expect(page.url()).toBe(initialUrl);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue