fix four more the high-effort review found, two of them my own reverts

Two of these exist because I reverted half of a coupled pair while keeping the other
half.

iOS: apiChangeConnectionUser returns nil rather than throwing when the network retry is
cancelled, so neither branch of the selectedProfile handler ran, profileSwitchStatus
stayed .switchingUser, switchingProfileByTimeout latched and the picker was left
permanently behind its spinner with hit testing off. I removed this else branch two
passes ago believing it was only reachable with no connection - it is reachable offline,
which is a row in this branch's own test matrix, and on the create path it also strands
the profile just created. Kotlin has had the equivalent guard all along.

Kotlin: appPreferences.incognito.set(false) went back to master's position, before the
connection change - but the early return that now keeps the picker open on failure stayed.
Together those clear the app-wide default while the picker still shows Incognito ticked
and nothing was moved. Moved below the reassignment, where the early return puts it out
of reach.

changeActiveUser_ throws, and withApi does not catch, so a failure inside onCreated
reached GlobalExceptionsHandler, which silently closes a modal or clears chatId - profile
created, invitation not moved, nothing said. Caught and reported, matching the runCatching
and the safe changeActiveUser wrapper this same function already uses.

iOS: when the profile list refresh fails, the new profile is absent from users while
changeProfile sets selectedUser to it, so otherUsers filters nobody out and the row count
exceeds what frame(maxHeight:) allows - clipping "Add profile" at the top, for exactly the
single-profile user this feature is for.

Also records that Terminal/Input.hs caches any CRActiveUser as the host's current user,
which keepActiveUser makes conditional. Display-only, and the response carries nothing to
distinguish the two cases.
This commit is contained in:
Narasimha-sc
2026-08-07 13:05:02 +00:00
parent 3a437a4597
commit 185c6e1d2b
5 changed files with 28 additions and 3 deletions
@@ -295,6 +295,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 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.
users.append(newUser)
}
// changingProfile here too: the defer clears creatingProfile as soon as this returns
showAddProfile = false
@@ -479,6 +479,15 @@ private struct ActiveProfilePicker: View {
)
}
}
} else {
// apiChangeConnectionUser returns nil rather than throwing when
// the retry is cancelled - offline. Without this the status stays
// .switchingUser, switchingProfileByTimeout latches, and the
// picker is left permanently behind its spinner.
await MainActor.run {
profileSwitchStatus = .idle
selectedProfile = chatModel.currentUser ?? selectedProfile
}
}
} catch {
await MainActor.run {
@@ -414,7 +414,14 @@ fun createProfileForInvitation(rhId: Long?, onCreated: suspend (User) -> Unit) {
return@withApi
}
close()
onCreated(newUser)
try {
onCreated(newUser)
} catch (e: Exception) {
// changeActiveUser_ throws, and withApi does not catch - the global handler
// would close a modal or clear chatId with nothing said about the failure.
Log.e(TAG, "createProfileForInvitation: moving the invitation failed: ${e.stackTraceToString()}")
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.error_changing_user))
}
} finally {
chatModel.creatingProfileForInvitation.value = false
}
@@ -323,8 +323,6 @@ fun ActiveProfilePicker(
try {
var updatedConn: PendingContactConnection? = null
appPreferences.incognito.set(false)
if (contactConnection != null) {
updatedConn = controller.apiChangeConnectionUser(rhId, contactConnection.pccConnId, user.userId)
// Not moved - leave the picker open rather than stranding a profile just created
@@ -335,6 +333,9 @@ fun ActiveProfilePicker(
updateShownConnection(updatedConn)
}
}
// After the move, not before: the picker now stays open on failure, and clearing the
// app-wide default there would leave it off with Incognito still ticked in the picker
appPreferences.incognito.set(false)
controller.changeActiveUser_(
rhId = user.remoteHostId,
@@ -188,6 +188,10 @@ Two hazards left as they are on master, so review does not keep re-raising them:
profile first is local and fast and barely widens it. Fixing it needs a `ModalViewId`
on the picker at both call sites, including `ShareListView`, which does not offer this
feature at all.
- `Terminal/Input.hs` caches any `CRActiveUser` as the remote host's current user. With
`keepActiveUser` that response can carry a profile that is deliberately not active, so a
CLI acting as a controller caches the wrong one until the next `/user`. Display-only,
and there is no flag in the response to distinguish the two cases.
- `alertAfterDismissal` waits a fixed 0.5s for a sheet transition rather than observing
it. A slow device or a late-released interactive dismissal can still outlast it. The
deterministic version needs the presenting controller's completion handler, which is