🛠️ refactor: Improve Input Placeholder Handling and Error Management 🔄 (#1296)

* chore: identify new chat buttons with testid

* fix: avoid parsing error in useSSE, which causes errorHandler to fail

* fix: ensure last message isn't setting latestMessage when conversationId is `new` and text is the same due to possible re-renders

* refactor: set placeholder through inputRef and useEffect

* Update useSSE.ts

* Update useSSE.ts
This commit is contained in:
Danny Avila 2023-12-06 14:10:06 -05:00 committed by GitHub
parent 2e390596ea
commit 9b2359fc27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 23 deletions

View file

@ -1,4 +1,5 @@
import { useEffect, useRef } from 'react';
import debounce from 'lodash/debounce';
import { TEndpointOption, getResponseSender } from 'librechat-data-provider';
import type { KeyboardEvent } from 'react';
import { useChatContext } from '~/Providers/ChatContext';
@ -15,8 +16,9 @@ export default function useTextarea({ setText, submitMessage, disabled = false }
const { handleFiles } = useFileHandling();
const localize = useLocalize();
const isNotAppendable = (latestMessage?.unfinished && !isSubmitting) || latestMessage?.error;
const { conversationId, jailbreak } = conversation || {};
const isNotAppendable = (latestMessage?.unfinished && !isSubmitting) || latestMessage?.error;
// && (conversationId?.length ?? 0) > 6; // also ensures that we don't show the wrong placeholder
// auto focus to input, when enter a conversation.
useEffect(() => {
@ -44,6 +46,44 @@ export default function useTextarea({ setText, submitMessage, disabled = false }
return () => clearTimeout(timeoutId);
}, [isSubmitting]);
useEffect(() => {
if (inputRef.current?.value) {
return;
}
const getPlaceholderText = () => {
if (disabled) {
return localize('com_endpoint_config_placeholder');
}
if (isNotAppendable) {
return localize('com_endpoint_message_not_appendable');
}
const sender = getResponseSender(conversation as TEndpointOption);
return `${localize('com_endpoint_message')} ${sender ? sender : 'ChatGPT'}`;
};
const placeholder = getPlaceholderText();
if (inputRef.current?.getAttribute('placeholder') === placeholder) {
return;
}
const setPlaceholder = () => {
const placeholder = getPlaceholderText();
if (inputRef.current?.getAttribute('placeholder') !== placeholder) {
inputRef.current?.setAttribute('placeholder', placeholder);
}
};
const debouncedSetPlaceholder = debounce(setPlaceholder, 80);
debouncedSetPlaceholder();
return () => debouncedSetPlaceholder.cancel();
}, [conversation, disabled, latestMessage, isNotAppendable, localize]);
const handleKeyDown = (e: KeyEvent) => {
if (e.key === 'Enter' && isSubmitting) {
return;
@ -82,19 +122,6 @@ export default function useTextarea({ setText, submitMessage, disabled = false }
isComposing.current = false;
};
const getPlaceholderText = () => {
if (disabled) {
return localize('com_endpoint_config_placeholder');
}
if (isNotAppendable) {
return localize('com_endpoint_message_not_appendable');
}
const sender = getResponseSender(conversation as TEndpointOption);
return `${localize('com_endpoint_message')} ${sender ? sender : 'ChatGPT'}`;
};
const handlePaste = (e: React.ClipboardEvent<HTMLTextAreaElement>) => {
if (e.clipboardData && e.clipboardData.files.length > 0) {
e.preventDefault();
@ -110,6 +137,5 @@ export default function useTextarea({ setText, submitMessage, disabled = false }
handlePaste,
handleCompositionStart,
handleCompositionEnd,
placeholder: getPlaceholderText(),
};
}

View file

@ -1,4 +1,4 @@
import { useEffect } from 'react';
import { useEffect, useRef } from 'react';
import copy from 'copy-to-clipboard';
import type { TMessage } from 'librechat-data-provider';
import type { TMessageProps } from '~/common';
@ -6,6 +6,7 @@ import Icon from '~/components/Endpoints/Icon';
import { useChatContext } from '~/Providers';
export default function useMessageHelpers(props: TMessageProps) {
const latestText = useRef('');
const { message, currentEditId, setCurrentEditId } = props;
const {
@ -26,10 +27,15 @@ export default function useMessageHelpers(props: TMessageProps) {
useEffect(() => {
if (!message) {
return;
} else if (isLast) {
} else if (
isLast &&
conversation?.conversationId !== 'new' &&
latestText.current !== message.text
) {
setLatestMessage({ ...message });
latestText.current = message.text;
}
}, [isLast, message, setLatestMessage]);
}, [isLast, message, setLatestMessage, conversation?.conversationId]);
const enterEdit = (cancel?: boolean) =>
setCurrentEditId && setCurrentEditId(cancel ? -1 : messageId);

View file

@ -210,6 +210,11 @@ export default function useSSE(submission: TSubmission | null, index = 0) {
const errorHandler = (data: TResData, submission: TSubmission) => {
const { messages, message } = submission;
if (!data.conversationId) {
setIsSubmitting(false);
return;
}
console.log('Error:', data);
const errorResponse = tMessageSchema.parse({
...data,