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
This commit is contained in:
Teo 2025-07-01 14:52:52 +08:00 committed by GitHub
parent 4aa77d5a82
commit 0c3720123d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 42 additions and 5 deletions

View File

@ -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<Props> = ({ keywords, onClick, onSearch, ...props
const { assistants } = useAssistants()
const { t } = useTranslation()
const { handleScroll, containerRef } = useScrollPosition('TopicsHistory')
const [sortType, setSortType] = useState<SortType>('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<Props> = ({ keywords, onClick, onSearch, ...props
return (
<ListContainer {...props} ref={containerRef} onScroll={handleScroll}>
<Segmented
shape="round"
size="small"
value={sortType}
onChange={setSortType}
options={[
{ label: t('export.created'), value: 'createdAt' },
{ label: t('export.last_updated'), value: 'updatedAt' }
]}
/>
<ContainerWrapper>
{Object.entries(groupedTopics).map(([date, items]) => (
<ListItem key={date}>
@ -59,7 +73,7 @@ const TopicsHistory: React.FC<Props> = ({ keywords, onClick, onSearch, ...props
onClick(_topic)
}}>
<TopicName>{topic.name.substring(0, 50)}</TopicName>
<TopicDate>{dayjs(topic.updatedAt).format('HH:mm')}</TopicDate>
<TopicDate>{dayjs(topic[sortType]).format('HH:mm')}</TopicDate>
</TopicItem>
))}
</ListItem>
@ -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;
`

View File

@ -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,

View File

@ -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)