Convert the timeline to use diffable datasources
This commit is contained in:
parent
3baca1d7c0
commit
07ca61f7cf
|
@ -124,6 +124,7 @@
|
||||||
51C452B82265178500C03939 /* styleSheet.css in Resources */ = {isa = PBXBuildFile; fileRef = 51C452B72265178500C03939 /* styleSheet.css */; };
|
51C452B82265178500C03939 /* styleSheet.css in Resources */ = {isa = PBXBuildFile; fileRef = 51C452B72265178500C03939 /* styleSheet.css */; };
|
||||||
51CC9B3E231720B2000E842F /* MasterFeedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51CC9B3D231720B2000E842F /* MasterFeedDataSource.swift */; };
|
51CC9B3E231720B2000E842F /* MasterFeedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51CC9B3D231720B2000E842F /* MasterFeedDataSource.swift */; };
|
||||||
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
|
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
|
||||||
|
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */; };
|
||||||
51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D87EE02311D34700E63F03 /* ActivityType.swift */; };
|
51D87EE12311D34700E63F03 /* ActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D87EE02311D34700E63F03 /* ActivityType.swift */; };
|
||||||
51E3EB33229AB02C00645299 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB32229AB02C00645299 /* ErrorHandler.swift */; };
|
51E3EB33229AB02C00645299 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB32229AB02C00645299 /* ErrorHandler.swift */; };
|
||||||
51E3EB3D229AB08300645299 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB3C229AB08300645299 /* ErrorHandler.swift */; };
|
51E3EB3D229AB08300645299 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E3EB3C229AB08300645299 /* ErrorHandler.swift */; };
|
||||||
|
@ -735,6 +736,7 @@
|
||||||
51C452B32265141B00C03939 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
|
51C452B32265141B00C03939 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
51C452B72265178500C03939 /* styleSheet.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = styleSheet.css; sourceTree = "<group>"; };
|
51C452B72265178500C03939 /* styleSheet.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = styleSheet.css; sourceTree = "<group>"; };
|
||||||
51CC9B3D231720B2000E842F /* MasterFeedDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterFeedDataSource.swift; sourceTree = "<group>"; };
|
51CC9B3D231720B2000E842F /* MasterFeedDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterFeedDataSource.swift; sourceTree = "<group>"; };
|
||||||
|
51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineDataSource.swift; sourceTree = "<group>"; };
|
||||||
51D87EE02311D34700E63F03 /* ActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityType.swift; sourceTree = "<group>"; };
|
51D87EE02311D34700E63F03 /* ActivityType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityType.swift; sourceTree = "<group>"; };
|
||||||
51E3EB32229AB02C00645299 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
|
51E3EB32229AB02C00645299 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
|
||||||
51E3EB3C229AB08300645299 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
|
51E3EB3C229AB08300645299 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1157,6 +1159,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
51C4526E2265091600C03939 /* MasterTimelineViewController.swift */,
|
51C4526E2265091600C03939 /* MasterTimelineViewController.swift */,
|
||||||
|
51D6A5BB23199C85001C27D8 /* MasterTimelineDataSource.swift */,
|
||||||
51C4526F2265091600C03939 /* Cell */,
|
51C4526F2265091600C03939 /* Cell */,
|
||||||
);
|
);
|
||||||
path = MasterTimeline;
|
path = MasterTimeline;
|
||||||
|
@ -2479,6 +2482,7 @@
|
||||||
51C452782265091600C03939 /* MasterTimelineCellData.swift in Sources */,
|
51C452782265091600C03939 /* MasterTimelineCellData.swift in Sources */,
|
||||||
51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
|
51C45259226508D300C03939 /* AppDefaults.swift in Sources */,
|
||||||
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,
|
51C45293226509C800C03939 /* StarredFeedDelegate.swift in Sources */,
|
||||||
|
51D6A5BC23199C85001C27D8 /* MasterTimelineDataSource.swift in Sources */,
|
||||||
51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */,
|
51934CCB230F599B006127BE /* ThemedNavigationController.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -468,11 +468,17 @@ class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||||
animatingChanges = false
|
animatingChanges = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func indexForArticleID(_ articleID: String?) -> Int? {
|
||||||
|
guard let articleID = articleID else { return nil }
|
||||||
|
updateArticleRowMapIfNeeded()
|
||||||
|
return articleRowMap[articleID]
|
||||||
|
}
|
||||||
|
|
||||||
func indexesForArticleIDs(_ articleIDs: Set<String>) -> IndexSet {
|
func indexesForArticleIDs(_ articleIDs: Set<String>) -> IndexSet {
|
||||||
var indexes = IndexSet()
|
var indexes = IndexSet()
|
||||||
|
|
||||||
articleIDs.forEach { (articleID) in
|
articleIDs.forEach { (articleID) in
|
||||||
guard let oneIndex = row(for: articleID) else {
|
guard let oneIndex = indexForArticleID(articleID) else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if oneIndex != NSNotFound {
|
if oneIndex != NSNotFound {
|
||||||
|
@ -482,7 +488,7 @@ class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||||
|
|
||||||
return indexes
|
return indexes
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectFeed(_ indexPath: IndexPath) {
|
func selectFeed(_ indexPath: IndexPath) {
|
||||||
if navControllerForTimeline().viewControllers.filter({ $0 is MasterTimelineViewController }).count > 0 {
|
if navControllerForTimeline().viewControllers.filter({ $0 is MasterTimelineViewController }).count > 0 {
|
||||||
currentMasterIndexPath = indexPath
|
currentMasterIndexPath = indexPath
|
||||||
|
@ -885,17 +891,12 @@ private extension AppCoordinator {
|
||||||
if articles != sortedArticles {
|
if articles != sortedArticles {
|
||||||
let article = currentArticle
|
let article = currentArticle
|
||||||
articles = sortedArticles
|
articles = sortedArticles
|
||||||
if let articleID = article?.articleID, let index = row(for: articleID) {
|
if let articleID = article?.articleID, let index = indexForArticleID(articleID) {
|
||||||
currentArticleIndexPath = IndexPath(row: index, section: 0)
|
currentArticleIndexPath = IndexPath(row: index, section: 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func row(for articleID: String) -> Int? {
|
|
||||||
updateArticleRowMapIfNeeded()
|
|
||||||
return articleRowMap[articleID]
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateArticleRowMap() {
|
func updateArticleRowMap() {
|
||||||
var rowMap = [String: Int]()
|
var rowMap = [String: Int]()
|
||||||
var index = 0
|
var index = 0
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// MasterTimelineDataSource.swift
|
||||||
|
// NetNewsWire-iOS
|
||||||
|
//
|
||||||
|
// Created by Maurice Parker on 8/30/19.
|
||||||
|
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class MasterTimelineDataSource<SectionIdentifierType, ItemIdentifierType>: UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType> where SectionIdentifierType : Hashable, ItemIdentifierType : Hashable {
|
||||||
|
|
||||||
|
private var coordinator: AppCoordinator!
|
||||||
|
|
||||||
|
init(coordinator: AppCoordinator, tableView: UITableView, cellProvider: @escaping UITableViewDiffableDataSource<SectionIdentifierType, ItemIdentifierType>.CellProvider) {
|
||||||
|
super.init(tableView: tableView, cellProvider: cellProvider)
|
||||||
|
self.coordinator = coordinator
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
|
@IBOutlet weak var markAllAsReadButton: UIBarButtonItem!
|
||||||
@IBOutlet weak var firstUnreadButton: UIBarButtonItem!
|
@IBOutlet weak var firstUnreadButton: UIBarButtonItem!
|
||||||
|
|
||||||
|
private lazy var dataSource = makeDataSource()
|
||||||
weak var coordinator: AppCoordinator!
|
weak var coordinator: AppCoordinator!
|
||||||
var undoableCommands = [UndoableCommand]()
|
var undoableCommands = [UndoableCommand]()
|
||||||
|
|
||||||
|
@ -28,7 +29,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
tableView.dataSource = dataSource
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(feedIconDidBecomeAvailable(_:)), name: .FeedIconDidBecomeAvailable, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(feedIconDidBecomeAvailable(_:)), name: .FeedIconDidBecomeAvailable, object: nil)
|
||||||
|
@ -44,6 +46,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
numberOfTextLines = AppDefaults.timelineNumberOfLines
|
numberOfTextLines = AppDefaults.timelineNumberOfLines
|
||||||
resetEstimatedRowHeight()
|
resetEstimatedRowHeight()
|
||||||
|
|
||||||
|
applyChanges(animate: false)
|
||||||
resetUI()
|
resetUI()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -71,8 +74,10 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
appDelegate.authorAvatarDownloader.resetCache()
|
appDelegate.authorAvatarDownloader.resetCache()
|
||||||
appDelegate.feedIconDownloader.resetCache()
|
appDelegate.feedIconDownloader.resetCache()
|
||||||
appDelegate.faviconDownloader.resetCache()
|
appDelegate.faviconDownloader.resetCache()
|
||||||
performBlockAndRestoreSelection {
|
|
||||||
tableView.reloadData()
|
// traitCollectionDidChange will get called on a background thread
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self.reloadAllVisibleCells()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,8 +120,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
}
|
}
|
||||||
|
|
||||||
func reloadArticles() {
|
func reloadArticles() {
|
||||||
performBlockAndRestoreSelection {
|
applyChanges(animate: true) { [weak self] in
|
||||||
tableView.reloadData()
|
self?.updateArticleSelection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,14 +136,6 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
|
|
||||||
// MARK: - Table view
|
// MARK: - Table view
|
||||||
|
|
||||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
||||||
return coordinator.articles.count
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
|
||||||
|
|
||||||
let article = coordinator.articles[indexPath.row]
|
let article = coordinator.articles[indexPath.row]
|
||||||
|
@ -251,13 +248,6 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
||||||
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MasterTimelineTableViewCell
|
|
||||||
let article = coordinator.articles[indexPath.row]
|
|
||||||
configureTimelineCell(cell, article: article)
|
|
||||||
return cell
|
|
||||||
}
|
|
||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
coordinator.selectArticle(indexPath)
|
coordinator.selectArticle(indexPath)
|
||||||
}
|
}
|
||||||
|
@ -279,15 +269,12 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
guard let feed = note.userInfo?[UserInfoKey.feed] as? Feed else {
|
guard let feed = note.userInfo?[UserInfoKey.feed] as? Feed else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
tableView.indexPathsForVisibleRows?.forEach { indexPath in
|
||||||
performBlockAndRestoreSelection {
|
guard let article = coordinator.articles.articleAtRow(indexPath.row) else {
|
||||||
tableView.indexPathsForVisibleRows?.forEach { indexPath in
|
return
|
||||||
guard let article = coordinator.articles.articleAtRow(indexPath.row) else {
|
}
|
||||||
return
|
if article.feed == feed, let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell, let image = avatarFor(article) {
|
||||||
}
|
cell.setAvatarImage(image)
|
||||||
if article.feed == feed, let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell, let image = avatarFor(article) {
|
|
||||||
cell.setAvatarImage(image)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -296,16 +283,13 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
guard coordinator.showAvatars, let avatarURL = note.userInfo?[UserInfoKey.url] as? String else {
|
guard coordinator.showAvatars, let avatarURL = note.userInfo?[UserInfoKey.url] as? String else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
tableView.indexPathsForVisibleRows?.forEach { indexPath in
|
||||||
performBlockAndRestoreSelection {
|
guard let article = coordinator.articles.articleAtRow(indexPath.row), let authors = article.authors, !authors.isEmpty else {
|
||||||
tableView.indexPathsForVisibleRows?.forEach { indexPath in
|
return
|
||||||
guard let article = coordinator.articles.articleAtRow(indexPath.row), let authors = article.authors, !authors.isEmpty else {
|
}
|
||||||
return
|
for author in authors {
|
||||||
}
|
if author.avatarURL == avatarURL, let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell, let image = avatarFor(article) {
|
||||||
for author in authors {
|
cell.setAvatarImage(image)
|
||||||
if author.avatarURL == avatarURL, let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell, let image = avatarFor(article) {
|
|
||||||
cell.setAvatarImage(image)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,18 +299,13 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
guard coordinator.showAvatars, let faviconURL = note.userInfo?["faviconURL"] as? String else {
|
guard coordinator.showAvatars, let faviconURL = note.userInfo?["faviconURL"] as? String else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
tableView.indexPathsForVisibleRows?.forEach { indexPath in
|
||||||
performBlockAndRestoreSelection {
|
guard let article = coordinator.articles.articleAtRow(indexPath.row), let articleFaviconURL = article.feed?.faviconURL else {
|
||||||
tableView.indexPathsForVisibleRows?.forEach { indexPath in
|
return
|
||||||
|
}
|
||||||
guard let article = coordinator.articles.articleAtRow(indexPath.row), let articleFaviconURL = article.feed?.faviconURL else {
|
if faviconURL == articleFaviconURL, let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell, let image = avatarFor(article) {
|
||||||
return
|
cell.setAvatarImage(image)
|
||||||
}
|
return
|
||||||
if faviconURL == articleFaviconURL, let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell, let image = avatarFor(article) {
|
|
||||||
cell.setAvatarImage(image)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,12 +314,12 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
if numberOfTextLines != AppDefaults.timelineNumberOfLines {
|
if numberOfTextLines != AppDefaults.timelineNumberOfLines {
|
||||||
numberOfTextLines = AppDefaults.timelineNumberOfLines
|
numberOfTextLines = AppDefaults.timelineNumberOfLines
|
||||||
resetEstimatedRowHeight()
|
resetEstimatedRowHeight()
|
||||||
tableView.reloadData()
|
reloadAllVisibleCells()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func contentSizeCategoryDidChange(_ note: Notification) {
|
@objc func contentSizeCategoryDidChange(_ note: Notification) {
|
||||||
tableView.reloadData()
|
reloadAllVisibleCells()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func progressDidChange(_ note: Notification) {
|
@objc func progressDidChange(_ note: Notification) {
|
||||||
|
@ -349,12 +328,9 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
|
|
||||||
// MARK: Reloading
|
// MARK: Reloading
|
||||||
|
|
||||||
@objc func reloadAllVisibleCells() {
|
private func reloadAllVisibleCells() {
|
||||||
tableView.beginUpdates()
|
let visibleArticles = tableView.indexPathsForVisibleRows!.map { return coordinator.articles[$0.row] }
|
||||||
performBlockAndRestoreSelection {
|
reloadCells(visibleArticles)
|
||||||
tableView.reloadRows(at: tableView.indexPathsForVisibleRows!, with: .none)
|
|
||||||
}
|
|
||||||
tableView.endUpdates()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func reloadVisibleCells(for articles: [Article]) {
|
private func reloadVisibleCells(for articles: [Article]) {
|
||||||
|
@ -374,13 +350,22 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
|
||||||
}
|
}
|
||||||
|
|
||||||
private func reloadVisibleCells(for indexes: IndexSet) {
|
private func reloadVisibleCells(for indexes: IndexSet) {
|
||||||
performBlockAndRestoreSelection {
|
let reloadArticles: [Article] = tableView.indexPathsForVisibleRows!.compactMap { indexPath in
|
||||||
tableView.indexPathsForVisibleRows?.forEach { indexPath in
|
if indexes.contains(indexPath.row) {
|
||||||
if indexes.contains(indexPath.row) {
|
return coordinator.articles[indexPath.row]
|
||||||
tableView.reloadRows(at: [indexPath], with: .none)
|
} else {
|
||||||
}
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
reloadCells(reloadArticles)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func reloadCells(_ articles: [Article]) {
|
||||||
|
var snapshot = dataSource.snapshot()
|
||||||
|
snapshot.reloadItems(articles)
|
||||||
|
dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
|
||||||
|
self?.restoreSelectionIfNecessary()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Cell Configuring
|
// MARK: Cell Configuring
|
||||||
|
@ -444,8 +429,27 @@ private extension MasterTimelineViewController {
|
||||||
navigationController?.updateAccountRefreshProgressIndicator()
|
navigationController?.updateAccountRefreshProgressIndicator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyChanges(animate: Bool, completion: (() -> Void)? = nil) {
|
||||||
|
var snapshot = NSDiffableDataSourceSnapshot<Int, Article>()
|
||||||
|
snapshot.appendSections([0])
|
||||||
|
snapshot.appendItems(coordinator.articles, toSection: 0)
|
||||||
|
|
||||||
|
dataSource.apply(snapshot, animatingDifferences: animate) { [weak self] in
|
||||||
|
self?.restoreSelectionIfNecessary()
|
||||||
|
completion?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func configureTimelineCell(_ cell: MasterTimelineTableViewCell, article: Article) {
|
func makeDataSource() -> UITableViewDiffableDataSource<Int, Article> {
|
||||||
|
return MasterTimelineDataSource(coordinator: coordinator, tableView: tableView, cellProvider: { [weak self] tableView, indexPath, article in
|
||||||
|
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! MasterTimelineTableViewCell
|
||||||
|
self?.configure(cell, article: article)
|
||||||
|
return cell
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(_ cell: MasterTimelineTableViewCell, article: Article) {
|
||||||
|
|
||||||
let avatar = avatarFor(article)
|
let avatar = avatarFor(article)
|
||||||
let featuredImage = featuredImageFor(article)
|
let featuredImage = featuredImageFor(article)
|
||||||
|
@ -470,19 +474,11 @@ private extension MasterTimelineViewController {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func queueReloadVisableCells() {
|
func restoreSelectionIfNecessary() {
|
||||||
CoalescingQueue.standard.add(self, #selector(reloadAllVisibleCells))
|
|
||||||
}
|
|
||||||
|
|
||||||
func performBlockAndRestoreSelection(_ block: (() -> Void)) {
|
|
||||||
guard traitCollection.userInterfaceIdiom == .pad else {
|
guard traitCollection.userInterfaceIdiom == .pad else {
|
||||||
block()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if let articleID = coordinator.currentArticle?.articleID, let index = coordinator.indexesForArticleIDs(Set([articleID])).first {
|
||||||
let articleID = coordinator.currentArticle?.articleID
|
|
||||||
block()
|
|
||||||
if let articleID = articleID, let index = coordinator.indexesForArticleIDs(Set([articleID])).first {
|
|
||||||
let indexPath = IndexPath(row: index, section: 0)
|
let indexPath = IndexPath(row: index, section: 0)
|
||||||
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
|
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue