- Modify saveMessagesImmediately to accept messages parameter - Pass updated message list to all save calls instead of relying on closure - Ensure complete message history is saved including the last message - Fix timing issue where old message state was being saved This fixes the issue where the last conversation was not being persisted to localStorage.
209 lines
6.4 KiB
JavaScript
209 lines
6.4 KiB
JavaScript
import { useCallback } from 'react';
|
|
import { Toast, Modal } from '@douyinfe/semi-ui';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { getTextContent } from '../utils/messageUtils';
|
|
import { ERROR_MESSAGES } from '../utils/constants';
|
|
|
|
export const useMessageActions = (message, setMessage, onMessageSend, saveMessages) => {
|
|
const { t } = useTranslation();
|
|
|
|
// 复制消息
|
|
const handleMessageCopy = useCallback((targetMessage) => {
|
|
const textToCopy = getTextContent(targetMessage);
|
|
|
|
if (!textToCopy) {
|
|
Toast.warning({
|
|
content: t(ERROR_MESSAGES.NO_TEXT_CONTENT),
|
|
duration: 2,
|
|
});
|
|
return;
|
|
}
|
|
|
|
const copyToClipboard = async (text) => {
|
|
if (navigator.clipboard?.writeText) {
|
|
try {
|
|
await navigator.clipboard.writeText(text);
|
|
Toast.success({
|
|
content: t('消息已复制到剪贴板'),
|
|
duration: 2,
|
|
});
|
|
} catch (err) {
|
|
console.error('Clipboard API 复制失败:', err);
|
|
fallbackCopy(text);
|
|
}
|
|
} else {
|
|
fallbackCopy(text);
|
|
}
|
|
};
|
|
|
|
const fallbackCopy = (text) => {
|
|
try {
|
|
const textArea = document.createElement('textarea');
|
|
textArea.value = text;
|
|
textArea.style.cssText = `
|
|
position: fixed;
|
|
top: -9999px;
|
|
left: -9999px;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
z-index: -1;
|
|
`;
|
|
textArea.setAttribute('readonly', '');
|
|
|
|
document.body.appendChild(textArea);
|
|
textArea.select();
|
|
textArea.setSelectionRange(0, text.length);
|
|
|
|
const successful = document.execCommand('copy');
|
|
document.body.removeChild(textArea);
|
|
|
|
if (successful) {
|
|
Toast.success({
|
|
content: t('消息已复制到剪贴板'),
|
|
duration: 2,
|
|
});
|
|
} else {
|
|
throw new Error('execCommand copy failed');
|
|
}
|
|
} catch (err) {
|
|
console.error('回退复制方案也失败:', err);
|
|
|
|
let errorMessage = t(ERROR_MESSAGES.COPY_FAILED);
|
|
if (window.location.protocol === 'http:' && window.location.hostname !== 'localhost') {
|
|
errorMessage = t(ERROR_MESSAGES.COPY_HTTPS_REQUIRED);
|
|
} else if (!navigator.clipboard && !document.execCommand) {
|
|
errorMessage = t(ERROR_MESSAGES.BROWSER_NOT_SUPPORTED);
|
|
}
|
|
|
|
Toast.error({
|
|
content: errorMessage,
|
|
duration: 4,
|
|
});
|
|
}
|
|
};
|
|
|
|
copyToClipboard(textToCopy);
|
|
}, [t]);
|
|
|
|
// 重新生成消息
|
|
const handleMessageReset = useCallback((targetMessage) => {
|
|
setMessage(prevMessages => {
|
|
const messageIndex = prevMessages.findIndex(msg => msg.id === targetMessage.id);
|
|
if (messageIndex === -1) return prevMessages;
|
|
|
|
if (targetMessage.role === 'user') {
|
|
const newMessages = prevMessages.slice(0, messageIndex);
|
|
const contentToSend = getTextContent(targetMessage);
|
|
|
|
setTimeout(() => {
|
|
onMessageSend(contentToSend);
|
|
}, 100);
|
|
|
|
return newMessages;
|
|
} else if (targetMessage.role === 'assistant') {
|
|
let userMessageIndex = messageIndex - 1;
|
|
while (userMessageIndex >= 0 && prevMessages[userMessageIndex].role !== 'user') {
|
|
userMessageIndex--;
|
|
}
|
|
|
|
if (userMessageIndex >= 0) {
|
|
const userMessage = prevMessages[userMessageIndex];
|
|
const newMessages = prevMessages.slice(0, userMessageIndex);
|
|
const contentToSend = getTextContent(userMessage);
|
|
|
|
setTimeout(() => {
|
|
onMessageSend(contentToSend);
|
|
}, 100);
|
|
|
|
return newMessages;
|
|
}
|
|
}
|
|
|
|
return prevMessages;
|
|
});
|
|
}, [setMessage, onMessageSend]);
|
|
|
|
// 删除消息
|
|
const handleMessageDelete = useCallback((targetMessage) => {
|
|
Modal.confirm({
|
|
title: t('确认删除'),
|
|
content: t('确定要删除这条消息吗?'),
|
|
okText: t('确定'),
|
|
cancelText: t('取消'),
|
|
okButtonProps: {
|
|
type: 'danger',
|
|
},
|
|
onOk: () => {
|
|
setMessage(prevMessages => {
|
|
const messageIndex = prevMessages.findIndex(msg => msg.id === targetMessage.id);
|
|
if (messageIndex === -1) return prevMessages;
|
|
|
|
let updatedMessages;
|
|
if (targetMessage.role === 'user' && messageIndex < prevMessages.length - 1) {
|
|
const nextMessage = prevMessages[messageIndex + 1];
|
|
if (nextMessage.role === 'assistant') {
|
|
Toast.success({
|
|
content: t('已删除消息及其回复'),
|
|
duration: 2,
|
|
});
|
|
updatedMessages = prevMessages.filter((_, index) =>
|
|
index !== messageIndex && index !== messageIndex + 1
|
|
);
|
|
} else {
|
|
Toast.success({
|
|
content: t('消息已删除'),
|
|
duration: 2,
|
|
});
|
|
updatedMessages = prevMessages.filter(msg => msg.id !== targetMessage.id);
|
|
}
|
|
} else {
|
|
Toast.success({
|
|
content: t('消息已删除'),
|
|
duration: 2,
|
|
});
|
|
updatedMessages = prevMessages.filter(msg => msg.id !== targetMessage.id);
|
|
}
|
|
|
|
// 删除消息后保存,传入更新后的消息列表
|
|
setTimeout(() => saveMessages(updatedMessages), 0);
|
|
return updatedMessages;
|
|
});
|
|
},
|
|
});
|
|
}, [setMessage, t, saveMessages]);
|
|
|
|
// 切换角色
|
|
const handleRoleToggle = useCallback((targetMessage) => {
|
|
if (!(targetMessage.role === 'assistant' || targetMessage.role === 'system')) {
|
|
return;
|
|
}
|
|
|
|
const newRole = targetMessage.role === 'assistant' ? 'system' : 'assistant';
|
|
|
|
setMessage(prevMessages => {
|
|
const updatedMessages = prevMessages.map(msg => {
|
|
if (msg.id === targetMessage.id &&
|
|
(msg.role === 'assistant' || msg.role === 'system')) {
|
|
return { ...msg, role: newRole };
|
|
}
|
|
return msg;
|
|
});
|
|
|
|
// 切换角色后保存,传入更新后的消息列表
|
|
setTimeout(() => saveMessages(updatedMessages), 0);
|
|
return updatedMessages;
|
|
});
|
|
|
|
Toast.success({
|
|
content: t(`已切换为${newRole === 'system' ? 'System' : 'Assistant'}角色`),
|
|
duration: 2,
|
|
});
|
|
}, [setMessage, t, saveMessages]);
|
|
|
|
return {
|
|
handleMessageCopy,
|
|
handleMessageReset,
|
|
handleMessageDelete,
|
|
handleRoleToggle,
|
|
};
|
|
};
|