Skip to content

Commit 3318c19

Browse files
committed
Move POS eligibility state based UI from PointOfSaleEntryPointView to PointOfSaleDashboardView to enable continuous loading animation. UI state is determined by PointOfSaleDashboardViewHelper.
1 parent bf13f8b commit 3318c19

File tree

9 files changed

+389
-64
lines changed

9 files changed

+389
-64
lines changed

WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ protocol PointOfSaleAggregateModelProtocol {
6666
var orderState: PointOfSaleOrderState { orderController.orderState.externalState }
6767
private var internalOrderState: PointOfSaleInternalOrderState { orderController.orderState }
6868

69+
let entryPointController: POSEntryPointController
6970
let purchasableItemsController: PointOfSaleItemsControllerProtocol
7071
let purchasableItemsSearchController: PointOfSaleSearchingItemsControllerProtocol
7172
let popularPurchasableItemsController: PointOfSaleItemsControllerProtocol
@@ -99,7 +100,8 @@ protocol PointOfSaleAggregateModelProtocol {
99100
_viewStateCoordinator
100101
}
101102

102-
init(itemsController: PointOfSaleItemsControllerProtocol,
103+
init(entryPointController: POSEntryPointController,
104+
itemsController: PointOfSaleItemsControllerProtocol,
103105
purchasableItemsSearchController: PointOfSaleSearchingItemsControllerProtocol,
104106
couponsController: PointOfSaleCouponsControllerProtocol,
105107
couponsSearchController: PointOfSaleSearchingItemsControllerProtocol,
@@ -112,6 +114,7 @@ protocol PointOfSaleAggregateModelProtocol {
112114
barcodeScanService: PointOfSaleBarcodeScanServiceProtocol,
113115
soundPlayer: PointOfSaleSoundPlayerProtocol = PointOfSaleSoundPlayer(),
114116
paymentState: PointOfSalePaymentState = .idle) {
117+
self.entryPointController = entryPointController
115118
self.purchasableItemsController = itemsController
116119
self.purchasableItemsSearchController = purchasableItemsSearchController
117120
self.couponsController = couponsController

WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -32,35 +32,55 @@ struct PointOfSaleDashboardView: View {
3232
}
3333
}
3434

35+
// MARK: View State
36+
37+
enum ViewState: Equatable {
38+
case loading
39+
case ineligible(reason: POSIneligibleReason)
40+
case error(PointOfSaleErrorState)
41+
case content
42+
case unsupportedWidth
43+
}
44+
45+
private var viewState: ViewState {
46+
PointOfSaleDashboardViewHelper.determineViewState(
47+
eligibilityState: posModel.entryPointController.eligibilityState,
48+
itemsContainerState: itemsViewState.containerState,
49+
horizontalSizeClass: horizontalSizeClass
50+
)
51+
}
52+
3553
var body: some View {
3654
@Bindable var posModel = posModel
3755
ZStack(alignment: .bottomLeading) {
38-
if case .regular = horizontalSizeClass {
39-
switch itemsViewState.containerState {
40-
case .loading:
41-
PointOfSaleLoadingView()
42-
.transition(.opacity)
43-
.ignoresSafeArea()
44-
case .error(let error):
45-
PointOfSaleItemListFullscreenErrorView(error: error, onAction: {
46-
Task {
47-
switch viewStateCoordinator.selectedItemListType {
48-
case .products(search: false):
49-
await posModel.purchasableItemsController.loadItems(base: .root)
50-
case .products(search: true):
51-
await posModel.purchasableItemsSearchController.loadItems(base: .root)
52-
case .coupons(search: false):
53-
await posModel.couponsSearchController.loadItems(base: .root)
54-
case .coupons(search: true):
55-
await posModel.couponsSearchController.loadItems(base: .root)
56-
}
56+
switch viewState {
57+
case .loading:
58+
PointOfSaleLoadingView()
59+
.transition(.opacity)
60+
.ignoresSafeArea()
61+
case .ineligible(let reason):
62+
POSIneligibleView(reason: reason, onRefresh: {
63+
try await posModel.entryPointController.refreshEligibility(reason: reason)
64+
})
65+
case .error(let error):
66+
PointOfSaleItemListFullscreenErrorView(error: error, onAction: {
67+
Task {
68+
switch viewStateCoordinator.selectedItemListType {
69+
case .products(search: false):
70+
await posModel.purchasableItemsController.loadItems(base: .root)
71+
case .products(search: true):
72+
await posModel.purchasableItemsSearchController.loadItems(base: .root)
73+
case .coupons(search: false):
74+
await posModel.couponsSearchController.loadItems(base: .root)
75+
case .coupons(search: true):
76+
await posModel.couponsSearchController.loadItems(base: .root)
5777
}
58-
})
59-
case .content:
60-
contentView
61-
.accessibilitySortPriority(2)
62-
}
63-
} else {
78+
}
79+
})
80+
case .content:
81+
contentView
82+
.accessibilitySortPriority(2)
83+
case .unsupportedWidth:
6484
PointOfSaleUnsupportedWidthView()
6585
.transition(.opacity)
6686
.ignoresSafeArea()
@@ -73,15 +93,15 @@ struct PointOfSaleDashboardView: View {
7393
.padding(.bottom, Constants.floatingControlBottomPadding)
7494
.trackSize(size: $floatingSize)
7595
.accessibilitySortPriority(1)
76-
.renderedIf(itemsViewState.containerState != .loading)
96+
.renderedIf(viewState != .loading)
7797

7898
POSConnectivityView()
7999
}
80100
.environment(\.floatingControlAreaSize,
81101
CGSizeMake(floatingSize.width + Constants.floatingControlHorizontalOffset,
82102
floatingSize.height + Constants.floatingControlVerticalOffset))
83103
.environment(\.posBackgroundAppearance, backgroundAppearance)
84-
.animation(.easeInOut, value: itemsViewState.containerState == .loading)
104+
.animation(.easeInOut, value: viewState == .loading)
85105
.background(Color.posSurface)
86106
.navigationBarBackButtonHidden(true)
87107
.posModal(item: $posModel.cardPresentPaymentOnboardingViewModel, onDismiss: {
@@ -108,10 +128,13 @@ struct PointOfSaleDashboardView: View {
108128
.sheet(isPresented: $showDocumentation) {
109129
documentationView
110130
}
111-
.task {
112-
await posModel.purchasableItemsController.loadItems(base: .root)
113-
await posModel.couponsController.loadItems(base: .root)
114-
await posModel.popularPurchasableItemsController.loadItems(base: .root)
131+
.onChange(of: posModel.entryPointController.eligibilityState) { oldValue, newValue in
132+
guard newValue == .eligible else { return }
133+
Task { @MainActor in
134+
await posModel.purchasableItemsController.loadItems(base: .root)
135+
await posModel.couponsController.loadItems(base: .root)
136+
await posModel.popularPurchasableItemsController.loadItems(base: .root)
137+
}
115138
}
116139
.ignoresSafeArea(.keyboard)
117140
}

WooCommerce/Classes/POS/Presentation/PointOfSaleEntryPointView.swift

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,19 @@ struct PointOfSaleEntryPointView: View {
5050

5151
var body: some View {
5252
Group {
53-
switch posEntryPointController.eligibilityState {
54-
case .none:
53+
if let posModel {
54+
PointOfSaleDashboardView()
55+
.environment(posModel)
56+
} else {
5557
PointOfSaleLoadingView()
56-
case .eligible:
57-
if let posModel = posModel {
58-
PointOfSaleDashboardView()
59-
.environment(posModel)
60-
} else {
61-
PointOfSaleLoadingView()
62-
}
63-
case let .ineligible(reason):
64-
POSIneligibleView(reason: reason, onRefresh: {
65-
try await posEntryPointController.refreshEligibility(reason: reason)
66-
})
6758
}
6859
}
6960
.task {
7061
// We create the posModel in a task, not init, to avoid creating multiple copies during the view's lifecycle.
7162
// Confusingly, init can be called more than once, but `task` matches the lifecycle.
7263
// See https://developer.apple.com/documentation/swiftui/state#Store-observable-objects for details.
7364
posModel = PointOfSaleAggregateModel(
65+
entryPointController: posEntryPointController,
7466
itemsController: itemsController,
7567
purchasableItemsSearchController: purchasableItemsSearchController,
7668
couponsController: couponsController,

WooCommerce/Classes/POS/Utils/PreviewHelpers.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ struct POSPreviewHelpers {
219219
barcodeScanService: PointOfSaleBarcodeScanServiceProtocol = PointOfSalePreviewBarcodeScanService()
220220
) -> PointOfSaleAggregateModel {
221221
return PointOfSaleAggregateModel(
222+
entryPointController: POSEntryPointController(eligibilityChecker: LegacyPOSTabEligibilityChecker(siteID: 0)),
222223
itemsController: itemsController,
223224
purchasableItemsSearchController: purchasableItemsSearchController,
224225
couponsController: couponsController,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import Foundation
2+
import SwiftUI
3+
4+
@available(iOS 17.0, *)
5+
struct PointOfSaleDashboardViewHelper {
6+
static func determineViewState(
7+
eligibilityState: POSEligibilityState?,
8+
itemsContainerState: ItemsContainerState,
9+
horizontalSizeClass: UserInterfaceSizeClass?
10+
) -> PointOfSaleDashboardView.ViewState {
11+
guard case .regular = horizontalSizeClass else {
12+
return .unsupportedWidth
13+
}
14+
15+
guard let eligibilityState else {
16+
return .loading
17+
}
18+
19+
switch eligibilityState {
20+
case .eligible:
21+
switch itemsContainerState {
22+
case .loading:
23+
return .loading
24+
case .error(let error):
25+
return .error(error)
26+
case .content:
27+
return .content
28+
}
29+
case .ineligible(let reason):
30+
return .ineligible(reason: reason)
31+
}
32+
}
33+
}

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,8 @@
359359
026826C22BF59E410036F959 /* PointOfSaleCardPresentPaymentRequiredReaderUpdateInProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026826B92BF59E400036F959 /* PointOfSaleCardPresentPaymentRequiredReaderUpdateInProgressView.swift */; };
360360
026826C42BF59E410036F959 /* PointOfSaleCardPresentPaymentFoundReaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026826BB2BF59E410036F959 /* PointOfSaleCardPresentPaymentFoundReaderView.swift */; };
361361
026826C72BF59E410036F959 /* PointOfSaleCardPresentPaymentScanningForReadersView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026826BE2BF59E410036F959 /* PointOfSaleCardPresentPaymentScanningForReadersView.swift */; };
362+
026878D62E293E7C00DBFD34 /* PointOfSaleDashboardViewHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026878D52E293E7300DBFD34 /* PointOfSaleDashboardViewHelper.swift */; };
363+
026878D82E2942E400DBFD34 /* PointOfSaleDashboardViewHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 026878D72E2942E200DBFD34 /* PointOfSaleDashboardViewHelperTests.swift */; };
362364
02691780232600A6002AFC20 /* ProductsTabProductViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0269177F232600A6002AFC20 /* ProductsTabProductViewModelTests.swift */; };
363365
02691782232605B9002AFC20 /* PaginatedListViewControllerStateCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02691781232605B9002AFC20 /* PaginatedListViewControllerStateCoordinatorTests.swift */; };
364366
0269576A23726304001BA0BF /* KeyboardFrameObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0269576923726304001BA0BF /* KeyboardFrameObserver.swift */; };
@@ -3528,6 +3530,8 @@
35283530
026826B92BF59E400036F959 /* PointOfSaleCardPresentPaymentRequiredReaderUpdateInProgressView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentRequiredReaderUpdateInProgressView.swift; sourceTree = "<group>"; };
35293531
026826BB2BF59E410036F959 /* PointOfSaleCardPresentPaymentFoundReaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentFoundReaderView.swift; sourceTree = "<group>"; };
35303532
026826BE2BF59E410036F959 /* PointOfSaleCardPresentPaymentScanningForReadersView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PointOfSaleCardPresentPaymentScanningForReadersView.swift; sourceTree = "<group>"; };
3533+
026878D52E293E7300DBFD34 /* PointOfSaleDashboardViewHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleDashboardViewHelper.swift; sourceTree = "<group>"; };
3534+
026878D72E2942E200DBFD34 /* PointOfSaleDashboardViewHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointOfSaleDashboardViewHelperTests.swift; sourceTree = "<group>"; };
35313535
0269177F232600A6002AFC20 /* ProductsTabProductViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsTabProductViewModelTests.swift; sourceTree = "<group>"; };
35323536
02691781232605B9002AFC20 /* PaginatedListViewControllerStateCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaginatedListViewControllerStateCoordinatorTests.swift; sourceTree = "<group>"; };
35333537
0269576923726304001BA0BF /* KeyboardFrameObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardFrameObserver.swift; sourceTree = "<group>"; };
@@ -7085,6 +7089,7 @@
70857089
026826912BF59D7A0036F959 /* ViewHelpers */ = {
70867090
isa = PBXGroup;
70877091
children = (
7092+
026878D52E293E7300DBFD34 /* PointOfSaleDashboardViewHelper.swift */,
70887093
6891C3632D364AFB00B5B48C /* CollectCashViewHelper.swift */,
70897094
6885E2CB2C32B14B004C8D70 /* TotalsViewHelper.swift */,
70907095
6837631B2C2E847D00AD51D0 /* CartViewHelper.swift */,
@@ -13016,6 +13021,7 @@
1301613021
children = (
1301713022
683763192C2E6F5900AD51D0 /* CartViewHelperTests.swift */,
1301813023
6891C3652D364C1A00B5B48C /* CollectCashViewHelperTests.swift */,
13024+
026878D72E2942E200DBFD34 /* PointOfSaleDashboardViewHelperTests.swift */,
1301913025
208628732D48E4CB003F45DC /* TotalsViewHelperTests.swift */,
1302013026
);
1302113027
path = ViewHelpers;
@@ -16278,6 +16284,7 @@
1627816284
68E674AD2A4DAC010034BA1E /* CurrentPlanDetailsView.swift in Sources */,
1627916285
20134CE82D4D38E000076A80 /* CardPresentPaymentPlugin+SetUpTapToPay.swift in Sources */,
1628016286
DE68B81F26F86B1700C86CFB /* OfflineBannerView.swift in Sources */,
16287+
026878D62E293E7C00DBFD34 /* PointOfSaleDashboardViewHelper.swift in Sources */,
1628116288
B90D21802D1ED1F300ED60ED /* WooShippingCustomsItemOriginCountryInfoDialog.swift in Sources */,
1628216289
D8610BCC256F284700A5DF27 /* ULErrorViewModel.swift in Sources */,
1628316290
CCFC50552743BC0D001E505F /* OrderForm.swift in Sources */,
@@ -16930,6 +16937,7 @@
1693016937
019130212CF5B0FF008C0C88 /* TapToPayEducationViewModelTests.swift in Sources */,
1693116938
026A50302D2F80B5002C42C2 /* ThresholdInfiniteScrollTriggerDeterminerTests.swift in Sources */,
1693216939
CC33238C29CDF67D00CA9709 /* ComponentSettingsViewModelTests.swift in Sources */,
16940+
026878D82E2942E400DBFD34 /* PointOfSaleDashboardViewHelperTests.swift in Sources */,
1693316941
86F5FFE42CA30D9200C767C4 /* CustomFieldsListViewModelTests.swift in Sources */,
1693416942
01AB2D162DDC8CDA00AA67FD /* MockAnalytics.swift in Sources */,
1693516943
0261F5A728D454CF00B7AC72 /* ProductSearchUICommandTests.swift in Sources */,

0 commit comments

Comments
 (0)