mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Convert iOS to use Javascript rendering
This commit is contained in:
@@ -53,6 +53,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||
super.init()
|
||||
appDelegate = self
|
||||
|
||||
// Force lazy initialization of the web view provider so that it can warm up the queue of prepared web views
|
||||
let _ = DetailViewControllerWebViewProvider.shared
|
||||
|
||||
AccountManager.shared = AccountManager(accountsFolder: RSDataSubfolder(nil, "Accounts")!)
|
||||
|
||||
registerBackgroundTasks()
|
||||
|
||||
33
iOS/ArticleActivityItemSource.swift
Normal file
33
iOS/ArticleActivityItemSource.swift
Normal file
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// ArticleActivityItemSource.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 9/20/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ArticleActivityItemSource: NSObject, UIActivityItemSource {
|
||||
|
||||
private let url: URL
|
||||
private let subject: String?
|
||||
|
||||
init(url: URL, subject: String?) {
|
||||
self.url = url
|
||||
self.subject = subject
|
||||
}
|
||||
|
||||
func activityViewControllerPlaceholderItem(_ : UIActivityViewController) -> Any {
|
||||
return url
|
||||
}
|
||||
|
||||
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
|
||||
return url
|
||||
}
|
||||
|
||||
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String {
|
||||
return subject ?? ""
|
||||
}
|
||||
|
||||
}
|
||||
@@ -99,14 +99,23 @@ class DetailViewController: UIViewController {
|
||||
}
|
||||
|
||||
func reloadHTML() {
|
||||
|
||||
guard let article = coordinator.currentArticle, let webView = webView else {
|
||||
return
|
||||
}
|
||||
let style = ArticleStylesManager.shared.currentStyle
|
||||
let (styleSheet, html) = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
webView.loadHTMLString(html, baseURL: nil)
|
||||
|
||||
let style = ArticleStylesManager.shared.currentStyle
|
||||
let rendering = ArticleRenderer.articleHTML(article: article, style: style)
|
||||
|
||||
let templateData = TemplateData(style: rendering.style, body: rendering.html)
|
||||
|
||||
let encoder = JSONEncoder()
|
||||
var render = "error();"
|
||||
if let data = try? encoder.encode(templateData) {
|
||||
let json = String(data: data, encoding: .utf8)!
|
||||
render = "render(\(json));"
|
||||
}
|
||||
|
||||
webView.evaluateJavaScript(render)
|
||||
}
|
||||
|
||||
// MARK: Notifications
|
||||
@@ -205,31 +214,8 @@ class DetailViewController: UIViewController {
|
||||
}
|
||||
|
||||
}
|
||||
//print("\(candidateY) : \(webView.scrollView.contentSize.height)")
|
||||
|
||||
class ArticleActivityItemSource: NSObject, UIActivityItemSource {
|
||||
|
||||
private let url: URL
|
||||
private let subject: String?
|
||||
|
||||
init(url: URL, subject: String?) {
|
||||
self.url = url
|
||||
self.subject = subject
|
||||
}
|
||||
|
||||
func activityViewControllerPlaceholderItem(_ : UIActivityViewController) -> Any {
|
||||
return url
|
||||
}
|
||||
|
||||
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
|
||||
return url
|
||||
}
|
||||
|
||||
func activityViewController(_ activityViewController: UIActivityViewController, subjectForActivityType activityType: UIActivity.ActivityType?) -> String {
|
||||
return subject ?? ""
|
||||
}
|
||||
|
||||
}
|
||||
// MARK: WKNavigationDelegate
|
||||
|
||||
extension DetailViewController: WKNavigationDelegate {
|
||||
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||
@@ -259,6 +245,8 @@ extension DetailViewController: WKNavigationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private extension DetailViewController {
|
||||
|
||||
func updateProgressIndicatorIfNeeded() {
|
||||
@@ -269,13 +257,26 @@ private extension DetailViewController {
|
||||
|
||||
}
|
||||
|
||||
private struct TemplateData: Codable {
|
||||
let style: String
|
||||
let body: String
|
||||
}
|
||||
|
||||
|
||||
// MARK: -
|
||||
|
||||
/// WKWebView has an awful behavior of a flash to white on first load when in dark mode.
|
||||
/// Keep a queue of WebViews where we've already done a trivial load so that by the time we need them in the UI, they're past the flash-to-shite part of their lifecycle.
|
||||
class DetailViewControllerWebViewProvider {
|
||||
|
||||
static var shared = DetailViewControllerWebViewProvider()
|
||||
|
||||
static let template: String = {
|
||||
let path = Bundle.main.path(forResource: "page", ofType: "html")!
|
||||
let s = try! NSString(contentsOfFile: path, encoding: String.Encoding.utf8.rawValue)
|
||||
return s as String
|
||||
}()
|
||||
|
||||
func dequeueWebView() -> WKWebView {
|
||||
if let webView = queue.popLast() {
|
||||
replenishQueueIfNeeded()
|
||||
@@ -295,8 +296,7 @@ class DetailViewControllerWebViewProvider {
|
||||
webView.uiDelegate = nil
|
||||
webView.navigationDelegate = nil
|
||||
|
||||
let html = ArticleRenderer.noContentHTML(style: .defaultStyle)
|
||||
webView.loadHTMLString(html, baseURL: nil)
|
||||
webView.loadHTMLString(DetailViewControllerWebViewProvider.template, baseURL: nil)
|
||||
|
||||
queue.insert(webView, at: 0)
|
||||
}
|
||||
|
||||
34
iOS/Resources/page.html
Normal file
34
iOS/Resources/page.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
function mouseDidEnterLink(anchor) {
|
||||
window.webkit.messageHandlers.mouseDidEnter.postMessage(anchor.href);
|
||||
}
|
||||
|
||||
function mouseDidExitLink(anchor) {
|
||||
window.webkit.messageHandlers.mouseDidExit.postMessage(anchor.href);
|
||||
}
|
||||
|
||||
function render(data) {
|
||||
document.getElementsByTagName("style")[0].innerHTML = data.style;
|
||||
document.body.innerHTML = data.body;
|
||||
window.scrollTo(0, 0);
|
||||
document.getElementsByTagName("body")[0].querySelectorAll("style, link[rel=stylesheet]").forEach(element => element.remove());
|
||||
document.getElementsByTagName("body")[0].querySelectorAll("[style]").forEach(element => element.removeAttribute("style"));
|
||||
}
|
||||
|
||||
function error() {
|
||||
document.body.innerHTML = "error";
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -269,8 +269,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(accountDidDownloadArticles(_:)), name: .AccountDidDownloadArticles, object: nil)
|
||||
|
||||
// Force lazy initialization of the web view provider so that it can warm up the queue of prepared web views
|
||||
let _ = DetailViewControllerWebViewProvider.shared
|
||||
}
|
||||
|
||||
func start(for size: CGSize) -> UIViewController {
|
||||
|
||||
Reference in New Issue
Block a user