Skip to content

Commit 638baa4

Browse files
authored
[Woo POS] Allow item list switch between Products and Coupons (#15366)
2 parents f4a6ae2 + 249f75f commit 638baa4

27 files changed

+251
-52
lines changed

Experiments/Experiments/DefaultFeatureFlagService.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public struct DefaultFeatureFlagService: FeatureFlagService {
7272
case .pointOfSale:
7373
return buildConfig == .localDeveloper || buildConfig == .alpha
7474
case .enableCouponsInPointOfSale:
75-
return buildConfig == .localDeveloper || buildConfig == .alpha
75+
return false
7676
case .googleAdsCampaignCreationOnWebView:
7777
return true
7878
case .backgroundTasks:
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import Observation
2+
import enum Yosemite.POSItem
3+
import protocol Yosemite.PointOfSaleItemServiceProtocol
4+
5+
@available(iOS 17.0, *)
6+
@Observable final class PointOfSaleCouponsController: PointOfSaleItemsControllerProtocol {
7+
var itemsViewState: ItemsViewState = ItemsViewState(containerState: .loading,
8+
itemsStack: ItemsStackState(root: .loading([]),
9+
itemStates: [:]))
10+
private let paginationTracker: AsyncPaginationTracker
11+
private var childPaginationTrackers: [POSItem: AsyncPaginationTracker] = [:]
12+
private let itemProvider: PointOfSaleItemServiceProtocol
13+
14+
init(itemProvider: PointOfSaleItemServiceProtocol) {
15+
self.itemProvider = itemProvider
16+
self.paginationTracker = .init()
17+
}
18+
19+
@MainActor
20+
func loadItems(base: ItemListBaseItem) async {
21+
debugPrint("🍍 CouponsController::loadItems called")
22+
itemsViewState = ItemsViewState(containerState: .content, itemsStack: .init(root: .loaded([], hasMoreItems: false), itemStates: [:]))
23+
}
24+
25+
func refreshItems(base: ItemListBaseItem) async {
26+
debugPrint("🍍 CouponsController::refreshItems called")
27+
itemsViewState = ItemsViewState(containerState: .content, itemsStack: .init(root: .loaded([], hasMoreItems: false), itemStates: [:]))
28+
}
29+
30+
func loadNextItems(base: ItemListBaseItem) async {
31+
debugPrint("🍍 CouponsController::loadNextItems called")
32+
itemsViewState = ItemsViewState(containerState: .content, itemsStack: .init(root: .loaded([], hasMoreItems: false), itemStates: [:]))
33+
}
34+
}

WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,14 @@ import enum Yosemite.PointOfSaleItemServiceError
66
import struct Yosemite.POSVariableParentProduct
77
import class Yosemite.Store
88

9+
enum ItemType {
10+
case products
11+
case coupons
12+
}
13+
914
@available(iOS 17.0, *)
1015
protocol PointOfSaleItemsControllerProtocol {
16+
///
1117
var itemsViewState: ItemsViewState { get }
1218
/// Loads the first page of items for a given base item.
1319
func loadItems(base: ItemListBaseItem) async
@@ -17,6 +23,8 @@ protocol PointOfSaleItemsControllerProtocol {
1723
func loadNextItems(base: ItemListBaseItem) async
1824
}
1925

26+
27+
2028
@available(iOS 17.0, *)
2129
@Observable final class PointOfSaleItemsController: PointOfSaleItemsControllerProtocol {
2230
var itemsViewState: ItemsViewState = ItemsViewState(containerState: .loading,
@@ -158,14 +166,6 @@ protocol PointOfSaleItemsControllerProtocol {
158166
}
159167
}
160168

161-
@available(iOS 17.0, *)
162-
private extension PointOfSaleItemsController {
163-
func loadPointOfSaleCoupons() {
164-
let posCoupons = itemProvider.providePointOfSaleCoupons()
165-
debugPrint(posCoupons)
166-
}
167-
}
168-
169169
@available(iOS 17.0, *)
170170
private extension PointOfSaleItemsController {
171171
func setLoadingState(base: ItemListBaseItem) {
@@ -202,6 +202,7 @@ private extension PointOfSaleItemsController {
202202
func fetchItems(pageNumber: Int, appendToExistingItems: Bool = true) async throws -> Bool {
203203
do {
204204
let pagedItems = try await itemProvider.providePointOfSaleItems(pageNumber: pageNumber)
205+
205206
let newItems = pagedItems.items
206207
var allItems = appendToExistingItems ? itemsViewState.itemsStack.root.items : []
207208
let uniqueNewItems = newItems.filter { newItem in

WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,16 @@ protocol PointOfSaleAggregateModelProtocol {
5353
var cardPresentPaymentOnboardingViewModel: CardPresentPaymentsOnboardingViewModel?
5454
private var onOnboardingCancellation: (() -> Void)?
5555

56-
var itemsViewState: ItemsViewState { itemsController.itemsViewState }
56+
var itemsViewState: ItemsViewState { currentController.itemsViewState }
5757

5858
private(set) var cart: Cart = .init()
5959

6060
var orderState: PointOfSaleOrderState { orderController.orderState.externalState }
6161
private var internalOrderState: PointOfSaleInternalOrderState { orderController.orderState }
6262

63+
private var currentController: PointOfSaleItemsControllerProtocol
6364
private let itemsController: PointOfSaleItemsControllerProtocol
65+
private let couponsController: PointOfSaleItemsControllerProtocol
6466

6567
private let cardPresentPaymentService: CardPresentPaymentFacade
6668
private let orderController: PointOfSaleOrderControllerProtocol
@@ -73,12 +75,15 @@ protocol PointOfSaleAggregateModelProtocol {
7375
private var cancellables: Set<AnyCancellable> = []
7476

7577
init(itemsController: PointOfSaleItemsControllerProtocol,
78+
couponsController: PointOfSaleItemsControllerProtocol,
7679
cardPresentPaymentService: CardPresentPaymentFacade,
7780
orderController: PointOfSaleOrderControllerProtocol,
7881
analytics: Analytics = ServiceLocator.analytics,
7982
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalyticsTracking,
8083
paymentState: PointOfSalePaymentState = .card(.idle)) {
84+
self.currentController = itemsController // Default current controller set to products
8185
self.itemsController = itemsController
86+
self.couponsController = couponsController
8287
self.cardPresentPaymentService = cardPresentPaymentService
8388
self.orderController = orderController
8489
self.analytics = analytics
@@ -95,17 +100,23 @@ protocol PointOfSaleAggregateModelProtocol {
95100
extension PointOfSaleAggregateModel {
96101
@MainActor
97102
func loadItems(base: ItemListBaseItem) async {
98-
await itemsController.loadItems(base: base)
103+
await currentController.loadItems(base: base)
99104
}
100105

101106
@MainActor
102107
func refreshItems(base: ItemListBaseItem) async {
103-
await itemsController.refreshItems(base: base)
108+
await currentController.refreshItems(base: base)
104109
}
105110

106111
@MainActor
107112
func loadNextItems(base: ItemListBaseItem) async {
108-
await itemsController.loadNextItems(base: base)
113+
await currentController.loadNextItems(base: base)
114+
}
115+
116+
func switchToItemType(_ type: ItemType) async {
117+
let newController = type == .products ? itemsController : couponsController
118+
currentController = newController
119+
await refreshItems(base: .root)
109120
}
110121
}
111122

WooCommerce/Classes/POS/Presentation/CardReaderConnection/CardReaderConnectionStatusView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ private extension CardReaderConnectionStatusView {
171171
#Preview {
172172
let posModel = PointOfSaleAggregateModel(
173173
itemsController: PointOfSalePreviewItemsController(),
174+
couponsController: PointOfSalePreviewItemsController(),
174175
cardPresentPaymentService: CardPresentPaymentPreviewService(),
175176
orderController: PointOfSalePreviewOrderController(),
176177
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics()

WooCommerce/Classes/POS/Presentation/CardReaderConnection/UI States/Reader Messages/PointOfSalePaymentSuccessView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ private extension PointOfSalePaymentSuccessView {
125125
#Preview {
126126
let posModel = PointOfSaleAggregateModel(
127127
itemsController: PointOfSalePreviewItemsController(),
128+
couponsController: PointOfSalePreviewItemsController(),
128129
cardPresentPaymentService: CardPresentPaymentPreviewService(),
129130
orderController: PointOfSalePreviewOrderController(),
130131
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics())

WooCommerce/Classes/POS/Presentation/CartView.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ private extension CartView {
302302
#Preview {
303303
let posModel = PointOfSaleAggregateModel(
304304
itemsController: PointOfSalePreviewItemsController(),
305+
couponsController: PointOfSalePreviewItemsController(),
305306
cardPresentPaymentService: CardPresentPaymentPreviewService(),
306307
orderController: PointOfSalePreviewOrderController(),
307308
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics())
@@ -313,6 +314,7 @@ private extension CartView {
313314
#Preview("Cart with one item") {
314315
let posModel = PointOfSaleAggregateModel(
315316
itemsController: PointOfSalePreviewItemsController(),
317+
couponsController: PointOfSalePreviewItemsController(),
316318
cardPresentPaymentService: CardPresentPaymentPreviewService(),
317319
orderController: PointOfSalePreviewOrderController(),
318320
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics())

WooCommerce/Classes/POS/Presentation/Item Selector/ChildItemList.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ private extension ChildItemList {
147147
], hasMoreItems: false)]))
148148
let posModel = PointOfSaleAggregateModel(
149149
itemsController: itemsController,
150+
couponsController: PointOfSalePreviewItemsController(),
150151
cardPresentPaymentService: CardPresentPaymentPreviewService(),
151152
orderController: PointOfSalePreviewOrderController(),
152153
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics())
@@ -172,6 +173,7 @@ private extension ChildItemList {
172173
]))
173174
let posModel = PointOfSaleAggregateModel(
174175
itemsController: itemsController,
176+
couponsController: PointOfSalePreviewItemsController(),
175177
cardPresentPaymentService: CardPresentPaymentPreviewService(),
176178
orderController: PointOfSalePreviewOrderController(),
177179
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics())

WooCommerce/Classes/POS/Presentation/Item Selector/ItemList.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ private extension ItemListRow {
167167
#Preview("Loading") {
168168
let posModel = PointOfSaleAggregateModel(
169169
itemsController: PointOfSalePreviewItemsController(),
170+
couponsController: PointOfSalePreviewItemsController(),
170171
cardPresentPaymentService: CardPresentPaymentPreviewService(),
171172
orderController: PointOfSalePreviewOrderController(),
172173
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics())

WooCommerce/Classes/POS/Presentation/ItemListView.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import SwiftUI
22
import enum Yosemite.POSItem
3+
import protocol Yosemite.POSOrderableItem
4+
import struct Yosemite.POSCoupon
35

46
@available(iOS 17.0, *)
57
struct ItemListView: View {
@@ -15,6 +17,12 @@ struct ItemListView: View {
1517
@AppStorage(BannerState.isSimpleProductsOnlyBannerDismissedKey)
1618
private var isHeaderBannerDismissed: Bool = false
1719

20+
private var shouldShowCoupons: Bool {
21+
ServiceLocator.featureFlagService.isFeatureFlagEnabled(.enableCouponsInPointOfSale)
22+
}
23+
24+
@State private var selectedItemType: ItemType = .products
25+
1826
var body: some View {
1927
if #available(iOS 18.0, *) {
2028
NavigationStack {
@@ -32,6 +40,21 @@ struct ItemListView: View {
3240
var content: some View {
3341
VStack {
3442
headerView
43+
44+
HStack {
45+
Button(action: {
46+
displayItemType(.products)
47+
}, label: {
48+
Text("Products")
49+
})
50+
Button(action: {
51+
displayItemType(.coupons)
52+
}, label: {
53+
Text("Coupons")
54+
})
55+
}
56+
.renderedIf(shouldShowCoupons)
57+
3558
switch itemListState {
3659
case .loading(let items),
3760
.loaded(let items, _),
@@ -138,6 +161,13 @@ private extension ItemListView {
138161
var shouldShowHeaderBanner: Bool {
139162
itemListState.eligibleToShowSimpleProductsBanner && !isHeaderBannerDismissed
140163
}
164+
165+
func displayItemType(_ itemType: ItemType) {
166+
selectedItemType = itemType
167+
Task { @MainActor in
168+
await posModel.switchToItemType(itemType)
169+
}
170+
}
141171
}
142172

143173
private extension ItemListState {
@@ -222,6 +252,7 @@ private extension ItemListView {
222252
}
223253
let posModel = PointOfSaleAggregateModel(
224254
itemsController: itemsController,
255+
couponsController: PointOfSalePreviewItemsController(),
225256
cardPresentPaymentService: CardPresentPaymentPreviewService(),
226257
orderController: PointOfSalePreviewOrderController(),
227258
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics())
@@ -233,6 +264,7 @@ private extension ItemListView {
233264
#Preview("Loading") {
234265
let posModel = PointOfSaleAggregateModel(
235266
itemsController: PointOfSalePreviewItemsController(),
267+
couponsController: PointOfSalePreviewItemsController(),
236268
cardPresentPaymentService: CardPresentPaymentPreviewService(),
237269
orderController: PointOfSalePreviewOrderController(),
238270
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalytics())

0 commit comments

Comments
 (0)