Merge pull request #418 from 1355873789/develop

历史消息懒加载
This commit is contained in:
亢奋猫 2024-12-07 14:41:48 +08:00 committed by GitHub
commit f66adcd217
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 126 additions and 25 deletions

View File

@ -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": {

View File

@ -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,7 +273,18 @@ 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
dataLength={displayMessages.length}
next={loadMoreMessages}
hasMore={hasMore}
loader={null}
inverse={true}
scrollableTarget="messages">
<ScrollContainer>
<LoaderContainer $loading={isLoadingMore}>
<BeatLoader size={8} color="var(--color-text-2)" />
</LoaderContainer>
{displayMessages.map((message, index) => (
<MessageItem <MessageItem
key={message.id} key={message.id}
message={message} message={message}
@ -219,18 +296,11 @@ const Messages: FC<Props> = ({ assistant, topic, setActiveTopic }) => {
onGetMessages={onGetMessages} 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

View File

@ -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"