mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Password reset fixes
This commit is contained in:
parent
ad5d4f5c75
commit
a2f0f5b1ce
@ -6549,3 +6549,4 @@ Sorry for the inconvenience.";
|
||||
"TwoStepAuth.CancelResetTitle" = "Cancel Reset";
|
||||
"TwoStepAuth.ResetAction" = "Reset Password";
|
||||
"TwoStepAuth.CancelResetText" = "Cancel the password resetting process? If you proceed, the expired part of the 7-day delay will be lost.";
|
||||
"TwoStepAuth.RecoveryEmailResetNoAccess" = "No access";
|
||||
|
@ -686,7 +686,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
controller.dismiss()
|
||||
}
|
||||
switch update {
|
||||
case .noPassword, .awaitingEmailConfirmation:
|
||||
case .noPassword, .awaitingEmailConfirmation, .pendingPasswordReset:
|
||||
break
|
||||
case .passwordSet:
|
||||
var updatedToken = webToken
|
||||
@ -754,7 +754,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
controller.dismiss()
|
||||
}
|
||||
switch update {
|
||||
case .noPassword, .awaitingEmailConfirmation:
|
||||
case .noPassword, .awaitingEmailConfirmation, .pendingPasswordReset:
|
||||
break
|
||||
case .passwordSet:
|
||||
var updatedToken = webToken
|
||||
@ -810,7 +810,7 @@ final class BotCheckoutControllerNode: ItemListControllerNode, PKPaymentAuthoriz
|
||||
controller.dismiss()
|
||||
}
|
||||
switch update {
|
||||
case .noPassword, .awaitingEmailConfirmation:
|
||||
case .noPassword, .awaitingEmailConfirmation, .pendingPasswordReset:
|
||||
break
|
||||
case .passwordSet:
|
||||
var updatedToken = token
|
||||
|
@ -558,7 +558,7 @@ public final class SecureIdAuthController: ViewController, StandalonePresentable
|
||||
return
|
||||
}
|
||||
switch update {
|
||||
case .noPassword:
|
||||
case .noPassword, .pendingPasswordReset:
|
||||
strongSelf.updateState(animated: false, { state in
|
||||
var state = state
|
||||
if let verificationState = state.verificationState, case .noChallenge = verificationState {
|
||||
|
@ -179,7 +179,7 @@ public func resetPasswordController(context: AccountContext, emailPattern: Strin
|
||||
state.checking = true
|
||||
return state
|
||||
}
|
||||
saveDisposable.set((context.engine.auth.recoverTwoStepVerificationPassword(code: state.code)
|
||||
saveDisposable.set((context.engine.auth.performPasswordRecovery(code: state.code, updatedPassword: .none)
|
||||
|> deliverOnMainQueue).start(error: { error in
|
||||
updateState { state in
|
||||
var state = state
|
||||
@ -190,7 +190,7 @@ public func resetPasswordController(context: AccountContext, emailPattern: Strin
|
||||
switch error {
|
||||
case .invalidCode:
|
||||
text = presentationData.strings.TwoStepAuth_RecoveryCodeInvalid
|
||||
case .codeExpired:
|
||||
case .expired:
|
||||
text = presentationData.strings.TwoStepAuth_RecoveryCodeExpired
|
||||
case .limitExceeded:
|
||||
text = presentationData.strings.TwoStepAuth_FloodError
|
||||
|
@ -138,6 +138,7 @@ public enum SetupTwoStepVerificationStateUpdate {
|
||||
case noPassword
|
||||
case awaitingEmailConfirmation(password: String, pattern: String, codeLength: Int32?)
|
||||
case passwordSet(password: String?, hasRecoveryEmail: Bool, hasSecureValues: Bool)
|
||||
case pendingPasswordReset
|
||||
}
|
||||
|
||||
final class SetupTwoStepVerificationControllerNode: ViewControllerTracingNode {
|
||||
|
@ -14,18 +14,27 @@ import AnimatedStickerNode
|
||||
|
||||
public enum TwoFactorDataInputMode {
|
||||
public struct Recovery {
|
||||
public var code: String
|
||||
public var syncContacts: Bool
|
||||
public var account: UnauthorizedAccount
|
||||
|
||||
public init(code: String, syncContacts: Bool, account: UnauthorizedAccount) {
|
||||
self.code = code
|
||||
self.syncContacts = syncContacts
|
||||
self.account = account
|
||||
public enum Mode {
|
||||
case notAuthorized(syncContacts: Bool)
|
||||
case authorized
|
||||
}
|
||||
|
||||
public var code: String
|
||||
public var mode: Mode
|
||||
|
||||
public init(code: String, mode: Mode) {
|
||||
self.code = code
|
||||
self.mode = mode
|
||||
}
|
||||
}
|
||||
|
||||
public enum PasswordRecoveryEmailMode {
|
||||
case notAuthorized(syncContacts: Bool)
|
||||
case authorized
|
||||
}
|
||||
|
||||
case password
|
||||
case passwordRecoveryEmail(emailPattern: String, mode: PasswordRecoveryEmailMode)
|
||||
case passwordRecovery(Recovery)
|
||||
case emailAddress(password: String, hint: String)
|
||||
case updateEmailAddress(password: String)
|
||||
@ -39,8 +48,9 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
private var presentationData: PresentationData
|
||||
private let mode: TwoFactorDataInputMode
|
||||
private let stateUpdated: (SetupTwoStepVerificationStateUpdate) -> Void
|
||||
private let actionDisposable = MetaDisposable()
|
||||
|
||||
public init(sharedContext: SharedAccountContext, engine: SomeTelegramEngine, mode: TwoFactorDataInputMode, stateUpdated: @escaping (SetupTwoStepVerificationStateUpdate) -> Void) {
|
||||
public init(sharedContext: SharedAccountContext, engine: SomeTelegramEngine, mode: TwoFactorDataInputMode, stateUpdated: @escaping (SetupTwoStepVerificationStateUpdate) -> Void, presentation: ViewControllerNavigationPresentation = .modalInLargeLayout) {
|
||||
self.sharedContext = sharedContext
|
||||
self.engine = engine
|
||||
self.mode = mode
|
||||
@ -54,7 +64,7 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
self.navigationPresentation = .modalInLargeLayout
|
||||
self.navigationPresentation = presentation
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
self.navigationBar?.intrinsicCanTransitionInline = false
|
||||
|
||||
@ -65,6 +75,10 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.actionDisposable.dispose()
|
||||
}
|
||||
|
||||
@objc private func backPressed() {
|
||||
self.dismiss()
|
||||
}
|
||||
@ -101,8 +115,57 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .passwordHint(recovery: nil, password: values[0]), stateUpdated: strongSelf.stateUpdated))
|
||||
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .passwordHint(recovery: nil, password: values[0]), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation))
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
case let .passwordRecoveryEmail(_, mode):
|
||||
guard let text = (strongSelf.displayNode as! TwoFactorDataInputScreenNode).inputText.first, !text.isEmpty else {
|
||||
return
|
||||
}
|
||||
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil))
|
||||
strongSelf.present(statusController, in: .window(.root))
|
||||
|
||||
strongSelf.actionDisposable.set((strongSelf.engine.auth.checkPasswordRecoveryCode(code: text)
|
||||
|> deliverOnMainQueue).start(error: { [weak statusController] error in
|
||||
statusController?.dismiss()
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let text: String
|
||||
switch error {
|
||||
case .limitExceeded:
|
||||
text = strongSelf.presentationData.strings.LoginPassword_FloodError
|
||||
case .invalidCode:
|
||||
text = strongSelf.presentationData.strings.Login_InvalidCodeError
|
||||
case .expired:
|
||||
text = strongSelf.presentationData.strings.Login_CodeExpiredError
|
||||
case .generic:
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
}
|
||||
|
||||
strongSelf.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: { [weak statusController] in
|
||||
statusController?.dismiss()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let mappedMode: TwoFactorDataInputMode.Recovery.Mode
|
||||
switch mode {
|
||||
case .authorized:
|
||||
mappedMode = .authorized
|
||||
case let .notAuthorized(syncContacts):
|
||||
mappedMode = .notAuthorized(syncContacts: syncContacts)
|
||||
}
|
||||
|
||||
let setupController = TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .passwordRecovery(TwoFactorDataInputMode.Recovery(code: text, mode: mappedMode)), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation)
|
||||
|
||||
guard let navigationController = strongSelf.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
navigationController.replaceController(strongSelf, with: setupController, animated: true)
|
||||
}))
|
||||
case let .passwordRecovery(recovery):
|
||||
let values = (strongSelf.displayNode as! TwoFactorDataInputScreenNode).inputText
|
||||
if values.count != 2 {
|
||||
@ -120,17 +183,7 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
guard let navigationController = strongSelf.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
var controllers = navigationController.viewControllers.filter { controller in
|
||||
if controller is TwoFactorAuthSplashScreen {
|
||||
return false
|
||||
}
|
||||
if controller is TwoFactorDataInputScreen && controller !== strongSelf {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .passwordHint(recovery: recovery, password: values[0]), stateUpdated: strongSelf.stateUpdated))
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
navigationController.replaceController(strongSelf, with: TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .passwordHint(recovery: recovery, password: values[0]), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation), animated: true)
|
||||
case let .emailAddress(password, hint):
|
||||
guard let text = (strongSelf.displayNode as! TwoFactorDataInputScreenNode).inputText.first, !text.isEmpty else {
|
||||
return
|
||||
@ -163,7 +216,7 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailConfirmation(passwordAndHint: (password, hint), emailPattern: text, codeLength: pendingEmail.codeLength.flatMap(Int.init)), stateUpdated: strongSelf.stateUpdated))
|
||||
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailConfirmation(passwordAndHint: (password, hint), emailPattern: text, codeLength: pendingEmail.codeLength.flatMap(Int.init)), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation))
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
} else {
|
||||
guard let navigationController = strongSelf.navigationController as? NavigationController else {
|
||||
@ -337,7 +390,7 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
if let recovery = recovery {
|
||||
strongSelf.performRecovery(recovery: recovery, password: password, hint: value)
|
||||
} else {
|
||||
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: value), stateUpdated: strongSelf.stateUpdated))
|
||||
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: value), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation))
|
||||
}
|
||||
}
|
||||
}, skipAction: { [weak self] in
|
||||
@ -405,7 +458,7 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
if let recovery = recovery {
|
||||
strongSelf.performRecovery(recovery: recovery, password: password, hint: "")
|
||||
} else {
|
||||
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: ""), stateUpdated: strongSelf.stateUpdated))
|
||||
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: ""), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation))
|
||||
}
|
||||
case let .passwordRecovery(recovery):
|
||||
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: strongSelf.presentationData.strings.TwoFactorSetup_PasswordRecovery_SkipAlertTitle, text: strongSelf.presentationData.strings.TwoFactorSetup_PasswordRecovery_SkipAlertText, actions: [
|
||||
@ -439,10 +492,32 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
}
|
||||
return true
|
||||
}
|
||||
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: hint), stateUpdated: strongSelf.stateUpdated))
|
||||
controllers.append(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .emailAddress(password: password, hint: hint), stateUpdated: strongSelf.stateUpdated, presentation: strongSelf.navigationPresentation))
|
||||
navigationController.setViewControllers(controllers, animated: true)
|
||||
} else {
|
||||
}
|
||||
case .passwordRecoveryEmail:
|
||||
switch strongSelf.engine {
|
||||
case let .authorized(engine):
|
||||
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailableResetTitle, text: strongSelf.presentationData.strings.TwoStepAuth_RecoveryEmailResetText, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.TwoStepAuth_RecoveryUnavailableResetAction, action: {
|
||||
let _ = (engine.auth.requestTwoStepPasswordReset()
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch result {
|
||||
case .done, .waitingForReset:
|
||||
strongSelf.stateUpdated(.pendingPasswordReset)
|
||||
case .declined:
|
||||
break
|
||||
case let .error(reason):
|
||||
break
|
||||
}
|
||||
})
|
||||
})]), in: .window(.root))
|
||||
case .unauthorized:
|
||||
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_RecoveryFailed, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -450,11 +525,38 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
switch strongSelf.mode {
|
||||
case .passwordRecoveryEmail:
|
||||
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil))
|
||||
strongSelf.present(statusController, in: .window(.root))
|
||||
|
||||
let _ = (strongSelf.engine.auth.resendTwoStepRecoveryEmail()
|
||||
let _ = (strongSelf.engine.auth.requestTwoStepVerificationPasswordRecoveryCode()
|
||||
|> deliverOnMainQueue).start(error: { [weak statusController] error in
|
||||
statusController?.dismiss()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let text: String
|
||||
switch error {
|
||||
case .limitExceeded:
|
||||
text = strongSelf.presentationData.strings.TwoStepAuth_FloodError
|
||||
case .generic:
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
}
|
||||
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}, completed: { [weak statusController] in
|
||||
statusController?.dismiss()
|
||||
})
|
||||
default:
|
||||
guard case let .authorized(engine) = strongSelf.engine else {
|
||||
return
|
||||
}
|
||||
let statusController = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil))
|
||||
strongSelf.present(statusController, in: .window(.root))
|
||||
|
||||
let _ = (engine.auth.resendTwoStepRecoveryEmail()
|
||||
|> deliverOnMainQueue).start(error: { [weak statusController] error in
|
||||
statusController?.dismiss()
|
||||
|
||||
@ -473,6 +575,7 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
}, completed: { [weak statusController] in
|
||||
statusController?.dismiss()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
self.displayNodeDidLoad()
|
||||
@ -485,13 +588,58 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
}
|
||||
|
||||
private func performRecovery(recovery: TwoFactorDataInputMode.Recovery, password: String, hint: String) {
|
||||
guard case let .unauthorized(engine) = self.engine else {
|
||||
return
|
||||
}
|
||||
let statusController = OverlayStatusController(theme: self.presentationData.theme, type: .loading(cancelled: nil))
|
||||
self.present(statusController, in: .window(.root))
|
||||
|
||||
let _ = (engine.auth.performPasswordRecovery(accountManager: self.sharedContext.accountManager, code: recovery.code, syncContacts: recovery.syncContacts, updatedPassword: password.isEmpty ? .none : .password(password: password, hint: hint, email: nil))
|
||||
switch self.engine {
|
||||
case let .unauthorized(engine):
|
||||
var syncContacts = false
|
||||
switch recovery.mode {
|
||||
case let .notAuthorized(syncContactsValue):
|
||||
syncContacts = syncContactsValue
|
||||
case .authorized:
|
||||
break
|
||||
}
|
||||
let _ = (engine.auth.performPasswordRecovery(code: recovery.code, updatedPassword: password.isEmpty ? .none : .password(password: password, hint: hint, email: nil))
|
||||
|> deliverOnMainQueue).start(next: { [weak self, weak statusController] recoveredAccountData in
|
||||
statusController?.dismiss()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
if password.isEmpty {
|
||||
strongSelf.stateUpdated(.noPassword)
|
||||
} else {
|
||||
strongSelf.stateUpdated(.passwordSet(password: password, hasRecoveryEmail: true, hasSecureValues: false))
|
||||
}
|
||||
|
||||
(strongSelf.navigationController as? NavigationController)?.replaceController(strongSelf, with: TwoFactorAuthSplashScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .recoveryDone(recoveredAccountData: recoveredAccountData, syncContacts: syncContacts)), animated: true)
|
||||
}, error: { [weak self, weak statusController] error in
|
||||
statusController?.dismiss()
|
||||
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let text: String
|
||||
switch error {
|
||||
case .limitExceeded:
|
||||
text = strongSelf.presentationData.strings.LoginPassword_FloodError
|
||||
case .invalidCode:
|
||||
text = strongSelf.presentationData.strings.Login_InvalidCodeError
|
||||
case .expired:
|
||||
text = strongSelf.presentationData.strings.Login_CodeExpiredError
|
||||
case .generic:
|
||||
text = strongSelf.presentationData.strings.Login_UnknownError
|
||||
}
|
||||
|
||||
strongSelf.present(textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
}, completed: { [weak self, weak statusController] in
|
||||
statusController?.dismiss()
|
||||
})
|
||||
case let .authorized(engine):
|
||||
let _ = (engine.auth.performPasswordRecovery(code: recovery.code, updatedPassword: password.isEmpty ? .none : .password(password: password, hint: hint, email: nil))
|
||||
|> deliverOnMainQueue).start(error: { [weak self, weak statusController] error in
|
||||
statusController?.dismiss()
|
||||
|
||||
@ -519,10 +667,16 @@ public final class TwoFactorDataInputScreen: ViewController {
|
||||
return
|
||||
}
|
||||
|
||||
if password.isEmpty {
|
||||
strongSelf.stateUpdated(.noPassword)
|
||||
} else {
|
||||
strongSelf.stateUpdated(.passwordSet(password: password, hasRecoveryEmail: true, hasSecureValues: false))
|
||||
}
|
||||
strongSelf.dismiss()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum TwoFactorDataInputTextNodeType {
|
||||
case password(confirmation: Bool)
|
||||
@ -805,7 +959,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
||||
self.scrollNode.canCancelAllTouchesInViews = true
|
||||
|
||||
switch mode {
|
||||
case .password, .passwordRecovery, .emailAddress, .updateEmailAddress:
|
||||
case .password, .passwordRecovery, .passwordRecoveryEmail, .emailAddress, .updateEmailAddress:
|
||||
self.monkeyNode = ManagedMonkeyAnimationNode()
|
||||
case .emailConfirmation:
|
||||
if let path = getAppBundle().path(forResource: "TwoFactorSetupMail", ofType: "tgs") {
|
||||
@ -909,6 +1063,33 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
||||
toggleTextHidden?(node)
|
||||
}),
|
||||
]
|
||||
case let .passwordRecoveryEmail(emailPattern, _):
|
||||
title = presentationData.strings.TwoFactorSetup_EmailVerification_Title
|
||||
let (rawText, ranges) = presentationData.strings.TwoFactorSetup_EmailVerification_Text(emailPattern)
|
||||
|
||||
let string = NSMutableAttributedString()
|
||||
string.append(NSAttributedString(string: rawText, font: Font.regular(16.0), textColor: presentationData.theme.list.itemPrimaryTextColor))
|
||||
for (_, range) in ranges {
|
||||
string.addAttribute(.font, value: Font.semibold(16.0), range: range)
|
||||
}
|
||||
|
||||
text = string
|
||||
|
||||
buttonText = presentationData.strings.TwoFactorSetup_EmailVerification_Action
|
||||
skipActionText = ""
|
||||
changeEmailActionText = presentationData.strings.TwoStepAuth_RecoveryEmailResetNoAccess
|
||||
resendCodeActionText = presentationData.strings.TwoFactorSetup_EmailVerification_ResendAction
|
||||
inputNodes = [
|
||||
TwoFactorDataInputTextNode(theme: presentationData.theme, mode: .code, placeholder: presentationData.strings.TwoFactorSetup_EmailVerification_Placeholder, focusUpdated: { node, focused in
|
||||
focusUpdated?(node, focused)
|
||||
}, next: { node in
|
||||
next?(node)
|
||||
}, updated: { node in
|
||||
updated?(node)
|
||||
}, toggleTextHidden: { node in
|
||||
toggleTextHidden?(node)
|
||||
}),
|
||||
]
|
||||
case let .emailConfirmation(_, emailPattern, _):
|
||||
title = presentationData.strings.TwoFactorSetup_EmailVerification_Title
|
||||
let (rawText, ranges) = presentationData.strings.TwoFactorSetup_EmailVerification_Text(emailPattern)
|
||||
@ -985,7 +1166,6 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
||||
self.changeEmailActionTitleNode.attributedText = NSAttributedString(string: changeEmailActionText, font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemAccentColor)
|
||||
self.changeEmailActionButtonNode = HighlightTrackingButtonNode()
|
||||
self.changeEmailActionButtonNode.isHidden = changeEmailActionText.isEmpty
|
||||
self.changeEmailActionButtonNode.isHidden = changeEmailActionText.isEmpty
|
||||
|
||||
self.resendCodeActionTitleNode = ImmediateTextNode()
|
||||
self.resendCodeActionTitleNode.isUserInteractionEnabled = false
|
||||
@ -1129,7 +1309,7 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
||||
break
|
||||
}
|
||||
}
|
||||
focusUpdated = { [weak self] node, _ in
|
||||
focusUpdated = { node, _ in
|
||||
DispatchQueue.main.async {
|
||||
updateAnimations()
|
||||
}
|
||||
@ -1156,6 +1336,18 @@ private final class TwoFactorDataInputScreenNode: ViewControllerTracingNode, UIS
|
||||
if let codeLength = codeLength, text.count == codeLength {
|
||||
action()
|
||||
}
|
||||
case .passwordRecoveryEmail:
|
||||
let text = strongSelf.inputNodes[0].text
|
||||
let hasText = !text.isEmpty
|
||||
strongSelf.buttonNode.isHidden = !hasText
|
||||
strongSelf.changeEmailActionTitleNode.isHidden = hasText
|
||||
strongSelf.changeEmailActionButtonNode.isHidden = hasText
|
||||
strongSelf.resendCodeActionTitleNode.isHidden = hasText
|
||||
strongSelf.resendCodeActionButtonNode.isHidden = hasText
|
||||
|
||||
if text.count == 6 {
|
||||
action()
|
||||
}
|
||||
case .passwordHint:
|
||||
let hasText = strongSelf.inputNodes.contains(where: { !$0.text.isEmpty })
|
||||
strongSelf.buttonNode.isHidden = !hasText
|
||||
|
@ -15,6 +15,7 @@ import TelegramCore
|
||||
public enum TwoFactorAuthSplashMode {
|
||||
case intro
|
||||
case done
|
||||
case recoveryDone(recoveredAccountData: RecoveredAccountData, syncContacts: Bool)
|
||||
}
|
||||
|
||||
public final class TwoFactorAuthSplashScreen: ViewController {
|
||||
@ -23,7 +24,7 @@ public final class TwoFactorAuthSplashScreen: ViewController {
|
||||
private var presentationData: PresentationData
|
||||
private var mode: TwoFactorAuthSplashMode
|
||||
|
||||
public init(sharedContext: SharedAccountContext, engine: SomeTelegramEngine, mode: TwoFactorAuthSplashMode) {
|
||||
public init(sharedContext: SharedAccountContext, engine: SomeTelegramEngine, mode: TwoFactorAuthSplashMode, presentation: ViewControllerNavigationPresentation = .modalInLargeLayout) {
|
||||
self.sharedContext = sharedContext
|
||||
self.engine = engine
|
||||
self.mode = mode
|
||||
@ -35,8 +36,9 @@ public final class TwoFactorAuthSplashScreen: ViewController {
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close)))
|
||||
|
||||
self.navigationPresentation = presentation
|
||||
|
||||
self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style
|
||||
self.navigationPresentation = .modalInLargeLayout
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
self.navigationBar?.intrinsicCanTransitionInline = false
|
||||
|
||||
@ -58,12 +60,22 @@ public final class TwoFactorAuthSplashScreen: ViewController {
|
||||
switch strongSelf.mode {
|
||||
case .intro:
|
||||
strongSelf.push(TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: strongSelf.engine, mode: .password, stateUpdated: { _ in
|
||||
}))
|
||||
}, presentation: strongSelf.navigationPresentation))
|
||||
case .done:
|
||||
guard let navigationController = strongSelf.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
navigationController.filterController(strongSelf, animated: true)
|
||||
case let .recoveryDone(recoveredAccountData, syncContacts):
|
||||
guard let navigationController = strongSelf.navigationController as? NavigationController else {
|
||||
return
|
||||
}
|
||||
switch strongSelf.engine {
|
||||
case let .unauthorized(engine):
|
||||
let _ = loginWithRecoveredAccountData(accountManager: strongSelf.sharedContext.accountManager, account: engine.account, recoveredAccountData: recoveredAccountData, syncContacts: syncContacts).start()
|
||||
case .authorized:
|
||||
navigationController.filterController(strongSelf, animated: true)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@ -124,6 +136,16 @@ private final class TwoFactorAuthSplashScreenNode: ViewControllerTracingNode {
|
||||
text = NSAttributedString(string: self.presentationData.strings.TwoFactorSetup_Done_Text, font: textFont, textColor: textColor)
|
||||
buttonText = self.presentationData.strings.TwoFactorSetup_Done_Action
|
||||
|
||||
if let path = getAppBundle().path(forResource: "TwoFactorSetupDone", ofType: "tgs") {
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationSize = CGSize(width: 124.0, height: 124.0)
|
||||
self.animationNode.visibility = true
|
||||
}
|
||||
case .recoveryDone:
|
||||
title = self.presentationData.strings.TwoFactorSetup_Done_Title
|
||||
text = NSAttributedString(string: self.presentationData.strings.TwoFactorSetup_Done_Text, font: textFont, textColor: textColor)
|
||||
buttonText = self.presentationData.strings.TwoFactorSetup_Done_Action
|
||||
|
||||
if let path = getAppBundle().path(forResource: "TwoFactorSetupDone", ofType: "tgs") {
|
||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, mode: .direct(cachePathPrefix: nil))
|
||||
self.animationSize = CGSize(width: 124.0, height: 124.0)
|
||||
|
@ -1,260 +0,0 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SyncCore
|
||||
import TelegramPresentationData
|
||||
import ItemListUI
|
||||
import PresentationDataUtils
|
||||
import TextFormat
|
||||
import AccountContext
|
||||
import AlertUI
|
||||
import PresentationDataUtils
|
||||
import Markdown
|
||||
|
||||
private final class TwoStepVerificationResetControllerArguments {
|
||||
let updateEntryText: (String) -> Void
|
||||
let next: () -> Void
|
||||
let openEmailInaccessible: () -> Void
|
||||
|
||||
init(updateEntryText: @escaping (String) -> Void, next: @escaping () -> Void, openEmailInaccessible: @escaping () -> Void) {
|
||||
self.updateEntryText = updateEntryText
|
||||
self.next = next
|
||||
self.openEmailInaccessible = openEmailInaccessible
|
||||
}
|
||||
}
|
||||
|
||||
private enum TwoStepVerificationResetSection: Int32 {
|
||||
case password
|
||||
}
|
||||
|
||||
private enum TwoStepVerificationResetTag: ItemListItemTag {
|
||||
case input
|
||||
|
||||
func isEqual(to other: ItemListItemTag) -> Bool {
|
||||
if let other = other as? TwoStepVerificationResetTag {
|
||||
switch self {
|
||||
case .input:
|
||||
if case .input = other {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum TwoStepVerificationResetEntry: ItemListNodeEntry {
|
||||
case codeEntry(PresentationTheme, PresentationStrings, String, String)
|
||||
case codeInfo(PresentationTheme, String)
|
||||
|
||||
var section: ItemListSectionId {
|
||||
return TwoStepVerificationResetSection.password.rawValue
|
||||
}
|
||||
|
||||
var stableId: Int32 {
|
||||
switch self {
|
||||
case .codeEntry:
|
||||
return 0
|
||||
case .codeInfo:
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
static func ==(lhs: TwoStepVerificationResetEntry, rhs: TwoStepVerificationResetEntry) -> Bool {
|
||||
switch lhs {
|
||||
case let .codeEntry(lhsTheme, lhsStrings, lhsPlaceholder, lhsText):
|
||||
if case let .codeEntry(rhsTheme, rhsStrings, rhsPlaceholder, rhsText) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsPlaceholder == rhsPlaceholder, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .codeInfo(lhsTheme, lhsText):
|
||||
if case let .codeInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func <(lhs: TwoStepVerificationResetEntry, rhs: TwoStepVerificationResetEntry) -> Bool {
|
||||
return lhs.stableId < rhs.stableId
|
||||
}
|
||||
|
||||
func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem {
|
||||
let arguments = arguments as! TwoStepVerificationResetControllerArguments
|
||||
switch self {
|
||||
case let .codeEntry(theme, strings, placeholder, text):
|
||||
return ItemListSingleLineInputItem(presentationData: presentationData, title: NSAttributedString(string: placeholder, textColor: theme.list.itemPrimaryTextColor), text: text, placeholder: "", type: .password, spacing: 10.0, tag: TwoStepVerificationResetTag.input, sectionId: self.section, textUpdated: { updatedText in
|
||||
arguments.updateEntryText(updatedText)
|
||||
}, action: {
|
||||
arguments.next()
|
||||
})
|
||||
case let .codeInfo(theme, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct TwoStepVerificationResetControllerState: Equatable {
|
||||
let codeText: String
|
||||
let checking: Bool
|
||||
|
||||
init(codeText: String, checking: Bool) {
|
||||
self.codeText = codeText
|
||||
self.checking = checking
|
||||
}
|
||||
|
||||
static func ==(lhs: TwoStepVerificationResetControllerState, rhs: TwoStepVerificationResetControllerState) -> Bool {
|
||||
if lhs.codeText != rhs.codeText {
|
||||
return false
|
||||
}
|
||||
if lhs.checking != rhs.checking {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func withUpdatedCodeText(_ codeText: String) -> TwoStepVerificationResetControllerState {
|
||||
return TwoStepVerificationResetControllerState(codeText: codeText, checking: self.checking)
|
||||
}
|
||||
|
||||
func withUpdatedChecking(_ checking: Bool) -> TwoStepVerificationResetControllerState {
|
||||
return TwoStepVerificationResetControllerState(codeText: self.codeText, checking: checking)
|
||||
}
|
||||
}
|
||||
|
||||
private func twoStepVerificationResetControllerEntries(presentationData: PresentationData, state: TwoStepVerificationResetControllerState, emailPattern: String) -> [TwoStepVerificationResetEntry] {
|
||||
var entries: [TwoStepVerificationResetEntry] = []
|
||||
|
||||
entries.append(.codeEntry(presentationData.theme, presentationData.strings, presentationData.strings.TwoStepAuth_RecoveryCode, state.codeText))
|
||||
entries.append(.codeInfo(presentationData.theme, "\(presentationData.strings.TwoStepAuth_RecoveryCodeHelp)\n\n[\(presentationData.strings.TwoStepAuth_RecoveryEmailUnavailable(escapedPlaintextForMarkdown(emailPattern)).0)]()"))
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
func twoStepVerificationResetController(context: AccountContext, emailPattern: String, result: Promise<Bool>, requestedRecoveryReset: @escaping () -> Void) -> ViewController {
|
||||
let initialState = TwoStepVerificationResetControllerState(codeText: "", checking: false)
|
||||
|
||||
let statePromise = ValuePromise(initialState, ignoreRepeated: true)
|
||||
let stateValue = Atomic(value: initialState)
|
||||
let updateState: ((TwoStepVerificationResetControllerState) -> TwoStepVerificationResetControllerState) -> Void = { f in
|
||||
statePromise.set(stateValue.modify { f($0) })
|
||||
}
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments) -> Void)?
|
||||
|
||||
let actionsDisposable = DisposableSet()
|
||||
|
||||
let resetPasswordDisposable = MetaDisposable()
|
||||
actionsDisposable.add(resetPasswordDisposable)
|
||||
|
||||
let checkCode: () -> Void = {
|
||||
var code: String?
|
||||
updateState { state in
|
||||
if state.checking || state.codeText.isEmpty {
|
||||
return state
|
||||
} else {
|
||||
code = state.codeText
|
||||
return state.withUpdatedChecking(true)
|
||||
}
|
||||
}
|
||||
if let code = code {
|
||||
resetPasswordDisposable.set((context.engine.auth.recoverTwoStepVerificationPassword(code: code) |> deliverOnMainQueue).start(error: { error in
|
||||
updateState {
|
||||
return $0.withUpdatedChecking(false)
|
||||
}
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let alertText: String
|
||||
switch error {
|
||||
case .generic:
|
||||
alertText = presentationData.strings.Login_UnknownError
|
||||
case .invalidCode:
|
||||
alertText = presentationData.strings.Login_InvalidCodeError
|
||||
case .codeExpired:
|
||||
alertText = presentationData.strings.Login_CodeExpiredError
|
||||
case .limitExceeded:
|
||||
alertText = presentationData.strings.Login_CodeFloodError
|
||||
}
|
||||
presentControllerImpl?(textAlertController(context: context, title: nil, text: alertText, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}, completed: {
|
||||
updateState {
|
||||
return $0.withUpdatedChecking(false)
|
||||
}
|
||||
result.set(.single(true))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
let arguments = TwoStepVerificationResetControllerArguments(updateEntryText: { updatedText in
|
||||
updateState {
|
||||
$0.withUpdatedCodeText(updatedText)
|
||||
}
|
||||
}, next: {
|
||||
checkCode()
|
||||
}, openEmailInaccessible: {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.TwoStepAuth_RecoveryUnavailableResetTitle, text: presentationData.strings.TwoStepAuth_RecoveryEmailResetText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.TwoStepAuth_RecoveryUnavailableResetAction, action: {
|
||||
let _ = (context.engine.auth.requestTwoStepPasswordReset()
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
switch result {
|
||||
case .done, .waitingForReset:
|
||||
requestedRecoveryReset()
|
||||
case .declined:
|
||||
break
|
||||
case let .error(reason):
|
||||
break
|
||||
}
|
||||
})
|
||||
})]), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
})
|
||||
|
||||
let signal = combineLatest(context.sharedContext.presentationData, statePromise.get()) |> deliverOnMainQueue
|
||||
|> map { presentationData, state -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
|
||||
let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: {
|
||||
dismissImpl?()
|
||||
})
|
||||
|
||||
var rightNavigationButton: ItemListNavigationButton?
|
||||
if state.checking {
|
||||
rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: true, action: {})
|
||||
} else {
|
||||
var nextEnabled = true
|
||||
if state.codeText.isEmpty {
|
||||
nextEnabled = false
|
||||
}
|
||||
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Next), style: .bold, enabled: nextEnabled, action: {
|
||||
checkCode()
|
||||
})
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.TwoStepAuth_RecoveryTitle), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: twoStepVerificationResetControllerEntries(presentationData: presentationData, state: state, emailPattern: emailPattern), style: .blocks, focusItemTag: TwoStepVerificationResetTag.input, emptyStateItem: nil, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
} |> afterDisposed {
|
||||
actionsDisposable.dispose()
|
||||
}
|
||||
|
||||
let controller = ItemListController(context: context, state: signal)
|
||||
presentControllerImpl = { [weak controller] c, p in
|
||||
if let controller = controller {
|
||||
controller.present(c, in: .window(.root), with: p)
|
||||
}
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
}
|
||||
|
||||
return controller
|
||||
}
|
@ -293,6 +293,7 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
|
||||
|
||||
var replaceControllerImpl: ((ViewController, Bool) -> Void)?
|
||||
var presentControllerImpl: ((ViewController, ViewControllerPresentationArguments?) -> Void)?
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
var dismissImpl: (() -> Void)?
|
||||
|
||||
let actionsDisposable = DisposableSet()
|
||||
@ -508,7 +509,27 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
|
||||
return state
|
||||
}
|
||||
|
||||
var completionImpl: ((Bool) -> Void)?
|
||||
var stateUpdated: ((SetupTwoStepVerificationStateUpdate) -> Void)?
|
||||
let controller = TwoFactorDataInputScreen(sharedContext: context.sharedContext, engine: .authorized(context.engine), mode: .passwordRecoveryEmail(emailPattern: emailPattern, mode: .authorized), stateUpdated: { state in
|
||||
stateUpdated?(state)
|
||||
})
|
||||
stateUpdated = { [weak controller] state in
|
||||
controller?.view.endEditing(true)
|
||||
controller?.dismiss()
|
||||
|
||||
switch state {
|
||||
case .noPassword, .awaitingEmailConfirmation, .passwordSet:
|
||||
controller?.dismiss()
|
||||
|
||||
dismissImpl?()
|
||||
case .pendingPasswordReset:
|
||||
dataPromise.set(context.engine.auth.twoStepVerificationConfiguration()
|
||||
|> map { TwoStepVerificationUnlockSettingsControllerData.access(configuration: TwoStepVerificationAccessConfiguration(configuration: $0, password: nil))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/*var completionImpl: ((Bool) -> Void)?
|
||||
let controller = resetPasswordController(context: context, emailPattern: emailPattern, completion: { result in
|
||||
completionImpl?(result)
|
||||
})
|
||||
@ -527,8 +548,8 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
presentControllerImpl?(OverlayStatusController(theme: presentationData.theme, type: .genericSuccess(presentationData.strings.TwoStepAuth_DisableSuccess, false)), nil)
|
||||
}
|
||||
}
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}*/
|
||||
pushControllerImpl?(controller)
|
||||
}, error: { _ in
|
||||
updateState { state in
|
||||
var state = state
|
||||
@ -592,6 +613,8 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
|
||||
case .notSet:
|
||||
let controller = SetupTwoStepVerificationController(context: context, initialState: .createPassword, stateUpdated: { update, shouldDismiss, controller in
|
||||
switch update {
|
||||
case .pendingPasswordReset:
|
||||
break
|
||||
case .noPassword:
|
||||
dataPromise.set(.single(.access(configuration: .notSet(pendingEmail: nil))))
|
||||
case let .awaitingEmailConfirmation(password, pattern, codeLength):
|
||||
@ -623,6 +646,8 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
|
||||
case let .manage(password, hasRecovery, pendingEmail, hasSecureValues):
|
||||
let controller = SetupTwoStepVerificationController(context: context, initialState: .updatePassword(current: password, hasRecoveryEmail: hasRecovery, hasSecureValues: hasSecureValues), stateUpdated: { update, shouldDismiss, controller in
|
||||
switch update {
|
||||
case .pendingPasswordReset:
|
||||
break
|
||||
case .noPassword:
|
||||
dataPromise.set(.single(.access(configuration: .notSet(pendingEmail: nil))))
|
||||
case .awaitingEmailConfirmation:
|
||||
@ -712,10 +737,10 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
|
||||
case .access:
|
||||
break
|
||||
case let .manage(password, emailSet, _, hasSecureValues):
|
||||
//let controller = TwoFactorDataInputScreen(context: context, mode: .updateEmailAddress(password: password))
|
||||
|
||||
let controller = SetupTwoStepVerificationController(context: context, initialState: .addEmail(hadRecoveryEmail: emailSet, hasSecureValues: hasSecureValues, password: password), stateUpdated: { update, shouldDismiss, controller in
|
||||
switch update {
|
||||
case .pendingPasswordReset:
|
||||
break
|
||||
case .noPassword:
|
||||
assertionFailure()
|
||||
break
|
||||
@ -803,6 +828,8 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
|
||||
}
|
||||
let controller = SetupTwoStepVerificationController(context: context, initialState: .confirmEmail(password: password, hasSecureValues: hasSecureValues, pattern: pendingEmail.pattern, codeLength: pendingEmail.codeLength), stateUpdated: { update, shouldDismiss, controller in
|
||||
switch update {
|
||||
case .pendingPasswordReset:
|
||||
break
|
||||
case .noPassword:
|
||||
assertionFailure()
|
||||
break
|
||||
@ -941,6 +968,9 @@ func twoStepVerificationUnlockSettingsController(context: AccountContext, mode:
|
||||
controller.present(c, in: .window(.root), with: p)
|
||||
}
|
||||
}
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
controller?.push(c)
|
||||
}
|
||||
dismissImpl = { [weak controller] in
|
||||
controller?.dismiss()
|
||||
}
|
||||
|
@ -342,30 +342,6 @@ public enum PasswordRecoveryOption {
|
||||
case email(pattern: String)
|
||||
}
|
||||
|
||||
public func requestPasswordRecovery(account: UnauthorizedAccount) -> Signal<PasswordRecoveryOption, PasswordRecoveryRequestError> {
|
||||
return account.network.request(Api.functions.auth.requestPasswordRecovery())
|
||||
|> map(Optional.init)
|
||||
|> `catch` { error -> Signal<Api.auth.PasswordRecovery?, PasswordRecoveryRequestError> in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .fail(.limitExceeded)
|
||||
} else if error.errorDescription.hasPrefix("PASSWORD_RECOVERY_NA") {
|
||||
return .single(nil)
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
|> map { result -> PasswordRecoveryOption in
|
||||
if let result = result {
|
||||
switch result {
|
||||
case let .passwordRecovery(emailPattern):
|
||||
return .email(pattern: emailPattern)
|
||||
}
|
||||
} else {
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum PasswordRecoveryError {
|
||||
case invalidCode
|
||||
case limitExceeded
|
||||
@ -373,7 +349,7 @@ public enum PasswordRecoveryError {
|
||||
case generic
|
||||
}
|
||||
|
||||
public func checkPasswordRecoveryCode(network: Network, code: String) -> Signal<Never, PasswordRecoveryError> {
|
||||
func _internal_checkPasswordRecoveryCode(network: Network, code: String) -> Signal<Never, PasswordRecoveryError> {
|
||||
return network.request(Api.functions.auth.checkRecoveryPassword(code: code), automaticFloodWait: false)
|
||||
|> mapError { error -> PasswordRecoveryError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
@ -389,46 +365,17 @@ public func checkPasswordRecoveryCode(network: Network, code: String) -> Signal<
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_performPasswordRecovery(accountManager: AccountManager, account: UnauthorizedAccount, code: String, syncContacts: Bool, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<Void, PasswordRecoveryError> {
|
||||
return _internal_twoStepAuthData(account.network)
|
||||
|> mapError { _ -> PasswordRecoveryError in
|
||||
return .generic
|
||||
public final class RecoveredAccountData {
|
||||
let authorization: Api.auth.Authorization
|
||||
|
||||
init(authorization: Api.auth.Authorization) {
|
||||
self.authorization = authorization
|
||||
}
|
||||
|> mapToSignal { authData -> Signal<Void, PasswordRecoveryError> in
|
||||
let newSettings: Api.account.PasswordInputSettings?
|
||||
switch updatedPassword {
|
||||
case .none:
|
||||
newSettings = nil
|
||||
case let .password(password, hint, email):
|
||||
var flags: Int32 = 1 << 0
|
||||
if email != nil {
|
||||
flags |= (1 << 1)
|
||||
}
|
||||
|
||||
guard let (updatedPasswordHash, updatedPasswordDerivation) = passwordUpdateKDF(encryptionProvider: account.network.encryptionProvider, password: password, derivation: authData.nextPasswordDerivation) else {
|
||||
return .fail(.invalidCode)
|
||||
}
|
||||
|
||||
newSettings = Api.account.PasswordInputSettings.passwordInputSettings(flags: flags, newAlgo: updatedPasswordDerivation.apiAlgo, newPasswordHash: Buffer(data: updatedPasswordHash), hint: hint, email: email, newSecureSettings: nil)
|
||||
}
|
||||
|
||||
var flags: Int32 = 0
|
||||
if newSettings != nil {
|
||||
flags |= 1 << 0
|
||||
}
|
||||
return account.network.request(Api.functions.auth.recoverPassword(flags: flags, code: code, newSettings: newSettings), automaticFloodWait: false)
|
||||
|> mapError { error -> PasswordRecoveryError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .limitExceeded
|
||||
} else if error.errorDescription.hasPrefix("PASSWORD_RECOVERY_EXPIRED") {
|
||||
return .expired
|
||||
} else {
|
||||
return .invalidCode
|
||||
}
|
||||
}
|
||||
|> mapToSignal { result -> Signal<Void, PasswordRecoveryError> in
|
||||
public func loginWithRecoveredAccountData(accountManager: AccountManager, account: UnauthorizedAccount, recoveredAccountData: RecoveredAccountData, syncContacts: Bool) -> Signal<Never, NoError> {
|
||||
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
|
||||
switch result {
|
||||
switch recoveredAccountData.authorization {
|
||||
case let .authorization(_, _, user):
|
||||
let user = TelegramUser(user: user)
|
||||
let state = AuthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, peerId: user.id, state: nil)
|
||||
@ -443,7 +390,48 @@ func _internal_performPasswordRecovery(accountManager: AccountManager, account:
|
||||
}
|
||||
}
|
||||
|> switchToLatest
|
||||
|> mapError { _ -> PasswordRecoveryError in }
|
||||
|> ignoreValues
|
||||
}
|
||||
|
||||
func _internal_performPasswordRecovery(network: Network, code: String, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<RecoveredAccountData, PasswordRecoveryError> {
|
||||
return _internal_twoStepAuthData(network)
|
||||
|> mapError { _ -> PasswordRecoveryError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { authData -> Signal<RecoveredAccountData, PasswordRecoveryError> in
|
||||
let newSettings: Api.account.PasswordInputSettings?
|
||||
switch updatedPassword {
|
||||
case .none:
|
||||
newSettings = nil
|
||||
case let .password(password, hint, email):
|
||||
var flags: Int32 = 1 << 0
|
||||
if email != nil {
|
||||
flags |= (1 << 1)
|
||||
}
|
||||
|
||||
guard let (updatedPasswordHash, updatedPasswordDerivation) = passwordUpdateKDF(encryptionProvider: network.encryptionProvider, password: password, derivation: authData.nextPasswordDerivation) else {
|
||||
return .fail(.invalidCode)
|
||||
}
|
||||
|
||||
newSettings = Api.account.PasswordInputSettings.passwordInputSettings(flags: flags, newAlgo: updatedPasswordDerivation.apiAlgo, newPasswordHash: Buffer(data: updatedPasswordHash), hint: hint, email: email, newSecureSettings: nil)
|
||||
}
|
||||
|
||||
var flags: Int32 = 0
|
||||
if newSettings != nil {
|
||||
flags |= 1 << 0
|
||||
}
|
||||
return network.request(Api.functions.auth.recoverPassword(flags: flags, code: code, newSettings: newSettings), automaticFloodWait: false)
|
||||
|> mapError { error -> PasswordRecoveryError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .limitExceeded
|
||||
} else if error.errorDescription.hasPrefix("PASSWORD_RECOVERY_EXPIRED") {
|
||||
return .expired
|
||||
} else {
|
||||
return .invalidCode
|
||||
}
|
||||
}
|
||||
|> mapToSignal { result -> Signal<RecoveredAccountData, PasswordRecoveryError> in
|
||||
return .single(RecoveredAccountData(authorization: result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,16 @@ public extension TelegramEngineUnauthorized {
|
||||
return _internal_updateTwoStepVerificationPassword(network: self.account.network, currentPassword: currentPassword, updatedPassword: updatedPassword)
|
||||
}
|
||||
|
||||
public func performPasswordRecovery(accountManager: AccountManager, code: String, syncContacts: Bool, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<Void, PasswordRecoveryError> {
|
||||
return _internal_performPasswordRecovery(accountManager: accountManager, account: self.account, code: code, syncContacts: syncContacts, updatedPassword: updatedPassword)
|
||||
public func requestTwoStepVerificationPasswordRecoveryCode() -> Signal<String, RequestTwoStepVerificationPasswordRecoveryCodeError> {
|
||||
return _internal_requestTwoStepVerificationPasswordRecoveryCode(network: self.account.network)
|
||||
}
|
||||
|
||||
public func checkPasswordRecoveryCode(code: String) -> Signal<Never, PasswordRecoveryError> {
|
||||
return _internal_checkPasswordRecoveryCode(network: self.account.network, code: code)
|
||||
}
|
||||
|
||||
public func performPasswordRecovery(code: String, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<RecoveredAccountData, PasswordRecoveryError> {
|
||||
return _internal_performPasswordRecovery(network: self.account.network, code: code, updatedPassword: updatedPassword)
|
||||
}
|
||||
|
||||
public func resendTwoStepRecoveryEmail() -> Signal<Never, ResendTwoStepRecoveryEmailError> {
|
||||
@ -90,8 +98,8 @@ public extension TelegramEngine {
|
||||
return _internal_requestTwoStepVerificationPasswordRecoveryCode(network: self.account.network)
|
||||
}
|
||||
|
||||
public func recoverTwoStepVerificationPassword(code: String) -> Signal<Void, RecoverTwoStepVerificationPasswordError> {
|
||||
return _internal_recoverTwoStepVerificationPassword(network: self.account.network, code: code)
|
||||
public func performPasswordRecovery(code: String, updatedPassword: UpdatedTwoStepVerificationPassword) -> Signal<RecoveredAccountData, PasswordRecoveryError> {
|
||||
return _internal_performPasswordRecovery(network: self.account.network, code: code, updatedPassword: updatedPassword)
|
||||
}
|
||||
|
||||
public func cachedTwoStepPasswordToken() -> Signal<TemporaryTwoStepPasswordToken?, NoError> {
|
||||
@ -106,6 +114,10 @@ public extension TelegramEngine {
|
||||
return _internal_requestTemporaryTwoStepPasswordToken(account: self.account, password: password, period: period, requiresBiometrics: requiresBiometrics)
|
||||
}
|
||||
|
||||
public func checkPasswordRecoveryCode(code: String) -> Signal<Never, PasswordRecoveryError> {
|
||||
return _internal_checkPasswordRecoveryCode(network: self.account.network, code: code)
|
||||
}
|
||||
|
||||
public func requestTwoStepPasswordReset() -> Signal<RequestTwoStepPasswordResetResult, NoError> {
|
||||
return _internal_requestTwoStepPasswordReset(network: self.account.network)
|
||||
}
|
||||
@ -154,12 +166,21 @@ public extension SomeTelegramEngine {
|
||||
}
|
||||
}
|
||||
|
||||
public func resendTwoStepRecoveryEmail() -> Signal<Never, ResendTwoStepRecoveryEmailError> {
|
||||
public func requestTwoStepVerificationPasswordRecoveryCode() -> Signal<String, RequestTwoStepVerificationPasswordRecoveryCodeError> {
|
||||
switch self.engine {
|
||||
case let .authorized(engine):
|
||||
return engine.auth.resendTwoStepRecoveryEmail()
|
||||
return engine.auth.requestTwoStepVerificationPasswordRecoveryCode()
|
||||
case let .unauthorized(engine):
|
||||
return engine.auth.resendTwoStepRecoveryEmail()
|
||||
return engine.auth.requestTwoStepVerificationPasswordRecoveryCode()
|
||||
}
|
||||
}
|
||||
|
||||
public func checkPasswordRecoveryCode(code: String) -> Signal<Never, PasswordRecoveryError> {
|
||||
switch self.engine {
|
||||
case let .authorized(engine):
|
||||
return engine.auth.checkPasswordRecoveryCode(code: code)
|
||||
case let .unauthorized(engine):
|
||||
return engine.auth.checkPasswordRecoveryCode(code: code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -312,12 +312,19 @@ func _internal_updateTwoStepVerificationEmail(network: Network, currentPassword:
|
||||
|
||||
public enum RequestTwoStepVerificationPasswordRecoveryCodeError {
|
||||
case generic
|
||||
case limitExceeded
|
||||
}
|
||||
|
||||
func _internal_requestTwoStepVerificationPasswordRecoveryCode(network: Network) -> Signal<String, RequestTwoStepVerificationPasswordRecoveryCodeError> {
|
||||
return network.request(Api.functions.auth.requestPasswordRecovery(), automaticFloodWait: false)
|
||||
|> mapError { _ -> RequestTwoStepVerificationPasswordRecoveryCodeError in
|
||||
|> mapError { error -> RequestTwoStepVerificationPasswordRecoveryCodeError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .limitExceeded
|
||||
} else if error.errorDescription.hasPrefix("PASSWORD_RECOVERY_NA") {
|
||||
return .generic
|
||||
} else {
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
|> map { result -> String in
|
||||
switch result {
|
||||
@ -334,35 +341,6 @@ public enum RecoverTwoStepVerificationPasswordError {
|
||||
case invalidCode
|
||||
}
|
||||
|
||||
func _internal_recoverTwoStepVerificationPassword(network: Network, code: String) -> Signal<Void, RecoverTwoStepVerificationPasswordError> {
|
||||
return _internal_twoStepAuthData(network)
|
||||
|> mapError { _ -> RecoverTwoStepVerificationPasswordError in
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { authData -> Signal<Void, RecoverTwoStepVerificationPasswordError> in
|
||||
var flags: Int32 = (1 << 1)
|
||||
if authData.currentPasswordDerivation != nil {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
|
||||
return network.request(Api.functions.auth.recoverPassword(flags: 0, code: code, newSettings: nil), automaticFloodWait: false)
|
||||
|> mapError { error -> RecoverTwoStepVerificationPasswordError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT_") {
|
||||
return .limitExceeded
|
||||
} else if error.errorDescription == "PASSWORD_RECOVERY_EXPIRED" {
|
||||
return .codeExpired
|
||||
} else if error.errorDescription == "CODE_INVALID" {
|
||||
return .invalidCode
|
||||
} else {
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Void, RecoverTwoStepVerificationPasswordError> in
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func _internal_cachedTwoStepPasswordToken(postbox: Postbox) -> Signal<TemporaryTwoStepPasswordToken?, NoError> {
|
||||
return postbox.transaction { transaction -> TemporaryTwoStepPasswordToken? in
|
||||
let key = ValueBoxKey(length: 1)
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -486,26 +486,26 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
controller.forgot = { [weak self, weak controller] in
|
||||
if let strongSelf = self, let strongController = controller {
|
||||
strongController.inProgress = true
|
||||
strongSelf.actionDisposable.set((requestPasswordRecovery(account: strongSelf.account)
|
||||
|> deliverOnMainQueue).start(next: { option in
|
||||
strongSelf.actionDisposable.set((TelegramEngineUnauthorized(account: strongSelf.account).auth.requestTwoStepVerificationPasswordRecoveryCode()
|
||||
|> deliverOnMainQueue).start(next: { pattern in
|
||||
if let strongSelf = self, let strongController = controller {
|
||||
strongController.inProgress = false
|
||||
switch option {
|
||||
case let .email(pattern):
|
||||
|
||||
let _ = (strongSelf.account.postbox.transaction { transaction -> Void in
|
||||
if let state = transaction.getState() as? UnauthorizedAccountState, case let .passwordEntry(hint, number, code, _, syncContacts) = state.contents {
|
||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .passwordRecovery(hint: hint, number: number, code: code, emailPattern: pattern, syncContacts: syncContacts)))
|
||||
}
|
||||
}).start()
|
||||
case .none:
|
||||
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
|
||||
}
|
||||
}
|
||||
}, error: { error in
|
||||
if let strongController = controller {
|
||||
strongController.inProgress = false
|
||||
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
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -542,82 +542,21 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
||||
return controller
|
||||
}
|
||||
|
||||
private func passwordRecoveryController(emailPattern: String, syncContacts: Bool) -> AuthorizationSequencePasswordRecoveryController {
|
||||
var currentController: AuthorizationSequencePasswordRecoveryController?
|
||||
private func passwordRecoveryController(emailPattern: String, syncContacts: Bool) -> TwoFactorDataInputScreen {
|
||||
var currentController: TwoFactorDataInputScreen?
|
||||
for c in self.viewControllers {
|
||||
if let c = c as? AuthorizationSequencePasswordRecoveryController {
|
||||
if let c = c as? TwoFactorDataInputScreen {
|
||||
currentController = c
|
||||
break
|
||||
}
|
||||
}
|
||||
let controller: AuthorizationSequencePasswordRecoveryController
|
||||
let controller: TwoFactorDataInputScreen
|
||||
if let currentController = currentController {
|
||||
controller = currentController
|
||||
} else {
|
||||
controller = AuthorizationSequencePasswordRecoveryController(strings: self.presentationData.strings, theme: self.presentationData.theme, back: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
controller = TwoFactorDataInputScreen(sharedContext: self.sharedContext, engine: .unauthorized(TelegramEngineUnauthorized(account: self.account)), mode: .passwordRecoveryEmail(emailPattern: emailPattern, mode: .notAuthorized(syncContacts: syncContacts)), stateUpdated: { _ in
|
||||
}, presentation: .default)
|
||||
}
|
||||
let countryCode = defaultCountryCode()
|
||||
|
||||
let _ = (strongSelf.account.postbox.transaction { transaction -> Void in
|
||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: strongSelf.account.testingEnvironment, masterDatacenterId: strongSelf.account.masterDatacenterId, contents: .phoneEntry(countryCode: countryCode, number: "")))
|
||||
}).start()
|
||||
})
|
||||
controller.recoverWithCode = { [weak self, weak controller] code in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
controller?.inProgress = true
|
||||
|
||||
strongSelf.actionDisposable.set((checkPasswordRecoveryCode(network: strongSelf.account.network, code: code)
|
||||
|> deliverOnMainQueue).start(error: { error in
|
||||
guard let strongSelf = self, let controller = controller else {
|
||||
return
|
||||
}
|
||||
controller.inProgress = false
|
||||
|
||||
let text: String
|
||||
switch error {
|
||||
case .limitExceeded:
|
||||
text = strongSelf.presentationData.strings.LoginPassword_FloodError
|
||||
case .invalidCode:
|
||||
text = strongSelf.presentationData.strings.Login_InvalidCodeError
|
||||
case .expired:
|
||||
text = strongSelf.presentationData.strings.Login_CodeExpiredError
|
||||
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))
|
||||
}, completed: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
controller?.inProgress = false
|
||||
|
||||
let setupController = TwoFactorDataInputScreen(sharedContext: strongSelf.sharedContext, engine: .unauthorized(TelegramEngineUnauthorized(account: strongSelf.account)), mode: .passwordRecovery(TwoFactorDataInputMode.Recovery(code: code, syncContacts: syncContacts, account: strongSelf.account)), stateUpdated: { _ in
|
||||
guard let _ = self else {
|
||||
return
|
||||
}
|
||||
})
|
||||
strongSelf.setViewControllers(strongSelf.viewControllers + [setupController], animated: true)
|
||||
}))
|
||||
}
|
||||
controller.noAccess = { [weak self, weak controller] in
|
||||
if let strongSelf = self, let controller = controller {
|
||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: strongSelf.presentationData.strings.TwoStepAuth_RecoveryFailed, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
let account = strongSelf.account
|
||||
let _ = (strongSelf.account.postbox.transaction { transaction -> Void in
|
||||
if let state = transaction.getState() as? UnauthorizedAccountState, case let .passwordRecovery(hint, number, code, _, syncContacts) = state.contents {
|
||||
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .passwordEntry(hint: hint, number: number, code: code, suggestReset: true, syncContacts: syncContacts)))
|
||||
}
|
||||
}).start()
|
||||
}
|
||||
}
|
||||
}
|
||||
controller.updateData(emailPattern: emailPattern)
|
||||
return controller
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user