Skip to content

Commit 8cfe67b

Browse files
authored
Merge pull request #6586 from woocommerce/issue/6489-product-selection-alt
Coupons: Duplicate product list selector view controller to prepare for the UI update
2 parents dc721ef + 4130eb3 commit 8cfe67b

File tree

7 files changed

+229
-26
lines changed

7 files changed

+229
-26
lines changed

WooCommerce/Classes/ViewRelated/Products/Edit Product/Linked Products List Selector/LinkedProductsListSelectorViewController.swift

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,22 @@ private extension LinkedProductsListSelectorViewController {
7676
ServiceLocator.analytics.track(.connectedProductsList, withProperties: ["action": "add_tapped", "context": viewConfiguration.trackingContext])
7777

7878
let excludedProductIDs = dataSource.linkedProductIDs + [productID]
79-
let listSelector = ProductListSelectorViewController(excludedProductIDs: excludedProductIDs,
80-
siteID: siteID) { [weak self] selectedProductIDs in
81-
if selectedProductIDs.isNotEmpty,
82-
let context = self?.viewConfiguration.trackingContext {
83-
ServiceLocator.analytics.track(.connectedProductsList,
84-
withProperties: ["action": "added", "context": context])
85-
}
86-
self?.dataSource.addProducts(selectedProductIDs)
87-
self?.navigationController?.popViewController(animated: true)
79+
let selectorCompletion: (_ selectedProductIDs: [Int64]) -> Void = { [weak self] selectedProductIDs in
80+
if selectedProductIDs.isNotEmpty,
81+
let context = self?.viewConfiguration.trackingContext {
82+
ServiceLocator.analytics.track(.connectedProductsList,
83+
withProperties: ["action": "added", "context": context])
84+
}
85+
self?.dataSource.addProducts(selectedProductIDs)
86+
self?.navigationController?.popViewController(animated: true)
8887
}
88+
let listSelector: UIViewController = {
89+
if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.couponEditing) {
90+
return ProductListSelectorViewController(excludedProductIDs: excludedProductIDs, siteID: siteID, onCompletion: selectorCompletion)
91+
} else {
92+
return LegacyProductListSelectorViewController(excludedProductIDs: excludedProductIDs, siteID: siteID, onCompletion: selectorCompletion)
93+
}
94+
}()
8995
show(listSelector, sender: self)
9096
}
9197

