chore: re-organize message modules, fix icon size, convo reset properly rebuilds Tree

This commit is contained in:
Danny Avila 2023-03-15 10:42:45 -04:00
parent 45ca0a8713
commit 96ca783517
8 changed files with 103 additions and 90 deletions

1
.gitignore vendored
View file

@ -47,7 +47,6 @@ bower_components/
.env .env
cache.json cache.json
api/data/ api/data/
.eslintrc.js
owner.yml owner.yml
archive archive
.vscode/settings.json .vscode/settings.json

View file

@ -8,7 +8,7 @@ import useDocumentTitle from '~/hooks/useDocumentTitle';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
const App = () => { const App = () => {
const { messages } = useSelector((state) => state.messages); const { messages, messageTree } = useSelector((state) => state.messages);
const { title } = useSelector((state) => state.convo); const { title } = useSelector((state) => state.convo);
const { conversationId } = useSelector((state) => state.convo); const { conversationId } = useSelector((state) => state.convo);
const [ navVisible, setNavVisible ]= useState(false) const [ navVisible, setNavVisible ]= useState(false)
@ -25,6 +25,7 @@ const App = () => {
) : ( ) : (
<Messages <Messages
messages={messages} messages={messages}
messageTree={messageTree}
/> />
)} )}
<TextChat messages={messages} /> <TextChat messages={messages} />

View file

