Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,21 +116,11 @@ final class POSTabCoordinator {
}

func onTabSelected() {
schedulePOSSurveyNotificationIfNeeded()
presentPOSView(siteID: siteID)
}
}

private extension POSTabCoordinator {
func schedulePOSSurveyNotificationIfNeeded() {
Task { @MainActor in
await POSNotificationScheduler().scheduleLocalNotificationIfEligible(for: .currentMerchant)

let action = AppSettingsAction.setHasPOSBeenOpenedAtLeastOnce { _ in }
storesManager.dispatch(action)
}
}

func presentPOSView(siteID: Int64) {
Task { @MainActor [weak self] in
guard let self else { return }
Expand Down
13 changes: 13 additions & 0 deletions WooCommerce/Classes/ViewRelated/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ final class AppCoordinator {

// Configures authenticator first in case `WordPressAuthenticator` is used in other `AppDelegate` launch events.
configureAuthenticator()

schedulePOSSurveyNotificationIfNeeded()
}

func start() {
Expand Down Expand Up @@ -101,6 +103,17 @@ final class AppCoordinator {
}
}

private extension AppCoordinator {
func schedulePOSSurveyNotificationIfNeeded() {
Task { @MainActor in
await POSNotificationScheduler(stores: stores).scheduleLocalNotificationIfEligible(for: .currentMerchant)

let action = AppSettingsAction.setHasPOSBeenOpenedAtLeastOnce { _ in }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we set POSBeenOpenedAtLeastOnce flag when opening the app, and not when opening the POS?

With this change, we can get a notification by opening the app twice.

As I understand, the desired behavior is to: show the notification after opening the app if POS was opened at least once

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're so right, I should've paid more attention 🙇. I've updated the logic on 3571be9

  • The 1st time they open the app (or after reset) when we perform the check for .currentMerchant this won't be scheduled as won't pass the guard for hasOpenedPOSAtLeastOnce
  • The merchant opens POS, which dispatches AppSettingsAction.setHasPOSBeenOpenedAtLeastOnce
  • The 2nd time they open the app, passes the guard for hasOpenedPOSAtLeastOnce and schedules the local notification.
  • The 3rd time they open the app, it will not pass the guard for isNotificationScheduled, so it won't be re-scheduled.

stores.dispatch(action)
}
}
}

private extension AppCoordinator {
// Fetch latest site properties and update the default store if anything has changed:
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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) {
Expand All @@ -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)
Expand Down Expand Up @@ -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 }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
}
}
}
Loading