WooCommerce/Classes/ViewRelated/Products/Edit Product/Linked Products List Selector/LinkedProductsListSelectorViewController.xib

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
33
<device id="retina6_1" orientation="portrait" appearance="light"/>
44
<dependencies>
55
<deployment identifier="iOS"/>
6-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
6+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
77
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
88
<capability name="System colors in document resources" minToolsVersion="11.0"/>
99
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
1010
</dependencies>
1111
<objects>
12-
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ProductListSelectorViewController" customModule="WooCommerce" customModuleProvider="target">
12+
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="LinkedProductsListSelectorViewController" customModule="WooCommerce" customModuleProvider="target">
1313
<connections>
1414
<outlet property="addButton" destination="V0q-d9-nEJ" id="gQ3-Q2-az4"/>
1515
<outlet property="addButtonBottomBorderView" destination="km4-PZ-rGi" id="wej-kc-FxN"/>
@@ -19,26 +19,26 @@
1919
</connections>
2020
</placeholder>
2121
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
22-
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
22+
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" ambiguous="YES" id="i5M-Pr-FkT">
2323
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
2424
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
2525
<subviews>
26-
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="TkB-a6-KpO">
27-
<rect key="frame" x="0.0" y="44" width="414" height="818"/>
26+
<stackView opaque="NO" contentMode="scaleToFill" ambiguous="YES" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="TkB-a6-KpO">
27+
<rect key="frame" x="0.0" y="0.0" width="414" height="1130"/>
2828
<subviews>
2929
<view contentMode="scaleToFill" id="e8L-Dl-u8g" userLabel="Top Container View">
30-
<rect key="frame" x="0.0" y="0.0" width="414" height="818"/>
30+
<rect key="frame" x="0.0" y="0.0" width="414" height="1052"/>
3131
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
3232
<subviews>
3333
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="V0q-d9-nEJ">
34-
<rect key="frame" x="16" y="16" width="382" height="786"/>
34+
<rect key="frame" x="16" y="16" width="382" height="1020"/>
3535
<constraints>
3636
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="50" id="u7J-wB-kuj"/>
3737
</constraints>
3838
<state key="normal" title="Button"/>
3939
</button>
4040
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="km4-PZ-rGi">
41-
<rect key="frame" x="0.0" y="817.5" width="414" height="0.5"/>
41+
<rect key="frame" x="0.0" y="1051.5" width="414" height="0.5"/>
4242
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
4343
<constraints>
4444
<constraint firstAttribute="height" constant="0.5" id="wmT-Zg-aY5"/>
@@ -56,8 +56,8 @@
5656
<constraint firstAttribute="trailing" secondItem="km4-PZ-rGi" secondAttribute="trailing" id="zIT-26-Ue0"/>
5757
</constraints>
5858
</view>
59-
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="L1e-K0-Lrl">
60-
<rect key="frame" x="0.0" y="818" width="414" height="0.0"/>
59+
<view contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L1e-K0-Lrl">
60+
<rect key="frame" x="0.0" y="1052" width="414" height="78"/>
6161
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
6262
</view>
6363
</subviews>
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import Combine
2+
import UIKit
3+
import Yosemite
4+
5+
/// Displays a paginated list of products where the user can select.
6+
/// This uses the old design for the product list selector,
7+
/// and should be removed once updates for `ProductListSelectorViewController` are complete.
8+
final class LegacyProductListSelectorViewController: UIViewController {
9+
private let excludedProductIDs: [Int64]
10+
private var productIDs: [Int64] = [] {
11+
didSet {
12+
if productIDs != oldValue {
13+
updateNavigationTitle(productIDs: productIDs)
14+
updateNavigationRightBarButtonItem(productIDs: productIDs)
15+
}
16+
}
17+
}
18+
19+
private let siteID: Int64
20+
private var selectedProductIDsSubscription: AnyCancellable?
21+
22+
private lazy var dataSource = ProductListMultiSelectorDataSource(siteID: siteID, excludedProductIDs: excludedProductIDs)
23+
24+
private lazy var paginatedListSelector: PaginatedListSelectorViewController
25+
<ProductListMultiSelectorDataSource, Product, StorageProduct, ProductsTabProductTableViewCell> = {
26+
let viewProperties = PaginatedListSelectorViewProperties(navigationBarTitle: nil,
27+
noResultsPlaceholderText: Localization.noResultsPlaceholder,
28+
noResultsPlaceholderImage: .emptyProductsImage,
29+
noResultsPlaceholderImageTintColor: .primary,
30+
tableViewStyle: .plain,
31+
separatorStyle: .none)
32+
return PaginatedListSelectorViewController(viewProperties: viewProperties, dataSource: dataSource, onDismiss: { _ in })
33+
}()
34+
35+
// Completion callback
36+
//
37+
typealias Completion = (_ selectedProductIDs: [Int64]) -> Void
38+
private let onCompletion: Completion
39+
40+
init(excludedProductIDs: [Int64], siteID: Int64, onCompletion: @escaping Completion) {
41+
self.excludedProductIDs = excludedProductIDs
42+
self.siteID = siteID
43+
self.onCompletion = onCompletion
44+
super.init(nibName: nil, bundle: nil)
45+
}
46+
47+
required init?(coder: NSCoder) {
48+
fatalError("init(coder:) has not been implemented")
49+
}
50+
51+
override func viewDidLoad() {
52+
super.viewDidLoad()
53+
54+
configureMainView()
55+
configureNavigation()
56+
configurePaginatedProductListSelectorChildViewController()
57+
}
58+
}
59+
60+
// MARK: - Actions
61+
private extension LegacyProductListSelectorViewController {
62+
@objc func doneButtonTapped() {
63+
completeUpdating()
64+
}
65+
66+
@objc func searchButtonTapped() {
67+
let productIDsToExclude = (excludedProductIDs + productIDs).removingDuplicates()
68+
let searchProductsCommand = ProductListMultiSelectorSearchUICommand(siteID: siteID,
69+
excludedProductIDs: productIDsToExclude) { [weak self] productIDs in
70+
self?.didSelectProductsFromSearch(ids: productIDs)
71+
}
72+
let searchViewController = SearchViewController(storeID: siteID,
73+
command: searchProductsCommand,
74+
cellType: ProductsTabProductTableViewCell.self,
75+
cellSeparator: .none)
76+
let navigationController = WooNavigationController(rootViewController: searchViewController)
77+
searchProductsCommand.configurePresentingViewControllerForDiscardChangesAlert(presentingViewController: navigationController)
78+
present(navigationController, animated: true, completion: nil)
79+
}
80+
81+
func didSelectProductsFromSearch(ids: [Int64]) {
82+
dataSource.addProducts(ids)
83+
paginatedListSelector.reloadData()
84+
}
85+
}
86+
87+
// MARK: - Navigation actions handling
88+
//
89+
extension LegacyProductListSelectorViewController {
90+
override func shouldPopOnBackButton() -> Bool {
91+
if hasUnsavedChanges() {
92+
presentBackNavigationActionSheet()
93+
return false
94+
}
95+
return true
96+
}
97+
98+
override func shouldPopOnSwipeBack() -> Bool {
99+
return shouldPopOnBackButton()
100+
}
101+
102+
private func completeUpdating() {
103+
onCompletion(productIDs)
104+
}
105+
106+
private func hasUnsavedChanges() -> Bool {
107+
return productIDs.isNotEmpty
108+
}
109+
110+
private func presentBackNavigationActionSheet() {
111+
UIAlertController.presentDiscardChangesActionSheet(viewController: self, onDiscard: { [weak self] in
112+
self?.navigationController?.popViewController(animated: true)
113+
})
114+
}
115+
}
116+
117+
// MARK: - UI updates
118+
private extension LegacyProductListSelectorViewController {
119+
func updateNavigationRightBarButtonItem(productIDs: [Int64]) {
120+
if productIDs.isEmpty {
121+
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .search, target: self, action: #selector(searchButtonTapped))
122+
} else {
123+
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonTapped))
124+
}
125+
}
126+
127+
func updateNavigationTitle(productIDs: [Int64]) {
128+
let title: String
129+
switch productIDs.count {
130+
case 0:
131+
title = Localization.titleWithoutSelectedProducts
132+
case 1:
133+
title = Localization.titleWithOneSelectedProduct
134+
default:
135+
title = String.localizedStringWithFormat(Localization.titleWithMultipleSelectedProductsFormat, productIDs.count)
136+
}
137+
navigationItem.title = title
138+
}
139+
}
140+
141+
// MARK: - UI configurations
142+
//
143+
private extension LegacyProductListSelectorViewController {
144+
func configureMainView() {
145+
view.backgroundColor = .basicBackground
146+
}
147+
148+
func configureNavigation() {
149+
updateNavigationTitle(productIDs: productIDs)
150+
updateNavigationRightBarButtonItem(productIDs: productIDs)
151+
}
152+
153+
func configurePaginatedProductListSelectorChildViewController() {
154+
observeSelectedProductIDs(observableProductIDs: dataSource.productIDs)
155+
156+
addChild(paginatedListSelector)
157+
paginatedListSelector.view.translatesAutoresizingMaskIntoConstraints = false
158+
view.addSubview(paginatedListSelector.view)
159+
paginatedListSelector.didMove(toParent: self)
160+
161+
view.pinSubviewToAllEdges(paginatedListSelector.view)
162+
}
163+
164+
func observeSelectedProductIDs(observableProductIDs: AnyPublisher<[Int64], Never>) {
165+
selectedProductIDsSubscription = observableProductIDs.sink { [weak self] selectedProductIDs in
166+
self?.productIDs = selectedProductIDs
167+
}
168+
}
169+
}
170+
171+
// MARK: - Constants
172+
//
173+
private extension LegacyProductListSelectorViewController {
174+
enum Localization {
175+
static let noResultsPlaceholder = NSLocalizedString("No products yet",
176+
comment: "Placeholder text when there are no products on the product list selector")
177+
static let titleWithoutSelectedProducts = NSLocalizedString("Add Products", comment: "Navigation bar title for selecting multiple products.")
178+
static let titleWithOneSelectedProduct =
179+
NSLocalizedString("1 Product Selected",
180+
comment: "Navigation bar title for selecting multiple products when one product has been selected.")
181+
static let titleWithMultipleSelectedProductsFormat =
182+
NSLocalizedString("%ld Products Selected",
183+
comment: "Navigation bar title for selecting multiple products when more multiple products have been selected.")
184+
}
185+
}

WooCommerce/Classes/ViewRelated/Products/Edit Product/Linked Products List Selector/ProductListMultiSelectorDataSource.swift renamed to WooCommerce/Classes/ViewRelated/Products/ProductListSelector/ProductListMultiSelectorDataSource.swift

File renamed without changes.

WooCommerce/Classes/ViewRelated/Products/Edit Product/Linked Products List Selector/ProductListMultiSelectorSearchUICommand.swift renamed to WooCommerce/Classes/ViewRelated/Products/ProductListSelector/ProductListMultiSelectorSearchUICommand.swift

File renamed without changes.

WooCommerce/Classes/ViewRelated/Products/Edit Product/Linked Products List Selector/ProductListSelectorViewController.swift renamed to WooCommerce/Classes/ViewRelated/Products/ProductListSelector/ProductListSelectorViewController.swift

File renamed without changes.

0 commit comments

Comments
 (0)