Skip to content

Commit 2489559

Browse files
committed
Merge branch 'develop' into issue/4990-display-version
2 parents e520cc4 + 3d003d5 commit 2489559

File tree

37 files changed

+1103
-286
lines changed

37 files changed

+1103
-286
lines changed

RELEASE-NOTES.txt

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

33
7.6
44
-----
5-
5+
- [*] Fix: when product image upload fails, the image cell stop loading. [https://github.com/woocommerce/woocommerce-ios/pull/4989]
66

77
7.5
88
-----

WooCommerce/Classes/Analytics/WooAnalyticsStat.swift

Lines changed: 234 additions & 226 deletions
Large diffs are not rendered by default.
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import SwiftUI
2+
import SafariServices
3+
4+
/// Hosting controller wrapper for `StorePickerError`
5+
///
6+
final class StorePickerErrorHostingController: UIHostingController<StorePickerError> {
7+
8+
/// Creates an `StorePickerErrorHostingController` with preconfigured button actions.
9+
///
10+
static func createWithActions(presenting: UIViewController) -> StorePickerErrorHostingController {
11+
let viewController = StorePickerErrorHostingController()
12+
viewController.setActions(troubleshootingAction: {
13+
let safariViewController = SFSafariViewController(url: WooConstants.URLs.troubleshootErrorLoadingData.asURL())
14+
viewController.present(safariViewController, animated: true)
15+
},
16+
contactSupportAction: {
17+
presenting.dismiss(animated: true) {
18+
ZendeskManager.shared.showNewRequestIfPossible(from: presenting)
19+
}
20+
},
21+
dismissAction: {
22+
presenting.dismiss(animated: true)
23+
})
24+
return viewController
25+
}
26+
27+
init() {
28+
super.init(rootView: StorePickerError())
29+
}
30+
31+
required dynamic init?(coder aDecoder: NSCoder) {
32+
fatalError("init(coder:) has not been implemented")
33+
}
34+
35+
/// Actions are set in a separate function because most of the time, they will require to access `self` to be able to present new view controllers.
36+
///
37+
func setActions(troubleshootingAction: @escaping () -> Void, contactSupportAction: @escaping () -> Void, dismissAction: @escaping () -> Void) {
38+
self.rootView.troubleshootingAction = troubleshootingAction
39+
self.rootView.contactSupportAction = contactSupportAction
40+
self.rootView.dismissAction = dismissAction
41+
}
42+
}
43+
44+
/// Generic Store Picker error view that allows the user to contact support.
45+
///
46+
struct StorePickerError: View {
47+
48+
/// Closure invoked when the "Troubleshooting" button is pressed
49+
///
50+
var troubleshootingAction: () -> Void = {}
51+
52+
/// Closure invoked when the "Contact Support" button is pressed
53+
///
54+
var contactSupportAction: () -> Void = {}
55+
56+
/// Closure invoked when the "Back To Sites" button is pressed
57+
///
58+
var dismissAction: () -> Void = {}
59+
60+
var body: some View {
61+
VStack(alignment: .center, spacing: Layout.mainVerticalSpacing) {
62+
// Title
63+
Text(Localization.title)
64+
.headlineStyle()
65+
66+
// Main image
67+
Image(uiImage: .errorImage)
68+
69+
// Body text
70+
Text(Localization.body)
71+
.multilineTextAlignment(.center)
72+
.bodyStyle()
73+
74+
VStack(spacing: Layout.buttonsSpacing) {
75+
// Primary Button
76+
Button(Localization.troubleshoot, action: troubleshootingAction)
77+
.buttonStyle(PrimaryButtonStyle())
78+
79+
// Secondary button
80+
Button(Localization.contact, action: contactSupportAction)
81+
.buttonStyle(SecondaryButtonStyle())
82+
83+
// Dismiss button
84+
Button(Localization.back, action: dismissAction)
85+
.buttonStyle(LinkButtonStyle())
86+
}
87+
}
88+
.padding([.leading, .trailing, .bottom])
89+
.padding(.top, Layout.topPadding)
90+
.background(Color(.tertiarySystemBackground))
91+
.cornerRadius(Layout.rounderCorners)
92+
}
93+
}
94+
95+
// MARK: Constant
96+
97+
private extension StorePickerError {
98+
enum Localization {
99+
static let title = NSLocalizedString("We couldn't load your site", comment: "Title for the default store picker error screen")
100+
static let body = NSLocalizedString("Please try again or reach out to us and we'll be happy to assist you!",
101+
comment: "Body text for the default store picker error screen")
102+
static let troubleshoot = NSLocalizedString("Read our Troubleshooting Tips",
103+
comment: "Text for the button to navigate to troubleshooting tips from the store picker error screen")
104+
static let contact = NSLocalizedString("Contact Support",
105+
comment: "Text for the button to contact support from the store picker error screen")
106+
static let back = NSLocalizedString("Back to Sites",
107+
comment: "Text for the button to dismiss the store picker error screen")
108+
}
109+
110+
enum Layout {
111+
static let rounderCorners: CGFloat = 10
112+
static let mainVerticalSpacing: CGFloat = 25
113+
static let buttonsSpacing: CGFloat = 15
114+
static let topPadding: CGFloat = 30
115+
}
116+
}
117+
118+
// MARK: Previews
119+
120+
struct StorePickerError_Preview: PreviewProvider {
121+
static var previews: some View {
122+
VStack {
123+
StorePickerError()
124+
}
125+
.padding()
126+
.background(Color.gray)
127+
.previewLayout(.sizeThatFits)
128+
129+
VStack {
130+
StorePickerError()
131+
}
132+
.padding()
133+
.background(Color.gray)
134+
.environment(\.colorScheme, .dark)
135+
.previewLayout(.sizeThatFits)
136+
}
137+
}

WooCommerce/Classes/Authentication/Navigation Exceptions/JetpackErrorViewModel.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import WordPressUI
88
/// an error when Jetpack is not installed or is not connected
99
struct JetpackErrorViewModel: ULErrorViewModel {
1010
private let siteURL: String
11+
private let analytics: Analytics
1112

12-
init(siteURL: String?) {
13+
init(siteURL: String?, analytics: Analytics = ServiceLocator.analytics) {
1314
self.siteURL = siteURL ?? Localization.yourSite
15+
self.analytics = analytics
1416
}
1517

1618
// MARK: - Data and configuration
@@ -46,6 +48,8 @@ struct JetpackErrorViewModel: ULErrorViewModel {
4648
let safariViewController = SFSafariViewController(url: url)
4749
safariViewController.modalPresentationStyle = .pageSheet
4850
viewController?.present(safariViewController, animated: true)
51+
52+
analytics.track(.loginJetpackRequiredViewInstructionsButtonTapped)
4953
}
5054

5155
func didTapSecondaryButton(in viewController: UIViewController?) {
@@ -54,10 +58,16 @@ struct JetpackErrorViewModel: ULErrorViewModel {
5458
}
5559

5660
func didTapAuxiliaryButton(in viewController: UIViewController?) {
57-
let fancyAlert = FancyAlertViewController.makeWhatIsJetpackAlertController()
61+
let fancyAlert = FancyAlertViewController.makeWhatIsJetpackAlertController(analytics: analytics)
5862
fancyAlert.modalPresentationStyle = .custom
5963
fancyAlert.transitioningDelegate = AppDelegate.shared.tabBarController
6064
viewController?.present(fancyAlert, animated: true)
65+
66+
analytics.track(.loginWhatIsJetpackHelpScreenViewed)
67+
}
68+
69+
func viewDidLoad() {
70+
analytics.track(.loginJetpackRequiredScreenViewed)
6171
}
6272
}
6373

WooCommerce/Classes/Authentication/Navigation Exceptions/NoSecureConnectionErrorViewModel.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ struct NoSecureConnectionErrorViewModel: ULErrorViewModel {
3232
// NO-OP
3333
}
3434

35+
func viewDidLoad() {
36+
// NO-OP
37+
}
38+
3539
}
3640

3741
private extension NoSecureConnectionErrorViewModel {

WooCommerce/Classes/Authentication/Navigation Exceptions/NotWPAccountViewModel.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ struct NotWPAccountViewModel: ULErrorViewModel {
3737
fancyAlert.transitioningDelegate = AppDelegate.shared.tabBarController
3838
viewController?.present(fancyAlert, animated: true)
3939
}
40+
41+
func viewDidLoad() {
42+
// NO-OP
43+
}
4044
}
4145

4246

WooCommerce/Classes/Authentication/Navigation Exceptions/NotWPErrorViewModel.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ struct NotWPErrorViewModel: ULErrorViewModel {
3434
func didTapAuxiliaryButton(in viewController: UIViewController?) {
3535
// NO-OP
3636
}
37+
38+
func viewDidLoad() {
39+
// NO-OP
40+
}
3741
}
3842

3943

WooCommerce/Classes/Authentication/Navigation Exceptions/ULErrorViewController.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ final class ULErrorViewController: UIViewController {
4949
configureSecondaryButton()
5050

5151
setUnifiedMargins(forWidth: view.frame.width)
52+
53+
viewModel.viewDidLoad()
5254
}
5355

5456
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {

WooCommerce/Classes/Authentication/Navigation Exceptions/ULErrorViewModel.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ protocol ULErrorViewModel {
2121
/// Provides a title for a secondary action button
2222
var secondaryButtonTitle: String { get }
2323

24+
/// Executed by the view controller when its view was loaded.
25+
func viewDidLoad()
26+
2427
/// Executes action associated to a tap in the view controller primary button
2528
/// - Parameter viewController: usually the view controller sending the tap
2629
func didTapPrimaryButton(in viewController: UIViewController?)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Foundation
2+
3+
extension NumberFormatter {
4+
/// Get a double from a string value, with locale taken into consideration.
5+
///
6+
static func double(from string: String, locale: Locale = .current) -> Double? {
7+
let formatter = NumberFormatter()
8+
formatter.locale = locale
9+
let number = formatter.number(from: string)
10+
return number?.doubleValue
11+
}
12+
13+
/// Get a string from a number with locale taken into consideration.
14+
///
15+
static func localizedString(from number: NSNumber, locale: Locale = .current) -> String? {
16+
let formatter = NumberFormatter()
17+
formatter.locale = locale
18+
formatter.usesGroupingSeparator = true
19+
formatter.groupingSize = 3
20+
formatter.formatterBehavior = .behavior10_4
21+
formatter.numberStyle = .decimal
22+
formatter.generatesDecimalNumbers = true
23+
formatter.roundingMode = .halfUp
24+
return formatter.string(from: number)
25+
}
26+
}

0 commit comments

Comments
 (0)