diff --git a/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift b/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift index 3dea3aab94e..4d1b5c96cbe 100644 --- a/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift +++ b/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift @@ -5,7 +5,7 @@ import protocol Yosemite.PointOfSaleItemServiceProtocol import protocol Yosemite.PointOfSaleCouponServiceProtocol @available(iOS 17.0, *) -@Observable final class PointOfSaleCouponsController: PointOfSaleItemsControllerProtocol { +@Observable final class PointOfSaleCouponsController: PointOfSaleCouponsControllerProtocol { var itemsViewState: ItemsViewState = ItemsViewState(containerState: .loading, itemsStack: ItemsStackState(root: .loading([]), itemStates: [:])) @@ -39,6 +39,19 @@ import protocol Yosemite.PointOfSaleCouponServiceProtocol // Pagination https://github.com/woocommerce/woocommerce-ios/issues/15343 await loadFirstPage() } + + @MainActor + func enableCoupons() async { + // TODO: WOOMOB-255 + // Handle loading state while coupons are being enabled + do { + try await couponProvider.enableCoupons() + } catch { + // TODO: WOOMOB-267 + // Handle error when failed to enable, and allow retry action + debugPrint(error) + } + } } @available(iOS 17.0, *) diff --git a/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift b/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift index 65e53752559..e64dc0052fe 100644 --- a/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift +++ b/WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift @@ -26,6 +26,12 @@ protocol PointOfSaleItemsControllerProtocol { func loadNextItems(base: ItemListBaseItem) async } +@available(iOS 17.0, *) +protocol PointOfSaleCouponsControllerProtocol: PointOfSaleItemsControllerProtocol { + /// Enables coupons in store settings, if needed + func enableCoupons() async +} + @available(iOS 17.0, *) protocol PointOfSaleSearchingItemsControllerProtocol: PointOfSaleItemsControllerProtocol { /// Searches for items diff --git a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift index 80952c11391..6ef9b1ba0cf 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift @@ -25,6 +25,7 @@ protocol PointOfSaleAggregateModelProtocol { func trackCardPaymentsOnboardingShown() var itemsViewState: ItemsViewState { get } + func loadItems(base: ItemListBaseItem) async func loadNextItems(base: ItemListBaseItem) async @@ -56,6 +57,7 @@ protocol PointOfSaleAggregateModelProtocol { var itemsViewState: ItemsViewState { itemsController.itemsViewState } var couponsViewState: ItemsViewState { couponsController.itemsViewState } + var currentViewState: ItemsViewState private(set) var cart: Cart = .init() @@ -63,7 +65,7 @@ protocol PointOfSaleAggregateModelProtocol { private var internalOrderState: PointOfSaleInternalOrderState { orderController.orderState } private let itemsController: PointOfSaleItemsControllerProtocol - private let couponsController: PointOfSaleItemsControllerProtocol + private let couponsController: PointOfSaleCouponsControllerProtocol private let cardPresentPaymentService: CardPresentPaymentFacade private let orderController: PointOfSaleOrderControllerProtocol @@ -76,7 +78,7 @@ protocol PointOfSaleAggregateModelProtocol { private var cancellables: Set = [] init(itemsController: PointOfSaleItemsControllerProtocol, - couponsController: PointOfSaleItemsControllerProtocol, + couponsController: PointOfSaleCouponsControllerProtocol, cardPresentPaymentService: CardPresentPaymentFacade, orderController: PointOfSaleOrderControllerProtocol, analytics: Analytics = ServiceLocator.analytics, @@ -89,6 +91,9 @@ protocol PointOfSaleAggregateModelProtocol { self.analytics = analytics self.collectOrderPaymentAnalyticsTracker = collectOrderPaymentAnalyticsTracker self.paymentState = paymentState + // Initial, set to items (products) + self.currentViewState = itemsController.itemsViewState + publishCardReaderConnectionStatus() publishPaymentMessages() setupReaderReconnectionObservation() @@ -98,22 +103,33 @@ protocol PointOfSaleAggregateModelProtocol { // MARK: - ItemList @available(iOS 17.0, *) extension PointOfSaleAggregateModel { + func updateCurrentViewState(base: ItemListBaseItem) { + let viewState = base.itemType == .products ? itemsViewState : couponsViewState + currentViewState = viewState + } + @MainActor func loadItems(base: ItemListBaseItem) async { let controller = base.itemType == .products ? itemsController : couponsController + await controller.loadItems(base: base) + updateCurrentViewState(base: base) } @MainActor func refreshItems(base: ItemListBaseItem) async { let controller = base.itemType == .products ? itemsController : couponsController + await controller.refreshItems(base: base) + updateCurrentViewState(base: base) } @MainActor func loadNextItems(base: ItemListBaseItem) async { let controller = base.itemType == .products ? itemsController : couponsController + await controller.loadNextItems(base: base) + updateCurrentViewState(base: base) } } @@ -157,6 +173,14 @@ extension PointOfSaleAggregateModel { } } +// MARK: - Coupons +@available(iOS 17.0, *) +extension PointOfSaleAggregateModel { + func enableCoupons() async { + await couponsController.enableCoupons() + } +} + // MARK: - Track events @available(iOS 17.0, *) private extension PointOfSaleAggregateModel { diff --git a/WooCommerce/Classes/POS/Models/PointOfSaleErrorState.swift b/WooCommerce/Classes/POS/Models/PointOfSaleErrorState.swift index d7e1bdc1c4a..a6a4709c106 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSaleErrorState.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSaleErrorState.swift @@ -1,49 +1,108 @@ import Foundation struct PointOfSaleErrorState: Equatable { + enum ErrorType: Equatable { + case productsLoadError + case variationsLoadError + case productsNextPageError + case variationsNextPageError + case couponsNotFound + case couponsLoadError + case couponsDisabled + } + + let errorType: ErrorType let title: String let subtitle: String let buttonText: String static func errorOnLoadingProducts() -> Self { - PointOfSaleErrorState(title: Constants.failedToLoadProductsTitle, - subtitle: Constants.failedToLoadProductsSubtitle, - buttonText: Constants.failedToLoadProductsButtonTitle) + PointOfSaleErrorState( + errorType: .productsLoadError, + title: Constants.failedToLoadProductsTitle, + subtitle: Constants.failedToLoadProductsSubtitle, + buttonText: Constants.failedToLoadProductsButtonTitle) } static func errorOnLoadingVariations() -> Self { - PointOfSaleErrorState(title: Constants.failedToLoadVariationsTitle, - subtitle: Constants.failedToLoadVariationsSubtitle, - buttonText: Constants.failedToLoadVariationsButtonTitle) + PointOfSaleErrorState( + errorType: .variationsLoadError, + title: Constants.failedToLoadVariationsTitle, + subtitle: Constants.failedToLoadVariationsSubtitle, + buttonText: Constants.failedToLoadVariationsButtonTitle) } static func errorOnLoadingProductsNextPage() -> Self { - PointOfSaleErrorState(title: Constants.failedToLoadProductsNextPageTitle, - subtitle: Constants.failedToLoadProductsNextPageSubtitle, - buttonText: Constants.failedToLoadProductsNextPageButtonTitle) + PointOfSaleErrorState( + errorType: .productsNextPageError, + title: Constants.failedToLoadProductsNextPageTitle, + subtitle: Constants.failedToLoadProductsNextPageSubtitle, + buttonText: Constants.failedToLoadProductsNextPageButtonTitle) } static func errorOnLoadingVariationsNextPage() -> Self { - PointOfSaleErrorState(title: Constants.failedToLoadVariationsNextPageTitle, - subtitle: Constants.failedToLoadVariationsNextPageSubtitle, - buttonText: Constants.failedToLoadVariationsNextPageButtonTitle) + PointOfSaleErrorState( + errorType: .variationsNextPageError, + title: Constants.failedToLoadVariationsNextPageTitle, + subtitle: Constants.failedToLoadVariationsNextPageSubtitle, + buttonText: Constants.failedToLoadVariationsNextPageButtonTitle) } static func errorCouponsNotFound() -> Self { - PointOfSaleErrorState(title: Constants.noCouponsFoundTitle, - subtitle: Constants.noCouponsFoundSubtitle, - buttonText: Constants.noCouponsFoundButtonTitle) + PointOfSaleErrorState( + errorType: .couponsNotFound, + title: Constants.noCouponsFoundTitle, + subtitle: Constants.noCouponsFoundSubtitle, + buttonText: Constants.noCouponsFoundButtonTitle) } static func errorOnLoadingCoupons() -> Self { - PointOfSaleErrorState(title: "Error loading coupons", subtitle: "Error loading coupons", buttonText: "Retry") + PointOfSaleErrorState( + errorType: .couponsLoadError, + title: Constants.loadingCouponsErrorTitle, + subtitle: Constants.loadingCouponsErrorSubtitle, + buttonText: Constants.loadingCouponsErrorRetry) } static func errorCouponsDisabled() -> Self { - PointOfSaleErrorState(title: "Error loading coupons", subtitle: "Please enable coupons in WooCommerce Settings, and tap Retry", buttonText: "Retry") + PointOfSaleErrorState( + errorType: .couponsDisabled, + title: Constants.loadingCouponsDisabledTitle, + subtitle: Constants.loadingCouponsDisabledSubtitle, + buttonText: Constants.loadingCouponsDisabledAction) } enum Constants { + static let loadingCouponsErrorTitle = NSLocalizedString( + "pos.itemList.loadingCouponsErrorTitle", + value: "Error loading coupons", + comment: "Title appearing on the coupon list screen when there's an error loading coupons." + ) + static let loadingCouponsErrorSubtitle = NSLocalizedString( + "pos.itemList.loadingCouponsErrorSubtitle", + value: "Error loading coupons", + comment: "Subtitle appearing on the coupon list screen when there's an error loading coupons." + ) + static let loadingCouponsErrorRetry = NSLocalizedString( + "pos.itemList.loadingCouponsErrorRetry", + value: "Retry", + comment: "Text of the button appearing on the coupon list screen when there's an error loading coupons." + ) + static let loadingCouponsDisabledTitle = NSLocalizedString( + "pos.itemList.loadingCouponsDisabledTitle", + value: "Error loading coupons", + comment: "Title appearing on the coupon list screen when coupons are disabled." + ) + static let loadingCouponsDisabledSubtitle = NSLocalizedString( + "pos.itemList.loadingCouponsDisabledSubtitle", + value: "Please enable the use of coupon codes in your store.", + comment: "Subtitle appearing on the coupon list screen when coupons are disabled." + ) + static let loadingCouponsDisabledAction = NSLocalizedString( + "pos.itemList.loadingCouponsDisabledAction", + value: "Enable", + comment: "Text of the button appearing on the coupon list screen when coupons are disabled." + ) static let noCouponsFoundTitle = NSLocalizedString( "pos.itemList.noCouponsFoundTitle", value: "No coupons found", diff --git a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/CardReaderConnectionStatusView.swift b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/CardReaderConnectionStatusView.swift index 3995afdc807..c77e4aba577 100644 --- a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/CardReaderConnectionStatusView.swift +++ b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/CardReaderConnectionStatusView.swift @@ -171,7 +171,7 @@ private extension CardReaderConnectionStatusView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics() diff --git a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListErrorView.swift b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListErrorView.swift index b80c3121f76..f933a0019ee 100644 --- a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListErrorView.swift +++ b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListErrorView.swift @@ -3,11 +3,11 @@ import SwiftUI /// A view that displays an error message with a retry CTA when the list of POS items fails to load. struct PointOfSaleItemListErrorView: View { private let error: PointOfSaleErrorState - private let onRetry: (() -> Void)? + private let onAction: (() -> Void)? - init(error: PointOfSaleErrorState, onRetry: (() -> Void)? = nil) { + init(error: PointOfSaleErrorState, onAction: (() -> Void)? = nil) { self.error = error - self.onRetry = onRetry + self.onAction = onAction } var body: some View { @@ -33,7 +33,7 @@ struct PointOfSaleItemListErrorView: View { Spacer().frame(height: PointOfSaleCardPresentPaymentLayout.textAndButtonSpacing) Button(action: { - onRetry?() + onAction?() }, label: { Text(error.buttonText) }) @@ -47,5 +47,5 @@ struct PointOfSaleItemListErrorView: View { } #Preview { - PointOfSaleItemListErrorView(error: .errorOnLoadingProducts(), onRetry: nil) + PointOfSaleItemListErrorView(error: .errorOnLoadingProducts(), onAction: nil) } diff --git a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListFullscreenErrorView.swift b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListFullscreenErrorView.swift index b0841b26fce..03bed0e00f0 100644 --- a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListFullscreenErrorView.swift +++ b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListFullscreenErrorView.swift @@ -3,20 +3,20 @@ import SwiftUI /// A view that displays an error message with a retry CTA when the list of products fails to load. struct PointOfSaleItemListFullscreenErrorView: View { private let error: PointOfSaleErrorState - private let onRetry: (() -> Void)? + private let onAction: (() -> Void)? - init(error: PointOfSaleErrorState, onRetry: (() -> Void)? = nil) { + init(error: PointOfSaleErrorState, onAction: (() -> Void)? = nil) { self.error = error - self.onRetry = onRetry + self.onAction = onAction } var body: some View { PointOfSaleItemListFullscreenView { - PointOfSaleItemListErrorView(error: error, onRetry: onRetry) + PointOfSaleItemListErrorView(error: error, onAction: onAction) } } } #Preview { - PointOfSaleItemListFullscreenErrorView(error: .errorOnLoadingProducts(), onRetry: nil) + PointOfSaleItemListFullscreenErrorView(error: .errorOnLoadingProducts(), onAction: nil) } diff --git a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListFullscreenView.swift b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListFullscreenView.swift index 2fde1d52f32..6222f88d24e 100644 --- a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListFullscreenView.swift +++ b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListFullscreenView.swift @@ -31,6 +31,6 @@ private enum Localization { PointOfSaleItemListFullscreenView( content: { PointOfSaleItemListErrorView( - error: .init(title: "Error", subtitle: "Something went wrong", buttonText: "Fix it")) + error: .init(errorType: .productsLoadError, title: "Error", subtitle: "Something went wrong", buttonText: "Fix it")) }) } diff --git a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift index 4badbe5dd11..ee4220e2e85 100644 --- a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift +++ b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift @@ -125,7 +125,7 @@ private extension PointOfSalePaymentSuccessView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/CartView.swift b/WooCommerce/Classes/POS/Presentation/CartView.swift index 4e5d604993c..9b4060b412f 100644 --- a/WooCommerce/Classes/POS/Presentation/CartView.swift +++ b/WooCommerce/Classes/POS/Presentation/CartView.swift @@ -351,7 +351,7 @@ private extension CartView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -363,7 +363,7 @@ private extension CartView { #Preview("Cart with one item") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift b/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift index f2f3d0c0cc7..f8412350f78 100644 --- a/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift +++ b/WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift @@ -89,7 +89,7 @@ private extension ChildItemList { Spacer() } - PointOfSaleItemListErrorView(error: error, onRetry: { + PointOfSaleItemListErrorView(error: error, onAction: { Task { await posModel.loadItems(base: node) } @@ -152,7 +152,7 @@ private extension ChildItemList { ], hasMoreItems: false)]) let posModel = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -177,7 +177,7 @@ private extension ChildItemList { ]) let posModel = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift b/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift index be989a78642..0ab7f2d53e9 100644 --- a/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift +++ b/WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift @@ -207,7 +207,7 @@ private extension ItemListRow { #Preview("Loading") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/ItemListView.swift b/WooCommerce/Classes/POS/Presentation/ItemListView.swift index ce9750609e1..f0a5ea6b3f6 100644 --- a/WooCommerce/Classes/POS/Presentation/ItemListView.swift +++ b/WooCommerce/Classes/POS/Presentation/ItemListView.swift @@ -84,7 +84,7 @@ struct ItemListView: View { listView(items) case .error(let errorState): if errorState == .errorCouponsNotFound() { - PointOfSaleItemListErrorView(error: .errorCouponsNotFound(), onRetry: { + PointOfSaleItemListErrorView(error: .errorCouponsNotFound(), onAction: { // TODO }) } else { @@ -285,7 +285,7 @@ private extension ItemListView { } let posModel = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -297,7 +297,7 @@ private extension ItemListView { #Preview("Loading") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift b/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift index aee962956d1..009eee10c52 100644 --- a/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift +++ b/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift @@ -139,7 +139,7 @@ private extension POSFloatingControlView { #Preview("Reader Disconnected") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -153,7 +153,7 @@ private extension POSFloatingControlView { let paymentService = CardPresentPaymentPreviewService() let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -167,7 +167,7 @@ private extension POSFloatingControlView { #Preview("Secondary/disabled Background") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/PaymentButtons.swift b/WooCommerce/Classes/POS/Presentation/PaymentButtons.swift index dd454101a62..4e6fabc7fa4 100644 --- a/WooCommerce/Classes/POS/Presentation/PaymentButtons.swift +++ b/WooCommerce/Classes/POS/Presentation/PaymentButtons.swift @@ -90,7 +90,7 @@ private extension PaymentsActionButtons { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift index d674bc9875c..6d6293bbc53 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift @@ -199,7 +199,7 @@ private extension PointOfSaleCollectCashView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift index 2aab966f597..e0bf21fea39 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift @@ -15,7 +15,7 @@ struct PointOfSaleDashboardView: View { @Bindable var posModel = posModel ZStack(alignment: .bottomLeading) { if case .regular = horizontalSizeClass { - switch posModel.itemsViewState.containerState { + switch posModel.currentViewState.containerState { case .loading: PointOfSaleLoadingView() .transition(.opacity) @@ -24,9 +24,12 @@ struct PointOfSaleDashboardView: View { PointOfSaleItemListFullscreenView { PointOfSaleItemListEmptyView(base: .root(.products)) } - case .error(let errorContents): - PointOfSaleItemListFullscreenErrorView(error: errorContents, onRetry: { + case .error(let error): + PointOfSaleItemListFullscreenErrorView(error: error, onAction: { Task { + if error.errorType == .couponsDisabled { + await posModel.enableCoupons() + } await posModel.loadItems(base: .root(.products)) } }) @@ -47,7 +50,7 @@ struct PointOfSaleDashboardView: View { .padding(.bottom, Constants.floatingControlBottomPadding) .trackSize(size: $floatingSize) .accessibilitySortPriority(1) - .renderedIf(posModel.itemsViewState.containerState != .loading) + .renderedIf(posModel.currentViewState.containerState != .loading) POSConnectivityView() } @@ -55,7 +58,7 @@ struct PointOfSaleDashboardView: View { CGSizeMake(floatingSize.width + Constants.floatingControlHorizontalOffset, floatingSize.height + Constants.floatingControlVerticalOffset)) .environment(\.posBackgroundAppearance, posModel.paymentState != .card(.processingPayment) ? .primary : .secondary) - .animation(.easeInOut, value: posModel.itemsViewState.containerState == .loading) + .animation(.easeInOut, value: posModel.currentViewState.containerState == .loading) .background(Color.posSurface) .navigationBarBackButtonHidden(true) .posModal(item: $posModel.cardPresentPaymentOnboardingViewModel, onDismiss: { @@ -191,7 +194,7 @@ private extension PointOfSaleDashboardView { #Preview("Container loading state") { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) @@ -207,7 +210,7 @@ private extension PointOfSaleDashboardView { let itemsController = PointOfSalePreviewItemsController() let posModel = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift index d7365c2677a..f56ba7cf65b 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift @@ -8,13 +8,13 @@ struct PointOfSaleEntryPointView: View { private let onPointOfSaleModeActiveStateChange: ((Bool) -> Void) private let itemsController: PointOfSaleItemsControllerProtocol - private let couponsController: PointOfSaleItemsControllerProtocol + private let couponsController: PointOfSaleCouponsControllerProtocol private let cardPresentPaymentService: CardPresentPaymentFacade private let orderController: PointOfSaleOrderControllerProtocol private let collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalyticsTracking init(itemsController: PointOfSaleItemsControllerProtocol, - couponsController: PointOfSaleItemsControllerProtocol, + couponsController: PointOfSaleCouponsControllerProtocol, onPointOfSaleModeActiveStateChange: @escaping ((Bool) -> Void), cardPresentPaymentService: CardPresentPaymentFacade, orderController: PointOfSaleOrderControllerProtocol, @@ -64,7 +64,7 @@ struct PointOfSaleEntryPointView: View { @available(iOS 17.0, *) #Preview { PointOfSaleEntryPointView(itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), onPointOfSaleModeActiveStateChange: { _ in }, cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), diff --git a/WooCommerce/Classes/POS/Presentation/Reusable Views/POSSendReceiptView.swift b/WooCommerce/Classes/POS/Presentation/Reusable Views/POSSendReceiptView.swift index 899d7c43cce..f3f926adced 100644 --- a/WooCommerce/Classes/POS/Presentation/Reusable Views/POSSendReceiptView.swift +++ b/WooCommerce/Classes/POS/Presentation/Reusable Views/POSSendReceiptView.swift @@ -160,7 +160,7 @@ private extension POSSendReceiptView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Presentation/TotalsView.swift b/WooCommerce/Classes/POS/Presentation/TotalsView.swift index 136493f2fc7..0d8515d8e70 100644 --- a/WooCommerce/Classes/POS/Presentation/TotalsView.swift +++ b/WooCommerce/Classes/POS/Presentation/TotalsView.swift @@ -457,7 +457,7 @@ private extension TotalsView { #Preview { let posModel = PointOfSaleAggregateModel( itemsController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewItemsController(), + couponsController: PointOfSalePreviewCouponsController(), cardPresentPaymentService: CardPresentPaymentPreviewService(), orderController: PointOfSalePreviewOrderController(), collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()) diff --git a/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift b/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift index 632229ab5bb..142b6e7d337 100644 --- a/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift +++ b/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift @@ -76,6 +76,17 @@ struct PointOfSalePreviewPurchasableItemFetchStrategy: PointOfSalePurchasableIte } } +@available(iOS 17.0, *) +final class PointOfSalePreviewCouponsController: PointOfSaleCouponsControllerProtocol { + @Published var itemsViewState: ItemsViewState = ItemsViewState(containerState: .loading, + itemsStack: ItemsStackState(root: .loading([]), + itemStates: [:])) + func enableCoupons() async { } + func loadItems(base: ItemListBaseItem) async { } + func refreshItems(base: ItemListBaseItem) async { } + func loadNextItems(base: ItemListBaseItem) async { } +} + @available(iOS 17.0, *) final class PointOfSalePreviewItemsController: PointOfSaleItemsControllerProtocol { @Published var itemsViewState: ItemsViewState = ItemsViewState(containerState: .loading, diff --git a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj index edb68b001fe..b115c408cab 100644 --- a/WooCommerce/WooCommerce.xcodeproj/project.pbxproj +++ b/WooCommerce/WooCommerce.xcodeproj/project.pbxproj @@ -1583,6 +1583,7 @@ 683DF5FF2C6AF46500A5CDC6 /* POSHeaderTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 683DF5FE2C6AF46500A5CDC6 /* POSHeaderTitleView.swift */; }; 684AB83A2870677F003DFDD1 /* CardReaderManualsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 684AB8392870677F003DFDD1 /* CardReaderManualsView.swift */; }; 684AB83C2873DF04003DFDD1 /* CardReaderManualsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 684AB83B2873DF04003DFDD1 /* CardReaderManualsViewModel.swift */; }; + 68503C362DA53E0A00C07909 /* MockPointOfSaleCouponsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 68503C352DA53E0800C07909 /* MockPointOfSaleCouponsController.swift */; }; 6850C5EE2B69E6580026A93B /* ReceiptViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6850C5ED2B69E6580026A93B /* ReceiptViewModel.swift */; }; 6850C5F12B69E74D0026A93B /* ReceiptViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6850C5F02B69E74D0026A93B /* ReceiptViewController.swift */; }; 6850C5F42B6A11CA0026A93B /* ReceiptViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6850C5F32B6A11CA0026A93B /* ReceiptViewModelTests.swift */; }; @@ -4770,6 +4771,7 @@ 683DF5FE2C6AF46500A5CDC6 /* POSHeaderTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSHeaderTitleView.swift; sourceTree = ""; }; 684AB8392870677F003DFDD1 /* CardReaderManualsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardReaderManualsView.swift; sourceTree = ""; }; 684AB83B2873DF04003DFDD1 /* CardReaderManualsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardReaderManualsViewModel.swift; sourceTree = ""; }; + 68503C352DA53E0800C07909 /* MockPointOfSaleCouponsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPointOfSaleCouponsController.swift; sourceTree = ""; }; 6850C5ED2B69E6580026A93B /* ReceiptViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptViewModel.swift; sourceTree = ""; }; 6850C5F02B69E74D0026A93B /* ReceiptViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptViewController.swift; sourceTree = ""; }; 6850C5F32B6A11CA0026A93B /* ReceiptViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReceiptViewModelTests.swift; sourceTree = ""; }; @@ -7800,6 +7802,7 @@ 02CD3BFC2C35D01600E575C4 /* Mocks */ = { isa = PBXGroup; children = ( + 68503C352DA53E0800C07909 /* MockPointOfSaleCouponsController.swift */, 02CD3BFD2C35D04C00E575C4 /* MockCardPresentPaymentService.swift */, 6801E4162D0FFF0100F9DF46 /* MockReceiptService.swift */, 207E71CA2C60F765008540FC /* MockPOSOrderService.swift */, @@ -17718,6 +17721,7 @@ CE29FEF62C009F5F007679C2 /* ShippingLineRowViewModelTests.swift in Sources */, 20CCBF212B0E15C0003102E6 /* WooPaymentsPayoutsCurrencyOverviewViewModelTests.swift in Sources */, 2602A64227BD89CE00B347F1 /* NewOrderInitialStatusResolverTests.swift in Sources */, + 68503C362DA53E0A00C07909 /* MockPointOfSaleCouponsController.swift in Sources */, DE6D84A52C3B8C9C0014FBFF /* GoogleAdsDashboardCardViewModelTests.swift in Sources */, 0235354E2999D17A00BF77D3 /* DomainSettingsViewModelTests.swift in Sources */, 4535EE80281BE4E0004212B4 /* CouponAmountInputFormatterTests.swift in Sources */, diff --git a/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleCouponsControllerTests.swift b/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleCouponsControllerTests.swift index 7850c259757..f7d3ab972f4 100644 --- a/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleCouponsControllerTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleCouponsControllerTests.swift @@ -27,6 +27,10 @@ final class MockPointOfSaleCouponService: PointOfSaleCouponServiceProtocol { let coupon3 = POSItem.coupon(POSCoupon(id: UUID(uuidString: ("DC55E3B9-9D83-4C07-82A7-4C300A50E84C")) ?? UUID(), code: "VALID3")) return [coupon1, coupon2, coupon3] } + + func enableCoupons() async throws { + // no-op + } } struct PointOfSaleCouponsControllerTests { diff --git a/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleItemsControllerTests.swift b/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleItemsControllerTests.swift index d29785475cb..b231e7b128e 100644 --- a/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleItemsControllerTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleItemsControllerTests.swift @@ -321,7 +321,8 @@ final class PointOfSaleItemsControllerTests { ) itemProvider.errorToThrow = MockError.requestFailed - let expectedError = PointOfSaleErrorState(title: "Error loading products", + let expectedError = PointOfSaleErrorState(errorType: .productsLoadError, + title: "Error loading products", subtitle: "Give it another go?", buttonText: "Retry") try #require(sut.itemsViewState.containerState == .loading) diff --git a/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift b/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift index 6a4c4729be1..7fccf331233 100644 --- a/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift +++ b/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift @@ -28,6 +28,7 @@ final class MockPointOfSaleAggregateModel: PointOfSaleAggregateModelProtocol { var orderState: WooCommerce.PointOfSaleOrderState var itemsViewState: ItemsViewState + var blockReturnToItemSelection: Bool = false init(cardReaderConnectionStatus: CardPresentPaymentReaderConnectionStatus = .disconnected, diff --git a/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleCouponsController.swift b/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleCouponsController.swift new file mode 100644 index 00000000000..60d43a66977 --- /dev/null +++ b/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleCouponsController.swift @@ -0,0 +1,16 @@ +@testable import WooCommerce + +@available(iOS 17.0, *) +final class MockPointOfSaleCouponsController: PointOfSaleCouponsControllerProtocol { + func enableCoupons() async { } + + var itemsViewState: ItemsViewState = .init(containerState: .empty, + itemsStack: .init(root: .loaded([], hasMoreItems: false), + itemStates: [:])) + + func loadItems(base: ItemListBaseItem) async { } + + func refreshItems(base: ItemListBaseItem) async { } + + func loadNextItems(base: ItemListBaseItem) async { } +} diff --git a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift index a3eb5a1142d..9196ec51682 100644 --- a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift @@ -13,7 +13,7 @@ struct PointOfSaleAggregateModelTests { @Test func inits_with_building_order_stage() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -26,7 +26,7 @@ struct PointOfSaleAggregateModelTests { @Test func startNewCart_removes_all_items_from_cart_and_moves_back_to_building() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -48,7 +48,7 @@ struct PointOfSaleAggregateModelTests { @Test func checkOut_moves_to_finalizing_order_stage() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -66,7 +66,7 @@ struct PointOfSaleAggregateModelTests { @Test func addMoreToCart_moves_to_building_order_stage() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -97,7 +97,7 @@ struct PointOfSaleAggregateModelTests { @Test func addItem_results_in_a_non_empty_cart() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -116,7 +116,7 @@ struct PointOfSaleAggregateModelTests { @Test func addItem_puts_new_items_first_in_the_cart() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -134,7 +134,7 @@ struct PointOfSaleAggregateModelTests { @Test func removeItem_after_adding_two_items_removes_item_correctly() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -159,7 +159,7 @@ struct PointOfSaleAggregateModelTests { @Test func removeAllItemsFromCart_removes_everything() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -188,7 +188,7 @@ struct PointOfSaleAggregateModelTests { func addToCart_tracks_analytics_event() async throws { // Given let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -218,7 +218,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -239,7 +239,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -263,7 +263,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -286,7 +286,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -309,7 +309,7 @@ struct PointOfSaleAggregateModelTests { // Given let orderController = MockPointOfSaleOrderController() let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -330,7 +330,7 @@ struct PointOfSaleAggregateModelTests { let expectedError = NSError(domain: "some error", code: -1) let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -353,7 +353,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -381,7 +381,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -397,7 +397,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), @@ -416,7 +416,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -438,7 +438,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(), @@ -457,7 +457,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -482,7 +482,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -502,7 +502,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -521,7 +521,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -542,7 +542,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -573,7 +573,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -596,7 +596,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -625,7 +625,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -657,7 +657,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -688,7 +688,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -709,7 +709,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -730,7 +730,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -762,7 +762,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -789,7 +789,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()), @@ -826,7 +826,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: analytics, @@ -856,7 +856,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: analytics, @@ -877,7 +877,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: analytics, @@ -896,7 +896,7 @@ struct PointOfSaleAggregateModelTests { let itemsController = MockPointOfSaleItemsController() let sut = PointOfSaleAggregateModel( itemsController: itemsController, - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, analytics: analytics, @@ -914,7 +914,7 @@ struct PointOfSaleAggregateModelTests { // Given let analyticsTracker = MockPOSCollectOrderPaymentAnalyticsTracker() let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: cardPresentPaymentService, orderController: orderController, collectOrderPaymentAnalyticsTracker: analyticsTracker) @@ -931,7 +931,7 @@ struct PointOfSaleAggregateModelTests { // Given let analyticsTracker = MockPOSCollectOrderPaymentAnalyticsTracker() let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: analytics, @@ -949,7 +949,7 @@ struct PointOfSaleAggregateModelTests { let mockAnalyticsProvider = MockAnalyticsProvider() let mockAnalytics = WooAnalytics(analyticsProvider: mockAnalyticsProvider) let sut = PointOfSaleAggregateModel(itemsController: MockPointOfSaleItemsController(), - couponsController: MockPointOfSaleItemsController(), + couponsController: MockPointOfSaleCouponsController(), cardPresentPaymentService: MockCardPresentPaymentService(), orderController: MockPointOfSaleOrderController(), analytics: mockAnalytics, diff --git a/Yosemite/Yosemite/PointOfSale/PointOfSaleCouponService.swift b/Yosemite/Yosemite/PointOfSale/PointOfSaleCouponService.swift index 78e72c979d1..537806d202f 100644 --- a/Yosemite/Yosemite/PointOfSale/PointOfSaleCouponService.swift +++ b/Yosemite/Yosemite/PointOfSale/PointOfSaleCouponService.swift @@ -12,6 +12,7 @@ public enum PointOfSaleCouponServiceError: Error { public protocol PointOfSaleCouponServiceProtocol { func providePointOfSaleCoupons(pageNumber: Int) async throws -> PagedItems + func enableCoupons() async throws } public final class PointOfSaleCouponService: PointOfSaleCouponServiceProtocol { @@ -68,6 +69,20 @@ public final class PointOfSaleCouponService: PointOfSaleCouponServiceProtocol { return .init(items: refreshedCoupons, hasMorePages: false) } } + + @MainActor + public func enableCoupons() async throws { + _ = await withCheckedContinuation { continuation in + settingsStoreMethods.enableCouponSetting(siteID: siteID) { result in + switch result { + case .success: + continuation.resume(returning: true) + case .failure: + continuation.resume(returning: false) + } + } + } + } } private extension PointOfSaleCouponService { @@ -119,10 +134,8 @@ private extension PointOfSaleCouponService { settingsStoreMethods.retrieveCouponSetting(siteID: siteID) { result in switch result { case let .success(isEnabled): - debugPrint("Coupons enabled? \(isEnabled)") continuation.resume(returning: isEnabled) - case let .failure(error): - debugPrint("Coupons settings error: \(error)") + case .failure: continuation.resume(returning: false) } }