diff --git a/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift b/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift index e1cbef7..a36237a 100644 --- a/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift +++ b/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift @@ -7,6 +7,7 @@ // import Foundation +import ReviewsFilterKit import ReviewsiTunesKit extension FeedListViewController { @@ -21,10 +22,14 @@ extension FeedListViewController { // MARK: Properties @Published var filter: FilterOption = .all @Published var isFilterEnabled: Bool = false + @Published var isFiltering: Bool = false @Published var isLoading: Bool = false var items: [Review] = [] + private var reviewsAll: [Review] = [] + private var reviewsFiltered: FilteredReviews = [:] + lazy private var iTunesService: iTunesService = { .init(configuration: .init(session: configuration.session)) }() @@ -46,19 +51,13 @@ extension FeedListViewController { countryCode: configuration.countryCode )) - items = output.reviews - .map { review -> Review in - .init( - author: review.author, - comment: review.content, - id: review.id, - rating: .init( - stars: review.rating, - appVersion: review.version - ), - title: review.title - ) + reviewsAll = output.reviews.map(Review.init) + reviewsFiltered = FilterOption.allCases + .reduce(into: FilteredReviews()) { partialResult, option in + partialResult[option] = reviewsAll.filter { $0.rating.stars == option.rawValue } } + + items = reviewsAll isFilterEnabled = !items.isEmpty } catch { // TODO: handle this error gracefully. @@ -68,6 +67,24 @@ extension FeedListViewController { 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]] + +} diff --git a/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift b/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift index fb60287..ed019a8 100644 --- a/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift +++ b/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift @@ -23,14 +23,7 @@ public class FeedListViewController: UITableViewController { // MARK: Outlets private lazy var filterButton = { - let allStars = UIAction( - title: FilterOption.all.text, - image: .init(systemName: FilterOption.all.icon) - ) { [weak self] _ in - self?.viewModel.filter = .all - } - - return UIBarButtonItem( + UIBarButtonItem( title: NSLocalizedString( .Key.Navigation.Button.filter, bundle: .module, @@ -45,26 +38,19 @@ public class FeedListViewController: UITableViewController { comment: .empty ), 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 public init(configuration: Configuration = .init()) { self.viewModel = .init(configuration: configuration) @@ -145,6 +131,14 @@ private extension FeedListViewController { // MARK: Functions func bindViewModel() { + viewModel.$filter + .receive(on: RunLoop.main) + .sink { [weak self] option in + self?.updateFilterMenu(option) + self?.tableView.reloadData() + } + .store(in: &cancellables) + viewModel.$isFilterEnabled .removeDuplicates() .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