core, ui: total match in moderation path, merge member profile, reload list for another group on iOS

This commit is contained in:
Narasimha-sc
2026-09-26 17:36:58 +00:00
parent de0b293da7
commit 9de8a82cfd
5 changed files with 10 additions and 6 deletions
+1
View File
@@ -1334,6 +1334,7 @@ final class ChatModel: ObservableObject {
if let current = getGroupMember(member.groupMemberId)?.wrapped {
m = current
m.supportChat = member.supportChat
m.memberProfile = member.memberProfile
}
_ = upsertGroupMember(groupInfo, m)
objectWillChange.send()
@@ -20,7 +20,7 @@ struct MemberSupportView: View {
var body: some View {
viewBody()
.onAppear {
if !chatModel.membersLoaded {
if !chatModel.membersLoaded || chatModel.groupMembers.first?.wrapped.groupId != groupInfo.groupId {
Task {
await chatModel.loadGroupMembers(groupInfo)
}
@@ -385,7 +385,7 @@ object ChatModel {
if (cInfo !is ChatInfo.Group) return
val member = (cInfo.groupChatScope as? GroupChatScopeInfo.MemberSupport)?.groupMember_ ?: return
val current = groupMembersIndexes.value[member.groupMemberId]?.let { groupMembers.value.getOrNull(it) }
chatsContext.upsertGroupMember(rhId, cInfo.groupInfo, current?.copy(supportChat = member.supportChat) ?: member)
chatsContext.upsertGroupMember(rhId, cInfo.groupInfo, current?.copy(supportChat = member.supportChat, memberProfile = member.memberProfile) ?: member)
}
fun withLoadedSupportChat(member: GroupMember): GroupMember {
+3 -2
View File
@@ -34,12 +34,12 @@ Load the list once per open group, then keep it current from what arrives.
- `deleteGroupCIs` puts the updated scope member into each deletion's chat info.
- The group send response re-reads the support scope member after `saveSndChatItems` has updated `support_chat_ts`, instead of returning the member from before the send. If that read returns a store error, it falls back to the pre-send member rather than failing a send that has already happened.
- Internal items go through `createChatItems`, for example "new member pending review" (unread and attention +1). It now builds its items from the `ChatInfo` returned by `updateChatTsStats`, as `saveRcvChatItem'` already does, instead of the pre-update `toChatInfo cd`. Otherwise a new pending member would appear without a badge.
- A moderation that arrives before its message creates the item and marks it deleted. The `ChatItemsDeleted` event now carries the chat info returned by creating the item, not the scope from before it.
- A moderation that arrives before its message creates the item and marks it deleted. The `ChatItemsDeleted` event now carries the group info and scope returned by creating the item, not those from before it.
- Opening a member's support chat for the first time sets `support_chat_ts` and now returns the re-read member, so the new chat appears in the list.
- Existing clients are unaffected: both apps strip the scope in `updateChatInfo`.
**Android/desktop, and iOS**
- New `upsertSupportChatMember(cInfo)`. When the chat info carries a support-chat member, it adds that member if absent. If the member is already present, it replaces only its `supportChat` stats. Status, role and profile keep coming from their dedicated events. That way an item event applied late, such as a leave item handled after `LeftMember`, cannot restore an old status.
- New `upsertSupportChatMember(cInfo)`. When the chat info carries a support-chat member, it adds that member if absent. If the member is already present, it replaces only its `supportChat` stats and profile. The profile is copied because a member who is also a contact gets profile updates over the direct connection, and no group event carries them. Status and role keep coming from their dedicated events. That way an item event applied late, such as a leave item handled after `LeftMember`, cannot restore an old status.
- It is called for:
- `NewChatItems` events;
- `ChatItemsDeleted` events;
@@ -63,6 +63,7 @@ A member's first support message arrives as a `NewChatItems` event with that mem
- 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, if the list is on screen while the app resumes, changes the notification extension made while suspended appear only after the list is left and reopened.
- On iOS, handlers that update an existing member in place without publishing a change, such as "Mark read" from the context menu or accept, update the row but not the list order or filter until the next `ChatModel` change. The existing TODO in `deleteMemberSupportChat` describes the same mechanism. Previously, returning to the list also re-sorted it.
- The connection-state labels in rows (failed, disabled, inactive) come from `activeConn`. Neither app handles `ConnectionDisabled` or `ConnectionInactive`, so these labels now refresh only when the group is reopened.
## Alternatives considered
+4 -2
View File
@@ -2248,8 +2248,10 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
| otherwise = do
file_ <- processFileInv gInfo' (Just m')
(ci, cInfo) <- createNonLive gInfo' (Just m') scopeInfo file_
let moderatedScopeInfo = case cInfo of GroupChat _ itemScopeInfo -> itemScopeInfo
deletions <- markGroupCIsDeleted user gInfo' moderatedScopeInfo [CChatItem SMDRcv ci] (Just moderator) moderatedAt
let (moderatedGInfo, moderatedScopeInfo) = case cInfo of
GroupChat itemGInfo itemScopeInfo -> (itemGInfo, itemScopeInfo)
_ -> (gInfo', scopeInfo)
deletions <- markGroupCIsDeleted user moderatedGInfo moderatedScopeInfo [CChatItem SMDRcv ci] (Just moderator) moderatedAt
toView $ CEvtChatItemsDeleted user deletions False False
-- m' is Maybe GroupMember
createNonLive gInfo' m' scopeInfo file_ = do