Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import SwiftUI

/// Shows the followup question to the store selling status question in the store creation flow, for users who are already online.
/// Displays a list of eCommerce platforms for the user to choose the ones they're already selling on.
struct StoreCreationSellingPlatformsQuestionView: View {
@ObservedObject private var viewModel: StoreCreationSellingPlatformsQuestionViewModel

init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
self.viewModel = StoreCreationSellingPlatformsQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip)
}

var body: some View {
OptionalStoreCreationProfilerQuestionView(viewModel: viewModel) {
VStack(spacing: 16) {
ForEach(viewModel.platforms, id: \.self) { platform in
Button(action: {
viewModel.selectPlatform(platform)
}, label: {
HStack {
Text(platform.description)
Spacer()
}
})
.buttonStyle(SelectableSecondaryButtonStyle(isSelected: viewModel.selectedPlatforms.contains(platform)))
}
}
}
}
}

struct StoreCreationSellingPlatformsQuestionView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
StoreCreationSellingPlatformsQuestionView(storeName: "New Year Store", onContinue: {}, onSkip: {})
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import Combine
import Foundation

/// View model for the second step of `StoreCreationSellingStatusQuestionView`, an optional profiler question about store selling status
/// in the store creation flow.
/// 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
/// already selling on.
@MainActor
final class StoreCreationSellingPlatformsQuestionViewModel: StoreCreationProfilerQuestionViewModel, ObservableObject {
/// Other online platforms that the user might be selling. Source of truth:
/// https://github.com/Automattic/woocommerce.com/blob/trunk/themes/woo/start/config/options.json
enum Platform: Equatable {
case amazon
case bigCartel
case bigCommerce
case eBay
case etsy
case facebookMarketplace
case googleShopping
case pinterest
case shopify
case square
case squarespace
case wix
case wordPress
}

let topHeader: String

let title: String = Localization.title

let subtitle: String = Localization.subtitle

/// Question content.
/// TODO: 8376 - update values when API is ready.
let platforms: [Platform] = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: If we will always use all the cases from Platform we could make Platform confirm to CaseIterable and use let platforms = Platform.allCases here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great suggestion! it looks like allCases follows the order of the case declarations so that's good for the use case. updated in 153cb15

.amazon, .bigCartel, .bigCommerce, .eBay, .etsy, .facebookMarketplace, .googleShopping, .pinterest, .shopify, .square, .squarespace, .wix, .wordPress
]

@Published private(set) var selectedPlatforms: Set<Platform> = []

private let onContinue: () -> Void
private let onSkip: () -> Void

init(storeName: String,
onContinue: @escaping () -> Void,
onSkip: @escaping () -> Void) {
self.topHeader = storeName
self.onContinue = onContinue
self.onSkip = onSkip
}
}

extension StoreCreationSellingPlatformsQuestionViewModel: OptionalStoreCreationProfilerQuestionViewModel {
func continueButtonTapped() async {
// TODO: submission API.
onContinue()
}

func skipButtonTapped() {
onSkip()
}
}

extension StoreCreationSellingPlatformsQuestionViewModel {
/// Called when a platform is selected.
func selectPlatform(_ platform: Platform) {
if selectedPlatforms.contains(platform) {
selectedPlatforms.remove(platform)
} else {
selectedPlatforms.insert(platform)
}
}
}

extension StoreCreationSellingPlatformsQuestionViewModel.Platform {
var description: String {
switch self {
case .amazon:
return NSLocalizedString(
"Amazon",
comment: "Option in the store creation selling platforms question."
)
case .bigCartel:
return NSLocalizedString(
"Big Cartel",
comment: "Option in the store creation selling platforms question."
)
case .bigCommerce:
return NSLocalizedString(
"Big Commerce",
comment: "Option in the store creation selling platforms question."
)
case .eBay:
return NSLocalizedString(
"Ebay",
comment: "Option in the store creation selling platforms question."
)
case .etsy:
return NSLocalizedString(
"Etsy",
comment: "Option in the store creation selling platforms question."
)
case .facebookMarketplace:
return NSLocalizedString(
"Facebook Marketplace",
comment: "Option in the store creation selling platforms question."
)
case .googleShopping:
return NSLocalizedString(
"Google Shopping",
comment: "Option in the store creation selling platforms question."
)
case .pinterest:
return NSLocalizedString(
"Pinterest",
comment: "Option in the store creation selling platforms question."
)
case .shopify:
return NSLocalizedString(
"Shopify",
comment: "Option in the store creation selling platforms question."
)
case .square:
return NSLocalizedString(
"Square",
comment: "Option in the store creation selling platforms question."
)
case .squarespace:
return NSLocalizedString(
"Squarespace",
comment: "Option in the store creation selling platforms question."
)
case .wix:
return NSLocalizedString(
"Wix",
comment: "Option in the store creation selling platforms question."
)
case .wordPress:
return NSLocalizedString(
"WordPress",
comment: "Option in the store creation selling platforms question."
)
}
}
}

