Swiftgram/submodules/SettingsUI/Sources/Privacy and Security/LoginEmailSetupController.swift
2025-11-04 14:43:17 +04:00

219 lines
12 KiB
Swift

import Foundation
import UIKit
import Display
import SwiftSignalKit
import TelegramCore
import AccountContext
import TelegramPresentationData
import AuthorizationUI
import AuthenticationServices
import UndoUI
final class LoginEmailSetupDelegate: NSObject, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding {
var authorizationCompletion: ((Any) -> Void)?
private var context: AccountContext
init(context: AccountContext) {
self.context = context
}
@available(iOS 13.0, *)
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
self.authorizationCompletion?(authorization.credential)
}
@available(iOS 13.0, *)
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
Logger.shared.log("AppleSignIn", "Failed with error: \(error.localizedDescription)")
}
@available(iOS 13.0, *)
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return self.context.sharedContext.mainWindow!.viewController!.view.window!
}
}
public func loginEmailSetupController(context: AccountContext, blocking: Bool, emailPattern: String?, canAutoDismissIfNeeded: Bool = false, navigationController: NavigationController?, completion: @escaping () -> Void, dismiss: @escaping () -> Void) -> ViewController {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
var dismissEmailControllerImpl: (() -> Void)?
var presentControllerImpl: ((ViewController) -> Void)?
let delegate = LoginEmailSetupDelegate(context: context)
let emailChangeCompletion: (AuthorizationSequenceCodeEntryController?) -> Void = { codeController in
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
codeController?.animateSuccess()
completion()
Queue.mainQueue().after(0.75) {
if let navigationController {
let controllers = navigationController.viewControllers.filter { controller in
if controller is AuthorizationSequenceEmailEntryController || controller is AuthorizationSequenceCodeEntryController {
return false
} else {
return true
}
}
navigationController.setViewControllers(controllers, animated: true)
Queue.mainQueue().after(0.5, {
navigationController.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.LoginEmail_Success_Title, text: presentationData.strings.LoginEmail_Success_Text, cancel: nil, destructive: false), elevatedLayout: false, animateInAsReplacement: false, action: { _ in
return false
}))
})
}
}
}
let emailController = AuthorizationSequenceEmailEntryController(context: canAutoDismissIfNeeded ? context : nil, presentationData: presentationData, mode: emailPattern != nil ? .change : .setup, blocking: blocking, back: {
dismissEmailControllerImpl?()
})
emailController.proceedWithEmail = { [weak emailController] email in
emailController?.inProgress = true
let _ = (sendLoginEmailChangeCode(account: context.account, email: email)
|> deliverOnMainQueue).start(next: { data in
var dismissCodeControllerImpl: (() -> Void)?
var presentControllerImpl: ((ViewController) -> Void)?
let codeController = AuthorizationSequenceCodeEntryController(presentationData: presentationData, back: {
dismissCodeControllerImpl?()
dismiss()
})
presentControllerImpl = { [weak codeController] c in
codeController?.present(c, in: .window(.root), with: nil)
}
codeController.loginWithCode = { [weak codeController] code in
let _ = (verifyLoginEmailChange(account: context.account, code: .emailCode(code))
|> deliverOnMainQueue).start(error: { error in
Queue.mainQueue().async {
codeController?.inProgress = false
if case .invalidCode = error {
codeController?.animateError(text: presentationData.strings.Login_WrongCodeError)
} else {
var resetCode = false
let text: String
switch error {
case .limitExceeded:
resetCode = true
text = presentationData.strings.Login_CodeFloodError
case .invalidCode:
resetCode = true
text = presentationData.strings.Login_InvalidCodeError
case .generic:
text = presentationData.strings.Login_UnknownError
case .codeExpired:
text = presentationData.strings.Login_CodeExpired
case .timeout:
text = presentationData.strings.Login_NetworkError
case .invalidEmailToken:
text = presentationData.strings.Login_InvalidEmailTokenError
case .emailNotAllowed:
text = presentationData.strings.Login_EmailNotAllowedError
}
if resetCode {
codeController?.resetCode()
}
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]))
}
}
}, completed: { [weak codeController] in
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, previousCodeType: nil, isPrevious: false)
navigationController?.pushViewController(codeController)
dismissCodeControllerImpl = { [weak codeController] in
codeController?.dismiss()
}
}, error: { [weak emailController] error in
emailController?.inProgress = false
let text: String
switch error {
case .limitExceeded:
text = presentationData.strings.Login_CodeFloodError
case .generic, .codeExpired:
text = presentationData.strings.Login_UnknownError
case .timeout:
text = presentationData.strings.Login_NetworkError
case .invalidEmail:
text = presentationData.strings.Login_InvalidEmailError
case .emailNotAllowed:
text = presentationData.strings.Login_EmailNotAllowedError
}
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]))
}, completed: { [weak emailController] in
emailController?.inProgress = false
})
}
emailController.signInWithApple = { [weak emailController] in
if #available(iOS 13.0, *) {
let appleIdProvider = ASAuthorizationAppleIDProvider()
let request = appleIdProvider.createRequest()
request.requestedScopes = [.email]
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = delegate
authorizationController.presentationContextProvider = delegate
authorizationController.performRequests()
emailController?.authorization = authorizationController
emailController?.authorizationDelegate = delegate
delegate.authorizationCompletion = { [weak emailController] credential in
guard let credential = credential as? ASAuthorizationCredential else {
return
}
switch credential {
case let appleIdCredential as ASAuthorizationAppleIDCredential:
guard let tokenData = appleIdCredential.identityToken, let token = String(data: tokenData, encoding: .utf8) else {
emailController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
return
}
let _ = (verifyLoginEmailChange(account: context.account, code: .appleToken(token))
|> deliverOnMainQueue).start(error: { error in
let text: String
switch error {
case .limitExceeded:
text = presentationData.strings.Login_CodeFloodError
case .generic, .codeExpired:
text = presentationData.strings.Login_UnknownError
case .invalidCode:
text = presentationData.strings.Login_InvalidCodeError
case .timeout:
text = presentationData.strings.Login_NetworkError
case .invalidEmailToken:
text = presentationData.strings.Login_InvalidEmailTokenError
case .emailNotAllowed:
text = presentationData.strings.Login_EmailNotAllowedError
}
emailController?.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}, completed: { [weak emailController] in
emailController?.authorization = nil
emailController?.authorizationDelegate = nil
emailChangeCompletion(nil)
})
default:
break
}
}
}
}
emailController.updateData(appleSignInAllowed: true)
presentControllerImpl = { [weak emailController] c in
emailController?.present(c, in: .window(.root), with: nil)
}
dismissEmailControllerImpl = { [weak emailController] in
emailController?.dismiss()
}
return emailController
}