feat: add functionality to insert messages at a specific index in the… (#6299)

* feat: add functionality to insert messages at a specific index in the Redux store

- Introduced a new interface for inserting messages at a specified index.
- Implemented the insertMessageAtIndex reducer to handle message insertion.
- Updated saveMessageAndBlocksToDB to support message insertion logic.
- Modified appendAssistantResponseThunk to utilize the new insertion functionality.

* feat: integrate multi-select mode handling in MessageGroup component

- Added useChatContext hook to access multi-select mode state.
- Updated isGrouped logic to account for multi-select mode, ensuring proper message grouping behavior.
- Enhanced MessageWrapper styles for better layout management in different modes.

---------

Co-authored-by: kangfenmao <kangfenmao@qq.com>
This commit is contained in:
MyPrototypeWhat 2025-05-22 14:57:54 +08:00 committed by GitHub
parent c000203ac1
commit 46ca79ef5c
3 changed files with 52 additions and 8 deletions

View File

@ -1,5 +1,6 @@
import Scrollbar from '@renderer/components/Scrollbar'
import { MessageEditingProvider } from '@renderer/context/MessageEditingContext'
import { useChatContext } from '@renderer/hooks/useChatContext'
import { useMessageOperations } from '@renderer/hooks/useMessageOperations'
import { useSettings } from '@renderer/hooks/useSettings'
import { MultiModelMessageStyle } from '@renderer/store/settings'
@ -24,6 +25,7 @@ interface Props {
const MessageGroup = ({ messages, topic, hidePresetMessages, registerMessageElement }: Props) => {
const { editMessage } = useMessageOperations(topic)
const { multiModelMessageStyle: multiModelMessageStyleSetting, gridColumns, gridPopoverTrigger } = useSettings()
const { isMultiSelectMode } = useChatContext(topic)
const [multiModelMessageStyle, setMultiModelMessageStyle] = useState<MultiModelMessageStyle>(
messages[0].multiModelMessageStyle || multiModelMessageStyleSetting
@ -59,7 +61,7 @@ const MessageGroup = ({ messages, topic, hidePresetMessages, registerMessageElem
[editMessage, selectedMessageId]
)
const isGrouped = messageLength > 1 && messages.every((m) => m.role === 'assistant')
const isGrouped = isMultiSelectMode ? false : messageLength > 1 && messages.every((m) => m.role === 'assistant')
const isHorizontal = multiModelMessageStyle === 'horizontal'
const isGrid = multiModelMessageStyle === 'grid'
@ -298,6 +300,19 @@ interface MessageWrapperProps {
const MessageWrapper = styled(Scrollbar)<MessageWrapperProps>`
width: 100%;
&.horizontal {
display: inline-block;
}
&.grid {
display: inline-block;
}
&.fold {
display: none;
&.selected {
display: inline-block;
}
}
${({ $layout, $isGrouped }) => {
if ($layout === 'horizontal' && $isGrouped) {
return css`

View File

@ -59,6 +59,13 @@ interface RemoveMessagesPayload {
messageIds: string[]
}
// Payload for inserting a message at a specific index
interface InsertMessageAtIndexPayload {
topicId: string
message: Message
index: number
}
// 4. Create the Slice with Refactored Reducers
const messagesSlice = createSlice({
name: 'newMessages',
@ -95,6 +102,20 @@ const messagesSlice = createSlice({
state.loadingByTopic[topicId] = false
}
},
insertMessageAtIndex(state, action: PayloadAction<InsertMessageAtIndexPayload>) {
const { topicId, message, index } = action.payload
messagesAdapter.addOne(state, message) // Add message to entities
if (!state.messageIdsByTopic[topicId]) {
state.messageIdsByTopic[topicId] = []
}
// Ensure index is within bounds
const safeIndex = Math.max(0, Math.min(index, state.messageIdsByTopic[topicId].length))
state.messageIdsByTopic[topicId].splice(safeIndex, 0, message.id) // Insert ID at specified index
if (!(topicId in state.loadingByTopic)) {
state.loadingByTopic[topicId] = false
}
},
updateMessage(
state,
action: PayloadAction<{

View File

@ -49,20 +49,24 @@ const handleChangeLoadingOfTopic = async (topicId: string) => {
store.dispatch(newMessagesActions.setTopicLoading({ topicId, loading: false }))
}
// TODO: 后续可以将db操作移到Listener Middleware中
export const saveMessageAndBlocksToDB = async (message: Message, blocks: MessageBlock[]) => {
export const saveMessageAndBlocksToDB = async (message: Message, blocks: MessageBlock[], messageIndex: number = -1) => {
try {
if (blocks.length > 0) {
await db.message_blocks.bulkPut(blocks)
}
const topic = await db.topics.get(message.topicId)
if (topic) {
const messageIndex = topic.messages.findIndex((m) => m.id === message.id)
const _messageIndex = topic.messages.findIndex((m) => m.id === message.id)
const updatedMessages = [...topic.messages]
if (messageIndex !== -1) {
updatedMessages[messageIndex] = message
if (_messageIndex !== -1) {
updatedMessages[_messageIndex] = message
} else {
updatedMessages.push(message)
if (messageIndex !== -1) {
updatedMessages.splice(messageIndex, 0, message)
} else {
updatedMessages.push(message)
}
}
await db.topics.update(message.topicId, { messages: updatedMessages })
} else {
@ -1244,10 +1248,14 @@ export const appendAssistantResponseThunk =
})
// 3. Update Redux Store
dispatch(newMessagesActions.addMessage({ topicId, message: newAssistantStub }))
const currentTopicMessageIds = getState().messages.messageIdsByTopic[topicId] || []
const existingMessageIndex = currentTopicMessageIds.findIndex((id) => id === existingAssistantMessageId)
const insertAtIndex = existingMessageIndex !== -1 ? existingMessageIndex + 1 : currentTopicMessageIds.length
dispatch(newMessagesActions.insertMessageAtIndex({ topicId, message: newAssistantStub, index: insertAtIndex }))
// 4. Update Database (Save the stub to the topic's message list)
await saveMessageAndBlocksToDB(newAssistantStub, [])
await saveMessageAndBlocksToDB(newAssistantStub, [], insertAtIndex)
// 5. Prepare and queue the processing task
const assistantConfigForThisCall = {