mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2025-02-01 11:36:56 +01:00
commit
1f7d40a537
@ -202,12 +202,7 @@ class ArticleViewController: UIViewController {
|
||||
}
|
||||
|
||||
@objc func contentSizeCategoryDidChange(_ note: Notification) {
|
||||
coordinator.webViewProvider.flushQueue()
|
||||
coordinator.webViewProvider.replenishQueueIfNeeded()
|
||||
if let controller = currentWebViewController {
|
||||
controller.fullReload()
|
||||
self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil)
|
||||
}
|
||||
resetWebViewController()
|
||||
}
|
||||
|
||||
@objc func willEnterForeground(_ note: Notification) {
|
||||
@ -215,6 +210,7 @@ class ArticleViewController: UIViewController {
|
||||
if AppDefaults.articleFullscreenEnabled {
|
||||
currentWebViewController?.hideBars()
|
||||
}
|
||||
resetWebViewController()
|
||||
}
|
||||
|
||||
// MARK: Actions
|
||||
@ -274,10 +270,6 @@ class ArticleViewController: UIViewController {
|
||||
currentWebViewController?.scrollPageDown()
|
||||
}
|
||||
|
||||
func fullReload() {
|
||||
currentWebViewController?.fullReload()
|
||||
}
|
||||
|
||||
func stopArticleExtractorIfProcessing() {
|
||||
currentWebViewController?.stopArticleExtractorIfProcessing()
|
||||
}
|
||||
@ -366,4 +358,13 @@ private extension ArticleViewController {
|
||||
return controller
|
||||
}
|
||||
|
||||
func resetWebViewController() {
|
||||
coordinator.webViewProvider.flushQueue()
|
||||
coordinator.webViewProvider.replenishQueueIfNeeded()
|
||||
if let controller = currentWebViewController {
|
||||
controller.fullReload()
|
||||
self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ class WebViewController: UIViewController {
|
||||
}
|
||||
|
||||
func fullReload() {
|
||||
self.loadWebView()
|
||||
loadWebView(replaceExistingWebView: true)
|
||||
}
|
||||
|
||||
func showBars() {
|
||||
@ -288,10 +288,16 @@ extension WebViewController: UIContextMenuInteractionDelegate {
|
||||
// MARK: WKNavigationDelegate
|
||||
|
||||
extension WebViewController: WKNavigationDelegate {
|
||||
|
||||
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
|
||||
if view.subviews.count > 1 {
|
||||
view.subviews.last?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
|
||||
|
||||
if navigationAction.navigationType == .linkActivated {
|
||||
|
||||
guard let url = navigationAction.request.url else {
|
||||
decisionHandler(.allow)
|
||||
return
|
||||
@ -313,13 +319,13 @@ extension WebViewController: WKNavigationDelegate {
|
||||
} else {
|
||||
decisionHandler(.allow)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
decisionHandler(.allow)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
|
||||
fullReload()
|
||||
}
|
||||
|
||||
}
|
||||
@ -402,10 +408,10 @@ private struct ImageClickMessage: Codable {
|
||||
|
||||
private extension WebViewController {
|
||||
|
||||
func loadWebView() {
|
||||
func loadWebView(replaceExistingWebView: Bool = false) {
|
||||
guard isViewLoaded else { return }
|
||||
|
||||
if let webView = webView {
|
||||
if !replaceExistingWebView, let webView = webView {
|
||||
self.renderPage(webView)
|
||||
return
|
||||
}
|
||||
|
@ -7,42 +7,116 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RSCore
|
||||
import WebKit
|
||||
|
||||
/// 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 WebViewProvider: NSObject {
|
||||
|
||||
let articleIconSchemeHandler: ArticleIconSchemeHandler
|
||||
|
||||
private let minimumQueueDepth = 3
|
||||
private let maximumQueueDepth = 6
|
||||
private let articleIconSchemeHandler: ArticleIconSchemeHandler
|
||||
private let operationQueue = MainThreadOperationQueue()
|
||||
private var queue = UIView()
|
||||
|
||||
init(coordinator: SceneCoordinator, viewController: UIViewController) {
|
||||
articleIconSchemeHandler = ArticleIconSchemeHandler(coordinator: coordinator)
|
||||
super.init()
|
||||
viewController.view.insertSubview(queue, at: 0)
|
||||
|
||||
replenishQueueIfNeeded()
|
||||
}
|
||||
|
||||
func flushQueue() {
|
||||
queue.subviews.forEach { $0.removeFromSuperview() }
|
||||
operationQueue.add(WebViewProviderFlushQueueOperation(queue: queue))
|
||||
}
|
||||
|
||||
func replenishQueueIfNeeded() {
|
||||
while queue.subviews.count < minimumQueueDepth {
|
||||
enqueueWebView(PreloadedWebView(articleIconSchemeHandler: articleIconSchemeHandler))
|
||||
}
|
||||
operationQueue.add(WebViewProviderReplenishQueueOperation(queue: queue, articleIconSchemeHandler: articleIconSchemeHandler))
|
||||
}
|
||||
|
||||
func dequeueWebView(completion: @escaping (PreloadedWebView) -> ()) {
|
||||
operationQueue.add(WebViewProviderDequeueOperation(queue: queue, articleIconSchemeHandler: articleIconSchemeHandler, completion: completion))
|
||||
operationQueue.add(WebViewProviderReplenishQueueOperation(queue: queue, articleIconSchemeHandler: articleIconSchemeHandler))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class WebViewProviderFlushQueueOperation: MainThreadOperation {
|
||||
|
||||
// MainThreadOperation
|
||||
public var isCanceled = false
|
||||
public var id: Int?
|
||||
public weak var operationDelegate: MainThreadOperationDelegate?
|
||||
public var name: String? = "WebViewProviderFlushQueueOperation"
|
||||
public var completionBlock: MainThreadOperation.MainThreadOperationCompletionBlock?
|
||||
|
||||
private var queue: UIView
|
||||
|
||||
init(queue: UIView) {
|
||||
self.queue = queue
|
||||
}
|
||||
|
||||
func run() {
|
||||
queue.subviews.forEach { $0.removeFromSuperview() }
|
||||
self.operationDelegate?.operationDidComplete(self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class WebViewProviderReplenishQueueOperation: MainThreadOperation {
|
||||
|
||||
// MainThreadOperation
|
||||
public var isCanceled = false
|
||||
public var id: Int?
|
||||
public weak var operationDelegate: MainThreadOperationDelegate?
|
||||
public var name: String? = "WebViewProviderReplenishQueueOperation"
|
||||
public var completionBlock: MainThreadOperation.MainThreadOperationCompletionBlock?
|
||||
|
||||
private let minimumQueueDepth = 3
|
||||
|
||||
private var queue: UIView
|
||||
private var articleIconSchemeHandler: ArticleIconSchemeHandler
|
||||
|
||||
init(queue: UIView, articleIconSchemeHandler: ArticleIconSchemeHandler) {
|
||||
self.queue = queue
|
||||
self.articleIconSchemeHandler = articleIconSchemeHandler
|
||||
}
|
||||
|
||||
func run() {
|
||||
while queue.subviews.count < minimumQueueDepth {
|
||||
let webView = PreloadedWebView(articleIconSchemeHandler: articleIconSchemeHandler)
|
||||
queue.insertSubview(webView, at: 0)
|
||||
webView.preload()
|
||||
}
|
||||
self.operationDelegate?.operationDidComplete(self)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class WebViewProviderDequeueOperation: MainThreadOperation {
|
||||
|
||||
// MainThreadOperation
|
||||
public var isCanceled = false
|
||||
public var id: Int?
|
||||
public weak var operationDelegate: MainThreadOperationDelegate?
|
||||
public var name: String? = "WebViewProviderFlushQueueOperation"
|
||||
public var completionBlock: MainThreadOperation.MainThreadOperationCompletionBlock?
|
||||
|
||||
private var queue: UIView
|
||||
private var articleIconSchemeHandler: ArticleIconSchemeHandler
|
||||
private var completion: (PreloadedWebView) -> ()
|
||||
|
||||
init(queue: UIView, articleIconSchemeHandler: ArticleIconSchemeHandler, completion: @escaping (PreloadedWebView) -> ()) {
|
||||
self.queue = queue
|
||||
self.articleIconSchemeHandler = articleIconSchemeHandler
|
||||
self.completion = completion
|
||||
}
|
||||
|
||||
func run() {
|
||||
if let webView = queue.subviews.last as? PreloadedWebView {
|
||||
webView.ready { preloadedWebView in
|
||||
preloadedWebView.removeFromSuperview()
|
||||
self.replenishQueueIfNeeded()
|
||||
completion(preloadedWebView)
|
||||
self.completion(preloadedWebView)
|
||||
self.operationDelegate?.operationDidComplete(self)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -50,18 +124,11 @@ class WebViewProvider: NSObject {
|
||||
assertionFailure("Creating PreloadedWebView in \(#function); queue has run dry.")
|
||||
|
||||
let webView = PreloadedWebView(articleIconSchemeHandler: articleIconSchemeHandler)
|
||||
webView.ready { preloadedWebView in
|
||||
self.replenishQueueIfNeeded()
|
||||
completion(preloadedWebView)
|
||||
}
|
||||
}
|
||||
|
||||
func enqueueWebView(_ webView: PreloadedWebView) {
|
||||
guard queue.subviews.count < maximumQueueDepth else {
|
||||
return
|
||||
}
|
||||
queue.insertSubview(webView, at: 0)
|
||||
webView.preload()
|
||||
webView.ready { preloadedWebView in
|
||||
self.completion(preloadedWebView)
|
||||
self.operationDelegate?.operationDidComplete(self)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -73,16 +73,13 @@ class MasterFeedTableViewSectionHeader: UITableViewHeaderFooterView {
|
||||
|
||||
private let unreadCountView = MasterFeedUnreadCountView(frame: CGRect.zero)
|
||||
|
||||
@available(iOS 13.4, *)
|
||||
private(set) lazy var disclosurePointerInteraction = UIPointerInteraction()
|
||||
|
||||
private lazy var disclosureButton: UIButton = {
|
||||
let button = NonIntrinsicButton()
|
||||
button.tintColor = UIColor.tertiaryLabel
|
||||
button.setImage(AppAssets.disclosureImage, for: .normal)
|
||||
button.contentMode = .center
|
||||
if #available(iOS 13.4, *) {
|
||||
button.addInteraction(disclosurePointerInteraction)
|
||||
button.addInteraction(UIPointerInteraction())
|
||||
}
|
||||
button.addTarget(self, action: #selector(toggleDisclosure), for: .touchUpInside)
|
||||
return button
|
||||
|
@ -35,10 +35,14 @@ class ImageViewer {
|
||||
this.hideLoadingIndicator();
|
||||
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = this.img.naturalWidth * window.devicePixelRatio;
|
||||
canvas.height = this.img.naturalHeight * window.devicePixelRatio;
|
||||
var pixelRatio = window.devicePixelRatio;
|
||||
do {
|
||||
canvas.width = this.img.naturalWidth * pixelRatio;
|
||||
canvas.height = this.img.naturalHeight * pixelRatio;
|
||||
pixelRatio--;
|
||||
} while (pixelRatio > 0 && canvas.width * canvas.height > 16777216)
|
||||
canvas.getContext("2d").drawImage(this.img, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
|
||||
const rect = this.img.getBoundingClientRect();
|
||||
const message = {
|
||||
x: rect.x,
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
// High Level Settings common to both the iOS application and any extensions we bundle with it
|
||||
MARKETING_VERSION = 5.0.1
|
||||
CURRENT_PROJECT_VERSION = 44
|
||||
CURRENT_PROJECT_VERSION = 46
|
||||
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon
|
||||
|
Loading…
x
Reference in New Issue
Block a user