diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index 73519e152..f971da1ed 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -57,7 +57,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, private var addFeedController: AddFeedController? private var addFolderWindowController: AddFolderWindowController? private var importOPMLController: ImportOPMLWindowController? - private var exportOPMLController: ExportOPMLWindowController? + private var exportOPMLController: ExportOPMLController? private var keyboardShortcutsWindowController: WebViewWindowController? private var inspectorWindowController: InspectorWindowController? private var crashReportWindowController: CrashReportWindowController? // For testing only @@ -438,7 +438,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, return } - exportOPMLController = ExportOPMLWindowController() + exportOPMLController = ExportOPMLController() exportOPMLController?.runSheetOnWindow(mainWindowController!.window!) } diff --git a/Mac/MainWindow/OPML/ExportOPMLAccessoryView.xib b/Mac/MainWindow/OPML/ExportOPMLAccessoryView.xib new file mode 100644 index 000000000..49376a471 --- /dev/null +++ b/Mac/MainWindow/OPML/ExportOPMLAccessoryView.xib @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Choose the account with the subscriptions you’d like to export. Subscriptions are exported in the standard OPML format, which most RSS readers can import. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mac/MainWindow/OPML/ExportOPMLAccessoryViewController.swift b/Mac/MainWindow/OPML/ExportOPMLAccessoryViewController.swift new file mode 100644 index 000000000..639d88ec6 --- /dev/null +++ b/Mac/MainWindow/OPML/ExportOPMLAccessoryViewController.swift @@ -0,0 +1,58 @@ +// +// ExportOPMLAccessoryViewController.swift +// NetNewsWire +// +// Created by Nate Weaver on 2019-10-20. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import AppKit +import Account + +class ExportOPMLAccessoryViewController: NSViewController { + + @IBOutlet weak var accountPopUpButton: NSPopUpButton! + + var selectedAccount: Account? { + accountPopUpButton.selectedItem?.representedObject as? Account + } + + init() { + super.init(nibName: "ExportOPMLAccessoryView", bundle: nil) + } + + // MARK: - NSViewController + + required init?(coder: NSCoder) { + preconditionFailure("ExportOPMLAccessoryView.init(coder) not implemented by design.") + } + + override func viewDidLoad() { + accountPopUpButton.removeAllItems() + + let menu = NSMenu() + accountPopUpButton.menu = menu + + for oneAccount in AccountManager.shared.sortedAccounts { + + let oneMenuItem = NSMenuItem() + oneMenuItem.title = oneAccount.nameForDisplay + oneMenuItem.representedObject = oneAccount + menu.addItem(oneMenuItem) + + if oneAccount.accountID == AppDefaults.exportOPMLAccountID { + accountPopUpButton.select(oneMenuItem) + } + + } + } + + @IBAction func accountSelected(_ popUpButton: NSPopUpButton) { + NotificationCenter.default.post(name: .ExportOPMLSelectedAccountDidChange, object: self) + } +} + +extension Notification.Name { + static let ExportOPMLSelectedAccountDidChange = Notification.Name(rawValue: "SelectedAccountDidChange") + +} diff --git a/Mac/MainWindow/OPML/ExportOPMLController.swift b/Mac/MainWindow/OPML/ExportOPMLController.swift new file mode 100644 index 000000000..f5c671fcb --- /dev/null +++ b/Mac/MainWindow/OPML/ExportOPMLController.swift @@ -0,0 +1,62 @@ +// +// ExportOPMLController.swift +// NetNewsWire +// +// Created by Maurice Parker on 5/1/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import AppKit +import Account + +struct ExportOPMLController { + + func runSheetOnWindow(_ hostWindow: NSWindow) { + + let accessoryViewController = ExportOPMLAccessoryViewController() + let panel = NSSavePanel() + panel.allowedFileTypes = ["opml"] + panel.allowsOtherFileTypes = false + panel.prompt = NSLocalizedString("Export OPML", comment: "Export OPML") + panel.title = NSLocalizedString("Export OPML", comment: "Export OPML") + panel.nameFieldLabel = NSLocalizedString("Export to:", comment: "Export OPML") + panel.message = NSLocalizedString("Choose a location for the exported OPML file.", comment: "Export OPML") + panel.isExtensionHidden = false + panel.accessoryView = accessoryViewController.view + + let observer = NotificationCenter.default.addObserver(forName: .ExportOPMLSelectedAccountDidChange, object: nil, queue: OperationQueue.main) { notification in + self.updateNameFieldStringValueIfAppropriate(savePanel: panel, from: accessoryViewController) + } + + updateNameFieldStringValueIfAppropriate(savePanel: panel, from: accessoryViewController, force: true) + + panel.beginSheetModal(for: hostWindow) { result in + if result == NSApplication.ModalResponse.OK, let url = panel.url { + DispatchQueue.main.async { + guard let account = accessoryViewController.selectedAccount else { return } + let filename = url.lastPathComponent + let opmlString = OPMLExporter.OPMLString(with: account, title: filename) + do { + try opmlString.write(to: url, atomically: true, encoding: .utf8) + } + catch let error as NSError { + NSApplication.shared.presentError(error) + } + } + } + + NotificationCenter.default.removeObserver(observer) + } + + } + + private func updateNameFieldStringValueIfAppropriate(savePanel panel: NSSavePanel, from accessoryViewController: ExportOPMLAccessoryViewController, force: Bool = false) { + + if !force && !panel.nameFieldStringValue.hasPrefix("Subscriptions-") { return } + + guard let account = accessoryViewController.selectedAccount else { return } + let accountName = account.nameForDisplay.replacingOccurrences(of: " ", with: "").trimmingCharacters(in: .whitespaces) + panel.nameFieldStringValue = "Subscriptions-\(accountName).opml" + + } +} diff --git a/Mac/MainWindow/OPML/ExportOPMLSheet.xib b/Mac/MainWindow/OPML/ExportOPMLSheet.xib deleted file mode 100644 index e6bc9f85b..000000000 --- a/Mac/MainWindow/OPML/ExportOPMLSheet.xib +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Choose the account with the subscriptions you’d like to export. Subscriptions are exported in the standard OPML format, which most RSS readers can import. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Mac/MainWindow/OPML/ExportOPMLWindowController.swift b/Mac/MainWindow/OPML/ExportOPMLWindowController.swift deleted file mode 100644 index cdbda991c..000000000 --- a/Mac/MainWindow/OPML/ExportOPMLWindowController.swift +++ /dev/null @@ -1,106 +0,0 @@ -// -// ExportOPMLWindowController.swift -// NetNewsWire -// -// Created by Maurice Parker on 5/1/19. -// Copyright © 2019 Ranchero Software. All rights reserved. -// - -import AppKit -import Account - -class ExportOPMLWindowController: NSWindowController { - - @IBOutlet weak var accountPopUpButton: NSPopUpButton! - private weak var hostWindow: NSWindow? - - convenience init() { - self.init(windowNibName: NSNib.Name("ExportOPMLSheet")) - } - - override func windowDidLoad() { - accountPopUpButton.removeAllItems() - - let menu = NSMenu() - accountPopUpButton.menu = menu - - for oneAccount in AccountManager.shared.sortedAccounts { - - let oneMenuItem = NSMenuItem() - oneMenuItem.title = oneAccount.nameForDisplay - oneMenuItem.representedObject = oneAccount - menu.addItem(oneMenuItem) - - if oneAccount.accountID == AppDefaults.exportOPMLAccountID { - accountPopUpButton.select(oneMenuItem) - } - - } - } - - // MARK: API - - func runSheetOnWindow(_ hostWindow: NSWindow) { - - self.hostWindow = hostWindow - - if AccountManager.shared.accounts.count == 1 { - let account = AccountManager.shared.accounts.first! - exportOPML(account: account) - } else { - hostWindow.beginSheet(window!) - } - - } - - // MARK: Actions - - @IBAction func cancel(_ sender: Any) { - hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.cancel) - } - - @IBAction func exportOPML(_ sender: Any) { - - guard let menuItem = accountPopUpButton.selectedItem else { - return - } - - let account = menuItem.representedObject as! Account - AppDefaults.exportOPMLAccountID = account.accountID - hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.OK) - exportOPML(account: account) - - } - - func exportOPML(account: Account) { - - let panel = NSSavePanel() - panel.allowedFileTypes = ["opml"] - panel.allowsOtherFileTypes = false - panel.prompt = NSLocalizedString("Export OPML", comment: "Export OPML") - panel.title = NSLocalizedString("Export OPML", comment: "Export OPML") - panel.nameFieldLabel = NSLocalizedString("Export to:", comment: "Export OPML") - panel.message = NSLocalizedString("Choose a location for the exported OPML file.", comment: "Export OPML") - panel.isExtensionHidden = false - - let accountName = account.nameForDisplay.replacingOccurrences(of: " ", with: "").trimmingCharacters(in: .whitespaces) - panel.nameFieldStringValue = "Subscriptions-\(accountName).opml" - - panel.beginSheetModal(for: hostWindow!) { result in - if result == NSApplication.ModalResponse.OK, let url = panel.url { - DispatchQueue.main.async { - let filename = url.lastPathComponent - let opmlString = OPMLExporter.OPMLString(with: account, title: filename) - do { - try opmlString.write(to: url, atomically: true, encoding: String.Encoding.utf8) - } - catch let error as NSError { - NSApplication.shared.presentError(error) - } - } - } - } - - } - -} diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index c24b1e5d4..1a9fa9f4e 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -22,9 +22,8 @@ 5144EA362279FC3D00D19003 /* AccountsAddLocal.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5144EA352279FC3D00D19003 /* AccountsAddLocal.xib */; }; 5144EA382279FC6200D19003 /* AccountsAddLocalWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA372279FC6200D19003 /* AccountsAddLocalWindowController.swift */; }; 5144EA3B227A379E00D19003 /* ImportOPMLSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5144EA3A227A379E00D19003 /* ImportOPMLSheet.xib */; }; - 5144EA3D227A37AF00D19003 /* ExportOPMLSheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5144EA3C227A37AF00D19003 /* ExportOPMLSheet.xib */; }; 5144EA40227A37EC00D19003 /* ImportOPMLWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA3E227A37EC00D19003 /* ImportOPMLWindowController.swift */; }; - 5144EA43227A380F00D19003 /* ExportOPMLWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA42227A380F00D19003 /* ExportOPMLWindowController.swift */; }; + 5144EA43227A380F00D19003 /* ExportOPMLController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA42227A380F00D19003 /* ExportOPMLController.swift */; }; 5144EA51227B8E4500D19003 /* AccountsFeedbinWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5144EA4F227B8E4500D19003 /* AccountsFeedbinWindowController.swift */; }; 5144EA52227B8E4500D19003 /* AccountsFeedbin.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */; }; 51543685228F6753005E1CDF /* DetailAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51543684228F6753005E1CDF /* DetailAccountViewController.swift */; }; @@ -313,6 +312,8 @@ 84FB9A2F1EDCD6C4003D53B9 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; }; 84FB9A301EDCD6C4003D53B9 /* Sparkle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; }; + B24C4F7B235D39D40000B924 /* ExportOPMLAccessoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B24C4F7A235D39D40000B924 /* ExportOPMLAccessoryViewController.swift */; }; + B24C4F85235D39F30000B924 /* ExportOPMLAccessoryView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B24C4F84235D39F30000B924 /* ExportOPMLAccessoryView.xib */; }; D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553737C20186C1F006D8857 /* Article+Scriptability.swift */; }; D57BE6E0204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57BE6DF204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift */; }; D5907D7F2004AC00005947E5 /* NSApplication+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5907D7E2004AC00005947E5 /* NSApplication+Scriptability.swift */; }; @@ -685,9 +686,8 @@ 5144EA352279FC3D00D19003 /* AccountsAddLocal.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountsAddLocal.xib; sourceTree = ""; }; 5144EA372279FC6200D19003 /* AccountsAddLocalWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsAddLocalWindowController.swift; sourceTree = ""; }; 5144EA3A227A379E00D19003 /* ImportOPMLSheet.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ImportOPMLSheet.xib; sourceTree = ""; }; - 5144EA3C227A37AF00D19003 /* ExportOPMLSheet.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ExportOPMLSheet.xib; sourceTree = ""; }; 5144EA3E227A37EC00D19003 /* ImportOPMLWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportOPMLWindowController.swift; sourceTree = ""; }; - 5144EA42227A380F00D19003 /* ExportOPMLWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportOPMLWindowController.swift; sourceTree = ""; }; + 5144EA42227A380F00D19003 /* ExportOPMLController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportOPMLController.swift; sourceTree = ""; }; 5144EA4F227B8E4500D19003 /* AccountsFeedbinWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsFeedbinWindowController.swift; sourceTree = ""; }; 5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AccountsFeedbin.xib; sourceTree = ""; }; 51543684228F6753005E1CDF /* DetailAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailAccountViewController.swift; sourceTree = ""; }; @@ -925,6 +925,8 @@ 84F9EAE4213660A100CF2DE4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Vendor/Sparkle.framework; sourceTree = SOURCE_ROOT; }; 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconURLFinder.swift; sourceTree = ""; }; + B24C4F7A235D39D40000B924 /* ExportOPMLAccessoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportOPMLAccessoryViewController.swift; sourceTree = ""; }; + B24C4F84235D39F30000B924 /* ExportOPMLAccessoryView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ExportOPMLAccessoryView.xib; sourceTree = ""; }; B24EFD482330FF99006C6242 /* NetNewsWire-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NetNewsWire-Bridging-Header.h"; sourceTree = ""; }; B24EFD5923310109006C6242 /* WKPreferencesPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKPreferencesPrivate.h; sourceTree = ""; }; D553737C20186C1F006D8857 /* Article+Scriptability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Article+Scriptability.swift"; sourceTree = ""; }; @@ -1035,8 +1037,9 @@ children = ( 5144EA3A227A379E00D19003 /* ImportOPMLSheet.xib */, 5144EA3E227A37EC00D19003 /* ImportOPMLWindowController.swift */, - 5144EA3C227A37AF00D19003 /* ExportOPMLSheet.xib */, - 5144EA42227A380F00D19003 /* ExportOPMLWindowController.swift */, + 5144EA42227A380F00D19003 /* ExportOPMLController.swift */, + B24C4F7A235D39D40000B924 /* ExportOPMLAccessoryViewController.swift */, + B24C4F84235D39F30000B924 /* ExportOPMLAccessoryView.xib */, ); path = OPML; sourceTree = ""; @@ -1989,12 +1992,12 @@ ORGANIZATIONNAME = "Ranchero Software"; TargetAttributes = { 6581C73220CED60000F4AD34 = { - DevelopmentTeam = M8L2WTLA8W; - ProvisioningStyle = Manual; + DevelopmentTeam = M72QZ9W58G; + ProvisioningStyle = Automatic; }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; - DevelopmentTeam = M8L2WTLA8W; + DevelopmentTeam = M72QZ9W58G; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { @@ -2010,8 +2013,8 @@ }; 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = M8L2WTLA8W; - ProvisioningStyle = Manual; + DevelopmentTeam = M72QZ9W58G; + ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.HardenedRuntime = { enabled = 1; @@ -2020,7 +2023,7 @@ }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 9C84TZ7Q6Z; + DevelopmentTeam = M72QZ9W58G; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; @@ -2286,9 +2289,9 @@ 5144EA3B227A379E00D19003 /* ImportOPMLSheet.xib in Resources */, 844B5B691FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist in Resources */, 84A3EE5F223B667F00557320 /* DefaultFeeds.opml in Resources */, + B24C4F85235D39F30000B924 /* ExportOPMLAccessoryView.xib in Resources */, 8459D0F92355794C0050076F /* NNW3OpenPanelAccessoryView.xib in Resources */, 84C9FC8222629E4800D921D6 /* Preferences.storyboard in Resources */, - 5144EA3D227A37AF00D19003 /* ExportOPMLSheet.xib in Resources */, 849C64681ED37A5D003D8FC0 /* Assets.xcassets in Resources */, 848362FD2262A30800DA1D35 /* styleSheet.css in Resources */, 8483630B2262A3F000DA1D35 /* RenameSheet.xib in Resources */, @@ -2497,6 +2500,7 @@ 849A97671ED9EB96007D329B /* UnreadCountView.swift in Sources */, 840BEE4121D70E64009BBAFA /* CrashReportWindowController.swift in Sources */, 8426118A1FCB67AA0086A189 /* FeedIconDownloader.swift in Sources */, + B24C4F7B235D39D40000B924 /* ExportOPMLAccessoryViewController.swift in Sources */, 84C9FC7B22629E1200D921D6 /* AccountsControlsBackgroundView.swift in Sources */, 84162A152038C12C00035290 /* MarkCommandValidationStatus.swift in Sources */, 84E95D241FB1087500552D99 /* ArticlePasteboardWriter.swift in Sources */, @@ -2507,7 +2511,7 @@ 849A97891ED9ECEF007D329B /* ArticleStyle.swift in Sources */, 84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */, 84B7178C201E66580091657D /* SidebarViewController+ContextualMenus.swift in Sources */, - 5144EA43227A380F00D19003 /* ExportOPMLWindowController.swift in Sources */, + 5144EA43227A380F00D19003 /* ExportOPMLController.swift in Sources */, 842611A21FCB769D0086A189 /* RSHTMLMetadata+Extension.swift in Sources */, 84A1500520048DDF0046AD9A /* SendToMarsEditCommand.swift in Sources */, D5907DB22004BB37005947E5 /* ScriptingObjectContainer.swift in Sources */,