From c7b61f7d871e700c5025de1869bb6e6aa78c44a4 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 27 May 2019 18:01:24 -0500 Subject: [PATCH] Enable copy between local accounts. --- .../LocalAccount/LocalAccountDelegate.swift | 2 + .../Sidebar/SidebarOutlineDataSource.swift | 90 +++++++++++++++++-- .../Sidebar/SidebarViewController.swift | 3 +- 3 files changed, 88 insertions(+), 7 deletions(-) diff --git a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift index 5a12f5b88..f9efdbc31 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift @@ -143,8 +143,10 @@ final class LocalAccountDelegate: AccountDelegate { func addFeed(for account: Account, to container: Container, with feed: Feed, completion: @escaping (Result) -> Void) { if let folder = container as? Folder { folder.addFeed(feed) + feed.account = folder.account } else if let account = container as? Account { account.addFeed(feed) + feed.account = account } completion(.success(())) } diff --git a/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift b/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift index f2a44feff..fed4cda2d 100644 --- a/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift +++ b/Mac/MainWindow/Sidebar/SidebarOutlineDataSource.swift @@ -179,14 +179,15 @@ private extension SidebarOutlineDataSource { if nodeHasChildRepresentingDraggedFeed(dropTargetNode, draggedFeed) { return SidebarOutlineDataSource.dragOperationNone } + let dragOperation: NSDragOperation = localFeedsDropOperation(dropTargetNode, Set([draggedFeed])) if parentNode == dropTargetNode && index == NSOutlineViewDropOnItemIndex { - return .move + return dragOperation } let updatedIndex = indexWhereDraggedFeedWouldAppear(dropTargetNode, draggedFeed) if parentNode !== dropTargetNode || index != updatedIndex { outlineView.setDropItem(dropTargetNode, dropChildIndex: updatedIndex) } - return .move + return dragOperation } func validateLocalFeedsDrop(_ outlineView: NSOutlineView, _ draggedFeeds: Set, _ parentNode: Node, _ index: Int) -> NSDragOperation { @@ -203,7 +204,18 @@ private extension SidebarOutlineDataSource { if parentNode !== dropTargetNode || index != NSOutlineViewDropOnItemIndex { outlineView.setDropItem(dropTargetNode, dropChildIndex: NSOutlineViewDropOnItemIndex) } - return .move + return localFeedsDropOperation(dropTargetNode, draggedFeeds) + } + + func localFeedsDropOperation(_ dropTargetNode: Node, _ draggedFeeds: Set) -> NSDragOperation { + if allParticipantsAreSameAccount(dropTargetNode, draggedFeeds) { + return .move + } + if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false { + return .copy + } else { + return .move + } } private func accountForNode(_ node: Node) -> Account? { @@ -231,12 +243,34 @@ private extension SidebarOutlineDataSource { return accounts } + private func copy(node: Node, to parentNode: Node) { + guard let feed = node.representedObject as? Feed else { + return + } + + let destination = parentNode.representedObject as? Container + + BatchUpdate.shared.start() + destination?.addFeed(feed) { result in + switch result { + case .success: + BatchUpdate.shared.end() + break + case .failure(let error): + BatchUpdate.shared.end() + NSApplication.shared.presentError(error) + } + } + } + private func move(node: Node, to parentNode: Node) { guard let feed = node.representedObject as? Feed else { return } + let source = node.parent?.representedObject as? Container let destination = parentNode.representedObject as? Container + BatchUpdate.shared.start() source?.removeFeed(feed) { result in switch result { @@ -256,6 +290,7 @@ private extension SidebarOutlineDataSource { case .failure(let error): NSApplication.shared.presentError(error) } + } } @@ -265,9 +300,19 @@ private extension SidebarOutlineDataSource { } BatchUpdate.shared.perform { - draggedNodes.forEach { move(node: $0, to: parentNode) } + + draggedNodes.forEach { node in + if sameAccount(node, parentNode) { + move(node: node, to: parentNode) + } else if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false { + copy(node: node, to: parentNode) + } else { + move(node: node, to: parentNode) + } + } + } - + let allReferencedNodes = draggedNodes.union(Set([parentNode])) let accounts = commonAccountsFor(allReferencedNodes) accounts.forEach { $0.structureDidChange() } @@ -364,6 +409,41 @@ private extension SidebarOutlineDataSource { } + func allParticipantsAreSameAccount(_ parentNode: Node, _ draggedFeeds: Set) -> Bool { + guard let parentAccountID = nodeAccountID(parentNode) else { + return false + } + + for draggedFeed in draggedFeeds { + if draggedFeed.accountID != parentAccountID { + return false + } + } + + return true + } + + func sameAccount(_ node: Node, _ parentNode: Node) -> Bool { + if let accountID = nodeAccountID(node), let parentAccountID = nodeAccountID(parentNode) { + if accountID == parentAccountID { + return true + } + } + return false + } + + func nodeAccountID(_ node: Node) -> String? { + if let account = node.representedObject as? Account { + return account.accountID + } else if let folder = node.representedObject as? Folder { + return folder.account?.accountID + } else if let feed = node.representedObject as? Feed { + return feed.account?.accountID + } else { + return nil + } + } + func nodeHasChildRepresentingAnyDraggedFeed(_ parentNode: Node, _ draggedFeeds: Set) -> Bool { for node in parentNode.childNodes { if nodeRepresentsAnyDraggedFeed(node, draggedFeeds) { diff --git a/Mac/MainWindow/Sidebar/SidebarViewController.swift b/Mac/MainWindow/Sidebar/SidebarViewController.swift index 7b5e64f85..cb890af36 100644 --- a/Mac/MainWindow/Sidebar/SidebarViewController.swift +++ b/Mac/MainWindow/Sidebar/SidebarViewController.swift @@ -47,8 +47,7 @@ protocol SidebarDelegate: class { sidebarCellAppearance = SidebarCellAppearance(fontSize: AppDefaults.sidebarFontSize) outlineView.dataSource = dataSource - outlineView.setDraggingSourceOperationMask(.move, forLocal: true) - outlineView.setDraggingSourceOperationMask(.copy, forLocal: false) + outlineView.setDraggingSourceOperationMask([.move, .copy], forLocal: true) outlineView.registerForDraggedTypes([FeedPasteboardWriter.feedUTIInternalType, FeedPasteboardWriter.feedUTIType, .URL, .string]) NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)