Refactor FeedPasteboardWriter and DraggedFeed — add PasteboardFeed.

This commit is contained in:
Brent Simmons 2018-09-20 13:29:01 -07:00
parent 089ffbd299
commit 28a7386fd4
3 changed files with 125 additions and 104 deletions

View File

@ -58,13 +58,13 @@
845A29241FC9255E007B49E3 /* SidebarCellAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845A29231FC9255E007B49E3 /* SidebarCellAppearance.swift */; };
845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845EE7B01FC2366500854A1F /* StarredFeedDelegate.swift */; };
845EE7C11FC2488C00854A1F /* SmartFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845EE7C01FC2488C00854A1F /* SmartFeed.swift */; };
845F52ED1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */; };
846E773D1F6EF67A00A165E2 /* Account.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846E773A1F6EF5D700A165E2 /* Account.framework */; };
846E773E1F6EF67A00A165E2 /* Account.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 846E773A1F6EF5D700A165E2 /* Account.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
84702AA41FA27AC0006B8943 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
8472058120142E8900AD578B /* FeedInspectorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8472058020142E8900AD578B /* FeedInspectorViewController.swift */; };
84754C8A213E471B009CFDFB /* GeneralPrefencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84754C89213E471B009CFDFB /* GeneralPrefencesViewController.swift */; };
847FA121202BA34100BB56C8 /* SidebarContextualMenuDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847FA120202BA34100BB56C8 /* SidebarContextualMenuDelegate.swift */; };
848D578E21543519005FFAD5 /* PasteboardFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848D578D21543519005FFAD5 /* PasteboardFeed.swift */; };
848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */; };
849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */; };
849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97511ED9EAC0007D329B /* AddFeedController.swift */; };
@ -543,13 +543,13 @@
845B14A51FC2299E0013CF92 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
845EE7B01FC2366500854A1F /* StarredFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarredFeedDelegate.swift; sourceTree = "<group>"; };
845EE7C01FC2488C00854A1F /* SmartFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartFeed.swift; sourceTree = "<group>"; };
845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedPasteboardWriter.swift; sourceTree = "<group>"; };
846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; 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>"; };
84754C89213E471B009CFDFB /* GeneralPrefencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = GeneralPrefencesViewController.swift; path = NetNewsWire/Preferences/GeneralPrefencesViewController.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>"; };
848D578D21543519005FFAD5 /* PasteboardFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardFeed.swift; sourceTree = "<group>"; };
848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconDownloader.swift; sourceTree = "<group>"; };
849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFolderWindowController.swift; sourceTree = "<group>"; };
849A97511ED9EAC0007D329B /* AddFeedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedController.swift; path = AddFeed/AddFeedController.swift; sourceTree = "<group>"; };
@ -985,7 +985,7 @@
849A97601ED9EB96007D329B /* SidebarOutlineView.swift */,
849A97611ED9EB96007D329B /* SidebarTreeControllerDelegate.swift */,
849A97631ED9EB96007D329B /* UnreadCountView.swift */,
845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */,
848D578D21543519005FFAD5 /* PasteboardFeed.swift */,
84AD1EA92031617300BC20B7 /* FolderPasteboardWriter.swift */,
849A97821ED9EC63007D329B /* SidebarStatusBarView.swift */,
84D5BA1F201E8FB6009092BD /* SidebarGearMenuDelegate.swift */,
@ -1537,8 +1537,8 @@
ORGANIZATIONNAME = "Ranchero Software";
TargetAttributes = {
6581C73220CED60000F4AD34 = {
DevelopmentTeam = SHJK2V3AJG;
ProvisioningStyle = Automatic;
DevelopmentTeam = M8L2WTLA8W;
ProvisioningStyle = Manual;
};
840D617B2029031C009BC708 = {
CreatedOnToolsVersion = 9.3;
@ -1559,12 +1559,12 @@
};
849C645F1ED37A5D003D8FC0 = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = SHJK2V3AJG;
ProvisioningStyle = Automatic;
DevelopmentTeam = M8L2WTLA8W;
ProvisioningStyle = Manual;
};
849C64701ED37A5D003D8FC0 = {
CreatedOnToolsVersion = 8.2.1;
DevelopmentTeam = SHJK2V3AJG;
DevelopmentTeam = 9C84TZ7Q6Z;
ProvisioningStyle = Automatic;
TestTargetID = 849C645F1ED37A5D003D8FC0;
};
@ -1925,7 +1925,6 @@
84DAEE321F870B390058304B /* DockBadge.swift in Sources */,
842E45DD1ED8C54B000A8B52 /* Browser.swift in Sources */,
842E45E31ED8C681000A8B52 /* KeyboardDelegateProtocol.swift in Sources */,
845F52ED1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift in Sources */,
D5907DB32005F45D005947E5 /* AppleEventUtils.swift in Sources */,
8444C8F21FED81840051386C /* OPMLExporter.swift in Sources */,
849A975E1ED9EB72007D329B /* MainWindowController.swift in Sources */,
@ -1988,6 +1987,7 @@
845213231FCA5B11003B6E93 /* ImageDownloader.swift in Sources */,
849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */,
844B5B671FEA18E300C7C76A /* MainWIndowKeyboardHandler.swift in Sources */,
848D578E21543519005FFAD5 /* PasteboardFeed.swift in Sources */,
849A97921ED9EF65007D329B /* IndeterminateProgressWindowController.swift in Sources */,
849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */,
8426119E1FCB6ED40086A189 /* HTMLMetadataDownloader.swift in Sources */,

