mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 23:00:09 +00:00
ui: don't match support chats in the unread filter when muted to "mute all"
supportUnreadCount never reads chatSettings, so the filter listed a group with an unread support chat even when the user had muted it entirely - while the profile picker's counter, which the button now reads, drops that group. Gate the support half on enableNtfs != None so the two agree on a fully muted chat. Mentions-only is deliberately left counting support unread. The counter takes a mentions-only group's item only when it mentions the user; this predicate takes any 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. "Mute all" is a statement about the whole chat, so it applies.
This commit is contained in:
@@ -1407,7 +1407,7 @@ final class Chat: ObservableObject, Identifiable, ChatLike {
|
||||
}
|
||||
}
|
||||
|
||||
var hasUnread: Bool { unreadTag || supportUnreadCount > 0 }
|
||||
var hasUnread: Bool { unreadTag || (chatInfo.chatSettings?.enableNtfs != MsgFilter.none && supportUnreadCount > 0) }
|
||||
|
||||
public static var sampleData: Chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
|
||||
}
|
||||
|
||||
@@ -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 or unread support chats |
|
||||
| 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 |
|
||||
|
||||
@@ -330,7 +330,7 @@ struct ChatStats: Decodable, Hashable {
|
||||
| `viewId` | Unique view identity including creation time | [L1338](../Shared/Model/ChatModel.swift#L1338) |
|
||||
| `unreadTag` | Whether chat counts as "unread" based on notification settings | [L1328](../Shared/Model/ChatModel.swift#L1328) |
|
||||
| `supportUnreadCount` | Unread count for group support scope | [L1340](../Shared/Model/ChatModel.swift#L1340) |
|
||||
| `hasUnread` | Whether chat matches the unread filter: `unreadTag` or any support unread | [L1410](../Shared/Model/ChatModel.swift#L1410) |
|
||||
| `hasUnread` | Whether chat matches the unread filter: `unreadTag`, or support unread when not muted to `.none` | [L1410](../Shared/Model/ChatModel.swift#L1410) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+1
-1
@@ -1437,7 +1437,7 @@ data class Chat(
|
||||
else -> 0
|
||||
}
|
||||
|
||||
val hasUnread: Boolean get() = unreadTag || supportUnreadCount > 0
|
||||
val hasUnread: Boolean get() = unreadTag || (chatInfo.chatSettings?.enableNtfs != None && supportUnreadCount > 0)
|
||||
|
||||
fun groupFeatureEnabled(feature: GroupFeature): Boolean =
|
||||
if (chatInfo is ChatInfo.Group) {
|
||||
|
||||
@@ -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 or unread support chats (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
|
||||
|
||||
@@ -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 `Chat.hasUnread` — unread messages, as the chat's notification setting counts them, or unread support chats.
|
||||
- `Unread`: Matches `Chat.hasUnread` — unread messages, as the chat's notification setting counts them, or unread support chats unless the chat is muted to "mute all".
|
||||
|
||||
### Search Bar
|
||||
|
||||
|
||||
@@ -282,7 +282,7 @@ data class ChatStats(
|
||||
| `id` | 1349 | Chat ID derived from `chatInfo.id` |
|
||||
| `unreadTag` | 1343 | Whether chat counts as "unread" for tag filtering (considers notification settings) |
|
||||
| `supportUnreadCount` | 1351 | Unread count in support/moderation context |
|
||||
| `hasUnread` | 1440 | Whether chat matches the unread filter: `unreadTag` or any support unread |
|
||||
| `hasUnread` | 1440 | Whether chat matches the unread filter: `unreadTag`, or support unread when not muted to `None` |
|
||||
| `nextSendGrpInv` | 1337 | Whether next message should send group invitation |
|
||||
|
||||
<a id="ChatInfo"></a>
|
||||
|
||||
@@ -28,7 +28,7 @@ Two consequences of taking `supportUnreadCount` whole are deliberate.
|
||||
|
||||
*Pending members count, and that signal is sticky.* For a moderator `supportUnreadCount` is `membersRequireAttention`, and the core computes `gmRequiresAttention` as `memberPending m || memberAttention > 0 || mentions > 0` (`Types.hs`) — so a member awaiting approval or review counts even with nothing to read. `updateSupportChatItemsRead` (`Store/Messages.hs`) zeroes the support counters but decrements the group's count only when the member stops requiring attention, which a pending member never does; approving or rejecting is what clears it. A moderator with a pending join request therefore sees the button tinted and the group listed under Unread with no unread badge until they act on the request. That is accepted: the member is genuinely waiting on the moderator, and the filter is where they will look for who is waiting. It does disagree with the chat list row, which renders `memberPending` as a grey flag rather than a call to action.
|
||||
|
||||
*Mute does not suppress the support half.* `unreadTag` gates on `enableNtfs`; `supportUnreadCount` never reads `chatSettings`, and this predicate does not add that gate. Muting a group silences its conversation but not its support chats — a message to or from admins still lights the button. That is intentional: mute is about group chatter, not about the user's own line to the people running the group.
|
||||
*Mute all suppresses the support half; mentions-only does not.* `supportUnreadCount` never reads `chatSettings`, so the predicate adds the gate: `enableNtfs != None && supportUnreadCount > 0`. A group muted to *mute all* therefore drops out of the filter even with a support chat waiting, matching what the profile picker's counter does with it. Under *mentions only* the support half still counts, which is where the filter and the counter part company — the counter takes a mentions-only group's item only when it mentions the user, and this predicate takes any support unread. That is deliberate: mentions-only is a statement about group chatter, and the user's own line to the admins is not chatter; *mute all* is a statement about the whole chat.
|
||||
|
||||
Support chats belong in this predicate because `unreadTag` alone cannot see them. `chatStats.unreadCount` is filled by a query scoped to the main conversation (`group_scope_tag IS NULL AND group_scope_group_member_id IS NULL`, `Store/Messages.hs`), so a group whose only unread sits in a member support scope satisfies neither `unreadTag` nor the filter. There was no way to reach the waiting member from the chat list.
|
||||
|
||||
@@ -41,7 +41,7 @@ The tint is `users.any { it.user.activeUser && it.unreadCount > 0 }` — the sam
|
||||
This is deliberately *not* the predicate the filter matches on, so the two can disagree. Where they do:
|
||||
|
||||
- **Marked unread.** `unread_chat` is a column on `contacts`/`groups`, and `getUsersInfo` never reads it. A chat you marked unread by hand lists under the filter with the button grey.
|
||||
- **Muted chats.** The counter's group clause is `enable_ntfs = 1 OR IS NULL OR (enable_ntfs = 2 AND user_mention = 1)` (`MFNone = 0`, `MFAll = 1`, `MFMentions = 2`, `Types.hs`). So under *mute all* no support item counts, and under *mentions only* a support item counts only if it mentions the user — an ordinary support message does not. Either way the group lists under the filter, which ignores mute for the support half, without lighting the button. The contact clause is stricter still: `enable_ntfs = 1 OR IS NULL`, with no mentions term.
|
||||
- **Mentions-only chats.** The counter's group clause is `enable_ntfs = 1 OR IS NULL OR (enable_ntfs = 2 AND user_mention = 1)` (`MFNone = 0`, `MFAll = 1`, `MFMentions = 2`, `Types.hs`). The two now agree on *mute all* — neither counts it — but under *mentions only* the counter takes a support item only when it mentions the user, while the filter takes any support unread. So an ordinary support message in a mentions-only group lists under the filter without lighting the button. The contact clause is stricter still: `enable_ntfs = 1 OR IS NULL`, with no mentions term.
|
||||
- **Pending members.** The counter counts messages, so a member awaiting approval with nothing written lists under the filter without lighting the button. The stickiness described above therefore never reaches the tint.
|
||||
|
||||
Accepted: the button is a cue that something is worth a tap, matching the count the user already recognises from the picker, and the filter is the authority on what is actually listed.
|
||||
|
||||
Reference in New Issue
Block a user