Skip to content

Commit 57ce042

Browse files
authored
[Woo POS] Prevent connection success flashing during payment (#15784)
2 parents c9f34ca + 834628b commit 57ce042

File tree

3 files changed

+85
-5
lines changed

3 files changed

+85
-5
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- [**] POS: a POS tab in the tab bar is now available in the app for stores eligible for Point of Sale, instead of the previous entry point in the Menu tab. [https://github.com/woocommerce/woocommerce-ios/pull/15766]
99
- [***] POS: Barcode scanning using Bluetooth scanners in Point of Sale (HID-based scanners are supported) [https://github.com/woocommerce/woocommerce-ios/pull/15776]
1010
- [*] POS: Support refreshing product list when it's empty [https://github.com/woocommerce/woocommerce-ios/pull/15782]
11+
- [*] POS: Prevent card reader connection success alert flashing when connecting during a payment [https://github.com/woocommerce/woocommerce-ios/pull/15784]
1112

1213
22.6
1314
-----

WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -399,8 +399,15 @@ extension PointOfSaleAggregateModel {
399399

400400
private func cancelCardReaderPreparation() {
401401
cardPresentPaymentService.cancelPayment()
402+
resetCardReaderObservation()
403+
}
404+
405+
private func resetCardReaderObservation() {
406+
// We set these to nil, so that we can check them when showing `Reader connected` on the Totals screen.
402407
startPaymentOnCardReaderConnection?.cancel()
408+
startPaymentOnCardReaderConnection = nil
403409
cardReaderDisconnection?.cancel()
410+
cardReaderDisconnection = nil
404411
}
405412

406413
private func observeReaderReconnection() {
@@ -444,6 +451,13 @@ private extension PointOfSaleAggregateModel {
444451
else {
445452
return nil
446453
}
454+
455+
// Filter connection success alerts when we're immediately starting a payment
456+
if case .connectionSuccess = eventDetails,
457+
startPaymentOnCardReaderConnection != nil {
458+
return nil
459+
}
460+
447461
return alertType
448462
}
449463
.sink(receiveValue: { [weak self] alertType in
@@ -569,17 +583,13 @@ extension PointOfSaleAggregateModel {
569583
try await cardPresentPaymentService.cancelPayment()
570584
}
571585

572-
// Cancels payment task
573-
cardPresentPaymentService.cancelPayment()
574-
575586
// Before exiting Point of Sale, we warn the merchant about losing their in-progress order.
576587
// We need to clear it down as any accidental retention can cause issues especially when reconnecting card readers.
577588
orderController.clearOrder()
578589

579590
// Ideally, we could rely on the POS being deallocated to cancel all these. Since we have memory leak issues,
580591
// cancelling them explicitly helps reduce the risk of user-visible bugs while we work on the memory leaks.
581-
startPaymentOnCardReaderConnection?.cancel()
582-
cardReaderDisconnection?.cancel()
592+
resetCardReaderObservation()
583593
cancellables.forEach { $0.cancel() }
584594
}
585595
}

WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,75 @@ struct PointOfSaleAggregateModelTests {
10611061
// Then
10621062
#expect(sut.cardPresentPaymentOnboardingViewModel?.state == .pluginNotActivated(plugin: .stripe))
10631063
}
1064+
1065+
@available(iOS 17.0, *)
1066+
@Test func connectionSuccessAlert_is_filtered_when_waiting_to_start_payment_on_card_reader_connection() async throws {
1067+
// Given
1068+
let itemsController = MockPointOfSaleItemsController()
1069+
let sut = PointOfSaleAggregateModel(
1070+
itemsController: itemsController,
1071+
purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(),
1072+
couponsController: MockPointOfSaleCouponsController(),
1073+
couponsSearchController: MockPointOfSaleCouponsController(),
1074+
cardPresentPaymentService: cardPresentPaymentService,
1075+
orderController: orderController,
1076+
analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()),
1077+
collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(),
1078+
searchHistoryService: MockPOSSearchHistoryService(),
1079+
popularPurchasableItemsController: MockPointOfSaleItemsController(),
1080+
barcodeScanService: MockPointOfSaleBarcodeScanService(),
1081+
soundPlayer: MockPointOfSaleSoundPlayer())
1082+
1083+
// Add item to cart and checkout to trigger payment waiting state
1084+
sut.addToCart(makePurchasableItem())
1085+
await sut.checkOut()
1086+
1087+
// Verify we're in finalizing stage and no alert is currently shown
1088+
try #require(sut.orderStage == .finalizing)
1089+
try #require(sut.cardPresentPaymentAlertViewModel == nil)
1090+
1091+
// When
1092+
// Simulate connection success event while waiting for payment
1093+
cardPresentPaymentService.paymentEvent = .show(eventDetails: .connectionSuccess(done: {}))
1094+
1095+
// Then
1096+
// The connection success alert should be filtered out and not shown
1097+
#expect(sut.cardPresentPaymentAlertViewModel == nil)
1098+
}
1099+
1100+
@available(iOS 17.0, *)
1101+
@Test func connectionSuccessAlert_is_shown_when_not_waiting_to_start_payment() async throws {
1102+
// Given
1103+
let itemsController = MockPointOfSaleItemsController()
1104+
let sut = PointOfSaleAggregateModel(
1105+
itemsController: itemsController,
1106+
purchasableItemsSearchController: MockPointOfSalePurchasableItemsSearchController(),
1107+
couponsController: MockPointOfSaleCouponsController(),
1108+
couponsSearchController: MockPointOfSaleCouponsController(),
1109+
cardPresentPaymentService: cardPresentPaymentService,
1110+
orderController: orderController,
1111+
analytics: WooAnalytics(analyticsProvider: MockAnalyticsProvider()),
1112+
collectOrderPaymentAnalyticsTracker: MockPOSCollectOrderPaymentAnalyticsTracker(),
1113+
searchHistoryService: MockPOSSearchHistoryService(),
1114+
popularPurchasableItemsController: MockPointOfSaleItemsController(),
1115+
barcodeScanService: MockPointOfSaleBarcodeScanService(),
1116+
soundPlayer: MockPointOfSaleSoundPlayer())
1117+
1118+
// Verify we're in building stage and no alert is currently shown
1119+
try #require(sut.orderStage == .building)
1120+
try #require(sut.cardPresentPaymentAlertViewModel == nil)
1121+
1122+
// When
1123+
// Simulate connection success event when not waiting for payment
1124+
cardPresentPaymentService.paymentEvent = .show(eventDetails: .connectionSuccess(done: {}))
1125+
1126+
// Then
1127+
// The connection success alert should be shown
1128+
guard case .connectionSuccess = sut.cardPresentPaymentAlertViewModel else {
1129+
Issue.record("Expected cardPresentPaymentAlertViewModel to be connectionSuccess")
1130+
return
1131+
}
1132+
}
10641133
}
10651134

10661135
struct AnalyticsTests {

0 commit comments

Comments
 (0)