diff --git a/iOS/AppAssets.swift b/iOS/AppAssets.swift index 9d3e76409..08b3a814f 100644 --- a/iOS/AppAssets.swift +++ b/iOS/AppAssets.swift @@ -52,6 +52,14 @@ struct AppAssets { return RSImage(named: "feedImage")! }() + static var markOlderAsReadDownImage: UIImage = { + return UIImage(systemName: "arrowtriangle.down.circle")! + }() + + static var markOlderAsReadUpImage: UIImage = { + return UIImage(systemName: "arrowtriangle.up.circle")! + }() + static var masterFolderImage: UIImage = { return UIImage(systemName: "folder.fill")! }() diff --git a/iOS/AppCoordinator.swift b/iOS/AppCoordinator.swift index 2cf8872b3..544e0e156 100644 --- a/iOS/AppCoordinator.swift +++ b/iOS/AppCoordinator.swift @@ -57,7 +57,7 @@ class AppCoordinator: NSObject, UndoableCommandRunner { private var expandedNodes = [Node]() private var shadowTable = [[Node]]() - private var sortDirection = AppDefaults.timelineSortDirection { + private(set) var sortDirection = AppDefaults.timelineSortDirection { didSet { if sortDirection != oldValue { sortDirectionDidChange() diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index eb23fe225..f67d76f5a 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -150,7 +150,34 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner starAction.image = AppAssets.starClosedImage starAction.backgroundColor = AppAssets.starColor - let configuration = UISwipeActionsConfiguration(actions: [readAction, starAction]) + // Set up the read action + let moreTitle = NSLocalizedString("More", comment: "More") + let moreAction = UIContextualAction(style: .normal, title: moreTitle) { [weak self] (action, view, completionHandler) in + + if let self = self { + + let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + if let popoverController = alert.popoverPresentationController { + popoverController.sourceView = view + 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)) + + let cancelTitle = NSLocalizedString("Cancel", comment: "Cancel") + alert.addAction(UIAlertAction(title: cancelTitle, style: .cancel) { _ in + completionHandler(true) + }) + + self.present(alert, animated: true) + + } + + } + + moreAction.backgroundColor = UIColor.systemGray + + let configuration = UISwipeActionsConfiguration(actions: [readAction, starAction, moreAction]) return configuration } @@ -162,6 +189,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner var actions = [UIAction]() actions.append(self.toggleArticleReadStatusAction(indexPath: indexPath)) actions.append(self.toggleArticleStarStatusAction(indexPath: indexPath)) + actions.append(self.markOlderAsReadAction(indexPath: indexPath)) let title = NSLocalizedString("Timeline Menu", comment: "Timeline Menu") return UIMenu(title: title, children: actions) @@ -469,6 +497,18 @@ private extension MasterTimelineViewController { runCommand(markReadCommand) } + func markOlderArticlesRead(_ article: Article) { + let articlesToMark = coordinator.articles.filter { $0.logicalDatePublished < article.logicalDatePublished } + if articlesToMark.isEmpty { + return + } + + guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: articlesToMark, markingRead: true, undoManager: undoManager) else { + return + } + runCommand(markReadCommand) + } + func toggleArticleReadStatusAction(indexPath: IndexPath) -> UIAction { let article = coordinator.articles[indexPath.row] @@ -477,8 +517,8 @@ private extension MasterTimelineViewController { NSLocalizedString("Mark as Read", comment: "Mark as Read") let image = article.status.read ? AppAssets.circleClosedImage : AppAssets.circleOpenImage - let action = UIAction(title: title, image: image) { [unowned self] action in - self.toggleArticleReadStatus(article) + let action = UIAction(title: title, image: image) { [weak self] action in + self?.toggleArticleReadStatus(article) } return action @@ -492,11 +532,31 @@ private extension MasterTimelineViewController { NSLocalizedString("Mark as Starred", comment: "Mark as Starred") let image = article.status.starred ? AppAssets.starOpenImage : AppAssets.starClosedImage - let action = UIAction(title: title, image: image) { [unowned self] action in - self.toggleArticleStarStatus(article) + let action = UIAction(title: title, image: image) { [weak self] action in + self?.toggleArticleStarStatus(article) } return action } + func markOlderAsReadAction(indexPath: IndexPath) -> UIAction { + let article = coordinator.articles[indexPath.row] + let title = NSLocalizedString("Mark Older as Read", comment: "Mark Older as Read") + let image = coordinator.sortDirection == .orderedDescending ? AppAssets.markOlderAsReadDownImage : AppAssets.markOlderAsReadUpImage + let action = UIAction(title: title, image: image) { [weak self] action in + self?.markOlderArticlesRead(article) + } + return action + } + + func markOlderAsReadAlertAction(indexPath: IndexPath, completionHandler: @escaping (Bool) -> Void) -> UIAlertAction { + let article = coordinator.articles[indexPath.row] + let title = NSLocalizedString("Mark Older as Read", comment: "Mark Older as Read") + let action = UIAlertAction(title: title, style: .default) { [weak self] action in + self?.markOlderArticlesRead(article) + completionHandler(true) + } + return action + } + }