mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-03-01 14:20:58 +00:00
Authorization improvements
This commit is contained in:
@@ -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 {
|
||||
@@ -308,7 +354,19 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
self.view.addSubview(signInWithAppleButton)
|
||||
}
|
||||
}
|
||||
|
||||
@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, _):
|
||||
|
||||
Reference in New Issue
Block a user