Skip to content

Commit 43c667d

Browse files
authored
Merge pull request #8497 from woocommerce/feat/8377-sc-selling-status-platforms
Store creation M3: profiler question - optional selling platforms
2 parents 148b0a9 + 153cb15 commit 43c667d

File tree

8 files changed

+399
-23
lines changed

8 files changed

+399
-23
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import SwiftUI
2+
3+
/// Shows the followup question to the store selling status question in the store creation flow, for users who are already online.
4+
/// Displays a list of eCommerce platforms for the user to choose the ones they're already selling on.
5+
struct StoreCreationSellingPlatformsQuestionView: View {
6+
@ObservedObject private var viewModel: StoreCreationSellingPlatformsQuestionViewModel
7+
8+
init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
9+
self.viewModel = StoreCreationSellingPlatformsQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip)
10+
}
11+
12+
var body: some View {
13+
OptionalStoreCreationProfilerQuestionView(viewModel: viewModel) {
14+
VStack(spacing: 16) {
15+
ForEach(viewModel.platforms, id: \.self) { platform in
16+
Button(action: {
17+
viewModel.selectPlatform(platform)
18+
}, label: {
19+
HStack {
20+
Text(platform.description)
21+
Spacer()
22+
}
23+
})
24+
.buttonStyle(SelectableSecondaryButtonStyle(isSelected: viewModel.selectedPlatforms.contains(platform)))
25+
}
26+
}
27+
}
28+
}
29+
}
30+
31+
struct StoreCreationSellingPlatformsQuestionView_Previews: PreviewProvider {
32+
static var previews: some View {
33+
NavigationView {
34+
StoreCreationSellingPlatformsQuestionView(storeName: "New Year Store", onContinue: {}, onSkip: {})
35+
}
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
import Combine
2+
import Foundation
3+
4+
/// View model for the second step of `StoreCreationSellingStatusQuestionView`, an optional profiler question about store selling status
5+
/// in the store creation flow.
6+
/// When the user previously indicates that they're already selling online, this view model provides data for the followup question on the platforms they're
7+
/// already selling on.
8+
@MainActor
9+
final class StoreCreationSellingPlatformsQuestionViewModel: StoreCreationProfilerQuestionViewModel, ObservableObject {
10+
/// Other online platforms that the user might be selling. Source of truth:
11+
/// https://github.com/Automattic/woocommerce.com/blob/trunk/themes/woo/start/config/options.json
12+
enum Platform: Equatable, CaseIterable {
13+
case amazon
14+
case bigCartel
15+
case bigCommerce
16+
case eBay
17+
case etsy
18+
case facebookMarketplace
19+
case googleShopping
20+
case pinterest
21+
case shopify
22+
case square
23+
case squarespace
24+
case wix
25+
case wordPress
26+
}
27+
28+
let topHeader: String
29+
30+
let title: String = Localization.title
31+
32+
let subtitle: String = Localization.subtitle
33+
34+
/// Question content.
35+
/// TODO: 8376 - update values when API is ready.
36+
let platforms: [Platform] = Platform.allCases
37+
38+
@Published private(set) var selectedPlatforms: Set<Platform> = []
39+
40+
private let onContinue: () -> Void
41+
private let onSkip: () -> Void
42+
43+
init(storeName: String,
44+
onContinue: @escaping () -> Void,
45+
onSkip: @escaping () -> Void) {
46+
self.topHeader = storeName
47+
self.onContinue = onContinue
48+
self.onSkip = onSkip
49+
}
50+
}
51+
52+
extension StoreCreationSellingPlatformsQuestionViewModel: OptionalStoreCreationProfilerQuestionViewModel {
53+
func continueButtonTapped() async {
54+
// TODO: submission API.
55+
onContinue()
56+
}
57+
58+
func skipButtonTapped() {
59+
onSkip()
60+
}
61+
}
62+
63+
extension StoreCreationSellingPlatformsQuestionViewModel {
64+
/// Called when a platform is selected.
65+
func selectPlatform(_ platform: Platform) {
66+
if selectedPlatforms.contains(platform) {
67+
selectedPlatforms.remove(platform)
68+
} else {
69+
selectedPlatforms.insert(platform)
70+
}
71+
}
72+
}
73+
74+
extension StoreCreationSellingPlatformsQuestionViewModel.Platform {
75+
var description: String {
76+
switch self {
77+
case .amazon:
78+
return NSLocalizedString(
79+
"Amazon",
80+
comment: "Option in the store creation selling platforms question."
81+
)
82+
case .bigCartel:
83+
return NSLocalizedString(
84+
"Big Cartel",
85+
comment: "Option in the store creation selling platforms question."
86+
)
87+
case .bigCommerce:
88+
return NSLocalizedString(
89+
"Big Commerce",
90+
comment: "Option in the store creation selling platforms question."
91+
)
92+
case .eBay:
93+
return NSLocalizedString(
94+
"Ebay",
95+
comment: "Option in the store creation selling platforms question."
96+
)
97+
case .etsy:
98+
return NSLocalizedString(
99+
"Etsy",
100+
comment: "Option in the store creation selling platforms question."
101+
)
102+
case .facebookMarketplace:
103+
return NSLocalizedString(
104+
"Facebook Marketplace",
105+
comment: "Option in the store creation selling platforms question."
106+
)
107+
case .googleShopping:
108+
return NSLocalizedString(
109+
"Google Shopping",
110+
comment: "Option in the store creation selling platforms question."
111+
)
112+
case .pinterest:
113+
return NSLocalizedString(
114+
"Pinterest",
115+
comment: "Option in the store creation selling platforms question."
116+
)
117+
case .shopify:
118+
return NSLocalizedString(
119+
"Shopify",
120+
comment: "Option in the store creation selling platforms question."
121+
)
122+
case .square:
123+
return NSLocalizedString(
124+
"Square",
125+
comment: "Option in the store creation selling platforms question."
126+
)
127+
case .squarespace:
128+
return NSLocalizedString(
129+
"Squarespace",
130+
comment: "Option in the store creation selling platforms question."
131+
)
132+
case .wix:
133+
return NSLocalizedString(
134+
"Wix",
135+
comment: "Option in the store creation selling platforms question."
136+
)
137+
case .wordPress:
138+
return NSLocalizedString(
139+
"WordPress",
140+
comment: "Option in the store creation selling platforms question."
141+
)
142+
}
143+
}
144+
}
145+
146+
private extension StoreCreationSellingPlatformsQuestionViewModel {
147+
enum Localization {
148+
static let title = NSLocalizedString(
149+
"In which platform are you currently selling?",
150+
comment: "Title of the store creation profiler question about the store selling platforms."
151+
)
152+
static let subtitle = NSLocalizedString(
153+
"You can choose multiple ones.",
154+
comment: "Subtitle of the store creation profiler question about the store selling platforms."
155+
)
156+
}
157+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import SwiftUI
2+
3+
/// Hosting controller that wraps the `StoreCreationSellingStatusQuestionContainerView`.
4+
final class StoreCreationSellingStatusQuestionHostingController: UIHostingController<StoreCreationSellingStatusQuestionContainerView> {
5+
init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
6+
super.init(rootView: StoreCreationSellingStatusQuestionContainerView(storeName: storeName, onContinue: onContinue, onSkip: onSkip))
7+
}
8+
9+
@available(*, unavailable)
10+
required dynamic init?(coder aDecoder: NSCoder) {
11+
fatalError("init(coder:) has not been implemented")
12+
}
13+
14+
override func viewDidLoad() {
15+
super.viewDidLoad()
16+
17+
configureTransparentNavigationBar()
18+
}
19+
}
20+
21+
/// Displays the selling status question initially. If the user chooses the "I'm already selling online" option, the selling
22+
/// platforms question is shown.
23+
struct StoreCreationSellingStatusQuestionContainerView: View {
24+
@StateObject private var viewModel: StoreCreationSellingStatusQuestionViewModel
25+
private let storeName: String
26+
private let onContinue: () -> Void
27+
private let onSkip: () -> Void
28+
29+
init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
30+
self._viewModel = StateObject(wrappedValue: StoreCreationSellingStatusQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip))
31+
self.storeName = storeName
32+
self.onContinue = onContinue
33+
self.onSkip = onSkip
34+
}
35+
36+
var body: some View {
37+
if viewModel.isAlreadySellingOnline {
38+
StoreCreationSellingPlatformsQuestionView(storeName: storeName, onContinue: onContinue, onSkip: onSkip)
39+
} else {
40+
StoreCreationSellingStatusQuestionView(viewModel: viewModel)
41+
}
42+
}
43+
}
44+
45+
struct StoreCreationSellingStatusQuestionContainerView_Previews: PreviewProvider {
46+
static var previews: some View {
47+
NavigationView {
48+
StoreCreationSellingStatusQuestionContainerView(storeName: "New Year Store", onContinue: {}, onSkip: {})
49+
}
50+
}
51+
}
Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,11 @@
11
import SwiftUI
22

