mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-17 23:18:27 +02:00
Rename completion to callback and simplify actor usage. (#6141)
This commit is contained in:
@@ -131,7 +131,7 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc
|
||||
let parameters = AuthenticationServerSelectionCoordinatorParameters(authenticationService: authenticationService,
|
||||
hasModalPresentation: false)
|
||||
let coordinator = AuthenticationServerSelectionCoordinator(parameters: parameters)
|
||||
coordinator.completion = { [weak self, weak coordinator] result in
|
||||
coordinator.callback = { [weak self, weak coordinator] result in
|
||||
guard let self = self, let coordinator = coordinator else { return }
|
||||
self.serverSelectionCoordinator(coordinator, didCompleteWith: result)
|
||||
}
|
||||
@@ -168,7 +168,7 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc
|
||||
registrationFlow: homeserver.registrationFlow,
|
||||
loginMode: homeserver.preferredLoginMode)
|
||||
let coordinator = AuthenticationRegistrationCoordinator(parameters: parameters)
|
||||
coordinator.completion = { [weak self, weak coordinator] result in
|
||||
coordinator.callback = { [weak self, weak coordinator] result in
|
||||
guard let self = self, let coordinator = coordinator else { return }
|
||||
self.registrationCoordinator(coordinator, didCompleteWith: result)
|
||||
}
|
||||
@@ -189,8 +189,6 @@ final class AuthenticationCoordinator: NSObject, AuthenticationCoordinatorProtoc
|
||||
@MainActor private func registrationCoordinator(_ coordinator: AuthenticationRegistrationCoordinator,
|
||||
didCompleteWith result: AuthenticationRegistrationCoordinatorResult) {
|
||||
switch result {
|
||||
case .selectServer:
|
||||
showServerSelectionScreen()
|
||||
case .completed(let result):
|
||||
handleRegistrationResult(result)
|
||||
}
|
||||
|
||||
@@ -437,7 +437,7 @@ final class OnboardingCoordinator: NSObject, OnboardingCoordinatorProtocol {
|
||||
let parameters = OnboardingAvatarCoordinatorParameters(userSession: userSession, avatar: selectedAvatar)
|
||||
let coordinator = OnboardingAvatarCoordinator(parameters: parameters)
|
||||
|
||||
coordinator.completion = { [weak self, weak coordinator] result in
|
||||
coordinator.callback = { [weak self, weak coordinator] result in
|
||||
guard let self = self, let coordinator = coordinator else { return }
|
||||
|
||||
switch result {
|
||||
|
||||
@@ -27,7 +27,7 @@ class AuthenticationRegistrationViewModel: AuthenticationRegistrationViewModelTy
|
||||
|
||||
// MARK: Public
|
||||
|
||||
@MainActor var completion: ((AuthenticationRegistrationViewModelResult) -> Void)?
|
||||
@MainActor var callback: ((AuthenticationRegistrationViewModelResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@@ -44,25 +44,19 @@ class AuthenticationRegistrationViewModel: AuthenticationRegistrationViewModelTy
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: AuthenticationRegistrationViewAction) {
|
||||
Task {
|
||||
await MainActor.run {
|
||||
switch viewAction {
|
||||
case .selectServer:
|
||||
completion?(.selectServer)
|
||||
case .validateUsername:
|
||||
state.hasEditedUsername = true
|
||||
completion?(.validateUsername(state.bindings.username))
|
||||
case .enablePasswordValidation:
|
||||
state.hasEditedPassword = true
|
||||
case .clearUsernameError:
|
||||
guard state.usernameErrorMessage != nil else { return }
|
||||
state.usernameErrorMessage = nil
|
||||
case .next:
|
||||
completion?(.createAccount(username: state.bindings.username, password: state.bindings.password))
|
||||
case .continueWithSSO(let id):
|
||||
break
|
||||
}
|
||||
}
|
||||
switch viewAction {
|
||||
case .selectServer:
|
||||
Task { await callback?(.selectServer) }
|
||||
case .validateUsername:
|
||||
Task { await validateUsername() }
|
||||
case .enablePasswordValidation:
|
||||
Task { await enablePasswordValidation() }
|
||||
case .clearUsernameError:
|
||||
Task { await clearUsernameError() }
|
||||
case .next:
|
||||
Task { await callback?(.createAccount(username: state.bindings.username, password: state.bindings.password)) }
|
||||
case .continueWithSSO(let id):
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,4 +86,27 @@ class AuthenticationRegistrationViewModel: AuthenticationRegistrationViewModelTy
|
||||
state.bindings.alertInfo = AlertInfo(id: type)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
/// Validate the supplied username with the homeserver.
|
||||
@MainActor private func validateUsername() {
|
||||
if !state.hasEditedUsername {
|
||||
state.hasEditedUsername = true
|
||||
}
|
||||
|
||||
callback?(.validateUsername(state.bindings.username))
|
||||
}
|
||||
|
||||
/// Allows password validation to take place.
|
||||
@MainActor private func enablePasswordValidation() {
|
||||
guard !state.hasEditedPassword else { return }
|
||||
state.hasEditedPassword = true
|
||||
}
|
||||
|
||||
/// Clear any errors being shown in the username text field footer.
|
||||
@MainActor private func clearUsernameError() {
|
||||
guard state.usernameErrorMessage != nil else { return }
|
||||
state.usernameErrorMessage = nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import Foundation
|
||||
|
||||
protocol AuthenticationRegistrationViewModelProtocol {
|
||||
|
||||
@MainActor var completion: ((AuthenticationRegistrationViewModelResult) -> Void)? { get set }
|
||||
@MainActor var callback: ((AuthenticationRegistrationViewModelResult) -> Void)? { get set }
|
||||
var context: AuthenticationRegistrationViewModelType.Context { get }
|
||||
|
||||
/// Update the view with new homeserver information.
|
||||
|
||||
@@ -28,8 +28,6 @@ struct AuthenticationRegistrationCoordinatorParameters {
|
||||
}
|
||||
|
||||
enum AuthenticationRegistrationCoordinatorResult {
|
||||
/// The user would like to select another server.
|
||||
case selectServer
|
||||
/// The screen completed with the associated registration result.
|
||||
case completed(RegistrationResult)
|
||||
}
|
||||
@@ -63,7 +61,7 @@ final class AuthenticationRegistrationCoordinator: Coordinator, Presentable {
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
@MainActor var completion: ((AuthenticationRegistrationCoordinatorResult) -> Void)?
|
||||
@MainActor var callback: ((AuthenticationRegistrationCoordinatorResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@@ -87,23 +85,8 @@ final class AuthenticationRegistrationCoordinator: Coordinator, Presentable {
|
||||
|
||||
// MARK: - Public
|
||||
func start() {
|
||||
Task {
|
||||
await MainActor.run {
|
||||
MXLog.debug("[AuthenticationRegistrationCoordinator] did start.")
|
||||
authenticationRegistrationViewModel.completion = { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
MXLog.debug("[AuthenticationRegistrationCoordinator] AuthenticationRegistrationViewModel did complete with result: \(result).")
|
||||
switch result {
|
||||
case .selectServer:
|
||||
self.presentServerSelectionScreen()
|
||||
case.validateUsername(let username):
|
||||
self.validateUsername(username)
|
||||
case .createAccount(let username, let password):
|
||||
self.createAccount(username: username, password: password)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MXLog.debug("[AuthenticationRegistrationCoordinator] did start.")
|
||||
Task { await setupViewModel() }
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
@@ -112,6 +95,22 @@ final class AuthenticationRegistrationCoordinator: Coordinator, Presentable {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
/// Set up the view model. This method is extracted from `start()` so it can run on the `MainActor`.
|
||||
@MainActor private func setupViewModel() {
|
||||
authenticationRegistrationViewModel.callback = { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
MXLog.debug("[AuthenticationRegistrationCoordinator] AuthenticationRegistrationViewModel did complete with result: \(result).")
|
||||
switch result {
|
||||
case .selectServer:
|
||||
self.presentServerSelectionScreen()
|
||||
case.validateUsername(let username):
|
||||
self.validateUsername(username)
|
||||
case .createAccount(let username, let password):
|
||||
self.createAccount(username: username, password: password)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Show a blocking activity indicator whilst saving.
|
||||
@MainActor private func startLoading(label: String? = nil) {
|
||||
waitingIndicator = indicatorPresenter.present(.loading(label: label ?? VectorL10n.loading, isInteractionBlocking: true))
|
||||
@@ -160,7 +159,7 @@ final class AuthenticationRegistrationCoordinator: Coordinator, Presentable {
|
||||
let result = try await registrationWizard.createAccount(username: username, password: password, initialDeviceDisplayName: deviceDisplayName)
|
||||
|
||||
guard !Task.isCancelled else { return }
|
||||
completion?(.completed(result))
|
||||
callback?(.completed(result))
|
||||
|
||||
self?.stopLoading()
|
||||
} catch {
|
||||
@@ -211,7 +210,7 @@ final class AuthenticationRegistrationCoordinator: Coordinator, Presentable {
|
||||
let parameters = AuthenticationServerSelectionCoordinatorParameters(authenticationService: authenticationService,
|
||||
hasModalPresentation: true)
|
||||
let coordinator = AuthenticationServerSelectionCoordinator(parameters: parameters)
|
||||
coordinator.completion = { [weak self, weak coordinator] result in
|
||||
coordinator.callback = { [weak self, weak coordinator] result in
|
||||
guard let self = self, let coordinator = coordinator else { return }
|
||||
self.serverSelectionCoordinator(coordinator, didCompleteWith: result)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ class AuthenticationServerSelectionViewModel: AuthenticationServerSelectionViewM
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var completion: ((AuthenticationServerSelectionViewModelResult) -> Void)?
|
||||
@MainActor var callback: ((AuthenticationServerSelectionViewModelResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@@ -41,20 +41,15 @@ class AuthenticationServerSelectionViewModel: AuthenticationServerSelectionViewM
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: AuthenticationServerSelectionViewAction) {
|
||||
Task {
|
||||
await MainActor.run {
|
||||
switch viewAction {
|
||||
case .confirm:
|
||||
completion?(.confirm(homeserverAddress: state.bindings.homeserverAddress))
|
||||
case .dismiss:
|
||||
completion?(.dismiss)
|
||||
case .getInTouch:
|
||||
getInTouch()
|
||||
case .clearFooterError:
|
||||
guard state.footerErrorMessage != nil else { return }
|
||||
withAnimation { state.footerErrorMessage = nil }
|
||||
}
|
||||
}
|
||||
switch viewAction {
|
||||
case .confirm:
|
||||
Task { await callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress)) }
|
||||
case .dismiss:
|
||||
Task { await callback?(.dismiss) }
|
||||
case .getInTouch:
|
||||
Task { await getInTouch() }
|
||||
case .clearFooterError:
|
||||
Task { await clearFooterError() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +66,12 @@ class AuthenticationServerSelectionViewModel: AuthenticationServerSelectionViewM
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
/// Clear any errors shown in the text field footer.
|
||||
@MainActor private func clearFooterError() {
|
||||
guard state.footerErrorMessage != nil else { return }
|
||||
withAnimation { state.footerErrorMessage = nil }
|
||||
}
|
||||
|
||||
/// Opens the EMS link in the user's browser.
|
||||
@MainActor private func getInTouch() {
|
||||
let url = BuildSettings.onboardingHostYourOwnServerLink
|
||||
|
||||
@@ -18,7 +18,7 @@ import Foundation
|
||||
|
||||
protocol AuthenticationServerSelectionViewModelProtocol {
|
||||
|
||||
@MainActor var completion: ((AuthenticationServerSelectionViewModelResult) -> Void)? { get set }
|
||||
@MainActor var callback: ((AuthenticationServerSelectionViewModelResult) -> Void)? { get set }
|
||||
var context: AuthenticationServerSelectionViewModelType.Context { get }
|
||||
|
||||
/// Displays an error to the user.
|
||||
|
||||
@@ -48,7 +48,7 @@ final class AuthenticationServerSelectionCoordinator: Coordinator, Presentable {
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
@MainActor var completion: ((AuthenticationServerSelectionCoordinatorResult) -> Void)?
|
||||
@MainActor var callback: ((AuthenticationServerSelectionCoordinatorResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@@ -70,22 +70,8 @@ final class AuthenticationServerSelectionCoordinator: Coordinator, Presentable {
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
Task {
|
||||
await MainActor.run {
|
||||
MXLog.debug("[AuthenticationServerSelectionCoordinator] did start.")
|
||||
authenticationServerSelectionViewModel.completion = { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
MXLog.debug("[AuthenticationServerSelectionCoordinator] AuthenticationServerSelectionViewModel did complete with result: \(result).")
|
||||
|
||||
switch result {
|
||||
case .confirm(let homeserverAddress):
|
||||
self.useHomeserver(homeserverAddress)
|
||||
case .dismiss:
|
||||
self.completion?(.dismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
MXLog.debug("[AuthenticationServerSelectionCoordinator] did start.")
|
||||
Task { await setupViewModel() }
|
||||
}
|
||||
|
||||
func toPresentable() -> UIViewController {
|
||||
@@ -94,6 +80,21 @@ final class AuthenticationServerSelectionCoordinator: Coordinator, Presentable {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
/// Set up the view model. This method is extracted from `start()` so it can run on the `MainActor`.
|
||||
@MainActor private func setupViewModel() {
|
||||
authenticationServerSelectionViewModel.callback = { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
MXLog.debug("[AuthenticationServerSelectionCoordinator] AuthenticationServerSelectionViewModel did complete with result: \(result).")
|
||||
|
||||
switch result {
|
||||
case .confirm(let homeserverAddress):
|
||||
self.useHomeserver(homeserverAddress)
|
||||
case .dismiss:
|
||||
self.callback?(.dismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Show an activity indicator whilst loading.
|
||||
/// - Parameters:
|
||||
/// - label: The label to show on the indicator.
|
||||
@@ -120,7 +121,7 @@ final class AuthenticationServerSelectionCoordinator: Coordinator, Presentable {
|
||||
try await authenticationService.startFlow(.register, for: homeserverAddress)
|
||||
stopLoading()
|
||||
|
||||
completion?(.updated)
|
||||
callback?(.updated)
|
||||
} catch {
|
||||
stopLoading()
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ struct OnboardingAvatarCoordinatorParameters {
|
||||
}
|
||||
|
||||
enum OnboardingAvatarCoordinatorResult {
|
||||
/// The user has chosen an image (but hasn't yet saved it).
|
||||
/// The user has chosen an image (but it won't be uploaded until `.complete` is sent).
|
||||
/// This result is to cache the image in the flow coordinator so it can be restored if the user was to navigate backwards.
|
||||
case selectedAvatar(UIImage?)
|
||||
/// The screen is finished and the next one can be shown.
|
||||
case complete(UserSession)
|
||||
@@ -63,7 +64,7 @@ final class OnboardingAvatarCoordinator: Coordinator, Presentable {
|
||||
|
||||
// Must be used only internally
|
||||
var childCoordinators: [Coordinator] = []
|
||||
var completion: ((OnboardingAvatarCoordinatorResult) -> Void)?
|
||||
var callback: ((OnboardingAvatarCoordinatorResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@@ -88,7 +89,7 @@ final class OnboardingAvatarCoordinator: Coordinator, Presentable {
|
||||
|
||||
func start() {
|
||||
MXLog.debug("[OnboardingAvatarCoordinator] did start.")
|
||||
onboardingAvatarViewModel.completion = { [weak self] result in
|
||||
onboardingAvatarViewModel.callback = { [weak self] result in
|
||||
guard let self = self else { return }
|
||||
MXLog.debug("[OnboardingAvatarCoordinator] OnboardingAvatarViewModel did complete with result: \(result).")
|
||||
switch result {
|
||||
@@ -99,7 +100,7 @@ final class OnboardingAvatarCoordinator: Coordinator, Presentable {
|
||||
case .save(let avatar):
|
||||
self.setAvatar(avatar)
|
||||
case .skip:
|
||||
self.completion?(.complete(self.parameters.userSession))
|
||||
self.callback?(.complete(self.parameters.userSession))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +162,7 @@ final class OnboardingAvatarCoordinator: Coordinator, Presentable {
|
||||
self.parameters.userSession.account.setUserAvatarUrl(urlString) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.stopWaiting()
|
||||
self.completion?(.complete(self.parameters.userSession))
|
||||
self.callback?(.complete(self.parameters.userSession))
|
||||
} failure: { [weak self] error in
|
||||
guard let self = self else { return }
|
||||
self.stopWaiting()
|
||||
@@ -182,7 +183,7 @@ extension OnboardingAvatarCoordinator: MediaPickerPresenterDelegate {
|
||||
/// so whilst this method may not appear to be called, everything works fine when run on a device.
|
||||
func mediaPickerPresenter(_ presenter: MediaPickerPresenter, didPickImage image: UIImage) {
|
||||
onboardingAvatarViewModel.updateAvatarImage(with: image)
|
||||
completion?(.selectedAvatar(image))
|
||||
callback?(.selectedAvatar(image))
|
||||
presenter.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
@@ -196,7 +197,7 @@ extension OnboardingAvatarCoordinator: MediaPickerPresenterDelegate {
|
||||
extension OnboardingAvatarCoordinator: CameraPresenterDelegate {
|
||||
func cameraPresenter(_ presenter: CameraPresenter, didSelectImage image: UIImage) {
|
||||
onboardingAvatarViewModel.updateAvatarImage(with: image)
|
||||
completion?(.selectedAvatar(image))
|
||||
callback?(.selectedAvatar(image))
|
||||
presenter.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class OnboardingAvatarViewModel: OnboardingAvatarViewModelType, OnboardingAvatar
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var completion: ((OnboardingAvatarViewModelResult) -> Void)?
|
||||
var callback: ((OnboardingAvatarViewModelResult) -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
@@ -46,13 +46,13 @@ class OnboardingAvatarViewModel: OnboardingAvatarViewModelType, OnboardingAvatar
|
||||
override func process(viewAction: OnboardingAvatarViewAction) {
|
||||
switch viewAction {
|
||||
case .pickImage:
|
||||
completion?(.pickImage)
|
||||
callback?(.pickImage)
|
||||
case .takePhoto:
|
||||
completion?(.takePhoto)
|
||||
callback?(.takePhoto)
|
||||
case .save:
|
||||
completion?(.save(state.avatar))
|
||||
callback?(.save(state.avatar))
|
||||
case .skip:
|
||||
completion?(.skip)
|
||||
callback?(.skip)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import SwiftUI
|
||||
|
||||
protocol OnboardingAvatarViewModelProtocol {
|
||||
|
||||
var completion: ((OnboardingAvatarViewModelResult) -> Void)? { get set }
|
||||
var callback: ((OnboardingAvatarViewModelResult) -> Void)? { get set }
|
||||
var context: OnboardingAvatarViewModelType.Context { get }
|
||||
|
||||
/// Update the view model to show the image that the user has picked.
|
||||
|
||||
1
changelog.d/pr-6141.wip
Normal file
1
changelog.d/pr-6141.wip
Normal file
@@ -0,0 +1 @@
|
||||
Onboarding: Rename completion to callback and simplify actor usage
|
||||
Reference in New Issue
Block a user