From 4a81be31c4bc7b41a5fabeab332fc5f9f65571cc Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Sat, 29 Aug 2026 05:37:38 +0000 Subject: [PATCH] ui: include unread support chats in the chat list unread filter A group where a member is waiting in a support chat has something to read - the chat list row already flags it - but the unread filter did not list it, so a moderator filtering for unread silently skipped the member waiting on them. chatStats.unreadCount is filled by a query scoped to the main conversation (group_scope_tag IS NULL), so unreadTag cannot see support scopes at all. Match on Chat.hasUnread, which adds supportUnreadCount to unreadTag, gated on the chat not being muted to "mute all" - so a support chat waiting in a fully muted group does not put it in the filter, matching what the profile's unread count does with that group. Marking such a chat unread by hand still lists it, through unreadTag's unreadChat branch. Two consequences of taking supportUnreadCount whole are deliberate. For a moderator it is membersRequireAttention, which counts members pending approval as well as unread messages, and only approving or rejecting clears those - so the group stays listed until the moderator acts. And mentions-only chats still count support unread, on the view that mentions-only is a statement about group chatter and the user's own line to the admins is not chatter. --- apps/ios/Shared/Model/ChatModel.swift | 2 ++ apps/ios/Shared/Views/ChatList/ChatListView.swift | 2 +- apps/ios/product/views/chat-list.md | 2 +- apps/ios/spec/client/chat-list.md | 2 +- .../commonMain/kotlin/chat/simplex/common/model/ChatModel.kt | 2 ++ .../kotlin/chat/simplex/common/views/chatlist/ChatListView.kt | 2 +- apps/multiplatform/product/views/chat-list.md | 2 +- apps/multiplatform/spec/client/chat-list.md | 2 +- 8 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index 419c93d4ac..184a28c27a 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -1412,6 +1412,8 @@ final class Chat: ObservableObject, Identifiable, ChatLike { } } + var hasUnread: Bool { unreadTag || (chatInfo.chatSettings?.enableNtfs != MsgFilter.none && supportUnreadCount > 0) } + public static var sampleData: Chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: []) } diff --git a/apps/ios/Shared/Views/ChatList/ChatListView.swift b/apps/ios/Shared/Views/ChatList/ChatListView.swift index b05e0696e3..afb82fe3d9 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -552,7 +552,7 @@ struct ChatListView: View { switch chatTagsModel.activeFilter { case let .presetTag(tag): presetTagMatchesChat(tag, chat.chatInfo, chat.chatStats) case let .userTag(tag): chat.chatInfo.chatTags?.contains(tag.chatTagId) == true - case .unread: chat.unreadTag + case .unread: chat.hasUnread case .none: true } } diff --git a/apps/ios/product/views/chat-list.md b/apps/ios/product/views/chat-list.md index 04d19bef9e..35a7905173 100644 --- a/apps/ios/product/views/chat-list.md +++ b/apps/ios/product/views/chat-list.md @@ -41,7 +41,7 @@ Managed by `ChatTagsModel` and `TagListView`: | Filter | PresetTag | Description | |---|---|---| | All | (none) | No filter, shows all chats | -| Unread | `.unread` | Chats with unread messages | +| Unread | `.unread` | Chats with unread messages, or unread support chats unless muted to "mute all" | | Favorites | `.favorites` | User-favorited chats | | Groups | `.groups` | Group conversations only | | Contacts | `.contacts` | Direct contacts only | diff --git a/apps/ios/spec/client/chat-list.md b/apps/ios/spec/client/chat-list.md index d35de1f80a..a0d07bd0e0 100644 --- a/apps/ios/spec/client/chat-list.md +++ b/apps/ios/spec/client/chat-list.md @@ -163,7 +163,7 @@ Horizontal scrolling tab bar below the navigation bar. Tabs: | Tab | Filter | Shows | |-----|--------|-------| | All | `nil` | All conversations | -| Unread | `.unread` | Conversations with unread messages | +| Unread | `.unread` | Conversations with unread messages, or unread support chats unless muted to "mute all" | | Favorites | `.presetTag(.favorites)` | Favorited conversations | | Groups | `.presetTag(.groups)` | Group conversations | | Contacts | `.presetTag(.contacts)` | Direct conversations | diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 7f1ff70709..a221233047 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -1456,6 +1456,8 @@ data class Chat( else -> 0 } + val hasUnread: Boolean get() = unreadTag || (chatInfo.chatSettings?.enableNtfs != None && supportUnreadCount > 0) + fun groupFeatureEnabled(feature: GroupFeature): Boolean = if (chatInfo is ChatInfo.Group) { chatInfo.groupInfo.groupFeatureEnabled(feature) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt index 68fa25d553..11c2c82e60 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt @@ -1476,7 +1476,7 @@ private fun filtered(chat: Chat, activeFilter: ActiveFilter?): Boolean = when (activeFilter) { is ActiveFilter.PresetTag -> presetTagMatchesChat(activeFilter.tag, chat.chatInfo, chat.chatStats) is ActiveFilter.UserTag -> chat.chatInfo.chatTags?.contains(activeFilter.tag.chatTagId) ?: false - is ActiveFilter.Unread -> chat.unreadTag + is ActiveFilter.Unread -> chat.hasUnread else -> true } diff --git a/apps/multiplatform/product/views/chat-list.md b/apps/multiplatform/product/views/chat-list.md index daa7907c5d..40d3b1537a 100644 --- a/apps/multiplatform/product/views/chat-list.md +++ b/apps/multiplatform/product/views/chat-list.md @@ -65,7 +65,7 @@ Managed by `chatModel.userTags`, `chatModel.presetTags`, and `chatModel.activeCh | Business | `BUSINESS` | Work | Business chat conversations | | Notes | `NOTES` | Folder | Notes to self | | Custom tags | `UserTag(ChatTag)` | Label/emoji | User-created tags with custom emoji and name | -| Unread | `ActiveFilter.Unread` | Filter list icon | Chats with unread messages (toggle via filter button) | +| Unread | `ActiveFilter.Unread` | Filter list icon | Chats with unread messages, or unread support chats unless muted to "mute all" (toggle via filter button) | Display logic: - When collapsible preset tags exceed 3 total (with user tags), they collapse into a `CollapsedTagsFilterView` dropdown menu diff --git a/apps/multiplatform/spec/client/chat-list.md b/apps/multiplatform/spec/client/chat-list.md index b0f3750659..572dd5bd82 100644 --- a/apps/multiplatform/spec/client/chat-list.md +++ b/apps/multiplatform/spec/client/chat-list.md @@ -143,7 +143,7 @@ The `filteredChats` function (line ~1188) applies filters in this order: 3. **Active filter:** - `PresetTag`: Matches chat type and characteristics (e.g., `CONTACTS` filters `ChatInfo.Direct`, `GROUPS` filters `ChatInfo.Group`). - `UserTag`: Matches chats whose `chatTags` contain the tag ID. - - `Unread`: Matches chats with `unreadCount > 0` or `unreadChat == true`. + - `Unread`: Matches `Chat.hasUnread` — a chat marked unread by hand, unread messages as the chat's notification setting counts them, or unread support chats unless the chat is muted to "mute all". ### Search Bar