|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import { useCallback, useState, useRef } from 'react'; |
|
|
import { Toast, Modal } from '@douyinfe/semi-ui'; |
|
|
import { useTranslation } from 'react-i18next'; |
|
|
import { |
|
|
getTextContent, |
|
|
buildApiPayload, |
|
|
createLoadingAssistantMessage, |
|
|
} from '../../helpers'; |
|
|
import { MESSAGE_ROLES } from '../../constants/playground.constants'; |
|
|
|
|
|
export const useMessageEdit = ( |
|
|
setMessage, |
|
|
inputs, |
|
|
parameterEnabled, |
|
|
sendRequest, |
|
|
saveMessages, |
|
|
) => { |
|
|
const { t } = useTranslation(); |
|
|
const [editingMessageId, setEditingMessageId] = useState(null); |
|
|
const [editValue, setEditValue] = useState(''); |
|
|
const editingMessageRef = useRef(null); |
|
|
|
|
|
const handleMessageEdit = useCallback((targetMessage) => { |
|
|
const editableContent = getTextContent(targetMessage); |
|
|
setEditingMessageId(targetMessage.id); |
|
|
editingMessageRef.current = targetMessage; |
|
|
setEditValue(editableContent); |
|
|
}, []); |
|
|
|
|
|
const handleEditSave = useCallback(() => { |
|
|
if (!editingMessageId || !editValue.trim()) return; |
|
|
|
|
|
setMessage((prevMessages) => { |
|
|
let messageIndex = prevMessages.findIndex( |
|
|
(msg) => msg === editingMessageRef.current, |
|
|
); |
|
|
|
|
|
if (messageIndex === -1) { |
|
|
messageIndex = prevMessages.findIndex( |
|
|
(msg) => msg.id === editingMessageId, |
|
|
); |
|
|
} |
|
|
|
|
|
const targetMessage = prevMessages[messageIndex]; |
|
|
let newContent; |
|
|
|
|
|
if (Array.isArray(targetMessage.content)) { |
|
|
newContent = targetMessage.content.map((item) => |
|
|
item.type === 'text' ? { ...item, text: editValue.trim() } : item, |
|
|
); |
|
|
} else { |
|
|
newContent = editValue.trim(); |
|
|
} |
|
|
|
|
|
const updatedMessages = prevMessages.map((msg) => |
|
|
msg.id === editingMessageId ? { ...msg, content: newContent } : msg, |
|
|
); |
|
|
|
|
|
|
|
|
if (targetMessage.role === MESSAGE_ROLES.USER) { |
|
|
const hasSubsequentAssistantReply = |
|
|
messageIndex < prevMessages.length - 1 && |
|
|
prevMessages[messageIndex + 1].role === MESSAGE_ROLES.ASSISTANT; |
|
|
|
|
|
if (hasSubsequentAssistantReply) { |
|
|
Modal.confirm({ |
|
|
title: t('消息已编辑'), |
|
|
content: t('检测到该消息后有AI回复,是否删除后续回复并重新生成?'), |
|
|
okText: t('重新生成'), |
|
|
cancelText: t('仅保存'), |
|
|
onOk: () => { |
|
|
const messagesUntilUser = updatedMessages.slice( |
|
|
0, |
|
|
messageIndex + 1, |
|
|
); |
|
|
setMessage(messagesUntilUser); |
|
|
|
|
|
setTimeout(() => saveMessages(messagesUntilUser), 0); |
|
|
|
|
|
setTimeout(() => { |
|
|
const payload = buildApiPayload( |
|
|
messagesUntilUser, |
|
|
null, |
|
|
inputs, |
|
|
parameterEnabled, |
|
|
); |
|
|
setMessage((prevMsg) => [ |
|
|
...prevMsg, |
|
|
createLoadingAssistantMessage(), |
|
|
]); |
|
|
sendRequest(payload, inputs.stream); |
|
|
}, 100); |
|
|
}, |
|
|
onCancel: () => { |
|
|
setMessage(updatedMessages); |
|
|
|
|
|
setTimeout(() => saveMessages(updatedMessages), 0); |
|
|
}, |
|
|
}); |
|
|
return prevMessages; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
setTimeout(() => saveMessages(updatedMessages), 0); |
|
|
return updatedMessages; |
|
|
}); |
|
|
|
|
|
setEditingMessageId(null); |
|
|
editingMessageRef.current = null; |
|
|
setEditValue(''); |
|
|
Toast.success({ content: t('消息已更新'), duration: 2 }); |
|
|
}, [ |
|
|
editingMessageId, |
|
|
editValue, |
|
|
t, |
|
|
inputs, |
|
|
parameterEnabled, |
|
|
sendRequest, |
|
|
setMessage, |
|
|
saveMessages, |
|
|
]); |
|
|
|
|
|
const handleEditCancel = useCallback(() => { |
|
|
setEditingMessageId(null); |
|
|
editingMessageRef.current = null; |
|
|
setEditValue(''); |
|
|
}, []); |
|
|
|
|
|
return { |
|
|
editingMessageId, |
|
|
editValue, |
|
|
setEditValue, |
|
|
handleMessageEdit, |
|
|
handleEditSave, |
|
|
handleEditCancel, |
|
|
}; |
|
|
}; |
|
|
|