Skip to content

Commit 71eaaa9

Browse files
committed
Merge branch 'trunk' into issue/8189-loading-state
# Conflicts: # WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsReportCard.swift
2 parents 90ec03d + 577ac14 commit 71eaaa9

File tree

14 files changed

+1231
-177
lines changed

14 files changed

+1231
-177
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import UIKit
2+
import Yosemite
3+
4+
final class CardPresentModalSelectSearchType: CardPresentPaymentsModalViewModel {
5+
var textMode: PaymentsModalTextMode
6+
7+
var actionsMode: PaymentsModalActionsMode
8+
9+
var topTitle: String = Localization.title
10+
11+
var topSubtitle: String? = nil
12+
13+
var image: UIImage = .paymentsLoading
14+
15+
var primaryButtonTitle: String?
16+
17+
var secondaryButtonTitle: String?
18+
19+
var auxiliaryButtonTitle: String? = nil
20+
21+
var bottomTitle: String? = Localization.description
22+
23+
var bottomSubtitle: String? = nil
24+
25+
var accessibilityLabel: String? = nil
26+
27+
private var tapOnIphoneAction: (() -> Void)
28+
29+
private var bluetoothProximityAction: (() -> Void)
30+
31+
private var cancelAction: (() -> Void)
32+
33+
func didTapPrimaryButton(in viewController: UIViewController?) {
34+
tapOnIphoneAction()
35+
}
36+
37+
func didTapSecondaryButton(in viewController: UIViewController?) {
38+
bluetoothProximityAction()
39+
}
40+
41+
func didTapAuxiliaryButton(in viewController: UIViewController?) {
42+
cancelAction()
43+
}
44+
45+
init(tapOnIPhoneAction: @escaping () -> Void,
46+
bluetoothAction: @escaping () -> Void,
47+
cancelAction: @escaping () -> Void) {
48+
textMode = .fullInfo
49+
actionsMode = .twoActionAndAuxiliary
50+
primaryButtonTitle = CardReaderDiscoveryMethod.localMobile.name
51+
self.tapOnIphoneAction = tapOnIPhoneAction
52+
secondaryButtonTitle = CardReaderDiscoveryMethod.bluetoothProximity.name
53+
self.bluetoothProximityAction = bluetoothAction
54+
auxiliaryButtonTitle = Localization.cancel
55+
self.cancelAction = cancelAction
56+
}
57+
}
58+
59+
private extension CardPresentModalSelectSearchType {
60+
enum Localization {
61+
static let title = NSLocalizedString(
62+
"Select reader type",
63+
comment: "The title for the alert shown when connecting a card reader, asking the user to choose a " +
64+
"reader type. Only shown when supported on their device.")
65+
66+
static let description = NSLocalizedString(
67+
"Your iPhone can be used as a card reader, or you can connect to an external reader via bluetooth.",
68+
comment: "The description on the alert shown when connecting a card reader, asking the user to choose a " +
69+
"reader type. Only shown when supported on their device.")
70+
71+
static let cancel = NSLocalizedString(
72+
"Cancel",
73+
comment: "Cancel button title")
74+
}
75+
}
76+
77+
private extension CardReaderDiscoveryMethod {
78+
var name: String {
79+
switch self {
80+
case .bluetoothProximity:
81+
return NSLocalizedString(
82+
"Bluetooth Reader",
83+
comment: "The button title on the reader type alert, for the user to choose a bluetooth reader.")
84+
case .localMobile:
85+
return NSLocalizedString(
86+
"Tap to Pay on iPhone",
87+
comment: "The button title on the reader type alert, for the user to choose the built-in reader.")
88+
}
89+
}
90+
}

WooCommerce/Classes/ViewRelated/CardPresentPayments/CardPresentPaymentPreflightController.swift

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import Foundation
22
import Yosemite
33
import Combine
4+
#if !targetEnvironment(simulator)
5+
import ProximityReader
6+
#endif
47

