Skip to content

Commit 90ec03d

Browse files
committed
Merge branch 'trunk' into issue/8189-loading-state
# Conflicts: # WooCommerce/Classes/ViewRelated/Dashboard/Analytics Hub/AnalyticsHubViewModel.swift
2 parents a51353e + 2d263b3 commit 90ec03d

File tree

45 files changed

+1481
-246
lines changed

Some content is hidden

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

45 files changed

+1481
-246
lines changed

Experiments/Experiments/ABTest.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ public enum ABTest: String, CaseIterable {
77
/// `An enum with no cases cannot declare a raw type`
88
case null
99

10+
/// A/A test to make sure there is no bias in the logged out state.
11+
/// Experiment ref: pbxNRc-1QS-p2
12+
case aaTestLoggedIn = "woocommerceios_explat_aa_test_logged_in_202212_v2"
13+
1014
/// A/A test to make sure there is no bias in the logged out state.
1115
/// Experiment ref: pbxNRc-1S0-p2
12-
case aaTestLoggedOut = "woocommerceios_explat_aa_test_logged_out_202211"
16+
case aaTestLoggedOut = "woocommerceios_explat_aa_test_logged_out_202212_v2"
1317

1418
/// A/B test to measure the sign-in success rate when only WPCom login is enabled.
1519
/// Experiment ref: pbxNRc-27s-p2
@@ -39,7 +43,7 @@ public enum ABTest: String, CaseIterable {
3943
/// When adding a new experiment, add it to the appropriate case depending on its context (logged-in or logged-out experience).
4044
public var context: ExperimentContext {
4145
switch self {
42-
case .productsOnboardingBanner, .productsOnboardingTemplateProducts, .nativeJetpackSetupFlow:
46+
case .aaTestLoggedIn, .productsOnboardingBanner, .productsOnboardingTemplateProducts, .nativeJetpackSetupFlow:
4347
return .loggedIn
4448
case .aaTestLoggedOut, .abTestLoginWithWPComOnly:
4549
return .loggedOut

RELEASE-NOTES.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
11.5
44
-----
5-
5+
- [*] Account deletion is now supported for all users in settings or in the empty stores screen. [https://github.com/woocommerce/woocommerce-ios/pull/8179]
66

77
11.4
88
-----

WooCommerce/Classes/Authentication/Epilogue/EmptyStoresTableViewCell.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,19 +28,19 @@ final class EmptyStoresTableViewCell: UITableViewCell {
2828

2929
@IBOutlet private weak var stackView: UIStackView!
3030
@IBOutlet private weak var emptyStoresImageView: UIImageView!
31-
@IBOutlet private weak var removeAppleIDAccessButton: UIButton!
31+
@IBOutlet private weak var closeAccountButton: UIButton!
3232

3333
override func awakeFromNib() {
3434
super.awakeFromNib()
3535

3636
configureStackView()
3737
configureImageView()
38-
configureRemoveAppleIDAccessButton()
39-
updateRemoveAppleIDAccessButtonVisibility(isVisible: false)
38+
configureCloseAccountButton()
39+
updateCloseAccountButtonVisibility(isVisible: false)
4040
}
4141

42-
func updateRemoveAppleIDAccessButtonVisibility(isVisible: Bool) {
43-
removeAppleIDAccessButton.isHidden = !isVisible
42+
func updateCloseAccountButtonVisibility(isVisible: Bool) {
43+
closeAccountButton.isHidden = !isVisible
4444
}
4545
}
4646

@@ -57,10 +57,10 @@ private extension EmptyStoresTableViewCell {
5757
emptyStoresImageView.image = .emptyStorePickerImage
5858
}
5959

60-
func configureRemoveAppleIDAccessButton() {
61-
removeAppleIDAccessButton.applyLinkButtonStyle()
62-
removeAppleIDAccessButton.setTitle(Localization.closeAccountTitle, for: .normal)
63-
removeAppleIDAccessButton.on(.touchUpInside) { [weak self] _ in
60+
func configureCloseAccountButton() {
61+
closeAccountButton.applyLinkButtonStyle()
62+
closeAccountButton.setTitle(Localization.closeAccountTitle, for: .normal)
63+
closeAccountButton.on(.touchUpInside) { [weak self] _ in
6464
self?.onCloseAccountButtonTapped?()
6565
}
6666
}

WooCommerce/Classes/Authentication/Epilogue/EmptyStoresTableViewCell.xib

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
3-
<device id="retina4_7" orientation="portrait" appearance="light"/>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
43
<dependencies>
5-
<deployment identifier="iOS"/>
6-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
4+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
75
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
86
</dependencies>
97
<objects>
@@ -57,9 +55,9 @@
5755
</tableViewCellContentView>
5856
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
5957
<connections>
58+
<outlet property="closeAccountButton" destination="XF9-r3-Vq9" id="sLP-Jl-0hK"/>
6059
<outlet property="emptyStoresImageView" destination="uuh-xp-DA5" id="4yf-Rz-3c2"/>
6160
<outlet property="legendLabel" destination="bPV-bJ-GBz" id="sQX-ir-ghQ"/>
62-
<outlet property="removeAppleIDAccessButton" destination="XF9-r3-Vq9" id="sLP-Jl-0hK"/>
6361
<outlet property="stackView" destination="pky-np-rBc" id="vRx-dY-Ma9"/>
6462
<outlet property="subtitleLabel" destination="Oil-C8-RKM" id="9VO-5Z-BNb"/>
6563
</connections>

WooCommerce/Classes/Authentication/Epilogue/StorePickerViewController.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,10 +155,10 @@ final class StorePickerViewController: UIViewController {
155155
}
156156
}
157157

158-
private lazy var removeAppleIDAccessCoordinator: RemoveAppleIDAccessCoordinator =
159-
RemoveAppleIDAccessCoordinator(sourceViewController: self) { [weak self] in
160-
guard let self = self else { throw RemoveAppleIDAccessError.presenterDeallocated }
161-
return try await self.removeAppleIDAccess()
158+
private lazy var closeAccountCoordinator: CloseAccountCoordinator =
159+
CloseAccountCoordinator(sourceViewController: self) { [weak self] in
160+
guard let self = self else { throw CloseAccountError.presenterDeallocated }
161+
return try await self.closeAccount()
162162
} onRemoveSuccess: { [weak self] in
163163
self?.restartAuthentication()
164164
}
@@ -698,13 +698,15 @@ extension StorePickerViewController: UITableViewDataSource {
698698
guard let site = viewModel.site(at: indexPath) else {
699699
hideActionButton()
700700
let cell = tableView.dequeueReusableCell(EmptyStoresTableViewCell.self, for: indexPath)
701-
let isRemoveAppleIDAccessButtonVisible = appleIDCredentialChecker.hasAppleUserID()
702-
cell.updateRemoveAppleIDAccessButtonVisibility(isVisible: isRemoveAppleIDAccessButtonVisible)
703-
if isRemoveAppleIDAccessButtonVisible {
701+
let isCloseAccountButtonVisible = appleIDCredentialChecker.hasAppleUserID()
702+
|| featureFlagService.isFeatureFlagEnabled(.storeCreationMVP)
703+
|| featureFlagService.isFeatureFlagEnabled(.storeCreationM2)
704+
cell.updateCloseAccountButtonVisibility(isVisible: isCloseAccountButtonVisible)
705+
if isCloseAccountButtonVisible {
704706
cell.onCloseAccountButtonTapped = { [weak self] in
705707
guard let self = self else { return }
706708
ServiceLocator.analytics.track(event: .closeAccountTapped(source: .emptyStores))
707-
self.removeAppleIDAccessCoordinator.start()
709+
self.closeAccountCoordinator.start()
708710
}
709711
}
710712
return cell
@@ -777,7 +779,7 @@ extension StorePickerViewController: UITableViewDelegate {
777779
}
778780

779781
private extension StorePickerViewController {
780-
func removeAppleIDAccess() async throws {
782+
func closeAccount() async throws {
781783
try await withCheckedThrowingContinuation { [weak self] continuation in
782784
guard let self = self else { return }
783785
let action = AccountAction.closeAccount { result in

WooCommerce/Classes/Extensions/UIButton+TitleAndImage.swift

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

WooCommerce/Classes/ViewModels/CardPresentPayments/CardPresentModalScanningForReader.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,33 +81,39 @@ private extension CardPresentModalScanningForReader {
8181

8282
enum Localization {
8383
static let title = NSLocalizedString(
84-
"Scanning for reader",
84+
"cardPresent.modalScanningForReader.title",
85+
value: "Scanning for reader",
8586
comment: "Title label for modal dialog that appears when searching for a card reader"
8687
)
8788

8889
static let instruction = NSLocalizedString(
89-
"To turn on your card reader, briefly press its power button.",
90+
"cardPresent.modalScanningForReader.instruction",
91+
value: "To turn on your card reader, briefly press its power button.",
9092
comment: "Label within the modal dialog that appears when searching for a card reader"
9193
)
9294

9395
static let cancel = NSLocalizedString(
94-
"Cancel",
96+
"cardPresent.modalScanningForReader.cancelButton",
97+
value: "Cancel",
9598
comment: "Label for a cancel button"
9699
)
97100

98101
static let learnMoreLink = NSLocalizedString(
99-
"Learn more",
102+
"cardPresent.modalScanningForReader.learnMore.link",
103+
value: "Learn more",
100104
comment: """
101105
A label prompting users to learn more about In-Person Payments.
102106
This is the link to the website, and forms part of a longer sentence which it should be considered a part of.
103107
"""
104108
)
105109

106110
static let learnMoreText = NSLocalizedString(
107-
"%1$@ about In\u{2011}Person Payments",
111+
"cardPresent.modalScanningForReader.learnMore.text",
112+
value: "%1$@ about In‑Person Payments",
108113
comment: """
109114
A label prompting users to learn more about In-Person Payments.
110-
\u{2011} is a special character that acts as nonbreaking hyphen for "-" in the "In-Person" string.
115+
The hyphen in "In‑Person" is a non-breaking hyphen (U+2011).
116+
If your translation of that term also happens to contains a hyphen, please be sure to use the non-breaking hyphen character for it.
111117
%1$@ is a placeholder that always replaced with \"Learn more\" string,
112118
which should be translated separately and considered part of this sentence.
113119
"""
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import Foundation
2+
import Yosemite
3+
import Combine
4+
5+
enum CardReaderConnectionResult {
6+
case connected(CardReader)
7+
case canceled
8+
}
9+
10+
final class CardPresentPaymentPreflightController {
11+
/// Store's ID.
12+
///
13+
private let siteID: Int64
14+
15+
/// Payment Gateway Account to use.
16+
///
17+
private let paymentGatewayAccount: PaymentGatewayAccount
18+
19+
/// IPP Configuration.
20+
///
21+
private let configuration: CardPresentPaymentsConfiguration
22+
23+
/// View Controller used to present alerts.
24+
///
25+
private var rootViewController: UIViewController
26+
27+
/// Stores manager.
28+
///
29+
private let stores: StoresManager
30+
31+
/// Analytics manager.
32+
///
33+
private let analytics: Analytics
34+
35+
/// Stores the connected card reader
36+
private var connectedReader: CardReader?
37+
38+
39+
/// Controller to connect a card reader.
40+
///
41+
private var connectionController: CardReaderConnectionController
42+
43+
44+
private(set) var readerConnection = CurrentValueSubject<CardReaderConnectionResult?, Never>(nil)
45+
46+
init(siteID: Int64,
47+
paymentGatewayAccount: PaymentGatewayAccount,
48+
configuration: CardPresentPaymentsConfiguration,
49+
rootViewController: UIViewController,
50+
stores: StoresManager = ServiceLocator.stores,
51+
analytics: Analytics = ServiceLocator.analytics) {
52+
self.siteID = siteID
53+
self.paymentGatewayAccount = paymentGatewayAccount
54+
self.configuration = configuration
55+
self.rootViewController = rootViewController
56+
self.stores = stores
57+
self.analytics = analytics
58+
self.connectedReader = nil
59+
let analyticsTracker = CardReaderConnectionAnalyticsTracker(configuration: configuration,
60+
stores: stores,
61+
analytics: analytics)
62+
// TODO: Replace this with a refactored (New)CardReaderConnectionController
63+
self.connectionController = CardReaderConnectionController(
64+
forSiteID: siteID,
65+
knownReaderProvider: CardReaderSettingsKnownReaderStorage(),
66+
alertsProvider: CardReaderSettingsAlerts(),
67+
configuration: configuration,
68+
analyticsTracker: analyticsTracker)
69+
}
70+
71+
func start() {
72+
configureBackend()
73+
observeConnectedReaders()
74+
// If we're already connected to a reader, return it
75+
if let connectedReader = connectedReader {
76+
readerConnection.send(CardReaderConnectionResult.connected(connectedReader))
77+
}
78+
79+
// TODO: Run onboarding if needed
80+
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+
}
95+
96+
switch connectionResult {
97+
case .success(let unwrapped):
98+
self.readerConnection.send(unwrapped)
99+
default:
100+
break
101+
}
102+
}
103+
}
104+
105+
106+
107+
/// Configure the CardPresentPaymentStore to use the appropriate backend
108+
///
109+
private func configureBackend() {
110+
let setAccount = CardPresentPaymentAction.use(paymentGatewayAccount: paymentGatewayAccount)
111+
stores.dispatch(setAccount)
112+
}
113+
114+
private func observeConnectedReaders() {
115+
let action = CardPresentPaymentAction.observeConnectedReaders() { [weak self] readers in
116+
self?.connectedReader = readers.first
117+
}
118+
stores.dispatch(action)
119+
}
120+
}

WooCommerce/Classes/ViewRelated/CardPresentPayments/CardPresentPaymentsModalViewController.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,9 @@ private extension CardPresentPaymentsModalViewController {
320320
auxiliaryButton.setAttributedTitle(viewModel.auxiliaryAttributedButtonTitle, for: .normal)
321321
auxiliaryButton.setImage(viewModel.auxiliaryButtonimage, for: .normal)
322322
if viewModel.auxiliaryButtonimage != nil {
323-
auxiliaryButton.distributeTitleAndImage(spacing: 8.0)
323+
var config = UIButton.Configuration.plain()
324+
config.imagePadding = Constants.buttonTitleAndImageSpacing
325+
auxiliaryButton.configuration = config
324326
}
325327
view.layoutIfNeeded()
326328
}
@@ -416,6 +418,7 @@ private extension CardPresentPaymentsModalViewController {
416418
static let extraInfoCustomInsets = UIEdgeInsets(top: 12, left: 10, bottom: 12, right: 10)
417419
static let modalHeight: CGFloat = 382
418420
static let modalWidth: CGFloat = 280
421+
static let buttonTitleAndImageSpacing: CGFloat = 8
419422
}
420423
}
421424

WooCommerce/Classes/ViewRelated/Containers/BottomButtonContainer/BottomButtonContainerView.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,14 @@ private extension BottomButtonContainerView {
7171
case .primary:
7272
button.applyPrimaryButtonStyle()
7373
case .link:
74+
if let image = viewModel.image {
75+
var configuration = UIButton.Configuration.plain()
76+
button.setImage(image, for: .normal)
77+
configuration.imagePadding = Constants.buttonTitleAndImageSpacing
78+
button.configuration = configuration
79+
}
7480
button.applyLinkButtonStyle()
7581
button.contentHorizontalAlignment = .leading
76-
button.contentEdgeInsets = .zero
77-
}
78-
79-
if let image = viewModel.image {
80-
button.setImage(image, for: .normal)
81-
button.distributeTitleAndImage(spacing: Constants.buttonTitleAndImageSpacing)
8282
}
8383
}
8484

0 commit comments

Comments
 (0)