private extension StoreCreationSellingPlatformsQuestionViewModel {
enum Localization {
static let title = NSLocalizedString(
"In which platform are you currently selling?",
comment: "Title of the store creation profiler question about the store selling platforms."
)
static let subtitle = NSLocalizedString(
"You can choose multiple ones.",
comment: "Subtitle of the store creation profiler question about the store selling platforms."
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import SwiftUI

/// Hosting controller that wraps the `StoreCreationSellingStatusQuestionContainerView`.
final class StoreCreationSellingStatusQuestionHostingController: UIHostingController<StoreCreationSellingStatusQuestionContainerView> {
init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
super.init(rootView: StoreCreationSellingStatusQuestionContainerView(storeName: storeName, onContinue: onContinue, onSkip: onSkip))
}

@available(*, unavailable)
required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

configureTransparentNavigationBar()
}
}

/// Displays the selling status question initially. If the user chooses the "I'm already selling online" option, the selling
/// platforms question is shown.
struct StoreCreationSellingStatusQuestionContainerView: View {
@StateObject private var viewModel: StoreCreationSellingStatusQuestionViewModel
private let storeName: String
private let onContinue: () -> Void
private let onSkip: () -> Void

init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
self._viewModel = StateObject(wrappedValue: StoreCreationSellingStatusQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip))
self.storeName = storeName
self.onContinue = onContinue
self.onSkip = onSkip
}

var body: some View {
if viewModel.isAlreadySellingOnline {
StoreCreationSellingPlatformsQuestionView(storeName: storeName, onContinue: onContinue, onSkip: onSkip)
} else {
StoreCreationSellingStatusQuestionView(viewModel: viewModel)
}
}
}

struct StoreCreationSellingStatusQuestionContainerView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
StoreCreationSellingStatusQuestionContainerView(storeName: "New Year Store", onContinue: {}, onSkip: {})
}
}
}
Original file line number Diff line number Diff line change
@@ -1,29 +1,11 @@
import SwiftUI

/// Hosting controller that wraps the `StoreCreationSellingStatusQuestionView`.
final class StoreCreationSellingStatusQuestionHostingController: UIHostingController<StoreCreationSellingStatusQuestionView> {
init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
super.init(rootView: StoreCreationSellingStatusQuestionView(storeName: storeName, onContinue: onContinue, onSkip: onSkip))
}

@available(*, unavailable)
required dynamic init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override func viewDidLoad() {
super.viewDidLoad()

configureTransparentNavigationBar()
}
}

/// Shows the store selling status question in the store creation flow.
struct StoreCreationSellingStatusQuestionView: View {
@ObservedObject private var viewModel: StoreCreationSellingStatusQuestionViewModel

init(storeName: String, onContinue: @escaping () -> Void, onSkip: @escaping () -> Void) {
self.viewModel = StoreCreationSellingStatusQuestionViewModel(storeName: storeName, onContinue: onContinue, onSkip: onSkip)
init(viewModel: StoreCreationSellingStatusQuestionViewModel) {
self.viewModel = viewModel
}

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

struct StoreCreationSellingStatusQuestionView_Previews: PreviewProvider {
static var previews: some View {
StoreCreationSellingStatusQuestionView(storeName: "New Year Store", onContinue: {}, onSkip: {})
NavigationView {
StoreCreationSellingStatusQuestionView(viewModel: .init(storeName: "New Year Store", onContinue: {}, onSkip: {}))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ final class StoreCreationSellingStatusQuestionViewModel: StoreCreationProfilerQu

@Published private(set) var selectedStatus: SellingStatus?

/// Set to `true` when the user selects the selling status as "I am already selling online".
@Published private(set) var isAlreadySellingOnline: Bool = false

private let onContinue: () -> Void
private let onSkip: () -> Void

Expand All @@ -36,6 +39,10 @@ final class StoreCreationSellingStatusQuestionViewModel: StoreCreationProfilerQu
self.topHeader = storeName
self.onContinue = onContinue
self.onSkip = onSkip

$selectedStatus
.map { $0 == .alreadySellingOnline }
.assign(to: &$isAlreadySellingOnline)
}
}

Expand All @@ -44,6 +51,11 @@ extension StoreCreationSellingStatusQuestionViewModel: OptionalStoreCreationProf
guard selectedStatus != nil else {
return onSkip()
}
guard selectedStatus != .alreadySellingOnline else {
// Handled in `StoreCreationSellingPlatformsQuestionViewModel`.
return
}
// TODO: submission API.
onContinue()
}

Expand Down
Loading