mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-24 15:15:16 +00:00
Update API
This commit is contained in:
@@ -39,6 +39,9 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
private var signInWithAppleButton: UIControl?
|
||||
private let proceedNode: SolidRoundedButtonNode
|
||||
|
||||
private let textField: TextFieldNode
|
||||
private let textSeparatorNode: ASDisplayNode
|
||||
|
||||
private let codeInputView: CodeInputView
|
||||
private let errorTextNode: ImmediateTextNode
|
||||
|
||||
@@ -64,7 +67,12 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
var email: String?
|
||||
|
||||
var currentCode: String {
|
||||
return self.codeInputView.text
|
||||
switch self.codeType {
|
||||
case .word, .phrase:
|
||||
return self.textField.textField.text ?? ""
|
||||
default:
|
||||
return self.codeInputView.text
|
||||
}
|
||||
}
|
||||
|
||||
var loginWithCode: ((String) -> Void)?
|
||||
@@ -81,6 +89,19 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
var inProgress: Bool = false {
|
||||
didSet {
|
||||
self.codeInputView.alpha = self.inProgress ? 0.6 : 1.0
|
||||
|
||||
switch self.codeType {
|
||||
case .word, .phrase:
|
||||
if self.inProgress != oldValue {
|
||||
if self.inProgress {
|
||||
self.proceedNode.transitionToProgress()
|
||||
} else {
|
||||
self.proceedNode.transitionFromProgress()
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +172,28 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
self.codeInputView.textField.keyboardType = .numberPad
|
||||
}
|
||||
|
||||
self.textSeparatorNode = ASDisplayNode()
|
||||
self.textSeparatorNode.isLayerBacked = true
|
||||
self.textSeparatorNode.backgroundColor = self.theme.list.itemPlainSeparatorColor
|
||||
|
||||
self.textField = TextFieldNode()
|
||||
self.textField.textField.font = Font.regular(20.0)
|
||||
self.textField.textField.textColor = self.theme.list.itemPrimaryTextColor
|
||||
self.textField.textField.textAlignment = .natural
|
||||
self.textField.textField.autocorrectionType = .yes
|
||||
self.textField.textField.autocorrectionType = .no
|
||||
self.textField.textField.spellCheckingType = .yes
|
||||
self.textField.textField.spellCheckingType = .no
|
||||
self.textField.textField.autocapitalizationType = .none
|
||||
self.textField.textField.keyboardType = .default
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
self.textField.textField.textContentType = UITextContentType(rawValue: "")
|
||||
}
|
||||
self.textField.textField.returnKeyType = .default
|
||||
self.textField.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance
|
||||
self.textField.textField.disableAutomaticKeyboardHandling = [.forward, .backward]
|
||||
self.textField.textField.tintColor = self.theme.list.itemAccentColor
|
||||
|
||||
self.errorTextNode = ImmediateTextNode()
|
||||
self.errorTextNode.alpha = 0.0
|
||||
self.errorTextNode.displaysAsynchronously = false
|
||||
@@ -175,11 +218,12 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
self.signInWithAppleButton?.isHidden = true
|
||||
(self.signInWithAppleButton as? ASAuthorizationAppleIDButton)?.cornerRadius = 11
|
||||
}
|
||||
self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_OpenFragment, theme: SolidRoundedButtonTheme(backgroundColor: UIColor(rgb: 0x37475a), foregroundColor: .white), height: 50.0, cornerRadius: 11.0, gloss: false)
|
||||
self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), height: 50.0, cornerRadius: 11.0, gloss: false)
|
||||
self.proceedNode.progressType = .embedded
|
||||
self.proceedNode.isHidden = true
|
||||
self.proceedNode.iconSpacing = 4.0
|
||||
self.proceedNode.animationSize = CGSize(width: 36.0, height: 36.0)
|
||||
self.proceedNode.isEnabled = false
|
||||
|
||||
super.init()
|
||||
|
||||
@@ -189,7 +233,11 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
|
||||
self.backgroundColor = self.theme.list.plainBackgroundColor
|
||||
|
||||
self.textField.textField.delegate = self
|
||||
|
||||
self.addSubnode(self.codeInputView)
|
||||
self.addSubnode(self.textSeparatorNode)
|
||||
self.addSubnode(self.textField)
|
||||
self.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.titleActivateAreaNode)
|
||||
self.addSubnode(self.titleIconNode)
|
||||
@@ -208,9 +256,11 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.textChanged(text: strongSelf.codeInputView.text)
|
||||
strongSelf.codeChanged(text: strongSelf.codeInputView.text)
|
||||
}
|
||||
|
||||
self.textField.textField.addTarget(self, action: #selector(self.textDidChange), for: .editingChanged)
|
||||
|
||||
self.codeInputView.longPressed = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@@ -261,7 +311,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
|
||||
func updateCode(_ code: String) {
|
||||
self.codeInputView.text = code
|
||||
self.textChanged(text: code)
|
||||
self.codeChanged(text: code)
|
||||
|
||||
if let codeLength = self.requiredCodeLength, code.count == Int(codeLength) {
|
||||
self.loginWithCode?(code)
|
||||
@@ -401,6 +451,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
|
||||
var animationName = "IntroMessage"
|
||||
var animationPlaybackMode: AnimatedStickerPlaybackMode = .once
|
||||
var textFieldPlaceholder = ""
|
||||
if let codeType = self.codeType {
|
||||
switch codeType {
|
||||
case .missedCall:
|
||||
@@ -412,9 +463,20 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_EnterCodeSMSTitle, font: Font.semibold(28.0), textColor: self.theme.list.itemPrimaryTextColor)
|
||||
case .fragment:
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_EnterCodeFragmentTitle, font: Font.semibold(28.0), textColor: self.theme.list.itemPrimaryTextColor)
|
||||
|
||||
self.proceedNode.title = self.strings.Login_OpenFragment
|
||||
self.proceedNode.updateTheme(SolidRoundedButtonTheme(backgroundColor: UIColor(rgb: 0x37475a), foregroundColor: .white))
|
||||
self.proceedNode.isEnabled = true
|
||||
|
||||
animationName = "IntroFragment"
|
||||
animationPlaybackMode = .count(3)
|
||||
self.proceedNode.animation = "anim_fragment"
|
||||
case .word:
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_EnterWordTitle, font: Font.semibold(28.0), textColor: self.theme.list.itemPrimaryTextColor)
|
||||
textFieldPlaceholder = self.strings.Login_EnterWordPlaceholder
|
||||
case .phrase:
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_EnterPhraseTitle, font: Font.semibold(28.0), textColor: self.theme.list.itemPrimaryTextColor)
|
||||
textFieldPlaceholder = self.strings.Login_EnterPhrasePlaceholder
|
||||
default:
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_EnterCodeTelegramTitle, font: Font.semibold(28.0), textColor: self.theme.list.itemPrimaryTextColor)
|
||||
}
|
||||
@@ -422,14 +484,17 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_EnterCodeTelegramTitle, font: Font.semibold(40.0), textColor: self.theme.list.itemPrimaryTextColor)
|
||||
}
|
||||
|
||||
self.textField.textField.placeholder = textFieldPlaceholder
|
||||
|
||||
self.titleActivateAreaNode.accessibilityLabel = self.titleNode.attributedText?.string ?? ""
|
||||
|
||||
if let inputHeight = layout.inputHeight {
|
||||
if let codeType = self.codeType, case .email = codeType {
|
||||
switch self.codeType {
|
||||
case .email, .fragment:
|
||||
insets.bottom = max(inputHeight, insets.bottom)
|
||||
} else if let codeType = self.codeType, case .fragment = codeType {
|
||||
insets.bottom = max(inputHeight, insets.bottom)
|
||||
} else {
|
||||
case .word, .phrase:
|
||||
insets.bottom = max(inputHeight, layout.standardKeyboardHeight)
|
||||
default:
|
||||
insets.bottom = max(inputHeight, layout.standardInputHeight)
|
||||
}
|
||||
}
|
||||
@@ -475,6 +540,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
codeLength = Int(length)
|
||||
case .emailSetupRequired:
|
||||
codeLength = 6
|
||||
case .word, .phrase:
|
||||
codeLength = 0
|
||||
case .none:
|
||||
codeLength = 6
|
||||
}
|
||||
@@ -498,8 +565,11 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
if layout.size.width > 320.0 {
|
||||
items.append(AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
self.animationNode.updateLayout(size: animationSize)
|
||||
self.animationNode.isHidden = false
|
||||
self.animationNode.visibility = true
|
||||
} else {
|
||||
insets.top = navigationBarHeight
|
||||
self.animationNode.isHidden = true
|
||||
}
|
||||
|
||||
var additionalBottomInset: CGFloat = 20.0
|
||||
@@ -550,7 +620,19 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
}
|
||||
}
|
||||
|
||||
items.append(AuthorizationLayoutItem(node: self.codeInputView, size: codeFieldSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 30.0, maxValue: 30.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: canReset || pendingDate != nil ? 0.0 : 104.0, maxValue: canReset ? 0.0 : 104.0)))
|
||||
switch codeType {
|
||||
case .word, .phrase:
|
||||
self.codeInputView.isHidden = true
|
||||
self.textField.isHidden = false
|
||||
self.textSeparatorNode.isHidden = false
|
||||
items.append(AuthorizationLayoutItem(node: self.textField, size: CGSize(width: maximumWidth - 88.0, height: 44.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 30.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
items.append(AuthorizationLayoutItem(node: self.textSeparatorNode, size: CGSize(width: maximumWidth - 48.0, height: UIScreenPixel), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
default:
|
||||
self.codeInputView.isHidden = false
|
||||
self.textField.isHidden = true
|
||||
self.textSeparatorNode.isHidden = true
|
||||
items.append(AuthorizationLayoutItem(node: self.codeInputView, size: codeFieldSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 30.0, maxValue: 30.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: canReset || pendingDate != nil ? 0.0 : 104.0, maxValue: canReset ? 0.0 : 104.0)))
|
||||
}
|
||||
|
||||
if canReset {
|
||||
self.resetNode.setAttributedTitle(NSAttributedString(string: self.strings.Login_Email_CantAccess, font: Font.regular(17.0), textColor: self.theme.list.itemAccentColor, paragraphAlignment: .center), for: [])
|
||||
@@ -614,7 +696,29 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
} else {
|
||||
self.signInWithAppleButton?.isHidden = true
|
||||
self.dividerNode.isHidden = true
|
||||
self.proceedNode.isHidden = true
|
||||
|
||||
switch codeType {
|
||||
case .word, .phrase:
|
||||
additionalBottomInset = 120.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)))
|
||||
if layout.size.width > 320.0 {
|
||||
self.proceedNode.isHidden = false
|
||||
} else {
|
||||
self.proceedNode.isHidden = true
|
||||
}
|
||||
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - proceedSize.width) / 2.0), y: layout.size.height - insets.bottom - proceedSize.height - inset), size: proceedSize)
|
||||
transition.updateFrame(node: self.proceedNode, frame: buttonFrame)
|
||||
case .email:
|
||||
self.nextOptionButtonNode.isHidden = true
|
||||
self.proceedNode.isHidden = true
|
||||
default:
|
||||
self.nextOptionButtonNode.isHidden = false
|
||||
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
|
||||
@@ -644,31 +748,58 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
}
|
||||
|
||||
func activateInput() {
|
||||
let _ = self.codeInputView.becomeFirstResponder()
|
||||
switch self.codeType {
|
||||
case .word, .phrase:
|
||||
self.textField.textField.becomeFirstResponder()
|
||||
default:
|
||||
let _ = self.codeInputView.becomeFirstResponder()
|
||||
}
|
||||
}
|
||||
|
||||
func animateError() {
|
||||
self.codeInputView.layer.addShakeAnimation()
|
||||
switch self.codeType {
|
||||
case .word, .phrase:
|
||||
self.textField.layer.addShakeAnimation()
|
||||
default:
|
||||
self.codeInputView.layer.addShakeAnimation()
|
||||
}
|
||||
}
|
||||
|
||||
func animateError(text: String) {
|
||||
self.codeInputView.animateError()
|
||||
self.codeInputView.layer.addShakeAnimation(amplitude: -30.0, duration: 0.5, count: 6, decay: true)
|
||||
let errorOriginY: CGFloat
|
||||
let errorOriginOffset: CGFloat
|
||||
switch self.codeType {
|
||||
case .word, .phrase:
|
||||
self.textField.layer.addShakeAnimation()
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.15, curve: .easeInOut)
|
||||
transition.updateBackgroundColor(node: self.textSeparatorNode, color: self.theme.list.itemDestructiveColor)
|
||||
errorOriginY = self.textField.frame.maxY
|
||||
errorOriginOffset = 5.0
|
||||
default:
|
||||
self.codeInputView.animateError()
|
||||
self.codeInputView.layer.addShakeAnimation(amplitude: -30.0, duration: 0.5, count: 6, decay: true)
|
||||
errorOriginY = self.codeInputView.frame.maxY
|
||||
errorOriginOffset = 11.0
|
||||
}
|
||||
|
||||
self.errorTextNode.attributedText = NSAttributedString(string: text, font: Font.regular(17.0), textColor: self.theme.list.itemDestructiveColor, paragraphAlignment: .center)
|
||||
self.errorTextNode.attributedText = NSAttributedString(string: text, font: Font.regular(13.0), textColor: self.theme.list.itemDestructiveColor, paragraphAlignment: .center)
|
||||
|
||||
if let (layout, _) = self.layoutArguments {
|
||||
let errorTextSize = self.errorTextNode.updateLayout(CGSize(width: layout.size.width - 48.0, height: .greatestFiniteMagnitude))
|
||||
let yOffset: CGFloat = layout.size.width > 320.0 ? 28.0 : 15.0
|
||||
self.errorTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - errorTextSize.width) / 2.0), y: self.codeInputView.frame.maxY + yOffset), size: errorTextSize)
|
||||
let yOffset: CGFloat = layout.size.width > 320.0 ? errorOriginOffset + 13.0 : errorOriginOffset
|
||||
self.errorTextNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - errorTextSize.width) / 2.0), y: errorOriginY + yOffset), size: errorTextSize)
|
||||
}
|
||||
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)
|
||||
|
||||
Queue.mainQueue().after(0.85) {
|
||||
Queue.mainQueue().after(1.6) {
|
||||
self.errorTextNode.alpha = 0.0
|
||||
self.errorTextNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15)
|
||||
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.15, curve: .easeInOut)
|
||||
transition.updateBackgroundColor(node: self.textSeparatorNode, color: self.theme.list.itemPlainSeparatorColor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -678,12 +809,14 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
let values: [NSNumber] = [1.0, 1.1, 1.0]
|
||||
self.codeInputView.layer.animateKeyframes(values: values, duration: 0.4, keyPath: "transform.scale")
|
||||
}
|
||||
|
||||
@objc func codeFieldTextChanged(_ textField: UITextField) {
|
||||
self.textChanged(text: textField.text ?? "")
|
||||
|
||||
@objc private func textDidChange() {
|
||||
let text = self.textField.textField.text ?? ""
|
||||
self.proceedNode.isEnabled = !text.isEmpty
|
||||
self.updateNextEnabled?(!text.isEmpty)
|
||||
}
|
||||
|
||||
private func textChanged(text: String) {
|
||||
|
||||
private func codeChanged(text: String) {
|
||||
self.updateNextEnabled?(!text.isEmpty)
|
||||
if let codeType = self.codeType {
|
||||
var codeLength: Int32?
|
||||
@@ -715,20 +848,36 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
if self.inProgress {
|
||||
return false
|
||||
}
|
||||
var result = ""
|
||||
for c in string {
|
||||
if c.unicodeScalars.count == 1 {
|
||||
let scalar = c.unicodeScalars.first!
|
||||
if scalar >= "0" && scalar <= "9" {
|
||||
result.append(c)
|
||||
|
||||
var updated = textField.text ?? ""
|
||||
updated.replaceSubrange(updated.index(updated.startIndex, offsetBy: range.lowerBound) ..< updated.index(updated.startIndex, offsetBy: range.upperBound), with: string)
|
||||
|
||||
if let codeType = self.codeType {
|
||||
switch codeType {
|
||||
case let .word(startsWith):
|
||||
if let startsWith, startsWith.count == 1, !updated.isEmpty && !updated.hasPrefix(startsWith) {
|
||||
if self.errorTextNode.alpha.isZero {
|
||||
//TODO:localize
|
||||
self.animateError(text: "Incorrect, please try again")
|
||||
}
|
||||
return false
|
||||
}
|
||||
case let .phrase(startsWith):
|
||||
if let startsWith, !updated.isEmpty {
|
||||
let firstWord = updated.components(separatedBy: " ").first ?? ""
|
||||
if !firstWord.isEmpty && !startsWith.hasPrefix(firstWord) {
|
||||
if self.errorTextNode.alpha.isZero {
|
||||
//TODO:localize
|
||||
self.animateError(text: "Incorrect, please try again")
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
if result != string {
|
||||
textField.text = result
|
||||
self.codeFieldTextChanged(textField)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -737,8 +886,15 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
}
|
||||
|
||||
@objc func proceedPressed() {
|
||||
if case let .fragment(url, _) = self.codeType {
|
||||
switch self.codeType {
|
||||
case let .fragment(url, _):
|
||||
self.openFragment?(url)
|
||||
case .word, .phrase:
|
||||
if let text = self.textField.textField.text, !text.isEmpty {
|
||||
self.loginWithCode?(text)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user