历史消息懒加载
性能优化
This commit is contained in:
parent
e35d928bcd
commit
14c9cb6001
@ -38,6 +38,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-toolkit/preload": "^3.0.0",
|
"@electron-toolkit/preload": "^3.0.0",
|
||||||
"@electron-toolkit/utils": "^3.0.0",
|
"@electron-toolkit/utils": "^3.0.0",
|
||||||
|
"@types/react-infinite-scroll-component": "^5.0.0",
|
||||||
"adm-zip": "^0.5.16",
|
"adm-zip": "^0.5.16",
|
||||||
"docx": "^9.0.2",
|
"docx": "^9.0.2",
|
||||||
"electron-log": "^5.1.5",
|
"electron-log": "^5.1.5",
|
||||||
@ -48,6 +49,7 @@
|
|||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
"officeparser": "^4.1.1",
|
"officeparser": "^4.1.1",
|
||||||
|
"react-infinite-scroll-component": "^6.1.0",
|
||||||
"webdav": "4.11.4"
|
"webdav": "4.11.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -17,8 +17,10 @@ import { estimateHistoryTokens } from '@renderer/services/TokenService'
|
|||||||
import { Assistant, Message, Model, Topic } from '@renderer/types'
|
import { Assistant, Message, Model, Topic } from '@renderer/types'
|
||||||
import { captureScrollableDiv, runAsyncFunction, uuid } from '@renderer/utils'
|
import { captureScrollableDiv, runAsyncFunction, uuid } from '@renderer/utils'
|
||||||
import { t } from 'i18next'
|
import { t } from 'i18next'
|
||||||
import { flatten, last, reverse, take } from 'lodash'
|
import { flatten, last, take } from 'lodash'
|
||||||
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
|
import InfiniteScroll from 'react-infinite-scroll-component'
|
||||||
|
import BeatLoader from 'react-spinners/BeatLoader'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import Suggestions from '../components/Suggestions'
|
import Suggestions from '../components/Suggestions'
|
||||||
@ -31,13 +33,53 @@ interface Props {
|
|||||||
setActiveTopic: (topic: Topic) => void
|
setActiveTopic: (topic: Topic) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface LoaderProps {
|
||||||
|
$loading: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const LoaderContainer = styled.div<LoaderProps>`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px;
|
||||||
|
width: 100%;
|
||||||
|
background: var(--color-background);
|
||||||
|
opacity: ${(props) => (props.$loading ? 1 : 0)};
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
pointer-events: none;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ScrollContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
`
|
||||||
|
|
||||||
|
interface ContainerProps {
|
||||||
|
right?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const Container = styled(Scrollbar)<ContainerProps>`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
padding: 10px 0;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
background-color: var(--color-background);
|
||||||
|
`
|
||||||
|
|
||||||
const Messages: FC<Props> = ({ assistant, topic, setActiveTopic }) => {
|
const Messages: FC<Props> = ({ assistant, topic, setActiveTopic }) => {
|
||||||
const [messages, setMessages] = useState<Message[]>([])
|
const [messages, setMessages] = useState<Message[]>([])
|
||||||
|
const [displayMessages, setDisplayMessages] = useState<Message[]>([])
|
||||||
|
const [hasMore, setHasMore] = useState(true)
|
||||||
|
const [isLoadingMore, setIsLoadingMore] = useState(false)
|
||||||
|
|
||||||
const containerRef = useRef<HTMLDivElement>(null)
|
const containerRef = useRef<HTMLDivElement>(null)
|
||||||
|
const messagesRef = useRef(messages)
|
||||||
const { updateTopic, addTopic } = useAssistant(assistant.id)
|
const { updateTopic, addTopic } = useAssistant(assistant.id)
|
||||||
const { showTopics, topicPosition, showAssistants, enableTopicNaming } = useSettings()
|
const { showTopics, topicPosition, showAssistants, enableTopicNaming } = useSettings()
|
||||||
|
|
||||||
const messagesRef = useRef(messages)
|
const INITIAL_MESSAGES_COUNT = 30
|
||||||
|
const LOAD_MORE_COUNT = 20
|
||||||
|
|
||||||
messagesRef.current = messages
|
messagesRef.current = messages
|
||||||
|
|
||||||
const maxWidth = useMemo(() => {
|
const maxWidth = useMemo(() => {
|
||||||
@ -158,7 +200,7 @@ const Messages: FC<Props> = ({ assistant, topic, setActiveTopic }) => {
|
|||||||
setActiveTopic(newTopic)
|
setActiveTopic(newTopic)
|
||||||
autoRenameTopic()
|
autoRenameTopic()
|
||||||
|
|
||||||
// 由于复制了消息,消息中附带的文件的总数变了,需要更新
|
// 由于复制了消<EFBFBD><EFBFBD><EFBFBD>,消息中附带的文件的总数变了,需要更新
|
||||||
const filesArr = branchMessages.map((m) => m.files)
|
const filesArr = branchMessages.map((m) => m.files)
|
||||||
const files = flatten(filesArr).filter(Boolean)
|
const files = flatten(filesArr).filter(Boolean)
|
||||||
files.map(async (f) => {
|
files.map(async (f) => {
|
||||||
@ -197,7 +239,31 @@ const Messages: FC<Props> = ({ assistant, topic, setActiveTopic }) => {
|
|||||||
})
|
})
|
||||||
}, [assistant, messages])
|
}, [assistant, messages])
|
||||||
|
|
||||||
const memoizedMessages = useMemo(() => reverse([...messages]), [messages])
|
// 初始化显示最新的消息
|
||||||
|
useEffect(() => {
|
||||||
|
if (messages.length > 0) {
|
||||||
|
const reversedMessages = [...messages].reverse()
|
||||||
|
setDisplayMessages(reversedMessages.slice(0, INITIAL_MESSAGES_COUNT))
|
||||||
|
setHasMore(messages.length > INITIAL_MESSAGES_COUNT)
|
||||||
|
}
|
||||||
|
}, [messages])
|
||||||
|
|
||||||
|
// 加载更多历史消息
|
||||||
|
const loadMoreMessages = useCallback(() => {
|
||||||
|
if (!hasMore || isLoadingMore) return
|
||||||
|
|
||||||
|
setIsLoadingMore(true)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const currentLength = displayMessages.length
|
||||||
|
const reversedMessages = [...messages].reverse()
|
||||||
|
const moreMessages = reversedMessages.slice(currentLength, currentLength + LOAD_MORE_COUNT)
|
||||||
|
|
||||||
|
setDisplayMessages((prev) => [...prev, ...moreMessages])
|
||||||
|
setHasMore(currentLength + LOAD_MORE_COUNT < messages.length)
|
||||||
|
setIsLoadingMore(false)
|
||||||
|
}, 300)
|
||||||
|
}, [displayMessages, hasMore, isLoadingMore, messages])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
@ -207,30 +273,34 @@ const Messages: FC<Props> = ({ assistant, topic, setActiveTopic }) => {
|
|||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
right={topicPosition === 'left'}>
|
right={topicPosition === 'left'}>
|
||||||
<Suggestions assistant={assistant} messages={messages} />
|
<Suggestions assistant={assistant} messages={messages} />
|
||||||
{memoizedMessages.map((message, index) => (
|
<InfiniteScroll
|
||||||
<MessageItem
|
dataLength={displayMessages.length}
|
||||||
key={message.id}
|
next={loadMoreMessages}
|
||||||
message={message}
|
hasMore={hasMore}
|
||||||
topic={topic}
|
loader={null}
|
||||||
index={index}
|
inverse={true}
|
||||||
hidePresetMessages={assistant.settings?.hideMessages}
|
scrollableTarget="messages">
|
||||||
onSetMessages={setMessages}
|
<ScrollContainer>
|
||||||
onDeleteMessage={onDeleteMessage}
|
<LoaderContainer $loading={isLoadingMore}>
|
||||||
onGetMessages={onGetMessages}
|
<BeatLoader size={8} color="var(--color-text-2)" />
|
||||||
/>
|
</LoaderContainer>
|
||||||
))}
|
{displayMessages.map((message, index) => (
|
||||||
|
<MessageItem
|
||||||
|
key={message.id}
|
||||||
|
message={message}
|
||||||
|
topic={topic}
|
||||||
|
index={index}
|
||||||
|
hidePresetMessages={assistant.settings?.hideMessages}
|
||||||
|
onSetMessages={setMessages}
|
||||||
|
onDeleteMessage={onDeleteMessage}
|
||||||
|
onGetMessages={onGetMessages}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ScrollContainer>
|
||||||
|
</InfiniteScroll>
|
||||||
<Prompt assistant={assistant} key={assistant.prompt} />
|
<Prompt assistant={assistant} key={assistant.prompt} />
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Container = styled(Scrollbar)`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column-reverse;
|
|
||||||
padding: 10px 0;
|
|
||||||
padding-bottom: 20px;
|
|
||||||
overflow-x: hidden;
|
|
||||||
background-color: var(--color-background);
|
|
||||||
`
|
|
||||||
|
|
||||||
export default Messages
|
export default Messages
|
||||||
|
|||||||
29
yarn.lock
29
yarn.lock
@ -2093,6 +2093,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@types/react-infinite-scroll-component@npm:^5.0.0":
|
||||||
|
version: 5.0.0
|
||||||
|
resolution: "@types/react-infinite-scroll-component@npm:5.0.0"
|
||||||
|
dependencies:
|
||||||
|
react-infinite-scroll-component: "npm:*"
|
||||||
|
checksum: 10c0/257e7b2fc6ebf200ada409a5c21415c1a56157807636405f3329521cf36344a81afa87f992497747534453e48145b0d4f32ccee80814548b466a0d83dfa23eec
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@types/react@npm:*, @types/react@npm:^18.2.48":
|
"@types/react@npm:*, @types/react@npm:^18.2.48":
|
||||||
version: 18.3.5
|
version: 18.3.5
|
||||||
resolution: "@types/react@npm:18.3.5"
|
resolution: "@types/react@npm:18.3.5"
|
||||||
@ -2343,6 +2352,7 @@ __metadata:
|
|||||||
"@types/node": "npm:^18.19.9"
|
"@types/node": "npm:^18.19.9"
|
||||||
"@types/react": "npm:^18.2.48"
|
"@types/react": "npm:^18.2.48"
|
||||||
"@types/react-dom": "npm:^18.2.18"
|
"@types/react-dom": "npm:^18.2.18"
|
||||||
|
"@types/react-infinite-scroll-component": "npm:^5.0.0"
|
||||||
"@types/tinycolor2": "npm:^1"
|
"@types/tinycolor2": "npm:^1"
|
||||||
"@vitejs/plugin-react": "npm:^4.2.1"
|
"@vitejs/plugin-react": "npm:^4.2.1"
|
||||||
adm-zip: "npm:^0.5.16"
|
adm-zip: "npm:^0.5.16"
|
||||||
@ -2384,6 +2394,7 @@ __metadata:
|
|||||||
react-dom: "npm:^18.2.0"
|
react-dom: "npm:^18.2.0"
|
||||||
react-hotkeys-hook: "npm:^4.6.1"
|
react-hotkeys-hook: "npm:^4.6.1"
|
||||||
react-i18next: "npm:^14.1.2"
|
react-i18next: "npm:^14.1.2"
|
||||||
|
react-infinite-scroll-component: "npm:^6.1.0"
|
||||||
react-markdown: "npm:^9.0.1"
|
react-markdown: "npm:^9.0.1"
|
||||||
react-redux: "npm:^9.1.2"
|
react-redux: "npm:^9.1.2"
|
||||||
react-router: "npm:6"
|
react-router: "npm:6"
|
||||||
@ -9965,6 +9976,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"react-infinite-scroll-component@npm:*, react-infinite-scroll-component@npm:^6.1.0":
|
||||||
|
version: 6.1.0
|
||||||
|
resolution: "react-infinite-scroll-component@npm:6.1.0"
|
||||||
|
dependencies:
|
||||||
|
throttle-debounce: "npm:^2.1.0"
|
||||||
|
peerDependencies:
|
||||||
|
react: ">=16.0.0"
|
||||||
|
checksum: 10c0/8de02f178ae861880dddbd0c882dc70b55e21737b87fe428140d81a2c5d13c5eeba8a4fc260b1e86e4c556fdea333d6babaed55a9e5987a42b181b2d92f69cd8
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"react-is@npm:^16.13.1, react-is@npm:^16.7.0":
|
"react-is@npm:^16.13.1, react-is@npm:^16.7.0":
|
||||||
version: 16.13.1
|
version: 16.13.1
|
||||||
resolution: "react-is@npm:16.13.1"
|
resolution: "react-is@npm:16.13.1"
|
||||||
@ -11546,6 +11568,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"throttle-debounce@npm:^2.1.0":
|
||||||
|
version: 2.3.0
|
||||||
|
resolution: "throttle-debounce@npm:2.3.0"
|
||||||
|
checksum: 10c0/41648e4cf46f935818af32ecac34f9876c618f24e300551cbe3a0ca2c5828cb8d2f9b73e6e1e2f8c64237f70fbc8c541f9b5c9114da70b33b1ed10ba4cc6b15f
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"throttle-debounce@npm:^5.0.0, throttle-debounce@npm:^5.0.2":
|
"throttle-debounce@npm:^5.0.0, throttle-debounce@npm:^5.0.2":
|
||||||
version: 5.0.2
|
version: 5.0.2
|
||||||
resolution: "throttle-debounce@npm:5.0.2"
|
resolution: "throttle-debounce@npm:5.0.2"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user