diff --git a/src/renderer/src/pages/home/Messages/ChatContext.tsx b/src/renderer/src/pages/home/Messages/ChatContext.tsx index 09edd7100a..6b07c9c06d 100644 --- a/src/renderer/src/pages/home/Messages/ChatContext.tsx +++ b/src/renderer/src/pages/home/Messages/ChatContext.tsx @@ -3,7 +3,7 @@ import { messageBlocksSelectors } from '@renderer/store/messageBlock' import { newMessagesActions, selectMessagesForTopic } from '@renderer/store/newMessage' import { Topic } from '@renderer/types' import { Modal } from 'antd' -import { createContext, FC, ReactNode, use, useState } from 'react' +import { createContext, FC, ReactNode, use, useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' @@ -12,6 +12,8 @@ interface ChatContextProps { toggleMultiSelectMode: (value: boolean) => void handleMultiSelectAction: (actionType: string, messageIds: string[]) => void activeTopic: Topic + messageRefs: Map + registerMessageElement: (id: string, element: HTMLElement | null) => void } const ChatContext = createContext(undefined) @@ -35,6 +37,7 @@ export const ChatProvider: FC = ({ children, activeTopic }) = const [isMultiSelectMode, setIsMultiSelectMode] = useState(false) const [confirmDeleteVisible, setConfirmDeleteVisible] = useState(false) const [messagesToDelete, setMessagesToDelete] = useState([]) + const [messageRefs, setMessageRefs] = useState>(new Map()) const messages = useSelector((state: RootState) => selectMessagesForTopic(state, activeTopic.id)) const messageBlocks = useSelector(messageBlocksSelectors.selectEntities) @@ -43,6 +46,18 @@ export const ChatProvider: FC = ({ children, activeTopic }) = setIsMultiSelectMode(value) } + const registerMessageElement = useCallback((id: string, element: HTMLElement | null) => { + setMessageRefs((prev) => { + const newRefs = new Map(prev) + if (element) { + newRefs.set(id, element) + } else { + newRefs.delete(id) + } + return newRefs + }) + }, []) + const handleMultiSelectAction = (actionType: string, messageIds: string[]) => { if (messageIds.length === 0) { window.message.warning(t('chat.multiple.select.empty')) @@ -134,7 +149,9 @@ export const ChatProvider: FC = ({ children, activeTopic }) = isMultiSelectMode, toggleMultiSelectMode, handleMultiSelectAction, - activeTopic + activeTopic, + messageRefs, + registerMessageElement } return ( diff --git a/src/renderer/src/pages/home/Messages/MessageGroup.tsx b/src/renderer/src/pages/home/Messages/MessageGroup.tsx index 35493e414c..77e85fafc5 100644 --- a/src/renderer/src/pages/home/Messages/MessageGroup.tsx +++ b/src/renderer/src/pages/home/Messages/MessageGroup.tsx @@ -1,7 +1,6 @@ import Scrollbar from '@renderer/components/Scrollbar' import { useMessageOperations } from '@renderer/hooks/useMessageOperations' import { useSettings } from '@renderer/hooks/useSettings' -import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { MultiModelMessageStyle } from '@renderer/store/settings' import type { Topic } from '@renderer/types' import type { Message } from '@renderer/types/newMessage' @@ -10,6 +9,7 @@ import { Popover } from 'antd' import { memo, useCallback, useEffect, useRef, useState } from 'react' import styled, { css } from 'styled-components' +import { useChatContext } from './ChatContext' import MessageItem from './Message' import MessageGroupMenuBar from './MessageGroupMenuBar' import SelectableMessage from './MessageSelect' @@ -18,9 +18,9 @@ interface Props { messages: (Message & { index: number })[] topic: Topic hidePresetMessages?: boolean - isMultiSelectMode?: boolean // 添加是否处于多选模式 - selectedMessages?: Set // 已选择的消息ID集合 - onSelectMessage?: (messageId: string, selected: boolean) => void // 消息选择回调 + isMultiSelectMode?: boolean + selectedMessages?: Set + onSelectMessage?: (messageId: string, selected: boolean) => void registerMessageElement?: (id: string, element: HTMLElement | null) => void } @@ -35,6 +35,7 @@ const MessageGroup = ({ }: Props) => { const { editMessage } = useMessageOperations(topic) const { multiModelMessageStyle: multiModelMessageStyleSetting, gridColumns, gridPopoverTrigger } = useSettings() + const { registerMessageElement: contextRegisterMessageElement } = useChatContext() const [multiModelMessageStyle, setMultiModelMessageStyle] = useState( messages[0].multiModelMessageStyle || multiModelMessageStyleSetting @@ -123,41 +124,20 @@ const MessageGroup = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [messages, selectedIndex, isGrouped, messageLength]) - // 添加对LOCATE_MESSAGE事件的监听 useEffect(() => { - // 为每个消息注册一个定位事件监听器 - const eventHandlers: { [key: string]: () => void } = {} - messages.forEach((message) => { - const eventName = EVENT_NAMES.LOCATE_MESSAGE + ':' + message.id - const handler = () => { - // 检查消息是否处于可见状态 - const element = document.getElementById(`message-${message.id}`) - if (element) { - const display = window.getComputedStyle(element).display - - if (display === 'none') { - // 如果消息隐藏,先切换标签 - setSelectedMessage(message) - } else { - // 直接滚动 - element.scrollIntoView({ behavior: 'smooth', block: 'start' }) - } - } + const element = document.getElementById(`message-${message.id}`) + if (element) { + contextRegisterMessageElement(message.id, element) } - - eventHandlers[eventName] = handler - EventEmitter.on(eventName, handler) }) - // 清理函数 return () => { - // 移除所有事件监听器 - Object.entries(eventHandlers).forEach(([eventName, handler]) => { - EventEmitter.off(eventName, handler) + messages.forEach((message) => { + contextRegisterMessageElement(message.id, null) }) } - }, [messages, setSelectedMessage]) + }, [messages, contextRegisterMessageElement]) const renderMessage = useCallback( (message: Message & { index: number }, index: number) => { @@ -227,16 +207,17 @@ const MessageGroup = ({ [ isGrid, isGrouped, - isHorizontal, - multiModelMessageStyle, - selectedIndex, topic, hidePresetMessages, - gridPopoverTrigger, + multiModelMessageStyle, + selectedIndex, + isHorizontal, getSelectedMessageId, - isMultiSelectMode, // 添加依赖项 - selectedMessages, // 添加依赖项 - onSelectMessage // 添加依赖项 + isMultiSelectMode, + selectedMessages, + registerMessageElement, + onSelectMessage, + gridPopoverTrigger ] ) diff --git a/src/renderer/src/pages/home/Messages/MessageSelect.tsx b/src/renderer/src/pages/home/Messages/MessageSelect.tsx index 42817be480..0a0cb1fe0d 100644 --- a/src/renderer/src/pages/home/Messages/MessageSelect.tsx +++ b/src/renderer/src/pages/home/Messages/MessageSelect.tsx @@ -2,6 +2,8 @@ import { Checkbox } from 'antd' import { FC, ReactNode, useEffect, useRef } from 'react' import styled from 'styled-components' +import { useChatContext } from './ChatContext' + interface SelectableMessageProps { children: ReactNode isMultiSelectMode: boolean @@ -20,14 +22,24 @@ const SelectableMessage: FC = ({ registerElement }) => { const containerRef = useRef(null) + const { registerMessageElement: contextRegister } = useChatContext() useEffect(() => { - if (registerElement && containerRef.current) { - registerElement(messageId, containerRef.current) - return () => registerElement(messageId, null) + if (containerRef.current) { + if (registerElement) { + registerElement(messageId, containerRef.current) + } + contextRegister(messageId, containerRef.current) + + return () => { + if (registerElement) { + registerElement(messageId, null) + } + contextRegister(messageId, null) + } } return undefined - }, [messageId, registerElement]) + }, [messageId, registerElement, contextRegister]) return (