diff --git a/Account/Sources/Account/FeedProvider/Twitter/TwitterSymbol.swift b/Account/Sources/Account/FeedProvider/Twitter/TwitterSymbol.swift
index 525a1a595..9b1119743 100644
--- a/Account/Sources/Account/FeedProvider/Twitter/TwitterSymbol.swift
+++ b/Account/Sources/Account/FeedProvider/Twitter/TwitterSymbol.swift
@@ -10,18 +10,18 @@ import Foundation
struct TwitterSymbol: Codable, TwitterEntity {
- let name: String?
+ let text: String?
let indices: [Int]?
enum CodingKeys: String, CodingKey {
- case name = "name"
+ case text = "text"
case indices = "indices"
}
func renderAsHTML() -> String {
var html = String()
- if let name = name {
- html += "$\(name)"
+ if let text = text {
+ html += "$\(text)"
}
return html
}
diff --git a/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift b/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift
index 418711849..418c1c81a 100644
--- a/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift
+++ b/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift
@@ -1259,7 +1259,7 @@ private extension FeedbinAccountDelegate {
let parsedItems: [ParsedItem] = entries.map { entry in
let authors = Set([ParsedAuthor(name: entry.authorName, url: entry.jsonFeed?.jsonFeedAuthor?.url, avatarURL: entry.jsonFeed?.jsonFeedAuthor?.avatarURL, emailAddress: nil)])
- return ParsedItem(syncServiceID: String(entry.articleID), uniqueID: String(entry.articleID), feedURL: String(entry.feedID), url: entry.url, externalURL: nil, title: entry.title, language: nil, contentHTML: entry.contentHTML, contentText: nil, summary: entry.summary, imageURL: nil, bannerImageURL: nil, datePublished: entry.parsedDatePublished, dateModified: nil, authors: authors, tags: nil, attachments: nil)
+ return ParsedItem(syncServiceID: String(entry.articleID), uniqueID: String(entry.articleID), feedURL: String(entry.feedID), url: entry.url, externalURL: entry.jsonFeed?.jsonFeedExternalURL, title: entry.title, language: nil, contentHTML: entry.contentHTML, contentText: nil, summary: entry.summary, imageURL: nil, bannerImageURL: nil, datePublished: entry.parsedDatePublished, dateModified: nil, authors: authors, tags: nil, attachments: nil)
}
return Set(parsedItems)
diff --git a/Account/Sources/Account/Feedbin/FeedbinEntry.swift b/Account/Sources/Account/Feedbin/FeedbinEntry.swift
index 741d6abfc..27e23a140 100644
--- a/Account/Sources/Account/Feedbin/FeedbinEntry.swift
+++ b/Account/Sources/Account/Feedbin/FeedbinEntry.swift
@@ -52,9 +52,11 @@ final class FeedbinEntry: Decodable {
struct FeedbinEntryJSONFeed: Decodable {
let jsonFeedAuthor: FeedbinEntryJSONFeedAuthor?
-
+ let jsonFeedExternalURL: String?
+
enum CodingKeys: String, CodingKey {
case jsonFeedAuthor = "author"
+ case jsonFeedExternalURL = "external_url"
}
public init(from decoder: Decoder) throws {
@@ -64,6 +66,11 @@ struct FeedbinEntryJSONFeed: Decodable {
} catch {
jsonFeedAuthor = nil
}
+ do {
+ jsonFeedExternalURL = try container.decode(String.self, forKey: .jsonFeedExternalURL)
+ } catch {
+ jsonFeedExternalURL = nil
+ }
}
}
diff --git a/Mac/MainWindow/Detail/DetailWebViewController.swift b/Mac/MainWindow/Detail/DetailWebViewController.swift
index ae78a0d4a..e6ccb7b69 100644
--- a/Mac/MainWindow/Detail/DetailWebViewController.swift
+++ b/Mac/MainWindow/Detail/DetailWebViewController.swift
@@ -17,7 +17,7 @@ protocol DetailWebViewControllerDelegate: AnyObject {
func mouseDidExit(_: DetailWebViewController)
}
-final class DetailWebViewController: NSViewController, WKUIDelegate {
+final class DetailWebViewController: NSViewController {
weak var delegate: DetailWebViewControllerDelegate?
var webView: DetailWebView!
@@ -184,16 +184,22 @@ extension DetailWebViewController: WKScriptMessageHandler {
}
}
-// MARK: - WKNavigationDelegate
+// MARK: - WKNavigationDelegate & WKUIDelegate
-extension DetailWebViewController: WKNavigationDelegate {
+extension DetailWebViewController: WKNavigationDelegate, WKUIDelegate {
+
+ // Bottleneck through which WebView-based URL opens go
+ func openInBrowser(_ url: URL, flags: NSEvent.ModifierFlags) {
+ let invert = flags.contains(.shift) || flags.contains(.command)
+ Browser.open(url.absoluteString, invertPreference: invert)
+ }
+
+ // 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)
+ self.openInBrowser(url, flags: navigationAction.modifierFlags)
}
decisionHandler(.cancel)
return
@@ -216,6 +222,20 @@ extension DetailWebViewController: WKNavigationDelegate {
}
}
}
+
+ // WKUIDelegate
+
+ func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
+ // This method is reached when WebKit handles a JavaScript based window.open() invocation, for example. One
+ // example where this is used is in YouTube's embedded video player when a user clicks on the video's title
+ // or on the "Watch in YouTube" button. For our purposes we'll handle such window.open calls the same way we
+ // handle clicks on a URL.
+ if let url = navigationAction.request.url {
+ self.openInBrowser(url, flags: navigationAction.modifierFlags)
+ }
+
+ return nil
+ }
}
// MARK: - Private
diff --git a/Shared/Article Rendering/shared.css b/Shared/Article Rendering/shared.css
index 7f4c6a255..713924ce3 100644
--- a/Shared/Article Rendering/shared.css
+++ b/Shared/Article Rendering/shared.css
@@ -272,7 +272,14 @@ blockquote {
border-top: 1px solid var(--header-table-border-color);
}
+/* Hide the external link at the bottom of Daring Fireball posts */
+
+.x-netnewswire-hide {
+ display: none;
+}
+
/* see removeWpSmiley; this rule is kept in case a wp-smiley is encountered without alt text */
+
.wp-smiley {
height: 1em;
max-height: 1em;
diff --git a/iOS/Article/WebViewController.swift b/iOS/Article/WebViewController.swift
index c5b776126..fe6188903 100644
--- a/iOS/Article/WebViewController.swift
+++ b/iOS/Article/WebViewController.swift
@@ -344,16 +344,7 @@ extension WebViewController: WKNavigationDelegate {
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
if components?.scheme == "http" || components?.scheme == "https" {
decisionHandler(.cancel)
-
- // If the resource cannot be opened with an installed app, present the web view.
- UIApplication.shared.open(url, options: [.universalLinksOnly: true]) { didOpen in
- assert(Thread.isMainThread)
- guard didOpen == false else {
- return
- }
- let vc = SFSafariViewController(url: url)
- self.present(vc, animated: true)
- }
+ openURL(url)
} else if components?.scheme == "mailto" {
decisionHandler(.cancel)
@@ -392,12 +383,23 @@ extension WebViewController: WKNavigationDelegate {
// MARK: WKUIDelegate
extension WebViewController: WKUIDelegate {
+
func webView(_ webView: WKWebView, contextMenuForElement elementInfo: WKContextMenuElementInfo, willCommitWithAnimator animator: UIContextMenuInteractionCommitAnimating) {
// We need to have at least an unimplemented WKUIDelegate assigned to the WKWebView. This makes the
// link preview launch Safari when the link preview is tapped. In theory, you shoud be able to get
// the link from the elementInfo above and transition to SFSafariViewController instead of launching
// Safari. As the time of this writing, the link in elementInfo is always nil. ¯\_(ツ)_/¯
}
+
+ func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
+ guard let url = navigationAction.request.url else {
+ return nil
+ }
+
+ openURL(url)
+ return nil
+ }
+
}
// MARK: WKScriptMessageHandler
@@ -745,7 +747,19 @@ private extension WebViewController {
self?.showActivityDialog()
}
}
-
+
+ // If the resource cannot be opened with an installed app, present the web view.
+ func openURL(_ url: URL) {
+ UIApplication.shared.open(url, options: [.universalLinksOnly: true]) { didOpen in
+ assert(Thread.isMainThread)
+ guard didOpen == false else {
+ return
+ }
+ let vc = SFSafariViewController(url: url)
+ self.present(vc, animated: true)
+ }
+ }
+
}
// MARK: Find in Article