diff --git a/Multiplatform/macOS/Article/Browser.swift b/Multiplatform/macOS/Article/Browser.swift new file mode 100644 index 000000000..dfcbb1571 --- /dev/null +++ b/Multiplatform/macOS/Article/Browser.swift @@ -0,0 +1,65 @@ +// +// Browser.swift +// Evergren +// +// Created by Brent Simmons on 2/23/16. +// Copyright © 2016 Ranchero Software, LLC. All rights reserved. +// + +import Foundation +import RSWeb + +struct Browser { + + /// The user-specified default browser for opening web pages. + /// + /// The user-assigned default browser, or `nil` if none was assigned + /// (i.e., the system default should be used). + static var defaultBrowser: MacWebBrowser? { + if let bundleID = AppDefaults.shared.defaultBrowserID, let browser = MacWebBrowser(bundleIdentifier: bundleID) { + return browser + } + + return nil + } + + + /// Opens a URL in the default browser. + /// + /// - Parameters: + /// - urlString: The URL to open. + /// - invert: Whether to invert the "open in background in browser" preference + static func open(_ urlString: String, invertPreference invert: Bool = false) { + // Opens according to prefs. + open(urlString, inBackground: invert ? !AppDefaults.shared.openInBrowserInBackground : AppDefaults.shared.openInBrowserInBackground) + } + + + /// Opens a URL in the default browser. + /// + /// - Parameters: + /// - urlString: The URL to open. + /// - inBackground: Whether to open the URL in the background or not. + /// - Note: Some browsers (specifically Chromium-derived ones) will ignore the request + /// to open in the background. + static func open(_ urlString: String, inBackground: Bool) { + if let url = URL(string: urlString) { + if let defaultBrowser = defaultBrowser { + defaultBrowser.openURL(url, inBackground: inBackground) + } else { + MacWebBrowser.openURL(url, inBackground: inBackground) + } + } + } +} + +extension Browser { + + static var titleForOpenInBrowserInverted: String { + let openInBackgroundPref = AppDefaults.shared.openInBrowserInBackground + + return openInBackgroundPref ? + NSLocalizedString("Open in Browser in Foreground", comment: "Open in Browser in Foreground menu item title") : + NSLocalizedString("Open in Browser in Background", comment: "Open in Browser in Background menu item title") + } +} diff --git a/Multiplatform/macOS/Article/WebViewController.swift b/Multiplatform/macOS/Article/WebViewController.swift index 2901c0cb4..a493a1781 100644 --- a/Multiplatform/macOS/Article/WebViewController.swift +++ b/Multiplatform/macOS/Article/WebViewController.swift @@ -6,9 +6,6 @@ // Copyright © 2020 Ranchero Software. All rights reserved. // -import AppKit -import Articles - import AppKit import RSCore import Articles @@ -207,6 +204,24 @@ extension WebViewController: WKScriptMessageHandler { } +extension WebViewController: WKNavigationDelegate { + + public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { + if navigationAction.navigationType == .linkActivated { + if let url = navigationAction.request.url { + let flags = navigationAction.modifierFlags + let invert = flags.contains(.shift) || flags.contains(.command) + Browser.open(url.absoluteString, invertPreference: invert) + } + decisionHandler(.cancel) + return + } + + decisionHandler(.allow) + } + +} + // MARK: Private private extension WebViewController { @@ -228,6 +243,8 @@ private extension WebViewController { self.view.topAnchor.constraint(equalTo: webView.topAnchor), self.view.bottomAnchor.constraint(equalTo: webView.bottomAnchor) ]) + + webView.navigationDelegate = self webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked) webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown) diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 186886ccb..eebabbda9 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -357,6 +357,7 @@ 51B80F1F24BE531200C6C32D /* SharingServiceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80F1E24BE531200C6C32D /* SharingServiceView.swift */; }; 51B80F4224BE588200C6C32D /* SharingServicePickerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80F4124BE588200C6C32D /* SharingServicePickerDelegate.swift */; }; 51B80F4424BE58BF00C6C32D /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80F4324BE58BF00C6C32D /* SharingServiceDelegate.swift */; }; + 51B80F4624BF76E700C6C32D /* Browser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B80F4524BF76E700C6C32D /* Browser.swift */; }; 51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */; }; 51BB7C312335ACDE008E8144 /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = 51BB7C302335ACDE008E8144 /* page.html */; }; 51BC4AFF247277E0000A6ED8 /* URL-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */; }; @@ -2010,6 +2011,7 @@ 51B80F1E24BE531200C6C32D /* SharingServiceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceView.swift; sourceTree = ""; }; 51B80F4124BE588200C6C32D /* SharingServicePickerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServicePickerDelegate.swift; sourceTree = ""; }; 51B80F4324BE58BF00C6C32D /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = ""; }; + 51B80F4524BF76E700C6C32D /* Browser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Browser.swift; sourceTree = ""; }; 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleActivityItemSource.swift; sourceTree = ""; }; 51BB7C302335ACDE008E8144 /* page.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = page.html; sourceTree = ""; }; 51BC4ADD247277DF000A6ED8 /* URL-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "URL-Extensions.swift"; sourceTree = ""; }; @@ -2768,6 +2770,7 @@ isa = PBXGroup; children = ( 51B54ABB24B5BEF20014348B /* ArticleView.swift */, + 51B80F4524BF76E700C6C32D /* Browser.swift */, 51B54A6824B54A490014348B /* IconView.swift */, 51B80F4324BE58BF00C6C32D /* SharingServiceDelegate.swift */, 51B80F4124BE588200C6C32D /* SharingServicePickerDelegate.swift */, @@ -5221,6 +5224,7 @@ 51E498CC24A8085D00B667CB /* SearchFeedDelegate.swift in Sources */, 51E498C824A8085D00B667CB /* SmartFeedsController.swift in Sources */, 175942AB24AD533200585066 /* RefreshInterval.swift in Sources */, + 51B80F4624BF76E700C6C32D /* Browser.swift in Sources */, 51E4992C24A8676300B667CB /* ArticleSorter.swift in Sources */, 514E6C0A24AD39AD00AC6F6E /* ArticleIconImageLoader.swift in Sources */, 51E4995024A8734C00B667CB /* ExtensionPoint.swift in Sources */,