From 3decd23c45a67c0054b8d8ddbe9518f64f8775d6 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Fri, 20 Sep 2019 20:33:28 -0500 Subject: [PATCH] Convert iOS to use Javascript rendering --- .../MainWindow/Detail}/page.html | 0 NetNewsWire.xcodeproj/project.pbxproj | 22 ++++--- iOS/AppDelegate.swift | 3 + iOS/ArticleActivityItemSource.swift | 33 ++++++++++ iOS/Detail/DetailViewController.swift | 60 +++++++++---------- iOS/Resources/page.html | 34 +++++++++++ iOS/SceneCoordinator.swift | 2 - 7 files changed, 114 insertions(+), 40 deletions(-) rename {Shared/Article Rendering => Mac/MainWindow/Detail}/page.html (100%) create mode 100644 iOS/ArticleActivityItemSource.swift create mode 100644 iOS/Resources/page.html diff --git a/Shared/Article Rendering/page.html b/Mac/MainWindow/Detail/page.html similarity index 100% rename from Shared/Article Rendering/page.html rename to Mac/MainWindow/Detail/page.html diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index f74cd7e25..ffcdd8a1a 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -100,6 +100,8 @@ 51AF460C23247F11001742EF /* SettingsFeedbinAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510D707F22B02A5F004E8F65 /* SettingsFeedbinAccountView.swift */; }; 51AF460E232488C6001742EF /* Account-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51AF460D232488C6001742EF /* Account-Extensions.swift */; }; 51B62E68233186730085F949 /* MasterTimelineAvatarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B62E67233186730085F949 /* MasterTimelineAvatarView.swift */; }; + 51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */; }; + 51BB7C312335ACDE008E8144 /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = 51BB7C302335ACDE008E8144 /* page.html */; }; 51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; }; 51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 51C451B9226377C900C03939 /* Articles.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 840716732262A60F00344432 /* Articles.framework */; }; @@ -373,7 +375,6 @@ 9EA33BB92318F8C10097B644 /* AccountsFeedlyWebWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EA33BB72318F8C10097B644 /* AccountsFeedlyWebWindowController.swift */; }; 9EA33BBA2318F8C10097B644 /* AccountsFeedlyWeb.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9EA33BB82318F8C10097B644 /* AccountsFeedlyWeb.xib */; }; B528F81E23333C7E00E735DD /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = B528F81D23333C7E00E735DD /* page.html */; }; - B528F81F23333C7E00E735DD /* page.html in Resources */ = {isa = PBXBuildFile; fileRef = B528F81D23333C7E00E735DD /* page.html */; }; D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D553737C20186C1F006D8857 /* Article+Scriptability.swift */; }; D57BE6E0204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift in Sources */ = {isa = PBXBuildFile; fileRef = D57BE6DF204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift */; }; D5907D7F2004AC00005947E5 /* NSApplication+Scriptability.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5907D7E2004AC00005947E5 /* NSApplication+Scriptability.swift */; }; @@ -826,6 +827,8 @@ 519E743422C663F900A78E47 /* SceneDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 51AF460D232488C6001742EF /* Account-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account-Extensions.swift"; sourceTree = ""; }; 51B62E67233186730085F949 /* MasterTimelineAvatarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MasterTimelineAvatarView.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 = ""; }; 51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = ""; }; 51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = ""; }; 51C45254226507D200C03939 /* AppAssets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppAssets.swift; sourceTree = ""; }; @@ -1385,7 +1388,6 @@ isa = PBXGroup; children = ( 849A977D1ED9EC42007D329B /* ArticleRenderer.swift */, - B528F81D23333C7E00E735DD /* page.html */, 848362FE2262A30E00DA1D35 /* template.html */, ); path = "Article Rendering"; @@ -1674,6 +1676,7 @@ 84216D0222128B9D0049B9B9 /* DetailWebViewController.swift */, 84E8E0EA202F693600562D8F /* DetailWebView.swift */, 84D52E941FE588BB00D14F5B /* DetailStatusBarView.swift */, + B528F81D23333C7E00E735DD /* page.html */, 848362FC2262A30800DA1D35 /* styleSheet.css */, 5127B235222B4849006D641D /* Keyboard */, ); @@ -1943,6 +1946,7 @@ 51C45254226507D200C03939 /* AppAssets.swift */, 51C45255226507D200C03939 /* AppDefaults.swift */, 51E3EB3C229AB08300645299 /* ErrorHandler.swift */, + 51BB7C262335A8E5008E8144 /* ArticleActivityItemSource.swift */, 51C4525D226508F600C03939 /* MasterFeed */, 51C4526D2265091600C03939 /* MasterTimeline */, 51C4527D2265092C00C03939 /* Detail */, @@ -1967,6 +1971,7 @@ 51F85BEE2272520B00C787DC /* Thanks.rtf */, 51F85BF22272531500C787DC /* Dedication.rtf */, 51C452B72265178500C03939 /* styleSheet.css */, + 51BB7C302335ACDE008E8144 /* page.html */, 84C9FC9B2262A1A900D921D6 /* Assets.xcassets */, 84C9FC9C2262A1A900D921D6 /* Info.plist */, 84BB0F812333426400DED65E /* NetNewsWire.entitlements */, @@ -2230,16 +2235,16 @@ TargetAttributes = { 513C5CE5232571C2003D4054 = { CreatedOnToolsVersion = 11.0; - DevelopmentTeam = DY2XQRVWN9; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; }; 6581C73220CED60000F4AD34 = { DevelopmentTeam = SHJK2V3AJG; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; - DevelopmentTeam = DY2XQRVWN9; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { @@ -2250,7 +2255,7 @@ 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; DevelopmentTeam = SHJK2V3AJG; - ProvisioningStyle = Manual; + ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.HardenedRuntime = { enabled = 1; @@ -2259,7 +2264,7 @@ }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 9C84TZ7Q6Z; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; @@ -2498,7 +2503,7 @@ 511D43EF231FBDE900FB1562 /* LaunchScreenPad.storyboard in Resources */, 511D43D2231FA62C00FB1562 /* GlobalKeyboardShortcuts.plist in Resources */, 84C9FCA12262A1B300D921D6 /* Main.storyboard in Resources */, - B528F81F23333C7E00E735DD /* page.html in Resources */, + 51BB7C312335ACDE008E8144 /* page.html in Resources */, 51F85BF32272531500C787DC /* Dedication.rtf in Resources */, 84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */, 51F85BEB22724CB600C787DC /* About.rtf in Resources */, @@ -2718,6 +2723,7 @@ 51938DF3231AFC660055A1A0 /* SearchTimelineFeedDelegate.swift in Sources */, 51C4525A226508D600C03939 /* UIStoryboard-Extensions.swift in Sources */, 519D740823243FEA008BB345 /* SettingsSubscriptionsImportDocumentPickerView.swift in Sources */, + 51BB7C272335A8E5008E8144 /* ArticleActivityItemSource.swift in Sources */, 51AF460C23247F11001742EF /* SettingsFeedbinAccountView.swift in Sources */, 51F85BF52273625800C787DC /* Bundle-Extensions.swift in Sources */, 519D740723243FE7008BB345 /* SettingsSubscriptionsExportDocumentPickerView.swift in Sources */, diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index 912675902..af2cca088 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -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() diff --git a/iOS/ArticleActivityItemSource.swift b/iOS/ArticleActivityItemSource.swift new file mode 100644 index 000000000..ba168651e --- /dev/null +++ b/iOS/ArticleActivityItemSource.swift @@ -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 ?? "" + } + +} diff --git a/iOS/Detail/DetailViewController.swift b/iOS/Detail/DetailViewController.swift index f7f3b37fd..521a17938 100644 --- a/iOS/Detail/DetailViewController.swift +++ b/iOS/Detail/DetailViewController.swift @@ -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) } diff --git a/iOS/Resources/page.html b/iOS/Resources/page.html new file mode 100644 index 000000000..7665aa643 --- /dev/null +++ b/iOS/Resources/page.html @@ -0,0 +1,34 @@ + + + + + + + + + + diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index 0e088349e..7d48eb653 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -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 {