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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Modules/Sources/Experiments/DefaultFeatureFlagService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,6 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
return false
case .productImageOptimizedHandling:
return true
case .pointOfSaleAsATabi2:
return true
case .pointOfSaleOrdersi1:
return true
case .pointOfSaleOrdersi2:
Expand Down
4 changes: 0 additions & 4 deletions Modules/Sources/Experiments/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,6 @@ public enum FeatureFlag: Int {
///
case inventoryProductLabelsInPOS

/// Enables displaying POS as a tab in the tab bar for stores in eligible countries
///
case pointOfSaleAsATabi2

/// Enables displaying Point Of Sale details in order list and order details
///
case pointOfSaleOrdersi1
Expand Down
39 changes: 0 additions & 39 deletions Modules/Sources/Yosemite/Tools/Plugins/PluginsService.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,8 @@
import Foundation
import WooFoundation
import protocol Storage.StorageManagerType

/// A service for system plugins.
public protocol PluginsServiceProtocol {
/// Waits for a specific plugin to be available in storage.
/// - Parameters:
/// - siteID: The site ID to search for the plugin.
/// - pluginPath: The plugin's file path (e.g., "woocommerce/woocommerce.php" for WooCommerce).
/// - isActive: Whether the plugin is active or not.
/// - Returns: The SystemPlugin when found in storage.
func waitForPluginInStorage(siteID: Int64, pluginPath: String, isActive: Bool) async -> SystemPlugin

/// Loads a specific plugin from storage synchronously.
/// - Parameters:
/// - siteID: The site ID to search for the plugin.
Expand All @@ -37,36 +28,6 @@ public class PluginsService: PluginsServiceProtocol {
self.storageManager = storageManager
}

@MainActor
public func waitForPluginInStorage(siteID: Int64, pluginPath: String, isActive: Bool) async -> SystemPlugin {
let predicate = \StorageSystemPlugin.siteID == siteID && \StorageSystemPlugin.plugin == pluginPath && \StorageSystemPlugin.active == isActive
let pluginDescriptor = NSSortDescriptor(keyPath: \StorageSystemPlugin.plugin, ascending: true)
let resultsController = ResultsController<StorageSystemPlugin>(storageManager: storageManager,
matching: predicate,
fetchLimit: 1,
sortedBy: [pluginDescriptor])
do {
try resultsController.performFetch()
if let plugin = resultsController.fetchedObjects.first {
return plugin
}
} catch {
DDLogError("Error loading plugin \(pluginPath) for site \(siteID) initially: \(error.localizedDescription)")
}

return await withCheckedContinuation { continuation in
var hasResumed = false
resultsController.onDidChangeContent = {
guard let plugin = resultsController.fetchedObjects.first,
!hasResumed else {
return
}
hasResumed = true
continuation.resume(returning: plugin)
}
}
}

@MainActor
public func loadPluginInStorage(siteID: Int64, plugin: Plugin, isActive: Bool?) -> SystemPlugin? {
storageManager.viewStorage.loadSystemPlugin(siteID: siteID,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,6 @@ struct PluginsServiceTests {
sut = PluginsService(storageManager: storageManager)
}

@Test func waitForPluginInStorage_returns_plugin_when_already_in_storage() async {
// Given
await storageManager.reset()
storageManager.insertPlugin(siteID: siteID, plugin: .wooCommerce, isActive: true, version: "1.0.0")

// When
let result = await sut.waitForPluginInStorage(siteID: siteID, pluginPath: "woocommerce/woocommerce.php", isActive: true)

// Then
#expect(result.siteID == siteID)
#expect(result.plugin == "woocommerce/woocommerce.php")
#expect(result.active == true)
#expect(result.version == "1.0.0")
}

@Test func waitForPluginInStorage_waits_to_return_plugin_when_not_in_storage_initially() async {
// Given
// Resets any existing state, otherwise test might fail if run multiple times.
await storageManager.reset()

// When
async let plugin = sut.waitForPluginInStorage(siteID: siteID, pluginPath: "woocommerce/woocommerce.php", isActive: true)
#expect(storageManager.viewStorage.loadSystemPlugins(siteID: siteID).count == 0)
storageManager.insertPlugin(siteID: siteID, plugin: .wooCommerce, isActive: true, version: "2.0.0")
#expect(storageManager.viewStorage.loadSystemPlugins(siteID: siteID).count == 1)

// Then
let result = await plugin
#expect(result.siteID == siteID)
#expect(result.plugin == "woocommerce/woocommerce.php")
#expect(result.active == true)
#expect(result.version == "2.0.0")
}

// MARK: - `loadPluginInStorage`

@Test(arguments: [(Plugin.wooCommerce, true, "1.5.0"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ private extension POSIneligibleReason {
return "feature_switch_disabled"
case .wooCommercePluginNotFound:
return "unknown_wc_plugin"
case .unsupportedIOSVersion:
return "ios_version"
case .unsupportedInCIABSites:
return "feature_unsupported_in_ciab"
case .siteSettingsNotAvailable,
.selfDeallocated:
return "other"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import SwiftUI
import protocol Experiments.FeatureFlagService

protocol POSEntryPointEligibilityCheckerProtocol {
/// Determines whether the site is eligible for POS.
func checkEligibility() async -> POSEligibilityState
/// Refreshes the eligibility state based on the provided ineligible reason.
func refreshEligibility(ineligibleReason: POSIneligibleReason) async throws -> POSEligibilityState
}

@Observable final class POSEntryPointController {
private(set) var eligibilityState: POSEligibilityState?
private let posEligibilityChecker: POSEntryPointEligibilityCheckerProtocol
private let featureFlagService: POSFeatureFlagProviding

init(eligibilityChecker: POSEntryPointEligibilityCheckerProtocol,
featureFlagService: POSFeatureFlagProviding) {
init(eligibilityChecker: POSEntryPointEligibilityCheckerProtocol) {
self.posEligibilityChecker = eligibilityChecker
self.featureFlagService = featureFlagService

guard featureFlagService.isFeatureFlagEnabled(.pointOfSaleAsATabi2) else {
self.eligibilityState = .eligible
return
}
Task { @MainActor in
eligibilityState = await posEligibilityChecker.checkEligibility()
}
Expand Down
2 changes: 0 additions & 2 deletions WooCommerce/Classes/POS/Models/POSIneligibleReason.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,12 @@ import enum WooFoundation.CurrencyCode

/// Represents the reasons why a site may be ineligible for POS.
enum POSIneligibleReason: Equatable {
case unsupportedIOSVersion
case unsupportedWooCommerceVersion(minimumVersion: String)
case siteSettingsNotAvailable
case wooCommercePluginNotFound
case featureSwitchDisabled
case unsupportedCurrency(countryCode: CountryCode, supportedCurrencies: [CurrencyCode])
case selfDeallocated
case unsupportedInCIABSites
}

/// Represents the eligibility state for POS.
Expand Down
13 changes: 0 additions & 13 deletions WooCommerce/Classes/POS/Models/PointOfSaleSettingsController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,6 @@ final class MockPointOfSaleSettingsService: PointOfSaleSettingsServiceProtocol {
}

final class PluginsServicePreview: PluginsServiceProtocol {
func waitForPluginInStorage(siteID: Int64, pluginPath: String, isActive: Bool) async -> SystemPlugin {
return SystemPlugin(siteID: 1234,
plugin: "",
name: "",
version: "",
versionLatest: "",
url: "",
authorName: "",
authorUrl: "",
networkActivated: false,
active: true)
}

func loadPluginInStorage(siteID: Int64, plugin: Plugin, isActive: Bool?) -> SystemPlugin? {
return SystemPlugin(siteID: 1234,
plugin: "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct PointOfSaleEntryPointView: View {
self.searchHistoryService = searchHistoryService
self.popularPurchasableItemsController = popularPurchasableItemsController
self.barcodeScanService = barcodeScanService
self.posEntryPointController = POSEntryPointController(eligibilityChecker: posEligibilityChecker, featureFlagService: services.featureFlags)
self.posEntryPointController = POSEntryPointController(eligibilityChecker: posEligibilityChecker)
self.orderListModel = POSOrderListModel(ordersController: ordersController, receiptSender: receiptSender)
self.siteTimezone = siteTimezone
self.services = services
Expand Down Expand Up @@ -129,7 +129,7 @@ struct PointOfSaleEntryPointView: View {
searchHistoryService: PointOfSalePreviewHistoryService(),
popularPurchasableItemsController: PointOfSalePreviewItemsController(),
barcodeScanService: PointOfSalePreviewBarcodeScanService(),
posEligibilityChecker: POSTabEligibilityChecker(site: .defaultMock()),
posEligibilityChecker: POSTabEligibilityChecker(siteID: 1),
services: POSPreviewServices())
}

Expand Down
28 changes: 1 addition & 27 deletions WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,6 @@ struct POSIneligibleView: View {

private var suggestionText: String {
switch reason {
case .unsupportedIOSVersion:
return NSLocalizedString("pos.ineligible.suggestion.unsupportedIOSVersion.1",
value: "The POS system requires iOS 17 or later. Please update your device to iOS 17+ to use this feature.",
comment: "Suggestion for unsupported iOS version: update iOS")
case let .unsupportedWooCommerceVersion(minimumVersion):
let format = NSLocalizedString("pos.ineligible.suggestion.unsupportedWooCommerceVersion",
value: "Your WooCommerce version is not supported. " +
Expand Down Expand Up @@ -155,12 +151,6 @@ struct POSIneligibleView: View {
return NSLocalizedString("pos.ineligible.suggestion.selfDeallocated",
value: "Try relaunching the app to resolve this issue.",
comment: "Suggestion for self deallocated: relaunch")
case .unsupportedInCIABSites:
return NSLocalizedString(
"pos.ineligible.suggestion.notSupportedForCIAB",
value: "The POS system is not supported for your store.",
comment: "Suggestion for CIAB sites: feature is not supported"
)
}
}
}
Expand All @@ -184,9 +174,6 @@ private extension POSIneligibleView {
private extension POSIneligibleReason {
var shouldShowRetryButton: Bool {
switch self {
case .unsupportedIOSVersion,
.unsupportedInCIABSites:
return false
case .unsupportedWooCommerceVersion,
.siteSettingsNotAvailable,
.wooCommercePluginNotFound,
Expand All @@ -205,8 +192,7 @@ private extension POSIneligibleReason {
value: "Enable POS feature",
comment: "Button title to enable the POS feature switch and refresh POS eligibility check"
)
case .unsupportedIOSVersion,
.unsupportedWooCommerceVersion,
case .unsupportedWooCommerceVersion,
.siteSettingsNotAvailable,
.wooCommercePluginNotFound,
.unsupportedCurrency,
Expand All @@ -216,9 +202,6 @@ private extension POSIneligibleReason {
value: "Retry",
comment: "Button title to refresh POS eligibility check"
)
case .unsupportedInCIABSites:
assertionFailure("Retry button should not be shown for `unsupportedInCIABSites`")
return String()
}
}
}
Expand All @@ -234,15 +217,6 @@ private extension POSIneligibleReason {
}
}

#Preview("Unsupported iOS version") {
if #available(iOS 17.0, *) {
POSIneligibleView(
reason: .unsupportedIOSVersion,
onRefresh: {}
)
}
}

#Preview("WooCommerce plugin not found") {
if #available(iOS 17.0, *) {
POSIneligibleView(
Expand Down
52 changes: 8 additions & 44 deletions WooCommerce/Classes/POS/TabBar/POSTabCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import class WooFoundationCore.CurrencyFormatter
import struct NetworkingCore.JetpackSite
import struct Combine.AnyPublisher

protocol POSTabVisibilityCheckerProtocol {
/// Checks the initial visibility of the POS tab.
func checkInitialVisibility() -> Bool
/// Checks the final visibility of the POS tab.
func checkVisibility() async -> Bool
}

/// View controller that provides the tab bar item for the Point of Sale tab.
/// It is never visible on the screen, only used to provide the tab bar item as all POS UI is full-screen.
final class POSTabViewController: UIViewController {
Expand All @@ -24,7 +31,7 @@ final class POSTabViewController: UIViewController {
/// Coordinator for the Point of Sale tab.
///
final class POSTabCoordinator {
private(set) var siteID: Int64
private let siteID: Int64
private let tabContainerController: TabContainerController
private let viewControllerToPresent: UIViewController
private let storesManager: StoresManager
Expand Down Expand Up @@ -110,49 +117,6 @@ final class POSTabCoordinator {
func onTabSelected() {
presentPOSView(siteID: siteID)
}

func didSwitchStore(id: Int64) {
self.siteID = id

// Resets lazy properties so they get recreated with new siteID
posItemFetchStrategyFactory = PointOfSaleItemFetchStrategyFactory(
siteID: siteID,
credentials: credentials,
selectedSite: defaultSitePublisher,
appPasswordSupportState: isAppPasswordSupported
)

posPopularItemFetchStrategyFactory =
PointOfSaleFixedItemFetchStrategyFactory(
fixedStrategy: posItemFetchStrategyFactory.popularStrategy()
)

posCouponFetchStrategyFactory = PointOfSaleCouponFetchStrategyFactory(
siteID: siteID,
currencySettings: currencySettings,
credentials: credentials,
selectedSite: defaultSitePublisher,
appPasswordSupportState: isAppPasswordSupported,
storage: storageManager
)

posCouponProvider = PointOfSaleCouponService(
siteID: siteID,
currencySettings: currencySettings,
credentials: credentials,
selectedSite: defaultSitePublisher,
appPasswordSupportState: isAppPasswordSupported,
storage: storageManager
)

barcodeScanService = PointOfSaleBarcodeScanService(
siteID: siteID,
credentials: credentials,
selectedSite: defaultSitePublisher,
appPasswordSupportState: isAppPasswordSupported,
currencySettings: currencySettings
)
}
}

private extension POSTabCoordinator {
Expand Down
12 changes: 7 additions & 5 deletions WooCommerce/Classes/POS/Utils/PreviewHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,10 @@ struct POSPreviewHelpers {
searchHistoryService: POSSearchHistoryProviding = PointOfSalePreviewHistoryService(),
popularItemsController: PointOfSaleItemsControllerProtocol = PointOfSalePreviewItemsController(),
barcodeScanService: PointOfSaleBarcodeScanServiceProtocol = PointOfSalePreviewBarcodeScanService(),
analytics: POSAnalyticsProviding = EmptyPOSAnalytics(),
featureFlags: POSFeatureFlagProviding = EmptyPOSFeatureFlags()
analytics: POSAnalyticsProviding = EmptyPOSAnalytics()
) -> PointOfSaleAggregateModel {
return PointOfSaleAggregateModel(
entryPointController: POSEntryPointController(
eligibilityChecker: LegacyPOSTabEligibilityChecker(site: Site.defaultMock()),
featureFlagService: featureFlags),
entryPointController: POSEntryPointController(eligibilityChecker: PointOfSalePreviewTabEligibilityChecker()),
itemsController: itemsController,
purchasableItemsSearchController: purchasableItemsSearchController,
couponsController: couponsController,
Expand Down Expand Up @@ -405,6 +402,11 @@ final class PointOfSalePreviewBarcodeScanService: PointOfSaleBarcodeScanServiceP
}
}

final class PointOfSalePreviewTabEligibilityChecker: POSEntryPointEligibilityCheckerProtocol {
func checkEligibility() async -> POSEligibilityState { .eligible }
func refreshEligibility(ineligibleReason: POSIneligibleReason) async throws -> POSEligibilityState { .eligible }
}

final class POSReceiptSenderPreview: POSReceiptSending {
func sendReceipt(orderID: Int64, recipientEmail: String) async throws {}
}
Expand Down
Loading