diff --git a/NetNewsWire/Base.lproj/MainWindow.storyboard b/NetNewsWire/Base.lproj/MainWindow.storyboard index b44312305..aad2d223f 100644 --- a/NetNewsWire/Base.lproj/MainWindow.storyboard +++ b/NetNewsWire/Base.lproj/MainWindow.storyboard @@ -624,59 +624,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/NetNewsWire/MainWindow/Detail/ArticleRenderer.swift b/NetNewsWire/MainWindow/Detail/ArticleRenderer.swift index 13b50167b..2d26b5d0c 100644 --- a/NetNewsWire/MainWindow/Detail/ArticleRenderer.swift +++ b/NetNewsWire/MainWindow/Detail/ArticleRenderer.swift @@ -18,7 +18,7 @@ var cachedTemplate = "" class ArticleRenderer { - let article: Article + let article: Article? let articleStyle: ArticleStyle let appearance: NSAppearance? @@ -47,7 +47,7 @@ class ArticleRenderer { }() lazy var title: String = { - if let articleTitle = self.article.title { + if let articleTitle = self.article?.title { return articleTitle } @@ -56,12 +56,12 @@ class ArticleRenderer { lazy var baseURL: URL? = { - var s = self.article.url + var s = self.article?.url if s == nil { - s = self.article.feed?.homePageURL + s = self.article?.feed?.homePageURL } if s == nil { - s = self.article.feed?.url + s = self.article?.feed?.url } if s == nil { return nil @@ -84,17 +84,25 @@ class ArticleRenderer { return nil }() - var html: String { - - return renderedHTML() + var articleHTML: String { + let body = RSMacroProcessor.renderedText(withTemplate: template(), substitutions: substitutions(), macroStart: "[[", macroEnd: "]]") + return renderHTML(withBody: body) } - init(article: Article, style: ArticleStyle, appearance: NSAppearance? = nil) { - + var multipleSelectionHTML: String { + let body = "

Multiple selection

" + return renderHTML(withBody: body) + } + + var noSelectionHTML: String { + let body = "

No selection

" + return renderHTML(withBody: body) + } + + init(article: Article?, style: ArticleStyle, appearance: NSAppearance? = nil) { self.article = article self.articleStyle = style self.appearance = appearance - } // MARK: Private @@ -157,7 +165,7 @@ class ArticleRenderer { private func titleOrTitleLink() -> String { - if let link = article.preferredLink { + if let link = article?.preferredLink { return linkWithText(title, link) } return title @@ -167,6 +175,11 @@ class ArticleRenderer { var d = [String: String]() + guard let article = article else { + assertionFailure("Article should have been set before calling this function.") + return d + } + let title = titleOrTitleLink() d["title"] = title @@ -220,10 +233,10 @@ class ArticleRenderer { } private func dateShouldBeLink() -> Bool { - guard let permalink = article.url else { + guard let permalink = article?.url else { return false } - guard let preferredLink = article.preferredLink else { // Title uses preferredLink + guard let preferredLink = article?.preferredLink else { // Title uses preferredLink return false } return permalink != preferredLink // Make date a link if it’s a different link from the title’s link @@ -301,7 +314,7 @@ class ArticleRenderer { // The author of this article, if just one. - if let authors = article.authors, authors.count == 1 { + if let authors = article?.authors, authors.count == 1 { return authors.first! } return nil @@ -309,7 +322,7 @@ class ArticleRenderer { private func singleFeedSpecifiedAuthor() -> Author? { - if let authors = article.feed?.authors, authors.count == 1 { + if let authors = article?.feed?.authors, authors.count == 1 { return authors.first! } return nil @@ -317,10 +330,10 @@ class ArticleRenderer { private func feedAvatar() -> Avatar? { - guard let feedIconURL = article.feed?.iconURL else { + guard let feedIconURL = article?.feed?.iconURL else { return nil } - return Avatar(imageURL: feedIconURL, url: article.feed?.homePageURL ?? article.feed?.url) + return Avatar(imageURL: feedIconURL, url: article?.feed?.homePageURL ?? article?.feed?.url) } private func authorAvatar() -> Avatar? { @@ -354,8 +367,8 @@ class ArticleRenderer { if let author = singleArticleSpecifiedAuthor(), let imageURL = author.avatarURL { return Avatar(imageURL: imageURL, url: author.url) } - if let feedIconURL = article.feed?.iconURL { - return Avatar(imageURL: feedIconURL, url: article.feed?.homePageURL ?? article.feed?.url) + if let feedIconURL = article?.feed?.iconURL { + return Avatar(imageURL: feedIconURL, url: article?.feed?.homePageURL ?? article?.feed?.url) } if let author = singleFeedSpecifiedAuthor(), let imageURL = author.avatarURL { return Avatar(imageURL: imageURL, url: author.url) @@ -370,11 +383,11 @@ class ArticleRenderer { if let author = singleArticleSpecifiedAuthor(), let imageURL = author.avatarURL { return Avatar(imageURL: imageURL, url: author.url).html(dimension: avatarDimension) } - if let feed = article.feed, let imgTag = feedIconImgTag(forFeed: feed) { + if let feed = article?.feed, let imgTag = feedIconImgTag(forFeed: feed) { return imgTag } - if let feedIconURL = article.feed?.iconURL { - return Avatar(imageURL: feedIconURL, url: article.feed?.homePageURL ?? article.feed?.url).html(dimension: avatarDimension) + if let feedIconURL = article?.feed?.iconURL { + return Avatar(imageURL: feedIconURL, url: article?.feed?.homePageURL ?? article?.feed?.url).html(dimension: avatarDimension) } if let author = singleFeedSpecifiedAuthor(), let imageURL = author.avatarURL { return Avatar(imageURL: imageURL, url: author.url).html(dimension: avatarDimension) @@ -400,7 +413,7 @@ class ArticleRenderer { private func byline() -> String { - guard let authors = article.authors ?? article.feed?.authors, !authors.isEmpty else { + guard let authors = article?.authors ?? article?.feed?.authors, !authors.isEmpty else { return "" } @@ -408,7 +421,7 @@ class ArticleRenderer { // This code assumes that multiple authors would never match the feed name so that // if there feed owner has an article co-author all authors are given the byline. if authors.count == 1, let author = authors.first { - if author.name == article.feed?.nameForDisplay { + if author.name == article?.feed?.nameForDisplay { return "" } } @@ -447,7 +460,7 @@ class ArticleRenderer { } - private func renderedHTML() -> String { + private func renderHTML(withBody body: String) -> String { var s = "\n\n" s += textInsideTag(title, "title") @@ -480,7 +493,7 @@ class ArticleRenderer { let appearanceClass = appearance?.isDarkMode ?? false ? "dark" : "light" s += "\n\n\n\n" - s += RSMacroProcessor.renderedText(withTemplate: template(), substitutions: substitutions(), macroStart: "[[", macroEnd: "]]") + s += body s += "\n\n" diff --git a/NetNewsWire/MainWindow/Detail/DetailViewController.swift b/NetNewsWire/MainWindow/Detail/DetailViewController.swift index 0013c6ff0..17f71dec2 100644 --- a/NetNewsWire/MainWindow/Detail/DetailViewController.swift +++ b/NetNewsWire/MainWindow/Detail/DetailViewController.swift @@ -15,7 +15,6 @@ import RSWeb final class DetailViewController: NSViewController, WKUIDelegate { @IBOutlet var containerView: DetailContainerView! - @IBOutlet var noSelectionView: NoSelectionView! var webview: DetailWebView! @@ -26,19 +25,13 @@ final class DetailViewController: NSViewController, WKUIDelegate { return } article = nil - if let _ = articles { - noSelectionView.showMultipleSelection() - } - else { - noSelectionView.showNoSelection() - } + reloadHTML() } } private var article: Article? { didSet { reloadHTML() - showOrHideWebView() } } @@ -78,9 +71,10 @@ final class DetailViewController: NSViewController, WKUIDelegate { webview.customUserAgent = userAgent } + reloadHTML() + containerView.contentView = webview containerView.viewController = self - showOrHideWebView() } // MARK: - Scrolling @@ -194,35 +188,21 @@ private extension DetailViewController { func reloadHTML() { - if let article = article { - let articleRenderer = ArticleRenderer(article: article, - style: ArticleStylesManager.shared.currentStyle, - appearance: self.view.effectiveAppearance) - webview.loadHTMLString(articleRenderer.html, baseURL: articleRenderer.baseURL) + let articleRenderer = ArticleRenderer(article: article, + style: ArticleStylesManager.shared.currentStyle, + appearance: self.view.effectiveAppearance) + + if article != nil { + webview.loadHTMLString(articleRenderer.articleHTML, baseURL: articleRenderer.baseURL) + } + else if articles != nil { + webview.loadHTMLString(articleRenderer.multipleSelectionHTML, baseURL: nil) } else { - webview.loadHTMLString("", baseURL: nil) + webview.loadHTMLString(articleRenderer.noSelectionHTML, baseURL: nil) } } - func showOrHideWebView() { - - if let _ = article { - switchToView(webview) - } - else { - switchToView(noSelectionView) - } - } - - func switchToView(_ view: NSView) { - - if containerView.contentView == view { - return - } - containerView.contentView = view - } - func fetchScrollInfo(_ callback: @escaping (ScrollInfo?) -> Void) { let javascriptString = "var x = {contentHeight: document.body.scrollHeight, offsetY: document.body.scrollTop}; x" @@ -251,12 +231,6 @@ final class DetailContainerView: NSView { weak var viewController: DetailViewController? = nil -// private var didConfigureLayer = false -// -// override var wantsUpdateLayer: Bool { -// return true -// } - var contentView: NSView? { didSet { if let oldContentView = oldValue { @@ -284,37 +258,8 @@ final class DetailContainerView: NSView { NSColor.textBackgroundColor.setFill() dirtyRect.fill() } -// override func updateLayer() { -// -// guard !didConfigureLayer else { -// return -// } -// if let layer = layer { -// let color = appDelegate.currentTheme.color(forKey: "MainWindow.Detail.backgroundColor") -// layer.backgroundColor = color.cgColor -// didConfigureLayer = true -// } -// } -} -// MARK: - - -final class NoSelectionView: NSView { - - @IBOutlet var noSelectionLabel: NSTextField! - @IBOutlet var multipleSelectionLabel: NSTextField! - - func showMultipleSelection() { - - noSelectionLabel.isHidden = true - multipleSelectionLabel.isHidden = false - } - - func showNoSelection() { - - noSelectionLabel.isHidden = false - multipleSelectionLabel.isHidden = true - } + } // MARK: - diff --git a/NetNewsWire/MainWindow/Detail/styleSheet.css b/NetNewsWire/MainWindow/Detail/styleSheet.css index 09d1141fb..509b015c5 100644 --- a/NetNewsWire/MainWindow/Detail/styleSheet.css +++ b/NetNewsWire/MainWindow/Detail/styleSheet.css @@ -21,6 +21,13 @@ a:hover { height: 68px; } +.systemMessage { + position: absolute; + top: 45%; + left: 50%; + transform: translateX(-55%) translateY(-50%); +} + /* Light mode */ body.light { @@ -44,6 +51,10 @@ body.light .articleDateline, body.light .articleDateLine.a:link, body.light .art color: rgba(0, 0, 0, 0.3); } +.light > .systemMessage { + color: #cbcbcb; +} + /* Dark mode */ body.dark { @@ -70,6 +81,10 @@ body.dark .articleDateline, body.dark .articleDateLine.a:link, body.dark .articl color: #d2d2d2; } +.dark > .systemMessage { + color: #5f5f5f; +} + .feedlink a:link, .feedlink a:visited { color: rgba(0, 0, 0, 0.6); } diff --git a/NetNewsWire/MainWindow/Timeline/ArticlePasteboardWriter.swift b/NetNewsWire/MainWindow/Timeline/ArticlePasteboardWriter.swift index 502e7b5fa..93b9735d3 100644 --- a/NetNewsWire/MainWindow/Timeline/ArticlePasteboardWriter.swift +++ b/NetNewsWire/MainWindow/Timeline/ArticlePasteboardWriter.swift @@ -27,7 +27,7 @@ extension Article: PasteboardWriterOwner { private lazy var renderedHTML: String = { let articleRenderer = ArticleRenderer(article: article, style: ArticleStylesManager.shared.currentStyle) - return articleRenderer.html + return articleRenderer.articleHTML }() init(article: Article) { diff --git a/NetNewsWire/Resources/DB5.plist b/NetNewsWire/Resources/DB5.plist index 6b7b51c17..dd95ce9df 100644 --- a/NetNewsWire/Resources/DB5.plist +++ b/NetNewsWire/Resources/DB5.plist @@ -127,11 +127,6 @@ Detail - noSelectionView - - backgroundColor - FFFFFF - statusBar backgroundColor