diff --git a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextProfilePickerView.swift b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextProfilePickerView.swift index 691607fade..0c5362df7f 100644 --- a/apps/ios/Shared/Views/Chat/ComposeMessage/ContextProfilePickerView.swift +++ b/apps/ios/Shared/Views/Chat/ComposeMessage/ContextProfilePickerView.swift @@ -54,7 +54,7 @@ struct ContextProfilePickerView: View { NavigationView { CreateProfile(onSubmit: { displayName, shortDescr, image in try await createProfileForChat(displayName, shortDescr, image) - }) + }, submitting: creatingProfile) } .interactiveDismissDisabled(creatingProfile) } diff --git a/apps/ios/Shared/Views/NewChat/NewChatView.swift b/apps/ios/Shared/Views/NewChat/NewChatView.swift index 214b3f4615..7ee916dfca 100644 --- a/apps/ios/Shared/Views/NewChat/NewChatView.swift +++ b/apps/ios/Shared/Views/NewChat/NewChatView.swift @@ -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 diff --git a/apps/ios/Shared/Views/Onboarding/CreateProfile.swift b/apps/ios/Shared/Views/Onboarding/CreateProfile.swift index cf26bf9835..26ee20429b 100644 --- a/apps/ios/Shared/Views/Onboarding/CreateProfile.swift +++ b/apps/ios/Shared/Views/Onboarding/CreateProfile.swift @@ -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.") diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt index 2c0e152a1f..5c8769ec81 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt @@ -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--