@ -157,15 +157,17 @@ export default function TextChat({ messages }) {
const message = text.trim(); const message = text.trim();
const sender = model === 'chatgptCustom' ? chatGptLabel : model; const sender = model === 'chatgptCustom' ? chatGptLabel : model;
let parentMessageId = convo.parentMessageId || '00000000-0000-0000-0000-000000000000'; let parentMessageId = convo.parentMessageId || '00000000-0000-0000-0000-000000000000';
if (resetConvo(messages, sender)) { let currentMessages = messages;
if (resetConvo(currentMessages, sender)) {
parentMessageId = '00000000-0000-0000-0000-000000000000'; parentMessageId = '00000000-0000-0000-0000-000000000000';
dispatch(setNewConvo()); dispatch(setNewConvo());
currentMessages = [];
} }
const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true, parentMessageId , messageId: fakeMessageId }; const currentMsg = { sender: 'User', text: message, current: true, isCreatedByUser: true, parentMessageId , messageId: fakeMessageId };
const initialResponse = { sender, text: '', parentMessageId: fakeMessageId, submitting: true }; const initialResponse = { sender, text: '', parentMessageId: fakeMessageId, submitting: true };
dispatch(setSubmitState(true)); dispatch(setSubmitState(true));
dispatch(setMessages([...messages, currentMsg, initialResponse])); dispatch(setMessages([...currentMessages, currentMsg, initialResponse]));
dispatch(setText('')); dispatch(setText(''));
const submission = { const submission = {
@ -177,7 +179,7 @@ export default function TextChat({ messages }) {
chatGptLabel, chatGptLabel,
promptPrefix, promptPrefix,
}, },
messages, messages: currentMessages,
currentMsg, currentMsg,
initialResponse, initialResponse,
sender, sender,

View file

@ -1,5 +1,6 @@
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import TextWrapper from './TextWrapper'; import TextWrapper from './TextWrapper';
import MultiMessage from './MultiMessage';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import HoverButtons from './HoverButtons'; import HoverButtons from './HoverButtons';
import SiblingSwitch from './SiblingSwitch'; import SiblingSwitch from './SiblingSwitch';
@ -11,42 +12,6 @@ import { setText } from '~/store/textSlice';
import { setConversation } from '../../store/convoSlice'; import { setConversation } from '../../store/convoSlice';
import { getIconOfModel } from '../../utils'; import { getIconOfModel } from '../../utils';
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 <Message
key={messageList[messageList.length - siblingIdx - 1].messageId}
message={messageList[messageList.length - siblingIdx - 1]}
messages={messages}
scrollToBottom={scrollToBottom}
currentEditId={currentEditId}
setCurrentEditId={setCurrentEditId}
siblingIdx={messageList.length - siblingIdx - 1}
siblingCount={messageList.length}
setSiblingIdx={setSiblingIdxRev}
/>
}
export { MultiMessage };
export default function Message({ export default function Message({
message, message,
messages, messages,
@ -84,9 +49,9 @@ export default function Message({
dispatch(setConversation({parentMessageId: message?.messageId})) dispatch(setConversation({parentMessageId: message?.messageId}))
}, [last, ]) }, [last, ])
if (sender === '') { // if (sender === '') {
return <Spinner />; // return <Spinner />;
} // }
const enterEdit = (cancel) => setCurrentEditId(cancel?-1:message.messageId) const enterEdit = (cancel) => setCurrentEditId(cancel?-1:message.messageId)
@ -167,7 +132,7 @@ export default function Message({
> >
<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 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 w-[30px] flex-col items-end text-right text-xs md:text-sm"> <div className="relative flex h-[30px] w-[30px] flex-col items-end text-right text-xs md:text-sm">
{typeof icon === 'string' && icon.match(/[^\u0000-\u007F]+/) ? ( {typeof icon === 'string' && icon.match(/[^\u0000-\u007F]+/) ? (
<span className=" direction-rtl w-40 overflow-x-scroll">{icon}</span> <span className=" direction-rtl w-40 overflow-x-scroll">{icon}</span>
) : ( ) : (

View file

@ -0,0 +1,40 @@
import React, { useState } from 'react';
import Message from './Message';
export default function 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 (!(messageList && messageList.length)) {
return null;
}
if (siblingIdx >= messageList?.length) {
setSiblingIdx(0);
return null;
}
return (
<Message
key={messageList[messageList.length - siblingIdx - 1].messageId}
message={messageList[messageList.length - siblingIdx - 1]}
messages={messages}
scrollToBottom={scrollToBottom}
currentEditId={currentEditId}
setCurrentEditId={setCurrentEditId}
siblingIdx={messageList.length - siblingIdx - 1}
siblingCount={messageList.length}
setSiblingIdx={setSiblingIdxRev}
/>
);
}

View file

@ -1,12 +1,13 @@
import React, { useEffect, useState, useRef, useMemo } from 'react'; import React, { useEffect, useState, useRef, useMemo } from 'react';
import Spinner from '../svg/Spinner';
import { CSSTransition } from 'react-transition-group'; import { CSSTransition } from 'react-transition-group';
import ScrollToBottom from './ScrollToBottom'; import ScrollToBottom from './ScrollToBottom';
import { MultiMessage } from './Message'; import MultiMessage from './MultiMessage';
import Conversation from '../Conversations/Conversation'; import buildTree from '~/utils/buildTree';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
const Messages = ({ messages }) => { const Messages = ({ messages, messageTree }) => {
const [currentEditId, setCurrentEditId] = useState(-1) const [currentEditId, setCurrentEditId] = useState(-1);
const { conversationId } = useSelector((state) => state.convo); const { conversationId } = useSelector((state) => state.convo);
const [showScrollButton, setShowScrollButton] = useState(false); const [showScrollButton, setShowScrollButton] = useState(false);
const scrollableRef = useRef(null); const scrollableRef = useRef(null);
@ -23,26 +24,6 @@ const Messages = ({ messages }) => {
clearTimeout(timeoutId); clearTimeout(timeoutId);
}; };
}, [messages]); }, [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: []};
const parentMessage = messageMap[message.parentMessageId];
if (parentMessage)
parentMessage.children.push(messageMap[message.messageId]);
else
rootMessages.push(messageMap[message.messageId]);
});
return rootMessages;
}
const scrollToBottom = () => { const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
@ -79,28 +60,33 @@ const Messages = ({ messages }) => {
onScroll={debouncedHandleScroll} onScroll={debouncedHandleScroll}
> >
{/* <div className="flex-1 overflow-hidden"> */} {/* <div className="flex-1 overflow-hidden"> */}
<div className="h-full dark:gpt-dark-gray"> <div className="dark:gpt-dark-gray h-full">
<div className="flex h-full flex-col items-center text-sm dark:gpt-dark-gray"> <div className="dark:gpt-dark-gray flex h-full flex-col items-center text-sm">
<MultiMessage {messageTree.length === 0 ? (
key={conversationId} // avoid internal state mixture <Spinner />
messageList={messageTree} ) : (
messages={messages} <>
scrollToBottom={scrollToBottom} <MultiMessage
currentEditId={currentEditId} key={conversationId} // avoid internal state mixture
setCurrentEditId={setCurrentEditId} messageList={messageTree}
/> messages={messages}
<CSSTransition scrollToBottom={scrollToBottom}
in={showScrollButton} currentEditId={currentEditId}
timeout={400} setCurrentEditId={setCurrentEditId}
classNames="scroll-down" />
unmountOnExit={false} <CSSTransition
// appear in={showScrollButton}
> timeout={400}
{() => showScrollButton && <ScrollToBottom scrollHandler={scrollHandler} />} classNames="scroll-down"
</CSSTransition> unmountOnExit={false}
// appear
>
{() => showScrollButton && <ScrollToBottom scrollHandler={scrollHandler} />}
</CSSTransition>
</>
)}
<div <div
className="group h-32 w-full flex-shrink-0 dark:border-gray-900/50 dark:gpt-dark-gray md:h-48" className="dark:gpt-dark-gray group h-32 w-full flex-shrink-0 dark:border-gray-900/50 md:h-48"
ref={messagesEndRef} ref={messagesEndRef}
/> />
</div> </div>

View file

@ -1,7 +1,9 @@
import { createSlice } from '@reduxjs/toolkit'; import { createSlice } from '@reduxjs/toolkit';
import buildTree from '~/utils/buildTree';
const initialState = { const initialState = {
messages: [], messages: [],
messageTree: []
}; };
const currentSlice = createSlice({ const currentSlice = createSlice({
@ -10,6 +12,7 @@ const currentSlice = createSlice({
reducers: { reducers: {
setMessages: (state, action) => { setMessages: (state, action) => {
state.messages = action.payload; state.messages = action.payload;
state.messageTree = buildTree(action.payload);
}, },
setEmptyMessage: (state) => { setEmptyMessage: (state) => {
state.messages = [ state.messages = [

View file

@ -0,0 +1,17 @@
export default 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: []};
const parentMessage = messageMap[message.parentMessageId];
if (parentMessage)
parentMessage.children.push(messageMap[message.messageId]);
else
rootMessages.push(messageMap[message.messageId]);
});
return rootMessages;
}