From 2abbcb2d3634496599e95f57dbbc6fa8b0dc5909 Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Sat, 26 Sep 2026 17:07:27 +0000 Subject: [PATCH] core, ui: return updated support member on send, ignore stale member loads --- apps/ios/Shared/Model/SimpleXAPI.swift | 5 +++++ .../simplex/common/views/chatlist/ChatListNavLinkView.kt | 1 + plans/member-support-list-events.md | 8 +++++--- src/Simplex/Chat/Library/Commands.hs | 6 +++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/ios/Shared/Model/SimpleXAPI.swift b/apps/ios/Shared/Model/SimpleXAPI.swift index f61a9810df..fb0c0fd9c7 100644 --- a/apps/ios/Shared/Model/SimpleXAPI.swift +++ b/apps/ios/Shared/Model/SimpleXAPI.swift @@ -592,6 +592,11 @@ private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async } else { r = await chatApiSendCmd(cmd, bgDelay: msgDelay) if case let .result(.newChatItems(_, aChatItems)) = r { + await MainActor.run { + for aChatItem in aChatItems { + chatModel.upsertSupportChatMember(aChatItem.chatInfo) + } + } return aChatItems.map { $0.chatItem } } sendMessageErrorAlert(r.unexpected) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt index ca1528a3ce..660561dc81 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt @@ -265,6 +265,7 @@ suspend fun setGroupMembers(rhId: Long?, groupInfo: GroupInfo, chatModel: ChatMo newMember } } + if (chatModel.chatId.value != groupInfo.id && chatModel.creatingChannelId.value != groupInfo.id) return@coroutineScope chatModel.groupMembersIndexes.value = emptyMap() chatModel.groupMembers.value = newMembers chatModel.membersLoaded.value = true diff --git a/plans/member-support-list-events.md b/plans/member-support-list-events.md index cb76143a65..8fb5d739f1 100644 --- a/plans/member-support-list-events.md +++ b/plans/member-support-list-events.md @@ -32,6 +32,7 @@ Load the list once per open group, then keep it current from what arrives. - `updateGroupScopeUnreadStats` returns the updated `GroupChatScopeInfo` together with `GroupInfo`. - `APIChatItemsRead` returns `GroupChat gInfo' chatScopeInfo'`. - `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. - Existing clients are unaffected: both apps strip the scope in `updateChatInfo`. **Android/desktop, and iOS** @@ -40,18 +41,19 @@ Load the list once per open group, then keep it current from what arrives. - `NewChatItems` events; - `ChatItemsDeleted` events; - delete responses (items and reports); - - send responses (Kotlin only); + - send responses; - the mark-read response; - the initial load of a support chat. - The member list loads only if `membersLoaded` is false. The mention picker already uses this flag the same way, and it is reset when leaving the group. +- Kotlin `setGroupMembers` writes its result only if the group is still the open chat (or the channel being created), as iOS `loadGroupMembers` already does. Without this check, a slow load from a previously opened channel could finish after a chat switch and mark another group's members as loaded. The old reload on every return hid that. - The refresh button is removed. The list is kept current by the updates above. - iOS also sends `objectWillChange`, because updating a `GMember` in place does not re-render or re-sort the list. A member's first support message arrives as a `NewChatItems` event with that member, so a new support chat appears in the list without a reload. -## Not covered +## Known limitation -- On iOS, `apiSendMessages` returns bare `ChatItem`s without chat info. The list's last-activity order after the user's own reply updates only when the group is reopened. +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. ## Alternatives considered diff --git a/src/Simplex/Chat/Library/Commands.hs b/src/Simplex/Chat/Library/Commands.hs index f61ffe7d84..47dcbda57b 100644 --- a/src/Simplex/Chat/Library/Commands.hs +++ b/src/Simplex/Chat/Library/Commands.hs @@ -4875,7 +4875,11 @@ processChatCommand cxt nm = \case forM_ (timed_ >>= timedDeleteAt') $ \deleteAt -> forM_ cis $ \ci -> startProximateTimedItemThread user (ChatRef CTGroup groupId scope, chatItemId' ci) deleteAt - pure $ CRNewChatItems user (map (AChatItem SCTGroup SMDSnd (GroupChat gInfo chatScopeInfo)) cis) + chatScopeInfo' <- case chatScopeInfo of + Just GCSIMemberSupport {groupMember_ = Just sentScopeMem} -> + Just . GCSIMemberSupport . Just <$> withFastStore (\db -> getGroupMemberById db cxt user (groupMemberId' sentScopeMem)) + _ -> pure chatScopeInfo + pure $ CRNewChatItems user (map (AChatItem SCTGroup SMDSnd (GroupChat gInfo chatScopeInfo')) cis) where setupSndFileTransfers :: Int -> CM (NonEmpty (Maybe FileInvitation, Maybe (CIFile 'MDSnd))) setupSndFileTransfers n =