ios: disable the submit button while creating, as Kotlin already does

The form's only in-flight protection was the picker's own check-and-set, which makes a
second tap a silent no-op - the button stayed live with nothing visibly happening for
the whole of the create plus the profile list refresh. Kotlin gained a submitting flag
for this; iOS did not.

Also trims the last of the long doc comments on isLastModalOpenNotClosing.
This commit is contained in:
Narasimha-sc
2026-08-06 18:24:35 +00:00
parent f940796aaa
commit 767db0af21
4 changed files with 8 additions and 9 deletions
@@ -54,7 +54,7 @@ struct ContextProfilePickerView: View {
NavigationView {
CreateProfile(onSubmit: { displayName, shortDescr, image in
try await createProfileForChat(displayName, shortDescr, image)
})
}, submitting: creatingProfile)
}
.interactiveDismissDisabled(creatingProfile)
}
@@ -697,7 +697,7 @@ private struct ActiveProfilePicker: View {
NavigationView {
CreateProfile(onSubmit: { displayName, shortDescr, image in
try await createProfileForConnection(displayName, shortDescr, image)
})
}, submitting: creatingProfile)
}
// The submit runs in an unstructured Task that SwiftUI does not cancel, so a
// swipe-to-dismiss mid-create would still create the profile and switch to it
@@ -33,6 +33,8 @@ struct CreateProfile: View {
// used to create a profile for an invitation (which must not switch the active user
// until the prepared chat has been reassigned). Errors are still shown by this view.
var onSubmit: ((_ displayName: String, _ shortDescr: String?, _ image: String?) async throws -> Void)? = nil
/// Set while onSubmit is in flight, so the button does not stay live with nothing happening.
var submitting: Bool = false
@Environment(\.colorScheme) var colorScheme
@Environment(\.dismiss) var dismiss
@EnvironmentObject var theme: AppTheme
@@ -108,7 +110,7 @@ struct CreateProfile: View {
Button(action: createProfile) {
settingsRow("checkmark", color: theme.colors.primary) { Text("Create profile") }
}
.disabled(!canCreateProfile(displayName) || !bioFitsLimit())
.disabled(submitting || !canCreateProfile(displayName) || !bioFitsLimit())
} footer: {
VStack(alignment: .leading, spacing: 8) {
Text("Your profile is stored on your device and only shared with your contacts.")
@@ -117,12 +117,9 @@ class ModalManager(private val placement: ModalPlacement? = null) {
fun isLastModalOpen(id: ModalViewId): Boolean = modalViews.lastOrNull()?.id == id
/** [isLastModalOpen], except that a modal already dismissed and only waiting out its
* close animation does not count - [closeModal] leaves it in [modalViews] and stages
* its index in [toRemove]. A caller deciding whether to close on "is my modal still the
* last one?" needs this, or a back-tap during a long operation pops the screen beneath
* as well. Kept separate because [isLastModalOpen] also gates secondary chat teardown,
* which relies on the existing behaviour. */
/** [isLastModalOpen], but a modal already dismissed and only waiting out its close
* animation does not count - [closeModal] leaves it in [modalViews] until then. Separate
* from [isLastModalOpen], which gates secondary chat teardown on the existing behaviour. */
fun isLastModalOpenNotClosing(id: ModalViewId): Boolean {
var i = modalViews.size - 1
while (i >= 0 && i in toRemove) i--