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.
This commit is contained in:
Narasimha-sc
2026-09-05 11:38:41 +00:00
parent 717241af5b
commit 4a81be31c4
8 changed files with 10 additions and 6 deletions
+2
View File
@@ -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: [])
}
@@ -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
}
}
+1 -1
View File
@@ -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 |
+1 -1
View File
@@ -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 |
@@ -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)
@@ -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
}
@@ -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
+1 -1
View File
@@ -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