Implemented the items filtering by star rating for the FeedListViewController view controller and its view model in the Feed framework.
This commit is contained in:
parent
8d84f15b40
commit
e9590990ea
@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import ReviewsFilterKit
|
||||||
import ReviewsiTunesKit
|
import ReviewsiTunesKit
|
||||||
|
|
||||||
extension FeedListViewController {
|
extension FeedListViewController {
|
||||||
@ -21,10 +22,14 @@ extension FeedListViewController {
|
|||||||
// MARK: Properties
|
// MARK: Properties
|
||||||
@Published var filter: FilterOption = .all
|
@Published var filter: FilterOption = .all
|
||||||
@Published var isFilterEnabled: Bool = false
|
@Published var isFilterEnabled: Bool = false
|
||||||
|
@Published var isFiltering: Bool = false
|
||||||
@Published var isLoading: Bool = false
|
@Published var isLoading: Bool = false
|
||||||
|
|
||||||
var items: [Review] = []
|
var items: [Review] = []
|
||||||
|
|
||||||
|
private var reviewsAll: [Review] = []
|
||||||
|
private var reviewsFiltered: FilteredReviews = [:]
|
||||||
|
|
||||||
lazy private var iTunesService: iTunesService = {
|
lazy private var iTunesService: iTunesService = {
|
||||||
.init(configuration: .init(session: configuration.session))
|
.init(configuration: .init(session: configuration.session))
|
||||||
}()
|
}()
|
||||||
@ -46,19 +51,13 @@ extension FeedListViewController {
|
|||||||
countryCode: configuration.countryCode
|
countryCode: configuration.countryCode
|
||||||
))
|
))
|
||||||
|
|
||||||
items = output.reviews
|
reviewsAll = output.reviews.map(Review.init)
|
||||||
.map { review -> Review in
|
reviewsFiltered = FilterOption.allCases
|
||||||
.init(
|
.reduce(into: FilteredReviews()) { partialResult, option in
|
||||||
author: review.author,
|
partialResult[option] = reviewsAll.filter { $0.rating.stars == option.rawValue }
|
||||||
comment: review.content,
|
|
||||||
id: review.id,
|
|
||||||
rating: .init(
|
|
||||||
stars: review.rating,
|
|
||||||
appVersion: review.version
|
|
||||||
),
|
|
||||||
title: review.title
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
items = reviewsAll
|
||||||
isFilterEnabled = !items.isEmpty
|
isFilterEnabled = !items.isEmpty
|
||||||
} catch {
|
} catch {
|
||||||
// TODO: handle this error gracefully.
|
// TODO: handle this error gracefully.
|
||||||
@ -68,6 +67,24 @@ extension FeedListViewController {
|
|||||||
isLoading = false
|
isLoading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func filter(by option: FilterOption) {
|
||||||
|
guard option != filter else { return }
|
||||||
|
|
||||||
|
items = option == .all
|
||||||
|
? reviewsAll
|
||||||
|
: reviewsFiltered[option] ?? []
|
||||||
|
|
||||||
|
filter = option
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Helpers
|
||||||
|
private extension FeedListViewController.ViewModel {
|
||||||
|
|
||||||
|
// MARK: Type aliases
|
||||||
|
typealias FilteredReviews = [FilterOption: [Review]]
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -23,14 +23,7 @@ public class FeedListViewController: UITableViewController {
|
|||||||
|
|
||||||
// MARK: Outlets
|
// MARK: Outlets
|
||||||
private lazy var filterButton = {
|
private lazy var filterButton = {
|
||||||
let allStars = UIAction(
|
UIBarButtonItem(
|
||||||
title: FilterOption.all.text,
|
|
||||||
image: .init(systemName: FilterOption.all.icon)
|
|
||||||
) { [weak self] _ in
|
|
||||||
self?.viewModel.filter = .all
|
|
||||||
}
|
|
||||||
|
|
||||||
return UIBarButtonItem(
|
|
||||||
title: NSLocalizedString(
|
title: NSLocalizedString(
|
||||||
.Key.Navigation.Button.filter,
|
.Key.Navigation.Button.filter,
|
||||||
bundle: .module,
|
bundle: .module,
|
||||||
@ -45,26 +38,19 @@ public class FeedListViewController: UITableViewController {
|
|||||||
comment: .empty
|
comment: .empty
|
||||||
),
|
),
|
||||||
image: UIImage.Icon.star,
|
image: UIImage.Icon.star,
|
||||||
children: [allStars, filterStarMenu]
|
children: {
|
||||||
|
FilterOption.allCases.map { option -> UIAction in
|
||||||
|
.init(title: option.text,
|
||||||
|
image: .init(systemName: option.icon)
|
||||||
|
) { [weak self] _ in
|
||||||
|
self?.viewModel.filter(by: option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
private lazy var filterStarMenu = {
|
|
||||||
UIMenu(
|
|
||||||
options: .displayInline,
|
|
||||||
children: {
|
|
||||||
FilterOption.allCases.map { option -> UIAction in
|
|
||||||
.init(title: option.text,
|
|
||||||
image: .init(systemName: option.icon)
|
|
||||||
) { [weak self] _ in
|
|
||||||
self?.viewModel.filter = option
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// MARK: Initialisers
|
// MARK: Initialisers
|
||||||
public init(configuration: Configuration = .init()) {
|
public init(configuration: Configuration = .init()) {
|
||||||
self.viewModel = .init(configuration: configuration)
|
self.viewModel = .init(configuration: configuration)
|
||||||
@ -145,6 +131,14 @@ private extension FeedListViewController {
|
|||||||
|
|
||||||
// MARK: Functions
|
// MARK: Functions
|
||||||
func bindViewModel() {
|
func bindViewModel() {
|
||||||
|
viewModel.$filter
|
||||||
|
.receive(on: RunLoop.main)
|
||||||
|
.sink { [weak self] option in
|
||||||
|
self?.updateFilterMenu(option)
|
||||||
|
self?.tableView.reloadData()
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
viewModel.$isFilterEnabled
|
viewModel.$isFilterEnabled
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.receive(on: RunLoop.main)
|
.receive(on: RunLoop.main)
|
||||||
@ -180,6 +174,18 @@ private extension FeedListViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateFilterMenu(_ option: FilterOption) {
|
||||||
|
filterButton
|
||||||
|
.menu?
|
||||||
|
.children
|
||||||
|
.compactMap { $0 as? UIAction }
|
||||||
|
.forEach { action in
|
||||||
|
action.state = action.title == option.text
|
||||||
|
? .on
|
||||||
|
: .off
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Configuration
|
// MARK: - Configuration
|
||||||
|
Loading…
x
Reference in New Issue
Block a user