mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-03 11:19:10 +08:00
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:
parent
4aa77d5a82
commit
0c3720123d
@ -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;
|
||||
`
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user