refactor: basic message and send message. as well as model

THIS IS NOT FINISHED. DONT USE THIS
This commit is contained in:
Wentao Lyu 2023-03-28 22:39:27 +08:00
parent de8f519742
commit c7c30d8bb5
24 changed files with 1057 additions and 1035 deletions

View file

@ -1,20 +1,19 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useRecoilValue, useSetRecoilState, useResetRecoilState } from 'recoil';
import SubRow from './Content/SubRow';
import Content from './Content/Content';
import MultiMessage from './MultiMessage';
import HoverButtons from './HoverButtons';
import SiblingSwitch from './SiblingSwitch';
import { setConversation, setLatestMessage } from '~/store/convoSlice';
import { setModel, setCustomModel, setCustomGpt, setDisabled } from '~/store/submitSlice';
import { setMessages } from '~/store/messageSlice';
import { fetchById } from '~/utils/fetchers';
import { getIconOfModel } from '~/utils';
import { useMessageHandler } from '~/utils/handleSubmit';
import store from '~/store';
export default function Message({
conversation,
message,
messages,
scrollToBottom,
currentEditId,
setCurrentEditId,
@ -22,30 +21,24 @@ export default function Message({
siblingCount,
setSiblingIdx
}) {
const { isSubmitting, model, chatGptLabel, cursor, promptPrefix } = useSelector(state => state.submit);
const isSubmitting = useRecoilValue(store.isSubmitting);
const setLatestMessage = useSetRecoilState(store.latestMessage);
const { model, chatGptLabel, promptPrefix } = conversation;
const [abortScroll, setAbort] = useState(false);
const { sender, text, searchResult, isCreatedByUser, error, submitting } = message;
const textEditor = useRef(null);
const last = !message?.children?.length;
const edit = message.messageId == currentEditId;
const { ask } = useMessageHandler();
const dispatch = useDispatch();
// const currentConvo = convoMap[message.conversationId];
// const notUser = !isCreatedByUser; // sender.toLowerCase() !== 'user';
// const blinker = submitting && isSubmitting && last && !isCreatedByUser;
const { switchToConversation } = store.useConversation();
const blinker = submitting && isSubmitting;
const generateCursor = useCallback(() => {
if (!blinker) {
return '';
}
if (!cursor) {
return '';
}
return <span className="result-streaming"></span>;
}, [blinker, cursor]);
}, [blinker]);
useEffect(() => {
if (blinker && !abortScroll) {
@ -55,9 +48,7 @@ export default function Message({
useEffect(() => {
if (last) {
// TODO: stop using conversation.parentMessageId and remove it.
dispatch(setConversation({ parentMessageId: message?.messageId }));
dispatch(setLatestMessage({ ...message }));
setLatestMessage({ ...message });
}
}, [last, message]);
@ -110,22 +101,23 @@ export default function Message({
const clickSearchResult = async () => {
if (!searchResult) return;
dispatch(setMessages([]));
// dispatch(setMessages([]));
const convoResponse = await fetchById('convos', message.conversationId);
const convo = convoResponse.data;
if (convo?.chatGptLabel) {
dispatch(setModel('chatgptCustom'));
dispatch(setCustomModel(convo.chatGptLabel.toLowerCase()));
} else {
dispatch(setModel(convo.model));
dispatch(setCustomModel(null));
}
// if (convo?.chatGptLabel) {
// // dispatch(setModel('chatgptCustom'));
// // dispatch(setCustomModel(convo.chatGptLabel.toLowerCase()));
// } else {
// // dispatch(setModel(convo.model));
// // dispatch(setCustomModel(null));
// }
dispatch(setCustomGpt(convo));
dispatch(setConversation(convo));
const { data } = await fetchById('messages', message.conversationId);
dispatch(setMessages(data));
dispatch(setDisabled(false));
// dispatch(setCustomGpt(convo));
switchToConversation(convo);
// dispatch(setConversation(convo));
// const { data } = await fetchById('messages', message.conversationId);
// dispatch(setMessages(data));
// dispatch(setDisabled(false));
};
return (
@ -133,7 +125,6 @@ export default function Message({
<div
{...props}
onWheel={handleWheel}
// onClick={clickSearchResult}
>
<div className="relative m-auto flex gap-4 p-4 text-base md:max-w-2xl md:gap-6 md:py-6 lg:max-w-2xl lg:px-0 xl:max-w-3xl">
<div className="relative flex h-[30px] w-[30px] flex-col items-end text-right text-xs md:text-sm">
@ -199,17 +190,13 @@ export default function Message({
<div className="flex min-h-[20px] flex-grow flex-col items-start gap-4 whitespace-pre-wrap">
{/* <div className={`${blinker ? 'result-streaming' : ''} markdown prose dark:prose-invert light w-full break-words`}> */}
<div className="markdown prose dark:prose-invert light w-full break-words">
{!isCreatedByUser ?
<>
<Content
content={text}
/>
{generateCursor()}
</> :
<>
{text}
</>
}
{!isCreatedByUser ? (
<>
<Content content={text} />
</>
) : (
<>{text}</>
)}
</div>
</div>
)}
@ -230,8 +217,8 @@ export default function Message({
</div>
</div>
<MultiMessage
messageList={message.children}
messages={messages}
conversation={conversation}
messagesTree={message.children}
scrollToBottom={scrollToBottom}
currentEditId={currentEditId}
setCurrentEditId={setCurrentEditId}

View file

@ -2,44 +2,44 @@ import React, { useEffect, useState } from 'react';
import Message from './Message';
export default function MultiMessage({
messageList,
messages,
conversation,
messagesTree,
scrollToBottom,
currentEditId,
setCurrentEditId,
setCurrentEditId
}) {
const [siblingIdx, setSiblingIdx] = useState(0);
const setSiblingIdxRev = (value) => {
setSiblingIdx(messageList?.length - value - 1);
const setSiblingIdxRev = value => {
setSiblingIdx(messagesTree?.length - value - 1);
};
useEffect(() => {
// reset siblingIdx when changes, mostly a new message is submitting.
setSiblingIdx(0);
}, [messageList?.length])
}, [messagesTree?.length]);
// if (!messageList?.length) return null;
if (!(messageList && messageList.length)) {
if (!(messagesTree && messagesTree.length)) {
return null;
}
if (siblingIdx >= messageList?.length) {
if (siblingIdx >= messagesTree?.length) {
setSiblingIdx(0);
return null;
}
const message = messageList[messageList.length - siblingIdx - 1];
const message = messagesTree[messagesTree.length - siblingIdx - 1];
return (
<Message
key={message.messageId}
conversation={conversation}
message={message}
messages={messages}
scrollToBottom={scrollToBottom}
currentEditId={currentEditId}
setCurrentEditId={setCurrentEditId}
siblingIdx={messageList.length - siblingIdx - 1}
siblingCount={messageList.length}
siblingIdx={messagesTree.length - siblingIdx - 1}
siblingCount={messagesTree.length}
setSiblingIdx={setSiblingIdxRev}
/>
);

View file

@ -1,27 +1,30 @@
import React, { useEffect, useState, useRef, useCallback } from 'react';
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil';
import Spinner from '../svg/Spinner';
import { throttle } from 'lodash';
import { CSSTransition } from 'react-transition-group';
import ScrollToBottom from './ScrollToBottom';
import MultiMessage from './MultiMessage';
import { useSelector } from 'react-redux';
export default function Messages({ messages, messageTree }) {
import store from '~/store';
export default function Messages() {
const [currentEditId, setCurrentEditId] = useState(-1);
const { conversationId } = useSelector((state) => state.convo);
const { model, customModel } = useSelector((state) => state.submit);
const { models } = useSelector((state) => state.models);
const messagesTree = useRecoilValue(store.messagesTree);
const conversation = useRecoilValue(store.conversation) || {};
const { conversationId, model, chatGptLabel } = conversation;
const models = useRecoilValue(store.models) || [];
const [showScrollButton, setShowScrollButton] = useState(false);
const scrollableRef = useRef(null);
const messagesEndRef = useRef(null);
const modelName = models.find((element) => element.model == model)?.name;
const modelName = models.find(element => element.model == model)?.name;
useEffect(() => {
const timeoutId = setTimeout(() => {
const { scrollTop, scrollHeight, clientHeight } = scrollableRef.current;
const diff = Math.abs(scrollHeight - scrollTop);
const percent = Math.abs(clientHeight - diff ) / clientHeight;
const percent = Math.abs(clientHeight - diff) / clientHeight;
const hasScrollbar = scrollHeight > clientHeight && percent > 0.2;
setShowScrollButton(hasScrollbar);
}, 650);
@ -33,17 +36,24 @@ export default function Messages({ messages, messageTree }) {
clearTimeout(timeoutId);
window.removeEventListener('scroll', handleScroll);
};
}, [messages]);
}, [messagesTree]);
const scrollToBottom = useCallback(throttle(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
setShowScrollButton(false);
}, 750, { leading: true }), [messagesEndRef]);
const scrollToBottom = useCallback(
throttle(
() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
setShowScrollButton(false);
},
750,
{ leading: true }
),
[messagesEndRef]
);
const handleScroll = () => {
const { scrollTop, scrollHeight, clientHeight } = scrollableRef.current;
const diff = Math.abs(scrollHeight - scrollTop);
const percent = Math.abs(clientHeight - diff ) / clientHeight;
const percent = Math.abs(clientHeight - diff) / clientHeight;
if (percent <= 0.2) {
setShowScrollButton(false);
} else {
@ -57,7 +67,7 @@ export default function Messages({ messages, messageTree }) {
timeoutId = setTimeout(handleScroll, 100);
};
const scrollHandler = (e) => {
const scrollHandler = e => {
e.preventDefault();
scrollToBottom();
};
@ -68,20 +78,19 @@ export default function Messages({ messages, messageTree }) {
ref={scrollableRef}
onScroll={debouncedHandleScroll}
>
{/* <div className="flex-1 overflow-hidden"> */}
<div className="dark:gpt-dark-gray h-full">
<div className="dark:gpt-dark-gray flex h-full flex-col items-center text-sm">
<div className="flex w-full items-center justify-center gap-1 border-b border-black/10 bg-gray-50 p-3 text-sm text-gray-500 dark:border-gray-900/50 dark:bg-gray-700 dark:text-gray-300">
Model: {modelName} {customModel ? `(${customModel})` : null}
Model: {modelName} {chatGptLabel ? `(${chatGptLabel})` : null}
</div>
{(messageTree.length === 0 || !messages) ? (
{messagesTree === null ? (
<Spinner />
) : (
<>
<MultiMessage
key={conversationId} // avoid internal state mixture
messageList={messageTree}
messages={messages}
conversation={conversation}
messagesTree={messagesTree}
scrollToBottom={scrollToBottom}
currentEditId={currentEditId}
setCurrentEditId={setCurrentEditId}
@ -103,7 +112,6 @@ export default function Messages({ messages, messageTree }) {
/>
</div>
</div>
{/* </div> */}
</div>
);
}