Unify DetailIconSchemeHandler and ArticleIconSchemeHandler into one shared ArticleIconSchemeHandler.
This commit is contained in:
parent
09397f0a74
commit
97757a567f
@ -1,46 +0,0 @@
|
||||
//
|
||||
// DetailIconSchemeHandler.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 11/7/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
import Articles
|
||||
|
||||
class DetailIconSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
|
||||
var currentArticle: Article?
|
||||
|
||||
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
|
||||
|
||||
guard let responseURL = urlSchemeTask.request.url, let iconImage = self.currentArticle?.iconImage() else {
|
||||
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
|
||||
return
|
||||
}
|
||||
|
||||
let iconView = IconView(frame: CGRect(x: 0, y: 0, width: 48, height: 48))
|
||||
iconView.iconImage = iconImage
|
||||
let renderedImage = iconView.asImage()
|
||||
|
||||
guard let data = renderedImage.dataRepresentation() else {
|
||||
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
|
||||
return
|
||||
}
|
||||
|
||||
let headerFields = ["Cache-Control": "no-cache"]
|
||||
if let response = HTTPURLResponse(url: responseURL, statusCode: 200, httpVersion: nil, headerFields: headerFields) {
|
||||
urlSchemeTask.didReceive(response)
|
||||
urlSchemeTask.didReceive(data)
|
||||
urlSchemeTask.didFinish()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
|
||||
urlSchemeTask.didFailWithError(URLError(.unknown))
|
||||
}
|
||||
|
||||
}
|
@ -56,8 +56,8 @@ final class DetailWebViewController: NSViewController {
|
||||
webView.configuration.preferences._developerExtrasEnabled = newValue
|
||||
}
|
||||
}
|
||||
|
||||
private let detailIconSchemeHandler = DetailIconSchemeHandler()
|
||||
|
||||
private lazy var articleIconSchemeHandler = ArticleIconSchemeHandler(delegate: self)
|
||||
private var waitingForFirstReload = false
|
||||
private let keyboardDelegate = DetailKeyboardDelegate()
|
||||
private var windowScrollY: CGFloat?
|
||||
@ -79,7 +79,7 @@ final class DetailWebViewController: NSViewController {
|
||||
|
||||
override func loadView() {
|
||||
|
||||
let configuration = WebViewConfiguration.configuration(with: detailIconSchemeHandler)
|
||||
let configuration = WebViewConfiguration.configuration(with: articleIconSchemeHandler)
|
||||
|
||||
webView = DetailWebView(frame: NSRect.zero, configuration: configuration)
|
||||
webView.uiDelegate = self
|
||||
@ -171,6 +171,25 @@ final class DetailWebViewController: NSViewController {
|
||||
|
||||
}
|
||||
|
||||
// MARK: - ArticleIconSchemeHandlerDelegate
|
||||
|
||||
extension DetailWebViewController: ArticleIconSchemeHandlerDelegate {
|
||||
|
||||
func articleIconSchemeHandler(_: ArticleIconSchemeHandler, imageForArticleID articleID: String) -> IconImage? {
|
||||
|
||||
guard let article else {
|
||||
assertionFailure("Did not expect request for article image when there is no current article.")
|
||||
return nil
|
||||
}
|
||||
guard articleID == article.articleID else {
|
||||
assertionFailure("Expected articleID to match current articleID.")
|
||||
return nil
|
||||
}
|
||||
|
||||
return article.iconImage() // May be nil — not a programming error
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WKScriptMessageHandler
|
||||
|
||||
extension DetailWebViewController: WKScriptMessageHandler {
|
||||
@ -282,10 +301,8 @@ private extension DetailWebViewController {
|
||||
case .loading:
|
||||
rendering = ArticleRenderer.loadingHTML(theme: theme)
|
||||
case .article(let article, _):
|
||||
detailIconSchemeHandler.currentArticle = article
|
||||
rendering = ArticleRenderer.articleHTML(article: article, theme: theme)
|
||||
case .extracted(let article, let extractedArticle, _):
|
||||
detailIconSchemeHandler.currentArticle = article
|
||||
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, theme: theme)
|
||||
}
|
||||
|
||||
|
79
Shared/Article Rendering/ArticleIconSchemeHandler.swift
Normal file
79
Shared/Article Rendering/ArticleIconSchemeHandler.swift
Normal file
@ -0,0 +1,79 @@
|
||||
//
|
||||
// ArticleIconSchemeHandler.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Brent Simmons on 1/20/25.
|
||||
// Copyright © 2025 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
|
||||
protocol ArticleIconSchemeHandlerDelegate: AnyObject {
|
||||
func articleIconSchemeHandler(_: ArticleIconSchemeHandler, imageForArticleID: String) -> IconImage?
|
||||
}
|
||||
|
||||
final class ArticleIconSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
|
||||
private weak var delegate: ArticleIconSchemeHandlerDelegate?
|
||||
private static let headerFields = ["Cache-Control": "no-cache"]
|
||||
|
||||
init(delegate: ArticleIconSchemeHandlerDelegate) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
// WKURLSchemeHandler is @MainActor, so this is @MainActor.
|
||||
|
||||
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
|
||||
|
||||
guard let url = urlSchemeTask.request.url,
|
||||
let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||
assertionFailure("Expected URL and components in ArticleIconSchemeHandler.")
|
||||
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
|
||||
return
|
||||
}
|
||||
|
||||
let articleID = components.path
|
||||
guard !articleID.isEmpty else {
|
||||
assertionFailure("Expected non-empty articleID in ArticleIconSchemeHandler.")
|
||||
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
|
||||
return
|
||||
}
|
||||
|
||||
guard let iconImage = delegate?.articleIconSchemeHandler(self, imageForArticleID: articleID) else {
|
||||
// There may not be an image — this is not a programming error.
|
||||
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
|
||||
return
|
||||
}
|
||||
|
||||
let iconView = IconView(frame: CGRect(x: 0, y: 0, width: 48, height: 48))
|
||||
iconView.iconImage = iconImage
|
||||
let renderedImage = iconView.asImage()
|
||||
|
||||
guard let data = renderedImage.dataRepresentation() else {
|
||||
assertionFailure("Expected non-empty image data ArticleIconSchemeHandler.")
|
||||
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
|
||||
return
|
||||
}
|
||||
|
||||
guard let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: Self.headerFields) else {
|
||||
assertionFailure("Expected to create HTTPURLResponse but failed.")
|
||||
urlSchemeTask.didFailWithError(URLError(.unknown))
|
||||
return
|
||||
}
|
||||
|
||||
urlSchemeTask.didReceive(response)
|
||||
urlSchemeTask.didReceive(data)
|
||||
urlSchemeTask.didFinish()
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
|
||||
urlSchemeTask.didFailWithError(URLError(.unknown))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The above code is re-rendering images multiple times, which is not good
|
||||
// for performance. Fixing this will probably require some refactoring
|
||||
// of the entire system for images, so that the code gets some kind of identifier —
|
||||
// probably a URL — so that it has a key for a cache, so it doesn’t have to
|
||||
// re-render images.
|
@ -1,60 +0,0 @@
|
||||
//
|
||||
// ArticleIconSchemeHandler.swift
|
||||
// NetNewsWire-iOS
|
||||
//
|
||||
// Created by Maurice Parker on 1/27/20.
|
||||
// Copyright © 2020 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import WebKit
|
||||
import Articles
|
||||
|
||||
class ArticleIconSchemeHandler: NSObject, WKURLSchemeHandler {
|
||||
|
||||
weak var coordinator: SceneCoordinator?
|
||||
|
||||
init(coordinator: SceneCoordinator) {
|
||||
self.coordinator = coordinator
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
|
||||
|
||||
guard let url = urlSchemeTask.request.url, let coordinator = coordinator else {
|
||||
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
|
||||
return
|
||||
}
|
||||
|
||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
|
||||
return
|
||||
}
|
||||
let articleID = components.path
|
||||
guard let iconImage = coordinator.articleFor(articleID)?.iconImage() else {
|
||||
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
|
||||
return
|
||||
}
|
||||
|
||||
let iconView = IconView(frame: CGRect(x: 0, y: 0, width: 48, height: 48))
|
||||
iconView.iconImage = iconImage
|
||||
let renderedImage = iconView.asImage()
|
||||
|
||||
guard let data = renderedImage.dataRepresentation() else {
|
||||
urlSchemeTask.didFailWithError(URLError(.fileDoesNotExist))
|
||||
return
|
||||
}
|
||||
|
||||
let headerFields = ["Cache-Control": "no-cache"]
|
||||
if let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: nil, headerFields: headerFields) {
|
||||
urlSchemeTask.didReceive(response)
|
||||
urlSchemeTask.didReceive(data)
|
||||
urlSchemeTask.didFinish()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
|
||||
urlSchemeTask.didFailWithError(URLError(.unknown))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,8 +18,8 @@ protocol WebViewControllerDelegate: AnyObject {
|
||||
func webViewController(_: WebViewController, articleExtractorButtonStateDidUpdate: ArticleExtractorButtonState)
|
||||
}
|
||||
|
||||
class WebViewController: UIViewController {
|
||||
|
||||
final class WebViewController: UIViewController {
|
||||
|
||||
private struct MessageName {
|
||||
static let imageWasClicked = "imageWasClicked"
|
||||
static let imageWasShown = "imageWasShown"
|
||||
@ -39,7 +39,7 @@ class WebViewController: UIViewController {
|
||||
private var isFullScreenAvailable: Bool {
|
||||
return AppDefaults.shared.articleFullscreenAvailable && traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed
|
||||
}
|
||||
private lazy var articleIconSchemeHandler = ArticleIconSchemeHandler(coordinator: coordinator);
|
||||
private lazy var articleIconSchemeHandler = ArticleIconSchemeHandler(delegate: self)
|
||||
private lazy var transition = ImageTransition(controller: self)
|
||||
private var clickedImageCompletion: (() -> Void)?
|
||||
|
||||
@ -279,6 +279,25 @@ class WebViewController: UIViewController {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ArticleIconSchemeHandlerDelegate
|
||||
|
||||
extension WebViewController: ArticleIconSchemeHandlerDelegate {
|
||||
|
||||
func articleIconSchemeHandler(_: ArticleIconSchemeHandler, imageForArticleID articleID: String) -> IconImage? {
|
||||
|
||||
guard let article else {
|
||||
assertionFailure("Did not expect request for article image when there is no current article.")
|
||||
return nil
|
||||
}
|
||||
guard articleID == article.articleID else {
|
||||
assertionFailure("Expected articleID to match current articleID.")
|
||||
return nil
|
||||
}
|
||||
|
||||
return article.iconImage() // May be nil — not a programming error
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: ArticleExtractorDelegate
|
||||
|
||||
extension WebViewController: ArticleExtractorDelegate {
|
||||
@ -300,7 +319,6 @@ extension WebViewController: ArticleExtractorDelegate {
|
||||
articleExtractorButtonState = .on
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: UIContextMenuInteractionDelegate
|
||||
|
Loading…
x
Reference in New Issue
Block a user