Skip to content

Commit eeb1b13

Browse files
authored
Merge pull request #6553 from woocommerce/issue/6523-remove-code-duplication-product-reviews-data-source
Remove code duplication in reviews data source
2 parents 772987b + 6c1ec40 commit eeb1b13

File tree

10 files changed

+67
-230
lines changed

10 files changed

+67
-230
lines changed

RELEASE-NOTES.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
*** PLEASE FOLLOW THIS FORMAT: [<priority indicator, more stars = higher priority>] <description> [<PR URL>]
22

3+
9.0
4+
-----
5+
- [internal] Reviews lists on Products and Menu tabs are refactored to avoid duplicated code. Please quickly smoke test them to make sure that everything still works as before. [https://github.com/woocommerce/woocommerce-ios/pull/6553]
6+
37
8.9
48
-----
59
- [*] Coupons: Fixed issue loading the coupon list from the local storage on initial load. [https://github.com/woocommerce/woocommerce-ios/pull/6463]

WooCommerce/Classes/ViewRelated/Products/Edit Product/Reviews/ProductReviewsDataSource.swift

Lines changed: 0 additions & 214 deletions
This file was deleted.

WooCommerce/Classes/ViewRelated/Products/Edit Product/Reviews/ProductReviewsViewController.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ final class ProductReviewsViewController: UIViewController, GhostableViewControl
99
private let viewModel: ProductReviewsViewModel
1010

1111
lazy var ghostTableViewController = GhostTableViewController(options: GhostTableViewOptions(cellClass: ProductReviewTableViewCell.self,
12-
estimatedRowHeight: ProductReviewsDataSource
12+
estimatedRowHeight: DefaultReviewsDataSource
1313
.Settings
1414
.estimatedRowHeight))
1515

@@ -60,7 +60,9 @@ final class ProductReviewsViewController: UIViewController, GhostableViewControl
6060
// MARK: - View Lifecycle
6161
init(product: Product) {
6262
self.product = product
63-
viewModel = ProductReviewsViewModel(siteID: product.siteID, data: ProductReviewsDataSource(product: product))
63+
viewModel = ProductReviewsViewModel(siteID: product.siteID,
64+
data: DefaultReviewsDataSource(siteID: product.siteID,
65+
customizer: ProductReviewsDataSourceCustomizer(product: product)))
6466
super.init(nibName: type(of: self).nibName, bundle: nil)
6567
}
6668

WooCommerce/Classes/ViewRelated/Products/Edit Product/Reviews/ProductReviewsViewModel.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,21 @@ private extension ProductReviewsViewModel {
100100
static let pageSize = 25
101101
}
102102
}
103+
104+
/// Customizes the `ReviewsDataSource` for a product related reviews screen (only the reviews of the passed product)
105+
final class ProductReviewsDataSourceCustomizer: ReviewsDataSourceCustomizing {
106+
let shouldShowProductTitleOnCells = false
107+
private let product: Product
108+
109+
init(product: Product) {
110+
self.product = product
111+
}
112+
113+
func reviewsFilterPredicate(with sitePredicate: NSPredicate) -> NSPredicate {
114+
let statusPredicate = NSPredicate(format: "statusKey ==[c] %@ AND productID == %lld",
115+
ProductReviewStatus.approved.rawValue,
116+
product.productID)
117+
118+
return NSCompoundPredicate(andPredicateWithSubpredicates: [sitePredicate, statusPredicate])
119+
}
120+
}

WooCommerce/Classes/ViewRelated/Reviews/DefaultReviewsDataSource.swift

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ final class DefaultReviewsDataSource: NSObject, ReviewsDataSource {
1212

1313
private let siteID: Int64
1414

15+
/// Adds an extra layer of logic customization depending on the case (e.g only reviews of a specific product, global site reviews...)
16+
private let customizer: ReviewsDataSourceCustomizing
17+
1518
/// Product Reviews
1619
///
1720
private lazy var reviewsResultsController: ResultsController<StorageProductReview> = {
@@ -20,7 +23,7 @@ final class DefaultReviewsDataSource: NSObject, ReviewsDataSource {
2023

2124
return ResultsController<StorageProductReview>(storageManager: storageManager,
2225
sectionNameKeyPath: "normalizedAgeAsString",
23-
matching: filterPredicate(),
26+
matching: customizer.reviewsFilterPredicate(with: sitePredicate()),
2427
sortedBy: [descriptor])
2528
}()
2629

@@ -78,9 +81,9 @@ final class DefaultReviewsDataSource: NSObject, ReviewsDataSource {
7881
return reviewsResultsController.numberOfObjects
7982
}
8083

81-
82-
init(siteID: Int64) {
84+
init(siteID: Int64, customizer: ReviewsDataSourceCustomizing) {
8385
self.siteID = siteID
86+
self.customizer = customizer
8487
super.init()
8588
observeResults()
8689
}
@@ -131,8 +134,9 @@ final class DefaultReviewsDataSource: NSObject, ReviewsDataSource {
131134
}
132135

133136
func refreshDataObservers() {
134-
reviewsResultsController.predicate = filterPredicate()
135-
productsResultsController.predicate = sitePredicate()
137+
let sitePredicate = sitePredicate()
138+
reviewsResultsController.predicate = customizer.reviewsFilterPredicate(with: sitePredicate)
139+
productsResultsController.predicate = sitePredicate
136140
}
137141
}
138142

@@ -179,7 +183,7 @@ private extension DefaultReviewsDataSource {
179183
let reviewProduct = product(id: review.productID)
180184
let note = notification(id: review.reviewID)
181185

182-
return ReviewViewModel(review: review, product: reviewProduct, notification: note)
186+
return ReviewViewModel(showProductTitle: customizer.shouldShowProductTitleOnCells, review: review, product: reviewProduct, notification: note)
183187
}
184188

185189
private func product(id productID: Int64) -> Product? {
@@ -237,7 +241,7 @@ extension DefaultReviewsDataSource: ReviewsInteractionDelegate {
237241
}
238242

239243

240-
private extension DefaultReviewsDataSource {
244+
extension DefaultReviewsDataSource {
241245
enum Settings {
242246
static let estimatedRowHeight = CGFloat(88)
243247
}

WooCommerce/Classes/ViewRelated/Reviews/ReviewsDataSource.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,22 @@ protocol ReviewsInteractionDelegate: UITableViewDelegate {
1515
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath, with syncingCoordinator: SyncingCoordinator)
1616
}
1717

18+
/// Implement this protocol to add an extra layer of customization to the the
19+
/// ReviewsDataSource
20+
protocol ReviewsDataSourceCustomizing: AnyObject {
21+
/// Whether it should show the product title on cell or not
22+
///
23+
var shouldShowProductTitleOnCells: Bool { get }
24+
25+
/// Implement this method to return the predicate that will provide reviews.
26+
///
27+
/// - parameter sitePredicate: This predicate adds a site constraint. By composing your predicate with it you will retrieve only reviews of that site.
28+
///
29+
func reviewsFilterPredicate(with sitePredicate: NSPredicate) -> NSPredicate
30+
}
1831

1932
/// Abstracts the dataSource used to render the Product Review list
2033
protocol ReviewsDataSource: UITableViewDataSource, ReviewsInteractionDelegate {
21-
2234
/// Boolean indicating if there are reviews
2335
///
2436
var isEmpty: Bool { get }

WooCommerce/Classes/ViewRelated/Reviews/ReviewsViewController.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ final class ReviewsViewController: UIViewController, GhostableViewController {
117117
//
118118
convenience init(siteID: Int64) {
119119
self.init(viewModel: ReviewsViewModel(siteID: siteID,
120-
data: DefaultReviewsDataSource(siteID: siteID)))
120+
data: DefaultReviewsDataSource(siteID: siteID,
121+
customizer: GlobalReviewsDataSourceCustomizer())))
121122
}
122123

123124
init(viewModel: ViewModel) {

WooCommerce/Classes/ViewRelated/Reviews/ReviewsViewModel.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,3 +244,17 @@ private extension ReviewsViewModel {
244244
static let section = "notifications"
245245
}
246246
}
247+
248+
/// Customizes the `ReviewsDataSource` for a global reviews query (all of a site)
249+
final class GlobalReviewsDataSourceCustomizer: ReviewsDataSourceCustomizing {
250+
let shouldShowProductTitleOnCells = true
251+
252+
func reviewsFilterPredicate(with sitePredicate: NSPredicate) -> NSPredicate {
253+
let statusPredicate = NSPredicate(format: "statusKey ==[c] %@ OR statusKey ==[c] %@",
254+
ProductReviewStatus.approved.rawValue,
255+
ProductReviewStatus.hold.rawValue)
256+
257+
return NSCompoundPredicate(andPredicateWithSubpredicates: [sitePredicate, statusPredicate])
258+
259+
}
260+
}

0 commit comments

Comments
 (0)