Skip to content

Commit 7be476b

Browse files
authored
[Woo POS] Coupons: List - Enable Coupons - Wrap Up (Designer UI and Copy) (#15505)
2 parents 104f8c9 + 55df609 commit 7be476b

File tree

11 files changed

+158
-64
lines changed

11 files changed

+158
-64
lines changed

WooCommerce/Classes/POS/Controllers/PointOfSaleCouponsController.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ import enum Yosemite.PointOfSaleCouponServiceError
44
import protocol Yosemite.PointOfSaleItemServiceProtocol
55
import protocol Yosemite.PointOfSaleCouponServiceProtocol
66

7+
@available(iOS 17.0, *)
8+
protocol PointOfSaleCouponsControllerProtocol: PointOfSaleItemsControllerProtocol {
9+
/// Enables coupons in store settings
10+
/// Returns true if coupons enabled
11+
func enableCoupons() async
12+
}
13+
714
@available(iOS 17.0, *)
815
@Observable final class PointOfSaleCouponsController: PointOfSaleCouponsControllerProtocol {
916
var itemsViewState: ItemsViewState = ItemsViewState(containerState: .content,
@@ -55,10 +62,11 @@ import protocol Yosemite.PointOfSaleCouponServiceProtocol
5562
itemsViewState.itemsStack.root = .loading([])
5663
do {
5764
try await couponProvider.enableCoupons()
65+
await loadItems(base: .root)
5866
} catch {
59-
// TODO: WOOMOB-267
60-
// Handle error when failed to enable, and allow retry action
61-
debugPrint(error)
67+
if let couponError = error as? PointOfSaleCouponServiceError {
68+
setCouponsErrorViewState(couponError)
69+
}
6270
}
6371
}
6472
}
@@ -129,6 +137,8 @@ private extension PointOfSaleCouponsController {
129137
stackState = ItemsStackState(root: .error(.errorOnLoadingCoupons), itemStates: [:])
130138
case .couponsDisabled:
131139
stackState = ItemsStackState(root: .error(.errorCouponsDisabled), itemStates: [:])
140+
case .couponsEnablingError:
141+
stackState = ItemsStackState(root: .error(.errorOnEnablingCoupons), itemStates: [:])
132142
}
133143

134144
itemsViewState = ItemsViewState(containerState: containerState, itemsStack: stackState)

WooCommerce/Classes/POS/Controllers/PointOfSaleItemsController.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,6 @@ protocol PointOfSaleItemsControllerProtocol {
2626
func loadNextItems(base: ItemListBaseItem) async
2727
}
2828

29-
@available(iOS 17.0, *)
30-
protocol PointOfSaleCouponsControllerProtocol: PointOfSaleItemsControllerProtocol {
31-
/// Enables coupons in store settings, if needed
32-
func enableCoupons() async
33-
}
34-
3529
@available(iOS 17.0, *)
3630
protocol PointOfSaleSearchingItemsControllerProtocol: PointOfSaleItemsControllerProtocol {
3731
/// Searches for items

WooCommerce/Classes/POS/Models/ItemListState.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,42 @@ enum ItemListState {
1515
return false
1616
}
1717
}
18+
19+
var isLoaded: Bool {
20+
switch self {
21+
case .loaded:
22+
return true
23+
default:
24+
return false
25+
}
26+
}
27+
28+
var isInlineError: Bool {
29+
switch self {
30+
case .inlineError:
31+
return true
32+
default:
33+
return false
34+
}
35+
}
36+
37+
var isError: Bool {
38+
switch self {
39+
case .error:
40+
return true
41+
default:
42+
return false
43+
}
44+
}
45+
46+
var isEmpty: Bool {
47+
switch self {
48+
case .empty:
49+
return true
50+
default:
51+
return false
52+
}
53+
}
1854
}
1955

2056
extension ItemListState {

WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,6 @@ extension PointOfSaleAggregateModel {
136136
}
137137
}
138138

139-
// MARK: - Coupons
140-
@available(iOS 17.0, *)
141-
extension PointOfSaleAggregateModel {
142-
func enableCoupons() async {
143-
await couponsController.enableCoupons()
144-
}
145-
}
146-
147139
// MARK: - Track events
148140
@available(iOS 17.0, *)
149141
private extension PointOfSaleAggregateModel {

WooCommerce/Classes/POS/Models/PointOfSaleErrorState.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ struct PointOfSaleErrorState: Equatable {
5656
buttonText: Constants.loadingCouponsErrorRetry)
5757
}
5858

59+
static var errorOnEnablingCoupons: Self {
60+
PointOfSaleErrorState(
61+
errorType: .couponsDisabled,
62+
title: Constants.enablingCouponsErrorTitle,
63+
subtitle: Constants.enablingCouponsErrorSubtitle,
64+
buttonText: Constants.enablingCouponsErrorRetry)
65+
}
66+
5967
static var errorCouponsDisabled: Self {
6068
PointOfSaleErrorState(
6169
errorType: .couponsDisabled,
@@ -161,5 +169,21 @@ struct PointOfSaleErrorState: Equatable {
161169
comment: "Text for the button appearing on the item list screen when there's an error loading a page of " +
162170
"variations after the first. Shown inline with the previously loaded items above."
163171
)
172+
173+
static let enablingCouponsErrorTitle = NSLocalizedString(
174+
"pos.itemList.enablingCouponsErrorTitle",
175+
value: "Error enabling coupons",
176+
comment: "Title appearing on the coupon list screen when there's an error enabling coupons setting in the store."
177+
)
178+
static let enablingCouponsErrorSubtitle = NSLocalizedString(
179+
"pos.itemList.enablingCouponsErrorSubtitle",
180+
value: "Give it another go?",
181+
comment: "Subtitle appearing on the coupon list screen when there's an error enabling coupons setting in the store."
182+
)
183+
static let enablingCouponsErrorRetry = NSLocalizedString(
184+
"pos.itemList.enablingCouponsErrorRetry",
185+
value: "Retry",
186+
comment: "Text of the button appearing on the coupon list screen when there's an error enabling coupons setting in the store.."
187+
)
164188
}
165189
}

