fix the incognito preference ordering, and register new profiles earlier on iOS

Move appPreferences.incognito.set(true) inside the success branch: written
before the call that can fail, it left the app-wide default on so the next
connection silently used a random profile. Mirror of the profile-path bug
fixed two blocks above; iOS does not have it.

Register the created profile in chatModel.users right after creation rather
than after the ownership guard, so a user switch mid-create cannot leave a
profile that exists in the database but in no list the app shows. Replaces
the later fallbacks, which this subsumes.

Also correct the plan on simplex-chat-client/typescript: it does import the
regenerated types package, and is insulated by a ^0.3.0 pin, not by having
its own types - adding the field there would break it.
This commit is contained in:
Narasimha-sc
2026-08-07 15:32:12 +00:00
parent d0b3c34561
commit 54e2fac3dd
4 changed files with 53 additions and 28 deletions
@@ -259,6 +259,14 @@ struct ContextProfilePickerView: View {
let ownerUserId = await MainActor.run { chatModel.currentUser?.userId }
let profile = Profile(displayName: displayName, fullName: "", shortDescr: shortDescr, image: image)
let newUser = try apiCreateActiveUser(profile, keepActiveUser: true)
// Before any early return below, as Kotlin does: nothing else on iOS refreshes
// chatModel.users, so a profile left out of it exists in the database but in no
// list the app shows - and creating it again fails on the duplicate name.
await MainActor.run {
if !chatModel.users.contains(where: { $0.user.userId == newUser.userId }) {
chatModel.users.append(UserInfo(user: newUser, unreadCount: 0))
}
}
// Checked before refreshing the lists below: on this path the core has already
// activated the new profile, so they would disagree with chatModel.currentUser
// until the resync lands - and changeActiveUserAsync_ refreshes them anyway.
@@ -295,17 +303,10 @@ struct ContextProfilePickerView: View {
chatModel.users = updatedUsers
// Only filled in onAppear otherwise, so the new profile is missing here
users = updatedUsers.map { $0.user }.filter { u in u.activeUser || !u.hidden }
} else {
} else if !users.contains(where: { $0.userId == newUser.userId }) {
// changeProfile sets selectedUser to it, and otherUsers filters on that -
// absent from users, nothing is filtered out and a row is clipped.
if !users.contains(where: { $0.userId == newUser.userId }) {
users.append(newUser)
}
// App-wide too, or the profile exists in the database but in no list the
// app shows, and creating it again fails on the duplicate name.
if !chatModel.users.contains(where: { $0.user.userId == newUser.userId }) {
chatModel.users.append(UserInfo(user: newUser, unreadCount: 0))
}
users.append(newUser)
}
// changingProfile here too: the defer clears creatingProfile as soon as this returns
showAddProfile = false
@@ -605,6 +605,14 @@ private struct ActiveProfilePicker: View {
let ownerUserId = await MainActor.run { chatModel.currentUser?.userId }
let profile = Profile(displayName: displayName, fullName: "", shortDescr: shortDescr, image: image)
let newUser = try apiCreateActiveUser(profile, keepActiveUser: true)
// Before any early return below, as Kotlin does: nothing else on iOS refreshes
// chatModel.users, so a profile left out of it exists in the database but in no
// list the app shows - and creating it again fails on the duplicate name.
await MainActor.run {
if !chatModel.users.contains(where: { $0.user.userId == newUser.userId }) {
chatModel.users.append(UserInfo(user: newUser, unreadCount: 0))
}
}
// Checked before refreshing the lists below: on this path the core has already
// activated the new profile, so they would disagree with chatModel.currentUser
// until the resync lands - and changeActiveUserAsync_ refreshes them anyway.
@@ -638,13 +646,8 @@ private struct ActiveProfilePicker: View {
let updatedUsers = try? await listUsersAsync()
await MainActor.run {
if let updatedUsers = updatedUsers { chatModel.users = updatedUsers }
// listUsersAsync failed, so chatModel.users predates the creation. Add it there
// as well as to profiles below, or the profile exists in the database but in no
// list the app shows, and creating it again fails on the duplicate name.
if !chatModel.users.contains(where: { $0.user.userId == newUser.userId }) {
chatModel.users.append(UserInfo(user: newUser, unreadCount: 0))
}
// Without this selectedProfile below points at a profile that has no row.
// Derived from chatModel.users, which already holds the new profile - without
// it selectedProfile below points at a profile that has no row in the picker.
profiles = chatModel.users.map { $0.user }
showAddProfile = false
selectedProfile = newUser
@@ -413,9 +413,12 @@ fun ActiveProfilePicker(
switchingProfile.value = true
withApi {
try {
appPreferences.incognito.set(true)
val conn = controller.apiSetConnectionIncognito(rhId, contactConnection.pccConnId, true)
if (conn != null) {
// Only once the connection is actually incognito, as on the profile path
// above: set before the call, a failure leaves the app-wide default on and
// the next connection silently uses a random profile.
appPreferences.incognito.set(true)
withContext(Dispatchers.Main) {
chatModel.chatsContext.updateContactConnection(rhId, conn)
updateShownConnection(conn)