diff --git a/Modules/Sources/Experiments/DefaultFeatureFlagService.swift b/Modules/Sources/Experiments/DefaultFeatureFlagService.swift index 35d93100b77..7e8a9168bb6 100644 --- a/Modules/Sources/Experiments/DefaultFeatureFlagService.swift +++ b/Modules/Sources/Experiments/DefaultFeatureFlagService.swift @@ -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: diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 608ae7dadc9..72ec7ed3083 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -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 ----- diff --git a/WooCommerce/Classes/POS/POSNotificationScheduler.swift b/WooCommerce/Classes/POS/POSNotificationScheduler.swift index 2557d3311a3..21e97d39bdb 100644 --- a/WooCommerce/Classes/POS/POSNotificationScheduler.swift +++ b/WooCommerce/Classes/POS/POSNotificationScheduler.swift @@ -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 @@ -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 } @@ -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 { diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Creation/EditableOrderViewModel.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Creation/EditableOrderViewModel.swift index c2c76f187bb..eaed4782e45 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Creation/EditableOrderViewModel.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Creation/EditableOrderViewModel.swift @@ -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) diff --git a/WooCommerce/WooCommerceTests/POS/POSNotificationSchedulerTests.swift b/WooCommerce/WooCommerceTests/POS/POSNotificationSchedulerTests.swift index 49dd2280407..e58126c5046 100644 --- a/WooCommerce/WooCommerceTests/POS/POSNotificationSchedulerTests.swift +++ b/WooCommerce/WooCommerceTests/POS/POSNotificationSchedulerTests.swift @@ -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(