From 8fb20269e302377f85f1626f21372587a60b5ac1 Mon Sep 17 00:00:00 2001 From: Justin Mazzocchi <2831158+jzzocc@users.noreply.github.com> Date: Sat, 16 Jan 2021 12:06:35 -0800 Subject: [PATCH] Refresh --- .../Services/AccountListService.swift | 1 + .../Services/CollectionService.swift | 3 ++ View Controllers/TableViewController.swift | 30 +++++++++++++++---- .../ViewModels/CollectionItemsViewModel.swift | 2 ++ .../ViewModels/CollectionViewModel.swift | 1 + .../Sources/ViewModels/ProfileViewModel.swift | 2 ++ Views/LoadingTableFooterView.swift | 1 + 7 files changed, 34 insertions(+), 6 deletions(-) diff --git a/ServiceLayer/Sources/ServiceLayer/Services/AccountListService.swift b/ServiceLayer/Sources/ServiceLayer/Services/AccountListService.swift index 0b3e949..ff9028c 100644 --- a/ServiceLayer/Sources/ServiceLayer/Services/AccountListService.swift +++ b/ServiceLayer/Sources/ServiceLayer/Services/AccountListService.swift @@ -10,6 +10,7 @@ public struct AccountListService { public let sections: AnyPublisher<[[CollectionItem]], Error> public let nextPageMaxId: AnyPublisher public let navigationService: NavigationService + public let canRefresh = false private let accountList = CurrentValueSubject<[Account], Error>([]) private let endpoint: AccountsEndpoint diff --git a/ServiceLayer/Sources/ServiceLayer/Services/CollectionService.swift b/ServiceLayer/Sources/ServiceLayer/Services/CollectionService.swift index 96670b2..13bb17e 100644 --- a/ServiceLayer/Sources/ServiceLayer/Services/CollectionService.swift +++ b/ServiceLayer/Sources/ServiceLayer/Services/CollectionService.swift @@ -7,6 +7,7 @@ public protocol CollectionService { var sections: AnyPublisher<[[CollectionItem]], Error> { get } var nextPageMaxId: AnyPublisher { get } var preferLastPresentIdOverNextPageMaxId: Bool { get } + var canRefresh: Bool { get } var title: AnyPublisher { get } var titleLocalizationComponents: AnyPublisher<[String], Never> { get } var navigationService: NavigationService { get } @@ -19,6 +20,8 @@ extension CollectionService { public var preferLastPresentIdOverNextPageMaxId: Bool { false } + public var canRefresh: Bool { true } + public var title: AnyPublisher { Empty().eraseToAnyPublisher() } public var titleLocalizationComponents: AnyPublisher<[String], Never> { Empty().eraseToAnyPublisher() } diff --git a/View Controllers/TableViewController.swift b/View Controllers/TableViewController.swift index 4c6546b..36ca865 100644 --- a/View Controllers/TableViewController.swift +++ b/View Controllers/TableViewController.swift @@ -47,6 +47,14 @@ class TableViewController: UITableViewController { tableView.tableFooterView = UIView() tableView.contentInset.bottom = Self.bottomInset + if viewModel.canRefresh { + refreshControl = UIRefreshControl() + refreshControl?.addAction( + UIAction { [weak self] _ in + self?.viewModel.request(maxId: nil, minId: nil) }, + for: .valueChanged) + } + view.addSubview(webfingerIndicatorView) webfingerIndicatorView.translatesAutoresizingMaskIntoConstraints = false @@ -208,6 +216,7 @@ extension TableViewController: ZoomAnimatorDelegate { private extension TableViewController { static let bottomInset: CGFloat = .newStatusButtonDimension + .defaultSpacing * 4 + static let loadingFooterDebounceInterval: TimeInterval = 0.5 var visibleLoadMoreViews: [LoadMoreView] { tableView.visibleCells.compactMap { $0.contentView as? LoadMoreView } @@ -237,13 +246,22 @@ private extension TableViewController { viewModel.loading.receive(on: DispatchQueue.main).assign(to: &$loading) - $loading.sink { [weak self] in - guard let self = self else { return } + $loading.debounce( + for: .seconds(Self.loadingFooterDebounceInterval), + scheduler: DispatchQueue.main) + .sink { [weak self] in + guard let self = self else { return } - self.tableView.tableFooterView = $0 ? self.loadingTableFooterView : UIView() - self.sizeTableHeaderFooterViews() - } - .store(in: &cancellables) + if !$0 { + self.refreshControl?.endRefreshing() + } + + let refreshControlVisibile = self.refreshControl?.isRefreshing ?? false + + self.tableView.tableFooterView = $0 && !refreshControlVisibile ? self.loadingTableFooterView : UIView() + self.sizeTableHeaderFooterViews() + } + .store(in: &cancellables) viewModel.alertItems .compactMap { $0 } diff --git a/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift b/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift index f2473cd..0010348 100644 --- a/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift +++ b/ViewModels/Sources/ViewModels/CollectionItemsViewModel.swift @@ -84,6 +84,8 @@ extension CollectionItemsViewModel: CollectionViewModel { public var preferLastPresentIdOverNextPageMaxId: Bool { collectionService.preferLastPresentIdOverNextPageMaxId } + public var canRefresh: Bool { collectionService.canRefresh } + public func request(maxId: String? = nil, minId: String? = nil) { let publisher: AnyPublisher diff --git a/ViewModels/Sources/ViewModels/CollectionViewModel.swift b/ViewModels/Sources/ViewModels/CollectionViewModel.swift index fc90338..778a7c3 100644 --- a/ViewModels/Sources/ViewModels/CollectionViewModel.swift +++ b/ViewModels/Sources/ViewModels/CollectionViewModel.swift @@ -14,6 +14,7 @@ public protocol CollectionViewModel { var shouldAdjustContentInset: Bool { get } var nextPageMaxId: String? { get } var preferLastPresentIdOverNextPageMaxId: Bool { get } + var canRefresh: Bool { get } func request(maxId: String?, minId: String?) func viewedAtTop(indexPath: IndexPath) func select(indexPath: IndexPath) diff --git a/ViewModels/Sources/ViewModels/ProfileViewModel.swift b/ViewModels/Sources/ViewModels/ProfileViewModel.swift index f7ea651..6828c6a 100644 --- a/ViewModels/Sources/ViewModels/ProfileViewModel.swift +++ b/ViewModels/Sources/ViewModels/ProfileViewModel.swift @@ -107,6 +107,8 @@ extension ProfileViewModel: CollectionViewModel { collectionViewModel.value.preferLastPresentIdOverNextPageMaxId } + public var canRefresh: Bool { collectionViewModel.value.canRefresh } + public func request(maxId: String?, minId: String?) { if case .statuses = collection, maxId == nil { profileService.fetchPinnedStatuses() diff --git a/Views/LoadingTableFooterView.swift b/Views/LoadingTableFooterView.swift index 4dda862..719211e 100644 --- a/Views/LoadingTableFooterView.swift +++ b/Views/LoadingTableFooterView.swift @@ -10,6 +10,7 @@ final class LoadingTableFooterView: UIView { addSubview(activityIndicatorView) activityIndicatorView.translatesAutoresizingMaskIntoConstraints = false + activityIndicatorView.style = .large NSLayoutConstraint.activate([ activityIndicatorView.centerXAnchor.constraint(equalTo: centerXAnchor),