Skip to content

Commit e36f5a3

Browse files
authored
Merge pull request #2275 from woocommerce/issue/2042-m2-feature-switch
Settings > Experimental Features: add back the Products switch for M2 features
2 parents 6bfa15a + 1d692fa commit e36f5a3

21 files changed

+294
-129
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
4.3
22
-----
33
- Products: now the Product details can be edited and saved outside Products tab (e.g. from Order details or Top Performers).
4+
- In Settings > Experimental Features, a Products switch is now available for turning Products M2 features on and off for simple products (default off for beta testing). Products M2 features: update product images, product settings, viewing and sharing a product.
45

56

67
4.2

WooCommerce/Classes/ViewRelated/Dashboard/Settings/Beta features/BetaFeaturesViewController.swift

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Yosemite
55
// MARK: - BetaFeaturesViewController's Notifications
66
//
77
extension Notification.Name {
8-
static let ProductsVisibilityDidChange = Notification.Name(rawValue: "ProductsVisibilityDidChange")
8+
static let ProductsFeatureSwitchDidChange = Notification.Name(rawValue: "ProductsFeatureSwitchDidChange")
99
}
1010

1111

@@ -88,18 +88,14 @@ private extension BetaFeaturesViewController {
8888
}
8989
guard eligibleStatsVersion == .v4 else {
9090
self.sections = [
91-
// TODO: commenting out this code since we are enabling products for all users in the next release.
92-
// Once product M2 editing features are live, we can enable product editing switch from this screen again.
93-
//self.productsSection()
91+
self.productsSection()
9492
]
9593

9694
return
9795
}
9896
self.sections = [
9997
self.statsSection(),
100-
// TODO: commenting out this code since we are enabling products for all users in the next release.
101-
// Once product M2 editing features are live, we can enable product editing switch from this screen again.
102-
//self.productsSection()
98+
self.productsSection()
10399
]
104100
}
105101
ServiceLocator.stores.dispatch(action)
@@ -198,16 +194,16 @@ private extension BetaFeaturesViewController {
198194

199195
cell.title = title
200196

201-
let action = AppSettingsAction.loadProductsVisibility() { isVisible in
197+
let action = AppSettingsAction.loadProductsFeatureSwitch() { isVisible in
202198
cell.isOn = isVisible
203199
}
204200
ServiceLocator.stores.dispatch(action)
205201

206202
cell.onChange = { isSwitchOn in
207203
ServiceLocator.analytics.track(.settingsBetaFeaturesProductsToggled)
208204

209-
let action = AppSettingsAction.setProductsVisibility(isVisible: isSwitchOn) {
210-
NotificationCenter.default.post(name: .ProductsVisibilityDidChange, object: self)
205+
let action = AppSettingsAction.setProductsFeatureSwitch(isEnabled: isSwitchOn) {
206+
NotificationCenter.default.post(name: .ProductsFeatureSwitchDidChange, object: self)
211207
}
212208
ServiceLocator.stores.dispatch(action)
213209
}
@@ -219,7 +215,7 @@ private extension BetaFeaturesViewController {
219215

220216
let description: String
221217
if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.editProducts) {
222-
description = NSLocalizedString("Test out the new product editing functionality as we get ready to launch",
218+
description = NSLocalizedString("Test out new product editing functionalities as we get ready to launch them",
223219
comment: "My Store > Settings > Experimental features > Product editing")
224220
} else {
225221
description = NSLocalizedString("Test out the new products section as we get ready to launch",

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ struct DefaultProductFormTableViewModel: ProductFormTableViewModel {
1818
init(product: Product,
1919
currency: String,
2020
currencyFormatter: CurrencyFormatter = CurrencyFormatter(),
21-
featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService) {
21+
isEditProductsRelease2Enabled: Bool,
22+
isEditProductsRelease3Enabled: Bool) {
2223
self.currency = currency
2324
self.currencyFormatter = currencyFormatter
24-
self.isEditProductsRelease2Enabled = featureFlagService.isFeatureFlagEnabled(.editProductsRelease2)
25-
self.isEditProductsRelease3Enabled = featureFlagService.isFeatureFlagEnabled(.editProductsRelease3)
25+
self.isEditProductsRelease2Enabled = isEditProductsRelease2Enabled
26+
self.isEditProductsRelease3Enabled = isEditProductsRelease3Enabled
2627
configureSections(product: product)
2728
}
2829
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ final class ProductFormTableViewDataSource: NSObject {
2525
init(viewModel: ProductFormTableViewModel,
2626
productImageStatuses: [ProductImageStatus],
2727
productUIImageLoader: ProductUIImageLoader,
28-
canEditImages: Bool = ServiceLocator.featureFlagService.isFeatureFlagEnabled(.editProductsRelease2)) {
28+
canEditImages: Bool) {
2929
self.viewModel = viewModel
3030
self.canEditImages = canEditImages
3131
self.productImageStatuses = productImageStatuses

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

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,14 @@ final class ProductFormViewController: UIViewController {
3232

3333
updateMoreDetailsButtonVisibility(product: product)
3434

35-
viewModel = DefaultProductFormTableViewModel(product: product, currency: currency)
35+
viewModel = DefaultProductFormTableViewModel(product: product,
36+
currency: currency,
37+
isEditProductsRelease2Enabled: isEditProductsRelease2Enabled,
38+
isEditProductsRelease3Enabled: isEditProductsRelease3Enabled)
3639
tableViewDataSource = ProductFormTableViewDataSource(viewModel: viewModel,
3740
productImageStatuses: productImageActionHandler.productImageStatuses,
38-
productUIImageLoader: productUIImageLoader)
41+
productUIImageLoader: productUIImageLoader,
42+
canEditImages: isEditProductsRelease2Enabled)
3943
tableViewDataSource.configureActions(onNameChange: { [weak self] name in
4044
self?.onEditProductNameCompletion(newName: name ?? "")
4145
}, onAddImage: { [weak self] in
@@ -70,7 +74,8 @@ final class ProductFormViewController: UIViewController {
7074
private let productUIImageLoader: ProductUIImageLoader
7175

7276
private let currency: String
73-
private let featureFlagService: FeatureFlagService
77+
private let isEditProductsRelease2Enabled: Bool
78+
private let isEditProductsRelease3Enabled: Bool
7479

7580
private lazy var exitForm: () -> Void = {
7681
presentationStyle.createExitForm(viewController: self)
@@ -83,20 +88,29 @@ final class ProductFormViewController: UIViewController {
8388
}
8489
private var cancellable: ObservationToken?
8590

86-
init(product: Product, currency: String, presentationStyle: PresentationStyle, featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService) {
91+
init(product: Product,
92+
currency: String,
93+
presentationStyle: PresentationStyle,
94+
isEditProductsRelease2Enabled: Bool,
95+
isEditProductsRelease3Enabled: Bool) {
8796
self.currency = currency
8897
self.presentationStyle = presentationStyle
89-
self.featureFlagService = featureFlagService
98+
self.isEditProductsRelease2Enabled = isEditProductsRelease2Enabled
99+
self.isEditProductsRelease3Enabled = isEditProductsRelease3Enabled
90100
self.originalProduct = product
91101
self.product = product
92-
self.viewModel = DefaultProductFormTableViewModel(product: product, currency: currency)
102+
self.viewModel = DefaultProductFormTableViewModel(product: product,
103+
currency: currency,
104+
isEditProductsRelease2Enabled: isEditProductsRelease2Enabled,
105+
isEditProductsRelease3Enabled: isEditProductsRelease3Enabled)
93106
self.productImageActionHandler = ProductImageActionHandler(siteID: product.siteID,
94107
product: product)
95108
self.productUIImageLoader = DefaultProductUIImageLoader(productImageActionHandler: productImageActionHandler,
96109
phAssetImageLoaderProvider: { PHImageManager.default() })
97110
self.tableViewDataSource = ProductFormTableViewDataSource(viewModel: viewModel,
98111
productImageStatuses: productImageActionHandler.productImageStatuses,
99-
productUIImageLoader: productUIImageLoader)
112+
productUIImageLoader: productUIImageLoader,
113+
canEditImages: isEditProductsRelease2Enabled)
100114
super.init(nibName: nil, bundle: nil)
101115
tableViewDataSource.configureActions(onNameChange: { [weak self] name in
102116
self?.onEditProductNameCompletion(newName: name ?? "")
@@ -214,7 +228,7 @@ private extension ProductFormViewController {
214228
}
215229

216230
func configureMoreDetailsContainerView() {
217-
guard featureFlagService.isFeatureFlagEnabled(.editProductsRelease2) else {
231+
guard isEditProductsRelease2Enabled else {
218232
moreDetailsContainerView.isHidden = true
219233
return
220234
}
@@ -243,7 +257,6 @@ private extension ProductFormViewController {
243257
let title = NSLocalizedString("Add more details",
244258
comment: "Title of the bottom sheet from the product form to add more product details.")
245259
let viewProperties = BottomSheetListSelectorViewProperties(title: title)
246-
let isEditProductsRelease3Enabled = featureFlagService.isFeatureFlagEnabled(.editProductsRelease3)
247260
let dataSource = ProductFormBottomSheetListSelectorCommand(product: product,
248261
isEditProductsRelease3Enabled: isEditProductsRelease3Enabled) { [weak self] action in
249262
self?.dismiss(animated: true) { [weak self] in
@@ -268,7 +281,7 @@ private extension ProductFormViewController {
268281
}
269282

270283
func updateMoreDetailsButtonVisibility(product: Product) {
271-
guard featureFlagService.isFeatureFlagEnabled(.editProductsRelease2) else {
284+
guard isEditProductsRelease2Enabled else {
272285
moreDetailsContainerView.isHidden = true
273286
return
274287
}
@@ -440,7 +453,7 @@ private extension ProductFormViewController {
440453
rightBarButtonItems.append(createUpdateBarButtonItem())
441454
}
442455

443-
if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.editProductsRelease2) {
456+
if isEditProductsRelease2Enabled {
444457
rightBarButtonItems.insert(createMoreOptionsBarButtonItem(), at: 0)
445458
}
446459

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import UIKit
2+
import Yosemite
3+
4+
struct ProductDetailsFactory {
5+
/// Creates a product details view controller asynchronously based on the app settings.
6+
/// - Parameters:
7+
/// - product: product model.
8+
/// - presentationStyle: how the product details are presented.
9+
/// - currencySettings: site currency settings.
10+
/// - featureFlagService: where edit product feature flags are read.
11+
/// - onCompletion: called when the view controller is created and ready for display.
12+
static func productDetails(product: Product,
13+
presentationStyle: ProductFormViewController.PresentationStyle,
14+
currencySettings: CurrencySettings = CurrencySettings.shared,
15+
featureFlagService: FeatureFlagService = ServiceLocator.featureFlagService,
16+
onCompletion: @escaping (UIViewController) -> Void) {
17+
let isEditProductsEnabled = featureFlagService.isFeatureFlagEnabled(.editProducts)
18+
if product.productType == .simple && isEditProductsEnabled {
19+
let action = AppSettingsAction.loadProductsFeatureSwitch { isEditProductsRelease2Enabled in
20+
let vc = productDetails(product: product,
21+
presentationStyle: presentationStyle,
22+
currencySettings: currencySettings,
23+
isEditProductsEnabled: isEditProductsEnabled,
24+
isEditProductsRelease2Enabled: isEditProductsRelease2Enabled,
25+
isEditProductsRelease3Enabled: featureFlagService.isFeatureFlagEnabled(.editProductsRelease3))
26+
onCompletion(vc)
27+
}
28+
ServiceLocator.stores.dispatch(action)
29+
} else {
30+
let vc = productDetails(product: product,
31+
presentationStyle: presentationStyle,
32+
currencySettings: currencySettings,
33+
isEditProductsEnabled: false,
34+
isEditProductsRelease2Enabled: false,
35+
isEditProductsRelease3Enabled: false)
36+
onCompletion(vc)
37+
}
38+
}
39+
}
40+
41+
private extension ProductDetailsFactory {
42+
static func productDetails(product: Product,
43+
presentationStyle: ProductFormViewController.PresentationStyle,
44+
currencySettings: CurrencySettings,
45+
isEditProductsEnabled: Bool,
46+
isEditProductsRelease2Enabled: Bool,
47+
isEditProductsRelease3Enabled: Bool) -> UIViewController {
48+
let currencyCode = currencySettings.currencyCode
49+
let currency = currencySettings.symbol(from: currencyCode)
50+
let vc: UIViewController
51+
if isEditProductsEnabled {
52+
vc = ProductFormViewController(product: product,
53+
currency: currency,
54+
presentationStyle: presentationStyle,
55+
isEditProductsRelease2Enabled: isEditProductsRelease2Enabled,
56+
isEditProductsRelease3Enabled: isEditProductsRelease3Enabled)
57+
// Since the edit Product UI could hold local changes, disables the bottom bar (tab bar) to simplify app states.
58+
vc.hidesBottomBarWhenPushed = true
59+
} else {
60+
let viewModel = ProductDetailsViewModel(product: product, currency: currency)
61+
vc = ProductDetailsViewController(viewModel: viewModel)
62+
}
63+
return vc
64+
}
65+
}

WooCommerce/Classes/ViewRelated/Products/ProductLoaderViewController.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,12 @@ private extension ProductLoaderViewController {
162162
}
163163

164164
func presentProductDetails(for product: Product, isEditProductsEnabled: Bool) {
165-
let viewController: UIViewController
166-
if product.productType == .simple && isEditProductsEnabled {
167-
viewController = ProductFormViewController(product: product, currency: currency, presentationStyle: .contained(containerViewController: self))
168-
} else {
169-
let viewModel = ProductDetailsViewModel(product: product, currency: currency)
170-
viewController = ProductDetailsViewController(viewModel: viewModel)
165+
ProductDetailsFactory.productDetails(product: product, presentationStyle: .contained(containerViewController: self)) { [weak self] viewController in
166+
self?.attachProductDetailsChildViewController(viewController)
171167
}
168+
}
172169

170+
func attachProductDetailsChildViewController(_ viewController: UIViewController) {
173171
// Attach
174172
addChild(viewController)
175173
attachSubview(viewController.view)

WooCommerce/Classes/ViewRelated/Products/ProductsViewController.swift

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -431,18 +431,9 @@ extension ProductsViewController: UITableViewDelegate {
431431

432432
private extension ProductsViewController {
433433
func didSelectProduct(product: Product, isEditProductsEnabled: Bool) {
434-
let currencyCode = CurrencySettings.shared.currencyCode
435-
let currency = CurrencySettings.shared.symbol(from: currencyCode)
436-
let viewController: UIViewController
437-
if product.productType == .simple && isEditProductsEnabled {
438-
viewController = ProductFormViewController(product: product, currency: currency, presentationStyle: .navigationStack)
439-
// Since the edit Product UI could hold local changes, disables the bottom bar (tab bar) to simplify app states.
440-
viewController.hidesBottomBarWhenPushed = true
441-
} else {
442-
let viewModel = ProductDetailsViewModel(product: product, currency: currency)
443-
viewController = ProductDetailsViewController(viewModel: viewModel)
434+
ProductDetailsFactory.productDetails(product: product, presentationStyle: .navigationStack) { [weak self] viewController in
435+
self?.navigationController?.pushViewController(viewController, animated: true)
444436
}
445-
navigationController?.pushViewController(viewController, animated: true)
446437
}
447438
}
448439

WooCommerce/Classes/ViewRelated/Search/Product/ProductSearchUICommand.swift

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,8 @@ final class ProductSearchUICommand: SearchUICommand {
6767
}
6868

6969
func didSelectSearchResult(model: Product, from viewController: UIViewController) {
70-
let isEditProductsEnabled = ServiceLocator.featureFlagService.isFeatureFlagEnabled(.editProducts)
71-
let currencyCode = CurrencySettings.shared.currencyCode
72-
let currency = CurrencySettings.shared.symbol(from: currencyCode)
73-
let vc: UIViewController
74-
if model.productType == .simple && isEditProductsEnabled {
75-
vc = ProductFormViewController(product: model, currency: currency, presentationStyle: .navigationStack)
76-
// Since the edit Product UI could hold local changes, disables the bottom bar (tab bar) to simplify app states.
77-
vc.hidesBottomBarWhenPushed = true
78-
} else {
79-
let viewModel = ProductDetailsViewModel(product: model, currency: currency)
80-
vc = ProductDetailsViewController(viewModel: viewModel)
70+
ProductDetailsFactory.productDetails(product: model, presentationStyle: .navigationStack) { [weak viewController] vc in
71+
viewController?.navigationController?.pushViewController(vc, animated: true)
8172
}
82-
viewController.navigationController?.pushViewController(vc, animated: true)
8373
}
8474
}

0 commit comments

Comments
 (0)