Swiftgram/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift
2024-04-16 17:21:40 +04:00

287 lines
11 KiB
Swift

import Foundation
import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
import TelegramCore
import TelegramPresentationData
import ProgressNavigationButtonNode
public final class AuthorizationSequenceCodeEntryController: ViewController {
private var controllerNode: AuthorizationSequenceCodeEntryControllerNode {
return self.displayNode as! AuthorizationSequenceCodeEntryControllerNode
}
private var validLayout: ContainerViewLayout?
private let strings: PresentationStrings
private let theme: PresentationTheme
public var loginWithCode: ((String) -> Void)?
public var signInWithApple: (() -> Void)?
public var openFragment: ((String) -> Void)?
var reset: (() -> Void)?
public var requestNextOption: (() -> Void)?
public var requestPreviousOption: (() -> Void)?
var resetEmail: (() -> Void)?
var retryResetEmail: (() -> Void)?
var data: (String, String?, SentAuthorizationCodeType, AuthorizationCodeNextType?, Int32?, Bool, Bool)?
var termsOfService: (UnauthorizedAccountTermsOfService, Bool)?
private let hapticFeedback = HapticFeedback()
private var appleSignInAllowed = false
public var inProgress: Bool = false {
didSet {
self.updateNavigationItems()
self.controllerNode.inProgress = self.inProgress
}
}
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
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: AuthorizationSequenceController.navigationBarTheme(theme), strings: NavigationBarStrings(presentationStrings: strings)))
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.hasActiveInput = true
self.statusBar.statusBarStyle = theme.intro.statusBarStyle.style
self.attemptNavigation = { _ in
return false
}
self.navigationBar?.backPressed = { [weak self] in
let text: String
let proceed: String
let stop: String
if let (_, _, type, _, _, _, _) = self?.data, case .email = type {
text = presentationData.strings.Login_CancelEmailVerification
proceed = presentationData.strings.Login_CancelEmailVerificationContinue
stop = presentationData.strings.Login_CancelEmailVerificationStop
} else {
text = presentationData.strings.Login_CancelPhoneVerification
proceed = presentationData.strings.Login_CancelPhoneVerificationContinue
stop = presentationData.strings.Login_CancelPhoneVerificationStop
}
self?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .genericAction, title: proceed, action: {
}), TextAlertAction(type: .defaultAction, title: stop, action: {
back()
})]), in: .window(.root))
}
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func loadDisplayNode() {
self.displayNode = AuthorizationSequenceCodeEntryControllerNode(strings: self.strings, theme: self.theme)
self.displayNodeDidLoad()
self.controllerNode.view.disableAutomaticKeyboardHandling = [.forward, .backward]
self.controllerNode.loginWithCode = { [weak self] code in
self?.continueWithCode(code)
}
self.controllerNode.signInWithApple = { [weak self] in
self?.signInWithApple?()
}
self.controllerNode.openFragment = { [weak self] url in
self?.openFragment?(url)
}
self.controllerNode.requestNextOption = { [weak self] in
self?.requestNextOption?()
}
self.controllerNode.requestAnotherOption = { [weak self] in
self?.requestNextOption?()
}
self.controllerNode.requestPreviousOption = { [weak self] in
self?.requestPreviousOption?()
}
self.controllerNode.updateNextEnabled = { [weak self] value in
self?.navigationItem.rightBarButtonItem?.isEnabled = value
}
self.controllerNode.reset = { [weak self] in
self?.resetEmail?()
}
self.controllerNode.retryReset = { [weak self] in
self?.retryResetEmail?()
}
self.controllerNode.present = { [weak self] c, a in
self?.present(c, in: .window(.root), with: a)
}
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, hasPreviousCode: hasPreviousCode, previousIsPhrase: previousIsPhrase)
}
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let navigationController = self.navigationController as? NavigationController, let layout = self.validLayout {
addTemporaryKeyboardSnapshotView(navigationController: navigationController, parentView: self.view, layout: layout)
}
self.controllerNode.activateInput()
}
public func resetCode() {
self.controllerNode.resetCode()
}
public func animateSuccess() {
self.controllerNode.animateSuccess()
}
public func animateError(text: String) {
self.hapticFeedback.error()
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
}
if self.inProgress {
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.theme.rootController.navigationBar.accentTextColor))
self.navigationItem.rightBarButtonItem = item
} else {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
}
}
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?.5 != hasPreviousCode || self.data?.6 != previousIsPhrase {
self.data = (number, email, codeType, nextType, timeout, hasPreviousCode, previousIsPhrase)
var appleSignInAllowed = false
if case let .email(_, _, _, _, appleSignInAllowedValue, _) = codeType {
appleSignInAllowed = appleSignInAllowedValue
}
if self.isNodeLoaded {
self.controllerNode.updateData(number: number, email: email, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed, hasPreviousCode: hasPreviousCode, previousIsPhrase: previousIsPhrase)
self.requestLayout(transition: .immediate)
}
}
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
let hadLayout = self.validLayout != nil
self.validLayout = layout
if !hadLayout {
self.updateNavigationItems()
}
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
}
@objc private func nextPressed() {
guard let (_, _, type, _, _, _, _) = self.data else {
return
}
var minimalCodeLength = 1
switch type {
case let .otherSession(length):
minimalCodeLength = Int(length)
case let .sms(length):
minimalCodeLength = Int(length)
case let .call(length):
minimalCodeLength = Int(length)
case let .missedCall(_, length):
minimalCodeLength = Int(length)
case let .email(_, length, _, _, _, _):
minimalCodeLength = Int(length)
case let .fragment(_, length):
minimalCodeLength = Int(length)
case let .firebase(_, length):
minimalCodeLength = Int(length)
case .flashCall, .emailSetupRequired, .word, .phrase:
break
}
if self.controllerNode.currentCode.count < minimalCodeLength {
self.hapticFeedback.error()
self.controllerNode.animateError()
} else {
self.continueWithCode(self.controllerNode.currentCode)
}
}
private func continueWithCode(_ code: String) {
self.loginWithCode?(code)
}
public func applyConfirmationCode(_ code: Int) {
self.controllerNode.updateCode("\(code)")
}
}
func addTemporaryKeyboardSnapshotView(navigationController: NavigationController, parentView: UIView, layout: ContainerViewLayout) {
if case .compact = layout.metrics.widthClass, let statusBarHost = navigationController.statusBarHost {
if let keyboardView = statusBarHost.keyboardView {
if let snapshotView = keyboardView.snapshotView(afterScreenUpdates: false) {
keyboardView.layer.removeAllAnimations()
UIView.performWithoutAnimation {
snapshotView.frame = CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - snapshotView.frame.size.height), size: snapshotView.frame.size)
if let keyboardWindow = statusBarHost.keyboardWindow {
keyboardWindow.addSubview(snapshotView)
}
Queue.mainQueue().after(0.5, {
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
})
}
}
}
}
}