import SwiftUI import WebKit /// An item's show notes. /// /// They are feed-supplied HTML, sanitized server-side with ammonia before they are sent. There /// is no good native renderer for that: NSAttributedString(html:) is slow, single-threaded and /// ugly, and writing a real one is a project. So this is a web view carrying only the notes, /// dressed to match, and that is a deliberate choice rather than a thing left undone. struct ShowNotesView: UIViewRepresentable { let html: String let baseURL: URL? /// The notes size themselves; the pane scrolls as one, so the view grows to fit. @Binding var height: CGFloat func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIView(context: Context) -> WKWebView { let cfg = WKWebViewConfiguration() // Cookies, so a picture behind the same sign-in loads. cfg.websiteDataStore = .default() let web = WKWebView(frame: .zero, configuration: cfg) web.navigationDelegate = context.coordinator web.scrollView.isScrollEnabled = false web.isOpaque = false web.backgroundColor = .clear web.scrollView.backgroundColor = .clear return web } func updateUIView(_ web: WKWebView, context: Context) { guard context.coordinator.shown != html else { return } context.coordinator.shown = html web.loadHTMLString(page(for: context.environment.colorScheme), baseURL: baseURL) } /// The notes wrapped in just enough stylesheet to belong to the app: the system font at the /// body size, the label colours, and links in the accent. Nothing else is imposed -- the /// markup is the publisher's and should read as they wrote it. private func page(for scheme: ColorScheme) -> String { let dark = scheme == .dark let fg = dark ? "#f5f5f7" : "#1d1d1f" let dim = dark ? "#aeaeb2" : "#515154" let link = dark ? "#409cff" : "#0055aa" let rule = dark ? "rgba(255,255,255,.14)" : "rgba(0,0,0,.12)" return """
\(html) """ } final class Coordinator: NSObject, WKNavigationDelegate { private let parent: ShowNotesView var shown: String? init(_ parent: ShowNotesView) { self.parent = parent } func webView(_ web: WKWebView, didFinish navigation: WKNavigation!) { // Measure once it has laid out, so the pane can give it the room it asked for. web.evaluateJavaScript("document.body.scrollHeight") { value, _ in if let h = value as? CGFloat { self.parent.height = max(h, 1) } } } /// A link in the notes opens in Safari. Following one inside this view would replace the /// notes with somebody's website and leave no way back. func webView(_ web: WKWebView, decidePolicyFor action: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { guard action.navigationType == .linkActivated, let url = action.request.url else { return decisionHandler(.allow) } UIApplication.shared.open(url) decisionHandler(.cancel) } } }