From 0c3720123d11303311a6b795248233f9319a78e0 Mon Sep 17 00:00:00 2001 From: Teo Date: Tue, 1 Jul 2025 14:52:52 +0800 Subject: [PATCH] feat(TopicsHistory): add sorting functionality for topics and update UI components (#7673) * feat(TopicsHistory): add sorting functionality for topics and update UI components * refactor(assistants): remove console log from updateTopicUpdatedAt function * refactor(TopicsHistory): update topic date display to use dynamic sorting type --- .../history/components/TopicsHistory.tsx | 24 +++++++++++++++---- src/renderer/src/store/assistants.ts | 11 +++++++++ src/renderer/src/store/thunk/messageThunk.ts | 12 ++++++++++ 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/src/renderer/src/pages/history/components/TopicsHistory.tsx b/src/renderer/src/pages/history/components/TopicsHistory.tsx index d95a3f7ae6..2051d536bf 100644 --- a/src/renderer/src/pages/history/components/TopicsHistory.tsx +++ b/src/renderer/src/pages/history/components/TopicsHistory.tsx @@ -4,12 +4,15 @@ import { useAssistants } from '@renderer/hooks/useAssistant' import useScrollPosition from '@renderer/hooks/useScrollPosition' import { getTopicById } from '@renderer/hooks/useTopic' import { Topic } from '@renderer/types' -import { Button, Divider, Empty } from 'antd' +import { Button, Divider, Empty, Segmented } from 'antd' import dayjs from 'dayjs' import { groupBy, isEmpty, orderBy } from 'lodash' +import { useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' +type SortType = 'createdAt' | 'updatedAt' + type Props = { keywords: string onClick: (topic: Topic) => void @@ -20,15 +23,16 @@ const TopicsHistory: React.FC = ({ keywords, onClick, onSearch, ...props const { assistants } = useAssistants() const { t } = useTranslation() const { handleScroll, containerRef } = useScrollPosition('TopicsHistory') + const [sortType, setSortType] = useState('createdAt') - const topics = orderBy(assistants.map((assistant) => assistant.topics).flat(), 'createdAt', 'desc') + const topics = orderBy(assistants.map((assistant) => assistant.topics).flat(), sortType, 'desc') const filteredTopics = topics.filter((topic) => { return topic.name.toLowerCase().includes(keywords.toLowerCase()) }) const groupedTopics = groupBy(filteredTopics, (topic) => { - return dayjs(topic.createdAt).format('MM/DD') + return dayjs(topic[sortType]).format('MM/DD') }) if (isEmpty(filteredTopics)) { @@ -46,6 +50,16 @@ const TopicsHistory: React.FC = ({ keywords, onClick, onSearch, ...props return ( + {Object.entries(groupedTopics).map(([date, items]) => ( @@ -59,7 +73,7 @@ const TopicsHistory: React.FC = ({ keywords, onClick, onSearch, ...props onClick(_topic) }}> {topic.name.substring(0, 50)} - {dayjs(topic.updatedAt).format('HH:mm')} + {dayjs(topic[sortType]).format('HH:mm')} ))} @@ -91,7 +105,7 @@ const ListContainer = styled.div` overflow-y: scroll; width: 100%; align-items: center; - padding-top: 20px; + padding-top: 10px; padding-bottom: 20px; ` diff --git a/src/renderer/src/store/assistants.ts b/src/renderer/src/store/assistants.ts index 35ea98af76..70dbfa9841 100644 --- a/src/renderer/src/store/assistants.ts +++ b/src/renderer/src/store/assistants.ts @@ -143,6 +143,16 @@ const assistantsSlice = createSlice({ return assistant }) }, + updateTopicUpdatedAt: (state, action: PayloadAction<{ topicId: string }>) => { + outer: for (const assistant of state.assistants) { + for (const topic of assistant.topics) { + if (topic.id === action.payload.topicId) { + topic.updatedAt = new Date().toISOString() + break outer + } + } + } + }, setModel: (state, action: PayloadAction<{ assistantId: string; model: Model }>) => { state.assistants = state.assistants.map((assistant) => assistant.id === action.payload.assistantId @@ -167,6 +177,7 @@ export const { updateTopic, updateTopics, removeAllTopics, + updateTopicUpdatedAt, setModel, setTagsOrder, updateAssistantSettings, diff --git a/src/renderer/src/store/thunk/messageThunk.ts b/src/renderer/src/store/thunk/messageThunk.ts index ee0d2685a3..8f1e4f38b0 100644 --- a/src/renderer/src/store/thunk/messageThunk.ts +++ b/src/renderer/src/store/thunk/messageThunk.ts @@ -7,6 +7,7 @@ import { NotificationService } from '@renderer/services/NotificationService' import { createStreamProcessor, type StreamProcessorCallbacks } from '@renderer/services/StreamProcessingService' import { estimateMessagesUsage } from '@renderer/services/TokenService' import store from '@renderer/store' +import { updateTopicUpdatedAt } from '@renderer/store/assistants' import type { Assistant, ExternalToolResult, FileType, MCPToolResponse, Model, Topic } from '@renderer/types' import type { CitationMessageBlock, @@ -68,6 +69,7 @@ export const saveMessageAndBlocksToDB = async (message: Message, blocks: Message } } await db.topics.update(message.topicId, { messages: updatedMessages }) + store.dispatch(updateTopicUpdatedAt({ topicId: message.topicId })) } else { console.error(`[saveMessageAndBlocksToDB] Topic ${message.topicId} not found.`) } @@ -107,6 +109,8 @@ const updateExistingMessageAndBlocksInDB = async ( }) } }) + + store.dispatch(updateTopicUpdatedAt({ topicId: updatedMessage.topicId })) } }) } catch (error) { @@ -862,6 +866,7 @@ export const sendMessage = if (userMessageBlocks.length > 0) { dispatch(upsertManyBlocks(userMessageBlocks)) } + dispatch(updateTopicUpdatedAt({ topicId })) const mentionedModels = userMessage.mentions const queue = getTopicQueue(topicId) @@ -954,6 +959,7 @@ export const deleteSingleMessageThunk = if (topic) { const finalMessagesToSave = selectMessagesForTopic(getState(), topicId) await db.topics.update(topicId, { messages: finalMessagesToSave }) + dispatch(updateTopicUpdatedAt({ topicId })) } } catch (error) { console.error(`[deleteSingleMessage] Failed to delete message ${messageId}:`, error) @@ -997,6 +1003,7 @@ export const deleteMessageGroupThunk = if (topic) { const finalMessagesToSave = selectMessagesForTopic(getState(), topicId) await db.topics.update(topicId, { messages: finalMessagesToSave }) + dispatch(updateTopicUpdatedAt({ topicId })) } } catch (error) { console.error(`[deleteMessageGroup] Failed to delete messages with askId ${askId}:`, error) @@ -1024,6 +1031,7 @@ export const clearTopicMessagesThunk = cleanupMultipleBlocks(dispatch, blockIdsToDelete) await db.topics.update(topicId, { messages: [] }) + dispatch(updateTopicUpdatedAt({ topicId })) if (blockIdsToDelete.length > 0) { await db.message_blocks.bulkDelete(blockIdsToDelete) } @@ -1623,6 +1631,8 @@ export const updateMessageAndBlocksThunk = await db.message_blocks.bulkPut(blockUpdatesList) } }) + + dispatch(updateTopicUpdatedAt({ topicId })) } catch (error) { console.error(`[updateMessageAndBlocksThunk] Failed to process updates for message ${messageId}:`, error) } @@ -1665,6 +1675,8 @@ export const removeBlocksThunk = } }) + dispatch(updateTopicUpdatedAt({ topicId })) + return } catch (error) { console.error(`[removeBlocksFromMessageThunk] Failed to remove blocks from message ${messageId}:`, error)