diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerPresenter.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerPresenter.swift index 0280ce6b198..f2188502bd9 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerPresenter.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerPresenter.swift @@ -41,18 +41,22 @@ final class PrivacyBannerPresenter { let privacyBanner = PrivacyBannerViewController(onCompletion: { [weak self] result in switch result { case .success(let destination): - switch destination { - case .dismiss: - viewController.dismiss(animated: true) - - case .settings: - print("Dismiss and Go to settings") + viewController.dismiss(animated: true) + if destination == .settings { + MainTabBarController.navigateToPrivacySettings() } + case .failure(let error): switch error { - case .sync(let analyticsOptOut): + case .sync(let analyticsOptOut, let intendedDestination): viewController.dismiss(animated: true) self?.showErrorNotice(optOut: analyticsOptOut) + + /// Even if we fail, we should redirect the user to settings screen so they can further customize their privacy settings + /// + if intendedDestination == .settings { + MainTabBarController.navigateToPrivacySettings() + } } } }) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerViewController.swift index a4a2ba02816..f5aba2f859b 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerViewController.swift @@ -86,7 +86,9 @@ struct PrivacyBanner: View { HStack { Button(Localization.goToSettings) { - print("Tapped Settings") + Task { + await viewModel.submitChanges(destination: .settings) + } } .buttonStyle(SecondaryButtonStyle()) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerViewModel.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerViewModel.swift index 43256e119c0..bdebaaf064c 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/PrivacyBannerViewModel.swift @@ -51,7 +51,7 @@ final class PrivacyBannerViewModel: ObservableObject { try await useCase.update(optOut: !analyticsEnabled) onCompletion(.success(destination)) } catch { - onCompletion(.failure(.sync(analyticsOptOut: !analyticsEnabled))) + onCompletion(.failure(.sync(analyticsOptOut: !analyticsEnabled, intendedDestination: destination))) } // Revert Loading state @@ -72,6 +72,6 @@ extension PrivacyBannerViewModel { /// Defined errors. /// enum Error: Swift.Error { - case sync(analyticsOptOut: Bool) + case sync(analyticsOptOut: Bool, intendedDestination: Destination) } } diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/UpdateAnalyticsSettingsUseCase.swift b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/UpdateAnalyticsSettingsUseCase.swift index 902ea7869b8..a6bd894d6ec 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/UpdateAnalyticsSettingsUseCase.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/Settings/Privacy/UpdateAnalyticsSettingsUseCase.swift @@ -27,7 +27,12 @@ final class UpdateAnalyticsSettingUseCase { /// For WPCOM stores: Updates remotely and locally. - The local update only happens after a successful remote update. /// For NON-WPCOM stores: Updates locally. /// - func update(optOut: Bool) async throws { + @MainActor func update(optOut: Bool) async throws { + // There is no need to perform any request if the user hasn't changed the current analytic setting. + guard analytics.userHasOptedIn == optOut else { + return updateLocally(optOut: optOut) + } + // If we can't find an account(non-jp sites), lets commit the change immediately. guard let defaultAccount = stores.sessionManager.defaultAccount else { return updateLocally(optOut: optOut) @@ -54,7 +59,7 @@ final class UpdateAnalyticsSettingUseCase { /// Updates the local analytics setting /// - private func updateLocally(optOut: Bool) { + @MainActor private func updateLocally(optOut: Bool) { analytics.setUserHasOptedOut(optOut) userDefaults[.hasSavedPrivacyBannerSettings] = true } diff --git a/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewController.swift b/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewController.swift index f3181631713..e29ec349e04 100644 --- a/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Hub Menu/HubMenuViewController.swift @@ -35,6 +35,20 @@ final class HubMenuViewController: UIHostingController { return inPersonPaymentsMenuViewController } + /// Pushes the Settings & Privacy screen onto the navigation stack. + /// + func showPrivacySettings() { + guard let navigationController else { + return DDLogError("⛔️ Could not find a navigation controller context.") + } + guard let privacy = UIStoryboard.dashboard.instantiateViewController(ofClass: PrivacySettingsViewController.self) else { + return DDLogError("⛔️ Could not instantiate PrivacySettingsViewController") + } + + let settings = SettingsViewController() + navigationController.setViewControllers(navigationController.viewControllers + [settings, privacy], animated: true) + } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) diff --git a/WooCommerce/Classes/ViewRelated/MainTabBarController.swift b/WooCommerce/Classes/ViewRelated/MainTabBarController.swift index 254e1692453..1bc62c5de1f 100644 --- a/WooCommerce/Classes/ViewRelated/MainTabBarController.swift +++ b/WooCommerce/Classes/ViewRelated/MainTabBarController.swift @@ -448,6 +448,17 @@ extension MainTabBarController { viewController?.openSimplePaymentsAmountFlow() } } + + /// Switches to the hub Menu & Navigates to the Privacy Settings Screen. + /// + static func navigateToPrivacySettings() { + switchToHubMenuTab { + guard let hubMenuViewController: HubMenuViewController = childViewController() else { + return DDLogError("⛔️ Could not switch to the Hub Menu") + } + hubMenuViewController.showPrivacySettings() + } + } } // MARK: - DeeplinkForwarder diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Settings/Privacy/PrivacyBannerViewModelTest.swift b/WooCommerce/WooCommerceTests/ViewRelated/Settings/Privacy/PrivacyBannerViewModelTest.swift index b0df19a737c..49df109cd8f 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Settings/Privacy/PrivacyBannerViewModelTest.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Settings/Privacy/PrivacyBannerViewModelTest.swift @@ -49,6 +49,7 @@ import TestKit } // When + viewModel.analyticsEnabled = false Task { await viewModel.submitChanges(destination: .dismiss) } diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Settings/Privacy/UpdateAnalyticsSettingsUseCaseTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Settings/Privacy/UpdateAnalyticsSettingsUseCaseTests.swift index 671f8791959..beece8a8c1f 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Settings/Privacy/UpdateAnalyticsSettingsUseCaseTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Settings/Privacy/UpdateAnalyticsSettingsUseCaseTests.swift @@ -82,6 +82,31 @@ final class UpdateAnalyticsSettingsUseCaseTests: XCTestCase { XCTAssertEqual(userDefaults[.hasSavedPrivacyBannerSettings], true) } + @MainActor func test_not_updating_analytic_setting_does_not_fire_a_request() async throws { + // Given + let stores = MockStoresManager(sessionManager: .makeForTesting(authenticated: true, isWPCom: true, displayName: "Test Account")) + stores.whenReceivingAction(ofType: AccountAction.self) { action in + switch action { + case .updateAccountSettings(_, _, let onCompletion): + onCompletion(.success(())) + XCTFail("Test should not fire a network request") + default: + break + } + } + let analytics = WaitingTimeTrackerTests.TestAnalytics() + let userDefaults = try XCTUnwrap(UserDefaults(suiteName: "TestingSuite")) + analytics.setUserHasOptedOut(true) + + // When + let useCase = UpdateAnalyticsSettingUseCase(stores: stores, analytics: analytics, userDefaults: userDefaults) + try await useCase.update(optOut: true) + + // Then + XCTAssertFalse(analytics.userHasOptedIn) + XCTAssertEqual(userDefaults[.hasSavedPrivacyBannerSettings], true) + } + override class func tearDown() { super.tearDown() SessionManager.removeTestingDatabase()