mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-27 21:01:32 +08:00
Optimize chat components with memoization and shared layout
- Wrap `SessionMessages` and `SessionInputBar` in `useMemo` to prevent unnecessary re-renders - Refactor `AgentSessionMessages` to use shared layout components and message grouping - Extract common styled components to `shared.tsx` for reuse across message components
This commit is contained in:
parent
cee78c6610
commit
dd5592ddbb
@ -17,7 +17,7 @@ import { classNames } from '@renderer/utils'
|
||||
import { Flex } from 'antd'
|
||||
import { debounce } from 'lodash'
|
||||
import { AnimatePresence, motion } from 'motion/react'
|
||||
import React, { FC, useState } from 'react'
|
||||
import React, { FC, useMemo,useState } from 'react'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
@ -141,27 +141,27 @@ const Chat: FC<Props> = (props) => {
|
||||
? 'calc(100vh - var(--navbar-height) - var(--navbar-height) - 12px)'
|
||||
: 'calc(100vh - var(--navbar-height))'
|
||||
|
||||
const SessionMessages = () => {
|
||||
const SessionMessages = useMemo(() => {
|
||||
if (activeAgentId === null) {
|
||||
return <div> Active Agent ID is invalid.</div>
|
||||
return () => <div> Active Agent ID is invalid.</div>
|
||||
}
|
||||
const sessionId = activeSessionId[activeAgentId]
|
||||
if (!sessionId) {
|
||||
return <div> Active Session ID is invalid.</div>
|
||||
return () => <div> Active Session ID is invalid.</div>
|
||||
}
|
||||
return <AgentSessionMessages agentId={activeAgentId} sessionId={sessionId} />
|
||||
}
|
||||
return () => <AgentSessionMessages agentId={activeAgentId} sessionId={sessionId} />
|
||||
}, [activeAgentId, activeSessionId])
|
||||
|
||||
const SessionInputBar = () => {
|
||||
const SessionInputBar = useMemo(() => {
|
||||
if (activeAgentId === null) {
|
||||
return <div> Active Agent ID is invalid.</div>
|
||||
return () => <div> Active Agent ID is invalid.</div>
|
||||
}
|
||||
const sessionId = activeSessionId[activeAgentId]
|
||||
if (!sessionId) {
|
||||
return <div> Active Session ID is invalid.</div>
|
||||
return () => <div> Active Session ID is invalid.</div>
|
||||
}
|
||||
return <AgentSessionInputbar agentId={activeAgentId} sessionId={sessionId} />
|
||||
}
|
||||
return () => <AgentSessionInputbar agentId={activeAgentId} sessionId={sessionId} />
|
||||
}, [activeAgentId, activeSessionId])
|
||||
|
||||
return (
|
||||
<Container id="chat" className={classNames([messageStyle, { 'multi-select-mode': isMultiSelectMode }])}>
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
import { loggerService } from '@logger'
|
||||
import ContextMenu from '@renderer/components/ContextMenu'
|
||||
import Scrollbar from '@renderer/components/Scrollbar'
|
||||
import { useSession } from '@renderer/hooks/agents/useSession'
|
||||
import Blocks from '@renderer/pages/home/Messages/Blocks'
|
||||
import { getGroupedMessages } from '@renderer/services/MessagesService'
|
||||
import { useAppSelector } from '@renderer/store'
|
||||
import { selectMessagesForTopic } from '@renderer/store/newMessage'
|
||||
import { Topic } from '@renderer/types'
|
||||
import { buildAgentSessionTopicId } from '@renderer/utils/agentSession'
|
||||
import { useMemo } from 'react'
|
||||
import { memo,useMemo } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import MessageGroup from './MessageGroup'
|
||||
import NarrowLayout from './NarrowLayout'
|
||||
import { MessagesContainer, ScrollContainer } from './shared'
|
||||
|
||||
const logger = loggerService.withContext('AgentSessionMessages')
|
||||
|
||||
@ -23,6 +25,33 @@ const AgentSessionMessages: React.FC<Props> = ({ agentId, sessionId }) => {
|
||||
const sessionTopicId = useMemo(() => buildAgentSessionTopicId(sessionId), [sessionId])
|
||||
const messages = useAppSelector((state) => selectMessagesForTopic(state, sessionTopicId))
|
||||
|
||||
const displayMessages = useMemo(() => {
|
||||
if (!messages || messages.length === 0) return []
|
||||
return [...messages].reverse()
|
||||
}, [messages])
|
||||
|
||||
const groupedMessages = useMemo(() => {
|
||||
if (!displayMessages || displayMessages.length === 0) return []
|
||||
return Object.entries(getGroupedMessages(displayMessages))
|
||||
}, [displayMessages])
|
||||
|
||||
const sessionAssistantId = session?.agent_id ?? agentId
|
||||
const sessionName = session?.name ?? sessionId
|
||||
const sessionCreatedAt = session?.created_at ?? session?.updated_at ?? FALLBACK_TIMESTAMP
|
||||
const sessionUpdatedAt = session?.updated_at ?? session?.created_at ?? FALLBACK_TIMESTAMP
|
||||
|
||||
const derivedTopic = useMemo<Topic>(
|
||||
() => ({
|
||||
id: sessionTopicId,
|
||||
assistantId: sessionAssistantId,
|
||||
name: sessionName,
|
||||
createdAt: sessionCreatedAt,
|
||||
updatedAt: sessionUpdatedAt,
|
||||
messages: []
|
||||
}),
|
||||
[sessionTopicId, sessionAssistantId, sessionName, sessionCreatedAt, sessionUpdatedAt]
|
||||
)
|
||||
|
||||
logger.silly('Rendering agent session messages', {
|
||||
sessionId,
|
||||
messageCount: messages.length
|
||||
@ -33,15 +62,13 @@ const AgentSessionMessages: React.FC<Props> = ({ agentId, sessionId }) => {
|
||||
<NarrowLayout style={{ display: 'flex', flexDirection: 'column-reverse' }}>
|
||||
<ContextMenu>
|
||||
<ScrollContainer>
|
||||
{messages
|
||||
.slice()
|
||||
.reverse()
|
||||
.map((message) => (
|
||||
<MessageRow key={message.id} $role={message.role}>
|
||||
<Blocks blocks={message.blocks ?? []} message={message} />
|
||||
</MessageRow>
|
||||
))}
|
||||
{!messages.length && <EmptyState>{session ? 'No messages yet.' : 'Loading session...'}</EmptyState>}
|
||||
{groupedMessages.length > 0 ? (
|
||||
groupedMessages.map(([key, groupMessages]) => (
|
||||
<MessageGroup key={key} messages={groupMessages} topic={derivedTopic} />
|
||||
))
|
||||
) : (
|
||||
<EmptyState>{session ? 'No messages yet.' : 'Loading session...'}</EmptyState>
|
||||
)}
|
||||
</ScrollContainer>
|
||||
</ContextMenu>
|
||||
</NarrowLayout>
|
||||
@ -49,25 +76,6 @@ const AgentSessionMessages: React.FC<Props> = ({ agentId, sessionId }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const ScrollContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
gap: 12px;
|
||||
padding: 10px 10px 20px;
|
||||
.multi-select-mode & {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
`
|
||||
|
||||
const MessageRow = styled.div<{ $role: string }>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: ${(props) => (props.$role === 'user' ? 'flex-end' : 'flex-start')};
|
||||
.block-wrapper {
|
||||
max-width: 700px;
|
||||
}
|
||||
`
|
||||
|
||||
const EmptyState = styled.div`
|
||||
color: var(--color-text-3);
|
||||
font-size: 12px;
|
||||
@ -75,16 +83,6 @@ const EmptyState = styled.div`
|
||||
padding: 20px 0;
|
||||
`
|
||||
|
||||
interface ContainerProps {
|
||||
$right?: boolean
|
||||
}
|
||||
const FALLBACK_TIMESTAMP = '1970-01-01T00:00:00.000Z'
|
||||
|
||||
const MessagesContainer = styled(Scrollbar)<ContainerProps>`
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
overflow-x: hidden;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
`
|
||||
|
||||
export default AgentSessionMessages
|
||||
export default memo(AgentSessionMessages)
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { loggerService } from '@logger'
|
||||
import ContextMenu from '@renderer/components/ContextMenu'
|
||||
import { LoadingIcon } from '@renderer/components/Icons'
|
||||
import Scrollbar from '@renderer/components/Scrollbar'
|
||||
import { LOAD_MORE_COUNT } from '@renderer/config/constant'
|
||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||
import { useChatContext } from '@renderer/hooks/useChatContext'
|
||||
@ -41,6 +40,7 @@ import MessageAnchorLine from './MessageAnchorLine'
|
||||
import MessageGroup from './MessageGroup'
|
||||
import NarrowLayout from './NarrowLayout'
|
||||
import Prompt from './Prompt'
|
||||
import { MessagesContainer, ScrollContainer } from './shared'
|
||||
|
||||
interface MessagesProps {
|
||||
assistant: Assistant
|
||||
@ -392,25 +392,4 @@ const LoaderContainer = styled.div`
|
||||
pointer-events: none;
|
||||
`
|
||||
|
||||
const ScrollContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
padding: 10px 10px 20px;
|
||||
.multi-select-mode & {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
`
|
||||
|
||||
interface ContainerProps {
|
||||
$right?: boolean
|
||||
}
|
||||
|
||||
const MessagesContainer = styled(Scrollbar)<ContainerProps>`
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
overflow-x: hidden;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
`
|
||||
|
||||
export default Messages
|
||||
|
||||
23
src/renderer/src/pages/home/Messages/shared.tsx
Normal file
23
src/renderer/src/pages/home/Messages/shared.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import Scrollbar from '@renderer/components/Scrollbar'
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const ScrollContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
padding: 10px 10px 20px;
|
||||
.multi-select-mode & {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
`
|
||||
|
||||
interface ContainerProps {
|
||||
$right?: boolean
|
||||
}
|
||||
|
||||
export const MessagesContainer = styled(Scrollbar)<ContainerProps>`
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
overflow-x: hidden;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
`
|
||||
Loading…
Reference in New Issue
Block a user