Rename MarkReadOrUnreadCommand to MarkStatusCommand and make it handle starring/unstarring and deleting/undeleting. Also: add contextual menu for smart feeds in the sidebar.
This commit is contained in:
parent
a13d21395e
commit
c8d2fac9a6
|
@ -1,63 +0,0 @@
|
|||
//
|
||||
// MarkReadOrUnreadCommand.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 10/26/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import Data
|
||||
|
||||
final class MarkReadOrUnreadCommand: UndoableCommand {
|
||||
|
||||
static private let markReadActionName = NSLocalizedString("Mark Read", comment: "command")
|
||||
static private let markUnreadActionName = NSLocalizedString("Mark Unread", comment: "command")
|
||||
let undoActionName: String
|
||||
let redoActionName: String
|
||||
let articles: Set<Article>
|
||||
let undoManager: UndoManager
|
||||
let markingRead: Bool
|
||||
|
||||
init?(initialArticles: [Article], markingRead: Bool, undoManager: UndoManager) {
|
||||
|
||||
// Filter out articles already read.
|
||||
let articlesToMark = initialArticles.filter { markingRead ? !$0.status.read : $0.status.read }
|
||||
if articlesToMark.isEmpty {
|
||||
return nil
|
||||
}
|
||||
self.articles = Set(articlesToMark)
|
||||
|
||||
self.markingRead = markingRead
|
||||
|
||||
self.undoManager = undoManager
|
||||
|
||||
if markingRead {
|
||||
self.undoActionName = MarkReadOrUnreadCommand.markReadActionName
|
||||
self.redoActionName = MarkReadOrUnreadCommand.markReadActionName
|
||||
}
|
||||
else {
|
||||
self.undoActionName = MarkReadOrUnreadCommand.markUnreadActionName
|
||||
self.redoActionName = MarkReadOrUnreadCommand.markUnreadActionName
|
||||
}
|
||||
}
|
||||
|
||||
func perform() {
|
||||
mark(read: markingRead)
|
||||
registerUndo()
|
||||
}
|
||||
|
||||
func undo() {
|
||||
mark(read: !markingRead)
|
||||
registerRedo()
|
||||
}
|
||||
}
|
||||
|
||||
private extension MarkReadOrUnreadCommand {
|
||||
|
||||
func mark(read: Bool) {
|
||||
|
||||
markArticles(articles, statusKey: .read, flag: read)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// MarkStatusCommand.swift
|
||||
// Evergreen
|
||||
//
|
||||
// Created by Brent Simmons on 10/26/17.
|
||||
// Copyright © 2017 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import Data
|
||||
|
||||
// Mark articles read/unread, starred/unstarred, deleted/undeleted.
|
||||
|
||||
final class MarkStatusCommand: UndoableCommand {
|
||||
|
||||
let undoActionName: String
|
||||
let redoActionName: String
|
||||
let articles: Set<Article>
|
||||
let undoManager: UndoManager
|
||||
let flag: Bool
|
||||
let statusKey: ArticleStatus.Key
|
||||
|
||||
init?(initialArticles: [Article], statusKey: ArticleStatus.Key, flag: Bool, undoManager: UndoManager) {
|
||||
|
||||
// Filter out articles that already have the desired status.
|
||||
let articlesToMark = MarkStatusCommand.filteredArticles(initialArticles, statusKey, flag)
|
||||
if articlesToMark.isEmpty {
|
||||
return nil
|
||||
}
|
||||
self.articles = Set(articlesToMark)
|
||||
|
||||
self.flag = flag
|
||||
self.statusKey = statusKey
|
||||
self.undoManager = undoManager
|
||||
|
||||
let actionName = MarkStatusCommand.actionName(statusKey, flag)
|
||||
self.undoActionName = actionName
|
||||
self.redoActionName = actionName
|
||||
}
|
||||
|
||||
convenience init?(initialArticles: [Article], markingRead: Bool, undoManager: UndoManager) {
|
||||
|
||||
self.init(initialArticles: initialArticles, statusKey: .read, flag: markingRead, undoManager: undoManager)
|
||||
}
|
||||
|
||||
convenience init?(initialArticles: [Article], markingStarred: Bool, undoManager: UndoManager) {
|
||||
|
||||
self.init(initialArticles: initialArticles, statusKey: .starred, flag: markingStarred, undoManager: undoManager)
|
||||
}
|
||||
|
||||
func perform() {
|
||||
mark(statusKey, flag)
|
||||
registerUndo()
|
||||
}
|
||||
|
||||
func undo() {
|
||||
mark(statusKey, !flag)
|
||||
registerRedo()
|
||||
}
|
||||
}
|
||||
|
||||
private extension MarkStatusCommand {
|
||||
|
||||
func mark(_ statusKey: ArticleStatus.Key, _ flag: Bool) {
|
||||
|
||||
markArticles(articles, statusKey: statusKey, flag: flag)
|
||||
}
|
||||
|
||||
static private let markReadActionName = NSLocalizedString("Mark Read", comment: "command")
|
||||
static private let markUnreadActionName = NSLocalizedString("Mark Unread", comment: "command")
|
||||
static private let markStarredActionName = NSLocalizedString("Mark Starred", comment: "command")
|
||||
static private let markUnstarredActionName = NSLocalizedString("Mark Unstarred", comment: "command")
|
||||
static private let markDeletedActionName = NSLocalizedString("Delete", comment: "command")
|
||||
static private let markUndeletedActionName = NSLocalizedString("Undelete", comment: "command")
|
||||
|
||||
static func actionName(_ statusKey: ArticleStatus.Key, _ flag: Bool) -> String {
|
||||
|
||||
switch statusKey {
|
||||
case .read:
|
||||
return flag ? markReadActionName : markUnreadActionName
|
||||
case .starred:
|
||||
return flag ? markStarredActionName : markUnstarredActionName
|
||||
case .userDeleted:
|
||||
return flag ? markDeletedActionName : markUndeletedActionName
|
||||
}
|
||||
}
|
||||
|
||||
static func filteredArticles(_ articles: [Article], _ statusKey: ArticleStatus.Key, _ flag: Bool) -> [Article] {
|
||||
|
||||
return articles.filter{ $0.status.boolStatus(forKey: statusKey) != flag }
|
||||
}
|
||||
}
|
|
@ -49,7 +49,7 @@
|
|||
846E773E1F6EF67A00A165E2 /* Account.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 846E773A1F6EF5D700A165E2 /* Account.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
846E77411F6EF6A100A165E2 /* Database.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846E77211F6EF5D100A165E2 /* Database.framework */; };
|
||||
846E77421F6EF6A100A165E2 /* Database.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 846E77211F6EF5D100A165E2 /* Database.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
84702AA41FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */; };
|
||||
84702AA41FA27AC0006B8943 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
|
||||
8472058120142E8900AD578B /* FeedInspectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8472058020142E8900AD578B /* FeedInspectorViewController.swift */; };
|
||||
847FA121202BA34100BB56C8 /* SidebarContextualMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847FA120202BA34100BB56C8 /* SidebarContextualMenuDelegate.swift */; };
|
||||
848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */; };
|
||||
|
@ -562,7 +562,7 @@
|
|||
845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedPasteboardWriter.swift; sourceTree = "<group>"; };
|
||||
846E77161F6EF5D000A165E2 /* Database.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Database.xcodeproj; path = Frameworks/Database/Database.xcodeproj; sourceTree = "<group>"; };
|
||||
846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; sourceTree = "<group>"; };
|
||||
84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkReadOrUnreadCommand.swift; sourceTree = "<group>"; };
|
||||
84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkStatusCommand.swift; sourceTree = "<group>"; };
|
||||
8472058020142E8900AD578B /* FeedInspectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedInspectorViewController.swift; sourceTree = "<group>"; };
|
||||
847752FE2008879500D93690 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
|
||||
847FA120202BA34100BB56C8 /* SidebarContextualMenuDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarContextualMenuDelegate.swift; sourceTree = "<group>"; };
|
||||
|
@ -893,7 +893,7 @@
|
|||
84702AB31FA27AE8006B8943 /* Commands */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84702AA31FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift */,
|
||||
84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */,
|
||||
84B99C9C1FAE83C600ECDEDB /* DeleteFromSidebarCommand.swift */,
|
||||
84A1500220048D660046AD9A /* SendToCommand.swift */,
|
||||
84A14FF220048CA70046AD9A /* SendToMicroBlogCommand.swift */,
|
||||
|
@ -1896,7 +1896,7 @@
|
|||
D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */,
|
||||
8403E75B201C4A79007F7246 /* FeedListKeyboardDelegate.swift in Sources */,
|
||||
845EE7C11FC2488C00854A1F /* SmartFeed.swift in Sources */,
|
||||
84702AA41FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift in Sources */,
|
||||
84702AA41FA27AC0006B8943 /* MarkStatusCommand.swift in Sources */,
|
||||
D5907D7F2004AC00005947E5 /* NSApplication+Scriptability.swift in Sources */,
|
||||
849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */,
|
||||
849A97651ED9EB96007D329B /* SidebarTreeControllerDelegate.swift in Sources */,
|
||||
|
|
|
@ -19,17 +19,22 @@ extension SidebarViewController {
|
|||
return menuForNoSelection()
|
||||
}
|
||||
|
||||
if objects.count == 1 {
|
||||
if let feed = objects.first as? Feed {
|
||||
return menuForFeed(feed)
|
||||
}
|
||||
if let folder = objects.first as? Folder {
|
||||
return menuForFolder(folder)
|
||||
}
|
||||
return nil
|
||||
if objects.count > 1 {
|
||||
return menuForMultipleObjects(objects)
|
||||
}
|
||||
|
||||
return menuForMultipleObjects(objects)
|
||||
let object = objects.first!
|
||||
|
||||
switch object {
|
||||
case is Feed:
|
||||
return menuForFeed(object as! Feed)
|
||||
case is Folder:
|
||||
return menuForFolder(object as! Folder)
|
||||
case is PseudoFeed:
|
||||
return menuForSmartFeed(object as! PseudoFeed)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,11 +65,7 @@ extension SidebarViewController {
|
|||
}
|
||||
|
||||
let articles = unreadArticles(for: objects)
|
||||
if articles.isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
guard let undoManager = undoManager, let markReadCommand = MarkReadOrUnreadCommand(initialArticles: Array(articles), markingRead: true, undoManager: undoManager) else {
|
||||
guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: Array(articles), markingRead: true, undoManager: undoManager) else {
|
||||
return
|
||||
}
|
||||
runCommand(markReadCommand)
|
||||
|
@ -160,11 +161,21 @@ private extension SidebarViewController {
|
|||
return menu.numberOfItems > 0 ? menu : nil
|
||||
}
|
||||
|
||||
func menuForSmartFeed(_ smartFeed: PseudoFeed) -> NSMenu? {
|
||||
|
||||
let menu = NSMenu(title: "")
|
||||
|
||||
if smartFeed.unreadCount > 0 {
|
||||
menu.addItem(markAllReadMenuItem([smartFeed]))
|
||||
}
|
||||
return menu.numberOfItems > 0 ? menu : nil
|
||||
}
|
||||
|
||||
func menuForMultipleObjects(_ objects: [Any]) -> NSMenu? {
|
||||
|
||||
guard allObjectsAreFeedsAndOrFolders(objects) else {
|
||||
return nil
|
||||
}
|
||||
// guard allObjectsAreFeedsAndOrFolders(objects) else {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
let menu = NSMenu(title: "")
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ private extension TimelineViewController {
|
|||
guard let articlesToMark = read ? unreadArticles(from: articles) : readArticles(from: articles) else {
|
||||
return
|
||||
}
|
||||
guard let undoManager = undoManager, let markReadCommand = MarkReadOrUnreadCommand(initialArticles: Array(articlesToMark), markingRead: read, undoManager: undoManager) else {
|
||||
guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: Array(articlesToMark), markingRead: read, undoManager: undoManager) else {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ class TimelineViewController: NSViewController, UndoableCommandRunner {
|
|||
|
||||
func markAllAsRead() {
|
||||
|
||||
guard let undoManager = undoManager, let markReadCommand = MarkReadOrUnreadCommand(initialArticles: articles, markingRead: true, undoManager: undoManager) else {
|
||||
guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: articles, markingRead: true, undoManager: undoManager) else {
|
||||
return
|
||||
}
|
||||
runCommand(markReadCommand)
|
||||
|
@ -201,7 +201,7 @@ class TimelineViewController: NSViewController, UndoableCommandRunner {
|
|||
|
||||
@IBAction func markSelectedArticlesAsRead(_ sender: Any?) {
|
||||
|
||||
guard let undoManager = undoManager, let markReadCommand = MarkReadOrUnreadCommand(initialArticles: selectedArticles, markingRead: true, undoManager: undoManager) else {
|
||||
guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: selectedArticles, markingRead: true, undoManager: undoManager) else {
|
||||
return
|
||||
}
|
||||
runCommand(markReadCommand)
|
||||
|
@ -209,7 +209,7 @@ class TimelineViewController: NSViewController, UndoableCommandRunner {
|
|||
|
||||
@IBAction func markSelectedArticlesAsUnread(_ sender: Any?) {
|
||||
|
||||
guard let undoManager = undoManager, let markUnreadCommand = MarkReadOrUnreadCommand(initialArticles: selectedArticles, markingRead: false, undoManager: undoManager) else {
|
||||
guard let undoManager = undoManager, let markUnreadCommand = MarkStatusCommand(initialArticles: selectedArticles, markingRead: false, undoManager: undoManager) else {
|
||||
return
|
||||
}
|
||||
runCommand(markUnreadCommand)
|
||||
|
@ -237,7 +237,7 @@ class TimelineViewController: NSViewController, UndoableCommandRunner {
|
|||
return
|
||||
}
|
||||
|
||||
guard let undoManager = undoManager, let markReadCommand = MarkReadOrUnreadCommand(initialArticles: articlesToMark, markingRead: true, undoManager: undoManager) else {
|
||||
guard let undoManager = undoManager, let markReadCommand = MarkStatusCommand(initialArticles: articlesToMark, markingRead: true, undoManager: undoManager) else {
|
||||
return
|
||||
}
|
||||
runCommand(markReadCommand)
|
||||
|
|
Loading…
Reference in New Issue