Skip to content

Commit 3611a9d

Browse files
authored
Merge pull request #8545 from woocommerce/feat/8538-store-creation-support-cta
Store creation: add support CTA to applicable screens
2 parents 2b593a6 + 995e714 commit 3611a9d

File tree

14 files changed

+205
-88
lines changed

14 files changed

+205
-88
lines changed

WooCommerce/Classes/Authentication/Store Creation/Profiler/Country/StoreCreationCountryButton.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,13 @@ struct StoreCreationCountryButton_Previews: PreviewProvider {
3030
static var previews: some View {
3131
VStack {
3232
StoreCreationCountryButton(countryCode: .US,
33-
viewModel: .init(storeName: "", onContinue: { _ in }))
33+
viewModel: .init(storeName: "",
34+
onContinue: { _ in },
35+
onSupport: {}))
3436
StoreCreationCountryButton(countryCode: .UM,
35-
viewModel: .init(storeName: "", onContinue: { _ in }))
37+
viewModel: .init(storeName: "",
38+
onContinue: { _ in },
39+
onSupport: {}))
3640
}
3741
}
3842
}

WooCommerce/Classes/Authentication/Store Creation/Profiler/Country/StoreCreationCountryQuestionView.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ struct StoreCreationCountryQuestionView_Previews: PreviewProvider {
5858
NavigationView {
5959
StoreCreationCountryQuestionView(viewModel: .init(storeName: "only in 2023",
6060
currentLocale: Locale.init(identifier: "en_US"),
61-
onContinue: { _ in }))
61+
onContinue: { _ in },
62+
onSupport: {}))
6263
}
6364
}
6465
}

WooCommerce/Classes/Authentication/Store Creation/Profiler/Country/StoreCreationCountryQuestionViewModel.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,15 @@ final class StoreCreationCountryQuestionViewModel: StoreCreationProfilerQuestion
2626
@Published private var isContinueButtonEnabledValue: Bool = false
2727

2828
private let onContinue: (CountryCode) -> Void
29+
private let onSupport: () -> Void
2930

3031
init(storeName: String,
3132
currentLocale: Locale = .current,
32-
onContinue: @escaping (CountryCode) -> Void) {
33+
onContinue: @escaping (CountryCode) -> Void,
34+
onSupport: @escaping () -> Void) {
3335
self.topHeader = storeName
3436
self.onContinue = onContinue
37+
self.onSupport = onSupport
3538

3639
currentCountryCode = currentLocale.regionCode.map { CountryCode(rawValue: $0) } ?? nil
3740
selectedCountryCode = currentCountryCode
@@ -65,6 +68,10 @@ extension StoreCreationCountryQuestionViewModel: RequiredStoreCreationProfilerQu
6568
}
6669
onContinue(selectedCountryCode)
6770
}
71+
72+
func supportButtonTapped() {
73+
onSupport()
74+
}
6875
}
6976

7077
extension StoreCreationCountryQuestionViewModel {

WooCommerce/Classes/Authentication/Store Creation/Profiler/Country/StoreCreationCountrySectionView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ struct StoreCreationCountrySectionView: View {
2828

2929
struct StoreCreationCountrySectionView_Previews: PreviewProvider {
3030
static var previews: some View {
31-
StoreCreationCountrySectionView(header: "EXAMPLES", countryCodes: [.FJ, .UM, .US], viewModel: .init(storeName: "", onContinue: { _ in }))
31+
StoreCreationCountrySectionView(header: "EXAMPLES", countryCodes: [.FJ, .UM, .US], viewModel: .init(storeName: "", onContinue: { _ in }, onSupport: {}))
3232
}
3333
}

WooCommerce/Classes/Authentication/Store Creation/Profiler/RequiredStoreCreationProfilerQuestionView.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ protocol RequiredStoreCreationProfilerQuestionViewModel {
77
/// Called when the continue button is tapped.
88
func continueButtonTapped() async
99

10+
/// Called when the Help & Support button is tapped.
11+
func supportButtonTapped()
12+
1013
/// Whether the continue button is enabled for the user to continue.
1114
var isContinueButtonEnabled: AnyPublisher<Bool, Never> { get }
1215
}
@@ -46,6 +49,13 @@ struct RequiredStoreCreationProfilerQuestionView<QuestionContent: View>: View {
4649
}
4750
.background(Color(.systemBackground))
4851
}
52+
.toolbar {
53+
ToolbarItem(placement: .navigationBarTrailing) {
54+
SupportButton {
55+
viewModel.supportButtonTapped()
56+
}
57+
}
58+
}
4959
// Disables large title to avoid a large gap below the navigation bar.
5060
.navigationBarTitleDisplayMode(.inline)
5161
.onReceive(viewModel.isContinueButtonEnabled) { isContinueButtonEnabled in
@@ -75,6 +85,7 @@ private final class StoreCreationQuestionPreviewViewModel: StoreCreationProfiler
7585
$isContinueButtonEnabledValue.eraseToAnyPublisher()
7686
}
7787
func continueButtonTapped() async {}
88+
func supportButtonTapped() {}
7889
}
7990

