mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Refactor PasswordSetupUI, PassportUI, GalleryUI and related modules
This commit is contained in:
652
submodules/PassportUI/Sources/SecureIdAuthController.swift
Normal file
652
submodules/PassportUI/Sources/SecureIdAuthController.swift
Normal file
@@ -0,0 +1,652 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import TextFormat
|
||||
import ProgressNavigationButtonNode
|
||||
import AccountContext
|
||||
import AlertUI
|
||||
import PasswordSetupUI
|
||||
|
||||
public enum SecureIdRequestResult: String {
|
||||
case success = "success"
|
||||
case cancel = "cancel"
|
||||
case error = "error"
|
||||
}
|
||||
|
||||
public func secureIdCallbackUrl(with baseUrl: String, peerId: PeerId, result: SecureIdRequestResult, parameters: [String : String]) -> String {
|
||||
var query = (parameters.compactMap({ (key, value) -> String in
|
||||
return "\(key)=\(value)"
|
||||
}) as Array).joined(separator: "&")
|
||||
|
||||
if !query.isEmpty {
|
||||
query = "?" + query
|
||||
}
|
||||
|
||||
let url: String
|
||||
if baseUrl.hasPrefix("tgbot") {
|
||||
url = "tgbot\(peerId.id)://passport/" + result.rawValue + query
|
||||
} else {
|
||||
url = baseUrl + (baseUrl.range(of: "?") != nil ? "&" : "?") + "tg_passport=" + result.rawValue + query
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
||||
final class SecureIdAuthControllerInteraction {
|
||||
let updateState: ((SecureIdAuthControllerState) -> SecureIdAuthControllerState) -> Void
|
||||
let present: (ViewController, Any?) -> Void
|
||||
let checkPassword: (String) -> Void
|
||||
let openPasswordHelp: () -> Void
|
||||
let setupPassword: () -> Void
|
||||
let grant: () -> Void
|
||||
let openUrl: (String) -> Void
|
||||
let openMention: (TelegramPeerMention) -> Void
|
||||
let deleteAll: () -> Void
|
||||
|
||||
fileprivate init(updateState: @escaping ((SecureIdAuthControllerState) -> SecureIdAuthControllerState) -> Void, present: @escaping (ViewController, Any?) -> Void, checkPassword: @escaping (String) -> Void, openPasswordHelp: @escaping () -> Void, setupPassword: @escaping () -> Void, grant: @escaping () -> Void, openUrl: @escaping (String) -> Void, openMention: @escaping (TelegramPeerMention) -> Void, deleteAll: @escaping () -> Void) {
|
||||
self.updateState = updateState
|
||||
self.present = present
|
||||
self.checkPassword = checkPassword
|
||||
self.openPasswordHelp = openPasswordHelp
|
||||
self.setupPassword = setupPassword
|
||||
self.grant = grant
|
||||
self.openUrl = openUrl
|
||||
self.openMention = openMention
|
||||
self.deleteAll = deleteAll
|
||||
}
|
||||
}
|
||||
|
||||
public enum SecureIdAuthControllerMode {
|
||||
case form(peerId: PeerId, scope: String, publicKey: String, callbackUrl: String, opaquePayload: Data, opaqueNonce: Data)
|
||||
case list
|
||||
}
|
||||
|
||||
public final class SecureIdAuthController: ViewController {
|
||||
private var controllerNode: SecureIdAuthControllerNode {
|
||||
return self.displayNode as! SecureIdAuthControllerNode
|
||||
}
|
||||
|
||||
private let context: AccountContext
|
||||
private var presentationData: PresentationData
|
||||
private let mode: SecureIdAuthControllerMode
|
||||
|
||||
private var didPlayPresentationAnimation = false
|
||||
|
||||
private let challengeDisposable = MetaDisposable()
|
||||
private let authenthicateDisposable = MetaDisposable()
|
||||
private var formDisposable: Disposable?
|
||||
private let deleteDisposable = MetaDisposable()
|
||||
private let recoveryDisposable = MetaDisposable()
|
||||
|
||||
private var state: SecureIdAuthControllerState
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
public init(context: AccountContext, mode: SecureIdAuthControllerMode) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
self.mode = mode
|
||||
|
||||
switch mode {
|
||||
case .form:
|
||||
self.state = .form(SecureIdAuthControllerFormState(twoStepEmail: nil, encryptedFormData: nil, formData: nil, verificationState: nil, removingValues: false))
|
||||
case .list:
|
||||
self.state = .list(SecureIdAuthControllerListState(accountPeer: nil, twoStepEmail: nil, verificationState: nil, encryptedValues: nil, primaryLanguageByCountry: [:], values: nil, removingValues: false))
|
||||
}
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData))
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
|
||||
self.title = self.presentationData.strings.Passport_Title
|
||||
switch mode {
|
||||
case .form:
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Cancel, style: .plain, target: self, action: #selector(self.cancelPressed))
|
||||
case .list:
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.cancelPressed))
|
||||
}
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationInfoIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.infoPressed))
|
||||
|
||||
self.challengeDisposable.set((twoStepAuthData(context.account.network)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] data in
|
||||
if let strongSelf = self {
|
||||
let storedPassword = context.getStoredSecureIdPassword()
|
||||
if data.currentPasswordDerivation != nil, let storedPassword = storedPassword {
|
||||
strongSelf.authenthicateDisposable.set((accessSecureId(network: strongSelf.context.account.network, password: storedPassword)
|
||||
|> deliverOnMainQueue).start(next: { context in
|
||||
guard let strongSelf = self, strongSelf.state.verificationState == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.updateState(animated: true, { state in
|
||||
var state = state
|
||||
state.verificationState = .verified(context.context)
|
||||
state.twoStepEmail = !context.settings.email.isEmpty ? context.settings.email : nil
|
||||
switch state {
|
||||
case var .form(form):
|
||||
form.formData = form.encryptedFormData.flatMap({ decryptedSecureIdForm(context: context.context, form: $0.form) })
|
||||
state = .form(form)
|
||||
case var .list(list):
|
||||
list.values = list.encryptedValues.flatMap({ decryptedAllSecureIdValues(context: context.context, encryptedValues: $0) })
|
||||
state = .list(list)
|
||||
}
|
||||
return state
|
||||
})
|
||||
}, error: { [weak self] error in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if strongSelf.state.verificationState == nil {
|
||||
strongSelf.updateState(animated: true, { state in
|
||||
var state = state
|
||||
state.verificationState = .passwordChallenge(hint: data.currentHint ?? "", state: .none, hasRecoveryEmail: data.hasRecovery)
|
||||
return state
|
||||
})
|
||||
}
|
||||
}))
|
||||
} else {
|
||||
strongSelf.updateState { state in
|
||||
var state = state
|
||||
if data.currentPasswordDerivation != nil {
|
||||
state.verificationState = .passwordChallenge(hint: data.currentHint ?? "", state: .none, hasRecoveryEmail: data.hasRecovery)
|
||||
} else if let unconfirmedEmailPattern = data.unconfirmedEmailPattern {
|
||||
state.verificationState = .noChallenge(.awaitingConfirmation(password: nil, emailPattern: unconfirmedEmailPattern, codeLength: nil))
|
||||
} else {
|
||||
state.verificationState = .noChallenge(.notSet)
|
||||
}
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
let handleError: (Any, String?, PeerId?) -> Void = { [weak self] error, callbackUrl, peerId in
|
||||
if let strongSelf = self {
|
||||
var passError: String?
|
||||
var appUpdateRequired = false
|
||||
switch error {
|
||||
case let error as RequestSecureIdFormError:
|
||||
if case let .serverError(error) = error, ["BOT_INVALID", "PUBLIC_KEY_REQUIRED", "PUBLIC_KEY_INVALID", "SCOPE_EMPTY", "PAYLOAD_EMPTY", "NONCE_EMPTY"].contains(error) {
|
||||
passError = error
|
||||
} else if case .versionOutdated = error {
|
||||
appUpdateRequired = true
|
||||
}
|
||||
break
|
||||
case let error as GetAllSecureIdValuesError:
|
||||
if case .versionOutdated = error {
|
||||
appUpdateRequired = true
|
||||
}
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
if appUpdateRequired {
|
||||
let errorText = strongSelf.presentationData.strings.Passport_UpdateRequiredError
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Application_Update, action: {
|
||||
context.sharedContext.applicationBindings.openAppStorePage()
|
||||
})]), in: .window(.root))
|
||||
} else if let callbackUrl = callbackUrl, let peerId = peerId {
|
||||
let errorText = strongSelf.presentationData.strings.Login_UnknownError
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
|
||||
if let error = passError {
|
||||
strongSelf.openUrl(secureIdCallbackUrl(with: callbackUrl, peerId: peerId, result: .error, parameters: ["error": error]))
|
||||
}
|
||||
})]), in: .window(.root))
|
||||
}
|
||||
strongSelf.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
switch self.mode {
|
||||
case let .form(peerId, scope, publicKey, callbackUrl, _, _):
|
||||
self.formDisposable = (combineLatest(requestSecureIdForm(postbox: context.account.postbox, network: context.account.network, peerId: peerId, scope: scope, publicKey: publicKey), secureIdConfiguration(postbox: context.account.postbox, network: context.account.network) |> introduceError(RequestSecureIdFormError.self))
|
||||
|> mapToSignal { form, configuration -> Signal<SecureIdEncryptedFormData, RequestSecureIdFormError> in
|
||||
return context.account.postbox.transaction { transaction -> Signal<SecureIdEncryptedFormData, RequestSecureIdFormError> in
|
||||
guard let accountPeer = transaction.getPeer(context.account.peerId), let servicePeer = transaction.getPeer(form.peerId) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
let primaryLanguageByCountry = configuration.nativeLanguageByCountry
|
||||
return .single(SecureIdEncryptedFormData(form: form, primaryLanguageByCountry: primaryLanguageByCountry, accountPeer: accountPeer, servicePeer: servicePeer))
|
||||
}
|
||||
|> mapError { _ in return RequestSecureIdFormError.generic }
|
||||
|> switchToLatest
|
||||
}
|
||||
|> deliverOnMainQueue).start(next: { [weak self] formData in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState { state in
|
||||
var state = state
|
||||
switch state {
|
||||
case var .form(form):
|
||||
form.encryptedFormData = formData
|
||||
state = .form(form)
|
||||
case .list:
|
||||
break
|
||||
}
|
||||
return state
|
||||
}
|
||||
}
|
||||
}, error: { error in
|
||||
handleError(error, callbackUrl, peerId)
|
||||
})
|
||||
case .list:
|
||||
self.formDisposable = (combineLatest(getAllSecureIdValues(network: self.context.account.network), secureIdConfiguration(postbox: context.account.postbox, network: context.account.network) |> introduceError(GetAllSecureIdValuesError.self), context.account.postbox.transaction { transaction -> Signal<Peer, GetAllSecureIdValuesError> in
|
||||
guard let accountPeer = transaction.getPeer(context.account.peerId) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
|
||||
return .single(accountPeer)
|
||||
}
|
||||
|> mapError { _ in return GetAllSecureIdValuesError.generic }
|
||||
|> switchToLatest)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] values, configuration, accountPeer in
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateState { state in
|
||||
let state = state
|
||||
let primaryLanguageByCountry = configuration.nativeLanguageByCountry
|
||||
|
||||
switch state {
|
||||
case .form:
|
||||
break
|
||||
case var .list(list):
|
||||
list.accountPeer = accountPeer
|
||||
list.primaryLanguageByCountry = primaryLanguageByCountry
|
||||
list.encryptedValues = values
|
||||
return .list(list)
|
||||
}
|
||||
return state
|
||||
}
|
||||
}
|
||||
}, error: { error in
|
||||
handleError(error, nil, nil)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.challengeDisposable.dispose()
|
||||
self.authenthicateDisposable.dispose()
|
||||
self.formDisposable?.dispose()
|
||||
self.deleteDisposable.dispose()
|
||||
self.recoveryDisposable.dispose()
|
||||
}
|
||||
|
||||
override public func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if !self.didPlayPresentationAnimation {
|
||||
self.didPlayPresentationAnimation = true
|
||||
self.controllerNode.animateIn()
|
||||
}
|
||||
}
|
||||
|
||||
override public func dismiss(completion: (() -> Void)? = nil) {
|
||||
self.controllerNode.animateOut(completion: { [weak self] in
|
||||
self?.presentingViewController?.dismiss(animated: false, completion: nil)
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
let interaction = SecureIdAuthControllerInteraction(updateState: { [weak self] f in
|
||||
self?.updateState(f)
|
||||
}, present: { [weak self] c, a in
|
||||
self?.present(c, in: .window(.root), with: a)
|
||||
}, checkPassword: { [weak self] password in
|
||||
self?.checkPassword(password: password, inBackground: false, completion: {})
|
||||
}, openPasswordHelp: { [weak self] in
|
||||
self?.openPasswordHelp()
|
||||
}, setupPassword: { [weak self] in
|
||||
self?.setupPassword()
|
||||
}, grant: { [weak self] in
|
||||
self?.grantAccess()
|
||||
}, openUrl: { [weak self] url in
|
||||
if let strongSelf = self {
|
||||
strongSelf.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: url, forceExternal: false, presentationData: strongSelf.presentationData, navigationController: strongSelf.navigationController as? NavigationController, dismissInput: {
|
||||
self?.view.endEditing(true)
|
||||
})
|
||||
}
|
||||
}, openMention: { [weak self] mention in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let _ = (strongSelf.context.account.postbox.loadedPeerWithId(mention.peerId)
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let infoController = strongSelf.context.sharedContext.makePeerInfoController(context: strongSelf.context, peer: peer) {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(infoController)
|
||||
}
|
||||
})
|
||||
}, deleteAll: { [weak self] in
|
||||
guard let strongSelf = self, case let .list(list) = strongSelf.state, let values = list.values else {
|
||||
return
|
||||
}
|
||||
|
||||
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: strongSelf.presentationData.theme))
|
||||
strongSelf.navigationItem.rightBarButtonItem = item
|
||||
strongSelf.deleteDisposable.set((deleteSecureIdValues(network: strongSelf.context.account.network, keys: Set(values.map({ $0.value.key })))
|
||||
|> deliverOnMainQueue).start(completed: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationInfoIcon(strongSelf.presentationData.theme), style: .plain, target: self, action: #selector(strongSelf.infoPressed))
|
||||
strongSelf.updateState { state in
|
||||
if case var .list(list) = state {
|
||||
list.values = []
|
||||
return .list(list)
|
||||
}
|
||||
return state
|
||||
}
|
||||
}))
|
||||
})
|
||||
|
||||
self.displayNode = SecureIdAuthControllerNode(context: self.context, presentationData: presentationData, requestLayout: { [weak self] transition in
|
||||
self?.requestLayout(transition: transition)
|
||||
}, interaction: interaction)
|
||||
self.controllerNode.updateState(self.state, transition: .immediate)
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationHeight, transition: transition)
|
||||
}
|
||||
|
||||
private func updateState(animated: Bool = true, _ f: (SecureIdAuthControllerState) -> SecureIdAuthControllerState) {
|
||||
let state = f(self.state)
|
||||
if state != self.state {
|
||||
var previousHadProgress = false
|
||||
if let verificationState = self.state.verificationState, case .passwordChallenge(_, .checking, _) = verificationState {
|
||||
previousHadProgress = true
|
||||
}
|
||||
if self.state.removingValues {
|
||||
previousHadProgress = true
|
||||
}
|
||||
var updatedHasProgress = false
|
||||
if let verificationState = state.verificationState, case .passwordChallenge(_, .checking, _) = verificationState {
|
||||
updatedHasProgress = true
|
||||
}
|
||||
if state.removingValues {
|
||||
updatedHasProgress = true
|
||||
}
|
||||
|
||||
self.state = state
|
||||
if self.isNodeLoaded {
|
||||
self.controllerNode.updateState(self.state, transition: animated ? .animated(duration: 0.3, curve: .spring) : .immediate)
|
||||
}
|
||||
|
||||
if previousHadProgress != updatedHasProgress {
|
||||
if updatedHasProgress {
|
||||
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: self.presentationData.theme))
|
||||
self.navigationItem.rightBarButtonItem = item
|
||||
} else {
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationInfoIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.infoPressed))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func openUrl(_ url: String) {
|
||||
self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: true, presentationData: self.presentationData, navigationController: nil, dismissInput: { [weak self] in
|
||||
self?.view.endEditing(true)
|
||||
})
|
||||
}
|
||||
|
||||
@objc private func cancelPressed() {
|
||||
self.dismiss()
|
||||
|
||||
if case let .form(reqForm) = self.mode {
|
||||
self.openUrl(secureIdCallbackUrl(with: reqForm.callbackUrl, peerId: reqForm.peerId, result: .cancel, parameters: [:]))
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func checkPassword(password: String, inBackground: Bool, completion: @escaping () -> Void) {
|
||||
if let verificationState = self.state.verificationState, case let .passwordChallenge(hint, challengeState, hasRecoveryEmail) = verificationState {
|
||||
switch challengeState {
|
||||
case .none, .invalid:
|
||||
break
|
||||
case .checking:
|
||||
return
|
||||
}
|
||||
self.updateState(animated: !inBackground, { state in
|
||||
var state = state
|
||||
state.verificationState = .passwordChallenge(hint: hint, state: .checking, hasRecoveryEmail: hasRecoveryEmail)
|
||||
return state
|
||||
})
|
||||
self.challengeDisposable.set((accessSecureId(network: self.context.account.network, password: password)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] context in
|
||||
guard let strongSelf = self, let verificationState = strongSelf.state.verificationState, case .passwordChallenge(_, .checking, _) = verificationState else {
|
||||
return
|
||||
}
|
||||
strongSelf.context.storeSecureIdPassword(password: password)
|
||||
strongSelf.updateState(animated: !inBackground, { state in
|
||||
var state = state
|
||||
state.verificationState = .verified(context.context)
|
||||
state.twoStepEmail = !context.settings.email.isEmpty ? context.settings.email : nil
|
||||
switch state {
|
||||
case var .form(form):
|
||||
form.formData = form.encryptedFormData.flatMap({ decryptedSecureIdForm(context: context.context, form: $0.form) })
|
||||
state = .form(form)
|
||||
case var .list(list):
|
||||
list.values = list.encryptedValues.flatMap({ decryptedAllSecureIdValues(context: context.context, encryptedValues: $0) })
|
||||
state = .list(list)
|
||||
}
|
||||
return state
|
||||
})
|
||||
completion()
|
||||
}, error: { [weak self] error in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
let errorText: String
|
||||
switch error {
|
||||
case let .passwordError(passwordError):
|
||||
switch passwordError {
|
||||
case .invalidPassword:
|
||||
errorText = strongSelf.presentationData.strings.LoginPassword_InvalidPasswordError
|
||||
case .limitExceeded:
|
||||
errorText = strongSelf.presentationData.strings.LoginPassword_FloodError
|
||||
case .generic:
|
||||
errorText = strongSelf.presentationData.strings.Login_UnknownError
|
||||
}
|
||||
case .generic:
|
||||
errorText = strongSelf.presentationData.strings.Login_UnknownError
|
||||
case .secretPasswordMismatch:
|
||||
errorText = strongSelf.presentationData.strings.Login_UnknownError
|
||||
}
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: errorText, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
|
||||
if let verificationState = strongSelf.state.verificationState, case let .passwordChallenge(hint, .checking, hasRecoveryEmail) = verificationState {
|
||||
strongSelf.updateState(animated: !inBackground, { state in
|
||||
var state = state
|
||||
state.verificationState = .passwordChallenge(hint: hint, state: .invalid, hasRecoveryEmail: hasRecoveryEmail)
|
||||
return state
|
||||
})
|
||||
}
|
||||
completion()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
private func openPasswordHelp() {
|
||||
guard let verificationState = self.state.verificationState, case let .passwordChallenge(passwordChallenge) = verificationState else {
|
||||
return
|
||||
}
|
||||
switch passwordChallenge.state {
|
||||
case .checking:
|
||||
return
|
||||
case .none, .invalid:
|
||||
break
|
||||
}
|
||||
|
||||
if passwordChallenge.hasRecoveryEmail {
|
||||
self.present(textAlertController(context: self.context, title: self.presentationData.strings.Passport_ForgottenPassword, text: self.presentationData.strings.Passport_PasswordReset, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Login_ResetAccountProtected_Reset, action: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.recoveryDisposable.set((requestTwoStepVerificationPasswordRecoveryCode(network: strongSelf.context.account.network)
|
||||
|> deliverOnMainQueue).start(next: { emailPattern in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
var completionImpl: (() -> Void)?
|
||||
let controller = resetPasswordController(context: strongSelf.context, emailPattern: emailPattern, completion: {
|
||||
completionImpl?()
|
||||
})
|
||||
completionImpl = { [weak controller] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateState(animated: false, { state in
|
||||
var state = state
|
||||
state.verificationState = .noChallenge(.notSet)
|
||||
return state
|
||||
})
|
||||
controller?.view.endEditing(true)
|
||||
controller?.dismiss()
|
||||
strongSelf.setupPassword()
|
||||
}
|
||||
strongSelf.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}))
|
||||
})]), in: .window(.root))
|
||||
} else {
|
||||
self.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.TwoStepAuth_RecoveryUnavailable, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
|
||||
private func setupPassword() {
|
||||
guard let verificationState = self.state.verificationState, case let .noChallenge(noChallengeState) = verificationState else {
|
||||
return
|
||||
}
|
||||
let initialState: SetupTwoStepVerificationInitialState
|
||||
switch noChallengeState {
|
||||
case .notSet:
|
||||
initialState = .createPassword
|
||||
case let .awaitingConfirmation(password, emailPattern, codeLength):
|
||||
initialState = .confirmEmail(password: password, hasSecureValues: false, pattern: emailPattern, codeLength: codeLength)
|
||||
}
|
||||
let controller = SetupTwoStepVerificationController(context: self.context, initialState: initialState, stateUpdated: { [weak self] update, shouldDismiss, controller in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch update {
|
||||
case .noPassword:
|
||||
strongSelf.updateState(animated: false, { state in
|
||||
var state = state
|
||||
if let verificationState = state.verificationState, case .noChallenge = verificationState {
|
||||
state.verificationState = .noChallenge(.notSet)
|
||||
}
|
||||
return state
|
||||
})
|
||||
if shouldDismiss {
|
||||
controller.dismiss()
|
||||
}
|
||||
case let .awaitingEmailConfirmation(password, pattern, codeLength):
|
||||
strongSelf.updateState(animated: false, { state in
|
||||
var state = state
|
||||
if let verificationState = state.verificationState, case .noChallenge = verificationState {
|
||||
state.verificationState = .noChallenge(.awaitingConfirmation(password: password, emailPattern: pattern, codeLength: codeLength))
|
||||
}
|
||||
return state
|
||||
})
|
||||
if shouldDismiss {
|
||||
controller.dismiss()
|
||||
}
|
||||
case let .passwordSet(password, hasRecoveryEmail, _):
|
||||
strongSelf.updateState(animated: false, { state in
|
||||
var state = state
|
||||
state.verificationState = .passwordChallenge(hint: "", state: .none, hasRecoveryEmail: hasRecoveryEmail)
|
||||
return state
|
||||
})
|
||||
if let password = password {
|
||||
strongSelf.checkPassword(password: password, inBackground: true, completion: { [weak controller] in
|
||||
controller?.dismiss()
|
||||
})
|
||||
} else if shouldDismiss {
|
||||
controller.dismiss()
|
||||
}
|
||||
}
|
||||
})
|
||||
self.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
|
||||
/*var completionImpl: ((String, String, Bool) -> Void)?
|
||||
let state: CreatePasswordState
|
||||
if let emailPattern = emailPattern {
|
||||
state = .pendingVerification(emailPattern: emailPattern)
|
||||
} else {
|
||||
state = .setup(currentPassword: nil)
|
||||
}
|
||||
let controller = createPasswordController(account: self.account, context: .secureId, state: state, completion: { password, hint, hasRecoveryEmail in
|
||||
completionImpl?(password, hint, hasRecoveryEmail)
|
||||
}, updatePasswordEmailConfirmation: { [weak self] pattern in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.updateState(animated: false, { state in
|
||||
var state = state
|
||||
if let verificationState = state.verificationState, case .noChallenge = verificationState {
|
||||
state.verificationState = .noChallenge(pattern?.1)
|
||||
}
|
||||
return state
|
||||
})
|
||||
})
|
||||
completionImpl = { [weak self, weak controller] password, hint, hasRecoveryEmail in
|
||||
guard let strongSelf = self else {
|
||||
controller?.dismiss()
|
||||
return
|
||||
}
|
||||
strongSelf.updateState(animated: false, { state in
|
||||
var state = state
|
||||
state.verificationState = .passwordChallenge(hint: hint, state: .none, hasRecoveryEmail: hasRecoveryEmail)
|
||||
return state
|
||||
})
|
||||
strongSelf.checkPassword(password: password, inBackground: true, completion: {
|
||||
controller?.view.endEditing(true)
|
||||
controller?.dismiss()
|
||||
})
|
||||
}
|
||||
self.present(controller, in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))*/
|
||||
}
|
||||
|
||||
@objc private func grantAccess() {
|
||||
switch self.state {
|
||||
case let .form(form):
|
||||
if case let .form(reqForm) = self.mode, let encryptedFormData = form.encryptedFormData, let formData = form.formData {
|
||||
let values = parseRequestedFormFields(formData.requestedFields, values: formData.values, primaryLanguageByCountry: encryptedFormData.primaryLanguageByCountry).map({ $0.1 }).flatMap({ $0 })
|
||||
|
||||
let _ = (grantSecureIdAccess(network: self.context.account.network, peerId: encryptedFormData.servicePeer.id, publicKey: reqForm.publicKey, scope: reqForm.scope, opaquePayload: reqForm.opaquePayload, opaqueNonce: reqForm.opaqueNonce, values: values, requestedFields: formData.requestedFields)
|
||||
|> deliverOnMainQueue).start(completed: { [weak self] in
|
||||
self?.dismiss()
|
||||
self?.openUrl(secureIdCallbackUrl(with: reqForm.callbackUrl, peerId: reqForm.peerId, result: .success, parameters: [:]))
|
||||
})
|
||||
}
|
||||
case .list:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func infoPressed() {
|
||||
self.present(textAlertController(context: self.context, title: self.presentationData.strings.Passport_InfoTitle, text: self.presentationData.strings.Passport_InfoText.replacingOccurrences(of: "**", with: ""), actions: [TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {}), TextAlertAction(type: .genericAction, title: self.presentationData.strings.Passport_InfoLearnMore, action: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.context.sharedContext.openExternalUrl(context: strongSelf.context, urlContext: .generic, url: strongSelf.presentationData.strings.Passport_InfoFAQ_URL, forceExternal: false, presentationData: strongSelf.presentationData, navigationController: strongSelf.navigationController as? NavigationController, dismissInput: {
|
||||
self?.view.endEditing(true)
|
||||
})
|
||||
})]), in: .window(.root))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user