Compare commits
No commits in common. "89b35fab6dca1b7dd84fe2346b3f10bc16451e1a" and "93828830a993334c2ba7ffd056c07e81f7053908" have entirely different histories.
89b35fab6d
...
93828830a9
|
@ -145,7 +145,6 @@
|
||||||
D6531DEE246B81C9000F9538 /* GifvAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */; };
|
D6531DEE246B81C9000F9538 /* GifvAttachmentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */; };
|
||||||
D6531DF0246B867E000F9538 /* GifvAttachmentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */; };
|
D6531DF0246B867E000F9538 /* GifvAttachmentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */; };
|
||||||
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */; };
|
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */; };
|
||||||
D65C6BF525478A9C00A6E89C /* BackgroundableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65C6BF425478A9C00A6E89C /* BackgroundableViewController.swift */; };
|
|
||||||
D65F613423AEAB6600F3CFD3 /* OnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65F613323AEAB6600F3CFD3 /* OnboardingTests.swift */; };
|
D65F613423AEAB6600F3CFD3 /* OnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D65F613323AEAB6600F3CFD3 /* OnboardingTests.swift */; };
|
||||||
D6620ACE2511A0ED00312CA0 /* StatusStateResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */; };
|
D6620ACE2511A0ED00312CA0 /* StatusStateResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */; };
|
||||||
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */; };
|
D663625D2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */; };
|
||||||
|
@ -483,7 +482,6 @@
|
||||||
D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentView.swift; sourceTree = "<group>"; };
|
D6531DED246B81C9000F9538 /* GifvAttachmentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentView.swift; sourceTree = "<group>"; };
|
||||||
D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentViewController.swift; sourceTree = "<group>"; };
|
D6531DEF246B867E000F9538 /* GifvAttachmentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GifvAttachmentViewController.swift; sourceTree = "<group>"; };
|
||||||
D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewSwipeActionProvider.swift; sourceTree = "<group>"; };
|
D6538944214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewSwipeActionProvider.swift; sourceTree = "<group>"; };
|
||||||
D65C6BF425478A9C00A6E89C /* BackgroundableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundableViewController.swift; sourceTree = "<group>"; };
|
|
||||||
D65F612D23AE990C00F3CFD3 /* Embassy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Embassy.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
D65F612D23AE990C00F3CFD3 /* Embassy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Embassy.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D65F613023AE99E000F3CFD3 /* Ambassador.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Ambassador.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
D65F613023AE99E000F3CFD3 /* Ambassador.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Ambassador.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
D65F613323AEAB6600F3CFD3 /* OnboardingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTests.swift; sourceTree = "<group>"; };
|
D65F613323AEAB6600F3CFD3 /* OnboardingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingTests.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1329,7 +1327,6 @@
|
||||||
D693DE5823FE24300061E07D /* InteractivePushTransition.swift */,
|
D693DE5823FE24300061E07D /* InteractivePushTransition.swift */,
|
||||||
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */,
|
D6DFC69D242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift */,
|
||||||
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
|
D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */,
|
||||||
D65C6BF425478A9C00A6E89C /* BackgroundableViewController.swift */,
|
|
||||||
);
|
);
|
||||||
path = Utilities;
|
path = Utilities;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1867,7 +1864,6 @@
|
||||||
D6BC9DDA232D8BE5002CA326 /* SearchResultsViewController.swift in Sources */,
|
D6BC9DDA232D8BE5002CA326 /* SearchResultsViewController.swift in Sources */,
|
||||||
D627FF7F217E95E000CC0648 /* DraftTableViewCell.swift in Sources */,
|
D627FF7F217E95E000CC0648 /* DraftTableViewCell.swift in Sources */,
|
||||||
D6DF95C12533F5DE0027A9B6 /* RelationshipMO.swift in Sources */,
|
D6DF95C12533F5DE0027A9B6 /* RelationshipMO.swift in Sources */,
|
||||||
D65C6BF525478A9C00A6E89C /* BackgroundableViewController.swift in Sources */,
|
|
||||||
D6AEBB4A23216F0400E5038B /* UnfollowAccountActivity.swift in Sources */,
|
D6AEBB4A23216F0400E5038B /* UnfollowAccountActivity.swift in Sources */,
|
||||||
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
||||||
D6D3FDE024F41B8400FF50A5 /* ComposeContainerView.swift in Sources */,
|
D6D3FDE024F41B8400FF50A5 /* ComposeContainerView.swift in Sources */,
|
||||||
|
|
|
@ -108,10 +108,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
||||||
// to restore the scene back to its current state.
|
// to restore the scene back to its current state.
|
||||||
|
|
||||||
if let rootVC = window?.rootViewController as? BackgroundableViewController {
|
|
||||||
rootVC.sceneDidEnterBackground()
|
|
||||||
}
|
|
||||||
|
|
||||||
try! scene.session.mastodonController?.persistentContainer.viewContext.save()
|
try! scene.session.mastodonController?.persistentContainer.viewContext.save()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -334,32 +334,4 @@ extension MainSplitViewController: TuskerRootViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController? {
|
|
||||||
if traitCollection.horizontalSizeClass == .compact {
|
|
||||||
return tabBarViewController?.getTabController(tab: tab)
|
|
||||||
} else {
|
|
||||||
if tab == .compose {
|
|
||||||
return nil
|
|
||||||
} else if case .tab(tab) = sidebar.selectedItem {
|
|
||||||
return viewController(for: .secondary)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 14.0, *)
|
|
||||||
extension MainSplitViewController: BackgroundableViewController {
|
|
||||||
func sceneDidEnterBackground() {
|
|
||||||
if traitCollection.horizontalSizeClass == .compact {
|
|
||||||
tabBarViewController.sceneDidEnterBackground()
|
|
||||||
} else {
|
|
||||||
// todo: should this do the same for the sidebar VC as well?
|
|
||||||
if let contentVC = viewController(for: .secondary) as? BackgroundableViewController {
|
|
||||||
contentVC.sceneDidEnterBackground()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,11 +136,3 @@ extension MainTabBarViewController: TuskerRootViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MainTabBarViewController: BackgroundableViewController {
|
|
||||||
func sceneDidEnterBackground() {
|
|
||||||
if let selectedVC = selectedViewController as? BackgroundableViewController {
|
|
||||||
selectedVC.sceneDidEnterBackground()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,5 +11,4 @@ import UIKit
|
||||||
protocol TuskerRootViewController: UIViewController {
|
protocol TuskerRootViewController: UIViewController {
|
||||||
func presentCompose()
|
func presentCompose()
|
||||||
func select(tab: MainTabBarViewController.Tab)
|
func select(tab: MainTabBarViewController.Tab)
|
||||||
func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController?
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,18 +19,15 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
weak var mastodonController: MastodonController!
|
weak var mastodonController: MastodonController!
|
||||||
|
|
||||||
private let excludedTypes: [Pachyderm.Notification.Kind]
|
let excludedTypes: [Pachyderm.Notification.Kind]
|
||||||
private let groupTypes = [Notification.Kind.favourite, .reblog, .follow]
|
let groupTypes = [Notification.Kind.favourite, .reblog, .follow]
|
||||||
|
|
||||||
private var loaded = false
|
private var loaded = false
|
||||||
|
|
||||||
private var groups: [NotificationGroup] = []
|
var groups: [NotificationGroup] = []
|
||||||
|
|
||||||
private let pageSize = 20
|
var newer: RequestRange?
|
||||||
private var newer: RequestRange?
|
var older: RequestRange?
|
||||||
private var older: RequestRange?
|
|
||||||
|
|
||||||
private var lastLastVisibleRow: IndexPath?
|
|
||||||
|
|
||||||
init(allowedTypes: [Pachyderm.Notification.Kind], mastodonController: MastodonController) {
|
init(allowedTypes: [Pachyderm.Notification.Kind], mastodonController: MastodonController) {
|
||||||
self.excludedTypes = Array(Set(Pachyderm.Notification.Kind.allCases).subtracting(allowedTypes))
|
self.excludedTypes = Array(Set(Pachyderm.Notification.Kind.allCases).subtracting(allowedTypes))
|
||||||
|
@ -87,41 +84,6 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool) {
|
|
||||||
super.viewWillDisappear(animated)
|
|
||||||
|
|
||||||
pruneOffscreenRows()
|
|
||||||
}
|
|
||||||
|
|
||||||
private func pruneOffscreenRows() {
|
|
||||||
guard let lastVisibleRow = lastLastVisibleRow else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let lastRowIndex = groups.count - 1
|
|
||||||
|
|
||||||
if lastVisibleRow.row < lastRowIndex - pageSize {
|
|
||||||
// if there are more than 20 rows below the lats visible one
|
|
||||||
|
|
||||||
let rowIndicesToRemove = (lastVisibleRow.row + pageSize)..<groups.count
|
|
||||||
|
|
||||||
let groupsToRemove = groups[rowIndicesToRemove]
|
|
||||||
for group in groupsToRemove {
|
|
||||||
for notification in group.notifications {
|
|
||||||
if let id = notification.status?.id {
|
|
||||||
mastodonController.persistentContainer.status(for: id)?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
groups.removeSubrange(rowIndicesToRemove)
|
|
||||||
|
|
||||||
let removedIndexPaths = rowIndicesToRemove.map { IndexPath(row: $0, section: 0) }
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
tableView.deleteRows(at: removedIndexPaths, with: .none)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Table view data source
|
// MARK: - Table view data source
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||||
|
@ -175,7 +137,32 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
||||||
// MARK: - Table view delegate
|
// MARK: - Table view delegate
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
lastLastVisibleRow = tableView.indexPathsForVisibleRows?.last
|
// see TimelineTableViewController.tableView(_:willDisplay:forRowAt:)
|
||||||
|
if !isCurrentlyScrollingToTop, scrollViewDirection < 0 {
|
||||||
|
let pageSize = 20
|
||||||
|
if groups.count > 2 * pageSize,
|
||||||
|
indexPath.row < groups.count - (2 * pageSize) {
|
||||||
|
let groupsToRemove = groups[groups.count - pageSize..<groups.count]
|
||||||
|
for group in groupsToRemove {
|
||||||
|
for notification in group.notifications {
|
||||||
|
// todo: reference count accounts
|
||||||
|
// mastodonController.persistentContainer.account(for: notification.account.id)?.decrementReferenceCount()
|
||||||
|
if let id = notification.status?.id {
|
||||||
|
mastodonController.persistentContainer.status(for: id)?.decrementReferenceCount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let removedIndexPaths = (groups.count - 20..<groups.count).map { IndexPath(row: $0, section: 0) }
|
||||||
|
|
||||||
|
groups.removeLast(pageSize)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
tableView.deleteRows(at: removedIndexPaths, with: .none)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if indexPath.row == groups.count - 1 {
|
if indexPath.row == groups.count - 1 {
|
||||||
guard let older = older else { return }
|
guard let older = older else { return }
|
||||||
|
@ -318,9 +305,3 @@ extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension NotificationsTableViewController: BackgroundableViewController {
|
|
||||||
func sceneDidEnterBackground() {
|
|
||||||
pruneOffscreenRows()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -224,6 +224,8 @@ class ProfileStatusesViewController: EnhancedTableViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
|
// todo: if scrolling up, remove statuses at bottom like timeline VC
|
||||||
|
|
||||||
// load older statuses if at bottom
|
// load older statuses if at bottom
|
||||||
if timelineSegments.count > 0,
|
if timelineSegments.count > 0,
|
||||||
indexPath.section == timelineSegments.count,
|
indexPath.section == timelineSegments.count,
|
||||||
|
|
|
@ -18,11 +18,8 @@ class TimelineTableViewController: EnhancedTableViewController, StatusTableViewC
|
||||||
|
|
||||||
var timelineSegments: [[(id: String, state: StatusState)]] = []
|
var timelineSegments: [[(id: String, state: StatusState)]] = []
|
||||||
|
|
||||||
private let pageSize = 20
|
var newer: RequestRange?
|
||||||
private var newer: RequestRange?
|
var older: RequestRange?
|
||||||
private var older: RequestRange?
|
|
||||||
|
|
||||||
private var lastLastVisibleRow: IndexPath?
|
|
||||||
|
|
||||||
init(for timeline: Timeline, mastodonController: MastodonController) {
|
init(for timeline: Timeline, mastodonController: MastodonController) {
|
||||||
self.timeline = timeline
|
self.timeline = timeline
|
||||||
|
@ -76,12 +73,6 @@ class TimelineTableViewController: EnhancedTableViewController, StatusTableViewC
|
||||||
loadInitialStatuses()
|
loadInitialStatuses()
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillDisappear(_ animated: Bool) {
|
|
||||||
super.viewWillDisappear(animated)
|
|
||||||
|
|
||||||
pruneOffscreenRows()
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadInitialStatuses() {
|
func loadInitialStatuses() {
|
||||||
guard !loaded else { return }
|
guard !loaded else { return }
|
||||||
loaded = true
|
loaded = true
|
||||||
|
@ -100,52 +91,6 @@ class TimelineTableViewController: EnhancedTableViewController, StatusTableViewC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func pruneOffscreenRows() {
|
|
||||||
guard let lastVisibleRow = lastLastVisibleRow else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let lastSectionIndex = timelineSegments.count - 1
|
|
||||||
|
|
||||||
if lastVisibleRow.section < lastSectionIndex {
|
|
||||||
// if there is a section below the last visible one
|
|
||||||
|
|
||||||
let sectionsToRemove = (lastVisibleRow.section + 1)...lastSectionIndex
|
|
||||||
|
|
||||||
for section in sectionsToRemove {
|
|
||||||
for (id, _) in timelineSegments.remove(at: section) {
|
|
||||||
mastodonController.persistentContainer.status(for: id)?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
tableView.deleteSections(IndexSet(sectionsToRemove), with: .none)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if lastVisibleRow.section == lastSectionIndex {
|
|
||||||
let lastSection = timelineSegments.last!
|
|
||||||
let lastRowIndex = lastSection.count - 1
|
|
||||||
|
|
||||||
if lastVisibleRow.row < lastRowIndex - 20 {
|
|
||||||
// if there are more than 20 rows in the current section below the last visible one
|
|
||||||
|
|
||||||
let rowIndicesInLastSectionToRemove = (lastVisibleRow.row + 20)..<lastSection.count
|
|
||||||
|
|
||||||
let statusesToRemove = lastSection[rowIndicesInLastSectionToRemove]
|
|
||||||
for (id, _) in statusesToRemove {
|
|
||||||
mastodonController.persistentContainer.status(for: id)?.decrementReferenceCount()
|
|
||||||
}
|
|
||||||
|
|
||||||
timelineSegments[lastSectionIndex].removeSubrange(rowIndicesInLastSectionToRemove)
|
|
||||||
|
|
||||||
let removedIndexPaths = rowIndicesInLastSectionToRemove.map { IndexPath(row: $0, section: lastSectionIndex) }
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
tableView.deleteRows(at: removedIndexPaths, with: .none)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Table view data source
|
// MARK: - Table view data source
|
||||||
|
|
||||||
|
@ -172,8 +117,58 @@ class TimelineTableViewController: EnhancedTableViewController, StatusTableViewC
|
||||||
// MARK: - Table view delegate
|
// MARK: - Table view delegate
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
// this assumes that indexPathsForVisibleRows is always in order
|
// don't remove rows when jumping to the top, otherwise jumping back down might try to show removed rows
|
||||||
lastLastVisibleRow = tableView.indexPathsForVisibleRows?.last
|
// when scrolling upwards, decrement reference counts for old statuses, if necessary
|
||||||
|
if !isCurrentlyScrollingToTop, scrollViewDirection < 0 {
|
||||||
|
if indexPath.section <= timelineSegments.count - 2 {
|
||||||
|
// decrement ref counts for all sections below the section below the current section
|
||||||
|
// (e.g., there exist sections 0, 1, 2 and we're currently scrolling upwards in section 0, we want to remove section 2)
|
||||||
|
|
||||||
|
// todo: this is in the hot path for scrolling, possibly move this to a background thread?
|
||||||
|
let sectionsToRemove = indexPath.section + 1..<timelineSegments.count
|
||||||
|
for section in sectionsToRemove {
|
||||||
|
for (id, _) in timelineSegments.remove(at: section) {
|
||||||
|
mastodonController.persistentContainer.status(for: id)?.decrementReferenceCount()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// see below comment about DispatchQueue.main.async
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
tableView.deleteSections(IndexSet(sectionsToRemove), with: .none)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we are scrolling in the last or second to last section
|
||||||
|
// grab the last section and, if there is more than one page in the last section
|
||||||
|
// (we never want to remove the only page in the last section unless there is another section between it and the user),
|
||||||
|
// remove the last page and decrement reference counts
|
||||||
|
let pageSize = 20 // todo: this should come from somewhere in Pachyderm
|
||||||
|
let lastSection = timelineSegments.last!
|
||||||
|
if lastSection.count > 2 * pageSize,
|
||||||
|
indexPath.row < lastSection.count - (2 * pageSize) {
|
||||||
|
// todo: this is in the hot path for scrolling, possibly move this to a background thread?
|
||||||
|
let statusesToRemove = lastSection[lastSection.count - pageSize..<lastSection.count]
|
||||||
|
for (id, _) in statusesToRemove {
|
||||||
|
mastodonController.persistentContainer.status(for: id)?.decrementReferenceCount()
|
||||||
|
}
|
||||||
|
timelineSegments[timelineSegments.count - 1].removeLast(pageSize)
|
||||||
|
|
||||||
|
let removedIndexPaths = (lastSection.count - 20..<lastSection.count).map { IndexPath(row: $0, section: timelineSegments.count - 1) }
|
||||||
|
// Removing this DispatchQueue.main.async call causes things to break when scrolling
|
||||||
|
// back down towards the removed rows. There would be a index out of bounds crash
|
||||||
|
// because, while we've already removed the statuses from our model, the table view doesn't seem to know that.
|
||||||
|
// It seems like tableView update calls made from inside tableView(_:willDisplay:forRowAt:) are silently ignored.
|
||||||
|
// Calling tableView.numberOfRows(inSection: 0) when trapped in the debugger after the aforementioned IOOB
|
||||||
|
// will produce an incorrect value (it will be some multiple of pageSize too high).
|
||||||
|
// Deferring the tableView update until the next runloop iteration seems to solve that.
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
tableView.deleteRows(at: removedIndexPaths, with: .none)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// load older statuses, if necessary
|
// load older statuses, if necessary
|
||||||
if indexPath.section == timelineSegments.count - 1,
|
if indexPath.section == timelineSegments.count - 1,
|
||||||
|
@ -287,9 +282,3 @@ extension TimelineTableViewController: UITableViewDataSourcePrefetching {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TimelineTableViewController: BackgroundableViewController {
|
|
||||||
func sceneDidEnterBackground() {
|
|
||||||
pruneOffscreenRows()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
//
|
|
||||||
// BackgroundableViewController.swift
|
|
||||||
// Tusker
|
|
||||||
//
|
|
||||||
// Created by Shadowfacts on 10/26/20.
|
|
||||||
// Copyright © 2020 Shadowfacts. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
protocol BackgroundableViewController {
|
|
||||||
func sceneDidEnterBackground()
|
|
||||||
}
|
|
|
@ -74,11 +74,3 @@ class EnhancedNavigationViewController: UINavigationController {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension EnhancedNavigationViewController: BackgroundableViewController {
|
|
||||||
func sceneDidEnterBackground() {
|
|
||||||
if let topVC = topViewController as? BackgroundableViewController {
|
|
||||||
topVC.sceneDidEnterBackground()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -71,11 +71,3 @@ extension SegmentedPageViewController: TabBarScrollableViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SegmentedPageViewController: BackgroundableViewController {
|
|
||||||
func sceneDidEnterBackground() {
|
|
||||||
if let current = pageControllers[currentIndex] as? BackgroundableViewController {
|
|
||||||
current.sceneDidEnterBackground()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -21,14 +21,14 @@ class UserActivityManager {
|
||||||
return scene.session.mastodonController!
|
return scene.session.mastodonController!
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getMainViewController() -> TuskerRootViewController {
|
private static func getMainTabBarController() -> MainTabBarViewController {
|
||||||
let scene = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.first!
|
let scene = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.first!
|
||||||
let window = scene.windows.first { $0.isKeyWindow }!
|
let window = scene.windows.first { $0.isKeyWindow }!
|
||||||
return window.rootViewController as! TuskerRootViewController
|
return window.rootViewController as! MainTabBarViewController
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func present(_ vc: UIViewController, animated: Bool = true) {
|
private static func present(_ vc: UIViewController, animated: Bool = true) {
|
||||||
getMainViewController().present(vc, animated: animated)
|
getMainTabBarController().present(vc, animated: animated)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - New Post
|
// MARK: - New Post
|
||||||
|
@ -66,9 +66,9 @@ class UserActivityManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func handleCheckNotifications(activity: NSUserActivity) {
|
static func handleCheckNotifications(activity: NSUserActivity) {
|
||||||
let mainViewController = getMainViewController()
|
let tabBarController = getMainTabBarController()
|
||||||
mainViewController.select(tab: .notifications)
|
tabBarController.select(tab: .notifications)
|
||||||
if let navigationController = mainViewController.getTabController(tab: .notifications) as? UINavigationController,
|
if let navigationController = tabBarController.getTabController(tab: .notifications) as? UINavigationController,
|
||||||
let notificationsPageController = navigationController.viewControllers.first as? NotificationsPageViewController {
|
let notificationsPageController = navigationController.viewControllers.first as? NotificationsPageViewController {
|
||||||
navigationController.popToRootViewController(animated: false)
|
navigationController.popToRootViewController(animated: false)
|
||||||
notificationsPageController.loadViewIfNeeded()
|
notificationsPageController.loadViewIfNeeded()
|
||||||
|
@ -86,9 +86,9 @@ class UserActivityManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func handleCheckMentions(activity: NSUserActivity) {
|
static func handleCheckMentions(activity: NSUserActivity) {
|
||||||
let mainViewController = getMainViewController()
|
let tabBarController = getMainTabBarController()
|
||||||
mainViewController.select(tab: .notifications)
|
tabBarController.select(tab: .notifications)
|
||||||
if let navController = mainViewController.getTabController(tab: .notifications) as? UINavigationController,
|
if let navController = tabBarController.getTabController(tab: .notifications) as? UINavigationController,
|
||||||
let notificationsPageController = navController.viewControllers.first as? NotificationsPageViewController {
|
let notificationsPageController = navController.viewControllers.first as? NotificationsPageViewController {
|
||||||
navController.popToRootViewController(animated: false)
|
navController.popToRootViewController(animated: false)
|
||||||
notificationsPageController.loadViewIfNeeded()
|
notificationsPageController.loadViewIfNeeded()
|
||||||
|
@ -133,12 +133,9 @@ class UserActivityManager {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let mainViewController = getMainViewController()
|
let tabBarController = getMainTabBarController()
|
||||||
mainViewController.select(tab: .timelines)
|
tabBarController.select(tab: .timelines)
|
||||||
guard let navigationController = mainViewController.getTabController(tab: .timelines) as? UINavigationController else {
|
let navigationController = tabBarController.viewControllers![0] as! UINavigationController
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch timeline {
|
switch timeline {
|
||||||
case .home, .public(true), .public(false):
|
case .home, .public(true), .public(false):
|
||||||
navigationController.popToRootViewController(animated: false)
|
navigationController.popToRootViewController(animated: false)
|
||||||
|
@ -173,9 +170,9 @@ class UserActivityManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func handleSearch(activity: NSUserActivity) {
|
static func handleSearch(activity: NSUserActivity) {
|
||||||
let mainViewController = getMainViewController()
|
let tabBarController = getMainTabBarController()
|
||||||
mainViewController.select(tab: .explore)
|
tabBarController.select(tab: .explore)
|
||||||
if let navigationController = mainViewController.getTabController(tab: .explore) as? UINavigationController,
|
if let navigationController = tabBarController.getTabController(tab: .explore) as? UINavigationController,
|
||||||
let exploreController = navigationController.viewControllers.first as? ExploreViewController {
|
let exploreController = navigationController.viewControllers.first as? ExploreViewController {
|
||||||
navigationController.popToRootViewController(animated: false)
|
navigationController.popToRootViewController(animated: false)
|
||||||
exploreController.searchController.isActive = true
|
exploreController.searchController.isActive = true
|
||||||
|
@ -192,9 +189,9 @@ class UserActivityManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func handleBookmarks(activity: NSUserActivity) {
|
static func handleBookmarks(activity: NSUserActivity) {
|
||||||
let mainViewController = getMainViewController()
|
let tabBarController = getMainTabBarController()
|
||||||
mainViewController.select(tab: .explore)
|
tabBarController.select(tab: .explore)
|
||||||
if let navigationController = mainViewController.getTabController(tab: .explore) as? UINavigationController {
|
if let navigationController = tabBarController.getTabController(tab: .explore) as? UINavigationController {
|
||||||
navigationController.popToRootViewController(animated: false)
|
navigationController.popToRootViewController(animated: false)
|
||||||
navigationController.pushViewController(BookmarksTableViewController(mastodonController: mastodonController), animated: false)
|
navigationController.pushViewController(BookmarksTableViewController(mastodonController: mastodonController), animated: false)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue