refactor: clean up MessageMenubar and enhance message handling
- Removed unused lodash dependency and optimized message resend logic in MessageMenubar. - Streamlined Popconfirm component for message deletion. - Updated NewTopicButton styling for improved layout. - Enhanced messages slice to ensure better state management and error handling.
This commit is contained in:
parent
cef32f4b36
commit
730b03cde8
@ -30,7 +30,6 @@ import {
|
|||||||
} from '@renderer/utils/export'
|
} from '@renderer/utils/export'
|
||||||
import { Button, Dropdown, Popconfirm, Tooltip } from 'antd'
|
import { Button, Dropdown, Popconfirm, Tooltip } from 'antd'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { isEmpty } from 'lodash'
|
|
||||||
import { FC, memo, useCallback, useMemo, useState } from 'react'
|
import { FC, memo, useCallback, useMemo, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
@ -55,7 +54,6 @@ const MessageMenubar: FC<Props> = (props) => {
|
|||||||
const [isTranslating, setIsTranslating] = useState(false)
|
const [isTranslating, setIsTranslating] = useState(false)
|
||||||
const assistantModel = assistant?.model
|
const assistantModel = assistant?.model
|
||||||
const {
|
const {
|
||||||
messages,
|
|
||||||
loading,
|
loading,
|
||||||
editMessage,
|
editMessage,
|
||||||
setStreamMessage,
|
setStreamMessage,
|
||||||
@ -87,18 +85,10 @@ const MessageMenubar: FC<Props> = (props) => {
|
|||||||
const handleResendUserMessage = useCallback(
|
const handleResendUserMessage = useCallback(
|
||||||
async (messageUpdate?: Message) => {
|
async (messageUpdate?: Message) => {
|
||||||
if (!loading) {
|
if (!loading) {
|
||||||
const groupdMessages = messages.filter((m) => m.askId === message.id)
|
|
||||||
|
|
||||||
// Resend all grouped messages
|
|
||||||
if (!isEmpty(groupdMessages)) {
|
|
||||||
await resendMessage(message, assistant)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await resendMessage(messageUpdate ?? message, assistant)
|
await resendMessage(messageUpdate ?? message, assistant)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[message, resendMessage, assistant, messages, loading]
|
[assistant, loading, message, resendMessage]
|
||||||
)
|
)
|
||||||
|
|
||||||
const onEdit = useCallback(async () => {
|
const onEdit = useCallback(async () => {
|
||||||
@ -331,19 +321,17 @@ const MessageMenubar: FC<Props> = (props) => {
|
|||||||
</ActionButton>
|
</ActionButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{!isGrouped && (
|
<Popconfirm
|
||||||
<Popconfirm
|
title={t('message.message.delete.content')}
|
||||||
title={t('message.message.delete.content')}
|
okButtonProps={{ danger: true }}
|
||||||
okButtonProps={{ danger: true }}
|
icon={<QuestionCircleOutlined style={{ color: 'red' }} />}
|
||||||
icon={<QuestionCircleOutlined style={{ color: 'red' }} />}
|
onConfirm={() => deleteMessage(message)}>
|
||||||
onConfirm={() => deleteMessage(message)}>
|
<ActionButton className="message-action-button" onClick={(e) => e.stopPropagation()}>
|
||||||
<ActionButton className="message-action-button" onClick={(e) => e.stopPropagation()}>
|
<Tooltip title={t('common.delete')} mouseEnterDelay={1}>
|
||||||
<Tooltip title={t('common.delete')} mouseEnterDelay={1}>
|
<DeleteOutlined />
|
||||||
<DeleteOutlined />
|
</Tooltip>
|
||||||
</Tooltip>
|
</ActionButton>
|
||||||
</ActionButton>
|
</Popconfirm>
|
||||||
</Popconfirm>
|
|
||||||
)}
|
|
||||||
{!isUserMessage && (
|
{!isUserMessage && (
|
||||||
<Dropdown
|
<Dropdown
|
||||||
menu={{ items: dropdownItems, onClick: (e) => e.domEvent.stopPropagation() }}
|
menu={{ items: dropdownItems, onClick: (e) => e.domEvent.stopPropagation() }}
|
||||||
|
|||||||
@ -29,6 +29,8 @@ const Container = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-top: -10px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const Button = styled(AntdButton)<{ $theme: ThemeMode }>`
|
const Button = styled(AntdButton)<{ $theme: ThemeMode }>`
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import type { AppDispatch, RootState } from '@renderer/store'
|
|||||||
import type { Assistant, Message, Topic } from '@renderer/types'
|
import type { Assistant, Message, Topic } from '@renderer/types'
|
||||||
import { Model } from '@renderer/types'
|
import { Model } from '@renderer/types'
|
||||||
import { clearTopicQueue, getTopicQueue, waitForTopicQueue } from '@renderer/utils/queue'
|
import { clearTopicQueue, getTopicQueue, waitForTopicQueue } from '@renderer/utils/queue'
|
||||||
import { cloneDeep, throttle } from 'lodash'
|
import { cloneDeep, isEmpty, throttle } from 'lodash'
|
||||||
|
|
||||||
export interface MessagesState {
|
export interface MessagesState {
|
||||||
messagesByTopic: Record<string, Message[]>
|
messagesByTopic: Record<string, Message[]>
|
||||||
@ -95,9 +95,11 @@ const messagesSlice = createSlice({
|
|||||||
},
|
},
|
||||||
addMessage: (state, action: PayloadAction<{ topicId: string; messages: Message | Message[] }>) => {
|
addMessage: (state, action: PayloadAction<{ topicId: string; messages: Message | Message[] }>) => {
|
||||||
const { topicId, messages } = action.payload
|
const { topicId, messages } = action.payload
|
||||||
|
|
||||||
if (!state.messagesByTopic[topicId]) {
|
if (!state.messagesByTopic[topicId]) {
|
||||||
state.messagesByTopic[topicId] = []
|
state.messagesByTopic[topicId] = []
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(messages)) {
|
if (Array.isArray(messages)) {
|
||||||
// 为了兼容多模型新发消息,一次性添加多个助手消息
|
// 为了兼容多模型新发消息,一次性添加多个助手消息
|
||||||
// 不是什么好主意,不符合语义
|
// 不是什么好主意,不符合语义
|
||||||
@ -112,6 +114,7 @@ const messagesSlice = createSlice({
|
|||||||
action: PayloadAction<{ topicId: string; messages: Message | Message[]; position?: number }>
|
action: PayloadAction<{ topicId: string; messages: Message | Message[]; position?: number }>
|
||||||
) => {
|
) => {
|
||||||
const { topicId, messages, position } = action.payload
|
const { topicId, messages, position } = action.payload
|
||||||
|
|
||||||
if (!state.messagesByTopic[topicId]) {
|
if (!state.messagesByTopic[topicId]) {
|
||||||
state.messagesByTopic[topicId] = []
|
state.messagesByTopic[topicId] = []
|
||||||
}
|
}
|
||||||
@ -136,6 +139,7 @@ const messagesSlice = createSlice({
|
|||||||
) => {
|
) => {
|
||||||
const { topicId, messageId, updates } = action.payload
|
const { topicId, messageId, updates } = action.payload
|
||||||
const topicMessages = state.messagesByTopic[topicId]
|
const topicMessages = state.messagesByTopic[topicId]
|
||||||
|
|
||||||
if (topicMessages) {
|
if (topicMessages) {
|
||||||
const message = topicMessages.find((msg) => msg.id === messageId)
|
const message = topicMessages.find((msg) => msg.id === messageId)
|
||||||
if (message) {
|
if (message) {
|
||||||
@ -160,9 +164,11 @@ const messagesSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setStreamMessage: (state, action: PayloadAction<{ topicId: string; message: Message | null }>) => {
|
setStreamMessage: (state, action: PayloadAction<{ topicId: string; message: Message | null }>) => {
|
||||||
const { topicId, message } = action.payload
|
const { topicId, message } = action.payload
|
||||||
|
|
||||||
if (!state.streamMessagesByTopic[topicId]) {
|
if (!state.streamMessagesByTopic[topicId]) {
|
||||||
state.streamMessagesByTopic[topicId] = {}
|
state.streamMessagesByTopic[topicId] = {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message) {
|
if (message) {
|
||||||
state.streamMessagesByTopic[topicId][message.id] = message
|
state.streamMessagesByTopic[topicId][message.id] = message
|
||||||
}
|
}
|
||||||
@ -193,11 +199,13 @@ const messagesSlice = createSlice({
|
|||||||
// 添加新消息
|
// 添加新消息
|
||||||
state.messagesByTopic[topicId].push(streamMessage)
|
state.messagesByTopic[topicId].push(streamMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除流状态
|
// 删除流状态
|
||||||
delete state.streamMessagesByTopic[topicId][messageId]
|
delete state.streamMessagesByTopic[topicId][messageId]
|
||||||
},
|
},
|
||||||
clearStreamMessage: (state, action: PayloadAction<{ topicId: string; messageId: string }>) => {
|
clearStreamMessage: (state, action: PayloadAction<{ topicId: string; messageId: string }>) => {
|
||||||
const { topicId, messageId } = action.payload
|
const { topicId, messageId } = action.payload
|
||||||
|
|
||||||
if (state.streamMessagesByTopic[topicId]) {
|
if (state.streamMessagesByTopic[topicId]) {
|
||||||
delete state.streamMessagesByTopic[topicId][messageId]
|
delete state.streamMessagesByTopic[topicId][messageId]
|
||||||
}
|
}
|
||||||
@ -231,6 +239,7 @@ const handleResponseMessageUpdate = (
|
|||||||
if (message.status === 'success') {
|
if (message.status === 'success') {
|
||||||
autoRenameTopic(assistant, topicId)
|
autoRenameTopic(assistant, topicId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.status !== 'sending') {
|
if (message.status !== 'sending') {
|
||||||
dispatch(commitStreamMessage({ topicId, messageId: message.id }))
|
dispatch(commitStreamMessage({ topicId, messageId: message.id }))
|
||||||
const state = getState()
|
const state = getState()
|
||||||
@ -246,9 +255,7 @@ const handleResponseMessageUpdate = (
|
|||||||
const syncMessagesWithDB = async (topicId: string, messages: Message[]) => {
|
const syncMessagesWithDB = async (topicId: string, messages: Message[]) => {
|
||||||
const topic = await db.topics.get(topicId)
|
const topic = await db.topics.get(topicId)
|
||||||
if (topic) {
|
if (topic) {
|
||||||
await db.topics.update(topicId, {
|
await db.topics.update(topicId, { messages })
|
||||||
messages
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
await db.topics.add({ id: topicId, messages })
|
await db.topics.add({ id: topicId, messages })
|
||||||
}
|
}
|
||||||
@ -281,12 +288,13 @@ export const sendMessage =
|
|||||||
|
|
||||||
// 处理助手消息
|
// 处理助手消息
|
||||||
let assistantMessages: Message[] = []
|
let assistantMessages: Message[] = []
|
||||||
if (options?.resendAssistantMessage) {
|
if (!isEmpty(options?.resendAssistantMessage)) {
|
||||||
// 直接使用传入的助手消息,进行重置
|
// 直接使用传入的助手消息,进行重置
|
||||||
const messageToReset = options.resendAssistantMessage
|
const messageToReset = options.resendAssistantMessage
|
||||||
if (Array.isArray(messageToReset)) {
|
if (Array.isArray(messageToReset)) {
|
||||||
assistantMessages = messageToReset.map((m) => {
|
assistantMessages = messageToReset.map((m) => {
|
||||||
const resetMessage = resetAssistantMessage(m, assistant.model)
|
const isGroupedMessage = messageToReset.length > 1
|
||||||
|
const resetMessage = resetAssistantMessage(m, isGroupedMessage ? m.model : assistant.model)
|
||||||
// 更新状态
|
// 更新状态
|
||||||
dispatch(updateMessage({ topicId: topic.id, messageId: m.id, updates: resetMessage }))
|
dispatch(updateMessage({ topicId: topic.id, messageId: m.id, updates: resetMessage }))
|
||||||
// 使用重置后的消息
|
// 使用重置后的消息
|
||||||
@ -324,18 +332,27 @@ export const sendMessage =
|
|||||||
// 最后一个具有相同askId的助手消息,在其后插入
|
// 最后一个具有相同askId的助手消息,在其后插入
|
||||||
let position: number | undefined
|
let position: number | undefined
|
||||||
if (options?.isMentionModel) {
|
if (options?.isMentionModel) {
|
||||||
|
// 寻找用户提问对应的助手回答消息位置
|
||||||
const lastAssistantIndex = currentMessages.findLastIndex(
|
const lastAssistantIndex = currentMessages.findLastIndex(
|
||||||
(m) => m.role === 'assistant' && m.askId === userMessage.id
|
(m) => m.role === 'assistant' && m.askId === userMessage.id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 如果找到了助手消息,在助手消息后插入
|
||||||
if (lastAssistantIndex !== -1) {
|
if (lastAssistantIndex !== -1) {
|
||||||
position = lastAssistantIndex + 1
|
position = lastAssistantIndex + 1
|
||||||
|
} else {
|
||||||
|
// 如果找不到助手消息,则在用户消息后插入
|
||||||
|
const userMessageIndex = currentMessages.findIndex((m) => m.role === 'user' && m.id === userMessage.id)
|
||||||
|
if (userMessageIndex !== -1) {
|
||||||
|
position = userMessageIndex + 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
appendMessage({
|
appendMessage({
|
||||||
topicId: topic.id,
|
topicId: topic.id,
|
||||||
messages: !options?.isMentionModel ? [userMessage, ...assistantMessages] : assistantMessages,
|
messages: options?.isMentionModel ? assistantMessages : [userMessage, ...assistantMessages],
|
||||||
position
|
position
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -393,15 +410,10 @@ export const sendMessage =
|
|||||||
return messagesUpToUser.filter((m) => !m.status?.includes('ing'))
|
return messagesUpToUser.filter((m) => !m.status?.includes('ing'))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果找不到对应的用户消息,使用原有逻辑
|
|
||||||
// 按理说不会找不到 先注释掉看看
|
|
||||||
// if (messageIndex !== -1) {
|
|
||||||
// const messagesUpToAssistant = messages.slice(0, messageIndex)
|
|
||||||
// return messagesUpToAssistant.filter((m) => !m.status?.includes('ing'))
|
|
||||||
// }
|
|
||||||
// 没有找到消息索引的情况,过滤所有消息
|
// 没有找到消息索引的情况,过滤所有消息
|
||||||
return messages.filter((m) => !m.status?.includes('ing'))
|
return messages.filter((m) => !m.status?.includes('ing'))
|
||||||
}
|
}
|
||||||
|
|
||||||
await fetchChatCompletion({
|
await fetchChatCompletion({
|
||||||
message: { ...assistantMessage },
|
message: { ...assistantMessage },
|
||||||
messages: handleMessages(),
|
messages: handleMessages(),
|
||||||
@ -461,12 +473,14 @@ export const resendMessage =
|
|||||||
if (message.role === 'user') {
|
if (message.role === 'user') {
|
||||||
// 查找此用户消息对应的助手消息
|
// 查找此用户消息对应的助手消息
|
||||||
const assistantMessage = topicMessages.filter((m) => m.role === 'assistant' && m.askId === message.id)
|
const assistantMessage = topicMessages.filter((m) => m.role === 'assistant' && m.askId === message.id)
|
||||||
|
|
||||||
return dispatch(
|
return dispatch(
|
||||||
sendMessage(message, assistant, topic, {
|
sendMessage(message, assistant, topic, {
|
||||||
resendAssistantMessage: assistantMessage,
|
resendAssistantMessage: assistantMessage,
|
||||||
// 用户可能把助手消息删了,然后重新发送用户消息
|
// 用户可能把助手消息删了,然后重新发送用户消息
|
||||||
// 如果isMentionModel为false,则只会发送add助手消息
|
// 如果 isMentionModel 为 false, 则只会发送 add 助手消息
|
||||||
isMentionModel: !assistantMessage
|
isMentionModel: isEmpty(assistantMessage),
|
||||||
|
mentions: message.mentions
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user