Skip to content

Commit 6bfa15a

Browse files
authored
Merge pull request #2217 from woocommerce/issue/2053-add-product-details-bottom-sheet
Edit Product: add product details bottom sheet
2 parents 4764e2d + d74e10c commit 6bfa15a

File tree

17 files changed

+666
-45
lines changed

17 files changed

+666
-45
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import Foundation
2+
3+
extension Optional where Wrapped == String {
4+
var isNilOrEmpty: Bool {
5+
guard let self = self else {
6+
return true
7+
}
8+
return self.isEmpty
9+
}
10+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import Foundation
2+
import Yosemite
3+
4+
/// Actions in the product form bottom sheet to add more product details.
5+
enum ProductFormBottomSheetAction {
6+
case editInventorySettings
7+
case editShippingSettings
8+
case editCategories
9+
case editBriefDescription
10+
}
11+
12+
extension ProductFormBottomSheetAction {
13+
func isVisible(product: Product) -> Bool {
14+
switch self {
15+
case .editInventorySettings:
16+
let hasStockData = product.manageStock ? product.stockQuantity != nil: true
17+
return product.sku == nil && hasStockData == false
18+
case .editShippingSettings:
19+
return product.weight.isNilOrEmpty &&
20+
product.dimensions.height.isEmpty && product.dimensions.width.isEmpty && product.dimensions.length.isEmpty
21+
case .editCategories:
22+
return product.categories.isEmpty
23+
case .editBriefDescription:
24+
return product.briefDescription.isNilOrEmpty
25+
}
26+
}
27+
}
28+
29+
extension ProductFormBottomSheetAction {
30+
var title: String {
31+
switch self {
32+
case .editInventorySettings:
33+
return NSLocalizedString("Inventory",
34+
comment: "Title of the product form bottom sheet action for editing inventory settings.")
35+
case .editShippingSettings:
36+
return NSLocalizedString("Shipping",
37+
comment: "Title of the product form bottom sheet action for editing shipping settings.")
38+
case .editCategories:
39+
return NSLocalizedString("Categories",
40+
comment: "Title of the product form bottom sheet action for editing categories.")
41+
case .editBriefDescription:
42+
return NSLocalizedString("Short description",
43+
comment: "Title of the product form bottom sheet action for editing short description.")
44+
}
45+
}
46+
47+
var subtitle: String {
48+
switch self {
49+
case .editInventorySettings:
50+
return NSLocalizedString("Update product inventory and SKU",
51+
comment: "Subtitle of the product form bottom sheet action for editing inventory settings.")
52+
case .editShippingSettings:
53+
return NSLocalizedString("Add weight and dimensions",
54+
comment: "Subtitle of the product form bottom sheet action for editing shipping settings.")
55+
case .editCategories:
56+
return NSLocalizedString("Organise your products into related groups",
57+
comment: "Subtitle of the product form bottom sheet action for editing categories.")
58+
case .editBriefDescription:
59+
return NSLocalizedString("A brief excerpt about your product",
60+
comment: "Subtitle of the product form bottom sheet action for editing short description.")
61+
}
62+
}
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import Yosemite
2+
3+
/// `BottomSheetListSelectorCommand` for selecting a Product form action.
4+
///
5+
final class ProductFormBottomSheetListSelectorCommand: BottomSheetListSelectorCommand {
6+
typealias Model = ProductFormBottomSheetAction
7+
typealias Cell = ImageAndTitleAndTextTableViewCell
8+
9+
let data: [ProductFormBottomSheetAction]
10+
11+
let selected: ProductFormBottomSheetAction? = nil
12+
13+
private let onSelection: (ProductFormBottomSheetAction) -> Void
14+
15+
init(product: Product,
16+
isEditProductsRelease3Enabled: Bool,
17+
onSelection: @escaping (ProductFormBottomSheetAction) -> Void) {
18+
self.onSelection = onSelection
19+
20+
let shouldShowShippingSettingsRow = product.isShippingEnabled
21+
let shouldShowCategoriesRow = isEditProductsRelease3Enabled
22+
let actions: [ProductFormBottomSheetAction?] = [
23+
.editInventorySettings,
24+
shouldShowShippingSettingsRow ? .editShippingSettings: nil,
25+
shouldShowCategoriesRow ? .editCategories: nil,
26+
.editBriefDescription
27+
]
28+
self.data = actions.compactMap({ $0 }).filter({ $0.isVisible(product: product) })
29+
}
30+
31+
func configureCell(cell: ImageAndTitleAndTextTableViewCell, model: ProductFormBottomSheetAction) {
32+
cell.selectionStyle = .none
33+
cell.accessoryType = .disclosureIndicator
34+
let viewModel = ImageAndTitleAndTextTableViewCell.ViewModel(title: model.title, text: model.subtitle)
35+
cell.updateUI(viewModel: viewModel)
36+
}
37+
38+
func handleSelectedChange(selected: ProductFormBottomSheetAction) {
39+
onSelection(selected)
40+
}
41+
42+
func isSelected(model: ProductFormBottomSheetAction) -> Bool {
43+
return model == selected
44+
}
45+
}

WooCommerce/Classes/ViewRelated/Products/Edit Product/DefaultProductFormTableViewModel.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private extension DefaultProductFormTableViewModel {
4949
}
5050

5151
func settingsRows(product: Product) -> [ProductFormSection.SettingsRow] {
52-
let shouldShowShippingSettingsRow = product.downloadable == false && product.virtual == false
52+
let shouldShowShippingSettingsRow = product.isShippingEnabled
5353
let shouldShowBriefDescriptionRow = isEditProductsRelease2Enabled
5454
let shouldShowCategoriesRow = isEditProductsRelease3Enabled
5555

@@ -61,7 +61,7 @@ private extension DefaultProductFormTableViewModel {
6161
shouldShowBriefDescriptionRow ? .briefDescription(viewModel: briefDescriptionRow(product: product)): nil
6262
]
6363

64-
return rows.compactMap { $0 }
64+
return rows.compactMap { $0 }.filter { $0.isVisible(product: product) }
6565
}
6666
}
6767

WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormTableViewModel.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,28 @@ extension ProductFormSection.SettingsRow.ViewModel: Equatable {
8585
lhs.details == rhs.details
8686
}
8787
}
88+
89+
extension ProductFormSection.SettingsRow {
90+
func isVisible(product: Product) -> Bool {
91+
guard let bottomSheetAction = bottomSheetAction else {
92+
// If there is no corresponding bottom sheet action, the settings row is visible by default.
93+
return true
94+
}
95+
return bottomSheetAction.isVisible(product: product) == false
96+
}
97+
98+
private var bottomSheetAction: ProductFormBottomSheetAction? {
99+
switch self {
100+
case .inventory:
101+
return .editInventorySettings
102+
case .shipping:
103+
return .editShippingSettings
104+
case .categories:
105+
return .editCategories
106+
case .briefDescription:
107+
return .editBriefDescription
108+
default:
109+
return nil
110+
}
111+
}
112+
}

