diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index 40fd9f29b..d0f3dd55c 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -159,7 +159,7 @@ private extension TimelineViewController { if articles.anyArticleIsUnread() { menu.addItem(markReadMenuItem(articles)) } - if articles.anyArticleIsRead() { + if articles.anyArticleIsReadAndCanMarkUnread() { menu.addItem(markUnreadMenuItem(articles)) } if articles.anyArticleIsUnstarred() { diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index 3f3d4e4ec..7bbb7ac25 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -367,7 +367,17 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr } func markReadCommandStatus() -> MarkCommandValidationStatus { - return MarkCommandValidationStatus.statusFor(selectedArticles) { $0.anyArticleIsUnread() } + let articles = selectedArticles + + if articles.anyArticleIsUnread() { + return .canMark + } + + if articles.anyArticleIsReadAndCanMarkUnread() { + return .canUnmark + } + + return .canDoNothing } func markOlderArticlesRead() { diff --git a/Shared/Commands/MarkStatusCommand.swift b/Shared/Commands/MarkStatusCommand.swift index 5d78d664a..2d2cd09ef 100644 --- a/Shared/Commands/MarkStatusCommand.swift +++ b/Shared/Commands/MarkStatusCommand.swift @@ -23,7 +23,7 @@ final class MarkStatusCommand: UndoableCommand { init?(initialArticles: [Article], statusKey: ArticleStatus.Key, flag: Bool, undoManager: UndoManager) { - // Filter out articles that already have the desired status. + // Filter out articles that already have the desired status or can't be marked. let articlesToMark = MarkStatusCommand.filteredArticles(initialArticles, statusKey, flag) if articlesToMark.isEmpty { return nil @@ -88,6 +88,12 @@ private extension MarkStatusCommand { static func filteredArticles(_ articles: [Article], _ statusKey: ArticleStatus.Key, _ flag: Bool) -> [Article] { - return articles.filter{ $0.status.boolStatus(forKey: statusKey) != flag } + return articles.filter{ article in + guard article.status.boolStatus(forKey: statusKey) != flag else { return false } + guard statusKey == .read else { return true } + guard !article.status.read || article.isAvailableToMarkUnread else { return false } + return true + } + } } diff --git a/Shared/Timeline/ArticleArray.swift b/Shared/Timeline/ArticleArray.swift index 36e53940f..4c0a63c89 100644 --- a/Shared/Timeline/ArticleArray.swift +++ b/Shared/Timeline/ArticleArray.swift @@ -14,7 +14,6 @@ typealias ArticleArray = [Article] extension Array where Element == Article { func articleAtRow(_ row: Int) -> Article? { - if row < 0 || row == NSNotFound || row > count - 1 { return nil } @@ -22,7 +21,6 @@ extension Array where Element == Article { } func rowOfNextUnreadArticle(_ selectedRow: Int) -> Int? { - if isEmpty { return nil } @@ -44,7 +42,6 @@ extension Array where Element == Article { } func articlesForIndexes(_ indexes: IndexSet) -> Set
{ - return Set(indexes.compactMap{ (oneIndex) -> Article? in return articleAtRow(oneIndex) }) @@ -55,12 +52,10 @@ extension Array where Element == Article { } func canMarkAllAsRead() -> Bool { - return anyArticleIsUnread() } func anyArticlePassesTest(_ test: ((Article) -> Bool)) -> Bool { - for article in self { if test(article) { return true @@ -69,28 +64,23 @@ extension Array where Element == Article { return false } - func anyArticleIsRead() -> Bool { - - return anyArticlePassesTest { $0.status.read } + func anyArticleIsReadAndCanMarkUnread() -> Bool { + return anyArticlePassesTest { $0.status.read && $0.isAvailableToMarkUnread } } func anyArticleIsUnread() -> Bool { - return anyArticlePassesTest { !$0.status.read } } func anyArticleIsStarred() -> Bool { - return anyArticlePassesTest { $0.status.starred } } func anyArticleIsUnstarred() -> Bool { - return anyArticlePassesTest { !$0.status.starred } } func unreadArticles() -> [Article]? { - let articles = self.filter{ !$0.status.read } return articles.isEmpty ? nil : articles }