diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index 88f0ac440..5e5425728 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -157,7 +157,7 @@ private extension TimelineViewController { func menu(for articles: [Article]) -> NSMenu? { let menu = NSMenu(title: "") - if articles.anyArticleIsUnread() { + if articles.anyArticleIsUnreadAndCanMarkRead() { menu.addItem(markReadMenuItem(articles)) } if articles.anyArticleIsReadAndCanMarkUnread() { @@ -169,10 +169,10 @@ private extension TimelineViewController { if articles.anyArticleIsStarred() { menu.addItem(markUnstarredMenuItem(articles)) } - if let first = articles.first, self.articles.articlesAbove(article: first).canMarkAllAsRead() { + if let first = articles.first, self.articles.articlesAbove(article: first).canMarkAllAsRead(exemptArticles: directlyMarkedAsUnreadArticles) { menu.addItem(markAboveReadMenuItem(articles)) } - if let last = articles.last, self.articles.articlesBelow(article: last).canMarkAllAsRead() { + if let last = articles.last, self.articles.articlesBelow(article: last).canMarkAllAsRead(exemptArticles: directlyMarkedAsUnreadArticles) { menu.addItem(markBelowReadMenuItem(articles)) } diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index c15a59a7b..30ce24961 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -248,7 +248,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr } func canMarkAllAsRead() -> Bool { - return articles.canMarkAllAsRead() + return articles.canMarkAllAsRead(exemptArticles: directlyMarkedAsUnreadArticles) } func canMarkSelectedArticlesAsRead() -> Bool { @@ -474,7 +474,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr func markReadCommandStatus() -> MarkCommandValidationStatus { let articles = selectedArticles - if articles.anyArticleIsUnread() { + if articles.anyArticleIsUnreadAndCanMarkRead() { return .canMark } @@ -499,12 +499,12 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr func canMarkAboveArticlesAsRead() -> Bool { guard let first = selectedArticles.first else { return false } - return articles.articlesAbove(article: first).canMarkAllAsRead() + return articles.articlesAbove(article: first).canMarkAllAsRead(exemptArticles: directlyMarkedAsUnreadArticles) } func canMarkBelowArticlesAsRead() -> Bool { guard let last = selectedArticles.last else { return false } - return articles.articlesBelow(article: last).canMarkAllAsRead() + return articles.articlesBelow(article: last).canMarkAllAsRead(exemptArticles: directlyMarkedAsUnreadArticles) } func markOlderArticlesRead(_ selectedArticles: [Article]) { diff --git a/Shared/Timeline/ArticleArray.swift b/Shared/Timeline/ArticleArray.swift index 2f8d69e06..7f99e8e6e 100644 --- a/Shared/Timeline/ArticleArray.swift +++ b/Shared/Timeline/ArticleArray.swift @@ -54,8 +54,8 @@ extension Array where Element == Article { return ArticleSorter.sortedByDate(articles: self, sortDirection: sortDirection, groupByFeed: groupByFeed) } - func canMarkAllAsRead() -> Bool { - return anyArticleIsUnread() + func canMarkAllAsRead(exemptArticles: Set
= .init()) -> Bool { + return anyArticleIsUnreadAndCanMarkRead(exemptArticles: exemptArticles) } func anyArticlePassesTest(_ test: ((Article) -> Bool)) -> Bool { @@ -71,8 +71,8 @@ extension Array where Element == Article { return anyArticlePassesTest { $0.status.read && $0.isAvailableToMarkUnread } } - func anyArticleIsUnread() -> Bool { - return anyArticlePassesTest { !$0.status.read } + func anyArticleIsUnreadAndCanMarkRead(exemptArticles: Set
= .init()) -> Bool { + return anyArticlePassesTest { !(exemptArticles.contains($0) || $0.status.read) } } func anyArticleIsStarred() -> Bool { diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index 0297387b4..15f974941 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -678,7 +678,7 @@ private extension MasterTimelineViewController { func updateToolbar() { guard firstUnreadButton != nil else { return } - markAllAsReadButton.isEnabled = coordinator.isTimelineUnreadAvailable + markAllAsReadButton.isEnabled = coordinator.canMarkAllAsRead() firstUnreadButton.isEnabled = coordinator.isTimelineUnreadAvailable if coordinator.isRootSplitCollapsed { @@ -875,7 +875,7 @@ private extension MasterTimelineViewController { } let articles = Array(fetchedArticles) - guard articles.canMarkAllAsRead(), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else { + guard coordinator.canMarkAllAsRead(articles), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else { return nil } @@ -898,7 +898,7 @@ private extension MasterTimelineViewController { } let articles = Array(fetchedArticles) - guard articles.canMarkAllAsRead(), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else { + guard coordinator.canMarkAllAsRead(articles), let contentView = self.tableView.cellForRow(at: indexPath)?.contentView else { return nil } diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index f680568ff..c46ee94d5 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -1042,10 +1042,18 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, Logging { completion?() } } + + func canMarkAllAsRead() -> Bool { + return articles.canMarkAllAsRead(exemptArticles: directlyMarkedAsUnreadArticles) + } + + func canMarkAllAsRead(_ articles: [Article]) -> Bool { + return articles.canMarkAllAsRead(exemptArticles: directlyMarkedAsUnreadArticles) + } func canMarkAboveAsRead(for article: Article) -> Bool { let articlesAboveArray = articles.articlesAbove(article: article) - return articlesAboveArray.canMarkAllAsRead() + return articlesAboveArray.canMarkAllAsRead(exemptArticles: directlyMarkedAsUnreadArticles) } func markAboveAsRead() { @@ -1063,7 +1071,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, Logging { func canMarkBelowAsRead(for article: Article) -> Bool { let articleBelowArray = articles.articlesBelow(article: article) - return articleBelowArray.canMarkAllAsRead() + return articleBelowArray.canMarkAllAsRead(exemptArticles: directlyMarkedAsUnreadArticles) } func markBelowAsRead() {