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
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ extension Storage.GeneralAppSettings {
feedbacks: CopiableProp<[FeedbackType: FeedbackSettings]> = .copy,
isViewAddOnsSwitchEnabled: CopiableProp<Bool> = .copy,
isApplicationPasswordsSwitchEnabled: CopiableProp<Bool> = .copy,
isPOSLocalCatalogSwitchEnabled: CopiableProp<Bool> = .copy,
knownCardReaders: CopiableProp<[String]> = .copy,
lastEligibilityErrorInfo: NullableCopiableProp<EligibilityErrorInfo> = .copy,
lastJetpackBenefitsBannerDismissedTime: NullableCopiableProp<Date> = .copy,
Expand All @@ -74,6 +75,7 @@ extension Storage.GeneralAppSettings {
let feedbacks = feedbacks ?? self.feedbacks
let isViewAddOnsSwitchEnabled = isViewAddOnsSwitchEnabled ?? self.isViewAddOnsSwitchEnabled
let isApplicationPasswordsSwitchEnabled = isApplicationPasswordsSwitchEnabled ?? self.isApplicationPasswordsSwitchEnabled
let isPOSLocalCatalogSwitchEnabled = isPOSLocalCatalogSwitchEnabled ?? self.isPOSLocalCatalogSwitchEnabled
let knownCardReaders = knownCardReaders ?? self.knownCardReaders
let lastEligibilityErrorInfo = lastEligibilityErrorInfo ?? self.lastEligibilityErrorInfo
let lastJetpackBenefitsBannerDismissedTime = lastJetpackBenefitsBannerDismissedTime ?? self.lastJetpackBenefitsBannerDismissedTime
Expand All @@ -90,6 +92,7 @@ extension Storage.GeneralAppSettings {
feedbacks: feedbacks,
isViewAddOnsSwitchEnabled: isViewAddOnsSwitchEnabled,
isApplicationPasswordsSwitchEnabled: isApplicationPasswordsSwitchEnabled,
isPOSLocalCatalogSwitchEnabled: isPOSLocalCatalogSwitchEnabled,
knownCardReaders: knownCardReaders,
lastEligibilityErrorInfo: lastEligibilityErrorInfo,
lastJetpackBenefitsBannerDismissedTime: lastJetpackBenefitsBannerDismissedTime,
Expand Down
10 changes: 10 additions & 0 deletions Modules/Sources/Storage/Model/GeneralAppSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
///
public var isApplicationPasswordsSwitchEnabled: Bool

/// The state(`true` or `false`) for the POS local catalog beta feature switch.
///
public var isPOSLocalCatalogSwitchEnabled: Bool

/// A list (possibly empty) of known card reader IDs - i.e. IDs of card readers that should be reconnected to automatically
/// e.g. ["CHB204909005931"]
///
Expand Down Expand Up @@ -72,6 +76,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
feedbacks: [FeedbackType: FeedbackSettings],
isViewAddOnsSwitchEnabled: Bool,
isApplicationPasswordsSwitchEnabled: Bool,
isPOSLocalCatalogSwitchEnabled: Bool,
knownCardReaders: [String],
lastEligibilityErrorInfo: EligibilityErrorInfo? = nil,
lastJetpackBenefitsBannerDismissedTime: Date? = nil,
Expand All @@ -86,6 +91,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
self.feedbacks = feedbacks
self.isViewAddOnsSwitchEnabled = isViewAddOnsSwitchEnabled
self.isApplicationPasswordsSwitchEnabled = isApplicationPasswordsSwitchEnabled
self.isPOSLocalCatalogSwitchEnabled = isPOSLocalCatalogSwitchEnabled
self.knownCardReaders = knownCardReaders
self.lastEligibilityErrorInfo = lastEligibilityErrorInfo
self.lastJetpackBenefitsBannerDismissedTime = lastJetpackBenefitsBannerDismissedTime
Expand All @@ -103,6 +109,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
feedbacks: [:],
isViewAddOnsSwitchEnabled: false,
isApplicationPasswordsSwitchEnabled: true,
isPOSLocalCatalogSwitchEnabled: true,
knownCardReaders: [],
lastEligibilityErrorInfo: nil,
featureAnnouncementCampaignSettings: [:],
Expand Down Expand Up @@ -136,6 +143,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
feedbacks: updatedFeedbacks,
isViewAddOnsSwitchEnabled: isViewAddOnsSwitchEnabled,
isApplicationPasswordsSwitchEnabled: isApplicationPasswordsSwitchEnabled,
isPOSLocalCatalogSwitchEnabled: isPOSLocalCatalogSwitchEnabled,
knownCardReaders: knownCardReaders,
lastEligibilityErrorInfo: lastEligibilityErrorInfo,
featureAnnouncementCampaignSettings: featureAnnouncementCampaignSettings,
Expand All @@ -160,6 +168,7 @@ public struct GeneralAppSettings: Codable, Equatable, GeneratedCopiable {
feedbacks: feedbacks,
isViewAddOnsSwitchEnabled: isViewAddOnsSwitchEnabled,
isApplicationPasswordsSwitchEnabled: isApplicationPasswordsSwitchEnabled,
isPOSLocalCatalogSwitchEnabled: isPOSLocalCatalogSwitchEnabled,
knownCardReaders: knownCardReaders,
lastEligibilityErrorInfo: lastEligibilityErrorInfo,
featureAnnouncementCampaignSettings: updatedSettings,
Expand All @@ -184,6 +193,7 @@ extension GeneralAppSettings {
self.feedbacks = try container.decodeIfPresent([FeedbackType: FeedbackSettings].self, forKey: .feedbacks) ?? [:]
self.isViewAddOnsSwitchEnabled = try container.decodeIfPresent(Bool.self, forKey: .isViewAddOnsSwitchEnabled) ?? false
self.isApplicationPasswordsSwitchEnabled = try container.decodeIfPresent(Bool.self, forKey: .isApplicationPasswordsSwitchEnabled) ?? false
self.isPOSLocalCatalogSwitchEnabled = try container.decodeIfPresent(Bool.self, forKey: .isPOSLocalCatalogSwitchEnabled) ?? true
self.knownCardReaders = try container.decodeIfPresent([String].self, forKey: .knownCardReaders) ?? []
self.lastEligibilityErrorInfo = try container.decodeIfPresent(EligibilityErrorInfo.self, forKey: .lastEligibilityErrorInfo)
self.lastJetpackBenefitsBannerDismissedTime = try container.decodeIfPresent(Date.self, forKey: .lastJetpackBenefitsBannerDismissedTime)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ public enum WooAnalyticsStat: String {
case settingsBetaFeaturesProductsToggled = "settings_beta_features_products_toggled"
case settingsBetaFeaturesOrderAddOnsToggled = "settings_beta_features_order_addons_toggled"
case settingsBetaFeaturesApplicationPasswordsToggled = "settings_beta_features_application_passwords_toggled"
case settingsBetaFeaturesPOSLocalCatalogToggled = "settings_beta_features_pos_local_catalog_toggled"
case settingsBetaFeatureToggled = "settings_beta_feature_toggled"

case settingsPrivacySettingsTapped = "settings_privacy_settings_button_tapped"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public actor POSLocalCatalogEligibilityService: POSLocalCatalogEligibilityServic
private let catalogSizeLimit: Int
private let isLocalCatalogFeatureFlagEnabled: Bool
private let remoteFeatureFlagProvider: @Sendable () async -> Bool
private let betaFeatureToggleProvider: @Sendable () async -> Bool

// Eligibility states cached per site
private var eligibilityStates: [Int64: POSLocalCatalogEligibilityState] = [:]
Expand All @@ -24,18 +25,21 @@ public actor POSLocalCatalogEligibilityService: POSLocalCatalogEligibilityServic
/// - systemStatusService: Service to check WooCommerce plugin version
/// - isLocalCatalogFeatureFlagEnabled: Whether the local catalog feature flag is enabled
/// - remoteFeatureFlagProvider: Async closure that fetches the remote feature flag value
/// - betaFeatureToggleProvider: Async closure that fetches the beta feature toggle value from app settings
/// - catalogSizeLimit: Maximum allowed catalog size (products + variations)
public init(
catalogSizeChecker: POSCatalogSizeCheckerProtocol,
systemStatusService: POSSystemStatusServiceProtocol,
isLocalCatalogFeatureFlagEnabled: Bool,
remoteFeatureFlagProvider: @escaping @Sendable () async -> Bool,
betaFeatureToggleProvider: @escaping @Sendable () async -> Bool,
catalogSizeLimit: Int? = nil
) {
self.catalogSizeChecker = catalogSizeChecker
self.systemStatusService = systemStatusService
self.isLocalCatalogFeatureFlagEnabled = isLocalCatalogFeatureFlagEnabled
self.remoteFeatureFlagProvider = remoteFeatureFlagProvider
self.betaFeatureToggleProvider = betaFeatureToggleProvider
self.catalogSizeLimit = catalogSizeLimit ?? Constants.defaultCatalogSizeLimit
// Eagerly start fetching the remote flag in the background
Task {
Expand All @@ -46,6 +50,12 @@ public actor POSLocalCatalogEligibilityService: POSLocalCatalogEligibilityServic
/// Get catalog eligibility for a specific site
/// If not cached, refreshes eligibility and returns the result
public func catalogEligibility(for siteID: Int64) async throws -> POSLocalCatalogEligibilityState {
guard await betaFeatureToggleProvider() else {
// If the user changes the toggle, we should respond to that immediately, ignoring the cache. It's cheap to check.
DDLogInfo("📋 POSLocalCatalogEligibilityService: Local catalog beta toggle disabled for site \(siteID)")
return .ineligible(reason: .featureFlagDisabled)
}

if let cached = eligibilityStates[siteID] {
return cached
}
Expand Down Expand Up @@ -97,13 +107,12 @@ public actor POSLocalCatalogEligibilityService: POSLocalCatalogEligibilityServic
return state
}

// Check feature flags - both local and remote must be enabled
let isRemoteEnabled = await isRemoteCatalogFeatureFlagEnabled()
guard isLocalCatalogFeatureFlagEnabled && isRemoteEnabled else {
let (isLocalCatalogFeatureFlagEnabled, isRemoteEnabled, isBetaToggleEnabled) = await featureFlagSettings()
guard isLocalCatalogFeatureFlagEnabled, isRemoteEnabled, isBetaToggleEnabled else {
let state = POSLocalCatalogEligibilityState.ineligible(reason: .featureFlagDisabled)
eligibilityStates[siteID] = state
DDLogInfo("📋 POSLocalCatalogEligibilityService: Local catalog feature flags disabled for site \(siteID) " +
"(local: \(isLocalCatalogFeatureFlagEnabled), remote: \(isRemoteEnabled))")
"(local: \(isLocalCatalogFeatureFlagEnabled), remote: \(isRemoteEnabled), betaToggle: \(isBetaToggleEnabled))")
return state
}

Expand Down Expand Up @@ -171,6 +180,13 @@ public actor POSLocalCatalogEligibilityService: POSLocalCatalogEligibilityServic
return state
}
}

private func featureFlagSettings() async -> (Bool, Bool, Bool) {
// Check feature flags - local, remote, and beta toggle must all be enabled
let isRemoteEnabled = await isRemoteCatalogFeatureFlagEnabled()
let isBetaToggleEnabled = await betaFeatureToggleProvider()
return (isLocalCatalogFeatureFlagEnabled, isRemoteEnabled, isBetaToggleEnabled)
}
}

// MARK: - Factory Method
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ final class GeneralAppSettingsTests: XCTestCase {
feedbacks: feedbackSettings,
isViewAddOnsSwitchEnabled: true,
isApplicationPasswordsSwitchEnabled: false,
isPOSLocalCatalogSwitchEnabled: true,
knownCardReaders: readers,
lastEligibilityErrorInfo: eligibilityInfo,
lastJetpackBenefitsBannerDismissedTime: jetpackBannerDismissedDate,
Expand Down Expand Up @@ -155,6 +156,7 @@ private extension GeneralAppSettingsTests {
feedbacks: feedbacks,
isViewAddOnsSwitchEnabled: isViewAddOnsSwitchEnabled,
isApplicationPasswordsSwitchEnabled: false,
isPOSLocalCatalogSwitchEnabled: true,
knownCardReaders: knownCardReaders,
lastEligibilityErrorInfo: lastEligibilityErrorInfo,
lastJetpackBenefitsBannerDismissedTime: lastJetpackBenefitsBannerDismissedTime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ private extension InAppFeedbackCardVisibilityUseCaseTests {
feedbacks: [feedback.name: feedback],
isViewAddOnsSwitchEnabled: false,
isApplicationPasswordsSwitchEnabled: false,
isPOSLocalCatalogSwitchEnabled: false,
knownCardReaders: [],
featureAnnouncementCampaignSettings: [:],
sitesWithAtLeastOneIPPTransactionFinished: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1816,6 +1816,7 @@ private extension AppSettingsStoreTests {
feedbacks: [feedback.name: feedback],
isViewAddOnsSwitchEnabled: false,
isApplicationPasswordsSwitchEnabled: false,
isPOSLocalCatalogSwitchEnabled: false,
knownCardReaders: [],
featureAnnouncementCampaignSettings: [:],
sitesWithAtLeastOneIPPTransactionFinished: [],
Expand All @@ -1834,6 +1835,7 @@ private extension AppSettingsStoreTests {
feedbacks: [:],
isViewAddOnsSwitchEnabled: false,
isApplicationPasswordsSwitchEnabled: false,
isPOSLocalCatalogSwitchEnabled: false,
knownCardReaders: [],
featureAnnouncementCampaignSettings: featureAnnouncementCampaignSettings,
sitesWithAtLeastOneIPPTransactionFinished: [],
Expand Down
Loading
Loading