Implement mark as unread window for accounts that need it. Issue #1407

This commit is contained in:
Maurice Parker 2020-02-18 13:49:29 -08:00
parent 8acd6a039a
commit 0e72811429
7 changed files with 51 additions and 18 deletions

View File

@ -14,25 +14,28 @@ import Foundation
user interface as much as possible. For example some sync services don't allow user interface as much as possible. For example some sync services don't allow
feeds to be in the root folder of the account. feeds to be in the root folder of the account.
*/ */
public struct AccountBehaviors: OptionSet { public typealias AccountBehaviors = [AccountBehavior]
public enum AccountBehavior: Equatable {
/** /**
Account doesn't support copies of a feed that are in a folder to be made to the root folder. Account doesn't support copies of a feed that are in a folder to be made to the root folder.
*/ */
public static let disallowFeedCopyInRootFolder = AccountBehaviors(rawValue: 1) case disallowFeedCopyInRootFolder
/** /**
Account doesn't support feeds in the root folder. Account doesn't support feeds in the root folder.
*/ */
public static let disallowFeedInRootFolder = AccountBehaviors(rawValue: 2) case disallowFeedInRootFolder
/** /**
Account doesn't support OPML imports Account doesn't support OPML imports
*/ */
public static let disallowOPMLImports = AccountBehaviors(rawValue: 4) case disallowOPMLImports
public let rawValue: Int /**
public init(rawValue: Int) { Account doesn't allow mark as read after a period of days
self.rawValue = rawValue */
} case disallowMarkAsUnreadAfterPeriod(Int)
} }

View File

@ -25,7 +25,7 @@ final class FeedlyAccountDelegate: AccountDelegate {
// TODO: Kiel, if you decide not to support OPML import you will have to disallow it in the behaviors // TODO: Kiel, if you decide not to support OPML import you will have to disallow it in the behaviors
// See https://developer.feedly.com/v3/opml/ // See https://developer.feedly.com/v3/opml/
var behaviors: AccountBehaviors = [.disallowFeedInRootFolder] var behaviors: AccountBehaviors = [.disallowFeedInRootFolder, .disallowMarkAsUnreadAfterPeriod(31)]
let isOPMLImportSupported = false let isOPMLImportSupported = false

View File

@ -63,6 +63,25 @@ extension Article {
var logicalDatePublished: Date { var logicalDatePublished: Date {
return datePublished ?? dateModified ?? status.dateArrived return datePublished ?? dateModified ?? status.dateArrived
} }
var isAvailableToMarkUnread: Bool {
guard let markUnreadWindow = account?.behaviors.compactMap( { behavior -> Int? in
switch behavior {
case .disallowMarkAsUnreadAfterPeriod(let days):
return days
default:
return nil
}
}).first else {
return true
}
if logicalDatePublished.byAdding(days: markUnreadWindow) > Date() {
return true
} else {
return false
}
}
func iconImage() -> IconImage? { func iconImage() -> IconImage? {
if let authors = authors, authors.count == 1, let author = authors.first { if let authors = authors, authors.count == 1, let author = authors.first {

View File

@ -157,9 +157,11 @@ class ArticleViewController: UIViewController {
if article.status.read { if article.status.read {
readBarButtonItem.image = AppAssets.circleOpenImage readBarButtonItem.image = AppAssets.circleOpenImage
readBarButtonItem.isEnabled = article.isAvailableToMarkUnread
readBarButtonItem.accLabelText = NSLocalizedString("Mark Article Unread", comment: "Mark Article Unread") readBarButtonItem.accLabelText = NSLocalizedString("Mark Article Unread", comment: "Mark Article Unread")
} else { } else {
readBarButtonItem.image = AppAssets.circleClosedImage readBarButtonItem.image = AppAssets.circleClosedImage
readBarButtonItem.isEnabled = true
readBarButtonItem.accLabelText = NSLocalizedString("Selected - Mark Article Unread", comment: "Selected - Mark Article Unread") readBarButtonItem.accLabelText = NSLocalizedString("Selected - Mark Article Unread", comment: "Selected - Mark Article Unread")
} }

View File

@ -264,7 +264,9 @@ extension WebViewController: UIContextMenuInteractionDelegate {
if let action = self.nextArticleAction() { if let action = self.nextArticleAction() {
actions.append(action) actions.append(action)
} }
actions.append(self.toggleReadAction()) if let action = self.toggleReadAction() {
actions.append(action)
}
actions.append(self.toggleStarredAction()) actions.append(self.toggleStarredAction())
if let action = self.nextUnreadArticleAction() { if let action = self.nextUnreadArticleAction() {
actions.append(action) actions.append(action)
@ -642,10 +644,11 @@ private extension WebViewController {
} }
} }
func toggleReadAction() -> UIAction { func toggleReadAction() -> UIAction? {
let read = article?.status.read ?? false guard let article = article, !article.status.read || article.isAvailableToMarkUnread else { return nil }
let title = read ? NSLocalizedString("Mark as Unread", comment: "Mark as Unread") : NSLocalizedString("Mark as Read", comment: "Mark as Read")
let readImage = read ? AppAssets.circleClosedImage : AppAssets.circleOpenImage let title = article.status.read ? NSLocalizedString("Mark as Unread", comment: "Mark as Unread") : NSLocalizedString("Mark as Read", comment: "Mark as Read")
let readImage = article.status.read ? AppAssets.circleClosedImage : AppAssets.circleOpenImage
return UIAction(title: title, image: readImage) { [weak self] action in return UIAction(title: title, image: readImage) { [weak self] action in
self?.coordinator.toggleReadForCurrentArticle() self?.coordinator.toggleReadForCurrentArticle()
} }

View File

@ -217,7 +217,8 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { override func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
guard let article = dataSource.itemIdentifier(for: indexPath) else { return nil } guard let article = dataSource.itemIdentifier(for: indexPath) else { return nil }
guard !article.status.read || article.isAvailableToMarkUnread else { return nil }
// Set up the read action // Set up the read action
let readTitle = article.status.read ? let readTitle = article.status.read ?
NSLocalizedString("Unread", comment: "Unread") : NSLocalizedString("Unread", comment: "Unread") :
@ -314,7 +315,10 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
guard let self = self else { return nil } guard let self = self else { return nil }
var actions = [UIAction]() var actions = [UIAction]()
actions.append(self.toggleArticleReadStatusAction(article)) if let action = self.toggleArticleReadStatusAction(article) {
actions.append(action)
}
actions.append(self.toggleArticleStarStatusAction(article)) actions.append(self.toggleArticleStarStatusAction(article))
if let action = self.markAboveAsReadAction(article) { if let action = self.markAboveAsReadAction(article) {
@ -672,8 +676,9 @@ private extension MasterTimelineViewController {
return nil return nil
} }
func toggleArticleReadStatusAction(_ article: Article) -> UIAction { func toggleArticleReadStatusAction(_ article: Article) -> UIAction? {
guard !article.status.read || article.isAvailableToMarkUnread else { return nil }
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") :
NSLocalizedString("Mark as Read", comment: "Mark as Read") NSLocalizedString("Mark as Read", comment: "Mark as Read")

View File

@ -1008,6 +1008,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
} }
func toggleRead(_ article: Article) { func toggleRead(_ article: Article) {
guard !article.status.read || article.isAvailableToMarkUnread else { return }
markArticlesWithUndo([article], statusKey: .read, flag: !article.status.read) markArticlesWithUndo([article], statusKey: .read, flag: !article.status.read)
} }