2017-11-05 03:03:47 +01:00
|
|
|
//
|
|
|
|
// DeleteFromSidebarCommand.swift
|
|
|
|
// Evergreen
|
|
|
|
//
|
|
|
|
// Created by Brent Simmons on 11/4/17.
|
|
|
|
// Copyright © 2017 Ranchero Software. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
import RSCore
|
|
|
|
import RSTree
|
|
|
|
import Account
|
|
|
|
import Data
|
|
|
|
|
|
|
|
final class DeleteFromSidebarCommand: UndoableCommand {
|
|
|
|
|
2017-11-05 06:51:14 +01:00
|
|
|
let undoManager: UndoManager
|
2017-11-05 03:03:47 +01:00
|
|
|
let undoActionName: String
|
|
|
|
var redoActionName: String {
|
|
|
|
get {
|
|
|
|
return undoActionName
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-05 06:51:14 +01:00
|
|
|
private let itemSpecifiers: [SidebarItemSpecifier]
|
|
|
|
|
2017-11-05 03:03:47 +01:00
|
|
|
init?(nodesToDelete: [Node], undoManager: UndoManager) {
|
|
|
|
|
2017-11-05 06:51:14 +01:00
|
|
|
guard DeleteFromSidebarCommand.canDelete(nodesToDelete) else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
guard let actionName = DeleteActionName.name(for: nodesToDelete) else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
self.undoActionName = actionName
|
2017-11-05 03:03:47 +01:00
|
|
|
self.undoManager = undoManager
|
2017-11-05 06:51:14 +01:00
|
|
|
|
|
|
|
let itemSpecifiers = nodesToDelete.flatMap{ SidebarItemSpecifier(node: $0) }
|
|
|
|
guard !itemSpecifiers.isEmpty else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
self.itemSpecifiers = itemSpecifiers
|
2017-11-05 03:03:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func perform() {
|
|
|
|
|
2017-11-05 06:51:14 +01:00
|
|
|
BatchUpdate.shared.perform {
|
|
|
|
itemSpecifiers.forEach { $0.delete() }
|
|
|
|
}
|
2017-11-05 03:03:47 +01:00
|
|
|
registerUndo()
|
|
|
|
}
|
|
|
|
|
|
|
|
func undo() {
|
|
|
|
|
2017-11-05 06:51:14 +01:00
|
|
|
BatchUpdate.shared.perform {
|
|
|
|
|
|
|
|
}
|
2017-11-05 03:03:47 +01:00
|
|
|
registerRedo()
|
|
|
|
}
|
|
|
|
|
|
|
|
static func canDelete(_ nodes: [Node]) -> Bool {
|
|
|
|
|
|
|
|
// Return true if all nodes are feeds and folders.
|
|
|
|
// Any other type: return false.
|
|
|
|
|
|
|
|
if nodes.isEmpty {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
for node in nodes {
|
|
|
|
if let _ = node.representedObject as? Feed {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if let _ = node.representedObject as? Folder {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remember as much as we can now about the items being deleted,
|
|
|
|
// so they can be restored to the correct place.
|
|
|
|
|
|
|
|
private struct SidebarItemSpecifier {
|
|
|
|
|
2017-11-05 06:51:14 +01:00
|
|
|
private weak var account: Account?
|
|
|
|
private let parentFolder: Folder?
|
|
|
|
private let folder: Folder?
|
|
|
|
private let feed: Feed?
|
|
|
|
private let path: ContainerPath
|
|
|
|
|
|
|
|
private var container: Container? {
|
|
|
|
get {
|
|
|
|
if let parentFolder = parentFolder {
|
|
|
|
return parentFolder
|
|
|
|
}
|
|
|
|
if let account = account {
|
|
|
|
return account
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-05 03:03:47 +01:00
|
|
|
init?(node: Node) {
|
|
|
|
|
|
|
|
var account: Account?
|
2017-11-05 03:29:43 +01:00
|
|
|
|
2017-11-05 06:51:14 +01:00
|
|
|
self.parentFolder = node.parentFolder()
|
|
|
|
|
|
|
|
if let feed = node.representedObject as? Feed {
|
2017-11-05 03:03:47 +01:00
|
|
|
self.feed = feed
|
2017-11-05 03:29:43 +01:00
|
|
|
self.folder = nil
|
2017-11-05 06:51:14 +01:00
|
|
|
account = feed.account
|
2017-11-05 03:03:47 +01:00
|
|
|
}
|
|
|
|
else if let folder = node.representedObject as? Folder {
|
2017-11-05 03:29:43 +01:00
|
|
|
self.feed = nil
|
2017-11-05 03:03:47 +01:00
|
|
|
self.folder = folder
|
|
|
|
account = folder.account
|
|
|
|
}
|
2017-11-05 03:29:43 +01:00
|
|
|
else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if account == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
self.account = account!
|
|
|
|
self.path = ContainerPath(account: account!, folders: node.containingFolders())
|
2017-11-05 03:03:47 +01:00
|
|
|
}
|
2017-11-05 06:51:14 +01:00
|
|
|
|
|
|
|
func delete() {
|
|
|
|
|
|
|
|
guard let container = container else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if let feed = feed {
|
|
|
|
container.deleteFeed(feed)
|
|
|
|
}
|
|
|
|
else if let folder = folder {
|
|
|
|
container.deleteFolder(folder)
|
|
|
|
}
|
|
|
|
}
|
2017-11-05 03:03:47 +01:00
|
|
|
}
|
|
|
|
|
2017-11-05 03:29:43 +01:00
|
|
|
private extension Node {
|
|
|
|
|
2017-11-05 06:51:14 +01:00
|
|
|
func parentFolder() -> Folder? {
|
|
|
|
|
|
|
|
guard let parentNode = self.parent else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if parentNode.isRoot {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if let folder = parentNode.representedObject as? Folder {
|
|
|
|
return folder
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-11-05 03:29:43 +01:00
|
|
|
func containingFolders() -> [Folder] {
|
|
|
|
|
|
|
|
var nomad = self.parent
|
|
|
|
var folders = [Folder]()
|
|
|
|
|
|
|
|
while nomad != nil {
|
|
|
|
if let folder = nomad!.representedObject as? Folder {
|
|
|
|
folders += [folder]
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
nomad = nomad!.parent
|
|
|
|
}
|
|
|
|
|
|
|
|
return folders.reversed()
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-11-05 06:51:14 +01:00
|
|
|
private struct DeleteActionName {
|
|
|
|
|
|
|
|
private static let deleteFeed = NSLocalizedString("Delete Feed", comment: "command")
|
|
|
|
private static let deleteFeeds = NSLocalizedString("Delete Feeds", comment: "command")
|
|
|
|
private static let deleteFolder = NSLocalizedString("Delete Folder", comment: "command")
|
|
|
|
private static let deleteFolders = NSLocalizedString("Delete Folders", comment: "command")
|
|
|
|
private static let deleteFeedsAndFolders = NSLocalizedString("Delete Feeds and Folders", comment: "command")
|
|
|
|
|
|
|
|
static func name(for nodes: [Node]) -> String? {
|
|
|
|
|
|
|
|
var numberOfFeeds = 0
|
|
|
|
var numberOfFolders = 0
|
|
|
|
|
|
|
|
for node in nodes {
|
|
|
|
if let _ = node.representedObject as? Feed {
|
|
|
|
numberOfFeeds += 1
|
|
|
|
}
|
|
|
|
else if let _ = node.representedObject as? Folder {
|
|
|
|
numberOfFolders += 1
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return nil // Delete only Feeds and Folders.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if numberOfFolders < 1 {
|
|
|
|
return numberOfFeeds == 1 ? deleteFeed : deleteFeeds
|
|
|
|
}
|
|
|
|
if numberOfFeeds < 1 {
|
|
|
|
return numberOfFolders == 1 ? deleteFolder : deleteFolders
|
|
|
|
}
|
|
|
|
|
|
|
|
return deleteFeedsAndFolders
|
|
|
|
}
|
|
|
|
}
|