Swiftgram/submodules/PassportUI/Sources/SecureIdPlaintextFormControllerNode.swift
2019-10-07 17:03:41 +04:00

997 lines
39 KiB
Swift

import Foundation
import UIKit
import AsyncDisplayKit
import Display
import TelegramCore
import Postbox
import SwiftSignalKit
import CoreTelephony
import TelegramPresentationData
import AccountContext
import AlertUI
import PresentationDataUtils
import CountrySelectionUI
private func cleanPhoneNumber(_ text: String?) -> String {
var cleanNumber = ""
if let text = text {
for c in text {
if c >= "0" && c <= "9" {
cleanNumber += String(c)
}
}
}
return cleanNumber
}
public final class SecureIdPlaintextFormParams {
fileprivate let openCountrySelection: () -> Void
fileprivate let updateTextField: (SecureIdPlaintextFormTextField, String) -> Void
fileprivate let usePhone: (String) -> Void
fileprivate let useEmailAddress: (String) -> Void
fileprivate let save: () -> Void
fileprivate init(openCountrySelection: @escaping () -> Void, updateTextField: @escaping (SecureIdPlaintextFormTextField, String) -> Void, usePhone: @escaping (String) -> Void, useEmailAddress: @escaping (String) -> Void, save: @escaping () -> Void) {
self.openCountrySelection = openCountrySelection
self.updateTextField = updateTextField
self.usePhone = usePhone
self.useEmailAddress = useEmailAddress
self.save = save
}
}
private struct PhoneInputState {
var countryCode: String
var number: String
var countryId: String
func isEqual(to: PhoneInputState) -> Bool {
if self.countryCode != to.countryCode {
return false
}
if self.number != to.number {
return false
}
if self.countryId != to.countryId {
return false
}
return true
}
}
private struct PhoneVerifyState {
let phone: String
let payload: SecureIdPreparePhoneVerificationPayload
var code: String
func isEqual(to: PhoneVerifyState) -> Bool {
if self.code != to.code {
return false
}
return true
}
}
private enum SecureIdPlaintextFormPhoneState {
case input(PhoneInputState)
case verify(PhoneVerifyState)
func isEqual(to: SecureIdPlaintextFormPhoneState) -> Bool {
switch self {
case let .input(lhsInput):
if case let .input(rhsInput) = to, lhsInput.isEqual(to: rhsInput) {
return true
} else {
return false
}
case let .verify(lhsInput):
if case let .verify(rhsInput) = to, lhsInput.isEqual(to: rhsInput) {
return true
} else {
return false
}
}
}
func isComplete() -> Bool {
switch self {
case let .input(input):
if input.countryCode.isEmpty {
return false
}
if input.number.isEmpty {
return false
}
return true
case let .verify(verify):
if verify.code.isEmpty {
return false
}
return true
}
}
}
private struct EmailInputState {
var email: String
func isEqual(to: EmailInputState) -> Bool {
if self.email != to.email {
return false
}
return true
}
}
private struct EmailVerifyState {
let email: String
let payload: SecureIdPrepareEmailVerificationPayload
var code: String
func isEqual(to: EmailVerifyState) -> Bool {
if self.code != to.code {
return false
}
return true
}
}
private enum SecureIdPlaintextFormEmailState {
case input(EmailInputState)
case verify(EmailVerifyState)
func isEqual(to: SecureIdPlaintextFormEmailState) -> Bool {
switch self {
case let .input(lhsInput):
if case let .input(rhsInput) = to, lhsInput.isEqual(to: rhsInput) {
return true
} else {
return false
}
case let .verify(lhsInput):
if case let .verify(rhsInput) = to, lhsInput.isEqual(to: rhsInput) {
return true
} else {
return false
}
}
}
func isComplete() -> Bool {
switch self {
case let .input(input):
if input.email.isEmpty {
return false
}
return true
case let .verify(verify):
if verify.code.isEmpty {
return false
}
return true
}
}
}
private enum SecureIdPlaintextFormTextField {
case countryCode
case number
case code
case email
}
private enum SecureIdPlaintextFormDataState {
case phone(SecureIdPlaintextFormPhoneState)
case email(SecureIdPlaintextFormEmailState)
mutating func updateTextField(type: SecureIdPlaintextFormTextField, value: String) {
switch self {
case let .phone(phone):
switch phone {
case var .input(input):
switch type {
case .countryCode:
input.countryCode = value
case .number:
input.number = value
default:
break
}
self = .phone(.input(input))
case var .verify(verify):
switch type {
case .code:
verify.code = value
default:
break
}
self = .phone(.verify(verify))
}
case let .email(email):
switch email {
case var .input(input):
switch type {
case .email:
input.email = value
default:
break
}
self = .email(.input(input))
case var .verify(verify):
switch type {
case .code:
verify.code = value
default:
break
}
self = .email(.verify(verify))
}
}
}
func isEqual(to: SecureIdPlaintextFormDataState) -> Bool {
switch self {
case let .phone(lhsValue):
if case let .phone(rhsValue) = to, lhsValue.isEqual(to: rhsValue) {
return true
} else {
return false
}
case let .email(lhsValue):
if case let .email(rhsValue) = to, lhsValue.isEqual(to: rhsValue) {
return true
} else {
return false
}
}
}
}
private enum SecureIdPlaintextFormActionState {
case none
case saving
case deleting
}
enum SecureIdPlaintextFormInputState {
case nextAvailable
case nextNotAvailable
case saveAvailable
case saveNotAvailable
case inProgress
}
public struct SecureIdPlaintextFormInnerState: FormControllerInnerState {
fileprivate let previousValue: SecureIdValue?
fileprivate var data: SecureIdPlaintextFormDataState
fileprivate var actionState: SecureIdPlaintextFormActionState
public func isEqual(to: SecureIdPlaintextFormInnerState) -> Bool {
if !self.data.isEqual(to: to.data) {
return false
}
if self.actionState != to.actionState {
return false
}
return true
}
public func entries() -> [FormControllerItemEntry<SecureIdPlaintextFormEntry>] {
switch self.data {
case let .phone(phone):
var result: [FormControllerItemEntry<SecureIdPlaintextFormEntry>] = []
switch phone {
case let .input(input):
result.append(.spacer)
if let value = self.previousValue, case let .phone(phone) = value {
result.append(.entry(SecureIdPlaintextFormEntry.immediatelyAvailablePhone(phone.phone)))
result.append(.entry(SecureIdPlaintextFormEntry.immediatelyAvailablePhoneInfo))
result.append(.spacer)
result.append(.entry(SecureIdPlaintextFormEntry.numberInputHeader))
}
result.append(.entry(SecureIdPlaintextFormEntry.numberInput(countryCode: input.countryCode, number: input.number)))
result.append(.entry(SecureIdPlaintextFormEntry.numberInputInfo))
case let .verify(verify):
result.append(.spacer)
var codeLength: Int32 = 5
switch verify.payload.type {
case let .sms(length):
codeLength = length
case let .call(length):
codeLength = length
case let .otherSession(length):
codeLength = length
default:
break
}
result.append(.entry(SecureIdPlaintextFormEntry.numberCode(verify.code, codeLength)))
result.append(.entry(SecureIdPlaintextFormEntry.numberVerifyInfo))
}
return result
case let .email(email):
var result: [FormControllerItemEntry<SecureIdPlaintextFormEntry>] = []
switch email {
case let .input(input):
result.append(.spacer)
if let value = self.previousValue, case let .email(email) = value {
result.append(.entry(SecureIdPlaintextFormEntry.immediatelyAvailableEmail(email.email)))
result.append(.entry(SecureIdPlaintextFormEntry.immediatelyAvailableEmailInfo))
result.append(.spacer)
result.append(.entry(SecureIdPlaintextFormEntry.emailInputHeader))
}
result.append(.entry(SecureIdPlaintextFormEntry.emailAddress(input.email)))
result.append(.entry(SecureIdPlaintextFormEntry.emailInputInfo))
case let .verify(verify):
result.append(.spacer)
result.append(.entry(SecureIdPlaintextFormEntry.numberCode(verify.code, verify.payload.length)))
result.append(.entry(SecureIdPlaintextFormEntry.emailVerifyInfo(verify.email)))
}
return result
}
}
func actionInputState() -> SecureIdPlaintextFormInputState {
switch self.actionState {
case .deleting, .saving:
return .inProgress
default:
break
}
switch self.data {
case let .phone(phone):
switch phone {
case .input:
if !phone.isComplete() {
return .nextNotAvailable
} else {
return .nextAvailable
}
case .verify:
if !phone.isComplete() {
return .saveNotAvailable
} else {
return .saveAvailable
}
}
case let .email(email):
switch email {
case .input:
if !email.isComplete() {
return .nextNotAvailable
} else {
return .nextAvailable
}
case .verify:
if !email.isComplete() {
return .saveNotAvailable
} else {
return .saveAvailable
}
}
}
}
}
extension SecureIdPlaintextFormInnerState {
init(type: SecureIdPlaintextFormType, immediatelyAvailableValue: SecureIdValue?) {
switch type {
case .phone:
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 countryCodeAndId: (Int32, String) = (1, "US")
if let countryId = countryId {
let normalizedId = countryId.uppercased()
for (code, idAndName) in countryCodeToIdAndName {
if idAndName.0 == normalizedId {
countryCodeAndId = (Int32(code), idAndName.0.uppercased())
break
}
}
}
self.init(previousValue: immediatelyAvailableValue, data: .phone(.input(PhoneInputState(countryCode: "+\(countryCodeAndId.0)", number: "", countryId: countryCodeAndId.1))), actionState: .none)
case .email:
self.init(previousValue: immediatelyAvailableValue, data: .email(.input(EmailInputState(email: ""))), actionState: .none)
}
}
}
public enum SecureIdPlaintextFormEntryId: Hashable {
case immediatelyAvailablePhone
case immediatelyAvailablePhoneInfo
case numberInputHeader
case numberInput
case numberInputInfo
case numberCode
case numberVerifyInfo
case immediatelyAvailableEmail
case immediatelyAvailableEmailInfo
case emailInputHeader
case emailAddress
case emailInputInfo
case emailCode
case emailVerifyInfo
}
public enum SecureIdPlaintextFormEntry: FormControllerEntry {
case immediatelyAvailablePhone(String)
case immediatelyAvailablePhoneInfo
case numberInputHeader
case numberInput(countryCode: String, number: String)
case numberInputInfo
case numberCode(String, Int32)
case numberVerifyInfo
case immediatelyAvailableEmail(String)
case immediatelyAvailableEmailInfo
case emailInputHeader
case emailAddress(String)
case emailInputInfo
case emailCode(String)
case emailVerifyInfo(String)
public var stableId: SecureIdPlaintextFormEntryId {
switch self {
case .immediatelyAvailablePhone:
return .immediatelyAvailablePhone
case .immediatelyAvailablePhoneInfo:
return .immediatelyAvailablePhoneInfo
case .numberInputHeader:
return .numberInputHeader
case .numberInput:
return .numberInput
case .numberInputInfo:
return .numberInputInfo
case .numberCode:
return .numberCode
case .numberVerifyInfo:
return .numberVerifyInfo
case .immediatelyAvailableEmail:
return .immediatelyAvailableEmail
case .immediatelyAvailableEmailInfo:
return .immediatelyAvailableEmailInfo
case .emailInputHeader:
return .emailInputHeader
case .emailAddress:
return .emailAddress
case .emailInputInfo:
return .emailInputInfo
case .emailCode:
return .emailCode
case .emailVerifyInfo:
return .emailVerifyInfo
}
}
public func isEqual(to: SecureIdPlaintextFormEntry) -> Bool {
switch self {
case let .immediatelyAvailablePhone(value):
if case .immediatelyAvailablePhone(value) = to {
return true
} else {
return false
}
case .immediatelyAvailablePhoneInfo:
if case .immediatelyAvailablePhoneInfo = to {
return true
} else {
return false
}
case .numberInputHeader:
if case .numberInputHeader = to {
return true
} else {
return false
}
case let .numberInput(countryCode, number):
if case .numberInput(countryCode, number) = to {
return true
} else {
return false
}
case .numberInputInfo:
if case .numberInputInfo = to {
return true
} else {
return false
}
case let .numberCode(code, length):
if case .numberCode(code, length) = to {
return true
} else {
return false
}
case .numberVerifyInfo:
if case .numberVerifyInfo = to {
return true
} else {
return false
}
case let .immediatelyAvailableEmail(value):
if case .immediatelyAvailableEmail(value) = to {
return true
} else {
return false
}
case .immediatelyAvailableEmailInfo:
if case .immediatelyAvailableEmailInfo = to {
return true
} else {
return false
}
case .emailInputHeader:
if case .emailInputHeader = to {
return true
} else {
return false
}
case let .emailAddress(code):
if case .emailAddress(code) = to {
return true
} else {
return false
}
case .emailInputInfo:
if case .emailInputInfo = to {
return true
} else {
return false
}
case let .emailCode(code):
if case .emailCode(code) = to {
return true
} else {
return false
}
case let .emailVerifyInfo(address):
if case .emailVerifyInfo(address) = to {
return true
} else {
return false
}
}
}
public func item(params: SecureIdPlaintextFormParams, strings: PresentationStrings) -> FormControllerItem {
switch self {
case let .immediatelyAvailablePhone(value):
return FormControllerActionItem(type: .accent, title: strings.Passport_Phone_UseTelegramNumber(formatPhoneNumber(value)).0, activated: {
params.usePhone(value)
})
case .immediatelyAvailablePhoneInfo:
return FormControllerTextItem(text: strings.Passport_Phone_UseTelegramNumberHelp)
case .numberInputHeader:
return FormControllerHeaderItem(text: strings.Passport_Phone_EnterOtherNumber)
case let .numberInput(countryCode, number):
var countryName = ""
if let codeNumber = Int(countryCode), let codeId = AuthorizationSequenceCountrySelectionController.lookupCountryIdByCode(codeNumber) {
countryName = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(codeId, strings: strings) ?? ""
}
return SecureIdValueFormPhoneItem(countryCode: countryCode, number: number, countryName: countryName, openCountrySelection: {
params.openCountrySelection()
}, updateCountryCode: { value in
params.updateTextField(.countryCode, value)
}, updateNumber: { value in
params.updateTextField(.number, value)
})
case .numberInputInfo:
return FormControllerTextItem(text: strings.Passport_Phone_Help)
case let .numberCode(code, length):
return FormControllerTextInputItem(title: strings.ChangePhoneNumberCode_CodePlaceholder, text: code, placeholder: strings.ChangePhoneNumberCode_CodePlaceholder, type: .number, textUpdated: { value in
params.updateTextField(.code, value)
if value.count == length {
params.save()
}
}, returnPressed: {
})
case .numberVerifyInfo:
return FormControllerTextItem(text: strings.ChangePhoneNumberCode_Help)
case let .immediatelyAvailableEmail(value):
return FormControllerActionItem(type: .accent, title: strings.Passport_Email_UseTelegramEmail(value).0, activated: {
params.useEmailAddress(value)
})
case .immediatelyAvailableEmailInfo:
return FormControllerTextItem(text: strings.Passport_Email_UseTelegramEmailHelp)
case .emailInputHeader:
return FormControllerHeaderItem(text: strings.Passport_Email_EnterOtherEmail)
case let .emailAddress(address):
return FormControllerTextInputItem(title: strings.TwoStepAuth_Email, text: address, placeholder: strings.Passport_Email_EmailPlaceholder, type: .email, textUpdated: { value in
params.updateTextField(.email, value)
}, returnPressed: {
params.save()
})
case .emailInputInfo:
return FormControllerTextItem(text: strings.Passport_Email_Help)
case let .emailCode(code):
return FormControllerTextInputItem(title: strings.TwoStepAuth_RecoveryCode, text: code, placeholder: strings.TwoStepAuth_RecoveryCode, type: .number, textUpdated: { value in
params.updateTextField(.code, value)
}, returnPressed: {
})
case let .emailVerifyInfo(address):
return FormControllerTextItem(text: strings.Passport_Email_CodeHelp(address).0)
}
}
}
public struct SecureIdPlaintextFormControllerNodeInitParams {
let context: AccountContext
let secureIdContext: SecureIdAccessContext
}
private enum SecureIdPlaintextFormNavigatonTransition {
case none
case push
}
public final class SecureIdPlaintextFormControllerNode: FormControllerNode<SecureIdPlaintextFormControllerNodeInitParams, SecureIdPlaintextFormInnerState> {
private var _itemParams: SecureIdPlaintextFormParams?
override public var itemParams: SecureIdPlaintextFormParams {
return self._itemParams!
}
private var theme: PresentationTheme
private var strings: PresentationStrings
private let context: AccountContext
private let secureIdContext: SecureIdAccessContext
var actionInputStateUpdated: ((SecureIdPlaintextFormInputState) -> Void)?
var completedWithValue: ((SecureIdValueWithContext?) -> Void)?
var dismiss: (() -> Void)?
private let actionDisposable = MetaDisposable()
required public init(initParams: SecureIdPlaintextFormControllerNodeInitParams, presentationData: PresentationData) {
self.theme = presentationData.theme
self.strings = presentationData.strings
self.context = initParams.context
self.secureIdContext = initParams.secureIdContext
super.init(initParams: initParams, presentationData: presentationData)
self._itemParams = SecureIdPlaintextFormParams(openCountrySelection: { [weak self] in
guard let strongSelf = self else {
return
}
let controller = AuthorizationSequenceCountrySelectionController(strings: strongSelf.strings, theme: strongSelf.theme, displayCodes: true)
controller.completeWithCountryCode = { code, _ in
if let strongSelf = self, var innerState = strongSelf.innerState {
innerState.data.updateTextField(type: .countryCode, value: "+\(code)")
strongSelf.updateInnerState(transition: .immediate, with: innerState)
}
}
strongSelf.view.endEditing(true)
strongSelf.present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
}, updateTextField: { [weak self] type, value in
guard let strongSelf = self else {
return
}
guard var innerState = strongSelf.innerState else {
return
}
innerState.data.updateTextField(type: type, value: value)
strongSelf.updateInnerState(transition: .immediate, with: innerState)
}, usePhone: { [weak self] value in
self?.savePhone(value)
}, useEmailAddress: { [weak self] value in
self?.saveEmailAddress(value)
}, save: { [weak self] in
self?.save()
})
}
deinit {
self.actionDisposable.dispose()
}
override func updateInnerState(transition: ContainedViewLayoutTransition, with innerState: SecureIdPlaintextFormInnerState) {
let previousActionInputState = self.innerState?.actionInputState()
super.updateInnerState(transition: transition, with: innerState)
let actionInputState = innerState.actionInputState()
if previousActionInputState != actionInputState {
self.actionInputStateUpdated?(actionInputState)
}
}
private func updateInnerState(transition: ContainedViewLayoutTransition, navigationTransition: SecureIdPlaintextFormNavigatonTransition, with innerState: SecureIdPlaintextFormInnerState) {
if case .push = navigationTransition {
if let snapshotView = self.scrollNode.view.snapshotContentTree() {
snapshotView.frame = self.scrollNode.view.frame.offsetBy(dx: 0.0, dy: self.scrollNode.view.contentInset.top)
self.scrollNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.scrollNode.view)
snapshotView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -self.scrollNode.view.bounds.width, y: 0.0), duration: 0.25, removeOnCompletion: false, additive: true, completion : { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
self.scrollNode.view.layer.animatePosition(from: CGPoint(x: self.scrollNode.view.bounds.width, y: 0.0), to: CGPoint(), duration: 0.25, additive: true)
}
}
self.updateInnerState(transition: transition, with: innerState)
}
func activateMainInput() {
self.enumerateItemsAndEntries({ itemEntry, itemNode in
switch itemEntry {
case .emailAddress, .numberCode, .emailCode:
if let inputNode = itemNode as? FormControllerTextInputItemNode {
inputNode.activate()
}
return false
case .numberInput:
if let inputNode = itemNode as? SecureIdValueFormPhoneItemNode {
inputNode.activate()
}
return false
default:
return true
}
})
}
override func didAppear() {
self.activateMainInput()
}
func save() {
guard var innerState = self.innerState else {
return
}
guard case .none = innerState.actionState else {
return
}
switch innerState.data {
case let .phone(phone):
switch phone {
case let .input(input):
self.savePhone(input.countryCode + input.number)
return
case .verify:
self.verifyPhoneCode()
return
}
case let .email(email):
switch email {
case let .input(input):
self.saveEmailAddress(input.email)
return
case let .verify(verify):
guard case .saveAvailable = innerState.actionInputState() else {
return
}
innerState.actionState = .saving
self.updateInnerState(transition: .immediate, with: innerState)
self.actionDisposable.set((secureIdCommitEmailVerification(postbox: self.context.account.postbox, network: self.context.account.network, context: self.secureIdContext, payload: verify.payload, code: verify.code)
|> deliverOnMainQueue).start(next: { [weak self] result in
if let strongSelf = self {
guard let innerState = strongSelf.innerState else {
return
}
guard case .saving = innerState.actionState else {
return
}
strongSelf.completedWithValue?(result)
}
}, error: { [weak self] error in
if let strongSelf = self {
guard var innerState = strongSelf.innerState else {
return
}
guard case .saving = innerState.actionState else {
return
}
innerState.actionState = .none
strongSelf.updateInnerState(transition: .immediate, with: innerState)
let errorText: String
switch error {
case .generic:
errorText = strongSelf.strings.Login_UnknownError
case .flood:
errorText = strongSelf.strings.Login_CodeFloodError
case .invalid:
errorText = strongSelf.strings.Login_InvalidCodeError
}
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.strings.Common_OK, action: {})]), nil)
}
}))
return
}
}
}
private func savePhone(_ value: String) {
guard var innerState = self.innerState else {
return
}
guard case .none = innerState.actionState else {
return
}
innerState.actionState = .saving
let inputPhone = cleanPhoneNumber(value)
self.updateInnerState(transition: .immediate, with: innerState)
self.actionDisposable.set((secureIdPreparePhoneVerification(network: self.context.account.network, value: SecureIdPhoneValue(phone: inputPhone))
|> deliverOnMainQueue).start(next: { [weak self] result in
if let strongSelf = self {
guard var innerState = strongSelf.innerState else {
return
}
guard case .saving = innerState.actionState else {
return
}
innerState.actionState = .none
innerState.data = .phone(.verify(PhoneVerifyState(phone: inputPhone, payload: result, code: "")))
strongSelf.updateInnerState(transition: .immediate, navigationTransition: .push, with: innerState)
strongSelf.activateMainInput()
}
}, error: { [weak self] error in
if let strongSelf = self {
guard var innerState = strongSelf.innerState else {
return
}
guard case .saving = innerState.actionState else {
return
}
innerState.actionState = .none
strongSelf.updateInnerState(transition: .immediate, with: innerState)
let errorText: String
switch error {
case .generic:
errorText = strongSelf.strings.Login_UnknownError
case .flood:
errorText = strongSelf.strings.Login_CodeFloodError
}
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.strings.Common_OK, action: {})]), nil)
}
}))
}
private func saveEmailAddress(_ value: String) {
guard var innerState = self.innerState else {
return
}
guard case .none = innerState.actionState else {
return
}
innerState.actionState = .saving
self.updateInnerState(transition: .immediate, with: innerState)
self.actionDisposable.set((saveSecureIdValue(postbox: self.context.account.postbox, network: self.context.account.network, context: self.secureIdContext, value: .email(SecureIdEmailValue(email: value)), uploadedFiles: [:])
|> deliverOnMainQueue).start(next: { [weak self] result in
guard let strongSelf = self else {
return
}
strongSelf.completedWithValue?(result)
}, error: { [weak self] _ in
guard let strongSelf = self else {
return
}
strongSelf.actionDisposable.set((secureIdPrepareEmailVerification(network: strongSelf.context.account.network, value: SecureIdEmailValue(email: value))
|> deliverOnMainQueue).start(next: { result in
guard let strongSelf = self else {
return
}
guard var innerState = strongSelf.innerState else {
return
}
guard case .saving = innerState.actionState else {
return
}
innerState.actionState = .none
innerState.data = .email(.verify(EmailVerifyState(email: value, payload: result, code: "")))
strongSelf.updateInnerState(transition: .immediate, navigationTransition: .push, with: innerState)
strongSelf.activateMainInput()
}, error: { [weak self] error in
guard let strongSelf = self else {
return
}
guard var innerState = strongSelf.innerState else {
return
}
guard case .saving = innerState.actionState else {
return
}
innerState.actionState = .none
strongSelf.updateInnerState(transition: .immediate, with: innerState)
let errorText: String
switch error {
case .generic:
errorText = strongSelf.strings.Login_UnknownError
case .invalidEmail:
errorText = strongSelf.strings.TwoStepAuth_EmailInvalid
case .flood:
errorText = strongSelf.strings.Login_CodeFloodError
}
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.strings.Common_OK, action: {})]), nil)
}))
}))
}
private func verifyPhoneCode() {
guard var innerState = self.innerState else {
return
}
guard case let .phone(phone) = innerState.data, case let .verify(verify) = phone else {
return
}
guard case .saveAvailable = innerState.actionInputState() else {
return
}
innerState.actionState = .saving
self.updateInnerState(transition: .immediate, with: innerState)
self.actionDisposable.set((secureIdCommitPhoneVerification(postbox: self.context.account.postbox, network: self.context.account.network, context: self.secureIdContext, payload: verify.payload, code: verify.code)
|> deliverOnMainQueue).start(next: { [weak self] result in
if let strongSelf = self {
guard let innerState = strongSelf.innerState else {
return
}
guard case .saving = innerState.actionState else {
return
}
strongSelf.completedWithValue?(result)
}
}, error: { [weak self] error in
if let strongSelf = self {
guard var innerState = strongSelf.innerState else {
return
}
guard case .saving = innerState.actionState else {
return
}
innerState.actionState = .none
strongSelf.updateInnerState(transition: .immediate, with: innerState)
let errorText: String
switch error {
case .generic:
errorText = strongSelf.strings.Login_UnknownError
case .flood:
errorText = strongSelf.strings.Login_CodeFloodError
case .invalid:
errorText = strongSelf.strings.Login_InvalidCodeError
}
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.strings.Common_OK, action: {})]), nil)
}
}))
}
func applyPhoneCode(_ code: Int) {
guard var innerState = self.innerState else {
return
}
switch innerState.data {
case let .phone(phone):
switch phone {
case var .verify(verify):
let value = "\(code)"
verify.code = value
innerState.data = .phone(.verify(verify))
self.updateInnerState(transition: .immediate, with: innerState)
self.verifyPhoneCode()
default:
break
}
default:
break
}
}
}