Skip to content

Commit ca67d0f

Browse files
authored
POSTabEligibilityCheckerI2: i2 logic (#15865)
2 parents da958db + b6854d4 commit ca67d0f

File tree

3 files changed

+272
-349
lines changed

3 files changed

+272
-349
lines changed

WooCommerce/Classes/POS/TabBar/POSIneligibleView.swift

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,6 @@ struct POSIneligibleView: View {
7777

7878
private var suggestionText: String {
7979
switch reason {
80-
case .notTablet:
81-
return NSLocalizedString("pos.ineligible.suggestion.notTablet",
82-
value: "Please use a tablet to access POS features.",
83-
comment: "Suggestion for not tablet: use iPad")
8480
case .unsupportedIOSVersion:
8581
return NSLocalizedString("pos.ineligible.suggestion.unsupportedIOSVersion",
8682
value: "Point of Sale requires iOS 17 or later. Please update your device to iOS 17+ to use this feature.",
@@ -105,16 +101,6 @@ struct POSIneligibleView: View {
105101
return NSLocalizedString("pos.ineligible.suggestion.featureSwitchSyncFailure",
106102
value: "Try relaunching the app or check your internet connection and try again.",
107103
comment: "Suggestion for feature switch sync failure: relaunch or check connection")
108-
case let .unsupportedCountry(supportedCountries):
109-
let countryNames = supportedCountries.map { $0.readableCountry }
110-
let formattedCountryList = ListFormatter.localizedString(byJoining: countryNames)
111-
let format = NSLocalizedString(
112-
"pos.ineligible.suggestion.unsupportedCountry",
113-
value: "POS is currently only available in %1$@. Check back later for availability in your region.",
114-
comment: "Suggestion for unsupported country with list of supported countries. " +
115-
"%1$@ is a placeholder for the localized list of supported country names."
116-
)
117-
return String.localizedStringWithFormat(format, formattedCountryList)
118104
case let .unsupportedCurrency(supportedCurrencies):
119105
let currencyList = supportedCurrencies.map { $0.rawValue }
120106
let formattedCurrencyList = ListFormatter.localizedString(byJoining: currencyList)
@@ -130,10 +116,6 @@ struct POSIneligibleView: View {
130116
return NSLocalizedString("pos.ineligible.suggestion.siteSettingsNotAvailable",
131117
value: "Check your internet connection and try relaunching the app. If the issue persists, please contact support.",
132118
comment: "Suggestion for site settings unavailable: check connection or contact support")
133-
case .featureFlagDisabled:
134-
return NSLocalizedString("pos.ineligible.suggestion.featureFlagDisabled",
135-
value: "POS is currently disabled.",
136-
comment: "Suggestion for disabled feature flag: notify that POS is disabled remotely")
137119
case .selfDeallocated:
138120
return NSLocalizedString("pos.ineligible.suggestion.selfDeallocated",
139121
value: "Try relaunching the app to resolve this issue.",
@@ -176,24 +158,6 @@ private extension POSIneligibleView {
176158
}
177159
}
178160

179-
#Preview("Unsupported country") {
180-
if #available(iOS 17.0, *) {
181-
POSIneligibleView(
182-
reason: .unsupportedCountry(supportedCountries: [.US, .GB]),
183-
onRefresh: {}
184-
)
185-
}
186-
}
187-
188-
#Preview("Not a tablet") {
189-
if #available(iOS 17.0, *) {
190-
POSIneligibleView(
191-
reason: .notTablet,
192-
onRefresh: {}
193-
)
194-
}
195-
}
196-
197161
#Preview("Unsupported iOS version") {
198162
if #available(iOS 17.0, *) {
199163
POSIneligibleView(
@@ -212,15 +176,6 @@ private extension POSIneligibleView {
212176
}
213177
}
214178

215-
#Preview("Feature flag disabled") {
216-
if #available(iOS 17.0, *) {
217-
POSIneligibleView(
218-
reason: .featureFlagDisabled,
219-
onRefresh: {}
220-
)
221-
}
222-
}
223-
224179
#Preview("Feature switch disabled") {
225180
if #available(iOS 17.0, *) {
226181
POSIneligibleView(

WooCommerce/Classes/ViewRelated/Dashboard/Settings/POS/POSTabEligibilityChecker.swift

Lines changed: 47 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Combine
21
import Foundation
32
import UIKit
43
import class WooFoundation.CurrencySettings
@@ -17,15 +16,12 @@ import class Yosemite.PluginsService
1716

1817
/// Represents the reasons why a site may be ineligible for POS.
1918
enum POSIneligibleReason: Equatable {
20-
case notTablet
2119
case unsupportedIOSVersion
2220
case unsupportedWooCommerceVersion(minimumVersion: String)
2321
case siteSettingsNotAvailable
2422
case wooCommercePluginNotFound
25-
case featureFlagDisabled
2623
case featureSwitchDisabled
2724
case featureSwitchSyncFailure
28-
case unsupportedCountry(supportedCountries: [CountryCode])
2925
case unsupportedCurrency(supportedCurrencies: [CurrencyCode])
3026
case selfDeallocated
3127
}
@@ -46,9 +42,6 @@ protocol POSEntryPointEligibilityCheckerProtocol {
4642
}
4743

4844
final class POSTabEligibilityChecker: POSEntryPointEligibilityCheckerProtocol {
49-
private var siteSettingsEligibility: POSEligibilityState?
50-
private var featureFlagEligibility: POSEligibilityState?
51-
5245
private let siteID: Int64
5346
private let userInterfaceIdiom: UIUserInterfaceIdiom
5447
private let siteSettings: SelectedSiteSettingsProtocol
@@ -80,34 +73,20 @@ final class POSTabEligibilityChecker: POSEntryPointEligibilityCheckerProtocol {
8073

8174
/// Determines whether the POS entry point can be shown based on the selected store and feature gates.
8275
func checkEligibility() async -> POSEligibilityState {
83-
switch checkDeviceEligibility() {
84-
case .eligible:
85-
break
86-
case .ineligible(let reason):
87-
return .ineligible(reason: reason)
76+
guard #available(iOS 17.0, *) else {
77+
return .ineligible(reason: .unsupportedIOSVersion)
8878
}
8979

9080
async let siteSettingsEligibility = checkSiteSettingsEligibility()
91-
async let featureFlagEligibility = checkRemoteFeatureEligibility()
9281
async let pluginEligibility = checkPluginEligibility()
9382

94-
// Checks site settings first since it's likely to complete fastest.
9583
switch await siteSettingsEligibility {
9684
case .eligible:
9785
break
9886
case .ineligible(let reason):
9987
return .ineligible(reason: reason)
10088
}
10189

102-
// Then checks feature flag.
103-
switch await featureFlagEligibility {
104-
case .eligible:
105-
break
106-
case .ineligible(let reason):
107-
return .ineligible(reason: reason)
108-
}
109-
110-
// Finally checks plugin eligibility.
11190
switch await pluginEligibility {
11291
case .eligible:
11392
return .eligible
@@ -118,55 +97,21 @@ final class POSTabEligibilityChecker: POSEntryPointEligibilityCheckerProtocol {
11897

11998
/// Checks the final visibility of the POS tab.
12099
func checkVisibility() async -> Bool {
121-
if featureFlagService.isFeatureFlagEnabled(.pointOfSaleAsATabi2) {
122-
return await checkVisibilityBasedOnCountryAndRemoteFeatureFlag()
123-
} else {
124-
let eligibility = await checkEligibility()
125-
return eligibility == .eligible
126-
}
127-
}
128-
}
129-
130-
private extension POSTabEligibilityChecker {
131-
func checkDeviceEligibility() -> POSEligibilityState {
132-
guard #available(iOS 17.0, *) else {
133-
return .ineligible(reason: .unsupportedIOSVersion)
134-
}
135-
136100
guard userInterfaceIdiom == .pad else {
137-
return .ineligible(reason: .notTablet)
138-
}
139-
140-
return .eligible
141-
}
142-
143-
func checkVisibilityBasedOnCountryAndRemoteFeatureFlag() async -> Bool {
144-
guard checkDeviceEligibility() == .eligible else {
145101
return false
146102
}
147103

148-
async let siteSettingsEligibility = checkSiteSettingsEligibility()
104+
async let siteSettingsEligibility = waitAndCheckSiteSettingsEligibility()
149105
async let featureFlagEligibility = checkRemoteFeatureEligibility()
150106

151-
self.siteSettingsEligibility = await siteSettingsEligibility
152107
switch await siteSettingsEligibility {
153-
case .eligible:
108+
case .ineligible(.unsupportedCountry):
109+
return false
110+
default:
154111
break
155-
case let .ineligible(reason):
156-
if case .unsupportedCurrency = reason {
157-
break
158-
} else {
159-
return false
160-
}
161112
}
162113

163-
self.featureFlagEligibility = await featureFlagEligibility
164-
switch await featureFlagEligibility {
165-
case .eligible:
166-
return true
167-
case .ineligible:
168-
return false
169-
}
114+
return await featureFlagEligibility == .eligible
170115
}
171116
}
172117

@@ -220,11 +165,36 @@ private extension POSTabEligibilityChecker {
220165
// MARK: - Site Settings Related Eligibility Check
221166

222167
private extension POSTabEligibilityChecker {
168+
enum SiteSettingsEligibilityState {
169+
case eligible
170+
case ineligible(reason: SiteSettingsIneligibleReason)
171+
}
172+
173+
enum SiteSettingsIneligibleReason {
174+
case siteSettingsNotAvailable
175+
case unsupportedCountry(supportedCountries: [CountryCode])
176+
case unsupportedCurrency(supportedCurrencies: [CurrencyCode])
177+
}
178+
223179
func checkSiteSettingsEligibility() async -> POSEligibilityState {
224-
if let siteSettingsEligibility {
225-
return siteSettingsEligibility
180+
let siteSettingsEligibility = await waitAndCheckSiteSettingsEligibility()
181+
switch siteSettingsEligibility {
182+
case .eligible:
183+
return .eligible
184+
case .ineligible(reason: let reason):
185+
switch reason {
186+
case .siteSettingsNotAvailable, .unsupportedCountry:
187+
// This is an edge case where the store country is expected to be eligible from the visilibity check, but site settings might have
188+
// changed to an unsupported country during the session. In this case, we return an ineligible reason that prompts the merchant to
189+
// relaunch the app.
190+
return .ineligible(reason: .siteSettingsNotAvailable)
191+
case let .unsupportedCurrency(supportedCurrencies: supportedCurrencies):
192+
return .ineligible(reason: .unsupportedCurrency(supportedCurrencies: supportedCurrencies))
193+
}
226194
}
195+
}
227196

197+
func waitAndCheckSiteSettingsEligibility() async -> SiteSettingsEligibilityState {
228198
// Waits for the first site settings that matches the given site ID.
229199
let siteSettings = await waitForSiteSettingsRefresh()
230200
guard siteSettings.isNotEmpty else {
@@ -249,7 +219,7 @@ private extension POSTabEligibilityChecker {
249219
return []
250220
}
251221

252-
func isEligibleFromCountryAndCurrencyCode(countryCode: CountryCode, currencyCode: CurrencyCode) -> POSEligibilityState {
222+
func isEligibleFromCountryAndCurrencyCode(countryCode: CountryCode, currencyCode: CurrencyCode) -> SiteSettingsEligibilityState {
253223
let supportedCountries: [CountryCode] = [.US, .GB]
254224
let supportedCurrencies: [CountryCode: [CurrencyCode]] = [.US: [.USD],
255225
.GB: [.GBP]]
@@ -270,15 +240,21 @@ private extension POSTabEligibilityChecker {
270240
// MARK: - Remote Feature Flag Eligibility Check
271241

272242
private extension POSTabEligibilityChecker {
273-
@MainActor
274-
func checkRemoteFeatureEligibility() async -> POSEligibilityState {
275-
if let featureFlagEligibility {
276-
return featureFlagEligibility
277-
}
243+
enum RemoteFeatureFlagEligibilityState: Equatable {
244+
case eligible
245+
case ineligible(reason: RemoteFeatureFlagIneligibleReason)
246+
}
247+
248+
enum RemoteFeatureFlagIneligibleReason: Equatable {
249+
case selfDeallocated
250+
case featureFlagDisabled
251+
}
278252

253+
@MainActor
254+
func checkRemoteFeatureEligibility() async -> RemoteFeatureFlagEligibilityState {
279255
// Only whitelisted accounts in WPCOM have the Point of Sale remote feature flag enabled. These can be found at D159901-code
280256
// If the account is whitelisted, then the remote value takes preference over the local feature flag configuration
281-
return await withCheckedContinuation { [weak self] continuation in
257+
await withCheckedContinuation { [weak self] continuation in
282258
guard let self else {
283259
return continuation.resume(returning: .ineligible(reason: .selfDeallocated))
284260
}

0 commit comments

Comments
 (0)