feat: performance optimisation

fixes partially #9 and #16
This commit is contained in:
Jing Hua 2023-03-05 22:59:31 +08:00
parent 59466e987a
commit 4fa5c14734
17 changed files with 350 additions and 410 deletions

View file

@ -6,38 +6,30 @@ import Menu from './components/Menu';
import ConfigMenu from './components/ConfigMenu'; import ConfigMenu from './components/ConfigMenu';
import useSaveToLocalStorage from '@hooks/useSaveToLocalStorage'; import useSaveToLocalStorage from '@hooks/useSaveToLocalStorage';
import useUpdateCharts from '@hooks/useUpdateChats';
import useInitialiseNewChat from '@hooks/useInitialiseNewChat'; import useInitialiseNewChat from '@hooks/useInitialiseNewChat';
import { ChatInterface } from '@type/chat'; import { ChatInterface } from '@type/chat';
function App() { function App() {
useSaveToLocalStorage(); useSaveToLocalStorage();
useUpdateCharts();
const initialiseNewChat = useInitialiseNewChat(); const initialiseNewChat = useInitialiseNewChat();
const [setChats, setMessages, setCurrentChatIndex] = useStore((state) => [ const setChats = useStore((state) => state.setChats);
state.setChats, const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
state.setMessages,
state.setCurrentChatIndex,
]);
useEffect(() => { useEffect(() => {
// localStorage.removeItem('chats');
const storedChats = localStorage.getItem('chats'); const storedChats = localStorage.getItem('chats');
if (storedChats) { if (storedChats) {
try { try {
const chats: ChatInterface[] = JSON.parse(storedChats); const chats: ChatInterface[] = JSON.parse(storedChats);
if (chats.length > 0) { if (chats.length > 0) {
setChats(chats); setChats(chats);
setMessages(chats[0].messages);
setCurrentChatIndex(0); setCurrentChatIndex(0);
} else { } else {
initialiseNewChat(); initialiseNewChat();
} }
} catch (e: unknown) { } catch (e: unknown) {
setChats([]); setChats([]);
setMessages([]);
setCurrentChatIndex(-1); setCurrentChatIndex(-1);
console.log(e); console.log(e);
} }

View file

@ -1,4 +1,4 @@
import React, { createRef, useState } from 'react'; import React from 'react';
import ScrollToBottom from 'react-scroll-to-bottom'; import ScrollToBottom from 'react-scroll-to-bottom';
import useStore from '@store/store'; import useStore from '@store/store';
@ -11,11 +11,15 @@ import CrossIcon from '@icon/CrossIcon';
import useSubmit from '@hooks/useSubmit'; import useSubmit from '@hooks/useSubmit';
const ChatContent = () => { const ChatContent = () => {
const [messages, inputRole, setError] = useStore((state) => [ const inputRole = useStore((state) => state.inputRole);
state.messages, const setError = useStore((state) => state.setError);
state.inputRole, const messages = useStore((state) =>
state.setError, state.chats ? state.chats[state.currentChatIndex].messages : []
]); );
const stickyIndex = useStore((state) =>
state.chats ? state.chats[state.currentChatIndex].messages.length : 0
);
const { handleSubmit, error } = useSubmit(); const { handleSubmit, error } = useSubmit();
return ( return (
@ -38,7 +42,12 @@ const ChatContent = () => {
<NewMessageButton messageIndex={index} /> <NewMessageButton messageIndex={index} />
</> </>
))} ))}
<Message role={inputRole} content='' messageIndex={-1} sticky /> <Message
role={inputRole}
content=''
messageIndex={stickyIndex}
sticky
/>
{error !== '' && ( {error !== '' && (
<div className='relative bg-red-600/50 p-2 rounded-sm w-3/5 mt-3'> <div className='relative bg-red-600/50 p-2 rounded-sm w-3/5 mt-3'>

View file

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import useStore from '@store/store';
import Avatar from './Avatar'; import Avatar from './Avatar';
import MessageContent from './MessageContent'; import MessageContent from './MessageContent';
@ -14,7 +13,8 @@ import RoleSelector from './RoleSelector';
// }; // };
const backgroundStyle = ['dark:bg-gray-800', 'bg-gray-50 dark:bg-[#444654]']; const backgroundStyle = ['dark:bg-gray-800', 'bg-gray-50 dark:bg-[#444654]'];
const Message = ({ const Message = React.memo(
({
role, role,
content, content,
messageIndex, messageIndex,
@ -25,14 +25,10 @@ const Message = ({
messageIndex: number; messageIndex: number;
sticky?: boolean; sticky?: boolean;
}) => { }) => {
const stickyIndex = useStore((state) => state.messages.length);
return ( return (
<div <div
className={`w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group ${ className={`w-full border-b border-black/10 dark:border-gray-900/50 text-gray-800 dark:text-gray-100 group ${
sticky backgroundStyle[messageIndex % 2]
? backgroundStyle[stickyIndex % 2]
: backgroundStyle[messageIndex % 2]
}`} }`}
key={ key={
messageIndex !== -1 messageIndex !== -1
@ -58,6 +54,7 @@ const Message = ({
</div> </div>
</div> </div>
); );
}; }
);
export default Message; export default Message;

View file

@ -17,7 +17,7 @@ import DownChevronArrow from '@icon/DownChevronArrow';
import useSubmit from '@hooks/useSubmit'; import useSubmit from '@hooks/useSubmit';
import { MessageInterface } from '@type/chat'; import { ChatInterface } from '@type/chat';
import PopupModal from '@components/PopupModal'; import PopupModal from '@components/PopupModal';
@ -56,7 +56,8 @@ const MessageContent = ({
); );
}; };
const ContentView = ({ const ContentView = React.memo(
({
role, role,
content, content,
setIsEdit, setIsEdit,
@ -67,24 +68,29 @@ const ContentView = ({
setIsEdit: React.Dispatch<React.SetStateAction<boolean>>; setIsEdit: React.Dispatch<React.SetStateAction<boolean>>;
messageIndex: number; messageIndex: number;
}) => { }) => {
const { handleSubmit, error } = useSubmit(); const { handleSubmit } = useSubmit();
const [isDelete, setIsDelete] = useState<boolean>(false); const [isDelete, setIsDelete] = useState<boolean>(false);
const [copied, setCopied] = useState<boolean>(false); const [copied, setCopied] = useState<boolean>(false);
const currentChatIndex = useStore((state) => state.currentChatIndex);
const [messages, setMessages] = useStore((state) => [ const setChats = useStore((state) => state.setChats);
state.messages, const lastMessageIndex = useStore((state) =>
state.setMessages, state.chats ? state.chats[state.currentChatIndex].messages.length - 1 : 0
]); );
const handleDelete = () => { const handleDelete = () => {
const updatedMessages = JSON.parse(JSON.stringify(messages)); const updatedChats: ChatInterface[] = JSON.parse(
updatedMessages.splice(messageIndex, 1); JSON.stringify(useStore.getState().chats)
setMessages(updatedMessages); );
updatedChats[currentChatIndex].messages.splice(messageIndex, 1);
setChats(updatedChats);
}; };
const handleMove = (direction: 'up' | 'down') => { const handleMove = (direction: 'up' | 'down') => {
const updatedMessages = JSON.parse(JSON.stringify(messages)); const updatedChats: ChatInterface[] = JSON.parse(
JSON.stringify(useStore.getState().chats)
);
const updatedMessages = updatedChats[currentChatIndex].messages;
const temp = updatedMessages[messageIndex]; const temp = updatedMessages[messageIndex];
if (direction === 'up') { if (direction === 'up') {
updatedMessages[messageIndex] = updatedMessages[messageIndex - 1]; updatedMessages[messageIndex] = updatedMessages[messageIndex - 1];
@ -93,13 +99,16 @@ const ContentView = ({
updatedMessages[messageIndex] = updatedMessages[messageIndex + 1]; updatedMessages[messageIndex] = updatedMessages[messageIndex + 1];
updatedMessages[messageIndex + 1] = temp; updatedMessages[messageIndex + 1] = temp;
} }
setMessages(updatedMessages); setChats(updatedChats);
}; };
const handleRefresh = () => { const handleRefresh = () => {
const updatedMessages = JSON.parse(JSON.stringify(messages)); const updatedChats: ChatInterface[] = JSON.parse(
JSON.stringify(useStore.getState().chats)
);
const updatedMessages = updatedChats[currentChatIndex].messages;
updatedMessages.splice(updatedMessages.length - 1, 1); updatedMessages.splice(updatedMessages.length - 1, 1);
setMessages(updatedMessages); setChats(updatedChats);
handleSubmit(); handleSubmit();
}; };
@ -177,13 +186,13 @@ const ContentView = ({
<div className='flex justify-end gap-2 w-full mt-2'> <div className='flex justify-end gap-2 w-full mt-2'>
{isDelete || ( {isDelete || (
<> <>
{role === 'assistant' && messageIndex === messages?.length - 1 && ( {role === 'assistant' && messageIndex === lastMessageIndex && (
<RefreshButton onClick={handleRefresh} /> <RefreshButton onClick={handleRefresh} />
)} )}
{messageIndex !== 0 && ( {messageIndex !== 0 && (
<UpButton onClick={() => handleMove('up')} /> <UpButton onClick={() => handleMove('up')} />
)} )}
{messageIndex !== messages?.length - 1 && ( {messageIndex !== lastMessageIndex && (
<DownButton onClick={() => handleMove('down')} /> <DownButton onClick={() => handleMove('down')} />
)} )}
@ -207,7 +216,8 @@ const ContentView = ({
</div> </div>
</> </>
); );
}; }
);
const MessageButton = ({ const MessageButton = ({
onClick, onClick,
@ -286,11 +296,9 @@ const EditView = ({
messageIndex: number; messageIndex: number;
sticky?: boolean; sticky?: boolean;
}) => { }) => {
const [messages, setMessages, inputRole] = useStore((state) => [ const inputRole = useStore((state) => state.inputRole);
state.messages, const setChats = useStore((state) => state.setChats);
state.setMessages, const currentChatIndex = useStore((state) => state.currentChatIndex);
state.inputRole,
]);
const [_content, _setContent] = useState<string>(content); const [_content, _setContent] = useState<string>(content);
const [isModalOpen, setIsModalOpen] = useState<boolean>(false); const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
@ -319,9 +327,10 @@ const EditView = ({
const handleSave = () => { const handleSave = () => {
if (_content === '') return; if (_content === '') return;
const updatedMessages: MessageInterface[] = JSON.parse( const updatedChats: ChatInterface[] = JSON.parse(
JSON.stringify(messages) JSON.stringify(useStore.getState().chats)
); );
const updatedMessages = updatedChats[currentChatIndex].messages;
if (sticky) { if (sticky) {
updatedMessages.push({ role: inputRole, content: _content }); updatedMessages.push({ role: inputRole, content: _content });
_setContent(''); _setContent('');
@ -330,26 +339,29 @@ const EditView = ({
updatedMessages[messageIndex].content = _content; updatedMessages[messageIndex].content = _content;
setIsEdit(false); setIsEdit(false);
} }
setMessages(updatedMessages); setChats(updatedChats);
}; };
const { handleSubmit } = useSubmit(); const { handleSubmit } = useSubmit();
const handleSaveAndSubmit = () => { const handleSaveAndSubmit = () => {
if (_content == '') return; if (_content == '') return;
const updatedMessages: MessageInterface[] = JSON.parse( const updatedChats: ChatInterface[] = JSON.parse(
JSON.stringify(messages) JSON.stringify(useStore.getState().chats)
); );
const updatedMessages = updatedChats[currentChatIndex].messages;
if (sticky) { if (sticky) {
updatedMessages.push({ role: inputRole, content: _content }); updatedMessages.push({ role: inputRole, content: _content });
_setContent(''); _setContent('');
setMessages(updatedMessages);
resetTextAreaHeight(); resetTextAreaHeight();
} else { } else {
updatedMessages[messageIndex].content = _content; updatedMessages[messageIndex].content = _content;
const _updatedMessages = updatedMessages.slice(0, messageIndex + 1); updatedChats[currentChatIndex].messages = updatedMessages.slice(
setMessages(_updatedMessages); 0,
messageIndex + 1
);
setIsEdit(false); setIsEdit(false);
} }
setChats(updatedChats);
handleSubmit(); handleSubmit();
}; };

View file

@ -3,27 +3,17 @@ import useStore from '@store/store';
import PlusIcon from '@icon/PlusIcon'; import PlusIcon from '@icon/PlusIcon';
import { ChatInterface, MessageInterface } from '@type/chat'; import { ChatInterface } from '@type/chat';
import { defaultSystemMessage } from '@constants/chat'; import { defaultSystemMessage } from '@constants/chat';
const NewMessageButton = ({ messageIndex }: { messageIndex: number }) => { const NewMessageButton = React.memo(
const [ ({ messageIndex }: { messageIndex: number }) => {
messages, const setChats = useStore((state) => state.setChats);
chats, const currentChatIndex = useStore((state) => state.currentChatIndex);
setMessages, const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
currentChatIndex,
setChats,
setCurrentChatIndex,
] = useStore((state) => [
state.messages,
state.chats,
state.setMessages,
state.currentChatIndex,
state.setChats,
state.setCurrentChatIndex,
]);
const addChat = () => { const addChat = () => {
const chats = useStore.getState().chats;
if (chats) { if (chats) {
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats)); const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
let titleIndex = 1; let titleIndex = 1;
@ -39,7 +29,6 @@ const NewMessageButton = ({ messageIndex }: { messageIndex: number }) => {
messages: [{ role: 'system', content: defaultSystemMessage }], messages: [{ role: 'system', content: defaultSystemMessage }],
}); });
setChats(updatedChats); setChats(updatedChats);
setMessages(updatedChats[0].messages);
setCurrentChatIndex(0); setCurrentChatIndex(0);
} }
}; };
@ -48,14 +37,14 @@ const NewMessageButton = ({ messageIndex }: { messageIndex: number }) => {
if (currentChatIndex === -1) { if (currentChatIndex === -1) {
addChat(); addChat();
} else { } else {
const updatedMessages: MessageInterface[] = JSON.parse( const updatedChats: ChatInterface[] = JSON.parse(
JSON.stringify(messages) JSON.stringify(useStore.getState().chats)
); );
updatedMessages.splice(messageIndex + 1, 0, { updatedChats[currentChatIndex].messages.splice(messageIndex + 1, 0, {
content: '', content: '',
role: 'user', role: 'user',
}); });
setMessages(updatedMessages); setChats(updatedChats);
} }
}; };
@ -69,6 +58,7 @@ const NewMessageButton = ({ messageIndex }: { messageIndex: number }) => {
</div> </div>
</div> </div>
); );
}; }
);
export default NewMessageButton; export default NewMessageButton;

View file

@ -2,7 +2,7 @@ import React, { useState } from 'react';
import useStore from '@store/store'; import useStore from '@store/store';
import DownChevronArrow from '@icon/DownChevronArrow'; import DownChevronArrow from '@icon/DownChevronArrow';
import { MessageInterface, Role, roles } from '@type/chat'; import { ChatInterface, Role, roles } from '@type/chat';
const RoleSelector = ({ const RoleSelector = ({
role, role,
@ -13,11 +13,9 @@ const RoleSelector = ({
messageIndex: number; messageIndex: number;
sticky?: boolean; sticky?: boolean;
}) => { }) => {
const [messages, setMessages, setInputRole] = useStore((state) => [ const setInputRole = useStore((state) => state.setInputRole);
state.messages, const setChats = useStore((state) => state.setChats);
state.setMessages, const currentChatIndex = useStore((state) => state.currentChatIndex);
state.setInputRole,
]);
const [dropDown, setDropDown] = useState<boolean>(false); const [dropDown, setDropDown] = useState<boolean>(false);
@ -46,11 +44,12 @@ const RoleSelector = ({
className='px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white cursor-pointer' className='px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white cursor-pointer'
onClick={() => { onClick={() => {
if (!sticky) { if (!sticky) {
const updatedMessages: MessageInterface[] = JSON.parse( const updatedChats: ChatInterface[] = JSON.parse(
JSON.stringify(messages) JSON.stringify(useStore.getState().chats)
); );
updatedMessages[messageIndex].role = r; updatedChats[currentChatIndex].messages[messageIndex].role =
setMessages(updatedMessages); r;
setChats(updatedChats);
} else { } else {
setInputRole(r); setInputRole(r);
} }
@ -66,5 +65,4 @@ const RoleSelector = ({
</div> </div>
); );
}; };
export default RoleSelector; export default RoleSelector;

View file

@ -5,15 +5,12 @@ import CrossIcon2 from '@icon/CrossIcon2';
import { validateApiKey } from '@api/customApi'; import { validateApiKey } from '@api/customApi';
const ConfigMenu = () => { const ConfigMenu = () => {
const [apiKey, setApiKey, apiFree, setApiFree, openConfig, setOpenConfig] = const apiKey = useStore((state) => state.apiKey);
useStore((state) => [ const setApiKey = useStore((state) => state.setApiKey);
state.apiKey, const apiFree = useStore((state) => state.apiFree);
state.setApiKey, const setApiFree = useStore((state) => state.setApiFree);
state.apiFree, const openConfig = useStore((state) => state.openConfig);
state.setApiFree, const setOpenConfig = useStore((state) => state.setOpenConfig);
state.openConfig,
state.setOpenConfig,
]);
const [_apiKey, _setApiKey] = useState<string>(apiKey || ''); const [_apiKey, _setApiKey] = useState<string>(apiKey || '');
const [error, setError] = useState<boolean>(false); const [error, setError] = useState<boolean>(false);

View file

@ -10,11 +10,8 @@ import CrossIcon from '@icon/CrossIcon';
import useInitialiseNewChat from '@hooks/useInitialiseNewChat'; import useInitialiseNewChat from '@hooks/useInitialiseNewChat';
const ChatHistoryList = () => { const ChatHistoryList = () => {
const [chats, setCurrentChatIndex, setMessages] = useStore((state) => [ const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
state.chats, const chats = useStore((state) => state.chats);
state.setCurrentChatIndex,
state.setMessages,
]);
return ( return (
<div className='flex-col flex-1 overflow-y-auto border-b border-white/20'> <div className='flex-col flex-1 overflow-y-auto border-b border-white/20'>
@ -27,7 +24,6 @@ const ChatHistoryList = () => {
chatIndex={index} chatIndex={index}
onClick={() => { onClick={() => {
setCurrentChatIndex(index); setCurrentChatIndex(index);
setMessages(chats[index].messages);
}} }}
/> />
))} ))}
@ -66,14 +62,9 @@ const ChatHistory = ({
onClick?: React.MouseEventHandler<HTMLElement>; onClick?: React.MouseEventHandler<HTMLElement>;
}) => { }) => {
const initialiseNewChat = useInitialiseNewChat(); const initialiseNewChat = useInitialiseNewChat();
const [chats, setChats, currentChatIndex, setMessages, setCurrentChatIndex] = const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
useStore((state) => [ const setChats = useStore((state) => state.setChats);
state.chats, const currentChatIndex = useStore((state) => state.currentChatIndex);
state.setChats,
state.currentChatIndex,
state.setMessages,
state.setCurrentChatIndex,
]);
const [isDelete, setIsDelete] = useState<boolean>(false); const [isDelete, setIsDelete] = useState<boolean>(false);
const [isEdit, setIsEdit] = useState<boolean>(false); const [isEdit, setIsEdit] = useState<boolean>(false);
@ -83,7 +74,7 @@ const ChatHistory = ({
const handleTick = (e: React.MouseEvent<HTMLButtonElement>) => { const handleTick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation(); e.stopPropagation();
const updatedChats = JSON.parse(JSON.stringify(chats)); const updatedChats = JSON.parse(JSON.stringify(useStore.getState().chats));
if (isEdit) { if (isEdit) {
updatedChats[chatIndex].title = _title; updatedChats[chatIndex].title = _title;
setChats(updatedChats); setChats(updatedChats);
@ -92,7 +83,6 @@ const ChatHistory = ({
updatedChats.splice(chatIndex, 1); updatedChats.splice(chatIndex, 1);
if (updatedChats.length > 0) { if (updatedChats.length > 0) {
setCurrentChatIndex(0); setCurrentChatIndex(0);
setMessages(updatedChats[0].messages);
setChats(updatedChats); setChats(updatedChats);
} else { } else {
initialiseNewChat(); initialiseNewChat();

View file

@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import useStore from '@store/store';
import NewChat from './NewChat'; import NewChat from './NewChat';
import ChatHistoryList from './ChatHistoryList'; import ChatHistoryList from './ChatHistoryList';

View file

@ -4,10 +4,8 @@ import useStore from '@store/store';
import PersonIcon from '@icon/PersonIcon'; import PersonIcon from '@icon/PersonIcon';
const Config = () => { const Config = () => {
const [apiFree, setOpenConfig] = useStore((state) => [ const apiFree = useStore((state) => state.apiFree);
state.apiFree, const setOpenConfig = useStore((state) => state.setOpenConfig);
state.setOpenConfig,
]);
return ( return (
<a <a

View file

@ -1,20 +1,10 @@
import React from 'react'; import React from 'react';
import useStore from '@store/store';
import PlusIcon from '@icon/PlusIcon'; import PlusIcon from '@icon/PlusIcon';
import useAddChat from '@hooks/useAddChat'; import useAddChat from '@hooks/useAddChat';
const NewChat = () => { const NewChat = () => {
const [chats, setChats, setCurrentChatIndex, setMessages] = useStore(
(state) => [
state.chats,
state.setChats,
state.setCurrentChatIndex,
state.setMessages,
]
);
const addChat = useAddChat(); const addChat = useAddChat();
return ( return (

View file

@ -5,10 +5,12 @@ import PlusIcon from '@icon/PlusIcon';
import useAddChat from '@hooks/useAddChat'; import useAddChat from '@hooks/useAddChat';
const MobileBar = () => { const MobileBar = () => {
const [chats, currentChatIndex] = useStore((state) => [ const chatTitle = useStore((state) =>
state.chats, state.chats && state.chats.length > 0
state.currentChatIndex, ? state.chats[state.currentChatIndex].title
]); : 'New Chat'
);
const addChat = useAddChat(); const addChat = useAddChat();
return ( return (
@ -42,11 +44,7 @@ const MobileBar = () => {
<line x1='3' y1='18' x2='21' y2='18'></line> <line x1='3' y1='18' x2='21' y2='18'></line>
</svg> </svg>
</button> </button>
<h1 className='flex-1 text-center text-base font-normal'> <h1 className='flex-1 text-center text-base font-normal'>{chatTitle}</h1>
{chats && chats.length > 0
? chats[currentChatIndex]?.title
: 'New Chat'}
</h1>
<button type='button' className='px-3 text-gray-400' onClick={addChat}> <button type='button' className='px-3 text-gray-400' onClick={addChat}>
<PlusIcon className='h-6 w-6' /> <PlusIcon className='h-6 w-6' />
</button> </button>

View file

@ -4,16 +4,11 @@ import { defaultSystemMessage } from '@constants/chat';
import { ChatInterface } from '@type/chat'; import { ChatInterface } from '@type/chat';
const useAddChat = () => { const useAddChat = () => {
const [chats, setChats, setCurrentChatIndex, setMessages] = useStore( const setChats = useStore((state) => state.setChats);
(state) => [ const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
state.chats,
state.setChats,
state.setCurrentChatIndex,
state.setMessages,
]
);
const addChat = () => { const addChat = () => {
const chats = useStore.getState().chats;
if (chats) { if (chats) {
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats)); const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
let titleIndex = 1; let titleIndex = 1;
@ -29,7 +24,6 @@ const useAddChat = () => {
messages: [{ role: 'system', content: defaultSystemMessage }], messages: [{ role: 'system', content: defaultSystemMessage }],
}); });
setChats(updatedChats); setChats(updatedChats);
setMessages(updatedChats[0].messages);
setCurrentChatIndex(0); setCurrentChatIndex(0);
} }
}; };

View file

@ -4,11 +4,8 @@ import { MessageInterface } from '@type/chat';
import { defaultSystemMessage } from '@constants/chat'; import { defaultSystemMessage } from '@constants/chat';
const useInitialiseNewChat = () => { const useInitialiseNewChat = () => {
const [setChats, setMessages, setCurrentChatIndex] = useStore((state) => [ const setChats = useStore((state) => state.setChats);
state.setChats, const setCurrentChatIndex = useStore((state) => state.setCurrentChatIndex);
state.setMessages,
state.setCurrentChatIndex,
]);
const initialiseNewChat = () => { const initialiseNewChat = () => {
const message: MessageInterface = { const message: MessageInterface = {
@ -21,7 +18,6 @@ const useInitialiseNewChat = () => {
messages: [message], messages: [message],
}, },
]); ]);
setMessages([message]);
setCurrentChatIndex(0); setCurrentChatIndex(0);
}; };

View file

@ -1,12 +1,19 @@
import React, { useEffect } from 'react'; import React, { useEffect, useRef } from 'react';
import useStore from '@store/store'; import useStore from '@store/store';
const useSaveToLocalStorage = () => { const useSaveToLocalStorage = () => {
const chats = useStore((state) => state.chats); const chatsRef = useRef(useStore.getState().chats);
useEffect(() => { useEffect(() => {
if (chats) localStorage.setItem('chats', JSON.stringify(chats)); const unsubscribe = useStore.subscribe((state) => {
}, [chats]); if (chatsRef && chatsRef.current !== state.chats) {
chatsRef.current = state.chats;
localStorage.setItem('chats', JSON.stringify(state.chats));
}
});
return unsubscribe;
}, []);
}; };
export default useSaveToLocalStorage; export default useSaveToLocalStorage;

View file

@ -1,48 +1,45 @@
import React from 'react'; import React from 'react';
import useStore from '@store/store'; import useStore from '@store/store';
import { MessageInterface } from '@type/chat'; import { ChatInterface } from '@type/chat';
import { getChatCompletionStream as getChatCompletionStreamFree } from '@api/freeApi'; import { getChatCompletionStream as getChatCompletionStreamFree } from '@api/freeApi';
import { getChatCompletionStream as getChatCompletionStreamCustom } from '@api/customApi'; import { getChatCompletionStream as getChatCompletionStreamCustom } from '@api/customApi';
import { parseEventSource } from '@api/helper'; import { parseEventSource } from '@api/helper';
const useSubmit = () => { const useSubmit = () => {
const [ const error = useStore((state) => state.error);
error, const setError = useStore((state) => state.setError);
setError, const apiFree = useStore((state) => state.apiFree);
apiFree, const apiKey = useStore((state) => state.apiKey);
apiKey, const setGenerating = useStore((state) => state.setGenerating);
setMessages, const generating = useStore((state) => state.generating);
setGenerating, const currentChatIndex = useStore((state) => state.currentChatIndex);
generating, const setChats = useStore((state) => state.setChats);
] = useStore((state) => [
state.error,
state.setError,
state.apiFree,
state.apiKey,
state.setMessages,
state.setGenerating,
state.generating,
]);
const handleSubmit = async () => { const handleSubmit = async () => {
if (generating) return; const chats = useStore.getState().chats;
const messages = useStore.getState().messages; if (generating || !chats) return;
const updatedMessages: MessageInterface[] = JSON.parse( const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
JSON.stringify(messages)
);
updatedMessages.push({ role: 'assistant', content: '' }); updatedChats[currentChatIndex].messages.push({
role: 'assistant',
content: '',
});
setMessages(updatedMessages); setChats(updatedChats);
setGenerating(true); setGenerating(true);
let stream; let stream;
try { try {
if (apiFree) { if (apiFree) {
stream = await getChatCompletionStreamFree(messages); stream = await getChatCompletionStreamFree(
chats[currentChatIndex].messages
);
} else if (apiKey) { } else if (apiKey) {
stream = await getChatCompletionStreamCustom(apiKey, messages); stream = await getChatCompletionStreamCustom(
apiKey,
chats[currentChatIndex].messages
);
} }
if (stream) { if (stream) {
@ -65,11 +62,12 @@ const useSubmit = () => {
} }
}, ''); }, '');
const updatedMessages: MessageInterface[] = JSON.parse( const updatedChats: ChatInterface[] = JSON.parse(
JSON.stringify(useStore.getState().messages) JSON.stringify(useStore.getState().chats)
); );
const updatedMessages = updatedChats[currentChatIndex].messages;
updatedMessages[updatedMessages.length - 1].content += resultString; updatedMessages[updatedMessages.length - 1].content += resultString;
setMessages(updatedMessages); setChats(updatedChats);
} }
} }
} }

View file

@ -1,25 +0,0 @@
import React, { useEffect } from 'react';
import useStore from '@store/store';
import { ChatInterface, MessageInterface } from '@type/chat';
const useUpdateCharts = () => {
const [chats, messages, setChats, currentChatIndex] = useStore((state) => [
state.chats,
state.messages,
state.setChats,
state.currentChatIndex,
]);
useEffect(() => {
if (currentChatIndex !== -1 && chats && chats.length > 0) {
const updatedChats: ChatInterface[] = JSON.parse(JSON.stringify(chats));
const updatedMessages: MessageInterface[] = JSON.parse(
JSON.stringify(messages)
);
updatedChats[currentChatIndex].messages = updatedMessages;
setChats(updatedChats);
}
}, [messages]);
};
export default useUpdateCharts;