From 5f8f640cab0bbdd193e59824a13b8338e0571a3c Mon Sep 17 00:00:00 2001 From: ZhuangYumin Date: Mon, 27 Jan 2025 18:04:58 +0800 Subject: [PATCH] feat: optimize topics list performance with virtual list --- package.json | 2 + .../src/pages/home/Tabs/TopicsTab.tsx | 104 ++++++++++++++++-- yarn.lock | 40 +++++++ 3 files changed, 134 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index d22cccdfd..119fc684f 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "@llm-tools/embedjs-loader-xml": "^0.1.25", "@llm-tools/embedjs-openai": "^0.1.25", "@types/react-infinite-scroll-component": "^5.0.0", + "@types/react-window": "^1.8.8", "adm-zip": "^0.5.16", "apache-arrow": "^18.1.0", "docx": "^9.0.2", @@ -73,6 +74,7 @@ "html2canvas": "^1.4.1", "markdown-it": "^14.1.0", "officeparser": "^4.1.1", + "react-window": "^1.8.11", "tokenx": "^0.4.1", "webdav": "4.11.4" }, diff --git a/src/renderer/src/pages/home/Tabs/TopicsTab.tsx b/src/renderer/src/pages/home/Tabs/TopicsTab.tsx index f18802460..d7453f8f5 100644 --- a/src/renderer/src/pages/home/Tabs/TopicsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/TopicsTab.tsx @@ -22,9 +22,11 @@ import { exportTopicAsMarkdown, topicToMarkdown } from '@renderer/utils/export' import { Dropdown, MenuProps } from 'antd' import dayjs from 'dayjs' import { findIndex } from 'lodash' -import { FC, useCallback } from 'react' +import { FC, useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' +import { FixedSizeList as List } from 'react-window' +import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd' interface Props { assistant: Assistant @@ -37,6 +39,7 @@ const Topics: FC = ({ assistant: _assistant, activeTopic, setActiveTopic const { assistant, removeTopic, moveTopic, updateTopic, updateTopics } = useAssistant(_assistant.id) const { t } = useTranslation() const { showTopicTime, topicPosition } = useSettings() + const [isDragging, setIsDragging] = useState(false) const borderRadius = showTopicTime ? 12 : 'var(--list-item-border-radius)' @@ -176,13 +179,57 @@ const Topics: FC = ({ assistant: _assistant, activeTopic, setActiveTopic [assistant, assistants, onClearMessages, onDeleteTopic, onMoveTopic, t, updateTopic] ) - return ( - - - {(topic) => { - const isActive = topic.id === activeTopic?.id - return ( - + const onDragEnd = (result: any) => { + setIsDragging(false) + if (!result.destination) return + + const newTopics = [...assistant.topics] + const [removed] = newTopics.splice(result.source.index, 1) + newTopics.splice(result.destination.index, 0, removed) + updateTopics(newTopics) + } + + // 创建一个内部组件来处理虚拟列表的渲染 + const VirtualList = ({ children, ...props }: any) => { + const outerRef = useCallback((node: any) => { + if (node !== null) { + // 将 Droppable 的 ref 传递给外部容器 + props.provided.innerRef(node) + } + }, [props.provided]) + + return ( +
+
}> + {children} + + {props.provided.placeholder} +
+ ) + } + + const renderRow = ({ index, style }: { index: number; style: React.CSSProperties }) => { + const topic = assistant.topics[index] + const isActive = topic.id === activeTopic?.id + + return ( + + {(provided) => ( +
+ onSwitchTopic(topic)} @@ -206,10 +253,43 @@ const Topics: FC = ({ assistant: _assistant, activeTopic, setActiveTopic )} - ) - }} - -
+
+ )} +
+ ) + } + + return ( + + setIsDragging(true)} + onDragEnd={onDragEnd}> + { + const topic = assistant.topics[rubric.source.index] + return ( +
+ + {topic.name.replace('`', '')} + {showTopicTime && ( + {dayjs(topic.createdAt).format('MM/DD HH:mm')} + )} + +
+ ) + }}> + {(provided) => {renderRow}} +
+
) } diff --git a/yarn.lock b/yarn.lock index c2c888464..21d11944a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -309,6 +309,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.0.0": + version: 7.26.7 + resolution: "@babel/runtime@npm:7.26.7" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10c0/60199c049f90e5e41c687687430052a370aca60bac7859ff4ee761c5c1739b8ba1604d391d01588c22dc0e93828cbadb8ada742578ad1b1df240746bce98729a + languageName: node + linkType: hard + "@babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.4, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.6, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.24.8, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.9.2": version: 7.26.0 resolution: "@babel/runtime@npm:7.26.0" @@ -2705,6 +2714,15 @@ __metadata: languageName: node linkType: hard +"@types/react-window@npm:^1.8.8": + version: 1.8.8 + resolution: "@types/react-window@npm:1.8.8" + dependencies: + "@types/react": "npm:*" + checksum: 10c0/2170a3957752603e8b994840c5d31b72ddf94c427c0f42b0175b343cc54f50fe66161d8871e11786ec7a59906bd33861945579a3a8f745455a3744268ec1069f + languageName: node + linkType: hard + "@types/react@npm:*": version: 19.0.3 resolution: "@types/react@npm:19.0.3" @@ -3006,6 +3024,7 @@ __metadata: "@types/react": "npm:^18.2.48" "@types/react-dom": "npm:^18.2.18" "@types/react-infinite-scroll-component": "npm:^5.0.0" + "@types/react-window": "npm:^1.8.8" "@types/tinycolor2": "npm:^1" "@vitejs/plugin-react": "npm:^4.2.1" adm-zip: "npm:^0.5.16" @@ -3054,6 +3073,7 @@ __metadata: react-router: "npm:6" react-router-dom: "npm:6" react-spinners: "npm:^0.14.1" + react-window: "npm:^1.8.11" redux: "npm:^5.0.1" redux-persist: "npm:^6.0.0" rehype-katex: "npm:^7.0.1" @@ -8982,6 +9002,13 @@ __metadata: languageName: node linkType: hard +"memoize-one@npm:>=3.1.1 <6": + version: 5.2.1 + resolution: "memoize-one@npm:5.2.1" + checksum: 10c0/fd22dbe9a978a2b4f30d6a491fc02fb90792432ad0dab840dc96c1734d2bd7c9cdeb6a26130ec60507eb43230559523615873168bcbe8fafab221c30b11d54c1 + languageName: node + linkType: hard + "memoize-one@npm:^6.0.0": version: 6.0.0 resolution: "memoize-one@npm:6.0.0" @@ -11678,6 +11705,19 @@ __metadata: languageName: node linkType: hard +"react-window@npm:^1.8.11": + version: 1.8.11 + resolution: "react-window@npm:1.8.11" + dependencies: + "@babel/runtime": "npm:^7.0.0" + memoize-one: "npm:>=3.1.1 <6" + peerDependencies: + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10c0/5ae8da1bc5c47d8f0a428b28a600256e2db511975573e52cb65a9b27ed1a0e5b9f7b3bee5a54fb0da93956d782c24010be434be451072f46ba5a89159d2b3944 + languageName: node + linkType: hard + "react@npm:^18.2.0": version: 18.3.1 resolution: "react@npm:18.3.1"