mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-25 14:00:05 +00:00
swallow Back while creating instead of leaving it unhandled
enableClose = false removes ModalView's handler rather than neutering it, so Compose passed Back down to ChatView's, closing the chat behind the form that was still on screen. Register a no-op handler that wins while creating. Also guard the iOS incognito row on contactConnection, as Kotlin already does: incognitoEnabled is a binding onto the app-wide default, so starting a change that cannot happen wrote that preference twice to undo itself.
This commit is contained in:
@@ -428,9 +428,11 @@ private struct ActiveProfilePicker: View {
|
||||
dismiss()
|
||||
}
|
||||
} else {
|
||||
// Nothing to change, so nothing happened. Without this the status
|
||||
// stays .switchingIncognito and busy leaves the whole picker -
|
||||
// including the new "Add profile" row - permanently dead.
|
||||
// Backstop for a nil connection coming back without a throw: the
|
||||
// row above cannot start this with contactConnection nil. Without
|
||||
// it the status stays .switchingIncognito and busy leaves the
|
||||
// whole picker - including "Add profile" - permanently dead. Both
|
||||
// writes in one hop, so the re-entrant onChange sees .idle.
|
||||
await MainActor.run {
|
||||
profileSwitchStatus = .idle
|
||||
incognitoEnabled = !incognito
|
||||
@@ -665,7 +667,11 @@ private struct ActiveProfilePicker: View {
|
||||
|
||||
@ViewBuilder private func profilePicker() -> some View {
|
||||
let incognitoOption = Button {
|
||||
if !incognitoEnabled {
|
||||
// contactConnection too, as Kotlin's IncognitoUserOption checks: with nothing to
|
||||
// change the handler below does nothing, and incognitoEnabled is a binding onto
|
||||
// the app-wide default, so toggling it here would write that preference twice
|
||||
// for an action that cannot happen.
|
||||
if !incognitoEnabled && contactConnection != nil {
|
||||
incognitoEnabled = true
|
||||
profileSwitchStatus = .switchingIncognito
|
||||
}
|
||||
|
||||
+4
@@ -376,6 +376,10 @@ fun createProfileForInvitation(rhId: Long?, onCreated: suspend (User) -> Unit) {
|
||||
// and the next attempt at the same name fails as a duplicate. iOS closes the same
|
||||
// window with interactiveDismissDisabled.
|
||||
ModalView(close, enableClose = !chatModel.creatingProfileForInvitation.value) {
|
||||
// Consume Back rather than leaving it unhandled: ModalView's own handler is disabled
|
||||
// above, and Compose would then pass the event down to ChatView's, closing the chat
|
||||
// behind the form. Registered after ModalView's, so it wins while it is enabled.
|
||||
BackHandler(enabled = chatModel.creatingProfileForInvitation.value, onBack = {})
|
||||
CreateProfile(chatModel, close, submitting = chatModel.creatingProfileForInvitation.value) { displayName, shortDescr, image ->
|
||||
if (chatModel.creatingProfileForInvitation.value) return@CreateProfile
|
||||
chatModel.creatingProfileForInvitation.value = true
|
||||
|
||||
@@ -371,10 +371,13 @@ re-derive them.
|
||||
not.** `ModalView(close, enableClose = !creatingProfileForInvitation)` now disables Back,
|
||||
Esc and the back arrow while the profile is being created — the Kotlin equivalent of
|
||||
iOS's `interactiveDismissDisabled`, following `MigrateFromDevice`/`ChooseServerOperators`,
|
||||
which use the same idiom. Note the Android consequence: with no enabled `BackHandler`,
|
||||
Back falls through to whatever handles it beneath rather than doing nothing. That is the
|
||||
same trade-off the migration screens already accept, and it beats the alternative of
|
||||
orphaning a profile. The separate case below is still open.
|
||||
which use the same idiom. **`enableClose = false` is not enough on its own**: it removes
|
||||
the handler rather than neutering it, and Compose then passes Back to the next enabled
|
||||
one — `ChatView`'s (`ChatView.kt`, unconditional), which closes the chat behind the still
|
||||
visible form. So the form also registers `BackHandler(enabled = creating, onBack = {})`
|
||||
to swallow the event; it composes after `ModalView`'s and therefore wins while enabled.
|
||||
The migration screens have the same fall-through, but they are not layered over a screen
|
||||
with its own handler. The separate case below is still open.
|
||||
|
||||
- **A modal pushed on top of the create form is indistinguishable from backing out of it**
|
||||
(`WelcomeView.kt`). The guard is `!isLastModalOpenNotClosing(...)`, which is also false
|
||||
|
||||
Reference in New Issue
Block a user