Refactor ChatPanel to use Redux for message handling

- Replace messageHandlers with Redux actions and selectors.
- Add newMessage, updateMessage, shiftMessage, and popMessage actions to chatSlice.
- Update MessageContainer to use useSelector for messages.
This commit is contained in:
Rankin Zheng 2023-06-08 19:54:21 +08:00
parent 6a0744515b
commit 1771e87028
3 changed files with 45 additions and 16 deletions

View File

@ -12,7 +12,8 @@ import { IconCheck, IconCopy } from "@tabler/icons-react";
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { import {
selectGenerating, selectGenerating,
selectResponsed selectResponsed,
selectMessages,
} from './chatSlice'; } from './chatSlice';
@ -162,9 +163,9 @@ const MessageHeader = (props: any) => {
}; };
const MessageContainer = (props: any) => { const MessageContainer = (props: any) => {
const { messages, width, onRefillClick } = props; const { width, onRefillClick } = props;
const generating = useSelector(selectGenerating); const messages = useSelector(selectMessages);
const messageList = messages.map((item: any, index: number) => { const messageList = messages.map((item: any, index: number) => {
const { message: messageText, type: messageType, contexts } = item; const { message: messageText, type: messageType, contexts } = item;
@ -193,7 +194,7 @@ const MessageContainer = (props: any) => {
}}> }}>
<MessageContext contexts={contexts} /> <MessageContext contexts={contexts} />
<CodeBlock messageText={messageText} /> <CodeBlock messageText={messageText} />
<MessageBlink generating={generating} messageType={messageType} lastMessage={index === messages.length - 1} /> <MessageBlink messageType={messageType} lastMessage={index === messages.length - 1} />
</Container > </Container >
</Stack > </Stack >
{index !== messages.length - 1 && <Divider my={3} />} {index !== messages.length - 1 && <Divider my={3} />}

View File

@ -7,6 +7,7 @@ export const chatSlice = createSlice({
responsed: false, responsed: false,
currentMessage: '', currentMessage: '',
errorMessage: '', errorMessage: '',
messages: <any>[],
}, },
reducers: { reducers: {
startGenerating: (state) => { startGenerating: (state) => {
@ -23,6 +24,21 @@ export const chatSlice = createSlice({
state.responsed = true; state.responsed = true;
state.currentMessage = action.payload; state.currentMessage = action.payload;
}, },
newMessage: (state, action) => {
state.messages.push(action.payload);
},
updateMessage: (state, action) => {
state.messages[action.payload.index] = action.payload.newMessage;
},
shiftMessage: (state) => {
state.messages.splice(0, 1);
},
popMessage: (state) => {
state.messages.pop();
},
clearMessages: (state) => {
state.messages.length = 0;
},
happendError: (state, action) => { happendError: (state, action) => {
state.errorMessage = action.payload; state.errorMessage = action.payload;
} }
@ -33,12 +49,18 @@ export const selectGenerating = state => state.chat.generating;
export const selectResponsed = state => state.chat.responsed; export const selectResponsed = state => state.chat.responsed;
export const selectCurrentMessage = state => state.chat.currentMessage; export const selectCurrentMessage = state => state.chat.currentMessage;
export const selectErrorMessage = state => state.chat.errorMessage; export const selectErrorMessage = state => state.chat.errorMessage;
export const selectMessages = state => state.chat.messages;
export const { export const {
startGenerating, startGenerating,
stopGenerating, stopGenerating,
startResponsing, startResponsing,
happendError, happendError,
newMessage,
shiftMessage,
popMessage,
clearMessages,
updateMessage,
} = chatSlice.actions; } = chatSlice.actions;
export default chatSlice.reducer; export default chatSlice.reducer;

View File

@ -16,10 +16,15 @@ import {
stopGenerating, stopGenerating,
startResponsing, startResponsing,
happendError, happendError,
newMessage,
updateMessage,
shiftMessage,
popMessage,
selectGenerating, selectGenerating,
selectResponsed, selectResponsed,
selectCurrentMessage, selectCurrentMessage,
selectErrorMessage, selectErrorMessage,
selectMessages,
} from './chatSlice'; } from './chatSlice';
import InputMessage from './InputMessage'; import InputMessage from './InputMessage';
@ -78,9 +83,9 @@ const chatPanel = () => {
const currentMessage = useSelector(selectCurrentMessage); const currentMessage = useSelector(selectCurrentMessage);
const errorMessage = useSelector(selectErrorMessage); const errorMessage = useSelector(selectErrorMessage);
const responsed = useSelector(selectResponsed); const responsed = useSelector(selectResponsed);
const messages = useSelector(selectMessages);
const [chatContainerRef, chatContainerRect] = useResizeObserver(); const [chatContainerRef, chatContainerRect] = useResizeObserver();
const scrollViewport = useRef<HTMLDivElement>(null); const scrollViewport = useRef<HTMLDivElement>(null);
const [messages, messageHandlers] = useListState<{ type: string; message: string; contexts?: any[] }>([]);
const { height, width } = useViewportSize(); const { height, width } = useViewportSize();
const [scrollPosition, onScrollPositionChange] = useState({ x: 0, y: 0 }); const [scrollPosition, onScrollPositionChange] = useState({ x: 0, y: 0 });
const [stopScrolling, setStopScrolling] = useState(false); const [stopScrolling, setStopScrolling] = useState(false);
@ -114,8 +119,8 @@ const chatPanel = () => {
message.entries?.forEach(({ hash, user, date, request, response, context }, index) => { message.entries?.forEach(({ hash, user, date, request, response, context }, index) => {
if (index < message.entries.length - messageCount) return; if (index < message.entries.length - messageCount) return;
const contexts = context.map(({ content, role }) => ({ context: JSON.parse(content) })); const contexts = context.map(({ content, role }) => ({ context: JSON.parse(content) }));
messageHandlers.append({ type: 'user', message: request, contexts: contexts }); dispatch(newMessage({ type: 'user', message: request, contexts: contexts }));
messageHandlers.append({ type: 'bot', message: response }); dispatch(newMessage({ type: 'bot', message: response }));
}); });
}); });
timer.start(); timer.start();
@ -138,7 +143,7 @@ const chatPanel = () => {
useEffect(() => { useEffect(() => {
if (generating) { if (generating) {
// new a bot message // new a bot message
messageHandlers.append({ type: 'bot', message: currentMessage }); dispatch(newMessage({ type: 'bot', message: currentMessage }));
} }
}, [generating]); }, [generating]);
@ -148,14 +153,18 @@ const chatPanel = () => {
const lastMessage = messages[lastIndex]; const lastMessage = messages[lastIndex];
if (currentMessage && lastMessage?.type === 'bot') { if (currentMessage && lastMessage?.type === 'bot') {
// update the last one bot message // update the last one bot message
messageHandlers.setItem(lastIndex, { type: 'bot', message: currentMessage }); // messageHandlers.setItem();
dispatch(updateMessage({
index: lastIndex,
newMessage: { type: 'bot', message: currentMessage }
}));
} }
timer.start(); timer.start();
}, [currentMessage]); }, [currentMessage]);
useEffect(() => { useEffect(() => {
if (messages.length > messageCount * 2) { if (messages.length > messageCount * 2) {
messageHandlers.remove(0, 1); dispatch(shiftMessage());
} }
timer.start(); timer.start();
}, [messages]); }, [messages]);
@ -190,10 +199,7 @@ const chatPanel = () => {
contexts.length = 0; contexts.length = 0;
contextsHandlers.append(...messageContexts); contextsHandlers.append(...messageContexts);
}} }}
width={chatContainerRect.width} width={chatContainerRect.width} />
generating={generating}
messages={messages}
responsed={responsed} />
{errorMessage && {errorMessage &&
<Alert styles={{ message: { fontSize: 'var(--vscode-editor-font-size)' } }} w={chatContainerRect.width} mb={20} color="gray" variant="filled"> <Alert styles={{ message: { fontSize: 'var(--vscode-editor-font-size)' } }} w={chatContainerRect.width} mb={20} color="gray" variant="filled">
{errorMessage} {errorMessage}
@ -221,7 +227,7 @@ const chatPanel = () => {
messageUtil.sendMessage({ messageUtil.sendMessage({
command: 'regeneration' command: 'regeneration'
}); });
messageHandlers.pop(); dispatch(popMessage());
dispatch(startGenerating()); dispatch(startGenerating());
}} /> }} />
</Center> </Center>
@ -233,7 +239,7 @@ const chatPanel = () => {
width={chatContainerRect.width} width={chatContainerRect.width}
onSendClick={(input: string, contexts: any) => { onSendClick={(input: string, contexts: any) => {
// Add the user's message to the chat UI // Add the user's message to the chat UI
messageHandlers.append({ type: 'user', message: input, contexts: contexts ? [...contexts].map((item) => ({ ...item })) : undefined }); dispatch(newMessage({ type: 'user', message: input, contexts: contexts ? [...contexts].map((item) => ({ ...item })) : undefined }));
// start generating // start generating
dispatch(startGenerating()); dispatch(startGenerating());
}} /> }} />