diff --git a/WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift b/WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift index ad5d6d401a0..fe44cc761e8 100644 --- a/WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift +++ b/WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift @@ -116,16 +116,14 @@ final class POSTabCoordinator { } func onTabSelected() { - schedulePOSSurveyNotificationIfNeeded() + setPOSHasBeenOpened() presentPOSView(siteID: siteID) } } private extension POSTabCoordinator { - func schedulePOSSurveyNotificationIfNeeded() { + func setPOSHasBeenOpened() { Task { @MainActor in - await POSNotificationScheduler().scheduleLocalNotificationIfEligible(for: .currentMerchant) - let action = AppSettingsAction.setHasPOSBeenOpenedAtLeastOnce { _ in } storesManager.dispatch(action) } diff --git a/WooCommerce/Classes/ViewRelated/AppCoordinator.swift b/WooCommerce/Classes/ViewRelated/AppCoordinator.swift index 2e134aa128f..d68eada9efb 100644 --- a/WooCommerce/Classes/ViewRelated/AppCoordinator.swift +++ b/WooCommerce/Classes/ViewRelated/AppCoordinator.swift @@ -65,6 +65,8 @@ final class AppCoordinator { // Configures authenticator first in case `WordPressAuthenticator` is used in other `AppDelegate` launch events. configureAuthenticator() + + schedulePOSSurveyNotificationIfNeeded() } func start() { @@ -101,6 +103,14 @@ final class AppCoordinator { } } +private extension AppCoordinator { + func schedulePOSSurveyNotificationIfNeeded() { + Task { @MainActor in + await POSNotificationScheduler(stores: stores).scheduleLocalNotificationIfEligible(for: .currentMerchant) + } + } +} + private extension AppCoordinator { // Fetch latest site properties and update the default store if anything has changed: // diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Creation/EditableOrderViewModel.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Creation/EditableOrderViewModel.swift index 8d14d6d2777..c2c76f187bb 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Creation/EditableOrderViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Creation/EditableOrderViewModel.swift @@ -19,6 +19,7 @@ final class EditableOrderViewModel: ObservableObject { private let currencyFormatter: CurrencyFormatter private let featureFlagService: FeatureFlagService private let permissionChecker: CaptureDevicePermissionChecker + private let posNotificationScheduler: POSNotificationScheduling @Published var syncRequired: Bool = false @@ -460,6 +461,7 @@ final class EditableOrderViewModel: ObservableObject { featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService, orderDurationRecorder: OrderDurationRecorderProtocol = OrderDurationRecorder.shared, permissionChecker: CaptureDevicePermissionChecker = AVCaptureDevicePermissionChecker(), + posNotificationScheduler: POSNotificationScheduling = POSNotificationScheduler(), initialItem: OrderBaseItem? = nil, initialCustomer: (id: Int64, billing: Address?, shipping: Address?)? = nil, quantityDebounceDuration: Double = Constants.quantityDebounceDuration) { @@ -473,6 +475,7 @@ final class EditableOrderViewModel: ObservableObject { self.featureFlagService = featureFlagService self.orderDurationRecorder = orderDurationRecorder self.permissionChecker = permissionChecker + self.posNotificationScheduler = posNotificationScheduler self.initialItem = initialItem self.initialCustomer = initialCustomer self.barcodeScannerItemFinder = BarcodeScannerItemFinder(stores: stores) @@ -1026,7 +1029,7 @@ final class EditableOrderViewModel: ObservableObject { self.onFinished(order) self.trackCreateOrderSuccess(usesGiftCard: usesGiftCard) Task { - await POSNotificationScheduler().scheduleLocalNotificationIfEligible(for: .potentialMerchant) + await self.posNotificationScheduler.scheduleLocalNotificationIfEligible(for: .potentialMerchant) } } onFailure: { [weak self] error, usesGiftCard in guard let self else { return } diff --git a/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Creation/EditableOrderViewModelTests.swift b/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Creation/EditableOrderViewModelTests.swift index a29f2fed390..601e71393c4 100644 --- a/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Creation/EditableOrderViewModelTests.swift +++ b/WooCommerce/WooCommerceTests/ViewRelated/Orders/Order Creation/EditableOrderViewModelTests.swift @@ -3362,6 +3362,56 @@ final class EditableOrderViewModelTests: XCTestCase { // Then XCTAssertTrue(viewModel.canBeDismissed) } + + func test_onCreateOrderTapped_schedules_potential_merchant_notification() { + // Given + let mockScheduler = MockPOSNotificationScheduler() + let viewModel = EditableOrderViewModel(siteID: sampleSiteID, + stores: stores, + posNotificationScheduler: mockScheduler) + + // When + stores.whenReceivingAction(ofType: OrderAction.self) { action in + switch action { + case let .createOrder(_, order, _, onCompletion): + onCompletion(.success(order)) + default: + XCTFail("Received unsupported action: \(action)") + } + } + viewModel.onCreateOrderTapped() + + // Then + waitUntil { + mockScheduler.scheduleCallCount > 0 + } + XCTAssertEqual(mockScheduler.scheduleCallCount, 1) + XCTAssertEqual(mockScheduler.lastMerchantType, .potentialMerchant) + } + + func test_onCreateOrderTapped_does_not_schedule_notification_on_failure() { + // Given + let mockScheduler = MockPOSNotificationScheduler() + let viewModel = EditableOrderViewModel(siteID: sampleSiteID, + stores: stores, + posNotificationScheduler: mockScheduler) + let error = NSError(domain: "Error", code: 0) + + // When + stores.whenReceivingAction(ofType: OrderAction.self) { action in + switch action { + case let .createOrder(_, _, _, onCompletion): + onCompletion(.failure(error)) + default: + XCTFail("Received unsupported action: \(action)") + } + } + viewModel.onCreateOrderTapped() + + // Then + XCTAssertEqual(mockScheduler.scheduleCallCount, 0) + XCTAssertNil(mockScheduler.lastMerchantType) + } } private extension EditableOrderViewModelTests { @@ -3458,3 +3508,16 @@ private extension EditableOrderViewModelTests { email: "") } } + +// MARK: - POS Notification Tests & MockPOSNotificationScheduler +private extension EditableOrderViewModelTests { + final class MockPOSNotificationScheduler: POSNotificationScheduling { + private(set) var scheduleCallCount = 0 + private(set) var lastMerchantType: POSNotificationScheduler.MerchantType? + + func scheduleLocalNotificationIfEligible(for merchantType: POSNotificationScheduler.MerchantType) async { + scheduleCallCount += 1 + lastMerchantType = merchantType + } + } +}