Authorization improvements

This commit is contained in:
Ilya Laktyushin 2024-04-16 17:21:40 +04:00
parent 2670be22be
commit a736b22baa
9 changed files with 371 additions and 62 deletions

View File

@ -12063,7 +12063,14 @@ Sorry for the inconvenience.";
"Login.EnterPhraseBeginningText" = "We've sent you an SMS with a secret phrase that starts with **\"%1$@\"** to your phone **%2$@**.";
"Login.EnterPhrasePlaceholder" = "Enter Phrase from SMS";
"Login.EnterPhraseHint" = "You can copy and paste the phrase here.";
"Login.BackToWord" = "Back to entering word";
"Login.BackToPhrase" = "Back to entering phrase";
"Login.WrongPhraseError" = "Incorrect, please try again.";
"Login.Paste" = "Paste";
"Login.ReturnToCode" = "Return to entering the code";
"Map.LiveLocationPrivateNewDescription" = "Choose for how long **%@** will see your accurate location, including when the app is closed.";
"Map.LiveLocationGroupNewDescription" = "Choose for how long people in this chat will see your accurate location, including when the app is closed.";
@ -12077,3 +12084,5 @@ Sorry for the inconvenience.";
"Map.LiveLocationForHours_any" = "For %@ hours";
"Map.LiveLocationIndefinite" = "Until I turn it off";
"Channel.AdminLog.Settings" = "Settings";

View File

