feat: 增加保持并发送的功能 #527

This commit is contained in:
kangfenmao 2024-12-30 14:09:59 +08:00
parent 96124cf58e
commit 47c455b125
9 changed files with 71 additions and 13 deletions

View File

@ -4,6 +4,7 @@ import { TextAreaProps } from 'antd/lib/input'
import { TextAreaRef } from 'antd/lib/input/TextArea' import { TextAreaRef } from 'antd/lib/input/TextArea'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
import { TopView } from '../TopView' import { TopView } from '../TopView'
@ -11,13 +12,14 @@ interface ShowParams {
text: string text: string
textareaProps?: TextAreaProps textareaProps?: TextAreaProps
modalProps?: ModalProps modalProps?: ModalProps
children?: (props: { onOk?: () => void; onCancel?: () => void }) => React.ReactNode
} }
interface Props extends ShowParams { interface Props extends ShowParams {
resolve: (data: any) => void resolve: (data: any) => void
} }
const PopupContainer: React.FC<Props> = ({ text, textareaProps, modalProps, resolve }) => { const PopupContainer: React.FC<Props> = ({ text, textareaProps, modalProps, resolve, children }) => {
const [open, setOpen] = useState(true) const [open, setOpen] = useState(true)
const { t } = useTranslation() const { t } = useTranslation()
const [textValue, setTextValue] = useState(text) const [textValue, setTextValue] = useState(text)
@ -73,12 +75,17 @@ const PopupContainer: React.FC<Props> = ({ text, textareaProps, modalProps, reso
onInput={resizeTextArea} onInput={resizeTextArea}
onChange={(e) => setTextValue(e.target.value)} onChange={(e) => setTextValue(e.target.value)}
/> />
<ChildrenContainer>{children && children({ onOk, onCancel })}</ChildrenContainer>
</Modal> </Modal>
) )
} }
const TopViewKey = 'TextEditPopup' const TopViewKey = 'TextEditPopup'
const ChildrenContainer = styled.div`
position: relative;
`
export default class TextEditPopup { export default class TextEditPopup {
static topviewId = 0 static topviewId = 0
static hide() { static hide() {

View File

@ -112,7 +112,8 @@
"topics.list": "Topic List", "topics.list": "Topic List",
"topics.move_to": "Move to", "topics.move_to": "Move to",
"topics.title": "Topics", "topics.title": "Topics",
"translate": "Translate" "translate": "Translate",
"resend": "Resend"
}, },
"common": { "common": {
"and": "and", "and": "and",

View File

@ -112,7 +112,8 @@
"topics.list": "トピックリスト", "topics.list": "トピックリスト",
"topics.move_to": "移動先", "topics.move_to": "移動先",
"topics.title": "トピック", "topics.title": "トピック",
"translate": "翻訳" "translate": "翻訳",
"resend": "再送信"
}, },
"common": { "common": {
"and": "と", "and": "と",

View File

@ -112,7 +112,8 @@
"topics.list": "Список топиков", "topics.list": "Список топиков",
"topics.move_to": "Переместить в", "topics.move_to": "Переместить в",
"topics.title": "Топики", "topics.title": "Топики",
"translate": "Перевести" "translate": "Перевести",
"resend": "Переотправить"
}, },
"common": { "common": {
"and": "и", "and": "и",

View File

@ -112,7 +112,8 @@
"topics.list": "话题列表", "topics.list": "话题列表",
"topics.move_to": "移动到", "topics.move_to": "移动到",
"topics.title": "话题", "topics.title": "话题",
"translate": "翻译" "translate": "翻译",
"resend": "重新发送"
}, },
"common": { "common": {
"and": "和", "and": "和",

View File

@ -112,7 +112,8 @@
"topics.list": "話題列表", "topics.list": "話題列表",
"topics.move_to": "移動到", "topics.move_to": "移動到",
"topics.title": "話題", "topics.title": "話題",
"translate": "翻譯" "translate": "翻譯",
"resend": "重新發送"
}, },
"common": { "common": {
"and": "與", "and": "與",

View File

@ -86,9 +86,12 @@ const MessageItem: FC<Props> = ({
} }
useEffect(() => { useEffect(() => {
const unsubscribes = [EventEmitter.on(EVENT_NAMES.LOCATE_MESSAGE + ':' + message.id, messageHighlightHandler)] const unsubscribes = [
EventEmitter.on(EVENT_NAMES.LOCATE_MESSAGE + ':' + message.id, messageHighlightHandler),
EventEmitter.on(EVENT_NAMES.RESEND_MESSAGE + ':' + message.id, onEditMessage)
]
return () => unsubscribes.forEach((unsub) => unsub()) return () => unsubscribes.forEach((unsub) => unsub())
}, [message]) }, [message, onEditMessage])
useEffect(() => { useEffect(() => {
if (message.role === 'user' && !message.usage) { if (message.role === 'user' && !message.usage) {
@ -178,6 +181,7 @@ const MessageItem: FC<Props> = ({
setModel={setModel} setModel={setModel}
onEditMessage={onEditMessage} onEditMessage={onEditMessage}
onDeleteMessage={onDeleteMessage} onDeleteMessage={onDeleteMessage}
onGetMessages={onGetMessages}
/> />
</MessageFooter> </MessageFooter>
)} )}

View File

@ -15,7 +15,7 @@ import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { translateText } from '@renderer/services/TranslateService' import { translateText } from '@renderer/services/TranslateService'
import { Message, Model } from '@renderer/types' import { Message, Model } from '@renderer/types'
import { removeTrailingDoubleSpaces } from '@renderer/utils' import { removeTrailingDoubleSpaces } from '@renderer/utils'
import { Dropdown, Popconfirm, Tooltip } from 'antd' import { Button, Dropdown, Popconfirm, Tooltip } from 'antd'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { FC, useCallback, useMemo, useState } from 'react' import { FC, useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -31,6 +31,7 @@ interface Props {
setModel: (model: Model) => void setModel: (model: Model) => void
onEditMessage?: (message: Message) => void onEditMessage?: (message: Message) => void
onDeleteMessage?: (message: Message) => void onDeleteMessage?: (message: Message) => void
onGetMessages?: () => Message[]
} }
const MessageMenubar: FC<Props> = (props) => { const MessageMenubar: FC<Props> = (props) => {
@ -43,7 +44,8 @@ const MessageMenubar: FC<Props> = (props) => {
assistantModel, assistantModel,
setModel, setModel,
onEditMessage, onEditMessage,
onDeleteMessage onDeleteMessage,
onGetMessages
} = props } = props
const { t } = useTranslation() const { t } = useTranslation()
const [copied, setCopied] = useState(false) const [copied, setCopied] = useState(false)
@ -75,10 +77,43 @@ const MessageMenubar: FC<Props> = (props) => {
}) })
}, [index, t]) }, [index, t])
const onResend = useCallback(() => {
const _messages = onGetMessages?.() || []
const index = _messages.findIndex((m) => m.id === message.id)
const nextIndex = index + 1
const nextMessage = _messages[nextIndex]
if (nextMessage && nextMessage.role === 'assistant') {
EventEmitter.emit(EVENT_NAMES.RESEND_MESSAGE + ':' + nextMessage.id, {
...nextMessage,
content: '',
status: 'sending',
modelId: assistantModel?.id || model?.id,
translatedContent: undefined
})
}
}, [assistantModel?.id, message.id, model?.id, onGetMessages])
const onEdit = useCallback(async () => { const onEdit = useCallback(async () => {
const editedText = await TextEditPopup.show({ text: message.content }) let resendMessage = false
const editedText = await TextEditPopup.show({
text: message.content,
children: (props) => (
<ReSendButton
icon={<i className="iconfont icon-ic_send" style={{ color: 'var(--color-primary)' }} />}
onClick={() => {
props.onOk?.()
resendMessage = true
}}>
{t('chat.resend')}
</ReSendButton>
)
})
editedText && onEditMessage?.({ ...message, content: editedText }) editedText && onEditMessage?.({ ...message, content: editedText })
}, [message, onEditMessage]) resendMessage && onResend()
}, [message, onEditMessage, onResend, t])
const handleTranslate = useCallback( const handleTranslate = useCallback(
async (language: string) => { async (language: string) => {
@ -287,4 +322,10 @@ const ActionButton = styled.div`
} }
` `
const ReSendButton = styled(Button)`
position: absolute;
top: 10px;
left: 0;
`
export default MessageMenubar export default MessageMenubar

View File

@ -20,5 +20,6 @@ export const EVENT_NAMES = {
NEW_BRANCH: 'NEW_BRANCH', NEW_BRANCH: 'NEW_BRANCH',
EXPORT_TOPIC_IMAGE: 'EXPORT_TOPIC_IMAGE', EXPORT_TOPIC_IMAGE: 'EXPORT_TOPIC_IMAGE',
LOCATE_MESSAGE: 'LOCATE_MESSAGE', LOCATE_MESSAGE: 'LOCATE_MESSAGE',
ADD_NEW_TOPIC: 'ADD_NEW_TOPIC' ADD_NEW_TOPIC: 'ADD_NEW_TOPIC',
RESEND_MESSAGE: 'RESEND_MESSAGE'
} }