From 1f471873261a6859db638f3713efde0b54fc429a Mon Sep 17 00:00:00 2001 From: Maksim Sukharev Date: Wed, 26 Aug 2026 12:06:09 +0200 Subject: [PATCH] feat(conversation): adjust scroll to unread mentions - previous 'unreadMentionIndices' was accounting only for sortedConversationsList, but we render ConversationsListVirtual#listItems and should compute indexes directly from it Assisted-by: ClaudeCode:claude-sonnet-5 Signed-off-by: Maksim Sukharev --- .../ConversationsListVirtual.vue | 26 +++++++++++++- src/components/LeftSidebar/LeftSidebar.vue | 34 +++++-------------- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/components/LeftSidebar/ConversationsList/ConversationsListVirtual.vue b/src/components/LeftSidebar/ConversationsList/ConversationsListVirtual.vue index 241ebe87c2a..c3863639456 100644 --- a/src/components/LeftSidebar/ConversationsList/ConversationsListVirtual.vue +++ b/src/components/LeftSidebar/ConversationsList/ConversationsListVirtual.vue @@ -14,7 +14,7 @@ import ConversationItem from './ConversationItem.vue' import ConversationTagHeader from './ConversationTagHeader.vue' import { AVATAR, CONVERSATION } from '../../../constants.ts' import { useConversationTagsStore } from '../../../stores/conversationTags.ts' -import { hasCall, hasUnreadMessages } from '../../../utils/conversation.ts' +import { hasCall, hasUnreadMentions, hasUnreadMessages } from '../../../utils/conversation.ts' export type VirtualListItem = (Conversation | TagHeaderItem) & { _key?: string } @@ -347,11 +347,35 @@ function scrollToConversation(token: string) { } } +/** + * Get the indices (in the flattened virtual-list, i.e. tag-header- and + * collapsed-section-aware) of conversations with unread mentions. + */ +function getUnreadMentionIndices(): number[] { + const indices: number[] = [] + listItems.value.forEach((item, index) => { + if (!isTagHeader(item) && hasUnreadMentions(item)) { + indices.push(index) + } + }) + return indices +} + +/** + * Get the index (in the flattened virtual-list) of the last unread-mention + * conversation currently below the viewport, or null if there is none. + */ +function getLastUnreadMentionBelowViewportIndex(): number | null { + const lastInViewport = getLastItemInViewportIndex() + return getUnreadMentionIndices().findLast((idx) => idx > lastInViewport) ?? null +} + defineExpose({ getFirstItemInViewportIndex, getLastItemInViewportIndex, scrollToItem, scrollToConversation, + getLastUnreadMentionBelowViewportIndex, }) diff --git a/src/components/LeftSidebar/LeftSidebar.vue b/src/components/LeftSidebar/LeftSidebar.vue index d067cedea24..39e0049e7b7 100644 --- a/src/components/LeftSidebar/LeftSidebar.vue +++ b/src/components/LeftSidebar/LeftSidebar.vue @@ -660,14 +660,8 @@ export default { return sortConversationsList(this.filteredConversationsList, this.groupMode, this.sortOrder) }, - unreadMentionIndices() { - const indices = [] - for (const i in this.sortedConversationsList) { - if (hasUnreadMentions(this.sortedConversationsList[i])) { - indices.push(i) - } - } - return indices + hasUnreadMentionsInList() { + return this.sortedConversationsList.filter(hasUnreadMentions).length }, emptyContentLabel() { @@ -780,7 +774,7 @@ export default { } }, - unreadMentionIndices() { + hasUnreadMentionsInList() { this.debounceHandleScroll() }, }, @@ -1155,22 +1149,12 @@ export default { }, handleScroll() { - this.computeLastUnreadMention() - }, - - /** - * Find position of the last unread conversation below viewport. - * Iterates only over indices with unread mentions (cached via the - * unreadMentionIndices computed) instead of the full list. - */ - computeLastUnreadMention() { - if (!this.$refs.scroller) { - this.lastUnreadMentionBelowViewportIndex = null - return - } - const lastInViewport = this.$refs.scroller.getLastItemInViewportIndex() - this.lastUnreadMentionBelowViewportIndex = this.unreadMentionIndices - .findLast((idx) => idx > lastInViewport) ?? null + /** + * Find position of the last unread conversation below viewport. + * Delegates to the scroller, which owns the flattened list (tag + * headers and sections) and its indexes. + */ + this.lastUnreadMentionBelowViewportIndex = this.$refs.scroller?.getLastUnreadMentionBelowViewportIndex() ?? null }, async scrollToConversation(token) {