diff --git a/src/renderer/src/components/Popups/MultiSelectionPopup.tsx b/src/renderer/src/components/Popups/MultiSelectionPopup.tsx index b2a3f4cdd4..262dfc3cf8 100644 --- a/src/renderer/src/components/Popups/MultiSelectionPopup.tsx +++ b/src/renderer/src/components/Popups/MultiSelectionPopup.tsx @@ -1,8 +1,7 @@ import { CloseOutlined, CopyOutlined, DeleteOutlined, SaveOutlined } from '@ant-design/icons' -import { EventEmitter } from '@renderer/services/EventService' -import type { Message } from '@renderer/types/newMessage' +import { useChatContext } from '@renderer/pages/home/Messages/ChatContext' import { Button, Tooltip } from 'antd' -import { FC, useEffect, useState } from 'react' +import { FC } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -12,50 +11,9 @@ interface MultiSelectActionPopupProps { onAction?: (action: string, messageIds: string[]) => void topic: any } - -interface MessageTypeInfo { - hasUserMessages: boolean - hasAssistantMessages: boolean - messageIds: string[] -} - const MultiSelectActionPopup: FC = ({ visible, onClose, onAction }) => { const { t } = useTranslation() - const [, setSelectedMessages] = useState([]) - const [selectedMessageIds, setSelectedMessageIds] = useState([]) - const [, setMessageTypeInfo] = useState({ - hasUserMessages: false, - hasAssistantMessages: false, - messageIds: [] - }) - - useEffect(() => { - const handleSelectedMessagesChanged = (messageIds: string[]) => { - setSelectedMessageIds(messageIds) - EventEmitter.emit('REQUEST_SELECTED_MESSAGE_DETAILS', messageIds) - } - - const handleSelectedMessageDetails = (messages: Message[]) => { - setSelectedMessages(messages) - - const hasUserMessages = messages.some((msg) => msg.role === 'user') - const hasAssistantMessages = messages.some((msg) => msg.role === 'assistant') - - setMessageTypeInfo({ - hasUserMessages, - hasAssistantMessages, - messageIds: selectedMessageIds - }) - } - - EventEmitter.on('SELECTED_MESSAGES_CHANGED', handleSelectedMessagesChanged) - EventEmitter.on('SELECTED_MESSAGE_DETAILS', handleSelectedMessageDetails) - - return () => { - EventEmitter.off('SELECTED_MESSAGES_CHANGED', handleSelectedMessagesChanged) - EventEmitter.off('SELECTED_MESSAGE_DETAILS', handleSelectedMessageDetails) - } - }, [selectedMessageIds]) + const { toggleMultiSelectMode, selectedMessageIds } = useChatContext() const handleAction = (action: string) => { if (onAction) { @@ -64,7 +22,7 @@ const MultiSelectActionPopup: FC = ({ visible, onCl } const handleClose = () => { - EventEmitter.emit('MESSAGE_MULTI_SELECT', false) + toggleMultiSelectMode(false) onClose() } diff --git a/src/renderer/src/pages/home/Messages/ChatContext.tsx b/src/renderer/src/pages/home/Messages/ChatContext.tsx index 39da0f07de..36b5c33266 100644 --- a/src/renderer/src/pages/home/Messages/ChatContext.tsx +++ b/src/renderer/src/pages/home/Messages/ChatContext.tsx @@ -9,8 +9,10 @@ import { useDispatch, useSelector } from 'react-redux' interface ChatContextProps { isMultiSelectMode: boolean + selectedMessageIds: string[] toggleMultiSelectMode: (value: boolean) => void handleMultiSelectAction: (actionType: string, messageIds: string[]) => void + handleSelectMessage: (messageId: string, selected: boolean) => void activeTopic: Topic locateMessage: (messageId: string) => void messageRefs: Map @@ -36,6 +38,7 @@ export const ChatProvider: FC = ({ children, activeTopic }) = const { t } = useTranslation() const dispatch = useDispatch() const [isMultiSelectMode, setIsMultiSelectMode] = useState(false) + const [selectedMessageIds, setSelectedMessageIds] = useState([]) const [confirmDeleteVisible, setConfirmDeleteVisible] = useState(false) const [messagesToDelete, setMessagesToDelete] = useState([]) const [messageRefs, setMessageRefs] = useState>(new Map()) @@ -45,6 +48,9 @@ export const ChatProvider: FC = ({ children, activeTopic }) = const toggleMultiSelectMode = (value: boolean) => { setIsMultiSelectMode(value) + if (!value) { + setSelectedMessageIds([]) + } } const registerMessageElement = useCallback((id: string, element: HTMLElement | null) => { @@ -80,6 +86,18 @@ export const ChatProvider: FC = ({ children, activeTopic }) = } } + const handleSelectMessage = (messageId: string, selected: boolean) => { + setSelectedMessageIds((prev) => { + const newSet = new Set(prev) + if (selected) { + newSet.add(messageId) + } else { + newSet.delete(messageId) + } + return Array.from(newSet) + }) + } + const handleMultiSelectAction = (actionType: string, messageIds: string[]) => { if (messageIds.length === 0) { window.message.warning(t('chat.multiple.select.empty')) @@ -169,8 +187,10 @@ export const ChatProvider: FC = ({ children, activeTopic }) = const value = { isMultiSelectMode, + selectedMessageIds, toggleMultiSelectMode, handleMultiSelectAction, + handleSelectMessage, activeTopic, locateMessage, messageRefs, diff --git a/src/renderer/src/pages/home/Messages/Messages.tsx b/src/renderer/src/pages/home/Messages/Messages.tsx index b8f18fea77..4349dbdf5e 100644 --- a/src/renderer/src/pages/home/Messages/Messages.tsx +++ b/src/renderer/src/pages/home/Messages/Messages.tsx @@ -49,6 +49,7 @@ interface MessagesProps { const Messages: FC = ({ assistant, topic, setActiveTopic, onComponentUpdate, onFirstUpdate }) => { const { t } = useTranslation() const { showPrompt, showTopics, topicPosition, showAssistants, messageNavigation } = useSettings() + const { isMultiSelectMode, selectedMessageIds, handleSelectMessage } = useChatContext() const { updateTopic, addTopic } = useAssistant(assistant.id) const dispatch = useAppDispatch() const containerRef = useRef(null) @@ -57,9 +58,6 @@ const Messages: FC = ({ assistant, topic, setActiveTopic, onCompo const [isLoadingMore, setIsLoadingMore] = useState(false) const [isProcessingContext, setIsProcessingContext] = useState(false) - const { isMultiSelectMode } = useChatContext() - - const [selectedMessages, setSelectedMessages] = useState>(new Set()) const [isDragging, setIsDragging] = useState(false) const [dragStart, setDragStart] = useState({ x: 0, y: 0 }) const [dragCurrent, setDragCurrent] = useState({ x: 0, y: 0 }) @@ -68,23 +66,12 @@ const Messages: FC = ({ assistant, topic, setActiveTopic, onCompo const { displayCount, clearTopicMessages, deleteMessage, createTopicBranch } = useMessageOperations(topic) const messagesRef = useRef(messages) + const selectedMessagesSet = useMemo(() => new Set(selectedMessageIds), [selectedMessageIds]) + useEffect(() => { messagesRef.current = messages }, [messages]) - const handleSelectMessage = useCallback((messageId: string, selected: boolean) => { - setSelectedMessages((prev) => { - const newSet = new Set(prev) - if (selected) { - newSet.add(messageId) - } else { - newSet.delete(messageId) - } - EventEmitter.emit('SELECTED_MESSAGES_CHANGED', Array.from(newSet)) - return newSet - }) - }, []) - useEffect(() => { if (!isMultiSelectMode) return @@ -157,25 +144,6 @@ const Messages: FC = ({ assistant, topic, setActiveTopic, onCompo } }, []) - useEffect(() => { - if (!isMultiSelectMode) { - setSelectedMessages(new Set()) - } - }, [isMultiSelectMode]) - - useEffect(() => { - const handleRequestSelectedMessageDetails = (messageIds: string[]) => { - const selectedMessages = messages.filter((msg) => messageIds.includes(msg.id)) - EventEmitter.emit('SELECTED_MESSAGE_DETAILS', selectedMessages) - } - - EventEmitter.on('REQUEST_SELECTED_MESSAGE_DETAILS', handleRequestSelectedMessageDetails) - - return () => { - EventEmitter.off('REQUEST_SELECTED_MESSAGE_DETAILS', handleRequestSelectedMessageDetails) - } - }, [messages]) - useEffect(() => { const newDisplayMessages = computeDisplayMessages(messages, 0, displayCount) setDisplayMessages(newDisplayMessages) @@ -397,7 +365,7 @@ const Messages: FC = ({ assistant, topic, setActiveTopic, onCompo topic={topic} hidePresetMessages={assistant.settings?.hideMessages} isMultiSelectMode={isMultiSelectMode} - selectedMessages={selectedMessages} + selectedMessages={selectedMessagesSet} onSelectMessage={handleSelectMessage} registerMessageElement={registerMessageElement} />