diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 2e5df8fa0e..c84880d009 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -192,6 +192,7 @@ public enum ResolvedUrlSettingsSection { case twoStepAuth case enableLog case phonePrivacy + case loginEmail } public struct ResolvedBotChoosePeerTypes: OptionSet { diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceEmailEntryController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceEmailEntryController.swift index 9a763b828d..0dc35c2220 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceEmailEntryController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceEmailEntryController.swift @@ -35,6 +35,9 @@ public final class AuthorizationSequenceEmailEntryController: ViewController { } } + public var authorization: Any? + public var authorizationDelegate: Any? + public init(presentationData: PresentationData, mode: Mode, back: @escaping () -> Void) { self.presentationData = presentationData self.mode = mode diff --git a/submodules/SettingsUI/Sources/Privacy and Security/LoginEmailSetupController.swift b/submodules/SettingsUI/Sources/Privacy and Security/LoginEmailSetupController.swift new file mode 100644 index 0000000000..8443fe1e20 --- /dev/null +++ b/submodules/SettingsUI/Sources/Privacy and Security/LoginEmailSetupController.swift @@ -0,0 +1,217 @@ +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, emailPattern: String?, navigationController: NavigationController?, completion: @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: .emoji(name: "IntroLetter", text: presentationData.strings.Login_EmailChanged), elevatedLayout: false, animateInAsReplacement: false, action: { _ in + return false + })) + }) + } + } + } + + let emailController = AuthorizationSequenceEmailEntryController(presentationData: presentationData, mode: emailPattern != nil ? .change : .setup, 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?() + }) + + 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 +} diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift index 203aa6800f..14ec641261 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift @@ -1486,33 +1486,7 @@ public func privacyAndSecurityController( controller.didAppear = { _ in updateHasTwoStepAuth() } - - let emailChangeCompletion: (AuthorizationSequenceCodeEntryController?) -> Void = { codeController in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - codeController?.animateSuccess() - updatedTwoStepAuthData?() - - Queue.mainQueue().after(0.75) { - if let navigationController = getNavigationControllerImpl?() { - 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: .emoji(name: "IntroLetter", text: presentationData.strings.Login_EmailChanged), elevatedLayout: false, animateInAsReplacement: false, action: { _ in - return false - })) - }) - } - } - } - showPrivacySuggestionImpl = { let presentationData = context.sharedContext.currentPresentationData.with { $0 } if reviewCallPrivacySuggestion { @@ -1557,156 +1531,10 @@ public func privacyAndSecurityController( } setupEmailImpl = { emailPattern in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - var dismissEmailControllerImpl: (() -> Void)? - var presentControllerImpl: ((ViewController) -> Void)? - - let emailController = AuthorizationSequenceEmailEntryController(presentationData: presentationData, mode: emailPattern != nil ? .change : .setup, back: { - dismissEmailControllerImpl?() + let controller = loginEmailSetupController(context: context, emailPattern: emailPattern, navigationController: getNavigationControllerImpl?(), completion: { + updatedTwoStepAuthData?() }) - emailController.proceedWithEmail = { [weak emailController] email in - emailController?.inProgress = true - - actionsDisposable.add((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?() - }) - - presentControllerImpl = { [weak codeController] c in - codeController?.present(c, in: .window(.root), with: nil) - } - - codeController.loginWithCode = { [weak codeController] code in - actionsDisposable.add((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) - pushControllerImpl?(codeController, true) - 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 controller, 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 = controller - authorizationController.presentationContextProvider = controller - authorizationController.performRequests() - - controller?.authorizationCompletion = { [weak controller, 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 - } - actionsDisposable.add((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 controller] in - controller?.authorizationCompletion = nil - emailChangeCompletion(nil) - })) - default: - break - } - } - } - } - emailController.updateData(appleSignInAllowed: true) - pushControllerImpl?(emailController, true) - - presentControllerImpl = { [weak emailController] c in - emailController?.present(c, in: .window(.root), with: nil) - } - - dismissEmailControllerImpl = { [weak emailController] in - emailController?.dismiss() - } + pushControllerImpl?(controller, true) } return controller diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index d3babce5fc..89a4ca3b7a 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -827,6 +827,11 @@ func openResolvedUrlImpl( navigationController.pushViewController(controller) } }) + case .loginEmail: + if let navigationController { + let controller = loginEmailSetupController(context: context, emailPattern: nil, navigationController: navigationController, completion: {}) + navigationController.pushViewController(controller) + } } case let .premiumOffer(reference): dismissInput() diff --git a/submodules/TelegramUI/Sources/OpenUrl.swift b/submodules/TelegramUI/Sources/OpenUrl.swift index a6f24f2535..b797fb7037 100644 --- a/submodules/TelegramUI/Sources/OpenUrl.swift +++ b/submodules/TelegramUI/Sources/OpenUrl.swift @@ -1088,6 +1088,8 @@ func openExternalUrlImpl(context: AccountContext, urlContext: OpenURLContext, ur section = .enableLog case "phone_privacy": section = .phonePrivacy + case "login_email": + section = .loginEmail default: break }