From 5512a8206c823de83625ff00b0315ea95f91ece0 Mon Sep 17 00:00:00 2001 From: Arvin Xu Date: Sun, 17 Nov 2024 00:53:47 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20perf:=20refactor=20to=20im?= =?UTF-8?q?prove=20chat=20performance=20(#4708)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ⚡️ perf: refactor to improve chat performance * ⚡️ perf: refactor to improve chat performance --- .../ChatInput/Desktop/TextArea.test.tsx | 26 +-- .../features/ChatInput/Desktop/TextArea.tsx | 29 +++ .../features/ChatInput/Desktop/index.tsx | 46 +++++ .../features/ChatInput/index.tsx | 3 +- .../features/ChatList/Content.tsx | 24 +-- src/const/message.ts | 4 + .../ChatInput/Desktop/Footer/ShortcutHint.tsx | 61 ++++++ .../ChatInput/Desktop/Footer/index.tsx | 57 ++---- .../{TextArea.tsx => InputArea/index.tsx} | 31 ++- src/features/ChatInput/Desktop/index.tsx | 55 +++--- src/features/Conversation/Messages/index.ts | 22 +-- .../components/ChatItem/index.tsx | 127 +++++++----- .../components/VirtualizedList/index.tsx | 182 +++++++++--------- src/features/Conversation/index.ts | 2 + src/types/topic/index.ts | 1 + src/types/{ => topic}/topic.ts | 0 16 files changed, 400 insertions(+), 270 deletions(-) rename src/{ => app/(main)/chat/(workspace)/@conversation}/features/ChatInput/Desktop/TextArea.test.tsx (94%) create mode 100644 src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx create mode 100644 src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx create mode 100644 src/features/ChatInput/Desktop/Footer/ShortcutHint.tsx rename src/features/ChatInput/Desktop/{TextArea.tsx => InputArea/index.tsx} (78%) create mode 100644 src/types/topic/index.ts rename src/types/{ => topic}/topic.ts (100%) diff --git a/src/features/ChatInput/Desktop/TextArea.test.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx similarity index 94% rename from src/features/ChatInput/Desktop/TextArea.test.tsx rename to src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx index 524ecbe18167..9e5d5a686484 100644 --- a/src/features/ChatInput/Desktop/TextArea.test.tsx +++ b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx @@ -7,10 +7,10 @@ import { useUserStore } from '@/store/user'; import InputArea from './TextArea'; -let setExpandMock: (expand: boolean) => void; +let onSendMock: () => void; beforeEach(() => { - setExpandMock = vi.fn(); + onSendMock = vi.fn(); }); describe('', () => { @@ -29,13 +29,13 @@ describe('', () => { }); it('renders with correct placeholder text', () => { - render(); + render(); const textArea = screen.getByPlaceholderText('sendPlaceholder'); expect(textArea).toBeInTheDocument(); }); it('has the correct initial value', () => { - render(); + render(); const textArea = screen.getByRole('textbox'); expect(textArea).toHaveValue(''); }); @@ -82,7 +82,7 @@ describe('', () => { useChatStore.setState({ updateInputMessage: updateInputMessageMock }); }); - render(); + render(); const textArea = screen.getByRole('textbox'); // Start composition (IME input starts) @@ -92,7 +92,7 @@ describe('', () => { fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter' }); // Since we are in the middle of IME composition, the message should not be sent - expect(setExpandMock).not.toHaveBeenCalled(); + expect(onSendMock).not.toHaveBeenCalled(); expect(updateInputMessageMock).not.toHaveBeenCalled(); // End composition (IME input ends) @@ -102,7 +102,7 @@ describe('', () => { fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter' }); // Since IME composition has ended, now the message should be sent - expect(setExpandMock).toHaveBeenCalled(); + expect(onSendMock).toHaveBeenCalled(); expect(updateInputMessageMock).toHaveBeenCalled(); }); @@ -112,7 +112,7 @@ describe('', () => { useChatStore.setState({ updateInputMessage: updateInputMessageMock }); }); - render(); + render(); const textArea = screen.getByRole('textbox'); const newText = 'New input text'; @@ -199,7 +199,7 @@ describe('', () => { useChatStore.setState({ chatLoadingIds: ['123'], sendMessage: sendMessageMock }); }); - render(); + render(); const textArea = screen.getByRole('textbox'); fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter', shiftKey: true }); @@ -236,7 +236,7 @@ describe('', () => { useUserStore.getState().updatePreference({ useCmdEnterToSend: true }); }); - render(); + render(); const textArea = screen.getByRole('textbox'); fireEvent.keyDown(textArea, { code: 'Enter', ctrlKey: true, key: 'Enter' }); @@ -256,7 +256,7 @@ describe('', () => { useUserStore.getState().updatePreference({ useCmdEnterToSend: false }); }); - render(); + render(); const textArea = screen.getByRole('textbox'); fireEvent.keyDown(textArea, { code: 'Enter', ctrlKey: true, key: 'Enter' }); @@ -279,7 +279,7 @@ describe('', () => { useUserStore.getState().updatePreference({ useCmdEnterToSend: true }); }); - render(); + render(); const textArea = screen.getByRole('textbox'); fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter', metaKey: true }); @@ -304,7 +304,7 @@ describe('', () => { useUserStore.getState().updatePreference({ useCmdEnterToSend: false }); }); - render(); + render(); const textArea = screen.getByRole('textbox'); fireEvent.keyDown(textArea, { code: 'Enter', key: 'Enter', metaKey: true }); diff --git a/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx new file mode 100644 index 000000000000..d87624c9e567 --- /dev/null +++ b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.tsx @@ -0,0 +1,29 @@ +import { memo } from 'react'; + +import InputArea from '@/features/ChatInput/Desktop/InputArea'; +import { useSendMessage } from '@/features/ChatInput/useSend'; +import { useChatStore } from '@/store/chat'; +import { chatSelectors } from '@/store/chat/slices/message/selectors'; + +const TextArea = memo<{ onSend?: () => void }>(({ onSend }) => { + const [loading, value, updateInputMessage] = useChatStore((s) => [ + chatSelectors.isAIGenerating(s), + s.inputMessage, + s.updateInputMessage, + ]); + const { send: sendMessage } = useSendMessage(); + + return ( + { + sendMessage(); + onSend?.(); + }} + value={value} + /> + ); +}); + +export default TextArea; diff --git a/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx new file mode 100644 index 000000000000..2e1ee205d9c5 --- /dev/null +++ b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/index.tsx @@ -0,0 +1,46 @@ +'use client'; + +import { memo } from 'react'; + +import { ActionKeys } from '@/features/ChatInput/ActionBar/config'; +import DesktopChatInput from '@/features/ChatInput/Desktop'; +import { useGlobalStore } from '@/store/global'; +import { systemStatusSelectors } from '@/store/global/selectors'; + +import TextArea from './TextArea'; + +const leftActions = [ + 'model', + 'fileUpload', + 'knowledgeBase', + 'temperature', + 'history', + 'stt', + 'tools', + 'token', +] as ActionKeys[]; + +const rightActions = ['clear'] as ActionKeys[]; + +const renderTextArea = (onSend: () => void) =>