Remove the usage of IndexPaths for the interface between the coordinator and the timeline

This commit is contained in:
Maurice Parker 2019-09-11 09:11:33 -05:00
parent d8513385e1
commit 8545252265
2 changed files with 92 additions and 154 deletions

View File

@ -146,7 +146,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
// MARK: API // MARK: API
func restoreSelectionIfNecessary() { func restoreSelectionIfNecessary() {
if let indexPath = coordinator.currentArticleIndexPath { if let article = coordinator.currentArticle, let indexPath = dataSource.indexPath(for: article) {
tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: false, deselect: coordinator.isRootSplitCollapsed) tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: false, deselect: coordinator.isRootSplitCollapsed)
} }
} }
@ -160,7 +160,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
} }
func updateArticleSelection(animate: Bool) { func updateArticleSelection(animate: Bool) {
if let indexPath = coordinator.currentArticleIndexPath { if let article = coordinator.currentArticle, let indexPath = dataSource.indexPath(for: article) {
if tableView.indexPathForSelectedRow != indexPath { if tableView.indexPathForSelectedRow != indexPath {
tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: true, deselect: coordinator.isRootSplitCollapsed) tableView.selectRowAndScrollIfNotVisible(at: indexPath, animated: true, deselect: coordinator.isRootSplitCollapsed)
} }
@ -185,7 +185,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let article = dataSource.itemIdentifier(for: indexPath)! guard let article = dataSource.itemIdentifier(for: indexPath) else { return nil }
// Set up the read action // Set up the read action
let readTitle = article.status.read ? let readTitle = article.status.read ?
@ -193,7 +193,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
NSLocalizedString("Read", comment: "Read") NSLocalizedString("Read", comment: "Read")
let readAction = UIContextualAction(style: .normal, title: readTitle) { [weak self] (action, view, completionHandler) in let readAction = UIContextualAction(style: .normal, title: readTitle) { [weak self] (action, view, completionHandler) in
self?.coordinator.toggleRead(for: indexPath) self?.coordinator.toggleRead(article)
completionHandler(true) completionHandler(true)
} }
@ -206,7 +206,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
NSLocalizedString("Star", comment: "Star") NSLocalizedString("Star", comment: "Star")
let starAction = UIContextualAction(style: .normal, title: starTitle) { [weak self] (action, view, completionHandler) in let starAction = UIContextualAction(style: .normal, title: starTitle) { [weak self] (action, view, completionHandler) in
self?.coordinator.toggleStar(for: indexPath) self?.coordinator.toggleStar(article)
completionHandler(true) completionHandler(true)
} }
@ -225,21 +225,21 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
popoverController.sourceRect = CGRect(x: view.frame.size.width/2, y: view.frame.size.height/2, width: 1, height: 1) popoverController.sourceRect = CGRect(x: view.frame.size.width/2, y: view.frame.size.height/2, width: 1, height: 1)
} }
alert.addAction(self.markOlderAsReadAlertAction(indexPath: indexPath, completionHandler: completionHandler)) alert.addAction(self.markOlderAsReadAlertAction(article, completionHandler: completionHandler))
if let action = self.discloseFeedAlertAction(indexPath: indexPath, completionHandler: completionHandler) { if let action = self.discloseFeedAlertAction(article, completionHandler: completionHandler) {
alert.addAction(action) alert.addAction(action)
} }
if let action = self.markAllInFeedAsReadAlertAction(indexPath: indexPath, completionHandler: completionHandler) { if let action = self.markAllInFeedAsReadAlertAction(article, completionHandler: completionHandler) {
alert.addAction(action) alert.addAction(action)
} }
if let action = self.openInBrowserAlertAction(indexPath: indexPath, completionHandler: completionHandler) { if let action = self.openInBrowserAlertAction(article, completionHandler: completionHandler) {
alert.addAction(action) alert.addAction(action)
} }
if let action = self.shareAlertAction(indexPath: indexPath, completionHandler: completionHandler) { if let action = self.shareAlertAction(article, indexPath: indexPath, completionHandler: completionHandler) {
alert.addAction(action) alert.addAction(action)
} }
@ -264,28 +264,30 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
guard let article = dataSource.itemIdentifier(for: indexPath) else { return nil }
return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { [weak self] suggestedActions in return UIContextMenuConfiguration(identifier: nil, previewProvider: nil, actionProvider: { [weak self] suggestedActions in
guard let self = self else { return nil } guard let self = self else { return nil }
var actions = [UIAction]() var actions = [UIAction]()
actions.append(self.toggleArticleReadStatusAction(indexPath: indexPath)) actions.append(self.toggleArticleReadStatusAction(article))
actions.append(self.toggleArticleStarStatusAction(indexPath: indexPath)) actions.append(self.toggleArticleStarStatusAction(article))
actions.append(self.markOlderAsReadAction(indexPath: indexPath)) actions.append(self.markOlderAsReadAction(article))
if let action = self.discloseFeedAction(indexPath: indexPath) { if let action = self.discloseFeedAction(article) {
actions.append(action) actions.append(action)
} }
if let action = self.markAllInFeedAsReadAction(indexPath: indexPath) { if let action = self.markAllInFeedAsReadAction(article) {
actions.append(action) actions.append(action)
} }
if let action = self.openInBrowserAction(indexPath: indexPath) { if let action = self.openInBrowserAction(article) {
actions.append(action) actions.append(action)
} }
if let action = self.shareAction(indexPath: indexPath) { if let action = self.shareAction(article, indexPath: indexPath) {
actions.append(action) actions.append(action)
} }
@ -297,7 +299,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
becomeFirstResponder() becomeFirstResponder()
coordinator.selectArticle(indexPath, automated: false) let article = dataSource.itemIdentifier(for: indexPath)
coordinator.selectArticle(article, automated: false)
} }
// MARK: Notifications // MARK: Notifications
@ -315,8 +318,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
let visibleUpdatedArticles = visibleArticles.filter { updatedArticles.contains($0) } let visibleUpdatedArticles = visibleArticles.filter { updatedArticles.contains($0) }
for article in visibleUpdatedArticles { for article in visibleUpdatedArticles {
if let articleIndex = coordinator.indexForArticleID(article.articleID) { if let indexPath = dataSource.indexPath(for: article) {
if let cell = tableView.cellForRow(at: IndexPath(row: articleIndex, section: 0)) as? MasterTimelineTableViewCell { if let cell = tableView.cellForRow(at: indexPath) as? MasterTimelineTableViewCell {
configure(cell, article: article) configure(cell, article: article)
} }
} }
@ -532,8 +535,7 @@ private extension MasterTimelineViewController {
return nil return nil
} }
func toggleArticleReadStatusAction(indexPath: IndexPath) -> UIAction { func toggleArticleReadStatusAction(_ article: Article) -> UIAction {
let article = dataSource.itemIdentifier(for: indexPath)!
let title = article.status.read ? let title = article.status.read ?
NSLocalizedString("Mark as Unread", comment: "Mark as Unread") : NSLocalizedString("Mark as Unread", comment: "Mark as Unread") :
@ -541,14 +543,13 @@ private extension MasterTimelineViewController {
let image = article.status.read ? AppAssets.circleClosedImage : AppAssets.circleOpenImage let image = article.status.read ? AppAssets.circleClosedImage : AppAssets.circleOpenImage
let action = UIAction(title: title, image: image) { [weak self] action in let action = UIAction(title: title, image: image) { [weak self] action in
self?.coordinator.toggleRead(for: indexPath) self?.coordinator.toggleRead(article)
} }
return action return action
} }
func toggleArticleStarStatusAction(indexPath: IndexPath) -> UIAction { func toggleArticleStarStatusAction(_ article: Article) -> UIAction {
let article = dataSource.itemIdentifier(for: indexPath)!
let title = article.status.starred ? let title = article.status.starred ?
NSLocalizedString("Mark as Unstarred", comment: "Mark as Unstarred") : NSLocalizedString("Mark as Unstarred", comment: "Mark as Unstarred") :
@ -556,34 +557,33 @@ private extension MasterTimelineViewController {
let image = article.status.starred ? AppAssets.starOpenImage : AppAssets.starClosedImage let image = article.status.starred ? AppAssets.starOpenImage : AppAssets.starClosedImage
let action = UIAction(title: title, image: image) { [weak self] action in let action = UIAction(title: title, image: image) { [weak self] action in
self?.coordinator.toggleStar(for: indexPath) self?.coordinator.toggleStar(article)
} }
return action return action
} }
func markOlderAsReadAction(indexPath: IndexPath) -> UIAction { func markOlderAsReadAction(_ article: Article) -> UIAction {
let title = NSLocalizedString("Mark Older as Read", comment: "Mark Older as Read") let title = NSLocalizedString("Mark Older as Read", comment: "Mark Older as Read")
let image = coordinator.sortDirection == .orderedDescending ? AppAssets.markOlderAsReadDownImage : AppAssets.markOlderAsReadUpImage let image = coordinator.sortDirection == .orderedDescending ? AppAssets.markOlderAsReadDownImage : AppAssets.markOlderAsReadUpImage
let action = UIAction(title: title, image: image) { [weak self] action in let action = UIAction(title: title, image: image) { [weak self] action in
self?.coordinator.markAsReadOlderArticlesInTimeline(indexPath) self?.coordinator.markAsReadOlderArticlesInTimeline(article)
} }
return action return action
} }
func markOlderAsReadAlertAction(indexPath: IndexPath, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction { func markOlderAsReadAlertAction(_ article: Article, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction {
let title = NSLocalizedString("Mark Older as Read", comment: "Mark Older as Read") let title = NSLocalizedString("Mark Older as Read", comment: "Mark Older as Read")
let action = UIAlertAction(title: title, style: .default) { [weak self] action in let action = UIAlertAction(title: title, style: .default) { [weak self] action in
self?.coordinator.markAsReadOlderArticlesInTimeline(indexPath) self?.coordinator.markAsReadOlderArticlesInTimeline(article)
completionHandler(true) completionHandler(true)
} }
return action return action
} }
func discloseFeedAction(indexPath: IndexPath) -> UIAction? { func discloseFeedAction(_ article: Article) -> UIAction? {
guard let feed = dataSource.itemIdentifier(for: indexPath)?.feed else { guard let feed = article.feed else { return nil }
return nil
}
let title = NSLocalizedString("Select Feed", comment: "Select Feed") let title = NSLocalizedString("Select Feed", comment: "Select Feed")
let action = UIAction(title: title, image: AppAssets.openInSidebarImage) { [weak self] action in let action = UIAction(title: title, image: AppAssets.openInSidebarImage) { [weak self] action in
self?.coordinator.discloseFeed(feed) self?.coordinator.discloseFeed(feed)
@ -591,10 +591,9 @@ private extension MasterTimelineViewController {
return action return action
} }
func discloseFeedAlertAction(indexPath: IndexPath, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? { func discloseFeedAlertAction(_ article: Article, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? {
guard let feed = dataSource.itemIdentifier(for: indexPath)?.feed else { guard let feed = article.feed else { return nil }
return nil
}
let title = NSLocalizedString("Select Feed", comment: "Select Feed") let title = NSLocalizedString("Select Feed", comment: "Select Feed")
let action = UIAlertAction(title: title, style: .default) { [weak self] action in let action = UIAlertAction(title: title, style: .default) { [weak self] action in
self?.coordinator.discloseFeed(feed) self?.coordinator.discloseFeed(feed)
@ -603,11 +602,9 @@ private extension MasterTimelineViewController {
return action return action
} }
func markAllInFeedAsReadAction(indexPath: IndexPath) -> UIAction? { func markAllInFeedAsReadAction(_ article: Article) -> UIAction? {
guard let feed = dataSource.itemIdentifier(for: indexPath)?.feed else { guard let feed = article.feed else { return nil }
return nil
}
let articles = Array(feed.fetchArticles()) let articles = Array(feed.fetchArticles())
guard articles.canMarkAllAsRead() else { guard articles.canMarkAllAsRead() else {
return nil return nil
@ -622,11 +619,9 @@ private extension MasterTimelineViewController {
return action return action
} }
func markAllInFeedAsReadAlertAction(indexPath: IndexPath, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? { func markAllInFeedAsReadAlertAction(_ article: Article, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? {
guard let feed = dataSource.itemIdentifier(for: indexPath)?.feed else { guard let feed = article.feed else { return nil }
return nil
}
let articles = Array(feed.fetchArticles()) let articles = Array(feed.fetchArticles())
guard articles.canMarkAllAsRead() else { guard articles.canMarkAllAsRead() else {
return nil return nil
@ -642,24 +637,24 @@ private extension MasterTimelineViewController {
return action return action
} }
func openInBrowserAction(indexPath: IndexPath) -> UIAction? { func openInBrowserAction(_ article: Article) -> UIAction? {
guard let preferredLink = dataSource.itemIdentifier(for: indexPath)?.preferredLink, let _ = URL(string: preferredLink) else { guard let preferredLink = article.preferredLink, let _ = URL(string: preferredLink) else {
return nil return nil
} }
let title = NSLocalizedString("Open in Browser", comment: "Open in Browser") let title = NSLocalizedString("Open in Browser", comment: "Open in Browser")
let action = UIAction(title: title, image: AppAssets.safariImage) { [weak self] action in let action = UIAction(title: title, image: AppAssets.safariImage) { [weak self] action in
self?.coordinator.showBrowserForArticle(indexPath) self?.coordinator.showBrowserForArticle(article)
} }
return action return action
} }
func openInBrowserAlertAction(indexPath: IndexPath, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? { func openInBrowserAlertAction(_ article: Article, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? {
guard let preferredLink = dataSource.itemIdentifier(for: indexPath)?.preferredLink, let _ = URL(string: preferredLink) else { guard let preferredLink = article.preferredLink, let _ = URL(string: preferredLink) else {
return nil return nil
} }
let title = NSLocalizedString("Open in Browser", comment: "Open in Browser") let title = NSLocalizedString("Open in Browser", comment: "Open in Browser")
let action = UIAlertAction(title: title, style: .default) { [weak self] action in let action = UIAlertAction(title: title, style: .default) { [weak self] action in
self?.coordinator.showBrowserForArticle(indexPath) self?.coordinator.showBrowserForArticle(article)
completionHandler(true) completionHandler(true)
} }
return action return action
@ -677,8 +672,7 @@ private extension MasterTimelineViewController {
present(activityViewController, animated: true) present(activityViewController, animated: true)
} }
func shareAction(indexPath: IndexPath) -> UIAction? { func shareAction(_ article: Article, indexPath: IndexPath) -> UIAction? {
let article = dataSource.itemIdentifier(for: indexPath)!
guard let preferredLink = article.preferredLink, let url = URL(string: preferredLink) else { guard let preferredLink = article.preferredLink, let url = URL(string: preferredLink) else {
return nil return nil
} }
@ -690,8 +684,7 @@ private extension MasterTimelineViewController {
return action return action
} }
func shareAlertAction(indexPath: IndexPath, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? { func shareAlertAction(_ article: Article, indexPath: IndexPath, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction? {
let article = dataSource.itemIdentifier(for: indexPath)!
guard let preferredLink = article.preferredLink, let url = URL(string: preferredLink) else { guard let preferredLink = article.preferredLink, let url = URL(string: preferredLink) else {
return nil return nil
} }

View File

@ -55,7 +55,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
private let fetchAndMergeArticlesQueue = CoalescingQueue(name: "Fetch and Merge Articles", interval: 0.5) private let fetchAndMergeArticlesQueue = CoalescingQueue(name: "Fetch and Merge Articles", interval: 0.5)
private var fetchSerialNumber = 0 private var fetchSerialNumber = 0
private let fetchRequestQueue = FetchRequestQueue() private let fetchRequestQueue = FetchRequestQueue()
private var articleRowMap = [String: Int]() // articleID: rowIndex
private var animatingChanges = false private var animatingChanges = false
private var expandedNodes = [Node]() private var expandedNodes = [Node]()
@ -180,31 +179,31 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
} }
var isPrevArticleAvailable: Bool { var isPrevArticleAvailable: Bool {
guard let indexPath = currentArticleIndexPath else { guard let articleRow = currentArticleRow else {
return false return false
} }
return indexPath.row > 0 return articleRow > 0
} }
var isNextArticleAvailable: Bool { var isNextArticleAvailable: Bool {
guard let indexPath = currentArticleIndexPath else { guard let articleRow = currentArticleRow else {
return false return false
} }
return indexPath.row + 1 < articles.count return articleRow + 1 < articles.count
} }
var prevArticleIndexPath: IndexPath? { var prevArticle: Article? {
guard isPrevArticleAvailable, let indexPath = currentArticleIndexPath else { guard isPrevArticleAvailable, let articleRow = currentArticleRow else {
return nil return nil
} }
return IndexPath(row: indexPath.row - 1, section: indexPath.section) return articles[articleRow]
} }
var nextArticleIndexPath: IndexPath? { var nextArticle: Article? {
guard isNextArticleAvailable, let indexPath = currentArticleIndexPath else { guard isNextArticleAvailable, let articleRow = currentArticleRow else {
return nil return nil
} }
return IndexPath(row: indexPath.row + 1, section: indexPath.section) return articles[articleRow]
} }
var firstUnreadArticleIndexPath: IndexPath? { var firstUnreadArticleIndexPath: IndexPath? {
@ -216,17 +215,13 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
return nil return nil
} }
var currentArticle: Article? {
if let indexPath = currentArticleIndexPath, indexPath.row < articles.count {
return articles[indexPath.row]
}
return nil
}
private(set) var currentArticleIndexPath: IndexPath?
private(set) var articles = ArticleArray() private(set) var articles = ArticleArray()
var currentArticle: Article?
var currentArticleRow: Int? {
guard let article = currentArticle else { return nil }
return articles.firstIndex(of: article)
}
var isTimelineUnreadAvailable: Bool { var isTimelineUnreadAvailable: Bool {
if let unreadProvider = timelineFetcher as? UnreadCountProvider { if let unreadProvider = timelineFetcher as? UnreadCountProvider {
return unreadProvider.unreadCount > 0 return unreadProvider.unreadCount > 0
@ -602,27 +597,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
return indexPathFor(node) return indexPathFor(node)
} }
func indexForArticleID(_ articleID: String?) -> Int? {
guard let articleID = articleID else { return nil }
updateArticleRowMapIfNeeded()
return articleRowMap[articleID]
}
func indexesForArticleIDs(_ articleIDs: Set<String>) -> IndexSet {
var indexes = IndexSet()
articleIDs.forEach { (articleID) in
guard let oneIndex = indexForArticleID(articleID) else {
return
}
if oneIndex != NSNotFound {
indexes.insert(oneIndex)
}
}
return indexes
}
func selectFeed(_ indexPath: IndexPath?, automated: Bool = true) { func selectFeed(_ indexPath: IndexPath?, automated: Bool = true) {
selectArticle(nil) selectArticle(nil)
currentFeedIndexPath = indexPath currentFeedIndexPath = indexPath
@ -673,11 +647,11 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
} }
} }
func selectArticle(_ indexPath: IndexPath?, automated: Bool = true) { func selectArticle(_ article: Article?, automated: Bool = true) {
currentArticleIndexPath = indexPath currentArticle = article
activityManager.reading(currentArticle) activityManager.reading(currentArticle)
if indexPath == nil { if article == nil {
if rootSplitViewController.isCollapsed { if rootSplitViewController.isCollapsed {
if masterNavigationController.children.last is DetailViewController { if masterNavigationController.children.last is DetailViewController {
masterNavigationController.popViewController(animated: !automated) masterNavigationController.popViewController(animated: !automated)
@ -751,14 +725,14 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
} }
func selectPrevArticle() { func selectPrevArticle() {
if let indexPath = prevArticleIndexPath { if let article = prevArticle {
selectArticle(indexPath) selectArticle(article)
} }
} }
func selectNextArticle() { func selectNextArticle() {
if let indexPath = nextArticleIndexPath { if let article = nextArticle {
selectArticle(indexPath) selectArticle(article)
} }
} }
@ -834,12 +808,12 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
} }
func markAsReadOlderArticlesInTimeline() { func markAsReadOlderArticlesInTimeline() {
if let indexPath = currentArticleIndexPath { if let article = currentArticle {
markAsReadOlderArticlesInTimeline(indexPath) markAsReadOlderArticlesInTimeline(article)
} }
} }
func markAsReadOlderArticlesInTimeline(_ indexPath: IndexPath) {
let article = articles[indexPath.row] func markAsReadOlderArticlesInTimeline(_ article: Article) {
let articlesToMark = articles.filter { $0.logicalDatePublished < article.logicalDatePublished } let articlesToMark = articles.filter { $0.logicalDatePublished < article.logicalDatePublished }
if articlesToMark.isEmpty { if articlesToMark.isEmpty {
return return
@ -865,8 +839,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
} }
} }
func toggleRead(for indexPath: IndexPath) { func toggleRead(_ article: Article) {
let article = articles[indexPath.row]
guard let undoManager = undoManager, guard let undoManager = undoManager,
let markReadCommand = MarkStatusCommand(initialArticles: [article], markingRead: !article.status.read, undoManager: undoManager) else { let markReadCommand = MarkStatusCommand(initialArticles: [article], markingRead: !article.status.read, undoManager: undoManager) else {
return return
@ -880,8 +853,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
} }
} }
func toggleStar(for indexPath: IndexPath) { func toggleStar(_ article: Article) {
let article = articles[indexPath.row]
guard let undoManager = undoManager, guard let undoManager = undoManager,
let markReadCommand = MarkStatusCommand(initialArticles: [article], markingStarred: !article.status.starred, undoManager: undoManager) else { let markReadCommand = MarkStatusCommand(initialArticles: [article], markingStarred: !article.status.starred, undoManager: undoManager) else {
return return
@ -940,8 +912,8 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
} }
} }
func showBrowserForArticle(_ indexPath: IndexPath) { func showBrowserForArticle(_ article: Article) {
guard let preferredLink = articles[indexPath.row].preferredLink, let url = URL(string: preferredLink) else { guard let preferredLink = article.preferredLink, let url = URL(string: preferredLink) else {
return return
} }
UIApplication.shared.open(url, options: [:]) UIApplication.shared.open(url, options: [:])
@ -960,8 +932,8 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
} }
func navigateToTimeline() { func navigateToTimeline() {
if currentArticleIndexPath == nil { if currentArticle == nil && articles.count > 0 {
selectArticle(IndexPath(row: 0, section: 0)) selectArticle(articles[0])
} }
masterTimelineViewController?.focus() masterTimelineViewController?.focus()
} }
@ -1124,8 +1096,8 @@ private extension SceneCoordinator {
@discardableResult @discardableResult
func selectPrevUnreadArticleInTimeline() -> Bool { func selectPrevUnreadArticleInTimeline() -> Bool {
let startingRow: Int = { let startingRow: Int = {
if let indexPath = currentArticleIndexPath { if let articleRow = currentArticleRow {
return indexPath.row - 1 return articleRow
} else { } else {
return articles.count - 1 return articles.count - 1
} }
@ -1143,7 +1115,7 @@ private extension SceneCoordinator {
for i in (0...startingRow).reversed() { for i in (0...startingRow).reversed() {
let article = articles[i] let article = articles[i]
if !article.status.read { if !article.status.read {
selectArticle(IndexPath(row: i, section: 0)) selectArticle(article)
return true return true
} }
} }
@ -1231,8 +1203,8 @@ private extension SceneCoordinator {
@discardableResult @discardableResult
func selectNextUnreadArticleInTimeline() -> Bool { func selectNextUnreadArticleInTimeline() -> Bool {
let startingRow: Int = { let startingRow: Int = {
if let indexPath = currentArticleIndexPath { if let articleRow = currentArticleRow {
return indexPath.row + 1 return articleRow + 1
} else { } else {
return 0 return 0
} }
@ -1250,7 +1222,7 @@ private extension SceneCoordinator {
for i in startingRow..<articles.count { for i in startingRow..<articles.count {
let article = articles[i] let article = articles[i]
if !article.status.read { if !article.status.read {
selectArticle(IndexPath(row: i, section: 0)) selectArticle(article)
return true return true
} }
} }
@ -1344,34 +1316,11 @@ private extension SceneCoordinator {
if articles != sortedArticles { if articles != sortedArticles {
let article = currentArticle
articles = sortedArticles articles = sortedArticles
updateShowAvatars() updateShowAvatars()
articleRowMap = [String: Int]()
updateUnreadCount() updateUnreadCount()
masterTimelineViewController?.reloadArticles(animate: animate) masterTimelineViewController?.reloadArticles(animate: animate)
if let articleID = article?.articleID, let index = indexForArticleID(articleID) {
currentArticleIndexPath = IndexPath(row: index, section: 0)
}
}
}
func updateArticleRowMap() {
var rowMap = [String: Int]()
var index = 0
articles.forEach { (article) in
rowMap[article.articleID] = index
index += 1
}
articleRowMap = rowMap
}
func updateArticleRowMapIfNeeded() {
if articleRowMap.isEmpty {
updateArticleRowMap()
} }
} }
@ -1698,12 +1647,8 @@ private extension SceneCoordinator {
discloseFeed(feedNode.representedObject as! Feed) { discloseFeed(feedNode.representedObject as! Feed) {
guard let articleID = activity.userInfo?[ActivityID.articleID.rawValue] as? String else { return } guard let articleID = activity.userInfo?[ActivityID.articleID.rawValue] as? String else { return }
if let article = self.articles.first(where: { $0.articleID == articleID }) {
for (index, article) in self.articles.enumerated() { self.selectArticle(article)
if article.articleID == articleID {
self.selectArticle(IndexPath(row: index, section: 0))
break
}
} }
} }