3-
/// Hosting controller that wraps the `StoreCreationSellingStatusQuestionView`.
4-
final class StoreCreationSellingStatusQuestionHostingController: UIHostingController<StoreCreationSellingStatusQuestionView> {
5-
init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
6-
super.init(rootView: StoreCreationSellingStatusQuestionView(storeName: storeName, onContinue: onContinue, onSkip: onSkip))
7-
}
8-
9-
@available(*, unavailable)
10-
required dynamic init?(coder aDecoder: NSCoder) {
11-
fatalError("init(coder:) has not been implemented")
12-
}
13-
14-
override func viewDidLoad() {
15-
super.viewDidLoad()
16-
17-
configureTransparentNavigationBar()
18-
}
19-
}
20-
213
/// Shows the store selling status question in the store creation flow.
224
struct StoreCreationSellingStatusQuestionView: View {
235
@ObservedObject private var viewModel: StoreCreationSellingStatusQuestionViewModel
246

25-
init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
26-
self.viewModel = StoreCreationSellingStatusQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip)
7+
init(viewModel: StoreCreationSellingStatusQuestionViewModel) {
8+
self.viewModel = viewModel
279
}
2810

2911
var body: some View {
@@ -47,6 +29,8 @@ struct StoreCreationSellingStatusQuestionView: View {
4729

4830
struct StoreCreationSellingStatusQuestionView_Previews: PreviewProvider {
4931
static var previews: some View {
50-
StoreCreationSellingStatusQuestionView(storeName: "New Year Store", onContinue: {}, onSkip: {})
32+
NavigationView {
33+
StoreCreationSellingStatusQuestionView(viewModel: .init(storeName: "New Year Store", onContinue: {}, onSkip: {}))
34+
}
5135
}
5236
}

WooCommerce/Classes/Authentication/Store Creation/Profiler/Selling Status/StoreCreationSellingStatusQuestionViewModel.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ final class StoreCreationSellingStatusQuestionViewModel: StoreCreationProfilerQu
2727

2828
@Published private(set) var selectedStatus: SellingStatus?
2929

30+
/// Set to `true` when the user selects the selling status as "I am already selling online".
31+
@Published private(set) var isAlreadySellingOnline: Bool = false
32+
3033
private let onContinue: () -> Void
3134
private let onSkip: () -> Void
3235

@@ -36,6 +39,10 @@ final class StoreCreationSellingStatusQuestionViewModel: StoreCreationProfilerQu
3639
self.topHeader = storeName
3740
self.onContinue = onContinue
3841
self.onSkip = onSkip
42+
43+
$selectedStatus
44+
.map { $0 == .alreadySellingOnline }
45+
.assign(to: &$isAlreadySellingOnline)
3946
}
4047
}
4148

@@ -44,6 +51,11 @@ extension StoreCreationSellingStatusQuestionViewModel: OptionalStoreCreationProf
4451
guard selectedStatus != nil else {
4552
return onSkip()
4653
}
54+
guard selectedStatus != .alreadySellingOnline else {
55+
// Handled in `StoreCreationSellingPlatformsQuestionViewModel`.
56+
return
57+
}
58+
// TODO: submission API.
4759
onContinue()
4860
}
4961

0 commit comments

Comments
 (0)