WooCommerce/Classes/ViewRelated/Products/Edit Product/ProductFormViewController.swift

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ final class ProductFormViewController: UIViewController {
3030
return
3131
}
3232

33+
updateMoreDetailsButtonVisibility(product: product)
34+
3335
viewModel = DefaultProductFormTableViewModel(product: product, currency: currency)
3436
tableViewDataSource = ProductFormTableViewDataSource(viewModel: viewModel,
3537
productImageStatuses: productImageActionHandler.productImageStatuses,
@@ -220,15 +222,60 @@ private extension ProductFormViewController {
220222
let title = NSLocalizedString("Add more details", comment: "Title of the button at the bottom of the product form to add more product details.")
221223
let viewModel = BottomButtonContainerView.ViewModel(style: .link,
222224
title: title,
223-
image: .plusImage) { _ in
224-
// TODO-2053: show more details bottom sheet
225+
image: .plusImage) { [weak self] button in
226+
self?.moreDetailsButtonTapped(button: button)
225227
}
226228
let buttonContainerView = BottomButtonContainerView(viewModel: viewModel)
227229

228230
moreDetailsContainerView.addSubview(buttonContainerView)
229231
moreDetailsContainerView.pinSubviewToAllEdges(buttonContainerView)
230232
moreDetailsContainerView.setContentCompressionResistancePriority(.required, for: .vertical)
231233
moreDetailsContainerView.setContentHuggingPriority(.required, for: .vertical)
234+
235+
updateMoreDetailsButtonVisibility(product: product)
236+
}
237+
}
238+
239+
// MARK: More details actions
240+
//
241+
private extension ProductFormViewController {
242+
func moreDetailsButtonTapped(button: UIButton) {
243+
let title = NSLocalizedString("Add more details",
244+
comment: "Title of the bottom sheet from the product form to add more product details.")
245+
let viewProperties = BottomSheetListSelectorViewProperties(title: title)
246+
let isEditProductsRelease3Enabled = featureFlagService.isFeatureFlagEnabled(.editProductsRelease3)
247+
let dataSource = ProductFormBottomSheetListSelectorCommand(product: product,
248+
isEditProductsRelease3Enabled: isEditProductsRelease3Enabled) { [weak self] action in
249+
self?.dismiss(animated: true) { [weak self] in
250+
switch action {
251+
case .editInventorySettings:
252+
self?.editInventorySettings()
253+
case .editShippingSettings:
254+
self?.editShippingSettings()
255+
case .editCategories:
256+
self?.editCategories()
257+
case .editBriefDescription:
258+
self?.editBriefDescription()
259+
}
260+
}
261+
}
262+
let listSelectorViewController = BottomSheetListSelectorViewController(viewProperties: viewProperties,
263+
command: dataSource) { [weak self] selectedSortOrder in
264+
self?.dismiss(animated: true, completion: nil)
265+
}
266+
let bottomSheet = BottomSheetViewController(childViewController: listSelectorViewController)
267+
bottomSheet.show(from: self, sourceView: button, arrowDirections: .down)
268+
}
269+
270+
func updateMoreDetailsButtonVisibility(product: Product) {
271+
guard featureFlagService.isFeatureFlagEnabled(.editProductsRelease2) else {
272+
moreDetailsContainerView.isHidden = true
273+
return
274+
}
275+
276+
let moreDetailsActions: [ProductFormBottomSheetAction] = [.editInventorySettings, .editShippingSettings, .editCategories, .editBriefDescription]
277+
let hasVisibleActions = moreDetailsActions.map({ $0.isVisible(product: product) }).contains(true)
278+
moreDetailsContainerView.isHidden = hasVisibleActions == false
232279
}
233280
}
234281

0 commit comments

Comments
 (0)