diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 4e8bee760..2e7e8bdf2 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; }; + 51EC114C2149FE3300B296E3 /* FolderTreeMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51EC114B2149FE3300B296E3 /* FolderTreeMenu.swift */; }; 6581C73820CED60100F4AD34 /* SafariExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6581C73720CED60100F4AD34 /* SafariExtensionHandler.swift */; }; 6581C73A20CED60100F4AD34 /* SafariExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6581C73920CED60100F4AD34 /* SafariExtensionViewController.swift */; }; 6581C73D20CED60100F4AD34 /* SafariExtensionViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6581C73B20CED60100F4AD34 /* SafariExtensionViewController.xib */; }; @@ -480,6 +481,7 @@ /* Begin PBXFileReference section */ 519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = ""; }; + 51EC114B2149FE3300B296E3 /* FolderTreeMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FolderTreeMenu.swift; path = AddFeed/FolderTreeMenu.swift; sourceTree = ""; }; 6581C73320CED60000F4AD34 /* Subscribe to Feed.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Subscribe to Feed.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; 6581C73420CED60100F4AD34 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 6581C73720CED60100F4AD34 /* SafariExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariExtensionHandler.swift; sourceTree = ""; }; @@ -952,6 +954,7 @@ 849A97511ED9EAC0007D329B /* AddFeedController.swift */, 849A97521ED9EAC0007D329B /* AddFeedWindowController.swift */, 849A97A01ED9F180007D329B /* InitialFeedDownloader.swift */, + 51EC114B2149FE3300B296E3 /* FolderTreeMenu.swift */, 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */, ); name = "Add Feed"; @@ -1528,8 +1531,8 @@ ORGANIZATIONNAME = "Ranchero Software"; TargetAttributes = { 6581C73220CED60000F4AD34 = { - DevelopmentTeam = M8L2WTLA8W; - ProvisioningStyle = Manual; + DevelopmentTeam = SHJK2V3AJG; + ProvisioningStyle = Automatic; }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; @@ -1550,12 +1553,12 @@ }; 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = M8L2WTLA8W; - ProvisioningStyle = Manual; + DevelopmentTeam = SHJK2V3AJG; + ProvisioningStyle = Automatic; }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 9C84TZ7Q6Z; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; @@ -2005,6 +2008,7 @@ 84B99C691FAE36B800ECDEDB /* FeedListFolder.swift in Sources */, 84411E711FE5FBFA004B527F /* SmallIconProvider.swift in Sources */, 844B5B591FE9FE4F00C7C76A /* SidebarKeyboardDelegate.swift in Sources */, + 51EC114C2149FE3300B296E3 /* FolderTreeMenu.swift in Sources */, 849A97A31ED9F180007D329B /* FolderTreeControllerDelegate.swift in Sources */, 84A1500320048D660046AD9A /* SendToCommand.swift in Sources */, 845A29091FC74B8E007B49E3 /* SingleFaviconDownloader.swift in Sources */, diff --git a/NetNewsWire/FeedList/FeedList.storyboard b/NetNewsWire/FeedList/FeedList.storyboard index 480c7b595..8b7413366 100644 --- a/NetNewsWire/FeedList/FeedList.storyboard +++ b/NetNewsWire/FeedList/FeedList.storyboard @@ -1,15 +1,16 @@ - + - + + - - + + @@ -32,25 +33,25 @@ - + - + - + - - + + - + @@ -81,7 +82,7 @@ - + @@ -120,21 +121,24 @@ - - + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - + + + + + + + + + + - + - + + diff --git a/NetNewsWire/FeedList/FeedListViewController.swift b/NetNewsWire/FeedList/FeedListViewController.swift index 6d7ca1790..c4259c318 100644 --- a/NetNewsWire/FeedList/FeedListViewController.swift +++ b/NetNewsWire/FeedList/FeedListViewController.swift @@ -7,6 +7,7 @@ // import AppKit +import Account import RSTree import RSCore @@ -22,10 +23,12 @@ struct FeedListUserInfoKey { final class FeedListViewController: NSViewController { - @IBOutlet var outlineView: NSOutlineView! - @IBOutlet var openHomePageButton: NSButton! - @IBOutlet var addToFeedsButton: NSButton! - + @IBOutlet weak var outlineView: NSOutlineView! + @IBOutlet weak var openHomePageButton: NSButton! + @IBOutlet weak var addToFeedsButton: NSButton! + @IBOutlet weak var folderPopupButton: NSPopUpButton! + + fileprivate var folderTreeController: TreeController? private var sidebarCellAppearance: SidebarCellAppearance! private let treeControllerDelegate = FeedListTreeControllerDelegate() lazy var treeController: TreeController = { @@ -51,16 +54,24 @@ final class FeedListViewController: NSViewController { sidebarCellAppearance = SidebarCellAppearance(theme: appDelegate.currentTheme, fontSize: AppDefaults.shared.sidebarFontSize) NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(childrenDidChange(_:)), name: .ChildrenDidChange, object: nil) + outlineView.needsLayout = true - updateUI() + + updateFolderMenu() + updateButtons() + } // MARK: - Notifications - @objc func faviconDidBecomeAvailable(_ note: Notification) { - configureAvailableCells() } + + @objc func childrenDidChange(_ note: Notification) { + updateFolderMenu() + } + } // MARK: Actions @@ -77,6 +88,51 @@ extension FeedListViewController { @IBAction func addToFeeds(_ sender: Any?) { + guard let container = folderPopupButton.selectedItem?.representedObject as? Container else { + assertionFailure("Expected the folderPopupButton to have a container.") + return + } + + var account: Account? + var folder: Folder? + if container is Folder { + folder = (container as! Folder) + account = folder!.account + } else { + account = (container as! Account) + } + + for selectedObject in selectedObjects { + + guard let feedListFeed = selectedObject as? FeedListFeed else { + continue + } + + if account!.hasFeed(withURL: feedListFeed.url) { + continue + } + + guard let feed = account!.createFeed(with: feedListFeed.nameForDisplay, editedName: nil, url: feedListFeed.url) else { + continue + } + + guard let url = URL(string: feedListFeed.url) else { + assertionFailure("Malformed URL string: \(feedListFeed.url).") + continue + } + + if account!.addFeed(feed, to: folder) { + NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed]) + } + + InitialFeedDownloader.download(url) { (parsedFeed) in + if let parsedFeed = parsedFeed { + account!.update(feed, with: parsedFeed, {}) + } + } + + } + } } @@ -124,8 +180,8 @@ extension FeedListViewController: NSOutlineViewDelegate { func outlineViewSelectionDidChange(_ notification: Notification) { - updateUI() - + updateButtons() + let selectedRow = self.outlineView.selectedRow if selectedRow < 0 || selectedRow == NSNotFound { @@ -211,11 +267,15 @@ private extension FeedListViewController { NotificationCenter.default.post(name: .FeedListSidebarSelectionDidChange, object: self, userInfo: userInfo) } - func updateUI() { - - updateButtons() + func updateFolderMenu() { + + let rootNode = Node(representedObject: AccountManager.shared.localAccount, parent: nil) + rootNode.canHaveChildNodes = true + folderTreeController = TreeController(delegate: FolderTreeControllerDelegate(), rootNode: rootNode) + + folderPopupButton.menu = FolderTreeMenu.createFolderPopupMenu(with: folderTreeController!.rootNode) } - + func updateButtons() { let objects = selectedObjects @@ -223,10 +283,12 @@ private extension FeedListViewController { if objects.isEmpty { openHomePageButton.isEnabled = false addToFeedsButton.isEnabled = false + folderPopupButton.isEnabled = false return } addToFeedsButton.isEnabled = true + folderPopupButton.isEnabled = true if let _ = singleSelectedHomePageURL() { openHomePageButton.isEnabled = true diff --git a/NetNewsWire/MainWindow/AddFeed/AddFeedWindowController.swift b/NetNewsWire/MainWindow/AddFeed/AddFeedWindowController.swift index 78e3f92b0..451f801d7 100644 --- a/NetNewsWire/MainWindow/AddFeed/AddFeedWindowController.swift +++ b/NetNewsWire/MainWindow/AddFeed/AddFeedWindowController.swift @@ -68,7 +68,7 @@ class AddFeedWindowController : NSWindowController { nameTextField.stringValue = initialName } - folderPopupButton.menu = createFolderPopupMenu() + folderPopupButton.menu = FolderTreeMenu.createFolderPopupMenu(with: folderTreeController.rootNode) updateUI() } @@ -139,35 +139,4 @@ private extension AddFeedWindowController { return folderPopupButton.selectedItem?.representedObject as? Container } - func createFolderPopupMenu() -> NSMenu { - - let menu = NSMenu(title: "Folders") - - let menuItem = NSMenuItem(title: NSLocalizedString("Top Level", comment: "Add Feed Sheet"), action: nil, keyEquivalent: "") - menuItem.representedObject = folderTreeController.rootNode.representedObject - menu.addItem(menuItem) - - let childNodes = folderTreeController.rootNode.childNodes - addFolderItemsToMenuWithNodes(menu: menu, nodes: childNodes, indentationLevel: 1) - - return menu - } - - func addFolderItemsToMenuWithNodes(menu: NSMenu, nodes: [Node], indentationLevel: Int) { - - nodes.forEach { (oneNode) in - - if let nameProvider = oneNode.representedObject as? DisplayNameProvider { - - let menuItem = NSMenuItem(title: nameProvider.nameForDisplay, action: nil, keyEquivalent: "") - menuItem.indentationLevel = indentationLevel - menuItem.representedObject = oneNode.representedObject - menu.addItem(menuItem) - - if oneNode.numberOfChildNodes > 0 { - addFolderItemsToMenuWithNodes(menu: menu, nodes: oneNode.childNodes, indentationLevel: indentationLevel + 1) - } - } - } - } } diff --git a/NetNewsWire/MainWindow/AddFeed/FolderTreeMenu.swift b/NetNewsWire/MainWindow/AddFeed/FolderTreeMenu.swift new file mode 100644 index 000000000..5b32307d3 --- /dev/null +++ b/NetNewsWire/MainWindow/AddFeed/FolderTreeMenu.swift @@ -0,0 +1,47 @@ +// +// FolderTreeMenu.swift +// NetNewsWire +// +// Created by Maurice Parker on 9/12/18. +// Copyright © 2018 Ranchero Software, LLC. All rights reserved. +// + +import AppKit +import RSCore +import RSTree + +class FolderTreeMenu { + + static func createFolderPopupMenu(with rootNode: Node) -> NSMenu { + + let menu = NSMenu(title: "Folders") + + let menuItem = NSMenuItem(title: NSLocalizedString("Top Level", comment: "Add Feed Sheet"), action: nil, keyEquivalent: "") + menuItem.representedObject = rootNode.representedObject + menu.addItem(menuItem) + + let childNodes = rootNode.childNodes + addFolderItemsToMenuWithNodes(menu: menu, nodes: childNodes, indentationLevel: 1) + + return menu + } + + private static func addFolderItemsToMenuWithNodes(menu: NSMenu, nodes: [Node], indentationLevel: Int) { + + nodes.forEach { (oneNode) in + + if let nameProvider = oneNode.representedObject as? DisplayNameProvider { + + let menuItem = NSMenuItem(title: nameProvider.nameForDisplay, action: nil, keyEquivalent: "") + menuItem.indentationLevel = indentationLevel + menuItem.representedObject = oneNode.representedObject + menu.addItem(menuItem) + + if oneNode.numberOfChildNodes > 0 { + addFolderItemsToMenuWithNodes(menu: menu, nodes: oneNode.childNodes, indentationLevel: indentationLevel + 1) + } + } + } + } + +}