feat: Add year to topic timestamp and improve unpin UX (#12408)

* Initial plan

* feat: Add year to topic time display format

Co-authored-by: GeorgeDong32 <98630204+GeorgeDong32@users.noreply.github.com>

* fix: improve topic unpin UX by moving to top of unpinned list

- Unpinned topics now move to the top of the unpinned section
- List automatically scrolls to the unpinned topic position
- Keeps the requirement to unpin before deleting pinned topics

Closes #12398

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: GeorgeDong32 <98630204+GeorgeDong32@users.noreply.github.com>
Co-authored-by: George·Dong <GeorgeDong32@qq.com>
This commit is contained in:
Copilot 2026-01-10 21:37:37 +08:00 committed by GitHub
parent e5a2980da8
commit 622e3f0db6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -1,4 +1,5 @@
import AssistantAvatar from '@renderer/components/Avatar/AssistantAvatar'
import type { DraggableVirtualListRef } from '@renderer/components/DraggableList'
import { DraggableVirtualList } from '@renderer/components/DraggableList'
import { CopyIcon, DeleteIcon, EditIcon } from '@renderer/components/Icons'
import ObsidianExportPopup from '@renderer/components/Popups/ObsidianExportPopup'
@ -85,6 +86,7 @@ export const Topics: React.FC<Props> = ({ assistant: _assistant, activeTopic, se
const [deletingTopicId, setDeletingTopicId] = useState<string | null>(null)
const deleteTimerRef = useRef<NodeJS.Timeout>(null)
const [editingTopicId, setEditingTopicId] = useState<string | null>(null)
const listRef = useRef<DraggableVirtualListRef>(null)
// 管理模式状态
const manageState = useTopicManageMode()
@ -168,10 +170,46 @@ export const Topics: React.FC<Props> = ({ assistant: _assistant, activeTopic, se
const onPinTopic = useCallback(
(topic: Topic) => {
let newIndex = 0
if (topic.pinned) {
// 取消固定:将话题移到未固定话题的顶部
const pinnedTopics = assistant.topics.filter((t) => t.pinned)
const unpinnedTopics = assistant.topics.filter((t) => !t.pinned)
// 构建新顺序:其他固定话题 + 取消固定的话题(移到顶部) + 其他未固定话题
const reorderedTopics = [
...pinnedTopics.filter((t) => t.id !== topic.id), // 其他固定话题
topic, // 取消固定的话题移到顶部
...unpinnedTopics // 其他未固定话题
]
newIndex = pinnedTopics.length - 1 // 最后一个固定话题的索引 + 1 = 第一个未固定的索引
updateTopics(reorderedTopics)
} else {
// 固定话题:移到固定区域顶部
const pinnedTopics = assistant.topics.filter((t) => t.pinned)
const unpinnedTopics = assistant.topics.filter((t) => !t.pinned)
const reorderedTopics = [
topic, // 新固定的话题移到顶部
...pinnedTopics, // 其他固定话题
...unpinnedTopics.filter((t) => t.id !== topic.id) // 其他未固定话题(排除 topic
]
newIndex = 0
updateTopics(reorderedTopics)
}
const updatedTopic = { ...topic, pinned: !topic.pinned }
updateTopic(updatedTopic)
// 延迟滚动到话题位置(等待渲染完成)
setTimeout(() => {
listRef.current?.scrollToIndex(newIndex, { align: 'auto' })
}, 50)
},
[updateTopic]
[assistant.topics, updateTopic, updateTopics]
)
const onDeleteTopic = useCallback(
@ -529,6 +567,7 @@ export const Topics: React.FC<Props> = ({ assistant: _assistant, activeTopic, se
return (
<>
<DraggableVirtualList
ref={listRef}
className="topics-tab"
list={filteredTopics}
onUpdate={updateTopics}
@ -663,7 +702,7 @@ export const Topics: React.FC<Props> = ({ assistant: _assistant, activeTopic, se
</TopicPromptText>
)}
{showTopicTime && (
<TopicTime className="time">{dayjs(topic.createdAt).format('MM/DD HH:mm')}</TopicTime>
<TopicTime className="time">{dayjs(topic.createdAt).format('YYYY/MM/DD HH:mm')}</TopicTime>
)}
</TopicListItem>
</Dropdown>