Swiftgram/TelegramUI/SecureIdPlaintextFormControllerNode.swift
2019-01-22 21:58:59 +04:00

974 lines
38 KiB
Swift

import Foundation
import AsyncDisplayKit
import Display
import TelegramCore
import Postbox
import SwiftSignalKit
import CoreTelephony
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
}
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
}
struct SecureIdPlaintextFormInnerState: FormControllerInnerState {
fileprivate let previousValue: SecureIdValue?
fileprivate var data: SecureIdPlaintextFormDataState
fileprivate var actionState: SecureIdPlaintextFormActionState
func isEqual(to: SecureIdPlaintextFormInnerState) -> Bool {
if !self.data.isEqual(to: to.data) {
return false
}
if self.actionState != to.actionState {
return false
}
return true
}
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)
}
}
}
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
}
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)
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
}
}
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
}
}
}
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)
}
}
}
struct SecureIdPlaintextFormControllerNodeInitParams {
let context: AccountContext
let secureIdContext: SecureIdAccessContext
}
final class SecureIdPlaintextFormControllerNode: FormControllerNode<SecureIdPlaintextFormControllerNodeInitParams, SecureIdPlaintextFormInnerState> {
private var _itemParams: SecureIdPlaintextFormParams?
override 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 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: AuthorizationSequenceCountrySelectionTheme(presentationTheme: 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)
}
}
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 let .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(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.theme), 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, 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(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.theme), 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, 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(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.theme), 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(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.theme), 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
}
}
}