From 832b146c826e987c9094555f183ef25ee6082575 Mon Sep 17 00:00:00 2001 From: CMK Date: Wed, 10 Nov 2021 17:55:12 +0800 Subject: [PATCH] feat: update list fetch trigger logic for UserTimeline scene --- Mastodon.xcodeproj/project.pbxproj | 4 ++ .../xcschemes/xcschememanagement.plist | 8 +-- .../Timeline/UserTimelineViewController.swift | 34 ++++++---- .../Timeline/UserTimelineViewModel.swift | 1 + .../ViewModel/ListBatchFetchViewModel.swift | 68 +++++++++++++++++++ 5 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 Mastodon/Scene/Share/ViewModel/ListBatchFetchViewModel.swift diff --git a/Mastodon.xcodeproj/project.pbxproj b/Mastodon.xcodeproj/project.pbxproj index bad59c2b5..d7bab5217 100644 --- a/Mastodon.xcodeproj/project.pbxproj +++ b/Mastodon.xcodeproj/project.pbxproj @@ -377,6 +377,7 @@ DB71FD5225F8CCAA00512AE1 /* APIService+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB71FD5125F8CCAA00512AE1 /* APIService+Status.swift */; }; DB72601C25E36A2100235243 /* MastodonServerRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */; }; DB72602725E36A6F00235243 /* MastodonServerRulesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */; }; + DB7274F4273BB9B200577D95 /* ListBatchFetchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */; }; DB73B490261F030A002E9E9F /* SafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73B48F261F030A002E9E9F /* SafariActivity.swift */; }; DB73BF3B2711885500781945 /* UserDefaults+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF3A2711885500781945 /* UserDefaults+Notification.swift */; }; DB73BF4127118B6D00781945 /* Instance.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB73BF4027118B6D00781945 /* Instance.swift */; }; @@ -1195,6 +1196,7 @@ DB71FD5125F8CCAA00512AE1 /* APIService+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "APIService+Status.swift"; sourceTree = ""; }; DB72601B25E36A2100235243 /* MastodonServerRulesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewController.swift; sourceTree = ""; }; DB72602625E36A6F00235243 /* MastodonServerRulesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonServerRulesViewModel.swift; sourceTree = ""; }; + DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListBatchFetchViewModel.swift; sourceTree = ""; }; DB73B48F261F030A002E9E9F /* SafariActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariActivity.swift; sourceTree = ""; }; DB73BF3A2711885500781945 /* UserDefaults+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+Notification.swift"; sourceTree = ""; }; DB73BF4027118B6D00781945 /* Instance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instance.swift; sourceTree = ""; }; @@ -2972,6 +2974,7 @@ DB9D6C2225E502C60051B173 /* MosaicImageViewModel.swift */, 2DA6055025F74407006356F9 /* AudioContainerViewModel.swift */, 5DF1054625F8870E00D6C0D4 /* VideoPlayerViewModel.swift */, + DB7274F3273BB9B200577D95 /* ListBatchFetchViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -4233,6 +4236,7 @@ 2D24E11D2626D8B100A59D4F /* NotificationStatusTableViewCell.swift in Sources */, DB6C8C0F25F0A6AE00AAA452 /* Mastodon+Entity+Error.swift in Sources */, DB1E346825F518E20079D7DF /* CategoryPickerSection.swift in Sources */, + DB7274F4273BB9B200577D95 /* ListBatchFetchViewModel.swift in Sources */, 2D61254D262547C200299647 /* APIService+Notification.swift in Sources */, DB040ED126538E3D00BEE9D8 /* Trie.swift in Sources */, DB73BF4B27140C0800781945 /* UITableViewDiffableDataSource.swift in Sources */, diff --git a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist index 7aa368605..921a52196 100644 --- a/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Mastodon.xcodeproj/xcuserdata/mainasuk.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,12 +7,12 @@ AppShared.xcscheme_^#shared#^_ orderHint - 42 + 36 CoreDataStack.xcscheme_^#shared#^_ orderHint - 43 + 38 Mastodon - ASDK.xcscheme_^#shared#^_ @@ -102,7 +102,7 @@ MastodonIntent.xcscheme_^#shared#^_ orderHint - 45 + 39 MastodonIntents.xcscheme_^#shared#^_ @@ -122,7 +122,7 @@ ShareActionExtension.xcscheme_^#shared#^_ orderHint - 44 + 37 SuppressBuildableAutocreation diff --git a/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift b/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift index 42e9376cf..4bee3b8af 100644 --- a/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift +++ b/Mastodon/Scene/Profile/Timeline/UserTimelineViewController.swift @@ -72,6 +72,16 @@ extension UserTimelineViewController { statusTableViewCellDelegate: self ) + // setup batch fetch + viewModel.listBatchFetchViewModel.setup(scrollView: tableView) + viewModel.listBatchFetchViewModel.shouldFetch + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + guard let self = self else { return } + self.viewModel.stateMachine.enter(UserTimelineViewModel.State.Loading.self) + } + .store(in: &disposeBag) + // trigger user timeline loading Publishers.CombineLatest( viewModel.domain.removeDuplicates().eraseToAnyPublisher(), @@ -104,11 +114,11 @@ extension UserTimelineViewController { extension UserTimelineViewController: StatusTableViewControllerAspect { } // MARK: - UIScrollViewDelegate -extension UserTimelineViewController { - func scrollViewDidScroll(_ scrollView: UIScrollView) { - aspectScrollViewDidScroll(scrollView) - } -} +//extension UserTimelineViewController { +// func scrollViewDidScroll(_ scrollView: UIScrollView) { +// aspectScrollViewDidScroll(scrollView) +// } +//} // MARK: - TableViewCellHeightCacheableContainer extension UserTimelineViewController: TableViewCellHeightCacheableContainer { @@ -186,13 +196,13 @@ extension UserTimelineViewController: ScrollViewContainer { } // MARK: - LoadMoreConfigurableTableViewContainer -extension UserTimelineViewController: LoadMoreConfigurableTableViewContainer { - typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell - typealias LoadingState = UserTimelineViewModel.State.Loading - - var loadMoreConfigurableTableView: UITableView { return tableView } - var loadMoreConfigurableStateMachine: GKStateMachine { return viewModel.stateMachine } -} +//extension UserTimelineViewController: LoadMoreConfigurableTableViewContainer { +// typealias BottomLoaderTableViewCell = TimelineBottomLoaderTableViewCell +// typealias LoadingState = UserTimelineViewModel.State.Loading +// +// var loadMoreConfigurableTableView: UITable``````View { return tableView } +// var loadMoreConfigurableStateMachine: GKStateMachine { return viewModel.stateMachine } +//} extension UserTimelineViewController { override var keyCommands: [UIKeyCommand]? { diff --git a/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel.swift b/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel.swift index 42edafb0f..5bf520d6d 100644 --- a/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel.swift +++ b/Mastodon/Scene/Profile/Timeline/UserTimelineViewModel.swift @@ -23,6 +23,7 @@ final class UserTimelineViewModel { let userID: CurrentValueSubject let queryFilter: CurrentValueSubject let statusFetchedResultsController: StatusFetchedResultsController + let listBatchFetchViewModel = ListBatchFetchViewModel() var cellFrameCache = NSCache() let isBlocking = CurrentValueSubject(false) diff --git a/Mastodon/Scene/Share/ViewModel/ListBatchFetchViewModel.swift b/Mastodon/Scene/Share/ViewModel/ListBatchFetchViewModel.swift new file mode 100644 index 000000000..78eaf6ae3 --- /dev/null +++ b/Mastodon/Scene/Share/ViewModel/ListBatchFetchViewModel.swift @@ -0,0 +1,68 @@ +// +// ListBatchFetchViewModel.swift +// Mastodon +// +// Created by Cirno MainasuK on 2021-11-10. +// + +import UIKit +import Combine + +// ref: Texture.ASBatchFetchingDelegate +final class ListBatchFetchViewModel { + + var disposeBag = Set() + + // timer running on `common` mode + let timerPublisher = Timer.publish(every: 1.0, on: .main, in: .common) + .autoconnect() + .share() + .eraseToAnyPublisher() + + // input + private(set) weak var scrollView: UIScrollView? + let hasMore = CurrentValueSubject(true) + + // output + let shouldFetch = PassthroughSubject() + + init() { + Publishers.CombineLatest( + hasMore, + timerPublisher + ) + .sink { [weak self] hasMore, _ in + guard let self = self else { return } + guard hasMore else { return } + guard let scrollView = self.scrollView else { return } + + // skip trigger if user interacting + if scrollView.isDragging || scrollView.isTracking { return } + + // send fetch request + if scrollView.contentSize.height < scrollView.frame.height { + self.shouldFetch.send() + } else { + let frame = scrollView.frame + let contentOffset = scrollView.contentOffset + let contentSize = scrollView.contentSize + + let visibleBottomY = contentOffset.y + frame.height + let offset = 2 * frame.height + let fetchThrottleOffsetY = contentSize.height - offset + + if visibleBottomY > fetchThrottleOffsetY { + self.shouldFetch.send() + } + } + } + .store(in: &disposeBag) + } + +} + +extension ListBatchFetchViewModel { + func setup(scrollView: UIScrollView) { + self.scrollView = scrollView + } +}