Skip to content

Commit e0f41f4

Browse files
Merge branch 'trunk' into task/15306-select-item
2 parents e067f4a + 638baa4 commit e0f41f4

File tree

44 files changed

+704
-181
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+704
-181
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/Controllers/PointOfSaleOrderController.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ protocol PointOfSaleOrderControllerProtocol {
2727
var orderState: PointOfSaleInternalOrderState { get }
2828

2929
@discardableResult
30-
func syncOrder(for cartProducts: [CartItem], retryHandler: @escaping () async -> Void) async -> Result<SyncOrderState, Error>
30+
func syncOrder(for cart: Cart, retryHandler: @escaping () async -> Void) async -> Result<SyncOrderState, Error>
3131
func sendReceipt(recipientEmail: String) async throws
3232
func clearOrder()
3333
func collectCashPayment() async throws
@@ -63,9 +63,9 @@ protocol PointOfSaleOrderControllerProtocol {
6363
private var order: Order? = nil
6464

6565
@MainActor @discardableResult
66-
func syncOrder(for cartItems: [CartItem],
66+
func syncOrder(for cart: Cart,
6767
retryHandler: @escaping () async -> Void) async -> Result<SyncOrderState, Error> {
68-
let posCartItems = cartItems.map {
68+
let posCartItems = cart.items.map {
6969
POSCartItem(item: $0.item, quantity: Decimal($0.quantity))
7070
}
7171

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import Foundation
2+
import protocol Yosemite.POSOrderableItem
3+
import enum Yosemite.POSItem
4+
5+
struct Cart {
6+
var items: [CartItem] = []
7+
var coupons: [CartCouponItem] = []
8+
}
9+
10+
struct CartItem {
11+
let id: UUID
12+
let item: POSOrderableItem
13+
let title: String
14+
let subtitle: String?
15+
let quantity: Int
16+
}
17+
18+
struct CartCouponItem {
19+
let id: UUID
20+
let code: String
21+
}
22+
23+
// MARK: - Helper Methods
24+
25+
extension Cart {
26+
mutating func add(_ posItem: POSItem) {
27+
switch posItem {
28+
case .simpleProduct(let simpleProduct):
29+
let productItem = CartItem(id: UUID(), item: simpleProduct, title: simpleProduct.name, subtitle: nil, quantity: 1)
30+
items.insert(productItem, at: items.startIndex)
31+
case .variation(let variation):
32+
let productItem = CartItem(id: UUID(), item: variation, title: variation.parentProductName, subtitle: variation.name, quantity: 1)
33+
items.insert(productItem, at: items.startIndex)
34+
case .variableParentProduct:
35+
return
36+
case .coupon(let coupon):
37+
let couponItem = CartCouponItem(id: UUID(), code: coupon.code)
38+
coupons.insert(couponItem, at: coupons.startIndex)
39+
}
40+
}
41+
42+
mutating func remove(_ cartItem: CartItem) {
43+
items.removeAll { $0.id == cartItem.id }
44+
}
45+
46+
mutating func remove(_ cartCouponItem: CartCouponItem) {
47+
coupons.removeAll { $0.id == cartCouponItem.id }
48+
}
49+
50+
mutating func removeAll() {
51+
items.removeAll()
52+
coupons.removeAll()
53+
}
54+
55+
var isEmpty: Bool {
56+
items.isEmpty && coupons.isEmpty
57+
}
58+
59+
var isNotEmpty: Bool {
60+
return !isEmpty
61+
}
62+
}

WooCommerce/Classes/POS/Models/CartItem.swift

Lines changed: 0 additions & 10 deletions
This file was deleted.

WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import protocol Yosemite.POSOrderableItem
66
import protocol WooFoundation.Analytics
77
import struct Yosemite.Order
88
import struct Yosemite.OrderItem
9-
import struct Yosemite.POSCartItem
9+
import struct Yosemite.POSCoupon
1010
import enum Yosemite.POSItem
1111
import enum Yosemite.SystemStatusAction
1212

@@ -28,9 +28,10 @@ protocol PointOfSaleAggregateModelProtocol {
2828
func loadItems(base: ItemListBaseItem) async
2929
func loadNextItems(base: ItemListBaseItem) async
3030

31-
var cart: [CartItem] { get }
31+
var cart: Cart { get }
3232
func addToCart(_ item: POSItem)
3333
func remove(cartItem: CartItem)
34+
func remove(cartCouponItem: CartCouponItem)
3435
func removeAllItemsFromCart()
3536
func addMoreToCart()
3637
func startNewCart()
@@ -52,14 +53,16 @@ protocol PointOfSaleAggregateModelProtocol {
5253
var cardPresentPaymentOnboardingViewModel: CardPresentPaymentsOnboardingViewModel?
5354
private var onOnboardingCancellation: (() -> Void)?
5455

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

57-
private(set) var cart: [CartItem] = []
58+
private(set) var cart: Cart = .init()
5859

5960
var orderState: PointOfSaleOrderState { orderController.orderState.externalState }
6061
private var internalOrderState: PointOfSaleInternalOrderState { orderController.orderState }
6162

63+
private var currentController: PointOfSaleItemsControllerProtocol
6264
private let itemsController: PointOfSaleItemsControllerProtocol
65+
private let couponsController: PointOfSaleItemsControllerProtocol
6366

6467
private let cardPresentPaymentService: CardPresentPaymentFacade
6568
private let orderController: PointOfSaleOrderControllerProtocol
@@ -72,12 +75,15 @@ protocol PointOfSaleAggregateModelProtocol {
7275
private var cancellables: Set<AnyCancellable> = []
7376

7477
init(itemsController: PointOfSaleItemsControllerProtocol,
78+
couponsController: PointOfSaleItemsControllerProtocol,
7579
cardPresentPaymentService: CardPresentPaymentFacade,
7680
orderController: PointOfSaleOrderControllerProtocol,
7781
analytics: Analytics = ServiceLocator.analytics,
7882
collectOrderPaymentAnalyticsTracker: POSCollectOrderPaymentAnalyticsTracking,
7983
paymentState: PointOfSalePaymentState = .card(.idle)) {
84+
self.currentController = itemsController // Default current controller set to products
8085
self.itemsController = itemsController
86+
self.couponsController = couponsController
8187
self.cardPresentPaymentService = cardPresentPaymentService
8288
self.orderController = orderController
8389
self.analytics = analytics
@@ -94,47 +100,39 @@ protocol PointOfSaleAggregateModelProtocol {
94100
extension PointOfSaleAggregateModel {
95101
@MainActor
96102
func loadItems(base: ItemListBaseItem) async {
97-
await itemsController.loadItems(base: base)
103+
await currentController.loadItems(base: base)
98104
}
99105

100106
@MainActor
101107
func refreshItems(base: ItemListBaseItem) async {
102-
await itemsController.refreshItems(base: base)
108+
await currentController.refreshItems(base: base)
103109
}
104110

105111
@MainActor
106112
func loadNextItems(base: ItemListBaseItem) async {
107-
await itemsController.loadNextItems(base: base)
113+
await currentController.loadNextItems(base: base)
108114
}
109-
}
110-
111-
// MARK: - Cart
112115

113-
private extension POSItem {
114-
var cartItem: CartItem? {
115-
switch self {
116-
case .simpleProduct(let simpleProduct):
117-
return CartItem(id: UUID(), item: simpleProduct, title: simpleProduct.name, subtitle: nil, quantity: 1)
118-
case .variation(let variation):
119-
return CartItem(id: UUID(), item: variation, title: variation.parentProductName, subtitle: variation.name, quantity: 1)
120-
case .variableParentProduct:
121-
return nil
122-
case .coupon:
123-
return nil
124-
}
116+
func switchToItemType(_ type: ItemType) async {
117+
let newController = type == .products ? itemsController : couponsController
118+
currentController = newController
119+
await refreshItems(base: .root)
125120
}
126121
}
127122

128123
@available(iOS 17.0, *)
129124
extension PointOfSaleAggregateModel {
130125
func addToCart(_ item: POSItem) {
131126
trackCustomerInteractionStarted()
132-
guard let cartItem = item.cartItem else { return }
133-
cart.insert(cartItem, at: cart.startIndex)
127+
cart.add(item)
134128
}
135129

136130
func remove(cartItem: CartItem) {
137-
cart.removeAll(where: { $0.id == cartItem.id } )
131+
cart.remove(cartItem)
132+
}
133+
134+
func remove(cartCouponItem: CartCouponItem) {
135+
cart.remove(cartCouponItem)
138136
}
139137

140138
func removeAllItemsFromCart() {
@@ -164,7 +162,7 @@ private extension PointOfSaleAggregateModel {
164162
func trackCustomerInteractionStarted() {
165163
// At the moment we're assumming that an interaction starts simply when the cart is zero
166164
// but a more complex logic will be needed for other cases
167-
if cart.count == 0 {
165+
if cart.isEmpty {
168166
collectOrderPaymentAnalyticsTracker.trackCustomerInteractionStarted()
169167
}
170168
}

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())

0 commit comments

Comments
 (0)