From 101d88c11997918e83cdd941c9f009cd111a1041 Mon Sep 17 00:00:00 2001 From: Narasimha-sc <166327228+Narasimha-sc@users.noreply.github.com> Date: Fri, 7 Aug 2026 22:28:04 +0000 Subject: [PATCH] check the active user after the refresh await, not before it The ownership guard sat immediately above listUsersAsync, so that await reopened the window the guard exists to close: a notification action switching users during it left the reassignment resolving under the wrong profile. Kotlin already ordered these the other way. Also refresh the picker's own profiles snapshot on the stale-core path, or it lists the old profiles with no checkmark anywhere; and bring the two incognito guards into line with the Kotlin ones changed in 2168851ad - gate the mutating branch, not the local expand/collapse. --- .../ComposeMessage/ContextProfilePickerView.swift | 14 ++++++++------ apps/ios/Shared/Views/NewChat/NewChatView.swift | 12 ++++++++++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextProfilePickerView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextProfilePickerView.swift index a140d95769..b7ada05208 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextProfilePickerView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextProfilePickerView.swift @@ -295,14 +295,16 @@ struct ContextProfilePickerView: View { users.append(newUser) } } + let updatedUsers = try? await listUsersAsync() // changeProfile resolves the prepared chat under whatever is active when it runs, - // and a notification action can have switched it while we were creating. + // and a notification action can have switched it while we were creating. After the + // await above, not before it: checked first, that await reopens the very window + // this closes. Kotlin orders it the same way. guard await MainActor.run({ chatModel.currentUser?.userId }) == ownerUserId else { await MainActor.run { showAddProfile = false } alertAfterDismissal(NSLocalizedString("Error changing chat profile", comment: "alert title")) return } - let updatedUsers = try? await listUsersAsync() await MainActor.run { if let updatedUsers = updatedUsers { chatModel.users = updatedUsers @@ -361,13 +363,13 @@ struct ContextProfilePickerView: View { private func incognitoOption() -> some View { Button { - // As in profilerPickerUserOption: a failed changeProfile would leave the - // incognito default on while the picker still shows the chat profile. - if busy { return } if !chat.chatInfo.profileChangeProhibited { if incognitoDefault { listExpanded.toggle() - } else { + } else if !busy { + // As in profilerPickerUserOption: a failed changeProfile would leave the + // incognito default on while the picker still shows the chat profile. + // Only this branch - expanding and collapsing is local, as on Kotlin. incognitoDefault = true listExpanded = false } diff --git a/apps/ios/Shared/Views/NewChat/NewChatView.swift b/apps/ios/Shared/Views/NewChat/NewChatView.swift index 44c8a1f131..887aba7e62 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatView.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatView.swift @@ -559,7 +559,9 @@ private struct ActiveProfilePicker: View { private func profilerPickerUserOption(_ user: User) -> some View { Button { - if selectedProfile == user && incognitoEnabled { + // contactConnection as in incognitoOption: with nothing to change the handler + // does nothing and the backstop writes the app-wide default straight back. + if selectedProfile == user && incognitoEnabled && contactConnection != nil { incognitoEnabled = false profileSwitchStatus = .switchingIncognito } else if selectedProfile != user { @@ -634,6 +636,10 @@ private struct ActiveProfilePicker: View { // previous profile if it threw. Not newUser unconditionally, or a failed // switch leaves the checkmark on a profile that is not active. selectedProfile = chatModel.currentUser ?? selectedProfile + // The resync refreshed chatModel.users but not this snapshot, which is + // otherwise only filled in onAppear: without it the picker lists the old + // profiles, none matches the active one, and no row shows a checkmark. + profiles = chatModel.users.map { $0.user } } alertAfterDismissal(NSLocalizedString("Error changing chat profile", comment: "alert title")) return @@ -649,14 +655,16 @@ private struct ActiveProfilePicker: View { } profiles = chatModel.users.map { $0.user } } + let updatedUsers = try? await listUsersAsync() // apiChangeConnectionUser resolves pccConnId under whatever is active when the // selectedProfile handler runs, and a notification action can have switched it. + // After the await above, not before it: checked first, that await reopens the very + // window this closes. Kotlin orders it the same way. guard await MainActor.run({ chatModel.currentUser?.userId }) == ownerUserId else { await MainActor.run { showAddProfile = false } alertAfterDismissal(NSLocalizedString("Error changing chat profile", comment: "alert title")) return } - let updatedUsers = try? await listUsersAsync() await MainActor.run { if let updatedUsers = updatedUsers { chatModel.users = updatedUsers } // Derived from chatModel.users, which already holds the new profile - without