ios: keep support list row structure stable while its chat is open

This commit is contained in:
Narasimha-sc
2026-09-26 18:58:22 +00:00
parent ac6c61949f
commit 7bed5ea95f
2 changed files with 4 additions and 3 deletions
@@ -89,8 +89,8 @@ struct MemberSupportView: View {
.frame(width: 1, height: 1)
.hidden()
}
.if(!memberWithChat.wrapped.memberPending && memberWithChat.wrapped.supportChatNotRead) { v in
v.swipeActions(edge: .leading, allowsFullSwipe: true) {
.swipeActions(edge: .leading, allowsFullSwipe: true) {
if !memberWithChat.wrapped.memberPending && memberWithChat.wrapped.supportChatNotRead {
Button {
Task { await markSupportChatRead(groupInfo, memberWithChat.wrapped) }
} label: {
+2 -1
View File
@@ -62,9 +62,10 @@ A member's first support message arrives as a `NewChatItems` event with that mem
- A reset of `membersLoaded` that lands between the load's result write and clearing the in-progress flag (one dispatch hop) is skipped. For example, an Android configuration change or an iOS resume at that exact moment. The list then stays unloaded until it is next returned to.
- A failed member load (`apiListMembers` error) is not retried while the list stays open; it is retried when the list is reopened.
- The in-progress flag belongs to one open instance of the list. Closing and reopening the list while its first load is still running starts a second full load (previously every open reloaded).
- A full member load that is in flight when a support-chat update arrives overwrites that update with its snapshot. For example, the first list load can race a member's first support message. The member then reappears on their next message, when their chat is opened, or when the group is reopened.
- Support stats snapshots from different events and responses are applied in arrival order, so a rare reordering can briefly show an older count until the next update for that member.
- On iOS, updating an existing member in place (including the new support-stats upserts) updates that row's badges, because rows observe their `GMember`, but not the list order or filter until the next `ChatModel` change. A re-render is forced only when an existing member gets their first support chat, so that the member appears in the list. It is not forced on every stats change, because re-sorting the list while a member's support chat is pushed from it (`NavigationLink(isActive:)`) risks popping that chat on older iOS versions. The existing TODO in `deleteMemberSupportChat` describes the same mechanism. Previously, returning to the list also re-sorted it.
- On iOS, updating an existing member in place (including the new support-stats upserts) updates that row's badges, because rows observe their `GMember`, but not the list order or filter until the next `ChatModel` change. A re-render is forced only when an existing member gets their first support chat, so that the member appears in the list. The next `ChatModel` change usually follows within about a second of a read (the unread counter), so the list can now re-sort while a member's support chat is pushed from it. Rows keep their identity (`ForEach` by member id), and the row's leading swipe action is now built unconditionally with the condition inside it, so the row that holds the active `NavigationLink(isActive:)` is not rebuilt when its unread state flips. Whether re-sorting alone can pop the pushed chat on older iOS versions needs a device test.
- The connection-state labels in rows (failed, disabled, inactive) come from `activeConn`. Neither app handles `ConnectionDisabled` or `ConnectionInactive`, so these labels now refresh only on the next full member event for that member (role, profile, connected) or when the group is reopened.
- On iOS, a reset on resume while a member's support chat is open over the list runs one full member load in the background (the list stays in the navigation stack). An open started during that load waits for it once.