From efe5513c4e9fe1f2ceb1026daa7b41c10b267961 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 15 Feb 2019 14:56:32 -0500 Subject: [PATCH] Sketch out the 'onboarding code verification' view. --- .../OnboardingBaseViewController.swift | 4 + .../Registration/OnboardingController.swift | 153 +++++++++++++++++- ...OnboardingVerificationViewController.swift | 22 ++- .../src/Account/TSAccountManager.h | 2 + 4 files changed, 179 insertions(+), 2 deletions(-) diff --git a/Signal/src/ViewControllers/Registration/OnboardingBaseViewController.swift b/Signal/src/ViewControllers/Registration/OnboardingBaseViewController.swift index ac9125a27..79bffb0c2 100644 --- a/Signal/src/ViewControllers/Registration/OnboardingBaseViewController.swift +++ b/Signal/src/ViewControllers/Registration/OnboardingBaseViewController.swift @@ -82,6 +82,8 @@ public class OnboardingBaseViewController: OWSViewController { super.viewWillAppear(animated) self.navigationController?.isNavigationBarHidden = true + // Disable "back" gesture. + self.navigationController?.navigationItem.backBarButtonItem?.isEnabled = false // TODO: Is there a better way to do this? if let navigationController = self.navigationController as? OWSNavigationController { @@ -95,6 +97,8 @@ public class OnboardingBaseViewController: OWSViewController { super.viewDidAppear(animated) self.navigationController?.isNavigationBarHidden = true + // Disable "back" gesture. + self.navigationController?.navigationItem.backBarButtonItem?.isEnabled = false } // MARK: - Orientation diff --git a/Signal/src/ViewControllers/Registration/OnboardingController.swift b/Signal/src/ViewControllers/Registration/OnboardingController.swift index 5909cd674..e500f393f 100644 --- a/Signal/src/ViewControllers/Registration/OnboardingController.swift +++ b/Signal/src/ViewControllers/Registration/OnboardingController.swift @@ -66,6 +66,14 @@ public class OnboardingController: NSObject { return TSAccountManager.sharedInstance() } + private var accountManager: AccountManager { + return AppEnvironment.shared.accountManager + } + + private var backup: OWSBackup { + return AppEnvironment.shared.backup + } + // MARK: - @objc @@ -148,6 +156,97 @@ public class OnboardingController: NSObject { navigationController.pushViewController(view, animated: true) } + @objc + public func verificationDidComplete(fromView view: UIViewController) { + AssertIsOnMainThread() + + Logger.info("") + + if tsAccountManager.isReregistering() { + showProfileView(fromView: view) + } else { + checkCanImportBackup(fromView: view) + } + } + + private func showProfileView(fromView view: UIViewController) { + AssertIsOnMainThread() + + Logger.info("") + + guard let navigationController = view.navigationController else { + owsFailDebug("Missing navigationController") + return + } + + ProfileViewController.present(forRegistration: navigationController) + } + + private func showBackupRestoreView(fromView view: UIViewController) { + AssertIsOnMainThread() + + Logger.info("") + + guard let navigationController = view.navigationController else { + owsFailDebug("Missing navigationController") + return + } + + let restoreView = BackupRestoreViewController() + navigationController.setViewControllers([restoreView], animated: true) + } + + private func checkCanImportBackup(fromView view: UIViewController) { + AssertIsOnMainThread() + + Logger.info("") + + self.backup.checkCanImport({ (canImport) in + Logger.info("canImport: \(canImport)") + + if (canImport) { + self.backup.setHasPendingRestoreDecision(true) + + self.showBackupRestoreView(fromView: view) + } else { + self.showProfileView(fromView: view) + } + }) { (_) in + self.showBackupCheckFailedAlert(fromView: view) + } + } + + private func showBackupCheckFailedAlert(fromView view: UIViewController) { + AssertIsOnMainThread() + + Logger.info("") + + let alert = UIAlertController(title: NSLocalizedString("CHECK_FOR_BACKUP_FAILED_TITLE", + comment: "Title for alert shown when the app failed to check for an existing backup."), + message: NSLocalizedString("CHECK_FOR_BACKUP_FAILED_MESSAGE", + comment: "Message for alert shown when the app failed to check for an existing backup."), + preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("REGISTER_FAILED_TRY_AGAIN", comment: ""), + style: .default) { (_) in + self.checkCanImportBackup(fromView: view) + }) + alert.addAction(UIAlertAction(title: NSLocalizedString("CHECK_FOR_BACKUP_DO_NOT_RESTORE", comment: "The label for the 'do not restore backup' button."), + style: .destructive) { (_) in + self.showProfileView(fromView: view) + }) + view.present(alert, animated: true) + } + + public func onboardingDidRequire2FAPin(viewController: UIViewController) { + AssertIsOnMainThread() + + Logger.info("") + + // TODO: +// let view = OnboardingCaptchaViewController(onboardingController: self) +// navigationController.pushViewController(view, animated: true) + } + // MARK: - State public private(set) var countryState: OnboardingCountryState = .defaultValue @@ -276,7 +375,6 @@ public class OnboardingController: NSObject { Logger.info("Captcha requested.") onboardingDidRequireCaptcha(viewController: viewController) - return } else if error.code == 400 { OWSAlerts.showAlert(title: NSLocalizedString("REGISTRATION_ERROR", comment: ""), message: NSLocalizedString("REGISTRATION_NON_VALID_NUMBER", comment: "")) @@ -286,4 +384,57 @@ public class OnboardingController: NSObject { message: error.localizedRecoverySuggestion) } } + + // MARK: - Verification + + public func tryToVerify(fromViewController: UIViewController, + verificationCode: String, + pin: String?) { + AssertIsOnMainThread() + + guard let phoneNumber = phoneNumber else { + owsFailDebug("Missing phoneNumber.") + return + } + + // Ensure the account manager state is up-to-date. + // + // TODO: We could skip this in production. + tsAccountManager.phoneNumberAwaitingVerification = phoneNumber.e164 + + ModalActivityIndicatorViewController.present(fromViewController: fromViewController, + canCancel: true) { (modal) in + + self.accountManager.register(verificationCode: verificationCode, pin: pin) + .done { (_) in + DispatchQueue.main.async { + modal.dismiss(completion: { + self.verificationDidComplete(fromView: fromViewController) + }) + } + }.catch({ (error) in + Logger.error("Error: \(error)") + + DispatchQueue.main.async { + modal.dismiss(completion: { + self.verificationFailed(fromViewController: fromViewController, error: error as NSError) + }) + } + }).retainUntilComplete() + } + } + + private func verificationFailed(fromViewController: UIViewController, error: NSError) { + if error.domain == OWSSignalServiceKitErrorDomain && + error.code == OWSErrorCode.registrationMissing2FAPIN.rawValue { + + Logger.info("Missing 2FA PIN.") + + onboardingDidRequire2FAPin(viewController: fromViewController) + } else { + OWSAlerts.showAlert(title: NSLocalizedString("REGISTRATION_VERIFICATION_FAILED_TITLE", comment: "Alert view title"), + message: error.localizedDescription, + fromViewController: fromViewController) + } + } } diff --git a/Signal/src/ViewControllers/Registration/OnboardingVerificationViewController.swift b/Signal/src/ViewControllers/Registration/OnboardingVerificationViewController.swift index 31c7f1b36..471e000f9 100644 --- a/Signal/src/ViewControllers/Registration/OnboardingVerificationViewController.swift +++ b/Signal/src/ViewControllers/Registration/OnboardingVerificationViewController.swift @@ -85,6 +85,9 @@ private class OnboardingCodeView: UIView { var isComplete: Bool { return digitText.count == digitCount } + var verificationCode: String { + return digitText + } private func createSubviews() { textfield.textAlignment = .left @@ -203,11 +206,15 @@ extension OnboardingCodeView: UITextFieldDelegate { updateViewState() + self.delegate?.codeViewDidChange() + // Inform our caller that we took care of performing the change. return false } public func textFieldShouldReturn(_ textField: UITextField) -> Bool { + self.delegate?.codeViewDidChange() + return false } } @@ -258,6 +265,8 @@ public class OnboardingVerificationViewController: OnboardingBaseViewController comment: "Label for the link that lets users change their phone number."), selector: #selector(backLinkTapped)) + onboardingCodeView.delegate = self + let codeStateLink = self.linkButton(title: "", selector: #selector(resendCodeLinkTapped)) codeStateLink.enableMultilineLabel() @@ -450,12 +459,23 @@ public class OnboardingVerificationViewController: OnboardingBaseViewController self.present(actionSheet, animated: true) } + + private func tryToVerify() { + Logger.info("") + + guard onboardingCodeView.isComplete else { + return + } + onboardingController.tryToVerify(fromViewController: self, verificationCode: onboardingCodeView.verificationCode, pin: nil) + } } // MARK: - extension OnboardingVerificationViewController: OnboardingCodeViewDelegate { public func codeViewDidChange() { - // TODO: + AssertIsOnMainThread() + + tryToVerify() } } diff --git a/SignalServiceKit/src/Account/TSAccountManager.h b/SignalServiceKit/src/Account/TSAccountManager.h index a2aff043e..1f0baf7b5 100644 --- a/SignalServiceKit/src/Account/TSAccountManager.h +++ b/SignalServiceKit/src/Account/TSAccountManager.h @@ -32,6 +32,8 @@ typedef NS_ENUM(NSUInteger, OWSRegistrationState) { @property (nonatomic, nullable) NSString *phoneNumberAwaitingVerification; #endif +- (void)setPhoneNumberAwaitingVerification:(NSString *_Nullable)phoneNumberAwaitingVerification; + #pragma mark - Initializers - (instancetype)init NS_UNAVAILABLE;