8091
struct RequiredStoreCreationProfilerQuestionView_Previews: PreviewProvider {

WooCommerce/Classes/Authentication/Store Creation/Store name/StoreNameForm.swift

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,12 @@ import SwiftUI
22

33
/// Hosting controller that wraps the `StoreNameForm`.
44
final class StoreNameFormHostingController: UIHostingController<StoreNameForm> {
5-
private let onContinue: (String) -> Void
6-
private let onClose: () -> Void
7-
85
init(onContinue: @escaping (String) -> Void,
9-
onClose: @escaping () -> Void) {
10-
self.onContinue = onContinue
11-
self.onClose = onClose
12-
super.init(rootView: StoreNameForm())
13-
14-
rootView.onContinue = { [weak self] storeName in
15-
self?.onContinue(storeName)
16-
}
6+
onClose: @escaping () -> Void,
7+
onSupport: @escaping () -> Void) {
8+
super.init(rootView: StoreNameForm(onContinue: onContinue,
9+
onClose: onClose,
10+
onSupport: onSupport))
1711
}
1812

1913
@available(*, unavailable)
@@ -24,38 +18,15 @@ final class StoreNameFormHostingController: UIHostingController<StoreNameForm> {
2418
override func viewDidLoad() {
2519
super.viewDidLoad()
2620

27-
configureNavigationBarAppearance()
28-
}
29-
30-
/// Shows a transparent navigation bar without a bottom border and with a close button to dismiss.
31-
func configureNavigationBarAppearance() {
32-
addCloseNavigationBarButton(title: Localization.cancelButtonTitle,
33-
target: self,
34-
action: #selector(closeButtonTapped))
35-
let appearance = UINavigationBarAppearance()
36-
appearance.configureWithTransparentBackground()
37-
appearance.backgroundColor = .systemBackground
38-
39-
navigationItem.standardAppearance = appearance
40-
navigationItem.scrollEdgeAppearance = appearance
41-
navigationItem.compactAppearance = appearance
42-
}
43-
44-
@objc private func closeButtonTapped() {
45-
onClose()
46-
}
47-
}
48-
49-
private extension StoreNameFormHostingController {
50-
enum Localization {
51-
static let cancelButtonTitle = NSLocalizedString("Cancel", comment: "Navigation bar button on the store name form to leave the store creation flow.")
21+
configureTransparentNavigationBar()
5222
}
5323
}
5424

5525
/// Allows the user to enter a store name during the store creation flow.
5626
struct StoreNameForm: View {
57-
/// Set in the hosting controller.
58-
var onContinue: (String) -> Void = { _ in }
27+
let onContinue: (String) -> Void
28+
let onClose: () -> Void
29+
let onSupport: () -> Void
5930

6031
@State private var name: String = ""
6132

@@ -104,6 +75,19 @@ struct StoreNameForm: View {
10475
.buttonStyle(PrimaryButtonStyle())
10576
.disabled(name.isEmpty)
10677
}
78+
.toolbar {
79+
ToolbarItem(placement: .navigationBarLeading) {
80+
Button(Localization.cancelButtonTitle) {
81+
onClose()
82+
}
83+
.buttonStyle(TextButtonStyle())
84+
}
85+
ToolbarItem(placement: .navigationBarTrailing) {
86+
SupportButton {
87+
onSupport()
88+
}
89+
}
90+
}
10791
// Disables large title to avoid a large gap below the navigation bar.
10892
.navigationBarTitleDisplayMode(.inline)
10993
// Hides the back button and shows a close button in the hosting controller instead.
@@ -148,11 +132,19 @@ private extension StoreNameForm {
148132
"Continue",
149133
comment: "Title of the button on the store creation store name form to continue."
150134
)
135+
static let cancelButtonTitle = NSLocalizedString(
136+
"Cancel",
137+
comment: "Navigation bar button on the store name form to leave the store creation flow."
138+
)
151139
}
152140
}
153141

154142
struct StoreNameForm_Previews: PreviewProvider {
155143
static var previews: some View {
156-
StoreNameForm()
144+
NavigationView {
145+
StoreNameForm(onContinue: { _ in },
146+
onClose: {},
147+
onSupport: {})
148+
}
157149
}
158150
}

WooCommerce/Classes/Authentication/Store Creation/StoreCreationCoordinator.swift

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ private extension StoreCreationCoordinator {
148148
}
149149
} onClose: { [weak self] in
150150
self?.showDiscardChangesAlert(flow: .native)
151+
} onSupport: { [weak self] in
152+
self?.showSupport(from: navigationController)
151153
}
152154
navigationController.pushViewController(storeNameForm, animated: true)
153155
analytics.track(event: .StoreCreation.siteCreationStep(step: .storeName))
@@ -297,6 +299,10 @@ private extension StoreCreationCoordinator {
297299
// Presents the alert with the presented webview.
298300
navigationController.presentedViewController?.present(alert, animated: true)
299301
}
302+
303+
func showSupport(from navigationController: UINavigationController) {
304+
ZendeskProvider.shared.showNewRequestIfPossible(from: navigationController, with: "origin:store-creation")
305+
}
300306
}
301307

