Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
case .ciabBookings:
return buildConfig == .localDeveloper || buildConfig == .alpha
case .pointOfSaleSurveys:
return buildConfig == .localDeveloper || buildConfig == .alpha
return true
case .pointOfSaleCatalogAPI:
return false
default:
Expand Down
1 change: 1 addition & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [**] We added support for collecting in-person payments (including Tap To Pay) using Stripe Payment Gateway extension in the UK. [https://github.com/woocommerce/woocommerce-ios/pull/16287]
- [*] Improve card payments onboarding error handling to show network errors correctly [https://github.com/woocommerce/woocommerce-ios/pull/16304]
- [*] Authenticate the admin page automatically for sites with SSO enabled in custom fields, in-person payment setup, and editing tax rates flows. [https://github.com/woocommerce/woocommerce-ios/pull/16318]
- [*] Show POS feedback surveys for eligible merchants [https://github.com/woocommerce/woocommerce-ios/pull/16325]

23.6
-----
Expand Down
22 changes: 18 additions & 4 deletions WooCommerce/Classes/POS/POSNotificationScheduler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ import UserNotifications
import Yosemite
import Experiments

// periphery: ignore - work in progress
protocol POSNotificationScheduling {
func scheduleLocalNotificationIfEligible(for merchantType: POSNotificationScheduler.MerchantType) async
}

// periphery: ignore - work in progress
final class POSNotificationScheduler: POSNotificationScheduling {
enum MerchantType {
case potentialMerchant
Expand Down Expand Up @@ -64,7 +62,7 @@ final class POSNotificationScheduler: POSNotificationScheduling {
func scheduleLocalNotificationIfEligible(for merchantType: POSNotificationScheduler.MerchantType) async {
guard featureFlagService.isFeatureFlagEnabled(.pointOfSaleSurveys) else { return }

let isScheduled = await isNotificationScheduled(for: merchantType)
let isScheduled = await isNotificationAlreadyScheduled(for: merchantType)
guard !isScheduled else { return }
guard isCountryEligible() else { return }

Expand All @@ -86,7 +84,23 @@ final class POSNotificationScheduler: POSNotificationScheduling {
}
}

private func isNotificationScheduled(for merchantType: MerchantType) async -> Bool {
private func isNotificationAlreadyScheduled(for merchantType: MerchantType) async -> Bool {
// Check if the specific notification type is already scheduled
let isCurrentMerchantTypeScheduled = await checkIfScheduled(for: merchantType)
if isCurrentMerchantTypeScheduled {
return true
}

// Don't schedule notification for potential merchant if the user is already marked as current merchant
guard merchantType == .potentialMerchant else {
return false
}

let isCurrentMerchantScheduled = await checkIfScheduled(for: .currentMerchant)
return isCurrentMerchantScheduled
}

private func checkIfScheduled(for merchantType: MerchantType) async -> Bool {
await withCheckedContinuation { continuation in
let action: AppSettingsAction
switch merchantType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,9 @@ final class EditableOrderViewModel: ObservableObject {
guard let self else { return }
self.collectPayment(for: order)
self.trackCreateOrderSuccess(usesGiftCard: usesGiftCard)
Task {
await self.posNotificationScheduler.scheduleLocalNotificationIfEligible(for: .potentialMerchant)
}
} onFailure: { [weak self] error, usesGiftCard in
guard let self else { return }
self.fixedNotice = NoticeFactory.createOrderErrorNotice(error, order: self.orderSynchronizer.order)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,46 @@ struct POSNotificationSchedulerTests {
#expect(mockPushNotesManager.requestedLocalNotifications.isEmpty)
}

@Test func scheduleLocalNotificationIfEligible_when_currentMerchant_already_scheduled_then_potentialMerchant_cannot_be_scheduled() async throws {
// Given
let siteSettings = sampleSiteSettings(countryCode: "US")
mockFeatureFlagService.isFeatureFlagEnabledReturnValue[.pointOfSaleSurveys] = true
setupMockStores(isCurrentMerchantScheduled: true)

let scheduler = POSNotificationScheduler(
stores: mockStores,
siteSettings: siteSettings,
featureFlagService: mockFeatureFlagService,
pushNotificationsManager: mockPushNotesManager
)

// When
await scheduler.scheduleLocalNotificationIfEligible(for: .potentialMerchant)

// Then - No notification should be scheduled. Prevents backwards conversion from 'current' to 'potential' merchant.
#expect(mockPushNotesManager.requestedLocalNotifications.isEmpty)
}

@Test func scheduleLocalNotificationIfEligible_when_potentialMerchant_already_scheduled_then_does_not_duplicate_notification() async throws {
// Given
let siteSettings = sampleSiteSettings(countryCode: "US")
mockFeatureFlagService.isFeatureFlagEnabledReturnValue[.pointOfSaleSurveys] = true
setupMockStores(isPotentialMerchantScheduled: true)

let scheduler = POSNotificationScheduler(
stores: mockStores,
siteSettings: siteSettings,
featureFlagService: mockFeatureFlagService,
pushNotificationsManager: mockPushNotesManager
)

// When
await scheduler.scheduleLocalNotificationIfEligible(for: .potentialMerchant)

// Then - No duplicate notification should be scheduled
#expect(mockPushNotesManager.requestedLocalNotifications.isEmpty)
}

private func sampleSiteSettings(countryCode: String) -> [SiteSetting] {
[
SiteSetting.fake().copy(
Expand Down
Loading