View File

@ -1,9 +1,9 @@
//
// FeedPasteboardWriter.swift
// PasteboardFeed.swift
// NetNewsWire
//
// Created by Brent Simmons on 11/7/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
// Created by Brent Simmons on 9/20/18.
// Copyright © 2018 Ranchero Software. All rights reserved.
//
import AppKit
@ -11,6 +11,113 @@ import Articles
import Account
import RSCore
typealias PasteboardFeedDictionary = [String: String]
struct PasteboardFeed: Hashable {
private struct Key {
static let url = "URL"
static let homePageURL = "homePageURL"
static let name = "name"
// Internal
static let accountID = "accountID"
static let feedID = "feedID"
static let editedName = "editedName"
}
let url: String
let feedID: String?
let homePageURL: String?
let name: String?
let editedName: String?
let accountID: String?
init(url: String, feedID: String?, homePageURL: String?, name: String?, editedName: String?, accountID: String?) {
self.url = url
self.feedID = feedID
self.homePageURL = homePageURL
self.name = name
self.editedName = editedName
self.accountID = accountID
}
// MARK: - Reading
init?(dictionary: PasteboardFeedDictionary) {
guard let url = dictionary[Key.url] else {
return nil
}
let homePageURL = dictionary[Key.homePageURL]
let name = dictionary[Key.name]
let accountID = dictionary[Key.accountID]
let feedID = dictionary[Key.feedID]
let editedName = dictionary[Key.editedName]
self.init(url: url, feedID: feedID, homePageURL: homePageURL, name: name, editedName: editedName, accountID: accountID)
}
init?(pasteboardItem: NSPasteboardItem) {
// TODO: This needs to handle strings and URLs also.
var pasteboardType: NSPasteboard.PasteboardType?
if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIInternalType) {
pasteboardType = FeedPasteboardWriter.feedUTIInternalType
}
else if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIType) {
pasteboardType = FeedPasteboardWriter.feedUTIType
}
guard let foundType = pasteboardType else {
return nil
}
guard let feedDictionary = pasteboardItem.propertyList(forType: foundType) as? [String: String] else {
return nil
}
self.init(dictionary: feedDictionary)
}
static func pasteboardFeeds(with pasteboard: NSPasteboard) -> Set<PasteboardFeed>? {
guard let items = pasteboard.pasteboardItems else {
return nil
}
let feeds = items.compactMap { PasteboardFeed(pasteboardItem: $0) }
return feeds.isEmpty ? nil : Set(feeds)
}
// MARK: - Writing
func exportDictionary() -> PasteboardFeedDictionary {
var d = PasteboardFeedDictionary()
d[Key.url] = url
d[Key.homePageURL] = homePageURL ?? ""
if let nameForDisplay = editedName ?? name {
d[Key.name] = nameForDisplay
}
return d
}
func internalDictionary() -> PasteboardFeedDictionary {
var d = PasteboardFeedDictionary()
d[PasteboardFeed.Key.feedID] = feedID
d[PasteboardFeed.Key.url] = url
if let homePageURL = homePageURL {
d[PasteboardFeed.Key.homePageURL] = homePageURL
}
if let name = name {
d[PasteboardFeed.Key.name] = name
}
if let editedName = editedName {
d[PasteboardFeed.Key.editedName] = editedName
}
if let accountID = accountID {
d[PasteboardFeed.Key.accountID] = accountID
}
return d
}
}
extension Feed: PasteboardWriterOwner {
public var pasteboardWriter: NSPasteboardWriting {
@ -26,20 +133,8 @@ extension Feed: PasteboardWriterOwner {
static let feedUTIInternal = "com.ranchero.NetNewsWire-Evergreen.internal.feed"
static let feedUTIInternalType = NSPasteboard.PasteboardType(rawValue: feedUTIInternal)
private struct Key {
static let url = "URL"
static let homePageURL = "homePageURL"
static let name = "name"
// Internal
static let accountID = "accountID"
static let feedID = "feedID"
static let editedName = "editedName"
}
init(feed: Feed) {
self.feed = feed
}
@ -69,91 +164,17 @@ extension Feed: PasteboardWriterOwner {
return plist
}
// MARK: - Dragged Feed
static func draggedFeeds(with pasteboard: NSPasteboard) -> Set<DraggedFeed>? {
guard let items = pasteboard.pasteboardItems else {
return nil
}
let feeds = items.compactMap { draggedFeed(with: $0) }
return feeds.isEmpty ? nil : Set(feeds)
}
}
private extension FeedPasteboardWriter {
func exportDictionary() -> [String: String] {
var d = [String: String]()
d[Key.url] = feed.url
d[Key.homePageURL] = feed.homePageURL ?? ""
d[Key.name] = feed.nameForDisplay
return d
let pasteboardFeed = PasteboardFeed(url: feed.url, feedID: feed.feedID, homePageURL: feed.homePageURL, name: feed.name, editedName: feed.editedName, accountID: feed.account?.accountID)
return pasteboardFeed.exportDictionary()
}
func internalDictionary() -> [String: Any] {
var d = [String: Any]()
d[Key.feedID] = feed.feedID
d[Key.url] = feed.url
if let homePageURL = feed.homePageURL {
d[Key.homePageURL] = homePageURL
}
if let name = feed.name {
d[Key.name] = name
}
if let editedName = feed.editedName {
d[Key.editedName] = editedName
}
if let accountID = feed.account?.accountID {
d[Key.accountID] = accountID
}
return d
}
static func draggedFeed(with dictionary: [String: String]) -> DraggedFeed? {
guard let url = dictionary[Key.url] else {
return nil
}
let homePageURL = dictionary[Key.homePageURL]
let name = dictionary[Key.name]
let accountID = dictionary[Key.accountID]
let feedID = dictionary[Key.feedID]
let editedName = dictionary[Key.editedName]
return DraggedFeed(url: url, feedID: feedID, homePageURL: homePageURL, name: name, editedName: editedName, accountID: accountID)
}
static func draggedFeed(with pasteboardItem: NSPasteboardItem) -> DraggedFeed? {
// TODO: This needs to handle strings and URLs also.
var pasteboardType: NSPasteboard.PasteboardType?
if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIInternalType) {
pasteboardType = FeedPasteboardWriter.feedUTIInternalType
}
else if pasteboardItem.types.contains(FeedPasteboardWriter.feedUTIType) {
pasteboardType = FeedPasteboardWriter.feedUTIType
}
guard let foundType = pasteboardType else {
return nil
}
let feedDictionary = pasteboardItem.propertyList(forType: foundType) as! [String: String]
return draggedFeed(with: feedDictionary)
let pasteboardFeed = PasteboardFeed(url: feed.url, feedID: feed.feedID, homePageURL: feed.homePageURL, name: feed.name, editedName: feed.editedName, accountID: feed.account?.accountID)
return pasteboardFeed.internalDictionary()
}
}
struct DraggedFeed: Hashable {
let url: String
let feedID: String?
let homePageURL: String?
let name: String?
let editedName: String?
let accountID: String?
}

View File

@ -54,7 +54,7 @@ import Account
return SidebarOutlineDataSource.dragOperationNone
}
guard let draggedFeeds = FeedPasteboardWriter.draggedFeeds(with: info.draggingPasteboard()) else {
guard let draggedFeeds = PasteboardFeed.pasteboardFeeds(with: info.draggingPasteboard()) else {
return SidebarOutlineDataSource.dragOperationNone
}
@ -100,7 +100,7 @@ private extension SidebarOutlineDataSource {
return node.representedObject is Feed
}
func validateLocalDrop(_ draggedFeeds: Set<DraggedFeed>, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
func validateLocalDrop(_ draggedFeeds: Set<PasteboardFeed>, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation {
// let parentNode = nodeForItem(item)