302308
// MARK: - Store creation M2
@@ -349,13 +355,15 @@ private extension StoreCreationCoordinator {
349355
planToPurchase: WPComPlanProduct) {
350356
let questionController = StoreCreationCountryQuestionHostingController(viewModel:
351357
.init(storeName: storeName) { [weak self] countryCode in
352-
guard let self else { return }
353-
self.showDomainSelector(from: navigationController,
354-
storeName: storeName,
355-
categoryName: categoryName,
356-
countryCode: countryCode,
357-
planToPurchase: planToPurchase)
358-
})
358+
guard let self else { return }
359+
self.showDomainSelector(from: navigationController,
360+
storeName: storeName,
361+
categoryName: categoryName,
362+
countryCode: countryCode,
363+
planToPurchase: planToPurchase)
364+
} onSupport: { [weak self] in
365+
self?.showSupport(from: navigationController)
366+
})
359367
navigationController.pushViewController(questionController, animated: true)
360368
// TODO: analytics
361369
}
@@ -375,6 +383,8 @@ private extension StoreCreationCoordinator {
375383
countryCode: countryCode,
376384
domain: domain,
377385
planToPurchase: planToPurchase)
386+
}, onSupport: { [weak self] in
387+
self?.showSupport(from: navigationController)
378388
})
379389
navigationController.pushViewController(domainSelector, animated: true)
380390
analytics.track(event: .StoreCreation.siteCreationStep(step: .domainPicker))
@@ -426,6 +436,8 @@ private extension StoreCreationCoordinator {
426436
planToPurchase: planToPurchase,
427437
siteID: result.siteID,
428438
siteSlug: result.siteSlug)
439+
} onSupport: { [weak self] in
440+
self?.showSupport(from: navigationController)
429441
}
430442
navigationController.pushViewController(storeSummary, animated: true)
431443
analytics.track(event: .StoreCreation.siteCreationStep(step: .storeSummary))

WooCommerce/Classes/Authentication/Store Creation/StoreCreationSummaryView.swift

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@ import SwiftUI
22

