mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-28 00:44:27 +00:00
ios: trim the comment bulk
The Swift side had been minimised least: 57 of its added lines were comments, several of them restating what the line below does or narrating what an earlier attempt got wrong. Kept the ones that record a non-obvious why - why the sheet hangs off the Group, why the check-and-set is atomic, why the resync is not rethrown, why the alert is deferred - and cut the rest. Also tried making changeProfile async so the create flow could await it and the existing creatingProfile flag would cover the reassignment, removing changingProfile entirely. It reads better but it takes changeProfile out of its Task and re-indents the whole function, which costs more diff than the flag it saves. Not done.
This commit is contained in:
@@ -47,9 +47,8 @@ struct ContextProfilePickerView: View {
|
||||
profilePicker()
|
||||
}
|
||||
}
|
||||
// On the Group, not the row or profilePicker(): both are disposed while this is
|
||||
// presented - the row by its lazy container, the picker when listExpanded flips.
|
||||
// Stacking with body's IncognitoHelp sheet is fine from iOS 14.5; target is 15.
|
||||
// On the Group: the row and profilePicker() are both disposed while this is
|
||||
// presented. Stacking sheets is supported from iOS 14.5; the target is 15.
|
||||
.sheet(isPresented: $showAddProfile) {
|
||||
NavigationView {
|
||||
CreateProfile(onSubmit: { displayName, shortDescr, image in
|
||||
@@ -177,7 +176,7 @@ struct ContextProfilePickerView: View {
|
||||
listExpanded = false
|
||||
}
|
||||
} else if selectedUser != user {
|
||||
// Only the branch that starts work: expand/collapse is local
|
||||
// Only the branch that starts work; expand/collapse is local
|
||||
if busy { return }
|
||||
changingProfile = true
|
||||
changeProfile(user)
|
||||
@@ -245,13 +244,11 @@ struct ContextProfilePickerView: View {
|
||||
.disabled(busy)
|
||||
}
|
||||
|
||||
// Creates a profile to use for this invitation. It is created without becoming
|
||||
// active, because changeProfile below reassigns the prepared chat and the API
|
||||
// resolves that chat under the currently active user - so the profile that owns the
|
||||
// invitation has to stay active until the chat has been moved.
|
||||
// Created without becoming active: changeProfile below resolves the prepared chat
|
||||
// under the active user, so the profile that owns it must stay active until it moves.
|
||||
private func createProfileForChat(_ displayName: String, _ shortDescr: String?, _ image: String?) async throws {
|
||||
// Atomic check-and-set on the main actor: a plain check-then-set leaves a window
|
||||
// where two submits both pass, and @State must not be read off the main actor.
|
||||
// Atomic check-and-set: check-then-set lets two submits through, and @State
|
||||
// must not be read off the main actor.
|
||||
let alreadyCreating = await MainActor.run { () -> Bool in
|
||||
if creatingProfile { return true }
|
||||
creatingProfile = true
|
||||
@@ -265,15 +262,13 @@ struct ContextProfilePickerView: View {
|
||||
await MainActor.run {
|
||||
if let updatedUsers = updatedUsers {
|
||||
chatModel.users = updatedUsers
|
||||
// Otherwise only filled in onAppear, so the profile just created is
|
||||
// missing from the picker, and the row count the frame uses is one short.
|
||||
// Only filled in onAppear otherwise, so the new profile is missing here
|
||||
users = updatedUsers.map { $0.user }.filter { u in u.activeUser || !u.hidden }
|
||||
}
|
||||
}
|
||||
if newUser.activeUser {
|
||||
// An older remote host ignored keepActiveUser and activated it, so the
|
||||
// reassignment would fail. Resync to what the host did and report it - not
|
||||
// rethrown, or the form reports it as a failure to create the profile.
|
||||
// An older remote host ignored keepActiveUser, so the reassignment would
|
||||
// fail. Resync and report - not rethrown, or the form blames the creation.
|
||||
let switched: Bool
|
||||
do {
|
||||
try await changeActiveUserAsync_(newUser.userId, viewPwd: nil)
|
||||
@@ -283,19 +278,15 @@ struct ContextProfilePickerView: View {
|
||||
switched = false
|
||||
}
|
||||
await MainActor.run {
|
||||
// Dismissed unconditionally: the switch removes this view - and the sheet
|
||||
// it presents - only when it succeeded. If it threw, the form would be
|
||||
// left over a profile that already exists.
|
||||
// Unconditional: the switch only removes this view when it succeeded
|
||||
showAddProfile = false
|
||||
// Only if the switch happened: the chat is then absent from the reloaded
|
||||
// list and would render blank. If it failed, the chat is still fine.
|
||||
// Only if it switched: the chat is then gone from the list and renders blank
|
||||
if switched && chatModel.chatId == chat.id { chatModel.chatId = nil }
|
||||
}
|
||||
alertAfterDismissal(NSLocalizedString("Error changing chat profile", comment: "alert title"))
|
||||
return
|
||||
}
|
||||
// changingProfile here too: the defer above clears creatingProfile as soon as this
|
||||
// returns, and changeProfile's Task has not necessarily started by then.
|
||||
// Here too: the defer clears creatingProfile as soon as this returns
|
||||
await MainActor.run {
|
||||
showAddProfile = false
|
||||
changingProfile = true
|
||||
|
||||
@@ -70,9 +70,8 @@ func showAlert(
|
||||
}
|
||||
}
|
||||
|
||||
/// An alert raised while a sheet is dismissing is presented on a controller that is going
|
||||
/// away, and is dropped - getTopViewController() keeps returning it until the transition
|
||||
/// ends. Use this when the caller has just dismissed something.
|
||||
/// getTopViewController() keeps returning a sheet until its dismissal transition ends, so
|
||||
/// an alert raised right after dismissing one is presented on it and dropped.
|
||||
func alertAfterDismissal(_ title: String, _ message: String? = nil) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
showAlert(title, message: message)
|
||||
|
||||
@@ -580,9 +580,8 @@ private struct ActiveProfilePicker: View {
|
||||
.disabled(busy)
|
||||
}
|
||||
|
||||
// Creates a profile for this invitation without activating it, then routes through
|
||||
// the same selectedProfile path as picking an existing profile, so the connection
|
||||
// change and the switch cannot drift apart.
|
||||
// Created without activating, then routed through the same selectedProfile path as
|
||||
// picking an existing profile, so the connection change and the switch cannot drift.
|
||||
private func createProfileForConnection(_ displayName: String, _ shortDescr: String?, _ image: String?) async throws {
|
||||
// Atomic check-and-set on the main actor: a plain check-then-set leaves a window
|
||||
// where two submits both pass, and @State must not be read off the main actor.
|
||||
@@ -601,21 +600,17 @@ private struct ActiveProfilePicker: View {
|
||||
profiles = chatModel.users.map { $0.user }
|
||||
}
|
||||
if newUser.activeUser {
|
||||
// An older core ignored keepActiveUser and switched instead, so the connection
|
||||
// change would fail. Not rethrown, or the form reports it as a creation failure.
|
||||
// An older core ignored keepActiveUser, so the connection change would fail
|
||||
do {
|
||||
try await changeActiveUserAsync_(newUser.userId, viewPwd: nil)
|
||||
} catch {
|
||||
logger.error("changeActiveUserAsync_ error: \(responseError(error))")
|
||||
}
|
||||
// Dismiss the form, as the compose picker does on the same failure: the app is
|
||||
// now showing a different profile and the connection stayed with the previous
|
||||
// one, so there is nothing left to do here and a second attempt would fail the
|
||||
// same way.
|
||||
// Dismiss: the app now shows a different profile and the connection stayed
|
||||
// with the previous one, so a second attempt would fail the same way.
|
||||
await MainActor.run {
|
||||
showAddProfile = false
|
||||
// Make the picker agree with the profile that is now active rather than
|
||||
// leaving the checkmark on one that is not.
|
||||
// Make the picker agree with the profile that is now active
|
||||
profileSwitchStatus = .idle
|
||||
selectedProfile = newUser
|
||||
}
|
||||
@@ -699,9 +694,7 @@ private struct ActiveProfilePicker: View {
|
||||
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
|
||||
// while every state write landed on a dismissed view.
|
||||
// The submit Task is unstructured and SwiftUI will not cancel it on dismissal
|
||||
.interactiveDismissDisabled(creatingProfile)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user