Skip to content

Commit 58cd33b

Browse files
committed
8082 Add ConnectionAlertsProviders for each flow
Built-in and Bluetooth connection flows are similar (technically) but different in the eyes of users. We will need to show different alerts even though we are doing similar work in the connection code. This adds various AlertsProviding protocols and two concrete providers, for the bluetooth and built in flows. Both implementations show the same alerts in this commit, but they will be specialised in upcoming commits. When we refactor the connection controllers, the shared code can use a `CardReaderConnectionAlertsProviding` for the common alerts, without knowing which flow it is serving.
1 parent 6fce3fe commit 58cd33b

File tree

7 files changed

+250
-39
lines changed

7 files changed

+250
-39
lines changed

WooCommerce/Classes/ViewRelated/CardPresentPayments/BuiltInCardReaderConnectionController.swift

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ final class BuiltInCardReaderConnectionController {
7474
private let alertsPresenter: CardPresentPaymentAlertsPresenting
7575
private let configuration: CardPresentPaymentsConfiguration
7676

77+
private let alertsProvider: CardReaderConnectionAlertsProviding
78+
7779
/// The reader we want the user to consider connecting to
7880
///
7981
private var candidateReader: CardReader?
@@ -105,6 +107,7 @@ final class BuiltInCardReaderConnectionController {
105107
storageManager: StorageManagerType = ServiceLocator.storageManager,
106108
stores: StoresManager = ServiceLocator.stores,
107109
alertsPresenter: CardPresentPaymentAlertsPresenting,
110+
alertsProvider: CardReaderConnectionAlertsProviding,
108111
configuration: CardPresentPaymentsConfiguration,
109112
analyticsTracker: CardReaderConnectionAnalyticsTracker
110113
) {
@@ -113,6 +116,7 @@ final class BuiltInCardReaderConnectionController {
113116
self.stores = stores
114117
state = .idle
115118
self.alertsPresenter = alertsPresenter
119+
self.alertsProvider = alertsProvider
116120
self.configuration = configuration
117121
self.analyticsTracker = analyticsTracker
118122

@@ -264,7 +268,7 @@ private extension BuiltInCardReaderConnectionController {
264268
/// If all else fails, display the "scanning" modal and
265269
/// stay in this state
266270
///
267-
alertsPresenter.present(viewModel: CardPresentModalScanningForReader(cancel: {
271+
alertsPresenter.present(viewModel: alertsProvider.scanningForReader(cancel: {
268272
self.state = .cancel
269273
}))
270274
}
@@ -288,9 +292,9 @@ private extension BuiltInCardReaderConnectionController {
288292
}
289293

290294
alertsPresenter.present(
291-
viewModel: CardPresentModalUpdateProgress(requiredUpdate: true,
292-
progress: progress,
293-
cancel: cancel))
295+
viewModel: alertsProvider.updateProgress(requiredUpdate: true,
296+
progress: progress,
297+
cancel: cancel))
294298
}
295299

296300
/// Retry a search for a card reader
@@ -382,7 +386,7 @@ private extension BuiltInCardReaderConnectionController {
382386
}
383387
stores.dispatch(action)
384388

385-
alertsPresenter.present(viewModel: CardPresentModalConnectingToReader())
389+
alertsPresenter.present(viewModel: alertsProvider.connectingToReader())
386390
}
387391

388392
/// An error occurred while connecting
@@ -402,23 +406,25 @@ private extension BuiltInCardReaderConnectionController {
402406
}
403407

404408
private func onUpdateFailed(error: Error) {
405-
guard case CardReaderServiceError.softwareUpdate(underlyingError: let underlyingError, batteryLevel: let batteryLevel) = error else {
409+
guard case CardReaderServiceError.softwareUpdate(underlyingError: let underlyingError, batteryLevel: _) = error else {
406410
return
407411
}
408412

413+
// Duplication of `readerSoftwareUpdateFailedBatteryLow` and `default is left to make factoring out easier later on.
409414
switch underlyingError {
410415
case .readerSoftwareUpdateFailedInterrupted:
411416
// Update was cancelled, don't treat this as an error
412417
return
413418
case .readerSoftwareUpdateFailedBatteryLow:
414419
alertsPresenter.present(
415-
viewModel: CardPresentModalUpdateFailedLowBattery(batteryLevel: batteryLevel,
416-
close: {
417-
self.state = .searching
418-
}))
420+
viewModel: alertsProvider.updatingFailed(tryAgain: nil,
421+
close: {
422+
self.state = .searching
423+
}))
419424
default:
420425
alertsPresenter.present(
421-
viewModel: CardPresentModalUpdateFailedNonRetryable(close: {
426+
viewModel: alertsProvider.updatingFailed(tryAgain: nil,
427+
close: {
422428
self.state = .searching
423429
}))
424430
}
@@ -429,6 +435,7 @@ private extension BuiltInCardReaderConnectionController {
429435
self.state = .retry
430436
}
431437

438+
// TODO: Consider removing this in favour of retry only – continue doesn't make sense for a built-in reader
432439
let continueSearch = {
433440
self.state = .searching
434441
}
@@ -439,30 +446,25 @@ private extension BuiltInCardReaderConnectionController {
439446

440447
guard case CardReaderServiceError.connection(let underlyingError) = error else {
441448
return alertsPresenter.present(
442-
viewModel: CardPresentModalConnectingFailed(continueSearch: continueSearch, cancelSearch: cancelSearch))
449+
viewModel: alertsProvider.connectingFailed(continueSearch: continueSearch, cancelSearch: cancelSearch))
443450
}
444451

445452
switch underlyingError {
446453
case .incompleteStoreAddress(let adminUrl):
447454
alertsPresenter.present(
448-
viewModel: CardPresentModalConnectingFailedUpdateAddress(
455+
viewModel: alertsProvider.connectingFailedIncompleteAddress(
449456
openWCSettings: openWCSettingsAction(adminUrl: adminUrl,
450457
retrySearch: retrySearch),
451458
retrySearch: retrySearch,
452459
cancelSearch: cancelSearch))
453460
case .invalidPostalCode:
454461
alertsPresenter.present(
455-
viewModel: CardPresentModalConnectingFailedUpdatePostalCode(
456-
retrySearch: retrySearch,
457-
cancelSearch: cancelSearch))
458-
case .bluetoothConnectionFailedBatteryCriticallyLow:
459-
alertsPresenter.present(
460-
viewModel: CardPresentModalConnectingFailedChargeReader(
462+
viewModel: alertsProvider.connectingFailedInvalidPostalCode(
461463
retrySearch: retrySearch,
462464
cancelSearch: cancelSearch))
463465
default:
464466
alertsPresenter.present(
465-
viewModel: CardPresentModalConnectingFailed(
467+
viewModel: alertsProvider.connectingFailed(
466468
continueSearch: continueSearch,
467469
cancelSearch: cancelSearch))
468470
}
@@ -521,7 +523,7 @@ private extension BuiltInCardReaderConnectionController {
521523
///
522524
private func onDiscoveryFailed(error: Error) {
523525
alertsPresenter.present(
524-
viewModel: CardPresentModalScanningFailed(error: error) { [weak self] in
526+
viewModel: alertsProvider.scanningFailed(error: error) { [weak self] in
525527
self?.returnFailure(error: error)
526528
})
527529
}

WooCommerce/Classes/ViewRelated/CardPresentPayments/CardPresentPaymentPreflightController.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,14 @@ final class CardPresentPaymentPreflightController {
7070
forSiteID: siteID,
7171
knownReaderProvider: CardReaderSettingsKnownReaderStorage(),
7272
alertsPresenter: alertsPresenter,
73+
alertsProvider: BluetoothReaderConnectionAlertsProvider(),
7374
configuration: configuration,
7475
analyticsTracker: analyticsTracker)
7576

7677
self.builtInConnectionController = BuiltInCardReaderConnectionController(
7778
forSiteID: siteID,
7879
alertsPresenter: alertsPresenter,
80+
alertsProvider: BuiltInReaderConnectionAlertsProvider(),
7981
configuration: configuration,
8082
analyticsTracker: analyticsTracker)
8183
}

WooCommerce/Classes/ViewRelated/CardPresentPayments/CardReaderConnectionController.swift

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ final class CardReaderConnectionController {
8383
private let alertsPresenter: CardPresentPaymentAlertsPresenting
8484
private let configuration: CardPresentPaymentsConfiguration
8585

86+
private let alertsProvider: BluetoothReaderConnnectionAlertsProviding
87+
8688
/// Reader(s) discovered by the card reader service
8789
///
8890
private var foundReaders: [CardReader]
@@ -134,6 +136,7 @@ final class CardReaderConnectionController {
134136
stores: StoresManager = ServiceLocator.stores,
135137
knownReaderProvider: CardReaderSettingsKnownReaderProvider,
136138
alertsPresenter: CardPresentPaymentAlertsPresenting,
139+
alertsProvider: BluetoothReaderConnnectionAlertsProviding,
137140
configuration: CardPresentPaymentsConfiguration,
138141
analyticsTracker: CardReaderConnectionAnalyticsTracker
139142
) {
@@ -143,6 +146,7 @@ final class CardReaderConnectionController {
143146
state = .idle
144147
knownCardReaderProvider = knownReaderProvider
145148
self.alertsPresenter = alertsPresenter
149+
self.alertsProvider = alertsProvider
146150
foundReaders = []
147151
knownReaderID = nil
148152
skippedReaderIDs = []
@@ -419,7 +423,7 @@ private extension CardReaderConnectionController {
419423
/// If all else fails, display the "scanning" modal and
420424
/// stay in this state
421425
///
422-
alertsPresenter.present(viewModel: CardPresentModalScanningForReader(cancel: {
426+
alertsPresenter.present(viewModel: alertsProvider.scanningForReader(cancel: {
423427
self.state = .cancel
424428
}))
425429
}
@@ -433,7 +437,7 @@ private extension CardReaderConnectionController {
433437
}
434438

435439
alertsPresenter.present(
436-
viewModel: CardPresentModalFoundReader(
440+
viewModel: alertsProvider.foundReader(
437441
name: candidateReader.id,
438442
connect: {
439443
self.state = .connectToReader
@@ -444,7 +448,7 @@ private extension CardReaderConnectionController {
444448
self.pruneSkippedReaders()
445449
self.state = .searching
446450
},
447-
cancel: { [weak self] in
451+
cancelSearch: { [weak self] in
448452
self?.state = .cancel
449453
}))
450454
}
@@ -487,9 +491,9 @@ private extension CardReaderConnectionController {
487491
}
488492

489493
alertsPresenter.present(
490-
viewModel: CardPresentModalUpdateProgress(requiredUpdate: true,
491-
progress: progress,
492-
cancel: cancel))
494+
viewModel: alertsProvider.updateProgress(requiredUpdate: true,
495+
progress: progress,
496+
cancel: cancel))
493497
}
494498

495499
/// Retry a search for a card reader
@@ -582,7 +586,7 @@ private extension CardReaderConnectionController {
582586
}
583587
stores.dispatch(action)
584588

585-
alertsPresenter.present(viewModel: CardPresentModalConnectingToReader())
589+
alertsPresenter.present(viewModel: alertsProvider.connectingToReader())
586590
}
587591

588592
/// An error occurred while connecting
@@ -612,13 +616,14 @@ private extension CardReaderConnectionController {
612616
return
613617
case .readerSoftwareUpdateFailedBatteryLow:
614618
alertsPresenter.present(
615-
viewModel: CardPresentModalUpdateFailedLowBattery(batteryLevel: batteryLevel,
616-
close: {
617-
self.state = .searching
618-
}))
619+
viewModel: alertsProvider.updatingFailedLowBattery(batteryLevel: batteryLevel,
620+
close: {
621+
self.state = .searching
622+
}))
619623
default:
620624
alertsPresenter.present(
621-
viewModel: CardPresentModalUpdateFailedNonRetryable(close: {
625+
viewModel: alertsProvider.updatingFailed(tryAgain: nil,
626+
close: {
622627
self.state = .searching
623628
}))
624629
}
@@ -639,30 +644,30 @@ private extension CardReaderConnectionController {
639644

640645
guard case CardReaderServiceError.connection(let underlyingError) = error else {
641646
return alertsPresenter.present(
642-
viewModel: CardPresentModalConnectingFailed(continueSearch: continueSearch, cancelSearch: cancelSearch))
647+
viewModel: alertsProvider.connectingFailed(continueSearch: continueSearch, cancelSearch: cancelSearch))
643648
}
644649

645650
switch underlyingError {
646651
case .incompleteStoreAddress(let adminUrl):
647652
alertsPresenter.present(
648-
viewModel: CardPresentModalConnectingFailedUpdateAddress(
653+
viewModel: alertsProvider.connectingFailedIncompleteAddress(
649654
openWCSettings: openWCSettingsAction(adminUrl: adminUrl,
650655
retrySearch: retrySearch),
651656
retrySearch: retrySearch,
652657
cancelSearch: cancelSearch))
653658
case .invalidPostalCode:
654659
alertsPresenter.present(
655-
viewModel: CardPresentModalConnectingFailedUpdatePostalCode(
660+
viewModel: alertsProvider.connectingFailedInvalidPostalCode(
656661
retrySearch: retrySearch,
657662
cancelSearch: cancelSearch))
658663
case .bluetoothConnectionFailedBatteryCriticallyLow:
659664
alertsPresenter.present(
660-
viewModel: CardPresentModalConnectingFailedChargeReader(
665+
viewModel: alertsProvider.connectingFailedCriticallyLowBattery(
661666
retrySearch: retrySearch,
662667
cancelSearch: cancelSearch))
663668
default:
664669
alertsPresenter.present(
665-
viewModel: CardPresentModalConnectingFailed(
670+
viewModel: alertsProvider.connectingFailed(
666671
continueSearch: continueSearch,
667672
cancelSearch: cancelSearch))
668673
}
@@ -721,7 +726,7 @@ private extension CardReaderConnectionController {
721726
///
722727
private func onDiscoveryFailed(error: Error) {
723728
alertsPresenter.present(
724-
viewModel: CardPresentModalScanningFailed(error: error) { [weak self] in
729+
viewModel: alertsProvider.scanningFailed(error: error) { [weak self] in
725730
self?.returnFailure(error: error)
726731
})
727732
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import Foundation
2+
import UIKit
3+
4+
struct BluetoothReaderConnectionAlertsProvider: BluetoothReaderConnnectionAlertsProviding {
5+
func scanningForReader(cancel: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
6+
CardPresentModalScanningForReader(cancel: cancel)
7+
}
8+
9+
func scanningFailed(error: Error, close: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
10+
CardPresentModalScanningFailed(error: error, primaryAction: close)
11+
}
12+
13+
func connectingToReader() -> CardPresentPaymentsModalViewModel {
14+
CardPresentModalConnectingToReader()
15+
}
16+
17+
func connectingFailed(continueSearch: @escaping () -> Void, cancelSearch: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
18+
CardPresentModalConnectingFailed(continueSearch: continueSearch, cancelSearch: cancelSearch)
19+
}
20+
21+
func connectingFailedIncompleteAddress(openWCSettings: ((UIViewController) -> Void)?, retrySearch: @escaping () -> Void, cancelSearch: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
22+
CardPresentModalConnectingFailedUpdateAddress(openWCSettings: openWCSettings, retrySearch: retrySearch, cancelSearch: cancelSearch)
23+
}
24+
25+
func connectingFailedInvalidPostalCode(retrySearch: @escaping () -> Void, cancelSearch: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
26+
CardPresentModalConnectingFailedUpdatePostalCode(retrySearch: retrySearch, cancelSearch: cancelSearch)
27+
}
28+
29+
func updatingFailed(tryAgain: (() -> Void)?, close: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
30+
if let tryAgain = tryAgain {
31+
return CardPresentModalUpdateFailed(tryAgain: tryAgain, close: close)
32+
} else {
33+
return CardPresentModalUpdateFailedNonRetryable(close: close)
34+
}
35+
}
36+
func updateProgress(requiredUpdate: Bool, progress: Float, cancel: (() -> Void)?) -> CardPresentPaymentsModalViewModel {
37+
CardPresentModalUpdateProgress(requiredUpdate: requiredUpdate, progress: progress, cancel: cancel)
38+
}
39+
40+
func foundReader(name: String, connect: @escaping () -> Void, continueSearch: @escaping () -> Void, cancelSearch: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
41+
CardPresentModalFoundReader(name: name, connect: connect, continueSearch: continueSearch, cancel: cancelSearch)
42+
}
43+
44+
func connectingFailedCriticallyLowBattery(retrySearch: @escaping () -> Void, cancelSearch: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
45+
CardPresentModalConnectingFailedChargeReader(retrySearch: retrySearch, cancelSearch: cancelSearch)
46+
}
47+
48+
func updatingFailedLowBattery(batteryLevel: Double?, close: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
49+
CardPresentModalUpdateFailedLowBattery(batteryLevel: batteryLevel, close: close)
50+
}
51+
52+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import Foundation
2+
import UIKit
3+
4+
struct BuiltInReaderConnectionAlertsProvider: CardReaderConnectionAlertsProviding {
5+
func scanningForReader(cancel: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
6+
CardPresentModalScanningForReader(cancel: cancel)
7+
}
8+
9+
func scanningFailed(error: Error, close: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
10+
CardPresentModalScanningFailed(error: error, primaryAction: close)
11+
}
12+
13+
func connectingToReader() -> CardPresentPaymentsModalViewModel {
14+
CardPresentModalConnectingToReader()
15+
}
16+
17+
func connectingFailed(continueSearch: @escaping () -> Void, cancelSearch: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
18+
CardPresentModalConnectingFailed(continueSearch: continueSearch, cancelSearch: cancelSearch)
19+
}
20+
21+
func connectingFailedIncompleteAddress(openWCSettings: ((UIViewController) -> Void)?,
22+
retrySearch: @escaping () -> Void,
23+
cancelSearch: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
24+
CardPresentModalConnectingFailedUpdateAddress(openWCSettings: openWCSettings, retrySearch: retrySearch, cancelSearch: cancelSearch)
25+
}
26+
27+
func connectingFailedInvalidPostalCode(retrySearch: @escaping () -> Void, cancelSearch: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
28+
CardPresentModalConnectingFailedUpdatePostalCode(retrySearch: retrySearch, cancelSearch: cancelSearch)
29+
}
30+
31+
func updatingFailed(tryAgain: (() -> Void)?, close: @escaping () -> Void) -> CardPresentPaymentsModalViewModel {
32+
if let tryAgain = tryAgain {
33+
return CardPresentModalUpdateFailed(tryAgain: tryAgain, close: close)
34+
} else {
35+
return CardPresentModalUpdateFailedNonRetryable(close: close)
36+
}
37+
}
38+
func updateProgress(requiredUpdate: Bool, progress: Float, cancel: (() -> Void)?) -> CardPresentPaymentsModalViewModel {
39+
CardPresentModalUpdateProgress(requiredUpdate: requiredUpdate, progress: progress, cancel: cancel)
40+
}
41+
}

0 commit comments

Comments
 (0)