diff --git a/Modules/Sources/Yosemite/Model/Model.swift b/Modules/Sources/Yosemite/Model/Model.swift index 621d92ab926..fa952d3a74e 100644 --- a/Modules/Sources/Yosemite/Model/Model.swift +++ b/Modules/Sources/Yosemite/Model/Model.swift @@ -31,6 +31,7 @@ public typealias FallibleCancelable = Hardware.FallibleCancelable public typealias CommentStatus = Networking.CommentStatus public typealias CompositeComponentOptionType = Networking.CompositeComponentOptionType public typealias Coupon = Networking.Coupon +public typealias CouponDiscountType = Networking.Coupon.DiscountType public typealias CouponReport = Networking.CouponReport public typealias Country = Networking.Country public typealias CreateAccountResult = Networking.CreateAccountResult diff --git a/Modules/Sources/Yosemite/PointOfSale/Coupons/PointOfSaleCouponFetchStrategy.swift b/Modules/Sources/Yosemite/PointOfSale/Coupons/PointOfSaleCouponFetchStrategy.swift index d1529830dbe..138b9b68702 100644 --- a/Modules/Sources/Yosemite/PointOfSale/Coupons/PointOfSaleCouponFetchStrategy.swift +++ b/Modules/Sources/Yosemite/PointOfSale/Coupons/PointOfSaleCouponFetchStrategy.swift @@ -8,7 +8,7 @@ public protocol PointOfSaleCouponFetchStrategy { func fetchLocalCoupons() async throws -> [POSItem] } -public struct PointOfSaleDefaultCouponFetchStrategy: PointOfSaleCouponFetchStrategy { +struct PointOfSaleDefaultCouponFetchStrategy: PointOfSaleCouponFetchStrategy { private let siteID: Int64 private let currencySettings: CurrencySettings private let storage: StorageManagerType @@ -24,7 +24,7 @@ public struct PointOfSaleDefaultCouponFetchStrategy: PointOfSaleCouponFetchStrat self.couponStoreMethods = couponStoreMethods } - public func fetchCoupons(pageNumber: Int) async throws -> PagedItems { + func fetchCoupons(pageNumber: Int) async throws -> PagedItems { // Update local storage with data from the remote let hasMorePages = try await syncCouponsFromRemote(pageNumber: pageNumber) // Return all local coupons, including updated ones from the remote @@ -35,7 +35,7 @@ public struct PointOfSaleDefaultCouponFetchStrategy: PointOfSaleCouponFetchStrat /// fetchLocalCoupons provides an array of coupons that are stored locally /// It does not accept any sort of pagination /// Limited to default page size to match remote results - public func fetchLocalCoupons() async throws -> [POSItem] { + func fetchLocalCoupons() async throws -> [POSItem] { return await fetchLocalCoupons(limit: Constants.defaultPageSize) } } @@ -66,7 +66,7 @@ extension PointOfSaleDefaultCouponFetchStrategy { // MARK: - Search Coupon Strategy -public struct PointOfSaleSearchCouponFetchStrategy: PointOfSaleCouponFetchStrategy { +struct PointOfSaleSearchCouponFetchStrategy: PointOfSaleCouponFetchStrategy { private let siteID: Int64 private let couponStoreMethods: CouponStoreMethodsProtocol private let storage: StorageManagerType @@ -89,7 +89,7 @@ public struct PointOfSaleSearchCouponFetchStrategy: PointOfSaleCouponFetchStrate self.analytics = analytics } - public func fetchCoupons(pageNumber: Int) async throws -> PagedItems { + func fetchCoupons(pageNumber: Int) async throws -> PagedItems { let startTime = Date() try await couponStoreMethods.searchCoupons(siteID: siteID, keyword: searchTerm, @@ -116,7 +116,7 @@ public struct PointOfSaleSearchCouponFetchStrategy: PointOfSaleCouponFetchStrate return resultsController.fetchCoupons(predicate: predicate) } - public func fetchLocalCoupons() async throws -> [POSItem] { + func fetchLocalCoupons() async throws -> [POSItem] { // No support for returning local search results return [] } diff --git a/Modules/Sources/Yosemite/PointOfSale/Coupons/PointOfSaleCouponFetchStrategyFactory.swift b/Modules/Sources/Yosemite/PointOfSale/Coupons/PointOfSaleCouponFetchStrategyFactory.swift index 4f656844be4..b52a32157c9 100644 --- a/Modules/Sources/Yosemite/PointOfSale/Coupons/PointOfSaleCouponFetchStrategyFactory.swift +++ b/Modules/Sources/Yosemite/PointOfSale/Coupons/PointOfSaleCouponFetchStrategyFactory.swift @@ -6,7 +6,12 @@ import class Networking.AlamofireNetwork import struct Combine.AnyPublisher import struct NetworkingCore.JetpackSite -public struct PointOfSaleCouponFetchStrategyFactory { +public protocol PointOfSaleCouponFetchStrategyFactoryProtocol { + var defaultStrategy: PointOfSaleCouponFetchStrategy { get } + func searchStrategy(searchTerm: String, analytics: POSItemFetchAnalyticsTracking) -> PointOfSaleCouponFetchStrategy +} + +public struct PointOfSaleCouponFetchStrategyFactory: PointOfSaleCouponFetchStrategyFactoryProtocol { private let siteID: Int64 private let currencySettings: CurrencySettings private let storage: StorageManagerType @@ -28,14 +33,14 @@ public struct PointOfSaleCouponFetchStrategyFactory { self.couponStoreMethods = CouponStoreMethods(storageManager: storage, remote: remote) } - public var defaultStrategy: PointOfSaleDefaultCouponFetchStrategy { + public var defaultStrategy: PointOfSaleCouponFetchStrategy { PointOfSaleDefaultCouponFetchStrategy(siteID: siteID, currencySettings: currencySettings, storage: storage, couponStoreMethods: couponStoreMethods) } - public func searchStrategy(searchTerm: String, analytics: POSItemFetchAnalyticsTracking) -> PointOfSaleSearchCouponFetchStrategy { + public func searchStrategy(searchTerm: String, analytics: POSItemFetchAnalyticsTracking) -> PointOfSaleCouponFetchStrategy { PointOfSaleSearchCouponFetchStrategy(siteID: siteID, currencySettings: currencySettings, storage: storage, diff --git a/Modules/Sources/Yosemite/PointOfSale/Items/PointOfSaleItemFetchStrategyFactory.swift b/Modules/Sources/Yosemite/PointOfSale/Items/PointOfSaleItemFetchStrategyFactory.swift index b047d47207d..6166cf02c9a 100644 --- a/Modules/Sources/Yosemite/PointOfSale/Items/PointOfSaleItemFetchStrategyFactory.swift +++ b/Modules/Sources/Yosemite/PointOfSale/Items/PointOfSaleItemFetchStrategyFactory.swift @@ -10,8 +10,6 @@ public protocol PointOfSaleItemFetchStrategyFactoryProtocol { func searchStrategy(searchTerm: String, analytics: POSItemFetchAnalyticsTracking) -> PointOfSalePurchasableItemFetchStrategy - - func popularStrategy(pageSize: Int) -> PointOfSalePurchasableItemFetchStrategy } public final class PointOfSaleItemFetchStrategyFactory: PointOfSaleItemFetchStrategyFactoryProtocol { @@ -69,8 +67,4 @@ public final class PointOfSaleFixedItemFetchStrategyFactory: PointOfSaleItemFetc analytics: POSItemFetchAnalyticsTracking) -> PointOfSalePurchasableItemFetchStrategy { fixedStrategy } - - public func popularStrategy(pageSize: Int) -> PointOfSalePurchasableItemFetchStrategy { - fixedStrategy - } } diff --git a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalyticsTracking.swift b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalyticsTracking.swift index cbe1c90aebd..2a7033743fe 100644 --- a/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalyticsTracking.swift +++ b/WooCommerce/Classes/POS/Analytics/POSCollectOrderPaymentAnalyticsTracking.swift @@ -1,4 +1,4 @@ -protocol POSCollectOrderPaymentAnalyticsTracking { +public protocol POSCollectOrderPaymentAnalyticsTracking { func trackCustomerInteractionStarted() func trackOrderSyncSuccess() func trackCardReaderReady() diff --git a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentCardReader.swift b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentCardReader.swift index 057637263e1..aeff2c882fb 100644 --- a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentCardReader.swift +++ b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentCardReader.swift @@ -1,6 +1,6 @@ import Foundation -struct CardPresentPaymentCardReader: Equatable { +public struct CardPresentPaymentCardReader: Equatable { let name: String /// The reader's battery level, if available. diff --git a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentEvent.swift b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentEvent.swift index 0e0da4391cd..4623ef8c3a3 100644 --- a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentEvent.swift +++ b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentEvent.swift @@ -1,6 +1,6 @@ import Foundation -enum CardPresentPaymentEvent { +public enum CardPresentPaymentEvent { case idle case show(eventDetails: CardPresentPaymentEventDetails) case showOnboarding(factory: CardPresentPaymentOnboardingViewContainer, onCancel: () -> Void) diff --git a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentEventDetails.swift b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentEventDetails.swift index e0aa3065c77..0a38cdcdaac 100644 --- a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentEventDetails.swift +++ b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentEventDetails.swift @@ -1,7 +1,7 @@ import Foundation import struct Yosemite.CardReaderInput -enum CardPresentPaymentEventDetails { +public enum CardPresentPaymentEventDetails { case scanningForReaders(endSearch: () -> Void) case scanningFailed(error: Error, endSearch: () -> Void) diff --git a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentFacade.swift b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentFacade.swift index e94787508e0..8227d7247ca 100644 --- a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentFacade.swift +++ b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentFacade.swift @@ -3,7 +3,7 @@ import enum Yosemite.PaymentChannel import struct Yosemite.Order import Combine -protocol CardPresentPaymentFacade { +public protocol CardPresentPaymentFacade { /// `paymentEventPublisher` provides a stream of events relating to a payment, including their view models, /// for subscribers to display to the user. e.g. onboarding screens, connection progress, payment progress, card reader messages. /// This is a long lasting stream, and will not finish during the life of the façade, instead it will publish events for each payment attempt. diff --git a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentReaderConnectionResult.swift b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentReaderConnectionResult.swift index c76c29f01aa..875f1d7e242 100644 --- a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentReaderConnectionResult.swift +++ b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentReaderConnectionResult.swift @@ -1,6 +1,6 @@ import Foundation -enum CardPresentPaymentReaderConnectionResult { +public enum CardPresentPaymentReaderConnectionResult { case connected(CardPresentPaymentCardReader) case canceled } diff --git a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentReaderConnectionStatus.swift b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentReaderConnectionStatus.swift index 9151432edeb..bff03df7a62 100644 --- a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentReaderConnectionStatus.swift +++ b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentReaderConnectionStatus.swift @@ -1,6 +1,6 @@ import Foundation -enum CardPresentPaymentReaderConnectionStatus: Equatable { +public enum CardPresentPaymentReaderConnectionStatus: Equatable { case disconnected case connected(CardPresentPaymentCardReader) case cancellingConnection diff --git a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentResult.swift b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentResult.swift index 1831eaaffe7..df4fd5812f7 100644 --- a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentResult.swift +++ b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentResult.swift @@ -1,6 +1,6 @@ import Foundation -enum CardPresentPaymentResult { +public enum CardPresentPaymentResult { case success(CardPresentPaymentTransaction) case cancellation } diff --git a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentTransaction.swift b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentTransaction.swift index afb93286640..b96b4b085eb 100644 --- a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentTransaction.swift +++ b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentTransaction.swift @@ -1,6 +1,6 @@ import Foundation /// A completed, paid transaction. -struct CardPresentPaymentTransaction { +public struct CardPresentPaymentTransaction { let receiptURL: URL } diff --git a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentsRetryApproach.swift b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentsRetryApproach.swift index d4e32a5f0aa..4305a00c770 100644 --- a/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentsRetryApproach.swift +++ b/WooCommerce/Classes/POS/Card Present Payments/CardPresentPaymentsRetryApproach.swift @@ -2,7 +2,7 @@ import Foundation import enum Yosemite.CardReaderServiceError import enum Yosemite.CardReaderServiceUnderlyingError -enum CardPresentPaymentRetryApproach { +public enum CardPresentPaymentRetryApproach { case dontRetry case tryAgain(retryAction: () -> Void) case tryAnotherPaymentMethod(retryAction: () -> Void) diff --git a/WooCommerce/Classes/POS/Card Present Payments/CardReaderConnectionMethod.swift b/WooCommerce/Classes/POS/Card Present Payments/CardReaderConnectionMethod.swift index 8db262fc112..58e03096871 100644 --- a/WooCommerce/Classes/POS/Card Present Payments/CardReaderConnectionMethod.swift +++ b/WooCommerce/Classes/POS/Card Present Payments/CardReaderConnectionMethod.swift @@ -1,6 +1,6 @@ import Foundation -enum CardReaderConnectionMethod { +public enum CardReaderConnectionMethod { case bluetooth case tapToPay } diff --git a/WooCommerce/Classes/POS/Controllers/POSEntryPointController.swift b/WooCommerce/Classes/POS/Controllers/POSEntryPointController.swift index 7095d3cf8cb..ab90a443fae 100644 --- a/WooCommerce/Classes/POS/Controllers/POSEntryPointController.swift +++ b/WooCommerce/Classes/POS/Controllers/POSEntryPointController.swift @@ -1,6 +1,6 @@ import SwiftUI -protocol POSEntryPointEligibilityCheckerProtocol { +public protocol POSEntryPointEligibilityCheckerProtocol { /// Determines whether the site is eligible for POS. func checkEligibility() async -> POSEligibilityState /// Refreshes the eligibility state based on the provided ineligible reason. diff --git a/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift b/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift index abfd33d16ee..7e86cb4fc6b 100644 --- a/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift +++ b/WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift @@ -2,6 +2,7 @@ import Observation import enum Yosemite.POSItem import enum Yosemite.PointOfSaleCouponServiceError import protocol Yosemite.PointOfSaleItemServiceProtocol +import protocol Yosemite.PointOfSaleCouponFetchStrategyFactoryProtocol import protocol Yosemite.PointOfSaleCouponServiceProtocol import struct Yosemite.PointOfSaleCouponFetchStrategyFactory import protocol Yosemite.PointOfSaleCouponFetchStrategy @@ -19,12 +20,12 @@ protocol PointOfSaleCouponsControllerProtocol: PointOfSaleSearchingItemsControll private let paginationTracker: AsyncPaginationTracker private var childPaginationTrackers: [POSItem: AsyncPaginationTracker] = [:] private let couponProvider: PointOfSaleCouponServiceProtocol - private let fetchStrategyFactory: PointOfSaleCouponFetchStrategyFactory + private let fetchStrategyFactory: PointOfSaleCouponFetchStrategyFactoryProtocol private var fetchStrategy: PointOfSaleCouponFetchStrategy private let analyticsProvider: POSAnalyticsProviding init(itemProvider: PointOfSaleCouponServiceProtocol, - fetchStrategyFactory: PointOfSaleCouponFetchStrategyFactory, + fetchStrategyFactory: PointOfSaleCouponFetchStrategyFactoryProtocol, analyticsProvider: POSAnalyticsProviding) { self.couponProvider = itemProvider self.fetchStrategyFactory = fetchStrategyFactory diff --git a/WooCommerce/Classes/POS/Models/POSIneligibleReason.swift b/WooCommerce/Classes/POS/Models/POSIneligibleReason.swift index a79044823c1..0c783c960dd 100644 --- a/WooCommerce/Classes/POS/Models/POSIneligibleReason.swift +++ b/WooCommerce/Classes/POS/Models/POSIneligibleReason.swift @@ -3,7 +3,7 @@ import enum WooFoundation.CountryCode import enum WooFoundation.CurrencyCode /// Represents the reasons why a site may be ineligible for POS. -enum POSIneligibleReason: Equatable { +public enum POSIneligibleReason: Equatable { case unsupportedWooCommerceVersion(minimumVersion: String) case siteSettingsNotAvailable case wooCommercePluginNotFound @@ -13,7 +13,7 @@ enum POSIneligibleReason: Equatable { } /// Represents the eligibility state for POS. -enum POSEligibilityState: Equatable { +public enum POSEligibilityState: Equatable { case eligible case ineligible(reason: POSIneligibleReason) } diff --git a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListEmptyView.swift b/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListEmptyView.swift deleted file mode 100644 index abc85d90ac6..00000000000 --- a/WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/PointOfSaleItemListEmptyView.swift +++ /dev/null @@ -1,274 +0,0 @@ -import SwiftUI -import struct WooFoundation.ScrollableVStack - -protocol POSEmptyViewModelProtocol { - var title: String { get } - var subtitle: String { get } - var hint: String? { get } - var buttonTitle: String? { get } - var iconName: String { get } -} - -struct POSListEmptyView: View { - @Environment(\.dynamicTypeSize) private var dynamicTypeSize - @Environment(\.floatingControlAreaSize) private var floatingControlAreaSize: CGSize - private let viewModel: any POSEmptyViewModelProtocol - - private let onAction: (() -> Void)? - - @State private var viewWidth: CGFloat = 0 - - @Environment(\.keyboardObserver) private var keyboard - - init(viewModel: any POSEmptyViewModelProtocol, onAction: (() -> Void)? = nil) { - self.viewModel = viewModel - self.onAction = onAction - } - - var body: some View { - ScrollableVStack { - Spacer() - VStack(alignment: .center, spacing: POSSpacing.none) { - let shouldShowIcon: Bool = !dynamicTypeSize.isAccessibilitySize && !keyboard.isFullSizeKeyboardVisible - - if shouldShowIcon { - icon - - Spacer().frame(height: PointOfSaleEmptyErrorStateViewLayout.imageAndTextSpacing) - } - - Text(viewModel.title) - .accessibilityAddTraits(.isHeader) - .foregroundStyle(Color.posOnSurface) - .font(.posHeadingBold) - .multilineTextAlignment(.center) - - Spacer().frame(height: PointOfSaleEmptyErrorStateViewLayout.textSpacing) - - Text(viewModel.subtitle) - .foregroundStyle(Color.posOnSurface) - .font(.posBodyLargeRegular()) - .padding([.leading, .trailing]) - .multilineTextAlignment(.center) - - if let hint = viewModel.hint { - Spacer().frame(height: POSSpacing.small) - Text(hint) - .foregroundStyle(Color.posOnSurfaceVariantHighest) - .font(.posBodyLargeRegular()) - .padding([.leading, .trailing]) - .multilineTextAlignment(.center) - } - - Spacer().frame(height: PointOfSaleEmptyErrorStateViewLayout.textAndButtonSpacing) - - if let onAction, let buttonTitle = viewModel.buttonTitle { - Button(action: { - onAction() - }, label: { - Text(buttonTitle) - }) - .buttonStyle(POSOutlinedButtonStyle(size: .normal)) - .frame(width: viewWidth / 2) - .padding([.leading, .trailing]) - } - } - Spacer() - } - .multilineTextAlignment(.center) - .padding(.bottom, !keyboard.isFullSizeKeyboardVisible ? floatingControlAreaSize.height : 0) - .animation(.default, value: keyboard.keyboardHeight) - .measureWidth { width in - viewWidth = width - } - } - - @ViewBuilder - private var icon: some View { - Image(decorative: viewModel.iconName) - .resizable() - .aspectRatio(contentMode: .fit) - .frame(width: Constants.iconSize, height: Constants.iconSize) - .foregroundColor(.posOnSurfaceVariantHighest) - } -} - -private extension POSListEmptyView { - enum Constants { - static let iconSize: CGFloat = 88 - } -} - -struct PointOfSaleItemListEmptyViewModel { - let itemListType: ItemListType - let baseItem: ItemListBaseItem - - var title: String { - switch (baseItem, itemListType) { - case (.root, .products(search: true)): - return Localization.emptyProductsSearchTitle - case (.root, .products(search: false)): - return Localization.emptyProductsTitle - case (.root, .coupons): - return Localization.emptyCouponsTitle - case (.parent, .products): - return Localization.emptyVariableParentProductTitle - default: - assertionFailure("No title defined for \(baseItem)") - return Localization.emptyProductsTitle - } - } - - var subtitle: String { - switch (baseItem, itemListType) { - case (.root, .products(search: true)): - return Localization.emptyProductsSearchSubtitle - case (.root, .products(search: false)): - return Localization.emptyProductsSubtitle - case (.root, .coupons(search: false)): - return Localization.emptyCouponsSubtitle - case (.root, .coupons(search: true)): - return Localization.emptyCouponSearchSubtitle - case (.parent, .products): - return Localization.emptyVariableParentProductSubtitle - default: - assertionFailure("No subtitle defined for \(baseItem)") - return Localization.emptyProductsTitle - } - } - - var hint: String? { - switch (baseItem, itemListType) { - case (.root, .products(search: true)): - return Localization.emptyProductsSearchHint - case (.root, .products(search: false)): - return Localization.emptyProductsHint - case (.root, .coupons): - return nil - case (.parent, .products): - return Localization.emptyVariableParentProductHint - default: - assertionFailure("No hint defined for \(baseItem)") - return Localization.emptyProductsTitle - } - } - - var buttonTitle: String? { - switch (baseItem, itemListType) { - case (.root, .coupons(search: false)): - return Localization.emptyCouponsButtonTitle - case (.root, .products(search: false)): - return Localization.emptyProductsButtonTitle - case (.parent, .products): - return Localization.emptyProductsButtonTitle - default: - return nil - } - } - - var iconName: String { - switch itemListType { - case .coupons(search: false): - PointOfSaleAssets.coupons.imageName - default: - PointOfSaleAssets.magnifierNotFound.imageName - } - } - - enum Localization { - static let emptyProductsTitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyProductsTitle.2", - value: "No supported products found", - comment: "Text appearing on screen when there are no products to load." - ) - static let emptyProductsSubtitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyProductsSubtitle.1", - value: "POS currently only supports simple and variable products.", - comment: "Subtitle text on screen when there are no products to load." - ) - static let emptyProductsHint = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyProductsHint.1", - value: "To add one, exit POS and go to Products.", - comment: "Text hinting the merchant to create a product." - ) - static let emptyProductsSearchTitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyProductsSearchTitle.2", - value: "No products found", - comment: "Text appearing on screen when a POS product search returns no results." - ) - static let emptyProductsSearchSubtitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyProductsSearchSubtitle.2", - value: "We couldn't find any matching products — try adjusting your search term.", - comment: "Subtitle text suggesting to modify search terms when no products are found in the POS product search." - ) - static let emptyProductsSearchHint = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyProductsSearchHint", - value: "Variation names can't be searched, so use the parent product name.", - comment: "Text providing additional search tips when no products are found in the POS product search." - ) - static let emptyVariableParentProductTitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyVariableParentProductTitle.2", - value: "No supported variations found", - comment: "Text appearing on screen when there are no variations to load." - ) - static let emptyVariableParentProductSubtitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyVariableParentProductSubtitle", - value: "POS only supports enabled, non-downloadable variations.", - comment: "Subtitle text on screen when there are no products to load." - ) - static let emptyVariableParentProductHint = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyVariableParentProductHint", - value: "To add one, exit POS and edit this product in the Products tab.", - comment: "Text hinting the merchant to create a product." - ) - static let emptyCouponsTitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyCouponsTitle2", - value: "No coupons found", - comment: "Text appearing on the coupon list screen when there's no coupons found." - ) - static let emptyCouponsSubtitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyCouponsSubtitle.2", - value: "Coupons can be an effective way to drive business. Would you like to create one?", - comment: "Text appearing on the coupons list screen as subtitle when there's no coupons found." - ) - static let emptyCouponsButtonTitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.noCouponsFoundButtonTitleButtonTitle", - value: "Create coupon", - comment: "Text for the button appearing on the coupons list screen when there's no coupons found." - ) - static let emptyCouponSearchSubtitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyCouponSearchSubtitle.2", - value: "We couldn’t find any coupons with that name — try adjusting your search term.", - comment: "Text appearing on the coupons list screen as subtitle when there's no coupons found." - ) - static let emptyProductsButtonTitle = NSLocalizedString( - "pos.pointOfSaleItemListEmptyView.emptyProductsButtonTitle", - value: "Refresh", - comment: "Text for the button appearing on the products list screen when there are no products found." - ) - } -} - -// MARK: - Preview - -#Preview { - POSListEmptyView( - viewModel: PointOfSaleItemListEmptyViewModel( - itemListType: .coupons(search: false), - baseItem: .root - ) - ) {} -} - -#Preview { - POSListEmptyView( - viewModel: PointOfSaleItemListEmptyViewModel( - itemListType: .products(search: true), - baseItem: .root - ) - ) {} -} - -// MARK: - Protocol Conformance - -extension PointOfSaleItemListEmptyViewModel: POSEmptyViewModelProtocol {} diff --git a/WooCommerce/Classes/POS/Presentation/Payments Onboarding/PointOfSaleCardPresentPaymentOnboardingViewModel.swift b/WooCommerce/Classes/POS/Presentation/Payments Onboarding/PointOfSaleCardPresentPaymentOnboardingViewModel.swift index 8b2ba3363fe..127f18fd6d1 100644 --- a/WooCommerce/Classes/POS/Presentation/Payments Onboarding/PointOfSaleCardPresentPaymentOnboardingViewModel.swift +++ b/WooCommerce/Classes/POS/Presentation/Payments Onboarding/PointOfSaleCardPresentPaymentOnboardingViewModel.swift @@ -8,7 +8,7 @@ protocol CardPresentPaymentsOnboardingViewConfiguration: ObservableObject { var state: CardPresentPaymentOnboardingState { get } } -final class CardPresentPaymentOnboardingViewContainer: ObservableObject, Equatable, Identifiable { +public class CardPresentPaymentOnboardingViewContainer: ObservableObject, Equatable, Identifiable { @Published var configuration: any CardPresentPaymentsOnboardingViewConfiguration @Published var view: any View @@ -17,7 +17,7 @@ final class CardPresentPaymentOnboardingViewContainer: ObservableObject, Equatab self.view = view } - static func == (lhs: CardPresentPaymentOnboardingViewContainer, rhs: CardPresentPaymentOnboardingViewContainer) -> Bool { + public static func == (lhs: CardPresentPaymentOnboardingViewContainer, rhs: CardPresentPaymentOnboardingViewContainer) -> Bool { lhs.configuration.state == rhs.configuration.state } } diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift index fd7a4dee71f..c318bfd124b 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift @@ -1,8 +1,23 @@ import SwiftUI +import protocol Storage.GRDBManagerProtocol +import protocol Yosemite.POSCatalogSyncCoordinatorProtocol +import protocol Yosemite.POSOrderListFetchStrategyFactoryProtocol +import protocol Yosemite.POSOrderServiceProtocol +import protocol Yosemite.POSReceiptServiceProtocol import protocol Yosemite.POSSearchHistoryProviding +import protocol Yosemite.PluginsServiceProtocol +import class Yosemite.PointOfSaleFixedItemFetchStrategyFactory import protocol Yosemite.PointOfSaleBarcodeScanServiceProtocol +import struct Yosemite.PointOfSaleCouponFetchStrategyFactory +import protocol Yosemite.PointOfSaleCouponServiceProtocol +import protocol Yosemite.PointOfSaleItemFetchStrategyFactoryProtocol +import class Yosemite.PointOfSaleItemService +import protocol Yosemite.PointOfSaleSettingsServiceProtocol +import struct Yosemite.SiteSetting +import protocol Yosemite.PointOfSaleCouponFetchStrategyFactoryProtocol -struct PointOfSaleEntryPointView: View { +/// periphery: ignore - public in preparation of move to POS module +public struct PointOfSaleEntryPointView: View { @State private var posModel: PointOfSaleAggregateModel? @StateObject private var posModalManager = POSModalManager() @StateObject private var posSheetManager = POSSheetManager() @@ -26,43 +41,83 @@ struct PointOfSaleEntryPointView: View { private let siteTimezone: TimeZone private let services: POSDependencyProviding - init(itemsController: PointOfSaleItemsControllerProtocol, - purchasableItemsSearchController: PointOfSaleSearchingItemsControllerProtocol, - couponsController: PointOfSaleCouponsControllerProtocol, - couponsSearchController: PointOfSaleSearchingItemsControllerProtocol, - ordersController: POSSearchingOrderListControllerProtocol, + /// periphery: ignore - public in preparation of move to POS module + public init(siteID: Int64, + itemFetchStrategyFactory: PointOfSaleItemFetchStrategyFactoryProtocol, + popularItemFetchStrategyFactory: PointOfSaleItemFetchStrategyFactoryProtocol, + couponProvider: PointOfSaleCouponServiceProtocol, + couponFetchStrategyFactory: PointOfSaleCouponFetchStrategyFactoryProtocol, + orderListFetchStrategyFactory: POSOrderListFetchStrategyFactoryProtocol, + orderService: POSOrderServiceProtocol, onPointOfSaleModeActiveStateChange: @escaping ((Bool) -> Void), cardPresentPaymentService: CardPresentPaymentFacade, - orderController: PointOfSaleOrderControllerProtocol, - receiptSender: POSReceiptSending, - settingsController: PointOfSaleSettingsControllerProtocol, + receiptService: POSReceiptServiceProtocol, + pluginsService: PluginsServiceProtocol, + settingsService: PointOfSaleSettingsServiceProtocol, collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalyticsTracking, searchHistoryService: POSSearchHistoryProviding, - popularPurchasableItemsController: PointOfSaleItemsControllerProtocol, barcodeScanService: PointOfSaleBarcodeScanServiceProtocol, posEligibilityChecker: POSEntryPointEligibilityCheckerProtocol, siteTimezone: TimeZone = .current, + defaultSiteName: String?, + siteSettings: [SiteSetting], + grdbManager: GRDBManagerProtocol?, + catalogSyncCoordinator: POSCatalogSyncCoordinatorProtocol?, services: POSDependencyProviding) { self.onPointOfSaleModeActiveStateChange = onPointOfSaleModeActiveStateChange - self.itemsController = itemsController - self.purchasableItemsSearchController = purchasableItemsSearchController - self.couponsController = couponsController - self.couponsSearchController = couponsSearchController + self.itemsController = PointOfSaleItemsController( + itemProvider: PointOfSaleItemService(currencySettings: services.currency.currencySettings), + itemFetchStrategyFactory: itemFetchStrategyFactory, + analyticsProvider: services.analytics + ) + self.purchasableItemsSearchController = PointOfSaleItemsController( + itemProvider: PointOfSaleItemService(currencySettings: services.currency.currencySettings), + itemFetchStrategyFactory: itemFetchStrategyFactory, + initialState: .init(containerState: .content, + itemsStack: .init(root: .loaded([], hasMoreItems: true), itemStates: [:])), + analyticsProvider: services.analytics + ) + self.couponsController = PointOfSaleCouponsController(itemProvider: couponProvider, + fetchStrategyFactory: couponFetchStrategyFactory, + analyticsProvider: services.analytics) + self.couponsSearchController = PointOfSaleCouponsController(itemProvider: couponProvider, + fetchStrategyFactory: couponFetchStrategyFactory, + analyticsProvider: services.analytics) self.cardPresentPaymentService = cardPresentPaymentService - self.orderController = orderController - self.settingsController = settingsController + let receiptSender = POSReceiptSender(siteID: siteID, + orderService: orderService, + receiptService: receiptService, + analytics: services.analytics, + pluginsService: pluginsService) + self.orderController = PointOfSaleOrderController(orderService: orderService, + receiptSender: receiptSender, + currencySettingsProvider: services.currency, + analytics: services.analytics) + self.settingsController = PointOfSaleSettingsController(siteID: siteID, + settingsService: settingsService, + cardPresentPaymentService: cardPresentPaymentService, + pluginsService: pluginsService, + defaultSiteName: defaultSiteName, + siteSettings: siteSettings, + grdbManager: grdbManager, + catalogSyncCoordinator: catalogSyncCoordinator) self.collectOrderPaymentAnalyticsTracker = collectOrderPaymentAnalyticsTracker self.searchHistoryService = searchHistoryService - self.popularPurchasableItemsController = popularPurchasableItemsController + self.popularPurchasableItemsController = PointOfSaleItemsController( + itemProvider: PointOfSaleItemService(currencySettings: services.currency.currencySettings), + itemFetchStrategyFactory: popularItemFetchStrategyFactory, + analyticsProvider: services.analytics + ) self.barcodeScanService = barcodeScanService self.posEntryPointController = POSEntryPointController(eligibilityChecker: posEligibilityChecker) + let ordersController = POSOrderListController(orderListFetchStrategyFactory: orderListFetchStrategyFactory) self.orderListModel = POSOrderListModel(ordersController: ordersController, receiptSender: receiptSender) self.siteTimezone = siteTimezone self.services = services } - var body: some View { + public var body: some View { Group { if let posModel { PointOfSaleDashboardView() @@ -115,22 +170,29 @@ struct PointOfSaleEntryPointView: View { #if DEBUG #Preview { - PointOfSaleEntryPointView(itemsController: PointOfSalePreviewItemsController(), - purchasableItemsSearchController: PointOfSalePreviewItemsController(), - couponsController: PointOfSalePreviewCouponsController(), - couponsSearchController: PointOfSalePreviewCouponsController(), - ordersController: POSConfigurablePreviewOrderListController(), - onPointOfSaleModeActiveStateChange: { _ in }, - cardPresentPaymentService: CardPresentPaymentPreviewService(), - orderController: PointOfSalePreviewOrderController(), - receiptSender: POSReceiptSenderPreview(), - settingsController: PointOfSaleSettingsPreviewController(), - collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentPreviewAnalytics(), - searchHistoryService: PointOfSalePreviewHistoryService(), - popularPurchasableItemsController: PointOfSalePreviewItemsController(), - barcodeScanService: PointOfSalePreviewBarcodeScanService(), - posEligibilityChecker: POSTabEligibilityChecker(siteID: 1), - services: POSPreviewServices()) + PointOfSaleEntryPointView( + siteID: 1, + itemFetchStrategyFactory: PointOfSaleItemFetchStrategyFactoryPreview(), + popularItemFetchStrategyFactory: PointOfSaleItemFetchStrategyFactoryPreview(), + couponProvider: PointOfSaleCouponServicePreview(), + couponFetchStrategyFactory: PointOfSaleCouponFetchStrategyFactoryPreview(), + orderListFetchStrategyFactory: POSOrderListFetchStrategyFactoryPreview(), + orderService: POSOrderServicePreview(), + onPointOfSaleModeActiveStateChange: { _ in }, + cardPresentPaymentService: CardPresentPaymentPreviewService(), + receiptService: POSReceiptServicePreview(), + pluginsService: PluginsServicePreview(), + settingsService: PointOfSaleSettingsServicePreview(), + collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentPreviewAnalytics(), + searchHistoryService: PointOfSalePreviewHistoryService(), + barcodeScanService: PointOfSalePreviewBarcodeScanService(), + posEligibilityChecker: PointOfSalePreviewTabEligibilityChecker(), + defaultSiteName: "Demo Store", + siteSettings: [], + grdbManager: nil, + catalogSyncCoordinator: nil, + services: POSPreviewServices() + ) } #endif diff --git a/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsView.swift b/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsView.swift index c647430165e..5ceaaf4fe37 100644 --- a/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsView.swift +++ b/WooCommerce/Classes/POS/Presentation/Settings/PointOfSaleSettingsView.swift @@ -3,6 +3,7 @@ import SwiftUI struct PointOfSaleSettingsView: View { @Environment(\.dismiss) private var dismiss @Environment(\.posAnalytics) private var analytics + @Environment(\.posFeatureFlags) private var featureFlags @State private var selection: SidebarNavigation? = .store let settingsController: PointOfSaleSettingsControllerProtocol @@ -55,7 +56,7 @@ extension PointOfSaleSettingsView { ) // TODO: WOOMOB-1287 - integrate with local catalog feature eligibility - if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.pointOfSaleLocalCatalogi1) && settingsController.localCatalogViewModel != nil { + if featureFlags.isFeatureFlagEnabled(.pointOfSaleLocalCatalogi1) && settingsController.localCatalogViewModel != nil { PointOfSaleSettingsCard( item: .localCatalog, isSelected: selection == .localCatalog, diff --git a/WooCommerce/Classes/POS/Protocols/POSDependencyProviding.swift b/WooCommerce/Classes/POS/Protocols/POSDependencyProviding.swift index 0974fa62167..d5428a429d3 100644 --- a/WooCommerce/Classes/POS/Protocols/POSDependencyProviding.swift +++ b/WooCommerce/Classes/POS/Protocols/POSDependencyProviding.swift @@ -3,16 +3,17 @@ import WooFoundationCore import WooFoundation import enum Experiments.FeatureFlag import struct Yosemite.Coupon +import enum Yosemite.CouponDiscountType import enum Yosemite.POSItem /// POSDepenencyProviding is part of the POS entry point that defines the external dependencies from the Woo app that POS depends on /// Protocol that provides analytics tracking capabilities for POS -protocol POSAnalyticsProviding { - func track(event: WooAnalyticsEvent) - func track(_ stat: WooAnalyticsStat) - func track(_ stat: WooAnalyticsStat, parameters: [String: WooAnalyticsEventPropertyType]) - func track(_ stat: WooAnalyticsStat, parameters: [String: WooAnalyticsEventPropertyType], error: Error) +public protocol POSAnalyticsProviding { + func track(event: WooFoundationCore.WooAnalyticsEvent) + func track(_ stat: WooFoundationCore.WooAnalyticsStat) + func track(_ stat: WooFoundationCore.WooAnalyticsStat, parameters: [String: WooAnalyticsEventPropertyType]) + func track(_ stat: WooFoundationCore.WooAnalyticsStat, parameters: [String: WooAnalyticsEventPropertyType], error: Error) } extension POSAnalyticsProviding { @@ -22,22 +23,22 @@ extension POSAnalyticsProviding { } /// Protocol that provides currency settings access for POS -protocol POSCurrencySettingsProviding { +public protocol POSCurrencySettingsProviding { var currencySettings: CurrencySettings { get } } /// Protocol that provides feature flag checking capabilities for POS -protocol POSFeatureFlagProviding { +public protocol POSFeatureFlagProviding { func isFeatureFlagEnabled(_ flag: FeatureFlag) -> Bool } /// Protocol that provides connectivity monitoring for POS -protocol POSConnectivityProviding { +public protocol POSConnectivityProviding { var connectivityObserver: ConnectivityObserver { get } } /// Protocol that provides main app navigation capabilities for POS -protocol POSExternalNavigationProviding { +public protocol POSExternalNavigationProviding { func navigateToCreateOrder() } @@ -45,13 +46,13 @@ protocol POSExternalNavigationProviding { /// and cannot be easily moved and reused in a shared module /// This is used as a workaround to enable POS modularization without requiring a larger refactoring effort /// -protocol POSExternalViewProviding { +public protocol POSExternalViewProviding { func createSupportFormView(isPresented: Binding, sourceTag: String) -> AnyView func createFormattableAmountTextField(preset: Decimal?, font: Font, onSubmit: @escaping () -> Void, onChange: @escaping (String) -> Void) -> AnyView - func createCouponCreationView(discountType: Coupon.DiscountType, + func createCouponCreationView(discountType: CouponDiscountType, showTypeSelection: Binding, onSuccess: @escaping (Coupon) -> Void, dismissHandler: @escaping () -> Void, @@ -59,12 +60,12 @@ protocol POSExternalViewProviding { func createDiscountTypeSelectionSheet(isPresented: Binding, title: String, cancelButtonTitle: String, - onSelection: @escaping (Coupon.DiscountType) -> Void) -> AnyView + onSelection: @escaping (CouponDiscountType) -> Void) -> AnyView } /// Main protocol that combines all POS dependency providers /// This enables dependency injection for POS code while maintaining clean separation from ServiceLocator -protocol POSDependencyProviding { +public protocol POSDependencyProviding { var analytics: POSAnalyticsProviding { get } var currency: POSCurrencySettingsProviding { get } var featureFlags: POSFeatureFlagProviding { get } diff --git a/WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift b/WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift index da74e82c9d8..30892b11b9f 100644 --- a/WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift +++ b/WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift @@ -148,68 +148,36 @@ private extension POSTabCoordinator { selectedSite: defaultSitePublisher, appPasswordSupportState: isAppPasswordSupported), #available(iOS 17.0, *) { - let receiptSender = POSReceiptSender(siteID: siteID, - orderService: orderService, - receiptService: receiptService, - analytics: serviceAdaptor.analytics, - pluginsService: pluginsService - ) let posView = PointOfSaleEntryPointView( - itemsController: PointOfSaleItemsController( - itemProvider: PointOfSaleItemService( - currencySettings: currencySettings), - itemFetchStrategyFactory: posItemFetchStrategyFactory, - analyticsProvider: serviceAdaptor.analytics), - purchasableItemsSearchController: PointOfSaleItemsController( - itemProvider: PointOfSaleItemService( - currencySettings: currencySettings), - itemFetchStrategyFactory: posItemFetchStrategyFactory, - initialState: .init(containerState: .content, - itemsStack: .init(root: .loaded([], hasMoreItems: true), itemStates: [:])), - analyticsProvider: serviceAdaptor.analytics), - couponsController: PointOfSaleCouponsController(itemProvider: posCouponProvider, - fetchStrategyFactory: posCouponFetchStrategyFactory, - analyticsProvider: serviceAdaptor.analytics), - couponsSearchController: PointOfSaleCouponsController(itemProvider: posCouponProvider, - fetchStrategyFactory: posCouponFetchStrategyFactory, - analyticsProvider: serviceAdaptor.analytics), - ordersController: POSOrderListController( - orderListFetchStrategyFactory: POSOrderListFetchStrategyFactory( - siteID: siteID, - credentials: credentials, - selectedSite: defaultSitePublisher, - appPasswordSupportState: isAppPasswordSupported, - currencyFormatter: CurrencyFormatter(currencySettings: currencySettings) - ) + siteID: siteID, + itemFetchStrategyFactory: posItemFetchStrategyFactory, + popularItemFetchStrategyFactory: posPopularItemFetchStrategyFactory, + couponProvider: posCouponProvider, + couponFetchStrategyFactory: posCouponFetchStrategyFactory, + orderListFetchStrategyFactory: POSOrderListFetchStrategyFactory( + siteID: siteID, + credentials: credentials, + selectedSite: defaultSitePublisher, + appPasswordSupportState: isAppPasswordSupported, + currencyFormatter: CurrencyFormatter(currencySettings: currencySettings) ), - + orderService: orderService, onPointOfSaleModeActiveStateChange: { [weak self] isEnabled in self?.updateDefaultConfigurationForPointOfSale(isEnabled) }, cardPresentPaymentService: cardPresentPaymentService, - orderController: PointOfSaleOrderController(orderService: orderService, - receiptSender: receiptSender, - currencySettingsProvider: serviceAdaptor.currency, - analytics: serviceAdaptor.analytics), - receiptSender: receiptSender, - settingsController: PointOfSaleSettingsController(siteID: siteID, - settingsService: settingsService, - cardPresentPaymentService: cardPresentPaymentService, - pluginsService: pluginsService, - defaultSiteName: storesManager.sessionManager.defaultSite?.name, - siteSettings: ServiceLocator.selectedSiteSettings.siteSettings, - grdbManager: grdbManager, - catalogSyncCoordinator: catalogSyncCoordinator), + receiptService: receiptService, + pluginsService: pluginsService, + settingsService: settingsService, collectOrderPaymentAnalyticsTracker: collectPaymentAnalyticsAdaptor, searchHistoryService: POSSearchHistoryService(siteID: siteID), - popularPurchasableItemsController: PointOfSaleItemsController( - itemProvider: PointOfSaleItemService(currencySettings: currencySettings), - itemFetchStrategyFactory: posPopularItemFetchStrategyFactory, - analyticsProvider: serviceAdaptor.analytics - ), barcodeScanService: barcodeScanService, posEligibilityChecker: eligibilityChecker, siteTimezone: siteTimezone, + defaultSiteName: storesManager.sessionManager.defaultSite?.name, + siteSettings: ServiceLocator.selectedSiteSettings.siteSettings, + grdbManager: grdbManager, + catalogSyncCoordinator: catalogSyncCoordinator, services: serviceAdaptor ) diff --git a/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift b/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift index 706382d49ec..420e650a805 100644 --- a/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift +++ b/WooCommerce/Classes/POS/Utils/PreviewHelpers.swift @@ -32,6 +32,19 @@ import protocol Yosemite.POSCatalogSyncCoordinatorProtocol import protocol Yosemite.POSCatalogSettingsServiceProtocol import struct Yosemite.POSCatalogInfo import struct Yosemite.Site +import struct Yosemite.Order +import struct Yosemite.POSCart +import struct Yosemite.POSReceiptInformation +import protocol Yosemite.POSOrderServiceProtocol +import protocol Yosemite.POSReceiptServiceProtocol +import protocol Yosemite.PointOfSaleCouponServiceProtocol +import protocol Yosemite.PointOfSaleCouponFetchStrategy +import protocol Yosemite.PointOfSaleSettingsServiceProtocol +import protocol Yosemite.PointOfSaleItemFetchStrategyFactoryProtocol +import protocol Yosemite.POSItemFetchAnalyticsTracking +import protocol Yosemite.POSOrderListFetchStrategyFactoryProtocol +import protocol Yosemite.POSOrderListFetchStrategy +import protocol Yosemite.PointOfSaleCouponFetchStrategyFactoryProtocol // MARK: - PreviewProvider helpers // @@ -425,28 +438,92 @@ final class POSCollectOrderPaymentPreviewAnalytics: POSCollectOrderPaymentAnalyt func resetCheckoutTapCountTracker() {} func trackSuccessfulCashPayment() {} +} + +final class POSOrderServicePreview: POSOrderServiceProtocol { + func syncOrder(cart: POSCart, currency: CurrencyCode) async throws -> Order { + .empty + } + + func updatePOSOrder(orderID: Int64, recipientEmail: String) async throws {} + + func markOrderAsCompletedWithCashPayment(order: Yosemite.Order, changeDueAmount: String?) async throws {} +} + +final class POSReceiptServicePreview: POSReceiptServiceProtocol { + func sendReceipt(orderID: Int64, recipientEmail: String, isEligibleForPOSReceipt: Bool) async throws {} +} + +final class PointOfSaleCouponServicePreview: PointOfSaleCouponServiceProtocol { + func provideLocalPointOfSaleCoupons(fetchStrategy: any PointOfSaleCouponFetchStrategy) async throws -> [POSItem] { + [] + } + + func providePointOfSaleCoupons(pageNumber: Int, fetchStrategy: any Yosemite.PointOfSaleCouponFetchStrategy) async throws -> PagedItems { + .init(items: [], hasMorePages: false, totalItems: 0) + } + + func enableCoupons() async throws {} +} + +final class PointOfSaleSettingsServicePreview: PointOfSaleSettingsServiceProtocol { + func retrievePointOfSaleSettings() async throws -> POSReceiptInformation { + .empty + } +} + +final class PointOfSaleItemFetchStrategyFactoryPreview: PointOfSaleItemFetchStrategyFactoryProtocol { + func defaultStrategy(analytics: any POSItemFetchAnalyticsTracking) -> any PointOfSalePurchasableItemFetchStrategy { + PointOfSalePreviewPurchasableItemFetchStrategy() + } + + func searchStrategy(searchTerm: String, analytics: any POSItemFetchAnalyticsTracking) -> any PointOfSalePurchasableItemFetchStrategy { + PointOfSalePreviewPurchasableItemFetchStrategy() + } +} - var connectedReaderModel: String? +final class POSOrderListFetchStrategyFactoryPreview: POSOrderListFetchStrategyFactoryProtocol { + func defaultStrategy() -> any POSOrderListFetchStrategy { + POSOrderListFetchStrategyPreview() + } - func preflightResultReceived(_ result: CardReaderPreflightResult?) {} + func searchStrategy(searchTerm: String) -> any Yosemite.POSOrderListFetchStrategy { + POSOrderListFetchStrategyPreview() + } +} - func trackProcessingCompletion(intent: PaymentIntent) {} +final class POSOrderListFetchStrategyPreview: POSOrderListFetchStrategy { + func loadOrder(orderID: Int64) async throws -> POSOrder { + POSPreviewHelpers.makePreviewOrder() + } - func trackSuccessfulCardPayment(capturedPaymentData: CardPresentCapturedPaymentData) {} + var supportsCaching: Bool = true - func trackPaymentFailure(with error: any Error) {} + var showsLoadingWithItems: Bool = false - func trackPaymentCancelation(cancelationSource: WooAnalyticsEvent.InPersonPayments.CancellationSource) {} + var id: String = "" - func trackEmailTapped() {} + func fetchOrders(pageNumber: Int) async throws -> PagedItems { + PagedItems(items: [], hasMorePages: false, totalItems: nil) + } +} - func trackReceiptPrintTapped() {} +final class PointOfSaleCouponFetchStrategyPreview: PointOfSaleCouponFetchStrategy { + func fetchCoupons(pageNumber: Int) async throws -> PagedItems { + .init(items: [], hasMorePages: false, totalItems: nil) + } - func trackReceiptPrintSuccess() {} + func fetchLocalCoupons() async throws -> [POSItem] { + [] + } +} - func trackReceiptPrintCanceled() {} +final class PointOfSaleCouponFetchStrategyFactoryPreview: PointOfSaleCouponFetchStrategyFactoryProtocol { + let defaultStrategy: PointOfSaleCouponFetchStrategy = PointOfSaleCouponFetchStrategyPreview() - func trackReceiptPrintFailed(error: any Error) {} + func searchStrategy(searchTerm: String, analytics: POSItemFetchAnalyticsTracking) -> PointOfSaleCouponFetchStrategy { + PointOfSaleCouponFetchStrategyPreview() + } } final class POSPreviewServices: POSDependencyProviding { diff --git a/WooCommerce/Classes/Tools/Binding.swift b/WooCommerce/Classes/Tools/Binding.swift index 342a0397835..079b025a3a1 100644 --- a/WooCommerce/Classes/Tools/Binding.swift +++ b/WooCommerce/Classes/Tools/Binding.swift @@ -3,4 +3,4 @@ import struct SwiftUI.Binding /// `Binding` resides inside `SwiftUI`. /// This alias allows us to use it inside view models to communicate with `SwiftUI` views without having the import the module on each file. /// -typealias Binding = SwiftUI.Binding +public typealias Binding = SwiftUI.Binding