From 1cd21892c12dea44e50ae456cf91569031a0899b Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Sun, 25 Sep 2022 15:27:06 -0500 Subject: [PATCH 01/16] Support copying multiple article URLs Requires and RSCore update. --- ...melineViewController+ContextualMenus.swift | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index 4a50eadf7..f15320591 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -97,10 +97,10 @@ extension TimelineViewController { } @objc func copyURLFromContextualMenu(_ sender: Any?) { - guard let menuItem = sender as? NSMenuItem, let urlString = menuItem.representedObject as? String else { + guard let menuItem = sender as? NSMenuItem, let urlStrings = menuItem.representedObject as? [String] else { return } - URLPasteboardWriter.write(urlString: urlString, to: .general) + URLPasteboardWriter.write(urlStrings: urlStrings, to: .general) } @objc func performShareServiceFromContextualMenu(_ sender: Any?) { @@ -180,10 +180,15 @@ private extension TimelineViewController { if articles.count == 1, let link = articles.first!.preferredLink { menu.addSeparatorIfNeeded() menu.addItem(openInBrowserMenuItem(link)) + } + + let links = articles.compactMap { $0.preferredLink } + + if links.count > 0 { menu.addSeparatorIfNeeded() - menu.addItem(copyArticleURLMenuItem(link)) - - if let externalLink = articles.first?.externalLink, externalLink != link { + menu.addItem(copyArticleURLsMenuItem(links)) + + if let externalLink = articles.first?.externalLink, externalLink != links.first { menu.addItem(copyExternalURLMenuItem(externalLink)) } } @@ -279,8 +284,8 @@ private extension TimelineViewController { return menuItem(NSLocalizedString("Open in Browser", comment: "Command"), #selector(openInBrowserFromContextualMenu(_:)), urlString) } - func copyArticleURLMenuItem(_ urlString: String) -> NSMenuItem { - return menuItem(NSLocalizedString("Copy Article URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), urlString) + func copyArticleURLsMenuItem(_ urlStrings: [String]) -> NSMenuItem { + return menuItem(NSLocalizedString("Copy Article URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), urlStrings) } func copyExternalURLMenuItem(_ urlString: String) -> NSMenuItem { From 93afee7e3cc1125f40aa161db9159224d9645ccf Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Tue, 27 Sep 2022 17:39:05 -0500 Subject: [PATCH 02/16] Use plural title for multiple URLs --- ...melineViewController+ContextualMenus.swift | 4 +++- Mac/Resources/Localizable.stringsdict | 22 +++++++++++++++++++ NetNewsWire.xcodeproj/project.pbxproj | 4 ++++ 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 Mac/Resources/Localizable.stringsdict diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index f15320591..7a4020456 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -285,7 +285,9 @@ private extension TimelineViewController { } func copyArticleURLsMenuItem(_ urlStrings: [String]) -> NSMenuItem { - return menuItem(NSLocalizedString("Copy Article URL", comment: "Command"), #selector(copyURLFromContextualMenu(_:)), urlStrings) + let format = NSLocalizedString("Copy Article URL", comment: "Command") + let title = String.localizedStringWithFormat(format, urlStrings.count) + return menuItem(title, #selector(copyURLFromContextualMenu(_:)), urlStrings) } func copyExternalURLMenuItem(_ urlString: String) -> NSMenuItem { diff --git a/Mac/Resources/Localizable.stringsdict b/Mac/Resources/Localizable.stringsdict new file mode 100644 index 000000000..d914ec0e6 --- /dev/null +++ b/Mac/Resources/Localizable.stringsdict @@ -0,0 +1,22 @@ + + + + + Copy Article URL + + NSStringLocalizedFormatKey + %#@copy_article_url@ + copy_article_url + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + other + Copy Article URLs + one + Copy Article URL + + + + diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index ac0592c71..f3bcf0fc3 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -818,6 +818,7 @@ 84F9EAF4213660A100CF2DE4 /* testGenericScript.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAE1213660A100CF2DE4 /* testGenericScript.applescript */; }; 84F9EAF5213660A100CF2DE4 /* establishMainWindowStartingState.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAE2213660A100CF2DE4 /* establishMainWindowStartingState.applescript */; }; 84FF69B11FC3793300DC198E /* FaviconURLFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */; }; + B20180AB28E3B76F0059686A /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = B20180AA28E3B76F0059686A /* Localizable.stringsdict */; }; B24E9ADC245AB88400DA5718 /* NSAttributedString+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = B24E9ABA245AB88300DA5718 /* NSAttributedString+NetNewsWire.swift */; }; B24E9ADD245AB88400DA5718 /* NSAttributedString+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = B24E9ABA245AB88300DA5718 /* NSAttributedString+NetNewsWire.swift */; }; B24E9ADE245AB88400DA5718 /* NSAttributedString+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = B24E9ABA245AB88300DA5718 /* NSAttributedString+NetNewsWire.swift */; }; @@ -1565,6 +1566,7 @@ 84F9EAE2213660A100CF2DE4 /* establishMainWindowStartingState.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = establishMainWindowStartingState.applescript; sourceTree = ""; }; 84F9EAE4213660A100CF2DE4 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 84FF69B01FC3793300DC198E /* FaviconURLFinder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconURLFinder.swift; sourceTree = ""; }; + B20180AA28E3B76F0059686A /* Localizable.stringsdict */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.stringsdict; path = Localizable.stringsdict; sourceTree = ""; }; B24E9ABA245AB88300DA5718 /* NSAttributedString+NetNewsWire.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+NetNewsWire.swift"; 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 = ""; }; @@ -2673,6 +2675,7 @@ children = ( 849C64671ED37A5D003D8FC0 /* Assets.xcassets */, 84C9FC8922629E8F00D921D6 /* Credits.rtf */, + B20180AA28E3B76F0059686A /* Localizable.stringsdict */, 84C9FC8A22629E8F00D921D6 /* NetNewsWire.sdef */, 84C9FC9022629ECB00D921D6 /* NetNewsWire.entitlements */, 51F805D32428499E0022C792 /* NetNewsWire-dev.entitlements */, @@ -3520,6 +3523,7 @@ BDCB516724282C8A00102A80 /* AccountsNewsBlur.xib in Resources */, 514A89A2244FD63F0085E65D /* AddTwitterFeedSheet.xib in Resources */, 5103A9982421643300410853 /* blank.html in Resources */, + B20180AB28E3B76F0059686A /* Localizable.stringsdict in Resources */, 515A516E243E7F950089E588 /* ExtensionPointDetail.xib in Resources */, 84BAE64921CEDAF20046DB56 /* CrashReporterWindow.xib in Resources */, 51DEE81226FB9233006DAA56 /* Appanoose.nnwtheme in Resources */, From a129fae73c45e88882e90823e4329c8b973cd2d2 Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Tue, 27 Sep 2022 17:48:00 -0500 Subject: [PATCH 03/16] Copy multiple article URLs from the Edit menu as well --- Mac/MainWindow/MainWindowController.swift | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index 0eef8995b..7ca1a1166 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -204,7 +204,15 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { public func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool { if item.action == #selector(copyArticleURL(_:)) { - return canCopyArticleURL() + let canCopyArticleURL = canCopyArticleURL() + + if let item = item as? NSMenuItem { + let format = NSLocalizedString("Copy Article URL", comment: "Copy Article URL"); + + item.title = String.localizedStringWithFormat(format, selectedArticles?.count ?? 0) + } + + return canCopyArticleURL } if item.action == #selector(copyExternalURL(_:)) { @@ -321,14 +329,14 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { } @IBAction func copyArticleURL(_ sender: Any?) { - if let link = oneSelectedArticle?.preferredURL?.absoluteString { - URLPasteboardWriter.write(urlString: link, to: .general) + if let links = selectedArticles?.compactMap({ $0.preferredLink }) { + URLPasteboardWriter.write(urlStrings: links, to: .general) } } @IBAction func copyExternalURL(_ sender: Any?) { - if let link = oneSelectedArticle?.externalLink { - URLPasteboardWriter.write(urlString: link, to: .general) + if let links = selectedArticles?.compactMap({ $0.preferredLink }) { + URLPasteboardWriter.write(urlStrings: links, to: .general) } } @@ -1060,7 +1068,7 @@ private extension MainWindowController { } var currentLink: String? { - return oneSelectedArticle?.preferredLink + return selectedArticles?.first { $0.preferredLink != nil }?.preferredLink } // MARK: - State Restoration From f2abbf6da8e5dab9e7ae75ff46f4c8804e12d52c Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Fri, 7 Oct 2022 17:56:50 -0500 Subject: [PATCH 04/16] Use ld instead of d --- Mac/Resources/Localizable.stringsdict | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mac/Resources/Localizable.stringsdict b/Mac/Resources/Localizable.stringsdict index d914ec0e6..bbe875a1f 100644 --- a/Mac/Resources/Localizable.stringsdict +++ b/Mac/Resources/Localizable.stringsdict @@ -11,7 +11,7 @@ NSStringFormatSpecTypeKey NSStringPluralRuleType NSStringFormatValueTypeKey - d + ld other Copy Article URLs one From 3420346fa3e2796b80b9eba0473149538af38fdc Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Fri, 7 Oct 2022 17:57:08 -0500 Subject: [PATCH 05/16] Support opening multiple articles in the browser --- ...melineViewController+ContextualMenus.swift | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index 7a4020456..04b5ea554 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -90,10 +90,33 @@ extension TimelineViewController { @objc func openInBrowserFromContextualMenu(_ sender: Any?) { - guard let menuItem = sender as? NSMenuItem, let urlString = menuItem.representedObject as? String else { + guard let menuItem = sender as? NSMenuItem, let urlStrings = menuItem.representedObject as? [String] else { return } - Browser.open(urlString, inBackground: false) + + func doOpenURLs() { + for urlString in urlStrings { + Browser.open(urlString, inBackground: false) + } + } + + if urlStrings.count > 20 { + let alert = NSAlert() + let messageFormat = NSLocalizedString("Are you sure you want to open %ld articles in your browser?", comment: "") + alert.messageText = String.localizedStringWithFormat(messageFormat, urlStrings.count) + alert.addButton(withTitle: NSLocalizedString("Open", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + + guard let window = self.view.window else { return } + + alert.beginSheetModal(for: window) { response in + if response == .alertFirstButtonReturn { + doOpenURLs() + } + } + } else { + doOpenURLs() + } } @objc func copyURLFromContextualMenu(_ sender: Any?) { @@ -176,15 +199,13 @@ private extension TimelineViewController { menu.addItem(markAllMenuItem) } } - - if articles.count == 1, let link = articles.first!.preferredLink { - menu.addSeparatorIfNeeded() - menu.addItem(openInBrowserMenuItem(link)) - } let links = articles.compactMap { $0.preferredLink } if links.count > 0 { + menu.addSeparatorIfNeeded() + menu.addItem(openInBrowserMenuItem(links)) + menu.addSeparatorIfNeeded() menu.addItem(copyArticleURLsMenuItem(links)) @@ -279,9 +300,9 @@ private extension TimelineViewController { return menuItem(menuText, #selector(markAllInFeedAsRead(_:)), articles) } - func openInBrowserMenuItem(_ urlString: String) -> NSMenuItem { + func openInBrowserMenuItem(_ urlStrings: [String]) -> NSMenuItem { - return menuItem(NSLocalizedString("Open in Browser", comment: "Command"), #selector(openInBrowserFromContextualMenu(_:)), urlString) + return menuItem(NSLocalizedString("Open in Browser", comment: "Command"), #selector(openInBrowserFromContextualMenu(_:)), urlStrings) } func copyArticleURLsMenuItem(_ urlStrings: [String]) -> NSMenuItem { From 2fbc2fb628c20841cb5e77f71e761ad0f79c50e9 Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Fri, 7 Oct 2022 18:48:10 -0500 Subject: [PATCH 06/16] Support opening multiple articles in browser from main menu Move openArticleURLs() to MainWindowController. --- Mac/MainWindow/MainWindowController.swift | 32 +++++++++++++++++-- ...melineViewController+ContextualMenus.swift | 26 ++------------- .../Timeline/TimelineViewController.swift | 9 +++--- 3 files changed, 35 insertions(+), 32 deletions(-) diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index 7ca1a1166..7052e8efb 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -340,10 +340,36 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { } } + func openArticleURLs(_ urlStrings: [String]) { + func doOpenURLs() { + for urlString in urlStrings { + Browser.open(urlString, inBackground: false) + } + } + + if urlStrings.count > 20 { + let alert = NSAlert() + let messageFormat = NSLocalizedString("Are you sure you want to open %ld articles in your browser?", comment: "") + alert.messageText = String.localizedStringWithFormat(messageFormat, urlStrings.count) + alert.addButton(withTitle: NSLocalizedString("Open", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + + guard let window = self.window else { return } + + alert.beginSheetModal(for: window) { response in + if response == .alertFirstButtonReturn { + doOpenURLs() + } + } + } else { + doOpenURLs() + } + } + @IBAction func openArticleInBrowser(_ sender: Any?) { - if let link = currentLink { - Browser.open(link, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false) - } + guard let selectedArticles else { return } + let urlStrings = selectedArticles.compactMap { $0.preferredLink } + openArticleURLs(urlStrings) } @IBAction func openInBrowser(_ sender: Any?) { diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index 04b5ea554..aca01d921 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -89,34 +89,12 @@ extension TimelineViewController { } @objc func openInBrowserFromContextualMenu(_ sender: Any?) { - guard let menuItem = sender as? NSMenuItem, let urlStrings = menuItem.representedObject as? [String] else { return } - func doOpenURLs() { - for urlString in urlStrings { - Browser.open(urlString, inBackground: false) - } - } - - if urlStrings.count > 20 { - let alert = NSAlert() - let messageFormat = NSLocalizedString("Are you sure you want to open %ld articles in your browser?", comment: "") - alert.messageText = String.localizedStringWithFormat(messageFormat, urlStrings.count) - alert.addButton(withTitle: NSLocalizedString("Open", comment: "")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) - - guard let window = self.view.window else { return } - - alert.beginSheetModal(for: window) { response in - if response == .alertFirstButtonReturn { - doOpenURLs() - } - } - } else { - doOpenURLs() - } + guard let windowController = self.view.window?.windowController as? MainWindowController else { return } + windowController.openArticleURLs(urlStrings) } @objc func copyURLFromContextualMenu(_ sender: Any?) { diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index 27b303339..5b024edbc 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -315,9 +315,9 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr // MARK: - Actions @objc func openArticleInBrowser(_ sender: Any?) { - if let link = oneSelectedArticle?.preferredLink { - Browser.open(link, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false) - } + guard let windowController = self.view.window?.windowController as? MainWindowController else { return } + let urlStrings = selectedArticles.compactMap { $0.preferredLink } + windowController.openArticleURLs(urlStrings) } @IBAction func toggleStatusOfSelectedArticles(_ sender: Any?) { @@ -779,8 +779,7 @@ extension TimelineViewController: NSUserInterfaceValidations { item.title = Browser.titleForOpenInBrowserInverted } - let currentLink = oneSelectedArticle?.preferredLink - return currentLink != nil + return selectedArticles.first { $0.preferredLink != nil } != nil } if item.action == #selector(copy(_:)) { From fd75387d9cc012e812ce67c72a2e4f92ef0ad3e9 Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Fri, 7 Oct 2022 18:48:58 -0500 Subject: [PATCH 07/16] Don't do anything if there are more than 500 URLs --- Mac/MainWindow/MainWindowController.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index 7052e8efb..3b6f23221 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -341,6 +341,10 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { } func openArticleURLs(_ urlStrings: [String]) { + if urlStrings.count > 500 { + return + } + func doOpenURLs() { for urlString in urlStrings { Browser.open(urlString, inBackground: false) From 48c5afffcdb03383f32606649aeafb2ea8d5621e Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Fri, 7 Oct 2022 21:30:29 -0500 Subject: [PATCH 08/16] copyExternalURL should copy externalLinks --- Mac/MainWindow/MainWindowController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index 3b6f23221..5dda5732d 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -335,7 +335,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { } @IBAction func copyExternalURL(_ sender: Any?) { - if let links = selectedArticles?.compactMap({ $0.preferredLink }) { + if let links = selectedArticles?.compactMap({ $0.externalLink }) { URLPasteboardWriter.write(urlStrings: links, to: .general) } } From 49e8b921bada380caec4d90fa6314d18a4870407 Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Fri, 7 Oct 2022 21:41:02 -0500 Subject: [PATCH 09/16] Add a currentLinks property --- Mac/MainWindow/MainWindowController.swift | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index 5dda5732d..bb29f6b65 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -329,8 +329,8 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { } @IBAction func copyArticleURL(_ sender: Any?) { - if let links = selectedArticles?.compactMap({ $0.preferredLink }) { - URLPasteboardWriter.write(urlStrings: links, to: .general) + if let currentLinks { + URLPasteboardWriter.write(urlStrings: currentLinks, to: .general) } } @@ -340,14 +340,14 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { } } - func openArticleURLs(_ urlStrings: [String]) { + func openArticleURLs(_ urlStrings: [String], invertPreference: Bool = false) { if urlStrings.count > 500 { return } func doOpenURLs() { for urlString in urlStrings { - Browser.open(urlString, inBackground: false) + Browser.open(urlString, invertPreference: invertPreference) } } @@ -373,7 +373,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { @IBAction func openArticleInBrowser(_ sender: Any?) { guard let selectedArticles else { return } let urlStrings = selectedArticles.compactMap { $0.preferredLink } - openArticleURLs(urlStrings) + openArticleURLs(urlStrings, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false) } @IBAction func openInBrowser(_ sender: Any?) { @@ -1101,6 +1101,10 @@ private extension MainWindowController { return selectedArticles?.first { $0.preferredLink != nil }?.preferredLink } + var currentLinks: [String]? { + return selectedArticles?.compactMap { $0.preferredLink } + } + // MARK: - State Restoration func savableState() -> [AnyHashable : Any] { @@ -1136,7 +1140,7 @@ private extension MainWindowController { // MARK: - Command Validation func canCopyArticleURL() -> Bool { - return currentLink != nil + return currentLinks != nil } func canCopyExternalURL() -> Bool { From 7f59f94542f6c3d070f75c920ef66ca92515bc4e Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Sat, 8 Oct 2022 01:23:24 -0500 Subject: [PATCH 10/16] Move multi-url opening to an extension on Browser Also reword the alert a bit and add the number of pages to the "Open" button --- Mac/Browser.swift | 39 +++++++++++++++++++ Mac/MainWindow/MainWindowController.swift | 32 +-------------- ...melineViewController+ContextualMenus.swift | 3 +- .../Timeline/TimelineViewController.swift | 3 +- 4 files changed, 42 insertions(+), 35 deletions(-) diff --git a/Mac/Browser.swift b/Mac/Browser.swift index d574bc7ad..f53e95631 100644 --- a/Mac/Browser.swift +++ b/Mac/Browser.swift @@ -73,3 +73,42 @@ extension Browser { NSLocalizedString("Open in Browser in Background", comment: "Open in Browser in Background menu item title") } } + +extension Browser { + + static func open(_ urlStrings: [String], alertingInWindow window: NSWindow?, invertPreference: Bool = false) { + if urlStrings.count > 500 { + return + } + + func doOpenURLs() { + for urlString in urlStrings { + Browser.open(urlString, invertPreference: invertPreference) + } + } + + if urlStrings.count > 20 { + let alert = NSAlert() + let messageFormat = NSLocalizedString("Are you sure you want to open %ld articles in your browser?", comment: "") + alert.messageText = String.localizedStringWithFormat(messageFormat, urlStrings.count) + let confirmButtonTitleFormat = NSLocalizedString("Open %ld Articles", comment: "") + alert.addButton(withTitle: String.localizedStringWithFormat(confirmButtonTitleFormat, urlStrings.count)) + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + + if let window { + alert.beginSheetModal(for: window) { response in + if response == .alertFirstButtonReturn { + doOpenURLs() + } + } + } else { + if alert.runModal() == .alertFirstButtonReturn { + doOpenURLs() + } + } + } else { + doOpenURLs() + } + } + +} diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index bb29f6b65..e27c8fbb5 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -340,40 +340,10 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { } } - func openArticleURLs(_ urlStrings: [String], invertPreference: Bool = false) { - if urlStrings.count > 500 { - return - } - - func doOpenURLs() { - for urlString in urlStrings { - Browser.open(urlString, invertPreference: invertPreference) - } - } - - if urlStrings.count > 20 { - let alert = NSAlert() - let messageFormat = NSLocalizedString("Are you sure you want to open %ld articles in your browser?", comment: "") - alert.messageText = String.localizedStringWithFormat(messageFormat, urlStrings.count) - alert.addButton(withTitle: NSLocalizedString("Open", comment: "")) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) - - guard let window = self.window else { return } - - alert.beginSheetModal(for: window) { response in - if response == .alertFirstButtonReturn { - doOpenURLs() - } - } - } else { - doOpenURLs() - } - } - @IBAction func openArticleInBrowser(_ sender: Any?) { guard let selectedArticles else { return } let urlStrings = selectedArticles.compactMap { $0.preferredLink } - openArticleURLs(urlStrings, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false) + Browser.open(urlStrings, alertingInWindow: window, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false) } @IBAction func openInBrowser(_ sender: Any?) { diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index aca01d921..cc90504a4 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -93,8 +93,7 @@ extension TimelineViewController { return } - guard let windowController = self.view.window?.windowController as? MainWindowController else { return } - windowController.openArticleURLs(urlStrings) + Browser.open(urlStrings, alertingInWindow: self.view.window) } @objc func copyURLFromContextualMenu(_ sender: Any?) { diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index 5b024edbc..424892a72 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -315,9 +315,8 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr // MARK: - Actions @objc func openArticleInBrowser(_ sender: Any?) { - guard let windowController = self.view.window?.windowController as? MainWindowController else { return } let urlStrings = selectedArticles.compactMap { $0.preferredLink } - windowController.openArticleURLs(urlStrings) + Browser.open(urlStrings, alertingInWindow: self.view.window) } @IBAction func toggleStatusOfSelectedArticles(_ sender: Any?) { From 7f69ddc749d397e4f5b36f18cd09274d80f3fd55 Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Sat, 8 Oct 2022 01:27:35 -0500 Subject: [PATCH 11/16] Add documentation comment and rename window parameter --- Mac/Browser.swift | 8 +++++++- Mac/MainWindow/MainWindowController.swift | 2 +- .../Timeline/TimelineViewController+ContextualMenus.swift | 2 +- Mac/MainWindow/Timeline/TimelineViewController.swift | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Mac/Browser.swift b/Mac/Browser.swift index f53e95631..3ab1fbcac 100644 --- a/Mac/Browser.swift +++ b/Mac/Browser.swift @@ -76,7 +76,13 @@ extension Browser { extension Browser { - static func open(_ urlStrings: [String], alertingInWindow window: NSWindow?, invertPreference: Bool = false) { + /// Open multiple pages in the default browser, warning if over a certain number of URLs are passed. + /// - Parameters: + /// - urlStrings: The URL strings to open. + /// - window: The window on which to display the "over limit" alert sheet. If `nil`, will be displayed as a + /// modal dialog. + /// - invertPreference: Whether to invert the user's "Open web pages in background in browser" preference. + static func open(_ urlStrings: [String], fromWindow window: NSWindow?, invertPreference: Bool = false) { if urlStrings.count > 500 { return } diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index e27c8fbb5..8455b8d1a 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -343,7 +343,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { @IBAction func openArticleInBrowser(_ sender: Any?) { guard let selectedArticles else { return } let urlStrings = selectedArticles.compactMap { $0.preferredLink } - Browser.open(urlStrings, alertingInWindow: window, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false) + Browser.open(urlStrings, fromWindow: window, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false) } @IBAction func openInBrowser(_ sender: Any?) { diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index cc90504a4..6da82f3e7 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -93,7 +93,7 @@ extension TimelineViewController { return } - Browser.open(urlStrings, alertingInWindow: self.view.window) + Browser.open(urlStrings, fromWindow: self.view.window) } @objc func copyURLFromContextualMenu(_ sender: Any?) { diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index 424892a72..67472a2b0 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -316,7 +316,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr @objc func openArticleInBrowser(_ sender: Any?) { let urlStrings = selectedArticles.compactMap { $0.preferredLink } - Browser.open(urlStrings, alertingInWindow: self.view.window) + Browser.open(urlStrings, fromWindow: self.view.window) } @IBAction func toggleStatusOfSelectedArticles(_ sender: Any?) { From 6dc1393143c50e14db17f57c8d3e1f651a8c8499 Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Sat, 8 Oct 2022 22:08:00 -0500 Subject: [PATCH 12/16] Always respect the user's "Open in Browser" preference --- .../TimelineViewController+ContextualMenus.swift | 11 +++++++++-- Mac/MainWindow/Timeline/TimelineViewController.swift | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index 6da82f3e7..716e8555e 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -93,7 +93,7 @@ extension TimelineViewController { return } - Browser.open(urlStrings, fromWindow: self.view.window) + Browser.open(urlStrings, fromWindow: self.view.window, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false) } @objc func copyURLFromContextualMenu(_ sender: Any?) { @@ -182,6 +182,7 @@ private extension TimelineViewController { if links.count > 0 { menu.addSeparatorIfNeeded() menu.addItem(openInBrowserMenuItem(links)) + menu.addItem(openInBrowserReversedMenuItem(links)) menu.addSeparatorIfNeeded() menu.addItem(copyArticleURLsMenuItem(links)) @@ -278,9 +279,15 @@ private extension TimelineViewController { } func openInBrowserMenuItem(_ urlStrings: [String]) -> NSMenuItem { - return menuItem(NSLocalizedString("Open in Browser", comment: "Command"), #selector(openInBrowserFromContextualMenu(_:)), urlStrings) } + + func openInBrowserReversedMenuItem(_ urlStrings: [String]) -> NSMenuItem { + let item = menuItem(Browser.titleForOpenInBrowserInverted, #selector(openInBrowserFromContextualMenu(_:)), urlStrings) + item.keyEquivalentModifierMask = .shift + item.isAlternate = true + return item; + } func copyArticleURLsMenuItem(_ urlStrings: [String]) -> NSMenuItem { let format = NSLocalizedString("Copy Article URL", comment: "Command") diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index 67472a2b0..654e44813 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -316,7 +316,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr @objc func openArticleInBrowser(_ sender: Any?) { let urlStrings = selectedArticles.compactMap { $0.preferredLink } - Browser.open(urlStrings, fromWindow: self.view.window) + Browser.open(urlStrings, fromWindow: self.view.window, invertPreference: NSApp.currentEvent?.modifierFlags.contains(.shift) ?? false) } @IBAction func toggleStatusOfSelectedArticles(_ sender: Any?) { From 3cd295bbb19a9c18dba01644c83e744bbe2545ee Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Mon, 10 Oct 2022 15:31:33 -0500 Subject: [PATCH 13/16] Add NSLocalizableString() comments --- Mac/Browser.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Mac/Browser.swift b/Mac/Browser.swift index 3ab1fbcac..7b47dad85 100644 --- a/Mac/Browser.swift +++ b/Mac/Browser.swift @@ -95,11 +95,11 @@ extension Browser { if urlStrings.count > 20 { let alert = NSAlert() - let messageFormat = NSLocalizedString("Are you sure you want to open %ld articles in your browser?", comment: "") + let messageFormat = NSLocalizedString("Are you sure you want to open %ld articles in your browser?", comment: "Open in Browser confirmation alert message format") alert.messageText = String.localizedStringWithFormat(messageFormat, urlStrings.count) - let confirmButtonTitleFormat = NSLocalizedString("Open %ld Articles", comment: "") + let confirmButtonTitleFormat = NSLocalizedString("Open %ld Articles", comment: "Open URLs in Browser confirm button format") alert.addButton(withTitle: String.localizedStringWithFormat(confirmButtonTitleFormat, urlStrings.count)) - alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "")) + alert.addButton(withTitle: NSLocalizedString("Cancel", comment: "Cancel button")) if let window { alert.beginSheetModal(for: window) { response in From cdf2dd5d6404d7331126d849cb28c24a33f6f576 Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Mon, 10 Oct 2022 16:43:36 -0500 Subject: [PATCH 14/16] Alert the user the first time they try to copy URLs from articles where some have no URLs --- Mac/AppDefaults.swift | 10 +++++++ Mac/MainWindow/MainWindowController.swift | 12 ++++++--- ...melineViewController+ContextualMenus.swift | 16 ++++++----- .../URLPasteboardWriter+NetNewsWire.swift | 27 +++++++++++++++++++ NetNewsWire.xcodeproj/project.pbxproj | 6 +++++ 5 files changed, 60 insertions(+), 11 deletions(-) create mode 100644 Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift diff --git a/Mac/AppDefaults.swift b/Mac/AppDefaults.swift index df9b7c618..685d6fd44 100644 --- a/Mac/AppDefaults.swift +++ b/Mac/AppDefaults.swift @@ -42,6 +42,7 @@ final class AppDefaults { static let exportOPMLAccountID = "exportOPMLAccountID" static let defaultBrowserID = "defaultBrowserID" static let currentThemeName = "currentThemeName" + static let hasSeenNotAllArticlesHaveURLsAlert = "hasSeenNotAllArticlesHaveURLsAlert" // Hidden prefs static let showDebugMenu = "ShowDebugMenu" @@ -220,6 +221,15 @@ final class AppDefaults { AppDefaults.setString(for: Key.currentThemeName, newValue) } } + + var hasSeenNotAllArticlesHaveURLsAlert: Bool { + get { + return UserDefaults.standard.bool(forKey: Key.hasSeenNotAllArticlesHaveURLsAlert) + } + set { + UserDefaults.standard.set(newValue, forKey: Key.hasSeenNotAllArticlesHaveURLsAlert) + } + } var showTitleOnMainWindow: Bool { return AppDefaults.bool(for: Key.showTitleOnMainWindow) diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index 8455b8d1a..496b0d6ba 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -330,7 +330,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { @IBAction func copyArticleURL(_ sender: Any?) { if let currentLinks { - URLPasteboardWriter.write(urlStrings: currentLinks, to: .general) + URLPasteboardWriter.write(urlStrings: currentLinks, alertingInWindow: window) } } @@ -1071,8 +1071,8 @@ private extension MainWindowController { return selectedArticles?.first { $0.preferredLink != nil }?.preferredLink } - var currentLinks: [String]? { - return selectedArticles?.compactMap { $0.preferredLink } + var currentLinks: [String?]? { + return selectedArticles?.map { $0.preferredLink } } // MARK: - State Restoration @@ -1110,7 +1110,11 @@ private extension MainWindowController { // MARK: - Command Validation func canCopyArticleURL() -> Bool { - return currentLinks != nil + if let currentLinks, currentLinks.count != 0 { + return true + } + + return false } func canCopyExternalURL() -> Bool { diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index 716e8555e..a2e5d29b7 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -97,10 +97,11 @@ extension TimelineViewController { } @objc func copyURLFromContextualMenu(_ sender: Any?) { - guard let menuItem = sender as? NSMenuItem, let urlStrings = menuItem.representedObject as? [String] else { + guard let menuItem = sender as? NSMenuItem, let urlStrings = menuItem.representedObject as? [String?] else { return } - URLPasteboardWriter.write(urlStrings: urlStrings, to: .general) + + URLPasteboardWriter.write(urlStrings: urlStrings, alertingInWindow: self.view.window) } @objc func performShareServiceFromContextualMenu(_ sender: Any?) { @@ -177,12 +178,13 @@ private extension TimelineViewController { } } - let links = articles.compactMap { $0.preferredLink } + let links = articles.map { $0.preferredLink } + let compactLinks = links.compactMap { $0 } - if links.count > 0 { + if compactLinks.count > 0 { menu.addSeparatorIfNeeded() - menu.addItem(openInBrowserMenuItem(links)) - menu.addItem(openInBrowserReversedMenuItem(links)) + menu.addItem(openInBrowserMenuItem(compactLinks)) + menu.addItem(openInBrowserReversedMenuItem(compactLinks)) menu.addSeparatorIfNeeded() menu.addItem(copyArticleURLsMenuItem(links)) @@ -289,7 +291,7 @@ private extension TimelineViewController { return item; } - func copyArticleURLsMenuItem(_ urlStrings: [String]) -> NSMenuItem { + func copyArticleURLsMenuItem(_ urlStrings: [String?]) -> NSMenuItem { let format = NSLocalizedString("Copy Article URL", comment: "Command") let title = String.localizedStringWithFormat(format, urlStrings.count) return menuItem(title, #selector(copyURLFromContextualMenu(_:)), urlStrings) diff --git a/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift b/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift new file mode 100644 index 000000000..679cd1602 --- /dev/null +++ b/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift @@ -0,0 +1,27 @@ +// +// URLPasteboardWriter+NetNewsWire.swift +// NetNewsWire +// +// Created by Nate Weaver on 2022-10-10. +// Copyright © 2022 Ranchero Software. All rights reserved. +// + +import RSCore + +extension URLPasteboardWriter { + + static func write(urlStrings: [String?], to pasteboard: NSPasteboard = .general, alertingInWindow window: NSWindow?) { + URLPasteboardWriter.write(urlStrings: urlStrings.compactMap { $0 }, to: pasteboard) + + if urlStrings.contains(nil), !AppDefaults.shared.hasSeenNotAllArticlesHaveURLsAlert, let window { + let alert = NSAlert() + alert.messageText = NSLocalizedString("Some articles don’t have links, so they weren't copied.", comment: "") + alert.informativeText = NSLocalizedString("You won't see this message again.", comment: "") + + alert.beginSheetModal(for: window) + + AppDefaults.shared.hasSeenNotAllArticlesHaveURLsAlert = true + } + } + +} diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index f3bcf0fc3..9fafa10ba 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -828,6 +828,8 @@ B2B8075E239C49D300F191E0 /* RSImage-AppIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2B8075D239C49D300F191E0 /* RSImage-AppIcons.swift */; }; B2B80778239C4C7000F191E0 /* RSImage-AppIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2B8075D239C49D300F191E0 /* RSImage-AppIcons.swift */; }; B2B80779239C4C7300F191E0 /* RSImage-AppIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2B8075D239C49D300F191E0 /* RSImage-AppIcons.swift */; }; + B2C12C6628F4C46800373730 /* URLPasteboardWriter+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C12C6528F4C46800373730 /* URLPasteboardWriter+NetNewsWire.swift */; }; + B2C12C6728F4C46800373730 /* URLPasteboardWriter+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2C12C6528F4C46800373730 /* URLPasteboardWriter+NetNewsWire.swift */; }; B528F81E23333C7E00E735DD /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = B528F81D23333C7E00E735DD /* page.html */; }; BDCB516724282C8A00102A80 /* AccountsNewsBlur.xib in Resources */ = {isa = PBXBuildFile; fileRef = BDCB514D24282C8A00102A80 /* AccountsNewsBlur.xib */; }; BDCB516824282C8A00102A80 /* AccountsNewsBlur.xib in Resources */ = {isa = PBXBuildFile; fileRef = BDCB514D24282C8A00102A80 /* AccountsNewsBlur.xib */; }; @@ -1572,6 +1574,7 @@ B24EFD5923310109006C6242 /* WKPreferencesPrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKPreferencesPrivate.h; sourceTree = ""; }; B27EEBDF244D15F2000932E6 /* stylesheet.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = stylesheet.css; sourceTree = ""; }; B2B8075D239C49D300F191E0 /* RSImage-AppIcons.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RSImage-AppIcons.swift"; sourceTree = ""; }; + B2C12C6528F4C46800373730 /* URLPasteboardWriter+NetNewsWire.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLPasteboardWriter+NetNewsWire.swift"; sourceTree = ""; }; B528F81D23333C7E00E735DD /* page.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = page.html; sourceTree = ""; }; BDCB514D24282C8A00102A80 /* AccountsNewsBlur.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsNewsBlur.xib; sourceTree = ""; }; C5A6ED5123C9AF4300AB6BE2 /* TitleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleActivityItemSource.swift; sourceTree = ""; }; @@ -2270,6 +2273,7 @@ 5117715424E1EA0F00A2A836 /* ArticleExtractorButton.swift */, 51FA73B62332D5F70090D516 /* LegacyArticleExtractorButton.swift */, 847CD6C9232F4CBF00FAC46D /* IconView.swift */, + B2C12C6528F4C46800373730 /* URLPasteboardWriter+NetNewsWire.swift */, 844B5B6B1FEA224B00C7C76A /* Keyboard */, 849A975F1ED9EB95007D329B /* Sidebar */, 849A97681ED9EBC8007D329B /* Timeline */, @@ -3873,6 +3877,7 @@ 65ED3FD0235DEF6C0081F399 /* Author+Scriptability.swift in Sources */, 65ED3FD1235DEF6C0081F399 /* PseudoFeed.swift in Sources */, 65ED3FD3235DEF6C0081F399 /* NSScriptCommand+NetNewsWire.swift in Sources */, + B2C12C6728F4C46800373730 /* URLPasteboardWriter+NetNewsWire.swift in Sources */, 65ED3FD4235DEF6C0081F399 /* Article+Scriptability.swift in Sources */, 515A5172243E802B0089E588 /* ExtensionPointDetailViewController.swift in Sources */, 65ED3FD5235DEF6C0081F399 /* SmartFeed.swift in Sources */, @@ -4223,6 +4228,7 @@ 848B937221C8C5540038DC0D /* CrashReporter.swift in Sources */, 515A5171243E802B0089E588 /* ExtensionPointDetailViewController.swift in Sources */, 847CD6CA232F4CBF00FAC46D /* IconView.swift in Sources */, + B2C12C6628F4C46800373730 /* URLPasteboardWriter+NetNewsWire.swift in Sources */, 84BBB12E20142A4700F054F5 /* InspectorWindowController.swift in Sources */, 51EF0F7A22771B890050506E /* ColorHash.swift in Sources */, 84E46C7D1F75EF7B005ECFB3 /* AppDefaults.swift in Sources */, From 016a2948c47f3ec14033401592c1bb9f5695e3aa Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Mon, 10 Oct 2022 16:49:13 -0500 Subject: [PATCH 15/16] Run alert modally if no window is passed; add doc comments --- .../URLPasteboardWriter+NetNewsWire.swift | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift b/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift index 679cd1602..6c6b1cfb4 100644 --- a/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift +++ b/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift @@ -10,15 +10,24 @@ import RSCore extension URLPasteboardWriter { + /// Copy URL strings, alerting the user the first time the array of URL strings contains `nil`. + /// - Parameters: + /// - urlStrings: The URL strings to copy. + /// - pasteboard: The pastebaord to copy to. + /// - window: The window to use as a sheet parent for the alert. If `nil`, will run the alert modally. static func write(urlStrings: [String?], to pasteboard: NSPasteboard = .general, alertingInWindow window: NSWindow?) { URLPasteboardWriter.write(urlStrings: urlStrings.compactMap { $0 }, to: pasteboard) - if urlStrings.contains(nil), !AppDefaults.shared.hasSeenNotAllArticlesHaveURLsAlert, let window { + if urlStrings.contains(nil), !AppDefaults.shared.hasSeenNotAllArticlesHaveURLsAlert { let alert = NSAlert() - alert.messageText = NSLocalizedString("Some articles don’t have links, so they weren't copied.", comment: "") - alert.informativeText = NSLocalizedString("You won't see this message again.", comment: "") + alert.messageText = NSLocalizedString("Some articles don’t have links, so they weren't copied.", comment: "\"Some articles have no links\" copy alert message text") + alert.informativeText = NSLocalizedString("You won't see this message again.", comment: "You won't see this message again") - alert.beginSheetModal(for: window) + if let window { + alert.beginSheetModal(for: window) + } else { + alert.runModal() // this should never happen + } AppDefaults.shared.hasSeenNotAllArticlesHaveURLsAlert = true } From 35e59dd6ddb642bc659587212b338ea89147133b Mon Sep 17 00:00:00 2001 From: Nate Weaver Date: Thu, 13 Oct 2022 13:21:18 -0500 Subject: [PATCH 16/16] Change parameter label --- Mac/MainWindow/MainWindowController.swift | 2 +- .../Timeline/TimelineViewController+ContextualMenus.swift | 2 +- Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index 496b0d6ba..a7b06a6fa 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -330,7 +330,7 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { @IBAction func copyArticleURL(_ sender: Any?) { if let currentLinks { - URLPasteboardWriter.write(urlStrings: currentLinks, alertingInWindow: window) + URLPasteboardWriter.write(urlStrings: currentLinks, alertingIn: window) } } diff --git a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift index a2e5d29b7..b658d9c3f 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController+ContextualMenus.swift @@ -101,7 +101,7 @@ extension TimelineViewController { return } - URLPasteboardWriter.write(urlStrings: urlStrings, alertingInWindow: self.view.window) + URLPasteboardWriter.write(urlStrings: urlStrings, alertingIn: self.view.window) } @objc func performShareServiceFromContextualMenu(_ sender: Any?) { diff --git a/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift b/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift index 6c6b1cfb4..81c3e3ce4 100644 --- a/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift +++ b/Mac/MainWindow/URLPasteboardWriter+NetNewsWire.swift @@ -15,7 +15,7 @@ extension URLPasteboardWriter { /// - urlStrings: The URL strings to copy. /// - pasteboard: The pastebaord to copy to. /// - window: The window to use as a sheet parent for the alert. If `nil`, will run the alert modally. - static func write(urlStrings: [String?], to pasteboard: NSPasteboard = .general, alertingInWindow window: NSWindow?) { + static func write(urlStrings: [String?], to pasteboard: NSPasteboard = .general, alertingIn window: NSWindow?) { URLPasteboardWriter.write(urlStrings: urlStrings.compactMap { $0 }, to: pasteboard) if urlStrings.contains(nil), !AppDefaults.shared.hasSeenNotAllArticlesHaveURLsAlert {