diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 029e361d3..51d72cc1b 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -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 = ""; }; 845EE7B01FC2366500854A1F /* StarredFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarredFeedDelegate.swift; sourceTree = ""; }; 845EE7C01FC2488C00854A1F /* SmartFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartFeed.swift; sourceTree = ""; }; - 845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedPasteboardWriter.swift; sourceTree = ""; }; 846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; sourceTree = ""; }; 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkStatusCommand.swift; sourceTree = ""; }; 8472058020142E8900AD578B /* FeedInspectorViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedInspectorViewController.swift; sourceTree = ""; }; 84754C89213E471B009CFDFB /* GeneralPrefencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = GeneralPrefencesViewController.swift; path = NetNewsWire/Preferences/GeneralPrefencesViewController.swift; sourceTree = ""; }; 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 = ""; }; + 848D578D21543519005FFAD5 /* PasteboardFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardFeed.swift; sourceTree = ""; }; 848F6AE41FC29CFA002D422E /* FaviconDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconDownloader.swift; sourceTree = ""; }; 849A97421ED9EAA9007D329B /* AddFolderWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddFolderWindowController.swift; sourceTree = ""; }; 849A97511ED9EAC0007D329B /* AddFeedController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AddFeedController.swift; path = AddFeed/AddFeedController.swift; sourceTree = ""; }; @@ -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 */, diff --git a/NetNewsWire/MainWindow/Sidebar/FeedPasteboardWriter.swift b/NetNewsWire/MainWindow/Sidebar/PasteboardFeed.swift similarity index 54% rename from NetNewsWire/MainWindow/Sidebar/FeedPasteboardWriter.swift rename to NetNewsWire/MainWindow/Sidebar/PasteboardFeed.swift index 92390f0c5..79a008f5b 100644 --- a/NetNewsWire/MainWindow/Sidebar/FeedPasteboardWriter.swift +++ b/NetNewsWire/MainWindow/Sidebar/PasteboardFeed.swift @@ -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? { + 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? { - 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? -} diff --git a/NetNewsWire/MainWindow/Sidebar/SidebarOutlineDataSource.swift b/NetNewsWire/MainWindow/Sidebar/SidebarOutlineDataSource.swift index 13bb5de72..d4bd75f35 100644 --- a/NetNewsWire/MainWindow/Sidebar/SidebarOutlineDataSource.swift +++ b/NetNewsWire/MainWindow/Sidebar/SidebarOutlineDataSource.swift @@ -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, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { + func validateLocalDrop(_ draggedFeeds: Set, proposedItem item: Any?, proposedChildIndex index: Int) -> NSDragOperation { // let parentNode = nodeForItem(item)