From fa0097e328499a95fdb6c78ff8250d2018aded0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Coye=20de=20Brune=CC=81lis?= Date: Wed, 11 Dec 2024 15:19:21 +0100 Subject: [PATCH] refactor(OnboardingViewController): Abstracted away the post login and register code to be able to use it from elsewhere. feat: UpsaleViewController actions are performing the auth actions like expected. --- kDrive/AppRouter.swift | 22 ++++ .../File List/FileListViewController.swift | 16 ++- ...eViewController+FooterButtonDelegate.swift | 2 +- .../UI/Controller/LoginDelegateHandler.swift | 117 ++++++++++++++++++ .../Controller/OnboardingViewController.swift | 102 ++++----------- kDriveCore/Utils/AppNavigable.swift | 7 ++ 6 files changed, 183 insertions(+), 83 deletions(-) create mode 100644 kDrive/UI/Controller/LoginDelegateHandler.swift diff --git a/kDrive/AppRouter.swift b/kDrive/AppRouter.swift index 483edd31e..bf96e98bb 100644 --- a/kDrive/AppRouter.swift +++ b/kDrive/AppRouter.swift @@ -19,6 +19,7 @@ import InfomaniakCore import InfomaniakCoreUIKit import InfomaniakDI +import InfomaniakLogin import kDriveCore import kDriveResources import SafariServices @@ -33,6 +34,7 @@ public struct AppRouter: AppNavigable { @LazyInjectService private var reviewManager: ReviewManageable @LazyInjectService private var availableOfflineManager: AvailableOfflineManageable @LazyInjectService private var accountManager: AccountManageable + @LazyInjectService private var infomaniakLogin: InfomaniakLoginable @LazyInjectService var backgroundDownloadSessionManager: BackgroundDownloadSessionManager @LazyInjectService var backgroundUploadSessionManager: BackgroundUploadSessionManager @@ -444,6 +446,26 @@ public struct AppRouter: AppNavigable { viewController.present(vc, animated: true) } + @MainActor public func showRegister(delegate: InfomaniakLoginDelegate) { + guard let topMostViewController else { + return + } + + MatomoUtils.track(eventWithCategory: .account, name: "openCreationWebview") + let registerViewController = RegisterViewController.instantiateInNavigationController(delegate: delegate) + topMostViewController.present(registerViewController, animated: true) + } + + @MainActor public func showLogin(delegate: InfomaniakLoginDelegate) { + guard let topMostViewController else { + return + } + + infomaniakLogin.webviewLoginFrom(viewController: topMostViewController, + hideCreateAccountButton: true, + delegate: delegate) + } + // MARK: AppExtensionRouter public func showStore(from viewController: UIViewController, driveFileManager: DriveFileManager) { diff --git a/kDrive/UI/Controller/Files/File List/FileListViewController.swift b/kDrive/UI/Controller/Files/File List/FileListViewController.swift index 9c01ca9f2..f03fe24a1 100644 --- a/kDrive/UI/Controller/Files/File List/FileListViewController.swift +++ b/kDrive/UI/Controller/Files/File List/FileListViewController.swift @@ -51,6 +51,7 @@ extension SortType: Selectable { class FileListViewController: UICollectionViewController, SwipeActionCollectionViewDelegate, SwipeActionCollectionViewDataSource, FilesHeaderViewDelegate, SceneStateRestorable { @LazyInjectService var accountManager: AccountManageable + @LazyInjectService var router: AppNavigable // MARK: - Constants @@ -301,15 +302,20 @@ class FileListViewController: UICollectionViewController, SwipeActionCollectionV // Create an account upsaleViewController.freeTrialCallback = { [weak self] in - self?.dismiss(animated: true) - // TODO: Present login -// MatomoUtils.track(eventWithCategory: .account, name: "openCreationWebview") -// present(RegisterViewController.instantiateInNavigationController(delegate: self), animated: true) + guard let self else { return } + self.dismiss(animated: true) { + let loginDelegateHandler = LoginDelegateHandler() + self.router.showRegister(delegate: loginDelegateHandler) + } } // Let the user login with the onboarding upsaleViewController.loginCallback = { [weak self] in - self?.dismiss(animated: true) + guard let self else { return } + self.dismiss(animated: true) { + let loginDelegateHandler = LoginDelegateHandler() + self.router.showLogin(delegate: loginDelegateHandler) + } } let floatingPanel = UpsaleFloatingPanelController(upsaleViewController: upsaleViewController) diff --git a/kDrive/UI/Controller/Files/Save File/SaveFileViewController+FooterButtonDelegate.swift b/kDrive/UI/Controller/Files/Save File/SaveFileViewController+FooterButtonDelegate.swift index 944b44daa..8a6942358 100644 --- a/kDrive/UI/Controller/Files/Save File/SaveFileViewController+FooterButtonDelegate.swift +++ b/kDrive/UI/Controller/Files/Save File/SaveFileViewController+FooterButtonDelegate.swift @@ -58,7 +58,7 @@ extension SaveFileViewController: FooterButtonDelegate { private func savePublicShareToDrive(sourceDriveId: Int, destinationDriveId: Int, fileIds: [Int], - exceptIds: [Int]) async { + exceptIds: [Int]) async throws { defer { dismiss(animated: true) } diff --git a/kDrive/UI/Controller/LoginDelegateHandler.swift b/kDrive/UI/Controller/LoginDelegateHandler.swift new file mode 100644 index 000000000..992c4f12b --- /dev/null +++ b/kDrive/UI/Controller/LoginDelegateHandler.swift @@ -0,0 +1,117 @@ +/* + Infomaniak kDrive - iOS App + Copyright (C) 2024 Infomaniak Network SA + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +import CocoaLumberjackSwift +import InfomaniakCore +import InfomaniakDI +import InfomaniakLogin +import kDriveCore +import kDriveResources +import Lottie +import UIKit + +/// Something to abstract away from the view the post login/register logic +public class LoginDelegateHandler: InfomaniakLoginDelegate { + @LazyInjectService var accountManager: AccountManageable + @LazyInjectService var router: AppNavigable + + var didStartLoginCallback: (() -> Void)? + var didCompleteLoginCallback: (() -> Void)? + var didFailLoginWithErrorCallback: ((Error) -> Void)? + + public init(didCompleteLoginCallback: (() -> Void)? = nil) { + self.didCompleteLoginCallback = didCompleteLoginCallback + } + + @MainActor public func didCompleteLoginWith(code: String, verifier: String) { + guard let topMostViewController = router.topMostViewController else { return } + + MatomoUtils.track(eventWithCategory: .account, name: "loggedIn") + let previousAccount = accountManager.currentAccount + + didStartLoginCallback?() + + Task { + do { + _ = try await accountManager.createAndSetCurrentAccount(code: code, codeVerifier: verifier) + guard let currentDriveFileManager = accountManager.currentDriveFileManager else { + throw DriveError.NoDriveError.noDriveFileManager + } + + MatomoUtils.connectUser() + goToMainScreen(with: currentDriveFileManager) + } catch { + didCompleteLoginWithError(error, previousAccount: previousAccount, topMostViewController: topMostViewController) + } + + didCompleteLoginCallback?() + } + } + + @MainActor private func goToMainScreen(with driveFileManager: DriveFileManager) { + UserDefaults.shared.legacyIsFirstLaunch = false + UserDefaults.shared.numberOfConnections = 1 + _ = router.showMainViewController(driveFileManager: driveFileManager, selectedIndex: nil) + } + + private func didCompleteLoginWithError(_ error: Error, + previousAccount: Account?, + topMostViewController: UIViewController) { + DDLogError("Error on didCompleteLoginWith \(error)") + + if let previousAccount { + accountManager.switchAccount(newAccount: previousAccount) + } + + if let noDriveError = error as? InfomaniakCore.ApiError, noDriveError.code == DriveError.noDrive.code { + let driveErrorVC = DriveErrorViewController.instantiate(errorType: .noDrive, drive: nil) + topMostViewController.present(driveErrorVC, animated: true) + } else if let driveError = error as? DriveError, + driveError == .noDrive + || driveError == .productMaintenance + || driveError == .driveMaintenance + || driveError == .blocked { + let errorViewType: DriveErrorViewController.DriveErrorViewType + switch driveError { + case .productMaintenance, .driveMaintenance: + errorViewType = .maintenance + case .blocked: + errorViewType = .blocked + default: + errorViewType = .noDrive + } + + let driveErrorVC = DriveErrorViewController.instantiate(errorType: errorViewType, drive: nil) + topMostViewController.present(driveErrorVC, animated: true) + } else { + let metadata = [ + "Underlying Error": error.asAFError?.underlyingError.debugDescription ?? "Not an AFError" + ] + SentryDebug.capture(error: error, context: metadata, contextKey: "Error") + + topMostViewController.okAlert( + title: KDriveResourcesStrings.Localizable.errorTitle, + message: KDriveResourcesStrings.Localizable.errorConnection + ) + } + } + + public func didFailLoginWith(error: Error) { + didFailLoginWithErrorCallback?(error) + } +} diff --git a/kDrive/UI/Controller/OnboardingViewController.swift b/kDrive/UI/Controller/OnboardingViewController.swift index 0bda0794d..42c848245 100644 --- a/kDrive/UI/Controller/OnboardingViewController.swift +++ b/kDrive/UI/Controller/OnboardingViewController.swift @@ -26,6 +26,8 @@ import Lottie import UIKit class OnboardingViewController: UIViewController { + @LazyInjectService var router: AppNavigable + @IBOutlet var navigationBar: UINavigationBar! @IBOutlet var collectionView: UICollectionView! @IBOutlet var pageControl: UIPageControl! @@ -47,6 +49,27 @@ class OnboardingViewController: UIViewController { var addUser = false var slides: [Slide] = [] + private lazy var loginDelegateHandler: LoginDelegateHandler = { + let loginDelegateHandler = LoginDelegateHandler() + loginDelegateHandler.didStartLoginCallback = { [weak self] in + guard let self else { return } + signInButton.setLoading(true) + registerButton.isEnabled = false + } + loginDelegateHandler.didCompleteLoginCallback = { [weak self] in + guard let self else { return } + self.signInButton.setLoading(false) + self.registerButton.isEnabled = true + self.endBackgroundTask() + } + loginDelegateHandler.didFailLoginWithErrorCallback = { [weak self] _ in + guard let self else { return } + self.signInButton.setLoading(false) + self.registerButton.isEnabled = true + } + return loginDelegateHandler + }() + private var backgroundTaskIdentifier: UIBackgroundTaskIdentifier = .invalid override func viewDidLoad() { @@ -130,26 +153,17 @@ class OnboardingViewController: UIViewController { SentryDebug.capture(message: "Background task expired while logging in") self?.endBackgroundTask() } - infomaniakLogin.webviewLoginFrom(viewController: self, - hideCreateAccountButton: true, - delegate: self) + router.showLogin(delegate: loginDelegateHandler) } @IBAction func registerButtonPressed(_ sender: Any) { - MatomoUtils.track(eventWithCategory: .account, name: "openCreationWebview") - present(RegisterViewController.instantiateInNavigationController(delegate: self), animated: true) + router.showRegister(delegate: loginDelegateHandler) } @IBAction func closeButtonPressed(_ sender: Any) { dismiss(animated: true) } - private func goToMainScreen(with driveFileManager: DriveFileManager) { - UserDefaults.shared.legacyIsFirstLaunch = false - UserDefaults.shared.numberOfConnections = 1 - appNavigable.showMainViewController(driveFileManager: driveFileManager, selectedIndex: nil) - } - private func updateButtonsState() { if pageControl.currentPage == slides.count - 1 { if buttonContentView.isHidden { @@ -260,69 +274,3 @@ extension OnboardingViewController: UICollectionViewDelegate, UICollectionViewDa updateButtonsState() } } - -// MARK: - Infomaniak Login Delegate - -extension OnboardingViewController: InfomaniakLoginDelegate { - func didCompleteLoginWith(code: String, verifier: String) { - MatomoUtils.track(eventWithCategory: .account, name: "loggedIn") - let previousAccount = accountManager.currentAccount - signInButton.setLoading(true) - registerButton.isEnabled = false - Task { - do { - _ = try await accountManager.createAndSetCurrentAccount(code: code, codeVerifier: verifier) - guard let currentDriveFileManager = accountManager.currentDriveFileManager else { - throw DriveError.NoDriveError.noDriveFileManager - } - signInButton.setLoading(false) - registerButton.isEnabled = true - MatomoUtils.connectUser() - goToMainScreen(with: currentDriveFileManager) - } catch { - DDLogError("Error on didCompleteLoginWith \(error)") - - if let previousAccount { - accountManager.switchAccount(newAccount: previousAccount) - } - signInButton.setLoading(false) - registerButton.isEnabled = true - if let noDriveError = error as? InfomaniakCore.ApiError, noDriveError.code == DriveError.noDrive.code { - let driveErrorVC = DriveErrorViewController.instantiate(errorType: .noDrive, drive: nil) - present(driveErrorVC, animated: true) - } else if let driveError = error as? DriveError, - driveError == .noDrive - || driveError == .productMaintenance - || driveError == .driveMaintenance - || driveError == .blocked { - let errorViewType: DriveErrorViewController.DriveErrorViewType - switch driveError { - case .productMaintenance, .driveMaintenance: - errorViewType = .maintenance - case .blocked: - errorViewType = .blocked - default: - errorViewType = .noDrive - } - let driveErrorVC = DriveErrorViewController.instantiate(errorType: errorViewType, drive: nil) - present(driveErrorVC, animated: true) - } else { - let metadata = [ - "Underlying Error": error.asAFError?.underlyingError.debugDescription ?? "Not an AFError" - ] - SentryDebug.capture(error: error, context: metadata, contextKey: "Error") - okAlert( - title: KDriveResourcesStrings.Localizable.errorTitle, - message: KDriveResourcesStrings.Localizable.errorConnection - ) - } - } - endBackgroundTask() - } - } - - func didFailLoginWith(error: Error) { - signInButton.setLoading(false) - registerButton.isEnabled = true - } -} diff --git a/kDriveCore/Utils/AppNavigable.swift b/kDriveCore/Utils/AppNavigable.swift index c55019227..a0e029a52 100644 --- a/kDriveCore/Utils/AppNavigable.swift +++ b/kDriveCore/Utils/AppNavigable.swift @@ -18,6 +18,7 @@ import Foundation import InfomaniakCore +import InfomaniakLogin import UIKit /// Something that can navigate to specific places of the kDrive app @@ -45,6 +46,12 @@ public protocol RouterAppNavigable { driveFileManager: DriveFileManager, files: [ImportedFile] ) + + /// Present login webView on top of the topMostViewController + @MainActor func showLogin(delegate: InfomaniakLoginDelegate) + + /// Present register webView on top of the topMostViewController + @MainActor func showRegister(delegate: InfomaniakLoginDelegate) } /// Routing methods available from both the AppExtension mode and App