WooCommerce/Classes/POS/Presentation/ItemListView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ private extension ItemListView {
172172

173173
Spacer()
174174

175-
if case .coupons = selectedItemType {
175+
if case .coupons = selectedItemType, itemListState.isLoaded || itemListState.isEmpty {
176176
Button(action: {
177177
showCouponCreationModal = true
178178
}, label: {
@@ -258,10 +258,10 @@ private extension ItemListView {
258258
@ViewBuilder
259259
func errorView(_ errorState: PointOfSaleErrorState) -> some View {
260260
switch errorState {
261-
case .errorCouponsDisabled:
261+
case .errorCouponsDisabled, .errorOnEnablingCoupons:
262262
PointOfSaleItemListErrorView(error: errorState, onAction: {
263263
Task {
264-
await posModel.enableCoupons()
264+
await posModel.couponsController.enableCoupons()
265265
}
266266
})
267267
default:

WooCommerce/WooCommerceTests/POS/Controllers/PointOfSaleCouponsControllerTests.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,37 @@ struct PointOfSaleCouponsControllerTests {
163163
// Then
164164
#expect(sut.itemsViewState == expectedViewState)
165165
}
166+
167+
@available(iOS 17.0, *)
168+
@Test func enableCoupons_sets_error_state_when_fails() async throws {
169+
// Given
170+
let couponProvider = MockPointOfSaleCouponService()
171+
couponProvider.errorToThrow = .couponsEnablingError
172+
let sut = PointOfSaleCouponsController(itemProvider: couponProvider)
173+
174+
// When
175+
await sut.enableCoupons()
176+
177+
// Then
178+
guard case .error = sut.itemsViewState.itemsStack.root else {
179+
Issue.record("Expected error state")
180+
return
181+
}
182+
}
183+
184+
@available(iOS 17.0, *)
185+
@Test func enableCoupons_loads_items_when_successful() async throws {
186+
// Given
187+
let couponProvider = MockPointOfSaleCouponService()
188+
let sut = PointOfSaleCouponsController(itemProvider: couponProvider)
189+
let expectedCoupons = MockPointOfSaleCouponService.makeInitialCoupons()
190+
let expectedItemStackState = ItemsStackState(root: .loaded(expectedCoupons, hasMoreItems: false), itemStates: [:])
191+
let expectedViewState = ItemsViewState(containerState: .content, itemsStack: expectedItemStackState)
192+
193+
// When
194+
_ = await sut.enableCoupons()
195+
196+
// Then
197+
#expect(sut.itemsViewState == expectedViewState)
198+
}
166199
}

WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleCouponsController.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@
22

33
@available(iOS 17.0, *)
44
final class MockPointOfSaleCouponsController: PointOfSaleCouponsControllerProtocol {
5-
func enableCoupons() async { }
5+
var loadItemsCalled = false
6+
var loadItemsBase: ItemListBaseItem?
67

78
var itemsViewState: ItemsViewState = .init(containerState: .content,
89
itemsStack: .init(root: .empty, itemStates: [:]))
910

10-
func loadItems(base: ItemListBaseItem) async { }
11+
func loadItems(base: ItemListBaseItem) async {
12+
loadItemsCalled = true
13+
loadItemsBase = base
14+
}
1115

1216
func refreshItems(base: ItemListBaseItem) async { }
13-
1417
func loadNextItems(base: ItemListBaseItem) async { }
18+
func enableCoupons() async { }
1519
}

Yosemite/Yosemite/PointOfSale/PointOfSaleCouponService.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Storage
88
public enum PointOfSaleCouponServiceError: Error {
99
case couponsLoadingError
1010
case couponsDisabled
11+
case couponsEnablingError
1112
}
1213

1314
public protocol PointOfSaleCouponServiceProtocol {
@@ -79,13 +80,13 @@ public final class PointOfSaleCouponService: PointOfSaleCouponServiceProtocol {
7980

8081
@MainActor
8182
public func enableCoupons() async throws {
82-
_ = await withCheckedContinuation { continuation in
83+
return try await withCheckedThrowingContinuation { continuation in
8384
settingsStoreMethods.enableCouponSetting(siteID: siteID) { result in
8485
switch result {
8586
case .success:
86-
continuation.resume(returning: true)
87+
continuation.resume(returning: ())
8788
case .failure:
88-
continuation.resume(returning: false)
89+
continuation.resume(throwing: PointOfSaleCouponServiceError.couponsEnablingError)
8990
}
9091
}
9192
}

Yosemite/YosemiteTests/Tools/POS/POSOrderServiceTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ struct POSOrderServiceTests {
4848

4949
// Then
5050
let createdOrderItems = try #require(mockOrdersRemote.spyCreatePOSOrder?.items)
51-
#expect(createdOrderItems.contains(where: { item in
51+
#expect(createdOrderItems.contains(where: { (item: OrderItem) -> Bool in
5252
item.productID == 100 && item.quantity == 2
5353
}))
54-
#expect(createdOrderItems.contains(where: { item in
54+
#expect(createdOrderItems.contains(where: { (item: OrderItem) -> Bool in
5555
item.productID == 102 && item.quantity == 1
5656
}))
5757
}

0 commit comments

Comments
 (0)