From e4de5331e0a1f44b9238699047e31a9f8fd30816 Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Sat, 19 Apr 2025 18:09:44 +0800 Subject: [PATCH] Revert "feat: add chat message translate copy button (#4620)" This reverts commit 8b462935b49a38e6722ccfc3769f0416fb6a071a. --- .../src/hooks/useMessageOperations.ts | 42 ++------- src/renderer/src/i18n/locales/en-us.json | 2 - src/renderer/src/i18n/locales/ja-jp.json | 2 - src/renderer/src/i18n/locales/ru-ru.json | 2 - src/renderer/src/i18n/locales/zh-cn.json | 2 - src/renderer/src/i18n/locales/zh-tw.json | 2 - .../src/pages/home/Inputbar/Inputbar.tsx | 6 +- .../pages/home/Messages/MessageMenubar.tsx | 91 ++++++++++--------- 8 files changed, 59 insertions(+), 90 deletions(-) diff --git a/src/renderer/src/hooks/useMessageOperations.ts b/src/renderer/src/hooks/useMessageOperations.ts index 668ebdc9..05d072c7 100644 --- a/src/renderer/src/hooks/useMessageOperations.ts +++ b/src/renderer/src/hooks/useMessageOperations.ts @@ -1,6 +1,5 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { estimateMessageUsage } from '@renderer/services/TokenService' -import { translateText } from '@renderer/services/TranslateService' import store, { useAppDispatch, useAppSelector } from '@renderer/store' import { clearStreamMessage, @@ -19,10 +18,8 @@ import { import type { Assistant, Message, Topic } from '@renderer/types' import { abortCompletion } from '@renderer/utils/abortController' import { useCallback } from 'react' -import { useTranslation } from 'react-i18next' import { TopicManager } from './useTopic' - /** * 自定义Hook,提供消息操作相关的功能 * @@ -31,7 +28,6 @@ import { TopicManager } from './useTopic' */ export function useMessageOperations(topic: Topic) { const dispatch = useAppDispatch() - const { t } = useTranslation() /** * 删除单个消息 @@ -64,7 +60,8 @@ export function useMessageOperations(topic: Topic) { const message = messages?.find((m) => m.id === messageId) if (message) { const updatedMessage = { ...message, ...updates } - updates.usage = await estimateMessageUsage(updatedMessage) + const usage = await estimateMessageUsage(updatedMessage) + updates.usage = usage } } await dispatch(updateMessageThunk(topic.id, messageId, updates)) @@ -131,7 +128,7 @@ export function useMessageOperations(topic: Topic) { const clearTopicMessagesAction = useCallback( async (_topicId?: string) => { const topicId = _topicId || topic.id - dispatch(clearTopicMessages(topicId)) + await dispatch(clearTopicMessages(topicId)) await TopicManager.clearTopicMessages(topicId) }, [dispatch, topic.id] @@ -151,7 +148,7 @@ export function useMessageOperations(topic: Topic) { * 创建新的上下文(clear message) */ const createNewContext = useCallback(async () => { - await EventEmitter.emit(EVENT_NAMES.NEW_CONTEXT) + EventEmitter.emit(EVENT_NAMES.NEW_CONTEXT) }, []) const displayCount = useAppSelector(selectDisplayCount) @@ -193,28 +190,6 @@ export function useMessageOperations(topic: Topic) { dispatch(setTopicLoading({ topicId: topic.id, loading: false })) }, [topic.id, dispatch]) - const translateMessage = useCallback( - async (messageId: string, language: string) => { - const messages = store.getState().messages.messagesByTopic[topic.id] - const message = messages?.find((m) => m.id === messageId) - if (!message) return - - translateText(message.content, language, (text) => { - setStreamMessageAction({ ...message, translatedContent: text }) - }) - .then(() => { - commitStreamMessageAction(messageId) - }) - .catch((error) => { - console.error('Translation failed:', error) - window.message.error({ content: t('translate.error.failed'), key: 'translate-message' }) - editMessage(messageId, { translatedContent: undefined }) - clearStreamMessageAction(messageId) - }) - }, - [topic.id, editMessage, t, clearStreamMessageAction, setStreamMessageAction, commitStreamMessageAction] - ) - /** * 恢复/重发消息 * 暂时不需要 @@ -241,15 +216,16 @@ export function useMessageOperations(topic: Topic) { clearTopicMessages: clearTopicMessagesAction, // pauseMessage, pauseMessages, - resumeMessage, - translateMessage + resumeMessage } } export const useTopicMessages = (topic: Topic) => { - return useAppSelector((state) => selectTopicMessages(state, topic.id)) + const messages = useAppSelector((state) => selectTopicMessages(state, topic.id)) + return messages } export const useTopicLoading = (topic: Topic) => { - return useAppSelector((state) => selectTopicLoading(state, topic.id)) + const loading = useAppSelector((state) => selectTopicLoading(state, topic.id)) + return loading } diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index dffe50b0..60b72808 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -561,7 +561,6 @@ "error": "Error occurred" }, "topic.added": "New topic added", - "translation.copied": "Translation copied", "upgrade.success.button": "Restart", "upgrade.success.content": "Please restart the application to complete the upgrade", "upgrade.success.title": "Upgrade successfully", @@ -1408,7 +1407,6 @@ "content": "Translation will replace the original text, continue?", "title": "Translation Confirmation" }, - "copy": "Copy", "error.failed": "Translation failed", "error.not_configured": "Translation model is not configured", "history": { diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index f9bf319f..528f6a21 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -560,7 +560,6 @@ "error": "エラーが発生しました" }, "topic.added": "新しいトピックが追加されました", - "translation.copied": "翻訳結果をコピーしました", "upgrade.success.button": "再起動", "upgrade.success.content": "アップグレードを完了するためにアプリケーションを再起動してください", "upgrade.success.title": "アップグレードに成功しました", @@ -1408,7 +1407,6 @@ "content": "翻訳すると元のテキストが上書きされます。続行しますか?", "title": "翻訳確認" }, - "copy": "翻訳をコピー", "error.failed": "翻訳に失敗しました", "error.not_configured": "翻訳モデルが設定されていません", "history": { diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 7166e84e..0349c617 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -561,7 +561,6 @@ "error": "Произошла ошибка" }, "topic.added": "Новый топик добавлен", - "translation.copied": "Перевод скопирован", "upgrade.success.button": "Перезапустить", "upgrade.success.content": "Пожалуйста, перезапустите приложение для завершения обновления", "upgrade.success.title": "Обновление успешно", @@ -1408,7 +1407,6 @@ "content": "Перевод заменит исходный текст, продолжить?", "title": "Перевод подтверждение" }, - "copy": "Копировать перевод", "error.failed": "Перевод не удалось", "error.not_configured": "Модель перевода не настроена", "history": { diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 9c5d130a..86156aeb 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -561,7 +561,6 @@ "error": "发生错误" }, "topic.added": "话题添加成功", - "translation.copied": "翻译已复制", "upgrade.success.button": "重启", "upgrade.success.content": "重启用以完成升级", "upgrade.success.title": "升级成功", @@ -1408,7 +1407,6 @@ "content": "翻译后将覆盖原文,是否继续?", "title": "翻译确认" }, - "copy": "复制翻译", "error.failed": "翻译失败", "error.not_configured": "翻译模型未配置", "history": { diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 746552f6..72d0ba7c 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -560,7 +560,6 @@ "invoking": "調用中", "error": "發生錯誤" }, - "translation.copied": "翻譯已複製", "topic.added": "新話題已新增", "upgrade.success.button": "重新啟動", "upgrade.success.content": "請重新啟動程式以完成升級", @@ -1408,7 +1407,6 @@ "content": "翻譯後將覆蓋原文,是否繼續?", "title": "翻譯確認" }, - "copy": "複製翻譯", "error.failed": "翻譯失敗", "error.not_configured": "翻譯模型未設定", "history": { diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index 9ba1653f..c08fa49a 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -181,7 +181,7 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = return } - await EventEmitter.emit(EVENT_NAMES.SEND_MESSAGE) + EventEmitter.emit(EVENT_NAMES.SEND_MESSAGE) try { // Dispatch the sendMessage action with all options @@ -209,7 +209,7 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = userMessage.usage = await estimateMessageUsage(userMessage) currentMessageId.current = userMessage.id - await dispatch( + dispatch( _sendMessage(userMessage, assistant, topic, { mentions: mentionModels }) @@ -525,7 +525,7 @@ const Inputbar: FC = ({ assistant: _assistant, setActiveTopic, topic }) = await onPause() await delay(1) } - await EventEmitter.emit(EVENT_NAMES.CLEAR_MESSAGES) + EventEmitter.emit(EVENT_NAMES.CLEAR_MESSAGES) } const onNewContext = () => { diff --git a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx index 09e3932f..b936dd0c 100644 --- a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx +++ b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx @@ -7,6 +7,7 @@ import { TranslateLanguageOptions } from '@renderer/config/translate' import { useMessageOperations, useTopicLoading } from '@renderer/hooks/useMessageOperations' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { getMessageTitle, resetAssistantMessage } from '@renderer/services/MessagesService' +import { translateText } from '@renderer/services/TranslateService' import { RootState } from '@renderer/store' import type { Message, Model } from '@renderer/types' import type { Assistant, Topic } from '@renderer/types' @@ -36,7 +37,7 @@ import { ThumbsUp, Trash } from 'lucide-react' -import React, { FC, memo, useCallback, useMemo, useState } from 'react' +import { FC, memo, useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import styled from 'styled-components' @@ -59,11 +60,12 @@ const MessageMenubar: FC = (props) => { props const { t } = useTranslation() const [copied, setCopied] = useState(false) - const [translationCopied, setTranslationCopied] = useState(false) + const [isTranslating, setIsTranslating] = useState(false) const [showRegenerateTooltip, setShowRegenerateTooltip] = useState(false) const [showDeleteTooltip, setShowDeleteTooltip] = useState(false) const assistantModel = assistant?.model - const { editMessage, deleteMessage, resendMessage, translateMessage } = useMessageOperations(topic) + const { editMessage, setStreamMessage, deleteMessage, resendMessage, commitStreamMessage, clearStreamMessage } = + useMessageOperations(topic) const loading = useTopicLoading(topic) const isUserMessage = message.role === 'user' @@ -90,23 +92,9 @@ const MessageMenubar: FC = (props) => { [message, t] ) - const onCopyTranslation = useCallback( - (e: React.MouseEvent) => { - e.stopPropagation() - - if (message.translatedContent) { - navigator.clipboard.writeText(removeTrailingDoubleSpaces(message.translatedContent.trimStart())) - window.message.success({ content: t('message.translation.copied'), key: 'copy-translation' }) - setTranslationCopied(true) - setTimeout(() => setTranslationCopied(false), 2000) - } - }, - [message.translatedContent, t] - ) - const onNewBranch = useCallback(async () => { if (loading) return - await EventEmitter.emit(EVENT_NAMES.NEW_BRANCH, index) + EventEmitter.emit(EVENT_NAMES.NEW_BRANCH, index) window.message.success({ content: t('chat.message.new.branch.created'), key: 'new-branch' }) }, [index, t, loading]) @@ -156,7 +144,7 @@ const MessageMenubar: FC = (props) => { if (editedText && editedText !== textToEdit) { // 解析编辑后的文本,提取图片 URL - const imageRegex = /!\[image-\d+]\((.*?)\)/g + const imageRegex = /!\[image-\d+\]\((.*?)\)/g const imageUrls: string[] = [] let match let content = editedText @@ -182,7 +170,7 @@ const MessageMenubar: FC = (props) => { }) resendMessage && - (await handleResendUserMessage({ + handleResendUserMessage({ ...message, content: content.trim(), metadata: { @@ -195,10 +183,38 @@ const MessageMenubar: FC = (props) => { } : undefined } - })) + }) } }, [message, editMessage, handleResendUserMessage, t]) + const handleTranslate = useCallback( + async (language: string) => { + if (isTranslating) return + + editMessage(message.id, { translatedContent: t('translate.processing') }) + + setIsTranslating(true) + + try { + await translateText(message.content, language, (text) => { + // 使用 setStreamMessage 来更新翻译内容 + setStreamMessage({ ...message, translatedContent: text }) + }) + + // 翻译完成后,提交流消息 + commitStreamMessage(message.id) + } catch (error) { + console.error('Translation failed:', error) + window.message.error({ content: t('translate.error.failed'), key: 'translate-message' }) + editMessage(message.id, { translatedContent: undefined }) + clearStreamMessage(message.id) + } finally { + setIsTranslating(false) + } + }, + [isTranslating, message, editMessage, setStreamMessage, commitStreamMessage, clearStreamMessage, t] + ) + const dropdownItems = useMemo( () => [ { @@ -265,7 +281,7 @@ const MessageMenubar: FC = (props) => { onClick: async () => { const markdown = messageToMarkdown(message) const title = await getMessageTitle(message) - await window.api.export.toWord(markdown, title) + window.api.export.toWord(markdown, title) } }, exportMenuOptions.notion && { @@ -274,7 +290,7 @@ const MessageMenubar: FC = (props) => { onClick: async () => { const title = await getMessageTitle(message) const markdown = messageToMarkdown(message) - await exportMarkdownToNotion(title, markdown) + exportMarkdownToNotion(title, markdown) } }, exportMenuOptions.yuque && { @@ -283,7 +299,7 @@ const MessageMenubar: FC = (props) => { onClick: async () => { const title = await getMessageTitle(message) const markdown = messageToMarkdown(message) - await exportMarkdownToYuque(title, markdown) + exportMarkdownToYuque(title, markdown) } }, exportMenuOptions.obsidian && { @@ -301,7 +317,7 @@ const MessageMenubar: FC = (props) => { onClick: async () => { const title = await getMessageTitle(message) const markdown = messageToMarkdown(message) - await exportMarkdownToJoplin(title, markdown) + exportMarkdownToJoplin(title, markdown) } }, exportMenuOptions.siyuan && { @@ -310,7 +326,7 @@ const MessageMenubar: FC = (props) => { onClick: async () => { const title = await getMessageTitle(message) const markdown = messageToMarkdown(message) - await exportMarkdownToSiyuan(title, markdown) + exportMarkdownToSiyuan(title, markdown) } } ].filter(Boolean) @@ -324,8 +340,8 @@ const MessageMenubar: FC = (props) => { if (loading) return const selectedModel = isGrouped ? model : assistantModel const _message = resetAssistantMessage(message, selectedModel) - await editMessage(message.id, { ..._message }) - await resendMessage(_message, assistant) + editMessage(message.id, { ..._message }) + resendMessage(_message, assistant) } const onMentionModel = async (e: React.MouseEvent) => { @@ -333,7 +349,7 @@ const MessageMenubar: FC = (props) => { if (loading) return const selectedModel = await SelectModelPopup.show({ model }) if (!selectedModel) return - await resendMessage(message, { ...assistant, model: selectedModel }, true) + resendMessage(message, { ...assistant, model: selectedModel }, true) } const onUseful = useCallback( @@ -398,26 +414,13 @@ const MessageMenubar: FC = (props) => { ...TranslateLanguageOptions.map((item) => ({ label: item.emoji + ' ' + item.label, key: item.value, - onClick: () => translateMessage(message.id, item.value) + onClick: () => handleTranslate(item.value) })), { label: '✖ ' + t('translate.close'), key: 'translate-close', onClick: () => editMessage(message.id, { translatedContent: undefined }) - }, - ...(message.translatedContent - ? [ - { - label: '📋 ' + t('translate.copy'), - key: 'translate-copy', - icon: translationCopied ? : null, - onClick: (e) => { - e.domEvent.stopPropagation() - onCopyTranslation(e.domEvent as unknown as React.MouseEvent) - } - } - ] - : []) + } ], onClick: (e) => e.domEvent.stopPropagation() }}