From 5f9fe505d56f5b1f22f7deaf5a7090c1afdc52a6 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Fri, 25 Jun 2021 23:28:14 -0400 Subject: [PATCH] Add pref to disable infinite scrolling on timelines Closes #125 --- Tusker.xcodeproj/project.pbxproj | 16 +++++ Tusker/Preferences/Preferences.swift | 4 ++ .../Preferences/WellnessPrefsView.swift | 9 +++ .../TimelineTableViewController.swift | 47 +++++++++++--- .../ConfirmLoadMoreTableViewCell.swift | 55 ++++++++++++++++ .../ConfirmLoadMoreTableViewCell.xib | 64 +++++++++++++++++++ 6 files changed, 187 insertions(+), 8 deletions(-) create mode 100644 Tusker/Views/Confirm Load More Cell/ConfirmLoadMoreTableViewCell.swift create mode 100644 Tusker/Views/Confirm Load More Cell/ConfirmLoadMoreTableViewCell.xib diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index dd4d1c08..3849df86 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -305,6 +305,8 @@ D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */; }; D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */; }; D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */; }; + D6DEA0DE268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DEA0DC268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift */; }; + D6DEA0DF268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D6DEA0DD268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib */; }; D6DF95C12533F5DE0027A9B6 /* RelationshipMO.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DF95C02533F5DE0027A9B6 /* RelationshipMO.swift */; }; D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */; }; D6DFC6A0242C4CCC00ACC392 /* WeakArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */; }; @@ -709,6 +711,8 @@ D6D4DDF1212518A200E1C4BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningCopyMode.swift; sourceTree = ""; }; D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Preferences+Notification.swift"; sourceTree = ""; }; + D6DEA0DC268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmLoadMoreTableViewCell.swift; sourceTree = ""; }; + D6DEA0DD268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConfirmLoadMoreTableViewCell.xib; sourceTree = ""; }; D6DF95C02533F5DE0027A9B6 /* RelationshipMO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RelationshipMO.swift; sourceTree = ""; }; D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackpadScrollGestureRecognizer.swift; sourceTree = ""; }; D6DFC69F242C4CCC00ACC392 /* WeakArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakArray.swift; sourceTree = ""; }; @@ -1465,6 +1469,7 @@ D6A3BC872321F78000FD64D5 /* Account Cell */, D611C2CC232DC5FC00C86A49 /* Hashtag Cell */, D61AC1DA232EA43100C54D2D /* Instance Cell */, + D6DEA0DB268400AF00FE896A /* Confirm Load More Cell */, ); path = Views; sourceTree = ""; @@ -1595,6 +1600,15 @@ path = TuskerUITests; sourceTree = ""; }; + D6DEA0DB268400AF00FE896A /* Confirm Load More Cell */ = { + isa = PBXGroup; + children = ( + D6DEA0DC268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift */, + D6DEA0DD268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib */, + ); + path = "Confirm Load More Cell"; + sourceTree = ""; + }; D6E343A9265AAD6B00C4AA01 /* OpenInTusker */ = { isa = PBXGroup; children = ( @@ -1889,6 +1903,7 @@ D6F2E966249E8BFD005846BB /* CrashReporterViewController.xib in Resources */, D662AEF0263A3B880082A153 /* PollFinishedTableViewCell.xib in Resources */, D6A4DCCD2553667800D9DE31 /* FastAccountSwitcherViewController.xib in Resources */, + D6DEA0DF268400C300FE896A /* ConfirmLoadMoreTableViewCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2022,6 +2037,7 @@ D6412B0324AFF6A600F5412E /* TabBarScrollableViewController.swift in Sources */, D6757A822157E8FA00721E32 /* XCBSession.swift in Sources */, D6093FB725BE0CF3004811E6 /* HashtagHistoryView.swift in Sources */, + D6DEA0DE268400C300FE896A /* ConfirmLoadMoreTableViewCell.swift in Sources */, D6EBF01723C55E0D00AE061B /* UISceneSession+MastodonController.swift in Sources */, 04DACE8C212CB14B009840C4 /* MainTabBarViewController.swift in Sources */, D68E525D24A3E8F00054355A /* SearchViewController.swift in Sources */, diff --git a/Tusker/Preferences/Preferences.swift b/Tusker/Preferences/Preferences.swift index 4a0ec233..2be9db5d 100644 --- a/Tusker/Preferences/Preferences.swift +++ b/Tusker/Preferences/Preferences.swift @@ -63,6 +63,7 @@ class Preferences: Codable, ObservableObject { self.showFavoriteAndReblogCounts = try container.decode(Bool.self, forKey: .showFavoriteAndReblogCounts) self.defaultNotificationsMode = try container.decode(NotificationsMode.self, forKey: .defaultNotificationsType) self.grayscaleImages = try container.decodeIfPresent(Bool.self, forKey: .grayscaleImages) ?? false + self.disableInfiniteScrolling = try container.decodeIfPresent(Bool.self, forKey: .disableInfiniteScrolling) ?? false self.silentActions = try container.decode([String: Permission].self, forKey: .silentActions) self.statusContentType = try container.decode(StatusContentType.self, forKey: .statusContentType) @@ -97,6 +98,7 @@ class Preferences: Codable, ObservableObject { try container.encode(showFavoriteAndReblogCounts, forKey: .showFavoriteAndReblogCounts) try container.encode(defaultNotificationsMode, forKey: .defaultNotificationsType) try container.encode(grayscaleImages, forKey: .grayscaleImages) + try container.encode(disableInfiniteScrolling, forKey: .disableInfiniteScrolling) try container.encode(silentActions, forKey: .silentActions) try container.encode(statusContentType, forKey: .statusContentType) @@ -133,6 +135,7 @@ class Preferences: Codable, ObservableObject { @Published var showFavoriteAndReblogCounts = true @Published var defaultNotificationsMode = NotificationsMode.allNotifications @Published var grayscaleImages = false + @Published var disableInfiniteScrolling = false // MARK: Advanced @Published var silentActions: [String: Permission] = [:] @@ -165,6 +168,7 @@ class Preferences: Codable, ObservableObject { case showFavoriteAndReblogCounts case defaultNotificationsType case grayscaleImages + case disableInfiniteScrolling case silentActions case statusContentType diff --git a/Tusker/Screens/Preferences/WellnessPrefsView.swift b/Tusker/Screens/Preferences/WellnessPrefsView.swift index 3b95867c..fc5fad21 100644 --- a/Tusker/Screens/Preferences/WellnessPrefsView.swift +++ b/Tusker/Screens/Preferences/WellnessPrefsView.swift @@ -16,6 +16,7 @@ struct WellnessPrefsView: View { showFavAndReblogCount notificationsMode grayscaleImages + disableInfiniteScrolling } .listStyle(InsetGroupedListStyle()) .navigationBarTitle(Text("Digital Wellness")) @@ -46,6 +47,14 @@ struct WellnessPrefsView: View { } } } + + private var disableInfiniteScrolling: some View { + Section(footer: Text("Require a button tap before loading more posts.")) { + Toggle(isOn: $preferences.disableInfiniteScrolling) { + Text("Disable Infinite Scrolling") + } + } + } } struct WellnessPrefsView_Previews: PreviewProvider { diff --git a/Tusker/Screens/Timeline/TimelineTableViewController.swift b/Tusker/Screens/Timeline/TimelineTableViewController.swift index 3848feaa..73485621 100644 --- a/Tusker/Screens/Timeline/TimelineTableViewController.swift +++ b/Tusker/Screens/Timeline/TimelineTableViewController.swift @@ -19,6 +19,8 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController String { return NSLocalizedString("Refresh Statuses", comment: "refresh status command discoverability title") } + + override func timelineContentSections() -> [Section] { + return [.statuses] + } override func cellProvider(_ tableView: UITableView, _ indexPath: IndexPath, _ item: Item) -> UITableViewCell? { - guard case let .status(id: id, state: state) = item, - let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? TimelineStatusTableViewCell else { - return nil + switch item { + case let .status(id: id, state: state): + let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as! TimelineStatusTableViewCell + + cell.delegate = self + cell.updateUI(statusID: id, state: state) + return cell + + case .confirmLoadMore: + let cell = tableView.dequeueReusableCell(withIdentifier: "confirmLoadMoreCell", for: indexPath) as! ConfirmLoadMoreTableViewCell + cell.confirmLoadMore = { + self.didConfirmLoadMore = true + self.loadOlder() + self.didConfirmLoadMore = false + } + return cell } - - cell.delegate = self - cell.updateUI(statusID: id, state: state) - - return cell } override func loadInitialItems(completion: @escaping (LoadResult) -> Void) { @@ -106,6 +121,20 @@ class TimelineTableViewController: DiffableTimelineLikeTableViewController Void)? + + @IBOutlet weak var confirmButton: UIButton! + + private var isLoading = false + + override func awakeFromNib() { + super.awakeFromNib() + + if #available(iOS 15.0, *) { + var config = UIButton.Configuration.tinted() + config.title = "Load More" + config.showsActivityIndicator = false + config.imagePadding = 4 + confirmButton.configuration = config + confirmButton.configurationUpdateHandler = { [unowned self] button in + button.configuration?.showsActivityIndicator = self.isLoading + } + } + } + + override func prepareForReuse() { + super.prepareForReuse() + + isLoading = false + if #available(iOS 15.0, *) { + confirmButton.setNeedsUpdateConfiguration() + } + } + + @IBAction func loadMorePressed(_ sender: Any) { + confirmLoadMore?() + if #available(iOS 15.0, *) { + isLoading = true + confirmButton.setNeedsUpdateConfiguration() + } + } + +} diff --git a/Tusker/Views/Confirm Load More Cell/ConfirmLoadMoreTableViewCell.xib b/Tusker/Views/Confirm Load More Cell/ConfirmLoadMoreTableViewCell.xib new file mode 100644 index 00000000..49c76e26 --- /dev/null +++ b/Tusker/Views/Confirm Load More Cell/ConfirmLoadMoreTableViewCell.xib @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +