diff --git a/apps/ios/Shared/Model/ChatModel.swift b/apps/ios/Shared/Model/ChatModel.swift index c928de85db..04c76fd9a6 100644 --- a/apps/ios/Shared/Model/ChatModel.swift +++ b/apps/ios/Shared/Model/ChatModel.swift @@ -623,12 +623,14 @@ final class ChatModel: ObservableObject { // Spec: spec/state.md#updateChatInfo func updateChatInfo(_ cInfo: ChatInfo) { if let i = getChatIndex(cInfo.id) { + let wasUnread = chats[i].hasUnread if case let .group(groupInfo, groupChatScope) = cInfo, groupChatScope != nil { chats[i].chatInfo = .group(groupInfo: groupInfo, groupChatScope: nil) } else { chats[i].chatInfo = cInfo } chats[i].created = Date.now + if chats[i].hasUnread != wasUnread { objectWillChange.send() } } } @@ -1470,6 +1472,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 5c5021b79f..5516cc4a1d 100644 --- a/apps/ios/Shared/Views/ChatList/ChatListView.swift +++ b/apps/ios/Shared/Views/ChatList/ChatListView.swift @@ -673,7 +673,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 dee9e1d427..5a2de9b59a 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 support chats that need attention (as flagged on the row: unread messages or mentions, or, for moderators, members pending, with unread messages nobody has answered, or with unread mentions) 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 2087d0dee3..9388a43633 100644 --- a/apps/ios/spec/client/chat-list.md +++ b/apps/ios/spec/client/chat-list.md @@ -164,7 +164,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 support chats that need attention (as flagged on the row: unread messages or mentions, or, for moderators, members pending, with unread messages nobody has answered, or with unread mentions) 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 b3c2ec3e92..4f92dba2f2 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 @@ -1507,6 +1507,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 52cb2b338b..b45e7454ef 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 @@ -1601,7 +1601,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 a09994d1e3..3324906435 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 support chats that need attention (as flagged on the row: unread messages or mentions, or, for moderators, members pending, with unread messages nobody has answered, or with unread mentions) 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 5a63ad92bc..8b0ab1c7ff 100644 --- a/apps/multiplatform/spec/client/chat-list.md +++ b/apps/multiplatform/spec/client/chat-list.md @@ -144,7 +144,7 @@ The `filteredChats` function (line ~1474) 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 support chats that need attention (as flagged on the row: unread messages or mentions, or, for moderators, members pending, with unread messages nobody has answered, or with unread mentions) unless the chat is muted to "mute all". ### Search Bar