-
Notifications
You must be signed in to change notification settings - Fork 121
Store creation M2: WPCOM plan screen (UI only) #8120
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 3 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
49e5ae3
Add two image assets for store creation plan screen.
jaclync d6d3eaf
Create `StoreCreationPlanView` for WPCOM plan screen for purchase.
jaclync 52c1024
StoreCreationPlanView: update the preferred color theme using environ…
jaclync 2ddae25
Move store creation plan features to a separate SwiftUI view.
jaclync File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
214 changes: 214 additions & 0 deletions
214
WooCommerce/Classes/Authentication/Store Creation/Plan/StoreCreationPlanView.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| import SwiftUI | ||
|
|
||
| /// Hosting controller that wraps the `StoreCreationPlanView`. | ||
| final class StoreCreationPlanHostingController: UIHostingController<StoreCreationPlanView> { | ||
| private let onPurchase: () -> Void | ||
| private let onClose: () -> Void | ||
|
|
||
| init(viewModel: StoreCreationPlanViewModel, | ||
| onPurchase: @escaping () -> Void, | ||
| onClose: @escaping () -> Void) { | ||
| self.onPurchase = onPurchase | ||
| self.onClose = onClose | ||
| super.init(rootView: StoreCreationPlanView(viewModel: viewModel)) | ||
|
|
||
| rootView.onPurchase = { [weak self] in | ||
| self?.onPurchase() | ||
| } | ||
| } | ||
|
|
||
| @available(*, unavailable) | ||
| required dynamic init?(coder aDecoder: NSCoder) { | ||
| fatalError("init(coder:) has not been implemented") | ||
| } | ||
|
|
||
| override func viewDidLoad() { | ||
| super.viewDidLoad() | ||
|
|
||
| configureNavigationBarAppearance() | ||
| } | ||
|
|
||
| /// Shows a transparent navigation bar without a bottom border and with a close button to dismiss. | ||
| func configureNavigationBarAppearance() { | ||
| addCloseNavigationBarButton(target: self, action: #selector(closeButtonTapped)) | ||
|
|
||
| let appearance = UINavigationBarAppearance() | ||
| appearance.configureWithTransparentBackground() | ||
| appearance.backgroundColor = .withColorStudio(.wooCommercePurple, shade: .shade90) | ||
|
|
||
| navigationItem.standardAppearance = appearance | ||
| navigationItem.scrollEdgeAppearance = appearance | ||
| navigationItem.compactAppearance = appearance | ||
| } | ||
|
|
||
| @objc private func closeButtonTapped() { | ||
| onClose() | ||
| } | ||
| } | ||
|
|
||
| /// Displays the WPCOM eCommerce plan for purchase during the store creation flow. | ||
| struct StoreCreationPlanView: View { | ||
| /// Set in the hosting controller. | ||
| var onPurchase: (() -> Void) = {} | ||
|
|
||
| let viewModel: StoreCreationPlanViewModel | ||
|
|
||
| var body: some View { | ||
| VStack(alignment: .leading, spacing: 0) { | ||
| ScrollView { | ||
| VStack(alignment: .leading, spacing: 0) { | ||
| HStack(alignment: .center) { | ||
| VStack(alignment: .leading, spacing: 12) { | ||
| // Plan name. | ||
| Text(Localization.planTitle) | ||
| .fontWeight(.semibold) | ||
| .font(.title3) | ||
| .foregroundColor(.white) | ||
|
|
||
| // Price information. | ||
| HStack(alignment: .bottom) { | ||
| Text(viewModel.plan.displayPrice) | ||
| .fontWeight(.bold) | ||
| .foregroundColor(.white) | ||
| .largeTitleStyle() | ||
| Text(Localization.priceDuration) | ||
| .foregroundColor(Color(.secondaryLabel)) | ||
| .bodyStyle() | ||
| } | ||
| } | ||
| .padding(.horizontal, insets: .init(top: 0, leading: 24, bottom: 0, trailing: 0)) | ||
|
|
||
| Spacer() | ||
|
|
||
| Image(uiImage: .storeCreationPlanImage) | ||
| } | ||
|
|
||
| Divider() | ||
| .frame(height: Layout.dividerHeight) | ||
| .foregroundColor(Color(Layout.dividerColor)) | ||
| .padding(.horizontal, insets: Layout.defaultPadding) | ||
|
|
||
| VStack(alignment: .leading, spacing: 0) { | ||
| Spacer() | ||
| .frame(height: 8) | ||
|
|
||
| // Header label. | ||
| Text(Localization.subtitle) | ||
| .fontWeight(.bold) | ||
| .foregroundColor(Color(.white)) | ||
| .titleStyle() | ||
|
|
||
| Spacer() | ||
| .frame(height: 16) | ||
|
|
||
| // Powered by WPCOM. | ||
| HStack(spacing: 5) { | ||
| Text(Localization.poweredByWPCOMPrompt) | ||
| .foregroundColor(Color(.secondaryLabel)) | ||
| .footnoteStyle() | ||
| Image(uiImage: .wpcomLogoImage) | ||
| } | ||
|
|
||
| Spacer() | ||
| .frame(height: 32) | ||
|
|
||
| // Plan features. | ||
| VStack(alignment: .leading, spacing: 16) { | ||
| ForEach(viewModel.features, id: \.title) { feature in | ||
| HStack(spacing: 12) { | ||
| Image(uiImage: feature.icon) | ||
| .renderingMode(.template) | ||
| .foregroundColor(Color(.wooCommercePurple(.shade90))) | ||
| Text(feature.title) | ||
| .foregroundColor(Color(.label)) | ||
| .bodyStyle() | ||
| } | ||
| } | ||
| } | ||
| } | ||
| .padding(Layout.defaultPadding) | ||
| } | ||
| } | ||
|
|
||
| VStack(spacing: 0) { | ||
| Divider() | ||
| .frame(height: Layout.dividerHeight) | ||
| .foregroundColor(Color(Layout.dividerColor)) | ||
|
|
||
| // Continue button. | ||
| Button(String(format: Localization.continueButtonTitleFormat, viewModel.plan.displayPrice)) { | ||
| onPurchase() | ||
| } | ||
| .buttonStyle(PrimaryButtonStyle()) | ||
| .padding(Layout.defaultButtonPadding) | ||
|
|
||
| // Refund information. | ||
| Text(Localization.refundableNote) | ||
| .multilineTextAlignment(.center) | ||
| .foregroundColor(Color(.secondaryLabel)) | ||
| .bodyStyle() | ||
|
|
||
| Spacer() | ||
| .frame(height: 24) | ||
| } | ||
| } | ||
| .background(Color(.withColorStudio(.wooCommercePurple, shade: .shade90))) | ||
| // This screen is using the dark theme for both light and dark modes. | ||
| .environment(\.colorScheme, .dark) | ||
| } | ||
| } | ||
|
|
||
| private extension StoreCreationPlanView { | ||
| enum Layout { | ||
| static let dividerHeight: CGFloat = 1 | ||
| static let defaultPadding: EdgeInsets = .init(top: 16, leading: 16, bottom: 16, trailing: 16) | ||
| static let defaultButtonPadding: EdgeInsets = .init(top: 16, leading: 16, bottom: 16, trailing: 16) | ||
| static let dividerColor: UIColor = .separator | ||
| } | ||
|
|
||
| enum Localization { | ||
| static let planTitle = NSLocalizedString( | ||
| "eCommerce", | ||
| comment: "Title of the store creation plan on the plan screen.") | ||
| static let priceDuration = NSLocalizedString( | ||
| "/month", | ||
| comment: "The text is preceded by the monthly price on the store creation plan screen.") | ||
| static let subtitle = NSLocalizedString( | ||
| "All the featues you need, already built in", | ||
| comment: "Subtitle of the store creation plan screen.") | ||
| static let poweredByWPCOMPrompt = NSLocalizedString( | ||
| "Powered by", | ||
| comment: "The text is followed by a WordPress.com logo on the store creation plan screen.") | ||
| static let continueButtonTitleFormat = NSLocalizedString( | ||
| "Create Store for %1$@/month", | ||
| comment: "Title of the button on the store creation plan view to purchase the plan. " + | ||
| "%1$@ is replaced by the monthly price." | ||
| ) | ||
| static let refundableNote = NSLocalizedString( | ||
| "There’s no risk, you can cancel for a full refund within 30 days.", | ||
| comment: "Refund policy under the purchase button on the store creation plan screen." | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| #if DEBUG | ||
|
|
||
| /// Only used for `StoreCreationPlanView` preview. | ||
| private struct Plan: WPComPlanProduct { | ||
| let displayName: String | ||
| let description: String | ||
| let id: String | ||
| let displayPrice: String | ||
| } | ||
|
|
||
| struct StoreCreationPlanView_Previews: PreviewProvider { | ||
| static var previews: some View { | ||
| StoreCreationPlanView(viewModel: | ||
| .init(plan: Plan(displayName: "Debug Monthly", | ||
| description: "1 Month of Debug Woo", | ||
| id: "debug.woocommerce.ecommerce.monthly", | ||
| displayPrice: "$69.99"))) | ||
| } | ||
| } | ||
|
|
||
| #endif | ||
58 changes: 58 additions & 0 deletions
58
WooCommerce/Classes/Authentication/Store Creation/Plan/StoreCreationPlanViewModel.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| import UIKit | ||
|
|
||
| /// View model for `StoreCreationPlanView`. | ||
| struct StoreCreationPlanViewModel { | ||
| /// Describes a feature for the WPCOM plan with an icon. | ||
| struct Feature { | ||
| let icon: UIImage | ||
| let title: String | ||
| } | ||
|
|
||
| /// The WPCOM plan to purchase. | ||
| let plan: WPComPlanProduct | ||
|
|
||
| /// A list of features included in the WPCOM plan. | ||
| let features: [Feature] = [ | ||
| .init(icon: .gridicon(.starOutline), title: Localization.themeFeature), | ||
| .init(icon: .gridicon(.product), title: Localization.productsFeature), | ||
| .init(icon: .gridicon(.gift), title: Localization.subscriptionsFeature), | ||
| .init(icon: .gridicon(.statsAlt), title: Localization.reportFeature), | ||
| // TODO: 8108 - update icon | ||
| .init(icon: .gridicon(.money), title: Localization.paymentOptionsFeature), | ||
| .init(icon: .gridicon(.shipping), title: Localization.shippingLabelsFeature), | ||
| .init(icon: .megaphoneIcon, title: Localization.salesChannelsFeature), | ||
| ] | ||
| } | ||
|
|
||
| private extension StoreCreationPlanViewModel { | ||
| enum Localization { | ||
| static let themeFeature = NSLocalizedString( | ||
| "Premium themes", | ||
| comment: "Title of eCommerce plan feature on the store creation plan screen." | ||
| ) | ||
| static let productsFeature = NSLocalizedString( | ||
| "Unlimited products", | ||
| comment: "Title of eCommerce plan feature on the store creation plan screen." | ||
| ) | ||
| static let subscriptionsFeature = NSLocalizedString( | ||
| "Subscriptions & giftcards", | ||
| comment: "Title of eCommerce plan feature on the store creation plan screen." | ||
| ) | ||
| static let reportFeature = NSLocalizedString( | ||
| "Ecommerce reports", | ||
| comment: "Title of eCommerce plan feature on the store creation plan screen." | ||
| ) | ||
| static let paymentOptionsFeature = NSLocalizedString( | ||
| "Multiple payment options", | ||
| comment: "Title of eCommerce plan feature on the store creation plan screen." | ||
| ) | ||
| static let shippingLabelsFeature = NSLocalizedString( | ||
| "Shipping labels", | ||
| comment: "Title of eCommerce plan feature on the store creation plan screen." | ||
| ) | ||
| static let salesChannelsFeature = NSLocalizedString( | ||
| "Sales channels", | ||
| comment: "Title of eCommerce plan feature on the store creation plan screen." | ||
| ) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
WooCommerce/Resources/Images.xcassets/store-creation-plan.imageset/Contents.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "images" : [ | ||
| { | ||
| "filename" : "store-creation-plan.pdf", | ||
| "idiom" : "universal" | ||
| } | ||
| ], | ||
| "info" : { | ||
| "author" : "xcode", | ||
| "version" : 1 | ||
| }, | ||
| "properties" : { | ||
| "preserves-vector-representation" : true | ||
| } | ||
| } |
Binary file added
BIN
+2.3 MB
WooCommerce/Resources/Images.xcassets/store-creation-plan.imageset/store-creation-plan.pdf
Binary file not shown.
15 changes: 15 additions & 0 deletions
15
WooCommerce/Resources/Images.xcassets/wpcom-logo.imageset/Contents.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| { | ||
| "images" : [ | ||
| { | ||
| "filename" : "wpcom-logo.pdf", | ||
| "idiom" : "universal" | ||
| } | ||
| ], | ||
| "info" : { | ||
| "author" : "xcode", | ||
| "version" : 1 | ||
| }, | ||
| "properties" : { | ||
| "preserves-vector-representation" : true | ||
| } | ||
| } |
Binary file added
BIN
+9.32 KB
WooCommerce/Resources/Images.xcassets/wpcom-logo.imageset/wpcom-logo.pdf
Binary file not shown.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: What do you think about breaking the
bodydown into smaller views? For example, this part can be a separatefeaturesListViewView.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure 👍 updated in 2ddae25