From a4d5f6a3f2dcc78478129f1f9368b1e4ce982e1c Mon Sep 17 00:00:00 2001
From: Wentao Lyu <35-wentao.lyu@users.noreply.git.stereye.tech>
Date: Mon, 13 Mar 2023 21:44:30 +0800
Subject: [PATCH] feat: fully multipath and resubmit
---
api/server/routes/convos.js | 2 +-
client/src/components/Main/TextChat.jsx | 14 +-
client/src/components/Messages/Message.jsx | 191 ++++++++++++------
.../src/components/Messages/SiblingSwitch.jsx | 26 +++
client/src/components/Messages/index.jsx | 53 +++--
5 files changed, 197 insertions(+), 89 deletions(-)
create mode 100644 client/src/components/Messages/SiblingSwitch.jsx
diff --git a/api/server/routes/convos.js b/api/server/routes/convos.js
index 5862919f17..a82242db4a 100644
--- a/api/server/routes/convos.js
+++ b/api/server/routes/convos.js
@@ -22,7 +22,7 @@ router.post('/gen_title', async (req, res) => {
: await titleConvo({
model: convo?.model,
message: firstMessage?.text,
- // response: JSON.stringify(secondMessage?.text || '')
+ response: JSON.stringify(secondMessage?.text || '')
});
await saveConvo({
diff --git a/client/src/components/Main/TextChat.jsx b/client/src/components/Main/TextChat.jsx
index b541c9a51f..d1a4ed511b 100644
--- a/client/src/components/Main/TextChat.jsx
+++ b/client/src/components/Main/TextChat.jsx
@@ -34,7 +34,7 @@ export default function TextChat({ messages }) {
const messageHandler = (data, currentState, currentMsg) => {
const { messages, _currentMsg, message, sender } = currentState;
- dispatch(setMessages([...messages, currentMsg, { sender, text: data }]));
+ dispatch(setMessages([...messages, currentMsg, { sender, text: data, parentMessageId: currentMsg?.messageId, messageId: currentMsg?.messageId + '_' }]));
};
const createdHandler = (data, currentState, currentMsg) => {
@@ -152,11 +152,13 @@ export default function TextChat({ messages }) {
return;
}
+ // this is not a real messageId, it is used as placeholder before real messageId returned
+ const fakeMessageId = crypto.randomUUID();
const isCustomModel = model === 'chatgptCustom' || !initial[model];
const message = text.trim();
- const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true };
+ const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true, parentMessageId: convo.parentMessageId || '00000000-0000-0000-0000-000000000000', messageId: fakeMessageId };
const sender = model === 'chatgptCustom' ? chatGptLabel : model;
- const initialResponse = { sender, text: '' };
+ const initialResponse = { sender, text: '', parentMessageId: fakeMessageId };
dispatch(setSubmitState(true));
dispatch(setMessages([...messages, currentMsg, initialResponse]));
@@ -166,9 +168,7 @@ export default function TextChat({ messages }) {
convo,
isCustomModel,
message: {
- sender: 'User',
- text: message,
- isCreatedByUser: true,
+ ...currentMsg,
model,
chatGptLabel,
promptPrefix,
@@ -193,7 +193,7 @@ export default function TextChat({ messages }) {
payload = {
...payload,
conversationId: convo.conversationId,
- parentMessageId: convo.parentMessageId
+ parentMessageId: convo.parentMessageId || '00000000-0000-0000-0000-000000000000'
};
}
diff --git a/client/src/components/Messages/Message.jsx b/client/src/components/Messages/Message.jsx
index 2d6df0360e..1eff8c7d98 100644
--- a/client/src/components/Messages/Message.jsx
+++ b/client/src/components/Messages/Message.jsx
@@ -4,20 +4,59 @@ import { useSelector, useDispatch } from 'react-redux';
import GPTIcon from '../svg/GPTIcon';
import BingIcon from '../svg/BingIcon';
import HoverButtons from './HoverButtons';
+import SiblingSwitch from './SiblingSwitch';
import Spinner from '../svg/Spinner';
import { setError } from '~/store/convoSlice';
import { setMessages } from '~/store/messageSlice';
import { setSubmitState, setSubmission } from '~/store/submitSlice';
import { setText } from '~/store/textSlice';
+import { setConversation } from '../../store/convoSlice';
+
+const MultiMessage = ({
+ messageList,
+ messages,
+ scrollToBottom,
+ currentEditId,
+ setCurrentEditId
+}) => {
+ const [siblingIdx, setSiblingIdx] = useState(0)
+
+ const setSiblingIdxRev = (value) => {
+ setSiblingIdx(messageList?.length - value - 1)
+ }
+
+ if (!messageList?.length) return null;
+
+ if (siblingIdx >= messageList?.length) {
+ setSiblingIdx(0)
+ return null
+ }
+
+ return
+}
+
+export { MultiMessage };
export default function Message({
message,
messages,
- last = false,
scrollToBottom,
- edit,
- currentEditIdx,
- enterEdit
+ currentEditId,
+ setCurrentEditId,
+ siblingIdx,
+ siblingCount,
+ setSiblingIdx
}) {
const { isSubmitting, model, chatGptLabel, promptPrefix } = useSelector((state) => state.submit);
const [abortScroll, setAbort] = useState(false);
@@ -26,6 +65,9 @@ export default function Message({
const convo = useSelector((state) => state.convo);
const { initial } = useSelector((state) => state.models);
const { error: convoError } = convo;
+ const last = !message?.children?.length
+
+ const edit = message.messageId == currentEditId;
const dispatch = useDispatch();
@@ -37,11 +79,18 @@ export default function Message({
scrollToBottom();
}
}, [isSubmitting, text, blinker, scrollToBottom, abortScroll]);
-
+
+ useEffect(() => {
+ if (last)
+ dispatch(setConversation({parentMessageId: message?.messageId}))
+ }, [last, ])
+
if (sender === '') {
return ;
}
+ const enterEdit = (cancel) => setCurrentEditId(cancel?-1:message.messageId)
+
const handleWheel = () => {
if (blinker) {
setAbort(true);
@@ -105,97 +154,109 @@ export default function Message({
return;
}
+ // this is not a real messageId, it is used as placeholder before real messageId returned
+ const fakeMessageId = crypto.randomUUID();
const isCustomModel = model === 'chatgptCustom' || !initial[model];
- const currentMsg = { ...message, sender: 'User', text: text.trim(), current: true, isCreatedByUser: true };
- console.log(model)
+ const currentMsg = { ...message, sender: 'User', text: text.trim(), current: true, isCreatedByUser: true, messageId: fakeMessageId };
const sender = model === 'chatgptCustom' ? chatGptLabel : model;
- const initialResponse = { sender, text: '' };
+ const initialResponse = { sender, text: '', parentMessageId: fakeMessageId };
dispatch(setSubmitState(true));
- dispatch(setMessages([...messages.slice(0, currentEditIdx), currentMsg, initialResponse]));
+ dispatch(setMessages([...messages, currentMsg, initialResponse]));
dispatch(setText(''));
const submission = {
isCustomModel,
message: {
- ...message,
- text: text.trim(),
+ ...currentMsg,
model,
chatGptLabel,
promptPrefix,
},
- messages: messages.slice(0, currentEditIdx),
+ messages: messages,
currentMsg,
initialResponse,
sender,
};
- console.log('User Input:', message);
+ console.log('User Input:', currentMsg?.text);
// handleSubmit(submission);
dispatch(setSubmission(submission));
+ setSiblingIdx(siblingCount - 1)
enterEdit(true);
};
return (
-
-
-
- {typeof icon === 'string' && icon.match(/[^\u0000-\u007F]+/) ? (
- {icon}
- ) : (
- icon
- )}
-
-
-
- {error ? (
-
- ) :
- edit ? (
-
- {/*
*/}
-
-
+ <>
+
+
+
+
+ {typeof icon === 'string' && icon.match(/[^\u0000-\u007F]+/) ? (
+ {icon}
+ ) : (
+ icon
+ )}
+
+
+
+
+ {error ? (
+
+
{text}
-
-
-
-
- ) : (
-
- {/*
*/}
-
- {!isCreatedByUser ? wrapText(text) : text}
- {blinker &&
█}
+ ) :
+ edit ? (
+
+ {/*
*/}
+
+
+ {text}
+
+
+
+
+
-
- )}
+ ) : (
+
+ {/*
*/}
+
+ {!isCreatedByUser ? wrapText(text) : text}
+ {blinker && █}
+
+
+ )}
+
+
enterEdit()}/>
-
enterEdit()}/>
-
+
+ >
);
}
diff --git a/client/src/components/Messages/SiblingSwitch.jsx b/client/src/components/Messages/SiblingSwitch.jsx
new file mode 100644
index 0000000000..b5586d6a8c
--- /dev/null
+++ b/client/src/components/Messages/SiblingSwitch.jsx
@@ -0,0 +1,26 @@
+import React from 'react';
+
+export default function SiblingSwitch({
+ siblingIdx,
+ siblingCount,
+ setSiblingIdx
+}) {
+ const previous = () => {
+ setSiblingIdx(siblingIdx - 1);
+ }
+
+ const next = () => {
+ setSiblingIdx(siblingIdx + 1);
+ }
+ return siblingCount > 1 ? (
+
+
+
{siblingIdx + 1}/{siblingCount}
+
+
+ ):null;
+}
diff --git a/client/src/components/Messages/index.jsx b/client/src/components/Messages/index.jsx
index 7d9bd69d44..5d191e61cf 100644
--- a/client/src/components/Messages/index.jsx
+++ b/client/src/components/Messages/index.jsx
@@ -1,15 +1,17 @@
-import React, { useEffect, useState, useRef } from 'react';
+import React, { useEffect, useState, useRef, useMemo } from 'react';
import { CSSTransition } from 'react-transition-group';
import ScrollToBottom from './ScrollToBottom';
-import Message from './Message';
+import { MultiMessage } from './Message';
+import Conversation from '../Conversations/Conversation';
+import { useSelector } from 'react-redux';
const Messages = ({ messages }) => {
- const [currentEditIdx, setCurrentEditIdx] = useState(-1)
+ const [currentEditId, setCurrentEditId] = useState(-1)
+ const { conversationId } = useSelector((state) => state.convo);
const [showScrollButton, setShowScrollButton] = useState(false);
const scrollableRef = useRef(null);
const messagesEndRef = useRef(null);
-
useEffect(() => {
const timeoutId = setTimeout(() => {
const scrollable = scrollableRef.current;
@@ -21,6 +23,29 @@ const Messages = ({ messages }) => {
clearTimeout(timeoutId);
};
}, [messages]);
+
+ const messageTree = useMemo(() => buildTree(messages), [messages, ]);
+
+ function buildTree(messages) {
+ let messageMap = {};
+ let rootMessages = [];
+
+ // Traverse the messages array and store each element in messageMap.
+ messages.forEach(message => {
+ messageMap[message.messageId] = {...message, children: []};
+
+ if (message.parentMessageId === "00000000-0000-0000-0000-000000000000") {
+ rootMessages.push(messageMap[message.messageId]);
+ } else {
+ const parentMessage = messageMap[message.parentMessageId];
+ if (parentMessage) {
+ parentMessage.children.push(messageMap[message.messageId]);
+ }
+ }
+ });
+
+ return rootMessages;
+ }
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
@@ -59,18 +84,14 @@ const Messages = ({ messages }) => {
{/*
*/}
- {messages.map((message, i) => (
- setCurrentEditIdx(cancel?-1:i)}
- />
- ))}
+