58
enum CardReaderConnectionResult {
69
case connected(CardReader)
@@ -20,9 +23,9 @@ final class CardPresentPaymentPreflightController {
2023
///
2124
private let configuration: CardPresentPaymentsConfiguration
2225

23-
/// View Controller used to present alerts.
26+
/// Alerts presenter to send alert view models
2427
///
25-
private var rootViewController: UIViewController
28+
private var alertsPresenter: CardPresentPaymentAlertsPresenting
2629

2730
/// Stores manager.
2831
///
@@ -40,30 +43,43 @@ final class CardPresentPaymentPreflightController {
4043
///
4144
private var connectionController: CardReaderConnectionController
4245

46+
/// Controller to connect a card reader.
47+
///
48+
private var builtInConnectionController: CardReaderConnectionController
49+
4350

4451
private(set) var readerConnection = CurrentValueSubject<CardReaderConnectionResult?, Never>(nil)
4552

4653
init(siteID: Int64,
4754
paymentGatewayAccount: PaymentGatewayAccount,
4855
configuration: CardPresentPaymentsConfiguration,
49-
rootViewController: UIViewController,
56+
alertsPresenter: CardPresentPaymentAlertsPresenting,
5057
stores: StoresManager = ServiceLocator.stores,
5158
analytics: Analytics = ServiceLocator.analytics) {
5259
self.siteID = siteID
5360
self.paymentGatewayAccount = paymentGatewayAccount
5461
self.configuration = configuration
55-
self.rootViewController = rootViewController
62+
self.alertsPresenter = alertsPresenter
5663
self.stores = stores
5764
self.analytics = analytics
5865
self.connectedReader = nil
5966
let analyticsTracker = CardReaderConnectionAnalyticsTracker(configuration: configuration,
6067
stores: stores,
6168
analytics: analytics)
62-
// TODO: Replace this with a refactored (New)CardReaderConnectionController
69+
// TODO: Replace this with a refactored (New)LegacyCardReaderConnectionController
6370
self.connectionController = CardReaderConnectionController(
6471
forSiteID: siteID,
72+
discoveryMethod: .bluetoothProximity,
6573
knownReaderProvider: CardReaderSettingsKnownReaderStorage(),
66-
alertsProvider: CardReaderSettingsAlerts(),
74+
alertsPresenter: alertsPresenter,
75+
configuration: configuration,
76+
analyticsTracker: analyticsTracker)
77+
78+
self.builtInConnectionController = CardReaderConnectionController(
79+
forSiteID: siteID,
80+
discoveryMethod: .localMobile,
81+
knownReaderProvider: CardReaderSettingsKnownReaderStorage(),
82+
alertsPresenter: alertsPresenter,
6783
configuration: configuration,
6884
analyticsTracker: analyticsTracker)
6985
}
@@ -78,31 +94,63 @@ final class CardPresentPaymentPreflightController {
7894

7995
// TODO: Run onboarding if needed
8096

81-
// TODO: Ask for a Reader type if supported by device
82-
83-
// Attempt to find a reader and connect
84-
connectionController.searchAndConnect(from: rootViewController) { result in
85-
let connectionResult = result.map { connection in
86-
switch connection {
87-
case .connected:
88-
// TODO: pass the reader from the (New)CardReaderConnectionController
89-
guard let connectedReader = self.connectedReader else { return CardReaderConnectionResult.canceled }
90-
return CardReaderConnectionResult.connected(connectedReader)
91-
case .canceled:
92-
return CardReaderConnectionResult.canceled
93-
}
94-
}
97+
// Ask for a Reader type if supported by device/in country
98+
guard localMobileReaderSupported(),
99+
configuration.supportedReaders.contains(.appleBuiltIn)
100+
else {
101+
// Attempt to find a bluetooth reader and connect
102+
connectionController.searchAndConnect(onCompletion: handleConnectionResult)
103+
return
104+
}
95105

96-
switch connectionResult {
97-
case .success(let unwrapped):
98-
self.readerConnection.send(unwrapped)
99-
default:
100-
break
101-
}
106+
alertsPresenter.present(viewModel: CardPresentModalSelectSearchType(
107+
tapOnIPhoneAction: { [weak self] in
108+
guard let self = self else { return }
109+
self.builtInConnectionController.searchAndConnect(
110+
onCompletion: self.handleConnectionResult)
111+
},
112+
bluetoothAction: { [weak self] in
113+
guard let self = self else { return }
114+
self.connectionController.searchAndConnect(
115+
onCompletion: self.handleConnectionResult)
116+
},
117+
cancelAction: { [weak self] in
118+
guard let self = self else { return }
119+
self.alertsPresenter.dismiss()
120+
self.handleConnectionResult(.success(.canceled))
121+
}))
122+
}
123+
124+
private func localMobileReaderSupported() -> Bool {
125+
#if !targetEnvironment(simulator)
126+
if #available(iOS 15.4, *) {
127+
return PaymentCardReader.isSupported
128+
} else {
129+
return false
102130
}
131+
#endif
132+
return true
103133
}
104134

135+
private func handleConnectionResult(_ result: Result<CardReaderConnectionController.ConnectionResult, Error>) {
136+
let connectionResult = result.map { connection in
137+
switch connection {
138+
case .connected:
139+
// TODO: pass the reader from the (New)CardReaderConnectionController
140+
guard let connectedReader = self.connectedReader else { return CardReaderConnectionResult.canceled }
141+
return CardReaderConnectionResult.connected(connectedReader)
142+
case .canceled:
143+
return CardReaderConnectionResult.canceled
144+
}
145+
}
105146

147+
switch connectionResult {
148+
case .success(let unwrapped):
149+
self.readerConnection.send(unwrapped)
150+
default:
151+
break
152+
}
153+
}
106154

107155
/// Configure the CardPresentPaymentStore to use the appropriate backend
108156
///

0 commit comments

Comments
 (0)