mastodon-ios/Mastodon/Scene/Onboarding/Register/MastodonRegisterViewControl...

257 lines
9.9 KiB
Swift
Raw Normal View History

2021-02-05 10:53:00 +01:00
//
// MastodonRegisterViewController.swift
// Mastodon
//
// Created by MainasuK Cirno on 2021-2-5.
//
import AlamofireImage
2021-02-05 10:53:00 +01:00
import Combine
import MastodonSDK
import PhotosUI
2021-02-20 06:55:06 +01:00
import UIKit
import SwiftUI
import MastodonUI
import MastodonAsset
2022-10-08 07:43:06 +02:00
import MastodonCore
import MastodonLocalization
2021-02-05 10:53:00 +01:00
2021-02-25 08:39:48 +01:00
final class MastodonRegisterViewController: UIViewController, NeedsDependency, OnboardingViewControllerAppearance {
2021-04-09 11:31:43 +02:00
static let avatarImageMaxSizeInPixel = CGSize(width: 400, height: 400)
2021-02-05 10:53:00 +01:00
var disposeBag = Set<AnyCancellable>()
2022-01-07 11:49:37 +01:00
private var observations = Set<NSKeyValueObservation>()
2021-02-05 10:53:00 +01:00
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
var viewModel: MastodonRegisterViewModel!
private(set) lazy var mastodonRegisterView = MastodonRegisterView(viewModel: viewModel)
2022-12-19 11:29:18 +01:00
var activityIndicator: UIActivityIndicatorView = {
let activityIndicator = UIActivityIndicatorView(style: .medium)
activityIndicator.color = Asset.Colors.Brand.blurple.color
return activityIndicator
}()
func nextBarButtonItem() -> UIBarButtonItem {
2022-12-19 12:26:31 +01:00
return UIBarButtonItem(title: L10n.Common.Controls.Actions.next, style: .done, target: self, action: #selector(MastodonRegisterViewController.nextButtonPressed(_:)))
2022-12-19 11:29:18 +01:00
}
2021-02-05 10:53:00 +01:00
}
extension MastodonRegisterViewController {
2021-02-05 10:53:00 +01:00
override func viewDidLoad() {
super.viewDidLoad()
setupOnboardingAppearance()
viewModel.backgroundColor = view.backgroundColor ?? .clear
defer {
setupNavigationBarBackgroundView()
}
2021-02-25 08:39:48 +01:00
let hostingViewController = UIHostingController(rootView: mastodonRegisterView)
hostingViewController.view.preservesSuperviewLayoutMargins = true
addChild(hostingViewController)
hostingViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(hostingViewController.view)
hostingViewController.view.pinToParent()
hostingViewController.view.backgroundColor = view.backgroundColor
2022-12-19 11:29:18 +01:00
navigationItem.rightBarButtonItem = nextBarButtonItem()
2022-01-07 11:49:37 +01:00
viewModel.$isAllValid
.receive(on: DispatchQueue.main)
.sink { [weak self] isAllValid in
guard let self = self else { return }
self.navigationItem.rightBarButtonItem?.isEnabled = isAllValid
}
.store(in: &disposeBag)
viewModel.endEditing
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
guard let self = self else { return }
self.view.endEditing(true)
}
.store(in: &disposeBag)
2022-01-07 11:49:37 +01:00
// // return
// if viewModel.approvalRequired {
// reasonTextField.returnKeyType = .done
// } else {
// passwordTextField.returnKeyType = .done
// }
2022-04-26 10:48:06 +02:00
viewModel.$error
.receive(on: DispatchQueue.main)
.sink { [weak self] error in
guard let self = self else { return }
guard let error = error as? Mastodon.API.Error else { return }
2022-12-19 11:47:15 +01:00
let alertController = UIAlertController(for: error, title: L10n.Common.Alerts.SignUpFailure.title, preferredStyle: .alert)
2022-04-26 10:48:06 +02:00
let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
alertController.addAction(okAction)
_ = self.coordinator.present(
2022-04-26 10:48:06 +02:00
scene: .alertController(alertController: alertController),
from: nil,
transition: .alertController(animated: true, completion: nil)
)
}
.store(in: &disposeBag)
2022-01-07 11:49:37 +01:00
2022-12-19 11:29:18 +01:00
viewModel.$isRegistering
.receive(on: DispatchQueue.main)
.sink { [weak self] isRegistering in
guard let self = self else { return }
let rightBarButtonItem: UIBarButtonItem
if isRegistering {
self.activityIndicator.startAnimating()
rightBarButtonItem = UIBarButtonItem(customView: self.activityIndicator)
rightBarButtonItem.isEnabled = false
} else {
self.activityIndicator.stopAnimating()
rightBarButtonItem = self.nextBarButtonItem()
}
self.navigationItem.rightBarButtonItem = rightBarButtonItem
}
.store(in: &disposeBag)
title = L10n.Scene.Register.title
2021-02-05 10:53:00 +01:00
}
2022-01-07 11:49:37 +01:00
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
2021-02-22 10:48:44 +01:00
2022-01-07 11:49:37 +01:00
viewModel.viewDidAppear.send()
}
2022-12-19 11:29:18 +01:00
//MARK: - Actions
2022-01-07 11:49:37 +01:00
@objc private func nextButtonPressed(_ sender: UIButton) {
guard viewModel.isAllValid else { return }
guard !viewModel.isRegistering else { return }
viewModel.isRegistering = true
let username = viewModel.username
let email = viewModel.email
let password = viewModel.password
let reason = viewModel.reason
let locale: String = {
guard let url = Bundle.main.url(forResource: "local-codes", withExtension: "json"),
let data = try? Data(contentsOf: url),
let localCode = try? JSONDecoder().decode(MastodonLocalCode.self, from: data)
else {
assertionFailure()
return "en"
}
let fallbackLanguageCode: String = {
let code = Locale.current.languageCode ?? "en"
guard localCode[code] != nil else { return "en" }
return code
}()
2022-01-07 11:49:37 +01:00
// pick device preferred language
guard let identifier = Locale.preferredLanguages.first else {
return fallbackLanguageCode
}
// prepare languageCode and validate then return fallback if needs
let local = Locale(identifier: identifier)
guard let languageCode = local.languageCode,
localCode[languageCode] != nil
else {
return fallbackLanguageCode
}
// prepare extendCode and validate then return fallback if needs
let extendCodes: [String] = {
let locales = Locale.preferredLanguages.map { Locale(identifier: $0) }
return locales.compactMap { locale in
guard let languageCode = locale.languageCode,
let regionCode = locale.regionCode
else { return nil }
return languageCode + "-" + regionCode
}
}()
let _firstMatchExtendCode = extendCodes.first { code in
localCode[code] != nil
}
guard let firstMatchExtendCode = _firstMatchExtendCode else {
return languageCode
}
return firstMatchExtendCode
2022-01-07 11:49:37 +01:00
}()
2021-02-26 11:27:47 +01:00
let query = Mastodon.API.Account.RegisterQuery(
2022-01-07 11:49:37 +01:00
reason: reason,
2021-02-26 11:27:47 +01:00
username: username,
email: email,
password: password,
agreement: true, // user confirmed in the server rules scene
locale: locale
2021-02-26 11:27:47 +01:00
)
2022-01-07 11:49:37 +01:00
var retryCount = 0
2022-01-07 11:49:37 +01:00
// register without show server rules
context.apiService.accountRegister(
domain: viewModel.domain,
query: query,
authorization: viewModel.applicationAuthorization
)
.tryCatch { [weak self] error -> AnyPublisher<Mastodon.Response.Content<Mastodon.Entity.Token>, Error> in
guard let self = self else { throw error }
2022-01-07 11:49:37 +01:00
guard let error = self.viewModel.error as? Mastodon.API.Error,
case let .generic(errorEntity) = error.mastodonError,
errorEntity.error == "Validation failed: Locale is not included in the list"
else {
throw error
}
guard retryCount == 0 else {
throw error
}
let retryQuery = Mastodon.API.Account.RegisterQuery(
reason: query.reason,
username: query.username,
email: query.email,
password: query.password,
agreement: query.agreement,
locale: self.viewModel.instance.languages?.first ?? "en"
)
retryCount += 1
return self.context.apiService.accountRegister(
domain: self.viewModel.domain,
query: retryQuery,
authorization: self.viewModel.applicationAuthorization
)
}
.receive(on: DispatchQueue.main)
.sink { [weak self] completion in
guard let self = self else { return }
2022-01-07 11:49:37 +01:00
self.viewModel.isRegistering = false
switch completion {
case .failure(let error):
2022-01-07 11:49:37 +01:00
self.viewModel.error = error
case .finished:
break
2021-02-26 11:27:47 +01:00
}
} receiveValue: { [weak self] response in
guard let self = self else { return }
let userToken = response.value
let updateCredentialQuery: Mastodon.API.Account.UpdateCredentialQuery = {
2022-01-07 11:49:37 +01:00
let displayName: String? = self.viewModel.name.isEmpty ? nil : self.viewModel.name
return Mastodon.API.Account.UpdateCredentialQuery(
displayName: displayName,
2022-12-18 17:42:11 +01:00
avatar: nil
)
}()
let viewModel = MastodonConfirmEmailViewModel(context: self.context, email: email, authenticateInfo: self.viewModel.authenticateInfo, userToken: userToken, updateCredentialQuery: updateCredentialQuery)
2022-11-10 17:52:48 +01:00
_ = self.coordinator.present(scene: .mastodonConfirmEmail(viewModel: viewModel), from: self, transition: .show)
2021-02-22 09:20:44 +01:00
}
.store(in: &disposeBag)
2021-02-05 10:53:00 +01:00
}
2022-01-07 11:49:37 +01:00
}