Add JavaScript and native message handler for getting mouseover urls from the detail view’s WKWebView.

This commit is contained in:
Brent Simmons 2017-11-05 20:31:50 -08:00
parent 9818278c9b
commit 3fcede7fb4
6 changed files with 115 additions and 8 deletions

View File

@ -148,10 +148,10 @@ private struct SidebarItemSpecifier {
func restore() {
if let feed = feed {
if let _ = feed {
restoreFeed()
}
else if let folder = folder {
else if let _ = folder {
restoreFolder()
}
}

View File

@ -620,8 +620,8 @@
849A977C1ED9EC42007D329B /* Detail */ = {
isa = PBXGroup;
children = (
849A977D1ED9EC42007D329B /* ArticleRenderer.swift */,
849A977E1ED9EC42007D329B /* DetailViewController.swift */,
849A977D1ED9EC42007D329B /* ArticleRenderer.swift */,
);
path = Detail;
sourceTree = "<group>";

View File

@ -17,6 +17,10 @@ extension Notification.Name {
static let AppNavigationKeyPressed = Notification.Name("AppNavigationKeyPressedNotification")
static let UserDidAddFeed = Notification.Name("UserDidAddFeedNotification")
// Sent by DetailViewController when mouse hovers over link in web view.
static let MouseDidEnterLink = Notification.Name("MouseDidEnterLinkNotification")
static let MouseDidExitLink = Notification.Name("MouseDidExitLinkNotification")
}
extension Notification {
@ -42,6 +46,7 @@ final class AppInfo {
var navigationKey: Int?
var objects: [AnyObject]?
var feed: Feed?
var url: String?
static let appInfoKey = "appInfo"

View File

@ -180,14 +180,40 @@ class ArticleRenderer {
private func renderedHTML() -> String {
var s = "<!DOCTYPE html><html><head>"
var s = "<!DOCTYPE html><html><head>\n\n"
s += textInsideTag(title, "title")
s += textInsideTag(styleString(), "style")
s += "</head></body>"
s += """
<script type="text/javascript">
function startup() {
var anchors = document.getElementsByTagName("a");
for (var i = 0; i < anchors.length; i++) {
anchors[i].addEventListener("mouseenter", function() { mouseDidEnterLink(this) });
anchors[i].addEventListener("mouseleave", function() { mouseDidExitLink(this) });
}
}
function mouseDidEnterLink(anchor) {
window.webkit.messageHandlers.mouseDidEnter.postMessage(anchor.href);
}
function mouseDidExitLink(anchor) {
window.webkit.messageHandlers.mouseDidExit.postMessage(anchor.href);
}
</script>
"""
s += "\n\n</head><body onload='startup()'>\n\n"
s += RSMacroProcessor.renderedText(withTemplate: template(), substitutions: substitutions(), macroStart: "[[", macroEnd: "]]")
s += "</body></html>"
s += "\n\n</body></html>"
// print(s)

View File

@ -12,7 +12,7 @@ import RSCore
import Data
class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDelegate {
var webview: WKWebView!
var article: Article? {
@ -20,7 +20,12 @@ class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDelegate
reloadHTML()
}
}
private struct MessageName {
static let mouseDidEnter = "mouseDidEnter"
static let mouseDidExit = "mouseDidExit"
}
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(timelineSelectionDidChange(_:)), name: .TimelineSelectionDidChange, object: nil)
@ -35,6 +40,11 @@ class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDelegate
let configuration = WKWebViewConfiguration()
configuration.preferences = preferences
let userContentController = WKUserContentController()
userContentController.add(self, name: MessageName.mouseDidEnter)
userContentController.add(self, name: MessageName.mouseDidExit)
configuration.userContentController = userContentController
webview = WKWebView(frame: self.view.bounds, configuration: configuration)
webview.uiDelegate = self
webview.navigationDelegate = self
@ -97,6 +107,41 @@ class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDelegate
}
}
extension DetailViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == MessageName.mouseDidEnter, let link = message.body as? String {
mouseDidEnter(link)
}
else if message.name == MessageName.mouseDidExit, let link = message.body as? String{
mouseDidExit(link)
}
}
private func mouseDidEnter(_ link: String) {
if link.isEmpty {
return
}
let appInfo = AppInfo()
appInfo.view = self.view
appInfo.url = link
NotificationCenter.default.post(name: .MouseDidEnterLink, object: self, userInfo: appInfo.userInfo)
}
private func mouseDidExit(_ link: String) {
let appInfo = AppInfo()
appInfo.view = self.view
appInfo.url = link
NotificationCenter.default.post(name: .MouseDidExitLink, object: self, userInfo: appInfo.userInfo)
}
}
class DetailBox: NSBox {
weak var viewController: DetailViewController?

View File

@ -40,6 +40,9 @@ final class StatusBarView: NSView {
NotificationCenter.default.addObserver(self, selector: #selector(progressDidChange(_:)), name: .AccountRefreshProgressDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(timelineSelectionDidChange(_:)), name: .TimelineSelectionDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(mouseDidEnterLink(_:)), name: .MouseDidEnterLink, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(mouseDidExitLink(_:)), name: .MouseDidExitLink, object: nil)
}
// MARK: Notifications
@ -51,6 +54,34 @@ final class StatusBarView: NSView {
updateProgressLabel(progress)
}
@objc dynamic func mouseDidEnterLink(_ notification: Notification) {
guard let appInfo = AppInfo.pullFromUserInfo(notification.userInfo) else {
return
}
guard let window = window, let notificationWindow = appInfo.view?.window, window === notificationWindow else {
return
}
guard let link = appInfo.url else {
return
}
print(link)
}
@objc dynamic func mouseDidExitLink(_ notification: Notification) {
guard let appInfo = AppInfo.pullFromUserInfo(notification.userInfo) else {
return
}
guard let window = window, let notificationWindow = appInfo.view?.window, window === notificationWindow else {
return
}
guard let link = appInfo.url else {
return
}
print(link)
}
// MARK: Notifications
@objc dynamic func timelineSelectionDidChange(_ note: Notification) {