@ -23,10 +23,11 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
var reset: (() -> Void)?
public var requestNextOption: (() -> Void)?
public var requestPreviousOption: (() -> Void)?
var resetEmail: (() -> Void)?
var retryResetEmail: (() -> Void)?
var data: (String, String?, SentAuthorizationCodeType, AuthorizationCodeNextType?, Int32?)?
var data: (String, String?, SentAuthorizationCodeType, AuthorizationCodeNextType?, Int32?, Bool, Bool)?
var termsOfService: (UnauthorizedAccountTermsOfService, Bool)?
private let hapticFeedback = HapticFeedback()
@ -40,6 +41,19 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
}
}
var isWordOrPhrase: Bool {
if let type = self.data?.2 {
switch type {
case .word, .phrase:
return true
default:
return false
}
} else {
return false
}
}
public init(presentationData: PresentationData, back: @escaping () -> Void) {
self.strings = presentationData.strings
self.theme = presentationData.theme
@ -60,7 +74,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
let proceed: String
let stop: String
if let (_, _, type, _, _) = self?.data, case .email = type {
if let (_, _, type, _, _, _, _) = self?.data, case .email = type {
text = presentationData.strings.Login_CancelEmailVerification
proceed = presentationData.strings.Login_CancelEmailVerificationContinue
stop = presentationData.strings.Login_CancelEmailVerificationStop
@ -107,6 +121,10 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
self?.requestNextOption?()
}
self.controllerNode.requestPreviousOption = { [weak self] in
self?.requestPreviousOption?()
}
self.controllerNode.updateNextEnabled = { [weak self] value in
self?.navigationItem.rightBarButtonItem?.isEnabled = value
}
@ -123,12 +141,12 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
self?.present(c, in: .window(.root), with: a)
}
if let (number, email, codeType, nextType, timeout) = self.data {
if let (number, email, codeType, nextType, timeout, hasPreviousCode, previousIsPhrase) = self.data {
var appleSignInAllowed = false
if case let .email(_, _, _, _, appleSignInAllowedValue, _) = codeType {
appleSignInAllowed = appleSignInAllowedValue
}
self.controllerNode.updateData(number: number, email: email, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed)
self.controllerNode.updateData(number: number, email: email, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed, hasPreviousCode: hasPreviousCode, previousIsPhrase: previousIsPhrase)
}
}
@ -155,6 +173,10 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
self.controllerNode.animateError(text: text)
}
func updateAppIsActive(_ isActive: Bool) {
self.controllerNode.updatePasteVisibility()
}
func updateNavigationItems() {
guard let layout = self.validLayout, layout.size.width < 360.0 else {
return
@ -168,10 +190,10 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
}
}
public func updateData(number: String, email: String?, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) {
public func updateData(number: String, email: String?, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?, hasPreviousCode: Bool, previousIsPhrase: Bool) {
self.termsOfService = termsOfService
if self.data?.0 != number || self.data?.1 != email || self.data?.2 != codeType || self.data?.3 != nextType || self.data?.4 != timeout {
self.data = (number, email, codeType, nextType, timeout)
if self.data?.0 != number || self.data?.1 != email || self.data?.2 != codeType || self.data?.3 != nextType || self.data?.4 != timeout || self.data?.5 != hasPreviousCode || self.data?.6 != previousIsPhrase {
self.data = (number, email, codeType, nextType, timeout, hasPreviousCode, previousIsPhrase)
var appleSignInAllowed = false
if case let .email(_, _, _, _, appleSignInAllowedValue, _) = codeType {
@ -179,7 +201,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
}
if self.isNodeLoaded {
self.controllerNode.updateData(number: number, email: email, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed)
self.controllerNode.updateData(number: number, email: email, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed, hasPreviousCode: hasPreviousCode, previousIsPhrase: previousIsPhrase)
self.requestLayout(transition: .immediate)
}
}
@ -199,7 +221,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController {
}
@objc private func nextPressed() {
guard let (_, _, type, _, _) = self.data else {
guard let (_, _, type, _, _, _, _) = self.data else {
return
}

View File

@ -31,6 +31,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
private let currentOptionInfoActivateAreaNode: AccessibilityAreaNode
private let nextOptionTitleNode: ImmediateTextNode
private let nextOptionButtonNode: HighlightableButtonNode
private let nextOptionArrowNode: ASImageNode
private let resetTextNode: ImmediateTextNode
private let resetNode: HighlightableButtonNode
@ -41,10 +42,15 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
private let textField: TextFieldNode
private let textSeparatorNode: ASDisplayNode
private let pasteButton: HighlightableButtonNode
private let codeInputView: CodeInputView
private let errorTextNode: ImmediateTextNode
private let hintButtonNode: HighlightTrackingButtonNode
private let hintTextNode: ImmediateTextNode
private let hintArrowNode: ASImageNode
private var codeType: SentAuthorizationCodeType?
private let countdownDisposable = MetaDisposable()
@ -53,6 +59,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
private var layoutArguments: (ContainerViewLayout, CGFloat)?
private var appleSignInAllowed = false
private var hasPreviousCode = false
private var previousIsPhrase = false
var phoneNumber: String = "" {
didSet {
@ -82,6 +90,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
var requestNextOption: (() -> Void)?
var requestAnotherOption: (() -> Void)?
var requestPreviousOption: (() -> Void)?
var updateNextEnabled: ((Bool) -> Void)?
var reset: (() -> Void)?
var retryReset: (() -> Void)?
@ -128,6 +137,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
self.titleIconNode.displaysAsynchronously = false
self.currentOptionNode = ImmediateTextNodeWithEntities()
self.currentOptionNode.balancedTextLayout = true
self.currentOptionNode.isUserInteractionEnabled = false
self.currentOptionNode.displaysAsynchronously = false
self.currentOptionNode.lineSpacing = 0.1
@ -159,6 +169,10 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
}
self.nextOptionButtonNode.addSubnode(self.nextOptionTitleNode)
self.nextOptionArrowNode = ASImageNode()
self.nextOptionArrowNode.displaysAsynchronously = false
self.nextOptionArrowNode.isUserInteractionEnabled = false
self.codeInputView = CodeInputView()
self.codeInputView.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance
self.codeInputView.textField.returnKeyType = .done
@ -194,10 +208,25 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
self.textField.textField.disableAutomaticKeyboardHandling = [.forward, .backward]
self.textField.textField.tintColor = self.theme.list.itemAccentColor
self.pasteButton = HighlightableButtonNode()
self.errorTextNode = ImmediateTextNode()
self.errorTextNode.alpha = 0.0
self.errorTextNode.displaysAsynchronously = false
self.errorTextNode.textAlignment = .center
self.errorTextNode.isUserInteractionEnabled = false
self.hintButtonNode = HighlightableButtonNode()
self.hintButtonNode.alpha = 0.0
self.hintButtonNode.isUserInteractionEnabled = false
self.hintTextNode = ImmediateTextNode()
self.hintTextNode.displaysAsynchronously = false
self.hintTextNode.textAlignment = .center
self.hintTextNode.isUserInteractionEnabled = false
self.hintArrowNode = ASImageNode()
self.hintArrowNode.displaysAsynchronously = false
self.resetNode = HighlightableButtonNode()
self.resetNode.displaysAsynchronously = false
@ -238,6 +267,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
self.addSubnode(self.codeInputView)
self.addSubnode(self.textSeparatorNode)
self.addSubnode(self.textField)
self.addSubnode(self.pasteButton)
self.addSubnode(self.titleNode)
self.addSubnode(self.titleActivateAreaNode)
self.addSubnode(self.titleIconNode)
@ -245,11 +275,15 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
self.addSubnode(self.currentOptionActivateAreaNode)
self.addSubnode(self.currentOptionInfoNode)
self.addSubnode(self.nextOptionButtonNode)
self.nextOptionButtonNode.addSubnode(self.nextOptionArrowNode)
self.addSubnode(self.animationNode)
self.addSubnode(self.resetNode)
self.addSubnode(self.resetTextNode)
self.addSubnode(self.dividerNode)
self.addSubnode(self.errorTextNode)
self.addSubnode(self.hintButtonNode)
self.hintButtonNode.addSubnode(self.hintTextNode)
self.hintButtonNode.addSubnode(self.hintArrowNode)
self.addSubnode(self.proceedNode)
self.codeInputView.updated = { [weak self] in
@ -295,6 +329,18 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
}
self.signInWithAppleButton?.addTarget(self, action: #selector(self.signInWithApplePressed), for: .touchUpInside)
self.resetNode.addTarget(self, action: #selector(self.resetPressed), forControlEvents: .touchUpInside)
self.pasteButton.setTitle(strings.Login_Paste, with: Font.medium(13.0), with: theme.list.itemAccentColor, for: .normal)
self.pasteButton.setBackgroundImage(generateStretchableFilledCircleImage(radius: 12.0, color: theme.list.itemAccentColor.withAlphaComponent(0.1)), for: .normal)
self.pasteButton.addTarget(self, action: #selector(self.pastePressed), forControlEvents: .touchUpInside)
self.hintButtonNode.addTarget(self, action: #selector(self.previousOptionNodePressed), forControlEvents: .touchUpInside)
self.hintArrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: theme.list.itemAccentColor)
self.hintArrowNode.transform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
self.nextOptionArrowNode.image = generateTintedImage(image: UIImage(bundleImageName: "Settings/TextArrowRight"), color: theme.list.itemAccentColor)
self.updatePasteVisibility()
}
deinit {
@ -309,6 +355,18 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
}
}
@objc private func pastePressed() {
if let text = UIPasteboard.general.string, !text.isEmpty {
if checkValidity(text: text) {
self.textField.textField.text = text
}
}
}
func updatePasteVisibility() {
self.pasteButton.isHidden = !UIPasteboard.general.hasStrings
}
func updateCode(_ code: String) {
self.codeInputView.text = code
self.codeChanged(text: code)
@ -343,10 +401,12 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
self.codeInputView.text = ""
}
func updateData(number: String, email: String?, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, appleSignInAllowed: Bool) {
func updateData(number: String, email: String?, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, appleSignInAllowed: Bool, hasPreviousCode: Bool, previousIsPhrase: Bool) {
self.codeType = codeType
self.phoneNumber = number
self.email = email
self.hasPreviousCode = hasPreviousCode
self.previousIsPhrase = previousIsPhrase
var appleSignInAllowed = appleSignInAllowed
if #available(iOS 13.0, *) {
@ -369,6 +429,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
self.currentOptionInfoActivateAreaNode.removeFromSupernode()
}
}
if let timeout = timeout {
#if DEBUG
let timeout = min(timeout, 5)
@ -378,7 +439,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
if let strongSelf = self {
if let currentTimeoutTime = strongSelf.currentTimeoutTime, currentTimeoutTime > 0 {
strongSelf.currentTimeoutTime = currentTimeoutTime - 1
let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: codeType, nextType: nextType, timeout: strongSelf.currentTimeoutTime, strings: strongSelf.strings, primaryColor: strongSelf.theme.list.itemPrimaryTextColor, accentColor: strongSelf.theme.list.itemAccentColor)
let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: codeType, nextType: nextType, returnToCode: hasPreviousCode, timeout: strongSelf.currentTimeoutTime, strings: strongSelf.strings, primaryColor: strongSelf.theme.list.itemPrimaryTextColor, accentColor: strongSelf.theme.list.itemAccentColor)
strongSelf.nextOptionTitleNode.attributedText = nextOptionText
strongSelf.nextOptionButtonNode.isUserInteractionEnabled = nextOptionActive
strongSelf.nextOptionButtonNode.accessibilityLabel = nextOptionText.string
@ -418,7 +479,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
self.currentTimeoutTime = nil
self.countdownDisposable.set(nil)
}
let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: codeType, nextType: nextType, timeout: self.currentTimeoutTime, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor)
let (nextOptionText, nextOptionActive) = authorizationNextOptionText(currentType: codeType, nextType: nextType, returnToCode: hasPreviousCode, timeout: self.currentTimeoutTime, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor)
self.nextOptionTitleNode.attributedText = nextOptionText
self.nextOptionButtonNode.isUserInteractionEnabled = nextOptionActive
self.nextOptionButtonNode.accessibilityLabel = nextOptionText.string
@ -427,6 +489,14 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
} else {
self.nextOptionButtonNode.accessibilityTraits = [.button, .notEnabled]
}
switch codeType {
case .word, .phrase:
self.nextOptionArrowNode.isHidden = !hasPreviousCode
default:
self.nextOptionArrowNode.isHidden = true
}
if let layoutArguments = self.layoutArguments {
self.containerLayoutUpdated(layoutArguments.0, navigationBarHeight: layoutArguments.1, transition: .immediate)
}
@ -699,7 +769,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
switch codeType {
case .word, .phrase:
additionalBottomInset = 120.0
additionalBottomInset = 100.0
self.nextOptionButtonNode.isHidden = false
items.append(AuthorizationLayoutItem(node: self.nextOptionButtonNode, size: nextOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 50.0, maxValue: 120.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
@ -719,13 +789,6 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
self.proceedNode.isHidden = true
items.append(AuthorizationLayoutItem(node: self.nextOptionButtonNode, size: nextOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 50.0, maxValue: 120.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
}
if case .email = codeType {
self.nextOptionButtonNode.isHidden = true
} else {
self.nextOptionButtonNode.isHidden = false
items.append(AuthorizationLayoutItem(node: self.nextOptionButtonNode, size: nextOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 50.0, maxValue: 120.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
}
}
}
} else {
@ -740,8 +803,57 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - additionalBottomInset)), items: items, transition: transition, failIfDoesNotFit: false)
if let codeType = self.codeType {
let yOffset: CGFloat = layout.size.width > 320.0 ? 18.0 : 5.0
if case .phrase = codeType {
self.hintButtonNode.alpha = 1.0
self.hintButtonNode.isUserInteractionEnabled = false
self.hintTextNode.attributedText = NSAttributedString(string: self.strings.Login_EnterPhraseHint, font: Font.regular(13.0), textColor: self.theme.list.itemSecondaryTextColor, paragraphAlignment: .center)
let hintTextSize = self.hintTextNode.updateLayout(CGSize(width: layout.size.width - 48.0, height: .greatestFiniteMagnitude))
transition.updateFrame(node: self.hintButtonNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - hintTextSize.width) / 2.0), y: self.textField.frame.maxY + yOffset), size: hintTextSize))
self.hintTextNode.frame = CGRect(origin: .zero, size: hintTextSize)
let pasteSize = self.pasteButton.measure(layout.size)
let pasteButtonSize = CGSize(width: pasteSize.width + 16.0, height: 24.0)
transition.updateFrame(node: self.pasteButton, frame: CGRect(origin: CGPoint(x: layout.size.width - 40.0 - pasteButtonSize.width, y: self.textField.frame.midY - pasteButtonSize.height / 2.0), size: pasteButtonSize))
self.hintArrowNode.isHidden = true
} else if case .word = codeType {
self.hintButtonNode.alpha = 0.0
self.hintButtonNode.isUserInteractionEnabled = false
self.hintArrowNode.isHidden = true
} else if self.hasPreviousCode {
self.hintButtonNode.alpha = 1.0
self.hintButtonNode.isUserInteractionEnabled = true
self.hintTextNode.attributedText = NSAttributedString(string: self.previousIsPhrase ? self.strings.Login_BackToPhrase : self.strings.Login_BackToWord, font: Font.regular(13.0), textColor: self.theme.list.itemAccentColor, paragraphAlignment: .center)
let hintTextSize = self.hintTextNode.updateLayout(CGSize(width: layout.size.width - 48.0, height: .greatestFiniteMagnitude))
transition.updateFrame(node: self.hintButtonNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - hintTextSize.width) / 2.0), y: self.codeInputView.frame.maxY + yOffset + 6.0), size: hintTextSize))
self.hintTextNode.frame = CGRect(origin: .zero, size: hintTextSize)
if let icon = self.hintArrowNode.image {
self.hintArrowNode.frame = CGRect(origin: CGPoint(x: self.hintTextNode.frame.minX - icon.size.width - 5.0, y: self.hintTextNode.frame.midY - icon.size.height / 2.0), size: icon.size)
}
self.hintArrowNode.isHidden = false
} else {
self.hintButtonNode.alpha = 0.0
self.hintButtonNode.isUserInteractionEnabled = false
self.hintArrowNode.isHidden = true
}
} else {
self.hintButtonNode.alpha = 0.0
self.hintButtonNode.isUserInteractionEnabled = false
self.hintArrowNode.isHidden = true
}
self.nextOptionTitleNode.frame = self.nextOptionButtonNode.bounds
if let icon = self.nextOptionArrowNode.image {
self.nextOptionArrowNode.frame = CGRect(origin: CGPoint(x: self.nextOptionTitleNode.frame.maxX + 7.0, y: self.nextOptionTitleNode.frame.midY - icon.size.height / 2.0), size: icon.size)
}
self.titleActivateAreaNode.frame = self.titleNode.frame
self.currentOptionActivateAreaNode.frame = self.currentOptionNode.frame
self.currentOptionInfoActivateAreaNode.frame = self.currentOptionInfoNode.frame
@ -792,12 +904,18 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
}
self.errorTextNode.alpha = 1.0
self.errorTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
self.errorTextNode.layer.addShakeAnimation(amplitude: -8.0, duration: 0.5, count: 6, decay: true)
let previousHintAlpha = self.hintButtonNode.alpha
self.hintButtonNode.alpha = 0.0
self.hintButtonNode.layer.animateAlpha(from: previousHintAlpha, to: 0.0, duration: 0.1)
Queue.mainQueue().after(1.6) {
self.errorTextNode.alpha = 0.0
self.errorTextNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
self.hintButtonNode.alpha = previousHintAlpha
self.hintButtonNode.layer.animateAlpha(from: 0.0, to: previousHintAlpha, duration: 0.1)
let transition: ContainedViewLayoutTransition = .animated(duration: 0.15, curve: .easeInOut)
transition.updateBackgroundColor(node: self.textSeparatorNode, color: self.theme.list.itemPlainSeparatorColor)
}
@ -852,23 +970,25 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
var updated = textField.text ?? ""
updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string)
return checkValidity(text: updated)
}
func checkValidity(text: String) -> Bool {
if let codeType = self.codeType {
switch codeType {
case let .word(startsWith):
if let startsWith, startsWith.count == 1, !updated.isEmpty && !updated.hasPrefix(startsWith) {
if let startsWith, startsWith.count == 1, !text.isEmpty && !text.hasPrefix(startsWith) {
if self.errorTextNode.alpha.isZero {
//TODO:localize
self.animateError(text: "Incorrect, please try again")
self.animateError(text: self.strings.Login_WrongPhraseError)
}
return false
}
case let .phrase(startsWith):
if let startsWith, !updated.isEmpty {
let firstWord = updated.components(separatedBy: " ").first ?? ""
if let startsWith, !text.isEmpty {
let firstWord = text.components(separatedBy: " ").first ?? ""
if !firstWord.isEmpty && !startsWith.hasPrefix(firstWord) {
if self.errorTextNode.alpha.isZero {
//TODO:localize
self.animateError(text: "Incorrect, please try again")
self.animateError(text: self.strings.Login_WrongPhraseError)
}
return false
}
@ -877,7 +997,6 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
break
}
}
return true
}
@ -885,6 +1004,10 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
self.requestAnotherOption?()
}
@objc func previousOptionNodePressed() {
self.requestPreviousOption?()
}
@objc func proceedPressed() {
switch self.codeType {
case let .fragment(url, _):

View File

@ -45,6 +45,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
private var stateDisposable: Disposable?
private let actionDisposable = MetaDisposable()
private var applicationStateDisposable: Disposable?
private var didPlayPresentationAnimation = false
@ -92,6 +93,18 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
|> deliverOnMainQueue).startStrict(next: { [weak self] state in
self?.updateState(state: state)
}).strict()
self.applicationStateDisposable = (self.sharedContext.applicationBindings.applicationIsActive
|> deliverOnMainQueue).start(next: { [weak self] isActive in
guard let self else {
return
}
for viewController in self.viewControllers {
if let codeController = viewController as? AuthorizationSequenceCodeEntryController {
codeController.updateAppIsActive(isActive)
}
}
})
}
required public init(coder aDecoder: NSCoder) {
@ -101,6 +114,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
deinit {
self.stateDisposable?.dispose()
self.actionDisposable.dispose()
self.applicationStateDisposable?.dispose()
}
override public func loadView() {
@ -296,7 +310,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
return controller
}
private func codeEntryController(number: String, phoneCodeHash: String, email: String?, type: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) -> AuthorizationSequenceCodeEntryController {
private func codeEntryController(number: String, phoneCodeHash: String, email: String?, type: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, hasPreviousCode: Bool, previousIsPhrase: Bool, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) -> AuthorizationSequenceCodeEntryController {
var currentController: AuthorizationSequenceCodeEntryController?
for c in self.viewControllers {
if let c = c as? AuthorizationSequenceCodeEntryController {
@ -568,6 +582,16 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
}
controller.requestNextOption = { [weak self, weak controller] in
if let strongSelf = self {
switch type {
case .word, .phrase:
if hasPreviousCode {
strongSelf.actionDisposable.set(togglePreviousCodeEntry(account: strongSelf.account).start())
return
}
default:
break
}
if nextType == nil {
if let controller {
let carrier = CTCarrier()
@ -616,11 +640,17 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
}
}
}
controller.reset = { [weak self] in
if let strongSelf = self {
let account = strongSelf.account
let _ = strongSelf.engine.auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).startStandalone()
controller.requestPreviousOption = { [weak self] in
guard let self else {
return
}
self.actionDisposable.set(togglePreviousCodeEntry(account: self.account).start())
}
controller.reset = { [weak self] in
guard let self else {
return
}
let _ = self.engine.auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: self.account.testingEnvironment, masterDatacenterId: self.account.masterDatacenterId, contents: .empty)).startStandalone()
}
controller.signInWithApple = { [weak self] in
guard let strongSelf = self else {
@ -645,7 +675,7 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
strongSelf.sharedContext.applicationBindings.openUrl(url)
}
}
controller.updateData(number: formatPhoneNumber(number), email: email, codeType: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)
controller.updateData(number: formatPhoneNumber(number), email: email, codeType: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService, hasPreviousCode: hasPreviousCode, previousIsPhrase: previousIsPhrase)
return controller
}
@ -1189,12 +1219,14 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
controllers.append(self.phoneEntryController(countryCode: countryCode, number: number, splashController: previousSplashController))
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty && (previousSplashController == nil || self.viewControllers.count > 2))
case let .confirmationCodeEntry(number, type, phoneCodeHash, timeout, nextType, _):
case let .confirmationCodeEntry(number, type, phoneCodeHash, timeout, nextType, _, previousCodeEntry, usePrevious):
var controllers: [ViewController] = []
if !self.otherAccountPhoneNumbers.1.isEmpty {
controllers.append(self.splashController())
}
controllers.append(self.phoneEntryController(countryCode: AuthorizationSequenceController.defaultCountryCode(), number: "", splashController: nil))
var isGoingBack = false
if case let .emailSetupRequired(appleSignInAllowed) = type {
self.appleSignInAllowed = appleSignInAllowed
controllers.append(self.emailSetupController(number: number, appleSignInAllowed: appleSignInAllowed))
@ -1202,9 +1234,35 @@ public final class AuthorizationSequenceController: NavigationController, ASAuth
if let _ = self.currentEmail {
controllers.append(self.emailSetupController(number: number, appleSignInAllowed: self.appleSignInAllowed))
}
controllers.append(self.codeEntryController(number: number, phoneCodeHash: phoneCodeHash, email: self.currentEmail, type: type, nextType: nextType, timeout: timeout, termsOfService: nil))
if let previousCodeEntry, case let .confirmationCodeEntry(number, type, phoneCodeHash, timeout, nextType, _, _, _) = previousCodeEntry, usePrevious {
var previousIsPhrase = false
if case .phrase = type {
previousIsPhrase = true
}
controllers.append(self.codeEntryController(number: number, phoneCodeHash: phoneCodeHash, email: self.currentEmail, type: type, nextType: nextType, timeout: timeout, hasPreviousCode: true, previousIsPhrase: previousIsPhrase, termsOfService: nil))
isGoingBack = true
} else {
var previousIsPhrase = false
if let previousCodeEntry, case let .confirmationCodeEntry(_, type, _, _, _, _, _, _) = previousCodeEntry {
if case .phrase = type {
previousIsPhrase = true
}
}
controllers.append(self.codeEntryController(number: number, phoneCodeHash: phoneCodeHash, email: self.currentEmail, type: type, nextType: nextType, timeout: timeout, hasPreviousCode: previousCodeEntry != nil, previousIsPhrase: previousIsPhrase, termsOfService: nil))
}
}
if isGoingBack, let currentLastController = self.viewControllers.last as? AuthorizationSequenceCodeEntryController, !currentLastController.isWordOrPhrase {
var tempControllers = controllers
tempControllers.append(currentLastController)
self.setViewControllers(tempControllers, animated: false)
Queue.mainQueue().justDispatch {
self.setViewControllers(controllers, animated: true)
}
} else {
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
}
case let .passwordEntry(hint, _, _, suggestReset, syncContacts):
var controllers: [ViewController] = []
if !self.otherAccountPhoneNumbers.1.isEmpty {

View File

@ -56,9 +56,18 @@ public func authorizationCurrentOptionText(_ type: SentAuthorizationCodeType, ph
}
}
public func authorizationNextOptionText(currentType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, strings: PresentationStrings, primaryColor: UIColor, accentColor: UIColor) -> (NSAttributedString, Bool) {
public func authorizationNextOptionText(currentType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, returnToCode: Bool = false, timeout: Int32?, strings: PresentationStrings, primaryColor: UIColor, accentColor: UIColor) -> (NSAttributedString, Bool) {
let font = Font.regular(16.0)
switch currentType {
case .word, .phrase:
if returnToCode {
return (NSAttributedString(string: strings.Login_ReturnToCode, font: font, textColor: accentColor, paragraphAlignment: .center), true)
}
default:
break
}
if let nextType = nextType, let timeout = timeout, timeout > 0 {
let minutes = timeout / 60
let seconds = timeout % 60

View File

@ -108,7 +108,7 @@ public func ChangePhoneNumberController(context: AccountContext) -> ViewControll
codeController.openFragment = { url in
context.sharedContext.applicationBindings.openUrl(url)
}
codeController.updateData(number: formatPhoneNumber(context: context, number: phoneNumber), email: nil, codeType: next.type, nextType: nil, timeout: next.timeout, termsOfService: nil)
codeController.updateData(number: formatPhoneNumber(context: context, number: phoneNumber), email: nil, codeType: next.type, nextType: nil, timeout: next.timeout, termsOfService: nil, hasPreviousCode: false, previousIsPhrase: false)
dismissImpl = { [weak codeController] in
codeController?.dismiss()
}

View File

@ -1446,7 +1446,7 @@ public func privacyAndSecurityController(
emailChangeCompletion(codeController)
}))
}
codeController.updateData(number: "", email: email, codeType: .email(emailPattern: "", length: data.length, resetAvailablePeriod: nil, resetPendingDate: nil, appleSignInAllowed: false, setup: true), nextType: nil, timeout: nil, termsOfService: nil)
codeController.updateData(number: "", email: email, codeType: .email(emailPattern: "", length: data.length, resetAvailablePeriod: nil, resetPendingDate: nil, appleSignInAllowed: false, setup: true), nextType: nil, timeout: nil, termsOfService: nil, hasPreviousCode: false, previousIsPhrase: false)
pushControllerImpl?(codeController, true)
dismissCodeControllerImpl = { [weak codeController] in
codeController?.dismiss()

View File

@ -306,7 +306,20 @@ public func sendAuthorizationCode(accountManager: AccountManager<TelegramAccount
|> mapToSignal { success -> Signal<SendAuthorizationCodeResult, AuthorizationCodeRequestError> in
if success {
return account.postbox.transaction { transaction -> SendAuthorizationCodeResult in
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts)))
var previousCodeEntry: UnauthorizedAccountStateContents?
if let state = transaction.getState() as? UnauthorizedAccountState, case let .confirmationCodeEntry(_, type, _, _, _, _, previousCodeEntryValue, _) = state.contents {
if let previousCodeEntryValue {
previousCodeEntry = previousCodeEntryValue
} else {
switch type {
case .word, .phrase:
previousCodeEntry = state.contents
default:
break
}
}
}
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
return .sentCode(account)
}
@ -318,7 +331,20 @@ public func sendAuthorizationCode(accountManager: AccountManager<TelegramAccount
}
}
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts)))
var previousCodeEntry: UnauthorizedAccountStateContents?
if let state = transaction.getState() as? UnauthorizedAccountState, case let .confirmationCodeEntry(_, type, _, _, _, _, previousCodeEntryValue, _) = state.contents {
if let previousCodeEntryValue {
previousCodeEntry = previousCodeEntryValue
} else {
switch type {
case .word, .phrase:
previousCodeEntry = state.contents
default:
break
}
}
}
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
case let .sentCodeSuccess(authorization):
switch authorization {
case let .authorization(_, otherwiseReloginDays, _, futureAuthToken, user):
@ -415,7 +441,20 @@ private func internalResendAuthorizationCode(accountManager: AccountManager<Tele
|> mapToSignal { success -> Signal<SendAuthorizationCodeResult, AuthorizationCodeRequestError> in
if success {
return account.postbox.transaction { transaction -> SendAuthorizationCodeResult in
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts)))
var previousCodeEntry: UnauthorizedAccountStateContents?
if let state = transaction.getState() as? UnauthorizedAccountState, case let .confirmationCodeEntry(_, type, _, _, _, _, previousCodeEntryValue, _) = state.contents {
if let previousCodeEntryValue {
previousCodeEntry = previousCodeEntryValue
} else {
switch type {
case .word, .phrase:
previousCodeEntry = state.contents
default:
break
}
}
}
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
return .sentCode(account)
}
@ -427,7 +466,20 @@ private func internalResendAuthorizationCode(accountManager: AccountManager<Tele
}
}
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts)))
var previousCodeEntry: UnauthorizedAccountStateContents?
if let state = transaction.getState() as? UnauthorizedAccountState, case let .confirmationCodeEntry(_, type, _, _, _, _, previousCodeEntryValue, _) = state.contents {
if let previousCodeEntryValue {
previousCodeEntry = previousCodeEntryValue
} else {
switch type {
case .word, .phrase:
previousCodeEntry = state.contents
default:
break
}
}
}
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
return .single(.sentCode(account))
case .sentCodeSuccess:
@ -443,8 +495,19 @@ public func resendAuthorizationCode(accountManager: AccountManager<TelegramAccou
return account.postbox.transaction { transaction -> Signal<Void, AuthorizationCodeRequestError> in
if let state = transaction.getState() as? UnauthorizedAccountState {
switch state.contents {
case let .confirmationCodeEntry(number, _, hash, _, nextType, syncContacts):
case let .confirmationCodeEntry(number, type, hash, _, nextType, syncContacts, previousCodeEntryValue, _):
if nextType != nil {
var previousCodeEntry: UnauthorizedAccountStateContents?
if let previousCodeEntryValue {
previousCodeEntry = previousCodeEntryValue
} else {
switch type {
case .word, .phrase:
previousCodeEntry = state.contents
default:
break
}
}
return account.network.request(Api.functions.auth.resendCode(phoneNumber: number, phoneCodeHash: hash), automaticFloodWait: false)
|> mapError { error -> AuthorizationCodeRequestError in
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
@ -503,7 +566,7 @@ public func resendAuthorizationCode(accountManager: AccountManager<TelegramAccou
|> mapToSignal { success -> Signal<SendAuthorizationCodeResult, AuthorizationCodeRequestError> in
if success {
return account.postbox.transaction { transaction -> SendAuthorizationCodeResult in
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts)))
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
return .sentCode(account)
}
@ -516,7 +579,7 @@ public func resendAuthorizationCode(accountManager: AccountManager<TelegramAccou
|> map { _ -> Void in return Void() }
}
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts)))
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: false)))
case .sentCodeSuccess:
break
}
@ -675,7 +738,7 @@ public func sendLoginEmailCode(account: UnauthorizedAccount, email: String) -> S
return account.postbox.transaction { transaction -> Signal<Never, AuthorizationSendEmailCodeError> in
if let state = transaction.getState() as? UnauthorizedAccountState {
switch state.contents {
case let .confirmationCodeEntry(phoneNumber, _, phoneCodeHash, _, _, syncContacts):
case let .confirmationCodeEntry(phoneNumber, _, phoneCodeHash, _, _, syncContacts, _, _):
return account.network.request(Api.functions.account.sendVerifyEmailCode(purpose: .emailVerifyPurposeLoginSetup(phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash), email: email), automaticFloodWait: false)
|> `catch` { error -> Signal<Api.account.SentEmailCode, AuthorizationSendEmailCodeError> in
let errorDescription = error.errorDescription ?? ""
@ -695,7 +758,7 @@ public func sendLoginEmailCode(account: UnauthorizedAccount, email: String) -> S
return account.postbox.transaction { transaction -> Signal<Void, NoError> in
switch result {
case let .sentEmailCode(emailPattern, length):
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: .email(emailPattern: emailPattern, length: length, resetAvailablePeriod: nil, resetPendingDate: nil, appleSignInAllowed: false, setup: true), hash: phoneCodeHash, timeout: nil, nextType: nil, syncContacts: syncContacts)))
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: .email(emailPattern: emailPattern, length: length, resetAvailablePeriod: nil, resetPendingDate: nil, appleSignInAllowed: false, setup: true), hash: phoneCodeHash, timeout: nil, nextType: nil, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
}
return .complete()
}
@ -754,7 +817,7 @@ public func verifyLoginEmailSetup(account: UnauthorizedAccount, code: Authorizat
return account.postbox.transaction { transaction -> Signal<Never, AuthorizationEmailVerificationError> in
if let state = transaction.getState() as? UnauthorizedAccountState {
switch state.contents {
case let .confirmationCodeEntry(phoneNumber, _, phoneCodeHash, _, _, syncContacts):
case let .confirmationCodeEntry(phoneNumber, _, phoneCodeHash, _, _, syncContacts, _, _):
let verification: Api.EmailVerification
switch code {
case let .emailCode(code):
@ -793,7 +856,7 @@ public func verifyLoginEmailSetup(account: UnauthorizedAccount, code: Authorizat
parsedNextType = AuthorizationCodeNextType(apiType: nextType)
}
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType, syncContacts: syncContacts)))
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: timeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
case .sentCodeSuccess:
break
}
@ -831,7 +894,7 @@ public func resetLoginEmail(account: UnauthorizedAccount, phoneNumber: String, p
return account.postbox.transaction { transaction -> Signal<Never, AuthorizationEmailResetError> in
if let state = transaction.getState() as? UnauthorizedAccountState {
switch state.contents {
case let .confirmationCodeEntry(phoneNumber, _, phoneCodeHash, _, _, syncContacts):
case let .confirmationCodeEntry(phoneNumber, _, phoneCodeHash, _, _, syncContacts, _, _):
return account.network.request(Api.functions.auth.resetLoginEmail(phoneNumber: phoneNumber, phoneCodeHash: phoneCodeHash), automaticFloodWait: false)
|> `catch` { error -> Signal<Api.auth.SentCode, AuthorizationEmailResetError> in
let errorDescription = error.errorDescription ?? ""
@ -854,7 +917,7 @@ public func resetLoginEmail(account: UnauthorizedAccount, phoneNumber: String, p
parsedNextType = AuthorizationCodeNextType(apiType: nextType)
}
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts)))
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .confirmationCodeEntry(number: phoneNumber, type: SentAuthorizationCodeType(apiType: type), hash: phoneCodeHash, timeout: codeTimeout, nextType: parsedNextType, syncContacts: syncContacts, previousCodeEntry: nil, usePrevious: false)))
return .complete()
case .sentCodeSuccess:
@ -883,7 +946,7 @@ public func authorizeWithCode(accountManager: AccountManager<TelegramAccountMana
return account.postbox.transaction { transaction -> Signal<AuthorizeWithCodeResult, AuthorizationCodeVerificationError> in
if let state = transaction.getState() as? UnauthorizedAccountState {
switch state.contents {
case let .confirmationCodeEntry(number, _, hash, _, _, syncContacts):
case let .confirmationCodeEntry(number, _, hash, _, _, syncContacts, _, _):
var flags: Int32 = 0
var phoneCode: String?
var emailVerification: Api.EmailVerification?
@ -1326,6 +1389,17 @@ public func resetAuthorizationState(account: UnauthorizedAccount, to value: Auth
}
}
public func togglePreviousCodeEntry(account: UnauthorizedAccount) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> Void in
if let state = transaction.getState() as? UnauthorizedAccountState {
if case let .confirmationCodeEntry(number, type, hash, timeout, nextType, syncContacts, previousCodeEntry, usePrevious) = state.contents, let previousCodeEntry {
transaction.setState(UnauthorizedAccountState(isTestingEnvironment: state.isTestingEnvironment, masterDatacenterId: state.masterDatacenterId, contents: .confirmationCodeEntry(number: number, type: type, hash: hash, timeout: timeout, nextType: nextType, syncContacts: syncContacts, previousCodeEntry: previousCodeEntry, usePrevious: !usePrevious)))
}
}
}
|> ignoreValues
}
public enum ReportMissingCodeError {
case generic
}

View File

@ -173,10 +173,10 @@ public struct UnauthorizedAccountTermsOfService: PostboxCoding, Equatable {
}
}
public enum UnauthorizedAccountStateContents: PostboxCoding, Equatable {
public indirect enum UnauthorizedAccountStateContents: PostboxCoding, Equatable {
case empty
case phoneEntry(countryCode: Int32, number: String)
case confirmationCodeEntry(number: String, type: SentAuthorizationCodeType, hash: String, timeout: Int32?, nextType: AuthorizationCodeNextType?, syncContacts: Bool)
case confirmationCodeEntry(number: String, type: SentAuthorizationCodeType, hash: String, timeout: Int32?, nextType: AuthorizationCodeNextType?, syncContacts: Bool, previousCodeEntry: UnauthorizedAccountStateContents?, usePrevious: Bool)
case passwordEntry(hint: String, number: String?, code: AuthorizationCode?, suggestReset: Bool, syncContacts: Bool)
case passwordRecovery(hint: String, number: String?, code: AuthorizationCode?, emailPattern: String, syncContacts: Bool)
case awaitingAccountReset(protectedUntil: Int32, number: String?, syncContacts: Bool)
@ -193,7 +193,7 @@ public enum UnauthorizedAccountStateContents: PostboxCoding, Equatable {
if let value = decoder.decodeOptionalInt32ForKey("nt") {
nextType = AuthorizationCodeNextType(rawValue: value)
}
self = .confirmationCodeEntry(number: decoder.decodeStringForKey("num", orElse: ""), type: decoder.decodeObjectForKey("t", decoder: { SentAuthorizationCodeType(decoder: $0) }) as! SentAuthorizationCodeType, hash: decoder.decodeStringForKey("h", orElse: ""), timeout: decoder.decodeOptionalInt32ForKey("tm"), nextType: nextType, syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0)
self = .confirmationCodeEntry(number: decoder.decodeStringForKey("num", orElse: ""), type: decoder.decodeObjectForKey("t", decoder: { SentAuthorizationCodeType(decoder: $0) }) as! SentAuthorizationCodeType, hash: decoder.decodeStringForKey("h", orElse: ""), timeout: decoder.decodeOptionalInt32ForKey("tm"), nextType: nextType, syncContacts: decoder.decodeInt32ForKey("syncContacts", orElse: 1) != 0, previousCodeEntry: decoder.decodeObjectForKey("previousCodeEntry", decoder: { UnauthorizedAccountStateContents(decoder: $0) }) as? UnauthorizedAccountStateContents, usePrevious: decoder.decodeInt32ForKey("usePrevious", orElse: 1) != 0)
case UnauthorizedAccountStateContentsValue.passwordEntry.rawValue:
var code: AuthorizationCode?
if let modernCode = decoder.decodeObjectForKey("modernCode", decoder: { AuthorizationCode(decoder: $0) }) as? AuthorizationCode {
@ -228,7 +228,7 @@ public enum UnauthorizedAccountStateContents: PostboxCoding, Equatable {
encoder.encodeInt32(UnauthorizedAccountStateContentsValue.phoneEntry.rawValue, forKey: "v")
encoder.encodeInt32(countryCode, forKey: "cc")
encoder.encodeString(number, forKey: "n")
case let .confirmationCodeEntry(number, type, hash, timeout, nextType, syncContacts):
case let .confirmationCodeEntry(number, type, hash, timeout, nextType, syncContacts, previousCodeEntry, usePrevious):
encoder.encodeInt32(UnauthorizedAccountStateContentsValue.confirmationCodeEntry.rawValue, forKey: "v")
encoder.encodeString(number, forKey: "num")
encoder.encodeObject(type, forKey: "t")
@ -244,6 +244,14 @@ public enum UnauthorizedAccountStateContents: PostboxCoding, Equatable {
encoder.encodeNil(forKey: "nt")
}
encoder.encodeInt32(syncContacts ? 1 : 0, forKey: "syncContacts")
if let previousCodeEntry = previousCodeEntry {
encoder.encodeObject(previousCodeEntry, forKey: "previousCodeEntry")
encoder.encodeInt32(usePrevious ? 1 : 0, forKey: "usePrevious")
} else {
encoder.encodeNil(forKey: "previousCodeEntry")
encoder.encodeInt32(0, forKey: "usePrevious")
}
case let .passwordEntry(hint, number, code, suggestReset, syncContacts):
encoder.encodeInt32(UnauthorizedAccountStateContentsValue.passwordEntry.rawValue, forKey: "v")
encoder.encodeString(hint, forKey: "h")
@ -312,8 +320,8 @@ public enum UnauthorizedAccountStateContents: PostboxCoding, Equatable {
} else {
return false
}
case let .confirmationCodeEntry(lhsNumber, lhsType, lhsHash, lhsTimeout, lhsNextType, lhsSyncContacts):
if case let .confirmationCodeEntry(rhsNumber, rhsType, rhsHash, rhsTimeout, rhsNextType, rhsSyncContacts) = rhs {
case let .confirmationCodeEntry(lhsNumber, lhsType, lhsHash, lhsTimeout, lhsNextType, lhsSyncContacts, lhsPreviousCodeEntry, lhsUsePrevious):
if case let .confirmationCodeEntry(rhsNumber, rhsType, rhsHash, rhsTimeout, rhsNextType, rhsSyncContacts, rhsPreviousCodeEntry, rhsUsePrevious) = rhs {
if lhsNumber != rhsNumber {
return false
}
@ -332,6 +340,12 @@ public enum UnauthorizedAccountStateContents: PostboxCoding, Equatable {
if lhsSyncContacts != rhsSyncContacts {
return false
}
if lhsPreviousCodeEntry != rhsPreviousCodeEntry {
return false
}
if lhsUsePrevious != rhsUsePrevious {
return false
}
return true
} else {
return false