From 0b5ca9ef9f44295bd513a23e59839e43497e5a1c Mon Sep 17 00:00:00 2001 From: Javier Cicchelli Date: Thu, 21 Mar 2024 02:29:43 +0000 Subject: [PATCH] [Framework] Pull to refresh in the Feed List (#13) This PR contains the work done to implement the pull to refresh into the `FeedListViewController` view controller. Reviewed-on: https://repo.rock-n-code.com/rock-n-code/app-reviews/pulls/13 Co-authored-by: Javier Cicchelli Co-committed-by: Javier Cicchelli --- .../Resources/Catalogs/Localizable.xcstrings | 12 +++++ .../Logic/View Models/FeedListViewModel.swift | 5 +- .../FeedListViewController.swift | 52 ++++++++++++++++--- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/Frameworks/Feed/Bundle/Resources/Catalogs/Localizable.xcstrings b/Frameworks/Feed/Bundle/Resources/Catalogs/Localizable.xcstrings index d39c508..6c279f8 100644 --- a/Frameworks/Feed/Bundle/Resources/Catalogs/Localizable.xcstrings +++ b/Frameworks/Feed/Bundle/Resources/Catalogs/Localizable.xcstrings @@ -108,6 +108,18 @@ } } } + }, + "view.feed-list.pull-to-refresh.title.text" : { + "comment" : "The title for the Pull To Refresh control associated to the table in the Feed List view.", + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pull to fetch the latest reviews" + } + } + } } }, "version" : "1.0" diff --git a/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift b/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift index a384c3a..059a3fa 100644 --- a/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift +++ b/Frameworks/Feed/Bundle/Sources/Logic/View Models/FeedListViewModel.swift @@ -82,7 +82,10 @@ extension FeedListViewController { topWords($0).map(TopWord.init) } - items = reviewsAll + items = filter == .all + ? reviewsAll + : reviewsFiltered[filter] ?? [] + isFilterEnabled = !items.isEmpty } catch { // TODO: handle this error gracefully. diff --git a/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift b/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift index eb78d32..6717ea6 100644 --- a/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift +++ b/Frameworks/Feed/Bundle/Sources/UI/View Controllers/FeedListViewController.swift @@ -51,6 +51,24 @@ public class FeedListViewController: UITableViewController { ) ) }() + + private lazy var pullControl = { + let control = UIRefreshControl() + + control.attributedTitle = .init(string: NSLocalizedString( + .Key.PullToRefresh.title, + bundle: .module, + comment: .empty + )) + + control.addTarget( + self, + action: #selector(refresh(_:)), + for: .valueChanged + ) + + return control + }() // MARK: Initialisers public init(configuration: Configuration = .init()) { @@ -73,6 +91,7 @@ public class FeedListViewController: UITableViewController { super.viewDidLoad() setNavigationBar() + setView() registerTableCells() bindViewModel() @@ -103,20 +122,28 @@ public class FeedListViewController: UITableViewController { _ tableView: UITableView, didSelectRowAt indexPath: IndexPath ) { - let details = FeedItemViewController(items[indexPath.row]) - + guard let item = viewModel.item(for: indexPath.row) else { return } + tableView.deselectRow( at: indexPath, animated: true ) - - navigationController?.pushViewController(details, animated: true) + + navigationController?.pushViewController( + FeedItemViewController(item), + animated: true + ) } } // MARK: - Helpers private extension FeedListViewController { + + // MARK: Actions + @objc func refresh(_ sender: AnyObject) { + self.viewModel.fetch() + } // MARK: Functions func bindViewModel() { @@ -139,10 +166,15 @@ private extension FeedListViewController { viewModel.$isLoading .dropFirst() .filter { $0 == false } - .removeDuplicates() .receive(on: RunLoop.main) .sink { [weak self] _ in + self?.pullControl.endRefreshing() self?.tableView.reloadData() + self?.tableView.scrollToRow( + at: .init(row: 0, section: 0), + at: .middle, + animated: true + ) } .store(in: &cancellables) } @@ -194,7 +226,7 @@ private extension FeedListViewController { return cell } - + func registerTableCells() { FeedItemCell.register(in: tableView) TopWordsCell.register(in: tableView) @@ -212,6 +244,10 @@ private extension FeedListViewController { ) } + func setView() { + tableView.refreshControl = pullControl + } + func updateFilterMenu(_ option: FilterOption) { filterButton .menu? @@ -267,6 +303,10 @@ private extension String { static let filter = "view.feed-list.navigation-bar.button.filter-list.text" } } + + enum PullToRefresh { + static let title = "view.feed-list.pull-to-refresh.title.text" + } } }