33
/// Hosting controller that wraps the `StoreCreationSummaryView`.
44
final class StoreCreationSummaryHostingController: UIHostingController<StoreCreationSummaryView> {
5-
private let onContinueToPayment: () -> Void
6-
75
init(viewModel: StoreCreationSummaryViewModel,
8-
onContinueToPayment: @escaping () -> Void) {
9-
self.onContinueToPayment = onContinueToPayment
10-
super.init(rootView: StoreCreationSummaryView(viewModel: viewModel))
11-
12-
rootView.onContinueToPayment = { [weak self] in
13-
self?.onContinueToPayment()
14-
}
6+
onContinueToPayment: @escaping () -> Void,
7+
onSupport: @escaping () -> Void) {
8+
super.init(rootView: StoreCreationSummaryView(viewModel: viewModel,
9+
onContinueToPayment: onContinueToPayment,
10+
onSupport: onSupport))
1511
}
1612

1713
@available(*, unavailable)
@@ -52,13 +48,17 @@ struct StoreCreationSummaryViewModel {
5248

5349
/// Displays a summary of the store creation flow with the store information (e.g. store name, store slug).
5450
struct StoreCreationSummaryView: View {
55-
/// Set in the hosting controller.
56-
var onContinueToPayment: (() -> Void) = {}
51+
private let onContinueToPayment: () -> Void
52+
private let onSupport: () -> Void
5753

5854
private let viewModel: StoreCreationSummaryViewModel
5955

60-
init(viewModel: StoreCreationSummaryViewModel) {
56+
init(viewModel: StoreCreationSummaryViewModel,
57+
onContinueToPayment: @escaping () -> Void,
58+
onSupport: @escaping () -> Void) {
6159
self.viewModel = viewModel
60+
self.onContinueToPayment = onContinueToPayment
61+
self.onSupport = onSupport
6262
}
6363

6464
var body: some View {
@@ -126,6 +126,13 @@ struct StoreCreationSummaryView: View {
126126
.padding(Layout.defaultButtonPadding)
127127
}
128128
}
129+
.toolbar {
130+
ToolbarItem(placement: .navigationBarTrailing) {
131+
SupportButton {
132+
onSupport()
133+
}
134+
}
135+
}
129136
.navigationTitle(Localization.title)
130137
.navigationBarTitleDisplayMode(.large)
131138
}
@@ -168,12 +175,16 @@ struct StoreCreationSummaryView_Previews: PreviewProvider {
168175
.init(storeName: "Fruity shop",
169176
storeSlug: "fruityshop.com",
170177
categoryName: "Arts and Crafts",
171-
countryCode: .UG))
178+
countryCode: .UG),
179+
onContinueToPayment: {},
180+
onSupport: {})
172181
StoreCreationSummaryView(viewModel:
173182
.init(storeName: "Fruity shop",
174183
storeSlug: "fruityshop.com",
175184
categoryName: "Arts and Crafts",
176-
countryCode: nil))
185+
countryCode: nil),
186+
onContinueToPayment: {},
187+
onSupport: {})
177188
.preferredColorScheme(.dark)
178189
}
179190
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import SwiftUI
2+
3+
/// A support button with a help icon that is currently shown in the navigation bar.
4+
struct SupportButton: View {
5+
let onTapped: () -> Void
6+
7+
var body: some View {
8+
Button {
9+
onTapped()
10+
} label: {
11+
Image(uiImage: .helpOutlineImage)
12+
.renderingMode(.template)
13+
.linkStyle()
14+
}
15+
.accessibilityLabel(Localization.accessibilityLabel)
16+
}
17+
}
18+
19+
private extension SupportButton {
20+
enum Localization {
21+
static let accessibilityLabel = NSLocalizedString(
22+
"Help & Support",
23+
comment: "Accessibility label for the Help & Support image navigation bar button in the store creation flow."
24+
)
25+
}
26+
}
27+
28+
struct SupportButton_Previews: PreviewProvider {
29+
static var previews: some View {
30+
SupportButton(onTapped: {})
31+
}
32+
}

WooCommerce/Classes/Extensions/UIImage+Woo.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,12 @@ extension UIImage {
361361
return UIImage.gridicon(.heartOutline)
362362
}
363363

364+
/// Help Outline
365+
///
366+
static var helpOutlineImage: UIImage {
367+
return UIImage.gridicon(.helpOutline)
368+
}
369+
364370
/// House Image
365371
///
366372
static var houseImage: UIImage {

0 commit comments

Comments
 (0)