diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 46ce4128d1..a9b4783420 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7998,6 +7998,12 @@ Sorry for the inconvenience."; "Login.WrongCodeError" = "Wrong code, please try again."; "PrivacySettings.LoginEmail" = "Login Email"; +"Login.EmailChanged" = "Your email has been changed."; + +"Login.InvalidEmailAddressError" = "An error occurred. Please try again."; +"Login.InvalidEmailError" = "Please enter a valid e-mail address."; +"Login.InvalidEmailTokenError" = "An error occurred. Please try again."; +"Login.EmailNotAllowedError" = "Sorry, this email can't be used."; "Conversation.EmojiCopied" = "Emoji copied to clipboard"; diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift index fdd03d4aae..4bc44291d0 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceCodeEntryController.swift @@ -111,7 +111,7 @@ public final class AuthorizationSequenceCodeEntryController: ViewController { self.controllerNode.activateInput() } - func resetCode() { + public func resetCode() { self.controllerNode.resetCode() } diff --git a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift index ae6f333db1..80af48a4ef 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationSequenceController.swift @@ -302,30 +302,38 @@ public final class AuthorizationSequenceController: NavigationController, MFMail if let strongSelf = self, let controller = controller { controller.inProgress = false - var resetCode = false - let text: String - switch error { - case .limitExceeded: - resetCode = true - text = strongSelf.presentationData.strings.Login_CodeFloodError - case .invalidCode: - resetCode = true - text = strongSelf.presentationData.strings.Login_InvalidCodeError - case .generic: - text = strongSelf.presentationData.strings.Login_UnknownError - case .codeExpired: - text = strongSelf.presentationData.strings.Login_CodeExpired - let account = strongSelf.account - let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start() - case .timeout: - text = strongSelf.presentationData.strings.Login_NetworkError + if case .invalidCode = error { + controller.animateError(text: strongSelf.presentationData.strings.Login_WrongCodeError) + } else { + var resetCode = false + let text: String + switch error { + case .limitExceeded: + resetCode = true + text = strongSelf.presentationData.strings.Login_CodeFloodError + case .invalidCode: + resetCode = true + text = strongSelf.presentationData.strings.Login_InvalidCodeError + case .generic: + text = strongSelf.presentationData.strings.Login_UnknownError + case .codeExpired: + text = strongSelf.presentationData.strings.Login_CodeExpired + let account = strongSelf.account + let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start() + case .timeout: + text = strongSelf.presentationData.strings.Login_NetworkError + case .invalidEmailToken: + text = strongSelf.presentationData.strings.Login_InvalidEmailTokenError + case .emailNotAllowed: + text = strongSelf.presentationData.strings.Login_EmailNotAllowedError + } + + if resetCode { + controller.resetCode() + } + + controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } - - if resetCode { - controller.resetCode() - } - - controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } } })) @@ -420,6 +428,10 @@ public final class AuthorizationSequenceController: NavigationController, MFMail text = strongSelf.presentationData.strings.Login_CodeExpired let account = strongSelf.account let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start() + case .invalidEmailToken: + text = strongSelf.presentationData.strings.Login_InvalidEmailTokenError + case .invalidEmailAddress: + text = strongSelf.presentationData.strings.Login_InvalidEmailAddressError } if resetCode { @@ -550,6 +562,10 @@ public final class AuthorizationSequenceController: NavigationController, MFMail text = strongSelf.presentationData.strings.Login_UnknownError case .timeout: text = strongSelf.presentationData.strings.Login_NetworkError + case .invalidEmail: + text = strongSelf.presentationData.strings.Login_InvalidEmailError + case .emailNotAllowed: + text = strongSelf.presentationData.strings.Login_EmailNotAllowedError } controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) @@ -610,6 +626,10 @@ public final class AuthorizationSequenceController: NavigationController, MFMail text = strongSelf.presentationData.strings.Login_InvalidCodeError case .timeout: text = strongSelf.presentationData.strings.Login_NetworkError + case .invalidEmailToken: + text = strongSelf.presentationData.strings.Login_InvalidEmailTokenError + case .emailNotAllowed: + text = strongSelf.presentationData.strings.Login_EmailNotAllowedError } lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) } @@ -649,6 +669,10 @@ public final class AuthorizationSequenceController: NavigationController, MFMail text = strongSelf.presentationData.strings.Login_CodeExpired let account = strongSelf.account let _ = TelegramEngineUnauthorized(account: strongSelf.account).auth.setState(state: UnauthorizedAccountState(isTestingEnvironment: account.testingEnvironment, masterDatacenterId: account.masterDatacenterId, contents: .empty)).start() + case .invalidEmailToken: + text = strongSelf.presentationData.strings.Login_InvalidEmailTokenError + case .invalidEmailAddress: + text = strongSelf.presentationData.strings.Login_InvalidEmailAddressError } lastController.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root)) diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift index 8db8d65b7d..08a458e78b 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PrivacyAndSecurityController.swift @@ -17,6 +17,7 @@ import PasswordSetupUI import UndoUI import PremiumUI import AuthorizationUI +import AuthenticationServices private final class PrivacyAndSecurityControllerArguments { let account: Account @@ -1082,11 +1083,76 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting let codeController = AuthorizationSequenceCodeEntryController(presentationData: presentationData, openUrl: { _ in }, back: { dismissCodeControllerImpl?() }) - codeController.loginWithCode = { code in - + 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 + codeController?.animateSuccess() + 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) + + navigationController.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .emoji(name: "IntroLetter", text: presentationData.strings.Login_EmailChanged), elevatedLayout: false, animateInAsReplacement: false, action: { _ in + return false + })) + } + } + })) } codeController.signInWithApple = { - +// if #available(iOS 13.0, *) { +// let appleIdProvider = ASAuthorizationAppleIDProvider() +// let passwordProvider = ASAuthorizationPasswordProvider() +// let request = appleIdProvider.createRequest() +// +// let passwordRequest = passwordProvider.createRequest() +// +// let authorizationController = ASAuthorizationController(authorizationRequests: [request, passwordRequest]) +// authorizationController.delegate = strongSelf +// authorizationController.presentationContextProvider = strongSelf +// authorizationController.performRequests() +// } } codeController.updateData(number: "", email: email, codeType: .email(emailPattern: "", length: data.length, nextPhoneLoginDate: nil, appleSignInAllowed: false, setup: true), nextType: nil, timeout: nil, termsOfService: nil) pushControllerImpl?(codeController, true) @@ -1104,6 +1170,10 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting 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: {})])) diff --git a/submodules/TelegramCore/Sources/Authorization.swift b/submodules/TelegramCore/Sources/Authorization.swift index 75b6700c8e..9f8d40caa1 100644 --- a/submodules/TelegramCore/Sources/Authorization.swift +++ b/submodules/TelegramCore/Sources/Authorization.swift @@ -231,6 +231,8 @@ public enum AuthorizationCodeVerificationError { case limitExceeded case generic case codeExpired + case invalidEmailToken + case invalidEmailAddress } private enum AuthorizationCodeResult { @@ -312,6 +314,8 @@ public enum AuthorizationSendEmailCodeError { case limitExceeded case codeExpired case timeout + case invalidEmail + case emailNotAllowed } public enum AuthorizationEmailVerificationError { @@ -320,6 +324,8 @@ public enum AuthorizationEmailVerificationError { case codeExpired case invalidCode case timeout + case invalidEmailToken + case emailNotAllowed } public struct ChangeLoginEmailData: Equatable { @@ -335,6 +341,10 @@ public func sendLoginEmailChangeCode(account: Account, email: String) -> Signal< return .fail(.limitExceeded) } else if errorDescription == "CODE_HASH_EXPIRED" || errorDescription == "PHONE_CODE_EXPIRED" { return .fail(.codeExpired) + } else if errorDescription.hasPrefix("EMAIL_INVALID") { + return .fail(.invalidEmail) + } else if errorDescription.hasPrefix("EMAIL_NOT_ALLOWED") { + return .fail(.emailNotAllowed) } else { return .fail(.generic) } @@ -359,6 +369,10 @@ public func sendLoginEmailCode(account: UnauthorizedAccount, email: String) -> S return .fail(.limitExceeded) } else if errorDescription == "CODE_HASH_EXPIRED" || errorDescription == "PHONE_CODE_EXPIRED" { return .fail(.codeExpired) + } else if errorDescription.hasPrefix("EMAIL_INVALID") { + return .fail(.invalidEmail) + } else if errorDescription.hasPrefix("EMAIL_NOT_ALLOWED") { + return .fail(.emailNotAllowed) } else { return .fail(.generic) } @@ -389,6 +403,39 @@ public func sendLoginEmailCode(account: UnauthorizedAccount, email: String) -> S |> ignoreValues } +public func verifyLoginEmailChange(account: Account, code: AuthorizationCode.EmailVerification) -> Signal { + let verification: Api.EmailVerification + switch code { + case let .emailCode(code): + verification = .emailVerificationCode(code: code) + case let .appleToken(token): + verification = .emailVerificationApple(token: token) + case let .googleToken(token): + verification = .emailVerificationGoogle(token: token) + } + + return account.network.request(Api.functions.account.verifyEmail(purpose: .emailVerifyPurposeLoginChange, verification: verification), automaticFloodWait: false) + |> `catch` { error -> Signal in + let errorDescription = error.errorDescription ?? "" + if errorDescription.hasPrefix("FLOOD_WAIT") { + return .fail(.limitExceeded) + } else if errorDescription == "CODE_HASH_EXPIRED" || errorDescription == "PHONE_CODE_EXPIRED" || errorDescription == "EMAIL_VERIFY_EXPIRED" { + return .fail(.codeExpired) + } else if errorDescription == "CODE_INVALID" { + return .fail(.invalidCode) + } else if errorDescription == "EMAIL_TOKEN_INVALID" { + return .fail(.invalidEmailToken) + } else if errorDescription == "EMAIL_NOT_ALLOWED" { + return .fail(.emailNotAllowed) + } else { + return .fail(.generic) + } + } + |> mapToSignal { _ -> Signal in + return .never() + } +} + public func verifyLoginEmailSetup(account: UnauthorizedAccount, code: AuthorizationCode.EmailVerification) -> Signal { return account.postbox.transaction { transaction -> Signal in if let state = transaction.getState() as? UnauthorizedAccountState { @@ -409,10 +456,14 @@ public func verifyLoginEmailSetup(account: UnauthorizedAccount, code: Authorizat let errorDescription = error.errorDescription ?? "" if errorDescription.hasPrefix("FLOOD_WAIT") { return .fail(.limitExceeded) - } else if errorDescription == "CODE_HASH_EXPIRED" || errorDescription == "PHONE_CODE_EXPIRED" { + } else if errorDescription == "CODE_HASH_EXPIRED" || errorDescription == "PHONE_CODE_EXPIRED" || errorDescription == "EMAIL_VERIFY_EXPIRED" { return .fail(.codeExpired) - } else if errorDescription == "" { + } else if errorDescription == "CODE_INVALID" { return .fail(.invalidCode) + } else if errorDescription == "EMAIL_TOKEN_INVALID" { + return .fail(.invalidEmailToken) + } else if errorDescription == "EMAIL_NOT_ALLOWED" { + return .fail(.emailNotAllowed) } else { return .fail(.generic) } @@ -502,12 +553,16 @@ public func authorizeWithCode(accountManager: AccountManager