Swiftgram/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift
Ilya Laktyushin bf94ea75e9 Various fixes
2023-02-06 00:55:17 +04:00

1215 lines
78 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Display
import Postbox
import TelegramCore
import SwiftSignalKit
import MtProtoKit
import MessageUI
import CoreTelephony
import TelegramPresentationData
import TextFormat
import AccountContext
import CountrySelectionUI
import PhoneNumberFormat
import LegacyComponents
import LegacyMediaPickerUI
import PasswordSetupUI
import TelegramNotices
import AuthenticationServices
private enum InnerState: Equatable {
case state(UnauthorizedAccountStateContents)
case authorized
}
public final class AuthorizationSequenceController: NavigationController, MFMailComposeViewControllerDelegate, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
static func navigationBarTheme(_ theme: PresentationTheme) -> NavigationBarTheme {
return NavigationBarTheme(buttonColor: theme.intro.accentTextColor, disabledButtonColor: theme.intro.disabledTextColor, primaryTextColor: theme.intro.primaryTextColor, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: theme.rootController.navigationBar.badgeBackgroundColor, badgeStrokeColor: theme.rootController.navigationBar.badgeStrokeColor, badgeTextColor: theme.rootController.navigationBar.badgeTextColor)
}
private let sharedContext: SharedAccountContext
private var account: UnauthorizedAccount
private let otherAccountPhoneNumbers: ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)])
private let apiId: Int32
private let apiHash: String
public var presentationData: PresentationData
private let openUrl: (String) -> Void
private let authorizationCompleted: () -> Void
private var stateDisposable: Disposable?
private let actionDisposable = MetaDisposable()
private var didPlayPresentationAnimation = false
private let _ready = Promise<Bool>()
override public var ready: Promise<Bool> {
return self._ready
}
private var didSetReady = false
public init(sharedContext: SharedAccountContext, account: UnauthorizedAccount, otherAccountPhoneNumbers: ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)]), presentationData: PresentationData, openUrl: @escaping (String) -> Void, apiId: Int32, apiHash: String, authorizationCompleted: @escaping () -> Void) {
self.sharedContext = sharedContext
self.account = account
self.otherAccountPhoneNumbers = otherAccountPhoneNumbers
self.apiId = apiId
self.apiHash = apiHash
self.presentationData = presentationData
self.openUrl = openUrl
self.authorizationCompleted = authorizationCompleted
let navigationStatusBar: NavigationStatusBarStyle
switch presentationData.theme.rootController.statusBarStyle {
case .black:
navigationStatusBar = .black
case .white:
navigationStatusBar = .white
}
super.init(mode: .single, theme: NavigationControllerTheme(statusBar: navigationStatusBar, navigationBar: AuthorizationSequenceController.navigationBarTheme(presentationData.theme), emptyAreaColor: .black), isFlat: true)
self.stateDisposable = (TelegramEngineUnauthorized(account: self.account).auth.state()
|> map { state -> InnerState in
if case .authorized = state {
return .authorized
} else if case let .unauthorized(state) = state {
return .state(state.contents)
} else {
return .state(.empty)
}
}
|> distinctUntilChanged
|> deliverOnMainQueue).start(next: { [weak self] state in
self?.updateState(state: state)
})
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.stateDisposable?.dispose()
self.actionDisposable.dispose()
}
override public func loadView() {
super.loadView()
self.view.backgroundColor = self.presentationData.theme.list.plainBackgroundColor
}
private func splashController() -> AuthorizationSequenceSplashController {
var currentController: AuthorizationSequenceSplashController?
for c in self.viewControllers {
if let c = c as? AuthorizationSequenceSplashController {
currentController = c
break
}
}
let controller: AuthorizationSequenceSplashController
if let currentController = currentController {
controller = currentController
} else {
controller = AuthorizationSequenceSplashController(accountManager: self.sharedContext.accountManager, account: self.account, theme: self.presentationData.theme)
controller.nextPressed = { [weak self] strings in
if let strongSelf = self {
if let strings = strings {
strongSelf.presentationData = strongSelf.presentationData.withStrings(strings)
}
let masterDatacenterId = strongSelf.account.masterDatacenterId
let isTestingEnvironment = strongSelf.account.testingEnvironment
let countryCode = AuthorizationSequenceController.defaultCountryCode()
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: isTestingEnvironment, masterDatacenterId: masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))).start()
}
}
}
return controller
}
private func phoneEntryController(countryCode: Int32, number: String, splashController: AuthorizationSequenceSplashController?) -> AuthorizationSequencePhoneEntryController {
var currentController: AuthorizationSequencePhoneEntryController?
for c in self.viewControllers {
if let c = c as? AuthorizationSequencePhoneEntryController {
currentController = c
break
}
}
let controller: AuthorizationSequencePhoneEntryController
if let currentController = currentController {
controller = currentController
} else {
controller = AuthorizationSequencePhoneEntryController(sharedContext: self.sharedContext, account: self.account, isTestingEnvironment: self.account.testingEnvironment, otherAccountPhoneNumbers: self.otherAccountPhoneNumbers, network: self.account.network, presentationData: self.presentationData, openUrl: { [weak self] url in
self?.openUrl(url)
}, back: { [weak self] in
guard let strongSelf = self else {
return
}
if !strongSelf.otherAccountPhoneNumbers.1.isEmpty {
let _ = (strongSelf.sharedContext.accountManager.transaction { transaction -> Void in
transaction.removeAuth()
}).start()
} else {
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .empty)).start()
}
})
if let splashController = splashController {
controller.animateWithSplashController(splashController)
}
controller.accountUpdated = { [weak self] updatedAccount in
guard let strongSelf = self else {
return
}
strongSelf.account = updatedAccount
}
controller.loginWithNumber = { [weak self, weak controller] number, syncContacts in
guard let self else {
return
}
let authorizationPushConfiguration = self.sharedContext.authorizationPushConfiguration
|> take(1)
|> timeout(2.0, queue: .mainQueue(), alternate: .single(nil))
let _ = (authorizationPushConfiguration
|> deliverOnMainQueue).start(next: { [weak self] authorizationPushConfiguration in
if let strongSelf = self {
controller?.inProgress = true
strongSelf.actionDisposable.set((sendAuthorizationCode(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, phoneNumber: number, apiId: strongSelf.apiId, apiHash: strongSelf.apiHash, pushNotificationConfiguration: authorizationPushConfiguration, firebaseSecretStream: strongSelf.sharedContext.firebaseSecretStream, syncContacts: syncContacts, forcedPasswordSetupNotice: { value in
guard let entry = CodableEntry(ApplicationSpecificCounterNotice(value: value)) else {
return nil
}
return (ApplicationSpecificNotice.forcedPasswordSetupKey(), entry)
}) |> deliverOnMainQueue).start(next: { [weak self] result in
if let strongSelf = self {
switch result {
case let .sentCode(account):
controller?.inProgress = false
strongSelf.account = account
case .loggedIn:
break
}
}
}, error: { error in
if let strongSelf = self, let controller = controller {
controller.inProgress = false
let text: String
var actions: [TextAlertAction] = []
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.Login_CodeFloodError
actions.append(TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {}))
case .invalidPhoneNumber:
text = strongSelf.presentationData.strings.Login_InvalidPhoneError
actions.append(TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {}))
actions.append(TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Login_PhoneNumberHelp, action: { [weak controller] in
guard let strongSelf = self, let controller = controller else {
return
}
let formattedNumber = formatPhoneNumber(number)
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
let systemVersion = UIDevice.current.systemVersion
let locale = Locale.current.identifier
let carrier = CTCarrier()
let mnc = carrier.mobileNetworkCode ?? "none"
strongSelf.presentEmailComposeController(address: "login@stel.com", subject: strongSelf.presentationData.strings.Login_InvalidPhoneEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_InvalidPhoneEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller)
}))
case .phoneLimitExceeded:
text = strongSelf.presentationData.strings.Login_PhoneFloodError
actions.append(TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {}))
case .phoneBanned:
text = strongSelf.presentationData.strings.Login_PhoneBannedError
actions.append(TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {}))
actions.append(TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Login_PhoneNumberHelp, action: { [weak controller] in
guard let strongSelf = self, let controller = controller else {
return
}
let formattedNumber = formatPhoneNumber(number)
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
let systemVersion = UIDevice.current.systemVersion
let locale = Locale.current.identifier
let carrier = CTCarrier()
let mnc = carrier.mobileNetworkCode ?? "none"
strongSelf.presentEmailComposeController(address: "login@stel.com", subject: strongSelf.presentationData.strings.Login_PhoneBannedEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneBannedEmailBody(formattedNumber, appVersion, systemVersion, locale, mnc).string, from: controller)
}))
case let .generic(info):
text = strongSelf.presentationData.strings.Login_UnknownError
actions.append(TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {}))
actions.append(TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Login_PhoneNumberHelp, action: { [weak controller] in
guard let strongSelf = self, let controller = controller else {
return
}
let formattedNumber = formatPhoneNumber(number)
let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown"
let systemVersion = UIDevice.current.systemVersion
let locale = Locale.current.identifier
let carrier = CTCarrier()
let mnc = carrier.mobileNetworkCode ?? "none"
let errorString: String
if let (code, description) = info {
errorString = "\(code): \(description)"
} else {
errorString = "unknown"
}
strongSelf.presentEmailComposeController(address: "login@stel.com", subject: strongSelf.presentationData.strings.Login_PhoneGenericEmailSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_PhoneGenericEmailBody(formattedNumber, errorString, appVersion, systemVersion, locale, mnc).string, from: controller)
}))
case .timeout:
text = strongSelf.presentationData.strings.Login_NetworkError
actions.append(TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {}))
actions.append(TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.ChatSettings_ConnectionType_UseProxy, action: { [weak controller] in
guard let strongSelf = self, let controller = controller else {
return
}
controller.present(strongSelf.sharedContext.makeProxySettingsController(sharedContext: strongSelf.sharedContext, account: strongSelf.account), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}))
}
(controller.navigationController as? NavigationController)?.presentOverlay(controller: standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: actions), inGlobal: true, blockInteraction: true)
controller.dismissConfirmation()
}
}))
}
})
}
}
controller.updateData(countryCode: countryCode, countryName: nil, number: number)
return controller
}
private func codeEntryController(number: String, email: String?, type: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) -> AuthorizationSequenceCodeEntryController {
var currentController: AuthorizationSequenceCodeEntryController?
for c in self.viewControllers {
if let c = c as? AuthorizationSequenceCodeEntryController {
if c.data?.2 == type {
currentController = c
}
break
}
}
let controller: AuthorizationSequenceCodeEntryController
if let currentController = currentController {
controller = currentController
} else {
controller = AuthorizationSequenceCodeEntryController(presentationData: self.presentationData, back: { [weak self] in
guard let strongSelf = self else {
return
}
let countryCode = AuthorizationSequenceController.defaultCountryCode()
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))).start()
})
controller.loginWithCode = { [weak self, weak controller] code in
if let strongSelf = self {
controller?.inProgress = true
let authorizationCode: AuthorizationCode
switch type {
case .email:
authorizationCode = .emailVerification(.emailCode(code))
default:
authorizationCode = .phoneCode(code)
}
if case let .email(_, _, _, _, setup) = type, setup, case let .emailVerification(emailCode) = authorizationCode {
strongSelf.actionDisposable.set(((verifyLoginEmailSetup(account: strongSelf.account, code: emailCode))
|> deliverOnMainQueue).start(error: { error in
Queue.mainQueue().async {
if let strongSelf = self, let controller = controller {
controller.inProgress = false
if case .invalidCode = error {
controller.animateError(text: strongSelf.presentationData.strings.Login_WrongCodeError)
} else {
var resetCode = false
let text: String
switch error {
case .limitExceeded:
resetCode = true
text = strongSelf.presentationData.strings.Login_CodeFloodError
case .invalidCode:
resetCode = true
text = strongSelf.presentationData.strings.Login_InvalidCodeError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
case .codeExpired:
text = strongSelf.presentationData.strings.Login_CodeExpired
let account = strongSelf.account
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start()
case .timeout:
text = strongSelf.presentationData.strings.Login_NetworkError
case .invalidEmailToken:
text = strongSelf.presentationData.strings.Login_InvalidEmailTokenError
case .emailNotAllowed:
text = strongSelf.presentationData.strings.Login_EmailNotAllowedError
}
if resetCode {
controller.resetCode()
}
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}
}
}))
} else {
strongSelf.actionDisposable.set((authorizeWithCode(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, code: authorizationCode, termsOfService: termsOfService?.0, forcedPasswordSetupNotice: { value in
guard let entry = CodableEntry(ApplicationSpecificCounterNotice(value: value)) else {
return nil
}
return (ApplicationSpecificNotice.forcedPasswordSetupKey(), entry)
})
|> deliverOnMainQueue).start(next: { result in
guard let strongSelf = self else {
return
}
controller?.inProgress = false
switch result {
case let .signUp(data):
if let (termsOfService, explicit) = termsOfService, explicit {
var presentAlertAgainImpl: (() -> Void)?
let presentAlertImpl: () -> Void = {
guard let strongSelf = self else {
return
}
var dismissImpl: (() -> Void)?
let alertTheme = AlertControllerTheme(presentationData: strongSelf.presentationData)
let attributedText = stringWithAppliedEntities(termsOfService.text, entities: termsOfService.entities, baseColor: alertTheme.primaryColor, linkColor: alertTheme.accentColor, baseFont: Font.regular(13.0), linkFont: Font.regular(13.0), boldFont: Font.semibold(13.0), italicFont: Font.italic(13.0), boldItalicFont: Font.semiboldItalic(13.0), fixedFont: Font.regular(13.0), blockQuoteFont: Font.regular(13.0), message: nil)
let contentNode = TextAlertContentNode(theme: alertTheme, title: NSAttributedString(string: strongSelf.presentationData.strings.Login_TermsOfServiceHeader, font: Font.medium(17.0), textColor: alertTheme.primaryColor, paragraphAlignment: .center), text: attributedText, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Login_TermsOfServiceAgree, action: {
dismissImpl?()
guard let strongSelf = self else {
return
}
let _ = beginSignUp(account: strongSelf.account, data: data).start()
}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Login_TermsOfServiceDecline, action: {
dismissImpl?()
guard let strongSelf = self else {
return
}
strongSelf.currentWindow?.present(standardTextAlertController(theme: alertTheme, title: strongSelf.presentationData.strings.Login_TermsOfServiceDecline, text: strongSelf.presentationData.strings.Login_TermsOfServiceSignupDecline, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
presentAlertAgainImpl?()
}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Login_TermsOfServiceDecline, action: {
guard let strongSelf = self else {
return
}
let account = strongSelf.account
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start()
})]), on: .root, blockInteraction: false, completion: {})
})
], actionLayout: .vertical, dismissOnOutsideTap: true)
contentNode.textAttributeAction = (NSAttributedString.Key(rawValue: TelegramTextAttributes.URL), { value in
if let value = value as? String {
strongSelf.openUrl(value)
}
})
let controller = AlertController(theme: alertTheme, contentNode: contentNode)
dismissImpl = { [weak controller] in
controller?.dismissAnimated()
}
strongSelf.view.endEditing(true)
strongSelf.currentWindow?.present(controller, on: .root, blockInteraction: false, completion: {})
}
presentAlertAgainImpl = {
presentAlertImpl()
}
presentAlertImpl()
} else {
let _ = beginSignUp(account: strongSelf.account, data: data).start()
}
case .loggedIn:
controller?.animateSuccess()
}
}, error: { error in
Queue.mainQueue().async {
if let strongSelf = self, let controller = controller {
controller.inProgress = false
if case .invalidCode = error {
controller.animateError(text: strongSelf.presentationData.strings.Login_WrongCodeError)
} else {
var resetCode = false
let text: String
switch error {
case .limitExceeded:
resetCode = true
text = strongSelf.presentationData.strings.Login_CodeFloodError
case .invalidCode:
resetCode = true
text = strongSelf.presentationData.strings.Login_InvalidCodeError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
case .codeExpired:
text = strongSelf.presentationData.strings.Login_CodeExpired
let account = strongSelf.account
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start()
case .invalidEmailToken:
text = strongSelf.presentationData.strings.Login_InvalidEmailTokenError
case .invalidEmailAddress:
text = strongSelf.presentationData.strings.Login_InvalidEmailAddressError
}
if resetCode {
controller.resetCode()
}
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}
}
}))
}
}
}
}
controller.requestNextOption = { [weak self, weak controller] in
if let strongSelf = self {
if nextType == nil {
if MFMailComposeViewController.canSendMail(), let controller = controller {
let formattedNumber = formatPhoneNumber(number)
strongSelf.presentEmailComposeController(address: "sms@telegram.org", subject: strongSelf.presentationData.strings.Login_EmailCodeSubject(formattedNumber).string, body: strongSelf.presentationData.strings.Login_EmailCodeBody(formattedNumber).string, from: controller)
} else {
controller?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
} else {
controller?.inProgress = true
strongSelf.actionDisposable.set((resendAuthorizationCode(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, apiId: strongSelf.apiId, apiHash: strongSelf.apiHash, firebaseSecretStream: strongSelf.sharedContext.firebaseSecretStream)
|> deliverOnMainQueue).start(next: { result in
controller?.inProgress = false
}, error: { error in
if let strongSelf = self, let controller = controller {
controller.inProgress = false
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.Login_CodeFloodError
case .invalidPhoneNumber:
text = strongSelf.presentationData.strings.Login_InvalidPhoneError
case .phoneLimitExceeded:
text = strongSelf.presentationData.strings.Login_PhoneFloodError
case .phoneBanned:
text = strongSelf.presentationData.strings.Login_PhoneBannedError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
case .timeout:
text = strongSelf.presentationData.strings.Login_NetworkError
}
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}))
}
}
}
controller.reset = { [weak self] in
if let strongSelf = self {
let account = strongSelf.account
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start()
}
}
controller.signInWithApple = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.signInWithAppleSetup = false
if #available(iOS 13.0, *) {
let appleIdProvider = ASAuthorizationAppleIDProvider()
let request = appleIdProvider.createRequest()
request.user = number
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = strongSelf
authorizationController.presentationContextProvider = strongSelf
authorizationController.performRequests()
}
}
controller.openFragment = { [weak self] url in
if let strongSelf = self {
strongSelf.sharedContext.applicationBindings.openUrl(url)
}
}
controller.updateData(number: formatPhoneNumber(number), email: email, codeType: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)
return controller
}
private var signInWithAppleSetup = false
private var appleSignInAllowed = false
private var currentEmail: String?
private func emailSetupController(number: String, appleSignInAllowed: Bool) -> AuthorizationSequenceEmailEntryController {
var currentController: AuthorizationSequenceEmailEntryController?
for c in self.viewControllers {
if let c = c as? AuthorizationSequenceEmailEntryController {
currentController = c
break
}
}
let controller: AuthorizationSequenceEmailEntryController
if let currentController = currentController {
controller = currentController
} else {
controller = AuthorizationSequenceEmailEntryController(presentationData: self.presentationData, mode: .setup, back: { [weak self] in
guard let strongSelf = self else {
return
}
let countryCode = AuthorizationSequenceController.defaultCountryCode()
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))).start()
})
}
controller.proceedWithEmail = { [weak self, weak controller] email in
guard let strongSelf = self else {
return
}
controller?.inProgress = true
strongSelf.currentEmail = email
strongSelf.actionDisposable.set((sendLoginEmailCode(account: strongSelf.account, email: email)
|> deliverOnMainQueue).start(error: { error in
if let strongSelf = self, let controller = controller {
controller.inProgress = false
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.Login_CodeFloodError
case .generic, .codeExpired:
text = strongSelf.presentationData.strings.Login_UnknownError
case .timeout:
text = strongSelf.presentationData.strings.Login_NetworkError
case .invalidEmail:
text = strongSelf.presentationData.strings.Login_InvalidEmailError
case .emailNotAllowed:
text = strongSelf.presentationData.strings.Login_EmailNotAllowedError
}
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}, completed: {
controller?.inProgress = false
}))
}
controller.signInWithApple = { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.signInWithAppleSetup = true
if #available(iOS 13.0, *) {
let appleIdProvider = ASAuthorizationAppleIDProvider()
let request = appleIdProvider.createRequest()
request.requestedScopes = [.email]
request.user = number
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = strongSelf
authorizationController.presentationContextProvider = strongSelf
authorizationController.performRequests()
}
}
controller.updateData(appleSignInAllowed: appleSignInAllowed)
return controller
}
@available(iOS 13.0, *)
public func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
let lastController = self.viewControllers.last as? ViewController
switch authorization.credential {
case let appleIdCredential as ASAuthorizationAppleIDCredential:
guard let tokenData = appleIdCredential.identityToken, let token = String(data: tokenData, encoding: .utf8) else {
lastController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return
}
if self.signInWithAppleSetup {
self.actionDisposable.set((verifyLoginEmailSetup(account: self.account, code: .appleToken(token))
|> deliverOnMainQueue).start(error: { [weak self, weak lastController] error in
if let strongSelf = self, let lastController = lastController {
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.Login_CodeFloodError
case .generic, .codeExpired:
text = strongSelf.presentationData.strings.Login_UnknownError
case .invalidCode:
text = strongSelf.presentationData.strings.Login_InvalidCodeError
case .timeout:
text = strongSelf.presentationData.strings.Login_NetworkError
case .invalidEmailToken:
text = strongSelf.presentationData.strings.Login_InvalidEmailTokenError
case .emailNotAllowed:
text = strongSelf.presentationData.strings.Login_EmailNotAllowedError
}
lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}))
} else {
self.actionDisposable.set(
authorizeWithCode(accountManager: self.sharedContext.accountManager, account: self.account, code: .emailVerification(.appleToken(token)), termsOfService: nil, forcedPasswordSetupNotice: { value in
guard let entry = CodableEntry(ApplicationSpecificCounterNotice(value: value)) else {
return nil
}
return (ApplicationSpecificNotice.forcedPasswordSetupKey(), entry)
}).start(next: { [weak self] result in
guard let strongSelf = self else {
return
}
switch result {
case let .signUp(data):
let _ = beginSignUp(account: strongSelf.account, data: data).start()
case .loggedIn:
break
}
}, error: { [weak self, weak lastController] error in
Queue.mainQueue().async {
if let strongSelf = self, let lastController = lastController {
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.Login_CodeFloodError
case .invalidCode:
text = strongSelf.presentationData.strings.Login_InvalidCodeError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
case .codeExpired:
text = strongSelf.presentationData.strings.Login_CodeExpired
let account = strongSelf.account
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start()
case .invalidEmailToken:
text = strongSelf.presentationData.strings.Login_InvalidEmailTokenError
case .invalidEmailAddress:
text = strongSelf.presentationData.strings.Login_InvalidEmailAddressError
}
lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}
})
)
}
default:
break
}
}
@available(iOS 13.0, *)
public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return self.view.window!
}
private func passwordEntryController(hint: String, suggestReset: Bool, syncContacts: Bool) -> AuthorizationSequencePasswordEntryController {
var currentController: AuthorizationSequencePasswordEntryController?
for c in self.viewControllers {
if let c = c as? AuthorizationSequencePasswordEntryController {
currentController = c
break
}
}
let controller: AuthorizationSequencePasswordEntryController
if let currentController = currentController {
controller = currentController
} else {
controller = AuthorizationSequencePasswordEntryController(presentationData: self.presentationData, back: { [weak self] in
guard let strongSelf = self else {
return
}
let countryCode = AuthorizationSequenceController.defaultCountryCode()
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))).start()
})
controller.loginWithPassword = { [weak self, weak controller] password in
if let strongSelf = self {
controller?.inProgress = true
strongSelf.actionDisposable.set((authorizeWithPassword(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, password: password, syncContacts: syncContacts) |> deliverOnMainQueue).start(error: { error in
Queue.mainQueue().async {
if let strongSelf = self, let controller = controller {
controller.inProgress = false
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.LoginPassword_FloodError
case .invalidPassword:
text = strongSelf.presentationData.strings.LoginPassword_InvalidPasswordError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
}
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
controller.passwordIsInvalid()
}
}
}))
}
}
}
controller.forgot = { [weak self, weak controller] in
if let strongSelf = self, let strongController = controller {
strongController.inProgress = true
strongSelf.actionDisposable.set((TelegramEngineUnauthorized(account: strongSelf.account).auth.requestTwoStepVerificationPasswordRecoveryCode()
|> deliverOnMainQueue).start(next: { pattern in
if let strongSelf = self, let strongController = controller {
strongController.inProgress = false
let _ = (TelegramEngineUnauthorized(account: strongSelf.account).auth.state()
|> take(1)
|> deliverOnMainQueue).start(next: { state in
guard let strongSelf = self else {
return
}
if case let .unauthorized(state) = state, case let .passwordEntry(hint, number, code, _, syncContacts) = state.contents {
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .passwordRecovery(hint: hint, number: number, code: code, emailPattern: pattern, syncContacts: syncContacts))).start()
}
})
}
}, error: { error in
guard let strongController = controller else {
return
}
strongController.inProgress = false
strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
strongController.didForgotWithNoRecovery = true
}))
}
}
controller.reset = { [weak self, weak controller] in
if let strongSelf = self, let strongController = controller {
strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: suggestReset ? strongSelf.presentationData.strings.TwoStepAuth_RecoveryFailed : strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.Login_ResetAccountProtected_Reset, action: {
if let strongSelf = self, let strongController = controller {
strongController.inProgress = true
strongSelf.actionDisposable.set((performAccountReset(account: strongSelf.account)
|> deliverOnMainQueue).start(next: {
if let strongController = controller {
strongController.inProgress = false
}
}, error: { error in
if let strongSelf = self, let strongController = controller {
strongController.inProgress = false
let text: String
switch error {
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
case .limitExceeded:
text = strongSelf.presentationData.strings.Login_ResetAccountProtected_LimitExceeded
}
strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}))
}
})]), in: .window(.root))
}
}
controller.updateData(hint: hint, suggestReset: suggestReset)
return controller
}
private func passwordRecoveryController(emailPattern: String, syncContacts: Bool) -> TwoFactorDataInputScreen {
var currentController: TwoFactorDataInputScreen?
for c in self.viewControllers {
if let c = c as? TwoFactorDataInputScreen {
currentController = c
break
}
}
let controller: TwoFactorDataInputScreen
if let currentController = currentController {
controller = currentController
} else {
controller = TwoFactorDataInputScreen(sharedContext: self.sharedContext, engine: .unauthorized(TelegramEngineUnauthorized(account: self.account)), mode: .passwordRecoveryEmail(emailPattern: emailPattern, mode: .notAuthorized(syncContacts: syncContacts), doneText: self.presentationData.strings.TwoFactorSetup_Done_Action), stateUpdated: { _ in
}, presentation: .default)
}
controller.passwordRecoveryFailed = { [weak self] in
guard let strongSelf = self else {
return
}
let _ = (TelegramEngineUnauthorized(account: strongSelf.account).auth.state()
|> take(1)
|> deliverOnMainQueue).start(next: { state in
guard let strongSelf = self else {
return
}
if case let .unauthorized(state) = state, case let .passwordRecovery(hint, number, code, _, syncContacts) = state.contents {
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .passwordEntry(hint: hint, number: number, code: code, suggestReset: true, syncContacts: syncContacts))).start()
}
})
}
return controller
}
private func awaitingAccountResetController(protectedUntil: Int32, number: String?) -> AuthorizationSequenceAwaitingAccountResetController {
var currentController: AuthorizationSequenceAwaitingAccountResetController?
for c in self.viewControllers {
if let c = c as? AuthorizationSequenceAwaitingAccountResetController {
currentController = c
break
}
}
let controller: AuthorizationSequenceAwaitingAccountResetController
if let currentController = currentController {
controller = currentController
} else {
controller = AuthorizationSequenceAwaitingAccountResetController(strings: self.presentationData.strings, theme: self.presentationData.theme, back: { [weak self] in
guard let strongSelf = self else {
return
}
let countryCode = AuthorizationSequenceController.defaultCountryCode()
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))).start()
})
controller.reset = { [weak self, weak controller] in
if let strongSelf = self, let strongController = controller {
strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_ResetAccountConfirmation, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}),
TextAlertAction(type: .destructiveAction, title: strongSelf.presentationData.strings.Login_ResetAccountProtected_Reset, action: {
if let strongSelf = self, let strongController = controller {
strongController.inProgress = true
strongSelf.actionDisposable.set((performAccountReset(account: strongSelf.account)
|> deliverOnMainQueue).start(next: {
if let strongController = controller {
strongController.inProgress = false
}
}, error: { error in
if let strongSelf = self, let strongController = controller {
strongController.inProgress = false
let text: String
switch error {
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
case .limitExceeded:
text = strongSelf.presentationData.strings.Login_ResetAccountProtected_LimitExceeded
}
strongController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}))
}
})]), in: .window(.root))
}
}
controller.logout = { [weak self] in
if let strongSelf = self {
let account = strongSelf.account
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start()
}
}
}
controller.updateData(protectedUntil: protectedUntil, number: number ?? "")
return controller
}
private func signUpController(firstName: String, lastName: String, termsOfService: UnauthorizedAccountTermsOfService?, displayCancel: Bool) -> AuthorizationSequenceSignUpController {
var currentController: AuthorizationSequenceSignUpController?
for c in self.viewControllers {
if let c = c as? AuthorizationSequenceSignUpController {
currentController = c
break
}
}
let controller: AuthorizationSequenceSignUpController
if let currentController = currentController {
controller = currentController
} else {
controller = AuthorizationSequenceSignUpController(presentationData: self.presentationData, back: { [weak self] in
guard let strongSelf = self else {
return
}
let countryCode = AuthorizationSequenceController.defaultCountryCode()
let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: ""))).start()
}, displayCancel: displayCancel)
controller.signUpWithName = { [weak self, weak controller] firstName, lastName, avatarData, avatarAsset, avatarAdjustments in
if let strongSelf = self {
controller?.inProgress = true
var videoStartTimestamp: Double? = nil
if let adjustments = avatarAdjustments, adjustments.videoStartValue > 0.0 {
videoStartTimestamp = adjustments.videoStartValue - adjustments.trimStartValue
}
let avatarVideo: Signal<UploadedPeerPhotoData?, NoError>?
if let avatarAsset = avatarAsset as? AVAsset {
let account = strongSelf.account
avatarVideo = Signal<TelegramMediaResource?, NoError> { subscriber in
let entityRenderer: LegacyPaintEntityRenderer? = avatarAdjustments.flatMap { adjustments in
if let paintingData = adjustments.paintingData, paintingData.hasAnimation {
return LegacyPaintEntityRenderer(account: nil, adjustments: adjustments)
} else {
return nil
}
}
let tempFile = EngineTempBox.shared.tempFile(fileName: "video.mp4")
let signal = TGMediaVideoConverter.convert(avatarAsset, adjustments: avatarAdjustments, path: tempFile.path, watcher: nil, entityRenderer: entityRenderer)!
let signalDisposable = signal.start(next: { next in
if let result = next as? TGMediaVideoConversionResult {
var value = stat()
if stat(result.fileURL.path, &value) == 0 {
if let data = try? Data(contentsOf: result.fileURL) {
let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max))
account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true)
subscriber.putNext(resource)
EngineTempBox.shared.dispose(tempFile)
}
}
subscriber.putCompletion()
}
}, error: { _ in
}, completed: nil)
let disposable = ActionDisposable {
signalDisposable?.dispose()
}
return ActionDisposable {
disposable.dispose()
}
}
|> mapToSignal { resource -> Signal<UploadedPeerPhotoData?, NoError> in
if let resource = resource {
return TelegramEngineUnauthorized(account: account).auth.uploadedPeerVideo(resource: resource) |> map(Optional.init)
} else {
return .single(nil)
}
}
} else {
avatarVideo = nil
}
strongSelf.actionDisposable.set((signUpWithName(accountManager: strongSelf.sharedContext.accountManager, account: strongSelf.account, firstName: firstName, lastName: lastName, avatarData: avatarData, avatarVideo: avatarVideo, videoStartTimestamp: videoStartTimestamp, forcedPasswordSetupNotice: { value in
guard let entry = CodableEntry(ApplicationSpecificCounterNotice(value: value)) else {
return nil
}
return (ApplicationSpecificNotice.forcedPasswordSetupKey(), entry)
})
|> deliverOnMainQueue).start(error: { error in
Queue.mainQueue().async {
if let strongSelf = self, let controller = controller {
controller.inProgress = false
let text: String
switch error {
case .limitExceeded:
text = strongSelf.presentationData.strings.Login_CodeFloodError
case .codeExpired:
text = strongSelf.presentationData.strings.Login_CodeExpiredError
case .invalidFirstName:
text = strongSelf.presentationData.strings.Login_InvalidFirstNameError
case .invalidLastName:
text = strongSelf.presentationData.strings.Login_InvalidLastNameError
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
}
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}
}))
}
}
}
controller.updateData(firstName: firstName, lastName: lastName, termsOfService: termsOfService)
return controller
}
private func updateState(state: InnerState) {
switch state {
case .authorized:
self.authorizationCompleted()
case let .state(state):
switch state {
case .empty:
if let _ = self.viewControllers.last as? AuthorizationSequenceSplashController {
} else {
var controllers: [ViewController] = []
if self.otherAccountPhoneNumbers.1.isEmpty {
controllers.append(self.splashController())
} else {
controllers.append(self.phoneEntryController(countryCode: AuthorizationSequenceController.defaultCountryCode(), number: "", splashController: nil))
}
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
}
case let .phoneEntry(countryCode, number):
var controllers: [ViewController] = []
if !self.otherAccountPhoneNumbers.1.isEmpty {
controllers.append(self.splashController())
}
var previousSplashController: AuthorizationSequenceSplashController?
for c in self.viewControllers {
if let c = c as? AuthorizationSequenceSplashController {
previousSplashController = c
break
}
}
if let validLayout = self.validLayout, case .tablet = validLayout.deviceMetrics.type {
previousSplashController = nil
}
controllers.append(self.phoneEntryController(countryCode: countryCode, number: number, splashController: previousSplashController))
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty && (previousSplashController == nil || self.viewControllers.count > 2))
case let .confirmationCodeEntry(number, type, _, timeout, nextType, _):
var controllers: [ViewController] = []
if !self.otherAccountPhoneNumbers.1.isEmpty {
controllers.append(self.splashController())
}
controllers.append(self.phoneEntryController(countryCode: AuthorizationSequenceController.defaultCountryCode(), number: "", splashController: nil))
if case let .emailSetupRequired(appleSignInAllowed) = type {
self.appleSignInAllowed = appleSignInAllowed
controllers.append(self.emailSetupController(number: number, appleSignInAllowed: appleSignInAllowed))
} else {
if let _ = self.currentEmail {
controllers.append(self.emailSetupController(number: number, appleSignInAllowed: self.appleSignInAllowed))
}
controllers.append(self.codeEntryController(number: number, email: self.currentEmail, type: type, nextType: nextType, timeout: timeout, termsOfService: nil))
}
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
case let .passwordEntry(hint, _, _, suggestReset, syncContacts):
var controllers: [ViewController] = []
if !self.otherAccountPhoneNumbers.1.isEmpty {
controllers.append(self.splashController())
}
controllers.append(self.passwordEntryController(hint: hint, suggestReset: suggestReset, syncContacts: syncContacts))
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
case let .passwordRecovery(_, _, _, emailPattern, syncContacts):
var controllers: [ViewController] = []
if !self.otherAccountPhoneNumbers.1.isEmpty {
controllers.append(self.splashController())
}
controllers.append(self.passwordRecoveryController(emailPattern: emailPattern, syncContacts: syncContacts))
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
case let .awaitingAccountReset(protectedUntil, number, _):
var controllers: [ViewController] = []
if !self.otherAccountPhoneNumbers.1.isEmpty {
controllers.append(self.splashController())
}
controllers.append(self.awaitingAccountResetController(protectedUntil: protectedUntil, number: number))
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
case let .signUp(_, _, firstName, lastName, termsOfService, _):
var controllers: [ViewController] = []
var displayCancel = false
if !self.otherAccountPhoneNumbers.1.isEmpty {
controllers.append(self.splashController())
} else {
displayCancel = true
}
controllers.append(self.signUpController(firstName: firstName, lastName: lastName, termsOfService: termsOfService, displayCancel: displayCancel))
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
}
}
}
override public func setViewControllers(_ viewControllers: [UIViewController], animated: Bool) {
let wasEmpty = self.viewControllers.isEmpty
super.setViewControllers(viewControllers, animated: animated)
if wasEmpty {
if self.topViewController is AuthorizationSequenceSplashController {
} else {
self.topViewController?.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
}
}
if !self.didSetReady {
self.didSetReady = true
self._ready.set(.single(true))
}
}
public func applyConfirmationCode(_ code: Int) {
if let controller = self.viewControllers.last as? AuthorizationSequenceCodeEntryController {
controller.applyConfirmationCode(code)
}
}
private func presentEmailComposeController(address: String, subject: String, body: String, from controller: ViewController) {
if MFMailComposeViewController.canSendMail() {
let composeController = MFMailComposeViewController()
composeController.setToRecipients([address])
composeController.setSubject(subject)
composeController.setMessageBody(body, isHTML: false)
composeController.mailComposeDelegate = self
controller.view.window?.rootViewController?.present(composeController, animated: true, completion: nil)
} else {
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_EmailNotConfiguredError, actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}
}
public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
controller.dismiss(animated: true, completion: nil)
}
private func animateIn() {
if !self.otherAccountPhoneNumbers.1.isEmpty {
self.view.layer.animatePosition(from: CGPoint(x: self.view.layer.position.x, y: self.view.layer.position.y + self.view.layer.bounds.size.height), to: self.view.layer.position, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
} else {
if let splashController = self.topViewController as? AuthorizationSequenceSplashController {
splashController.animateIn()
}
}
}
private func animateOut(completion: (() -> Void)? = nil) {
self.view.layer.animatePosition(from: self.view.layer.position, to: CGPoint(x: self.view.layer.position.x, y: self.view.layer.position.y + self.view.layer.bounds.size.height), duration: 0.2, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, completion: { _ in
completion?()
})
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.didPlayPresentationAnimation {
self.didPlayPresentationAnimation = true
self.animateIn()
}
}
public func dismiss() {
self.animateOut(completion: { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
})
}
public static func defaultCountryCode() -> Int32 {
var countryId: String? = nil
let networkInfo = CTTelephonyNetworkInfo()
if let carrier = networkInfo.subscriberCellularProvider {
countryId = carrier.isoCountryCode
}
if countryId == nil {
countryId = (Locale.current as NSLocale).object(forKey: .countryCode) as? String
}
var countryCode: Int32 = 1
if let countryId = countryId {
let normalizedId = countryId.uppercased()
for (code, idAndName) in countryCodeToIdAndName {
if idAndName.0 == normalizedId {
countryCode = Int32(code)
break
}
}
}
return countryCode
}
}