Merge branch 'ios-release'

This commit is contained in:
Maurice Parker 2020-03-17 17:19:31 -05:00
commit 770206df60
25 changed files with 451 additions and 245 deletions

View File

@ -8,6 +8,7 @@
import AppKit import AppKit
import WebKit import WebKit
import RSCore
import RSWeb import RSWeb
import Articles import Articles
@ -118,8 +119,7 @@ final class DetailWebViewController: NSViewController, WKUIDelegate {
NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(avatarDidBecomeAvailable(_:)), name: .AvatarDidBecomeAvailable, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
webView.loadFileURL(ArticleRenderer.page.url, allowingReadAccessTo: ArticleRenderer.page.baseURL) webView.loadFileURL(ArticleRenderer.blank.url, allowingReadAccessTo: ArticleRenderer.blank.baseURL)
} }
// MARK: Notifications // MARK: Notifications
@ -202,12 +202,6 @@ extension DetailWebViewController: WKNavigationDelegate {
} }
// MARK: - Private // MARK: - Private
struct TemplateData: Codable {
let style: String
let body: String
let title: String
let baseURL: String
}
private extension DetailWebViewController { private extension DetailWebViewController {
@ -242,16 +236,15 @@ private extension DetailWebViewController {
rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, style: style) rendering = ArticleRenderer.articleHTML(article: article, extractedArticle: extractedArticle, style: style)
} }
let templateData = TemplateData(style: rendering.style, body: rendering.html, title: rendering.title, baseURL: rendering.baseURL) let substitutions = [
"title": rendering.title,
"baseURL": rendering.baseURL,
"style": rendering.style,
"body": rendering.html
]
let encoder = JSONEncoder() let html = try! MacroProcessor.renderedText(withTemplate: ArticleRenderer.page.html, substitutions: substitutions)
var render = "error();" webView.loadHTMLString(html, baseURL: ArticleRenderer.page.baseURL)
if let data = try? encoder.encode(templateData) {
let json = String(data: data, encoding: .utf8)!
render = "render(\(json), 0);"
}
webView.evaluateJavaScript(render)
} }
func fetchScrollInfo(_ completion: @escaping (ScrollInfo?) -> Void) { func fetchScrollInfo(_ completion: @escaping (ScrollInfo?) -> Void) {

View File

@ -0,0 +1,6 @@
<html>
<head>
</head>
<body>
</body>
</html>

View File

@ -1,13 +1,20 @@
<html> <html>
<head> <head>
<title></title> <title>[[title]]</title>
<base href="">
<style> <style>
[[style]]
</style> </style>
<script src="main.js"></script> <script src="main.js"></script>
<script src="main_mac.js"></script> <script src="main_mac.js"></script>
<script src="newsfoot.js" async="async"></script> <script src="newsfoot.js" async="async"></script>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
processPage();
})
</script>
<base href="[[baseURL]]">
</head> </head>
<body> <body>
[[body]]
</body> </body>
</html> </html>

View File

@ -14,6 +14,9 @@
3B826DCE2385C89600FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */; }; 3B826DCE2385C89600FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */; };
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; }; 49F40DF82335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
49F40DF92335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; }; 49F40DF92335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; };
5103A9982421643300410853 /* blank.html in Resources */ = {isa = PBXBuildFile; fileRef = 5103A9972421643300410853 /* blank.html */; };
5103A9992421643300410853 /* blank.html in Resources */ = {isa = PBXBuildFile; fileRef = 5103A9972421643300410853 /* blank.html */; };
5103A9B424216A4200410853 /* blank.html in Resources */ = {isa = PBXBuildFile; fileRef = 5103A9B324216A4200410853 /* blank.html */; };
5108F6B62375E612001ABC45 /* CacheCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6B52375E612001ABC45 /* CacheCleaner.swift */; }; 5108F6B62375E612001ABC45 /* CacheCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6B52375E612001ABC45 /* CacheCleaner.swift */; };
5108F6B72375E612001ABC45 /* CacheCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6B52375E612001ABC45 /* CacheCleaner.swift */; }; 5108F6B72375E612001ABC45 /* CacheCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6B52375E612001ABC45 /* CacheCleaner.swift */; };
5108F6D22375EED2001ABC45 /* TimelineCustomizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */; }; 5108F6D22375EED2001ABC45 /* TimelineCustomizerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */; };
@ -95,6 +98,7 @@
515D4FC123257A3200EE1167 /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; }; 515D4FC123257A3200EE1167 /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; };
515D4FCA23257CB500EE1167 /* Node-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97971ED9EFAA007D329B /* Node-Extensions.swift */; }; 515D4FCA23257CB500EE1167 /* Node-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97971ED9EFAA007D329B /* Node-Extensions.swift */; };
515D4FCC2325815A00EE1167 /* SafariExt.js in Resources */ = {isa = PBXBuildFile; fileRef = 515D4FCB2325815A00EE1167 /* SafariExt.js */; }; 515D4FCC2325815A00EE1167 /* SafariExt.js in Resources */ = {isa = PBXBuildFile; fileRef = 515D4FCB2325815A00EE1167 /* SafariExt.js */; };
516244E3241E19F000B61C47 /* ColorPaletteTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516244E2241E19F000B61C47 /* ColorPaletteTableViewController.swift */; };
51627A6723861DA3007B3B4B /* MasterFeedViewController+Drag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6623861DA3007B3B4B /* MasterFeedViewController+Drag.swift */; }; 51627A6723861DA3007B3B4B /* MasterFeedViewController+Drag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6623861DA3007B3B4B /* MasterFeedViewController+Drag.swift */; };
51627A6923861DED007B3B4B /* MasterFeedViewController+Drop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */; }; 51627A6923861DED007B3B4B /* MasterFeedViewController+Drop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */; };
51627A6B238629D8007B3B4B /* MasterFeedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6A238629D8007B3B4B /* MasterFeedDataSource.swift */; }; 51627A6B238629D8007B3B4B /* MasterFeedDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51627A6A238629D8007B3B4B /* MasterFeedDataSource.swift */; };
@ -1249,6 +1253,8 @@
3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsFeedWrangler.xib; sourceTree = "<group>"; }; 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsFeedWrangler.xib; sourceTree = "<group>"; };
3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsFeedWranglerWindowController.swift; sourceTree = "<group>"; }; 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsFeedWranglerWindowController.swift; sourceTree = "<group>"; };
49F40DEF2335B71000552BF4 /* newsfoot.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = newsfoot.js; sourceTree = "<group>"; }; 49F40DEF2335B71000552BF4 /* newsfoot.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = newsfoot.js; sourceTree = "<group>"; };
5103A9972421643300410853 /* blank.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = blank.html; sourceTree = "<group>"; };
5103A9B324216A4200410853 /* blank.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = blank.html; sourceTree = "<group>"; };
5108F6B52375E612001ABC45 /* CacheCleaner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheCleaner.swift; sourceTree = "<group>"; }; 5108F6B52375E612001ABC45 /* CacheCleaner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheCleaner.swift; sourceTree = "<group>"; };
5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineCustomizerViewController.swift; sourceTree = "<group>"; }; 5108F6D12375EED2001ABC45 /* TimelineCustomizerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineCustomizerViewController.swift; sourceTree = "<group>"; };
5108F6D32375EEEF001ABC45 /* TimelinePreviewTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePreviewTableViewController.swift; sourceTree = "<group>"; }; 5108F6D32375EEEF001ABC45 /* TimelinePreviewTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelinePreviewTableViewController.swift; sourceTree = "<group>"; };
@ -1303,6 +1309,7 @@
515D4FCB2325815A00EE1167 /* SafariExt.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = SafariExt.js; sourceTree = "<group>"; }; 515D4FCB2325815A00EE1167 /* SafariExt.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = SafariExt.js; sourceTree = "<group>"; };
515D4FCD2325909200EE1167 /* NetNewsWire_iOS_ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NetNewsWire_iOS_ShareExtension.entitlements; sourceTree = "<group>"; }; 515D4FCD2325909200EE1167 /* NetNewsWire_iOS_ShareExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NetNewsWire_iOS_ShareExtension.entitlements; sourceTree = "<group>"; };
515D4FCE2325B3D000EE1167 /* NetNewsWire_iOSshareextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSshareextension_target.xcconfig; sourceTree = "<group>"; }; 515D4FCE2325B3D000EE1167 /* NetNewsWire_iOSshareextension_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSshareextension_target.xcconfig; sourceTree = "<group>"; };
516244E2241E19F000B61C47 /* ColorPaletteTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPaletteTableViewController.swift; sourceTree = "<group>"; };
51627A6623861DA3007B3B4B /* MasterFeedViewController+Drag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MasterFeedViewController+Drag.swift"; sourceTree = "<group>"; }; 51627A6623861DA3007B3B4B /* MasterFeedViewController+Drag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MasterFeedViewController+Drag.swift"; sourceTree = "<group>"; };
51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MasterFeedViewController+Drop.swift"; sourceTree = "<group>"; }; 51627A6823861DED007B3B4B /* MasterFeedViewController+Drop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MasterFeedViewController+Drop.swift"; sourceTree = "<group>"; };
51627A6A238629D8007B3B4B /* MasterFeedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterFeedDataSource.swift; sourceTree = "<group>"; }; 51627A6A238629D8007B3B4B /* MasterFeedDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MasterFeedDataSource.swift; sourceTree = "<group>"; };
@ -1875,6 +1882,7 @@
51A16990235E10D600EB091F /* Settings.storyboard */, 51A16990235E10D600EB091F /* Settings.storyboard */,
51A16995235E10D600EB091F /* AboutViewController.swift */, 51A16995235E10D600EB091F /* AboutViewController.swift */,
51A16992235E10D600EB091F /* AddAccountViewController.swift */, 51A16992235E10D600EB091F /* AddAccountViewController.swift */,
516244E2241E19F000B61C47 /* ColorPaletteTableViewController.swift */,
516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */, 516A09382360A2AE00EAE89B /* SettingsAccountTableViewCell.swift */,
516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */, 516A091D23609A3600EAE89B /* SettingsAccountTableViewCell.xib */,
516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */, 516A093A2360A4A000EAE89B /* SettingsTableViewCell.xib */,
@ -2367,6 +2375,7 @@
84E8E0EA202F693600562D8F /* DetailWebView.swift */, 84E8E0EA202F693600562D8F /* DetailWebView.swift */,
84D52E941FE588BB00D14F5B /* DetailStatusBarView.swift */, 84D52E941FE588BB00D14F5B /* DetailStatusBarView.swift */,
5141E7552374A2890013FF27 /* DetailIconSchemeHandler.swift */, 5141E7552374A2890013FF27 /* DetailIconSchemeHandler.swift */,
5103A9972421643300410853 /* blank.html */,
B528F81D23333C7E00E735DD /* page.html */, B528F81D23333C7E00E735DD /* page.html */,
5142194A2353C1CF00E07E2C /* main_mac.js */, 5142194A2353C1CF00E07E2C /* main_mac.js */,
848362FC2262A30800DA1D35 /* styleSheet.css */, 848362FC2262A30800DA1D35 /* styleSheet.css */,
@ -2674,6 +2683,7 @@
51F85BF02272524100C787DC /* Credits.rtf */, 51F85BF02272524100C787DC /* Credits.rtf */,
51F85BEE2272520B00C787DC /* Thanks.rtf */, 51F85BEE2272520B00C787DC /* Thanks.rtf */,
51F85BF22272531500C787DC /* Dedication.rtf */, 51F85BF22272531500C787DC /* Dedication.rtf */,
5103A9B324216A4200410853 /* blank.html */,
51BB7C302335ACDE008E8144 /* page.html */, 51BB7C302335ACDE008E8144 /* page.html */,
514219572353C28900E07E2C /* main_ios.js */, 514219572353C28900E07E2C /* main_ios.js */,
51C452B72265178500C03939 /* styleSheet.css */, 51C452B72265178500C03939 /* styleSheet.css */,
@ -3472,6 +3482,7 @@
3B826DCD2385C89600FC1ADB /* AccountsFeedWrangler.xib in Resources */, 3B826DCD2385C89600FC1ADB /* AccountsFeedWrangler.xib in Resources */,
65ED4069235DEF6C0081F399 /* AccountsReaderAPI.xib in Resources */, 65ED4069235DEF6C0081F399 /* AccountsReaderAPI.xib in Resources */,
65ED406A235DEF6C0081F399 /* newsfoot.js in Resources */, 65ED406A235DEF6C0081F399 /* newsfoot.js in Resources */,
5103A9992421643300410853 /* blank.html in Resources */,
65ED406B235DEF6C0081F399 /* CrashReporterWindow.xib in Resources */, 65ED406B235DEF6C0081F399 /* CrashReporterWindow.xib in Resources */,
65ED406C235DEF6C0081F399 /* Credits.rtf in Resources */, 65ED406C235DEF6C0081F399 /* Credits.rtf in Resources */,
65ED406D235DEF6C0081F399 /* Inspector.storyboard in Resources */, 65ED406D235DEF6C0081F399 /* Inspector.storyboard in Resources */,
@ -3504,6 +3515,7 @@
516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */, 516A093723609A3600EAE89B /* SettingsAccountTableViewCell.xib in Resources */,
51F85BF32272531500C787DC /* Dedication.rtf in Resources */, 51F85BF32272531500C787DC /* Dedication.rtf in Resources */,
516A09422361248000EAE89B /* Inspector.storyboard in Resources */, 516A09422361248000EAE89B /* Inspector.storyboard in Resources */,
5103A9B424216A4200410853 /* blank.html in Resources */,
84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */, 84C9FCA42262A1B800D921D6 /* LaunchScreenPhone.storyboard in Resources */,
51F85BEB22724CB600C787DC /* About.rtf in Resources */, 51F85BEB22724CB600C787DC /* About.rtf in Resources */,
516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */, 516A093B2360A4A000EAE89B /* SettingsTableViewCell.xib in Resources */,
@ -3558,6 +3570,7 @@
3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */, 3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */,
55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */, 55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */,
49F40DF82335B71000552BF4 /* newsfoot.js in Resources */, 49F40DF82335B71000552BF4 /* newsfoot.js in Resources */,
5103A9982421643300410853 /* blank.html in Resources */,
84BAE64921CEDAF20046DB56 /* CrashReporterWindow.xib in Resources */, 84BAE64921CEDAF20046DB56 /* CrashReporterWindow.xib in Resources */,
84C9FC8E22629E8F00D921D6 /* Credits.rtf in Resources */, 84C9FC8E22629E8F00D921D6 /* Credits.rtf in Resources */,
84BBB12D20142A4700F054F5 /* Inspector.storyboard in Resources */, 84BBB12D20142A4700F054F5 /* Inspector.storyboard in Resources */,
@ -3957,6 +3970,7 @@
5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */, 5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */,
51C4529D22650A1000C03939 /* FaviconURLFinder.swift in Sources */, 51C4529D22650A1000C03939 /* FaviconURLFinder.swift in Sources */,
5142192A23522B5500E07E2C /* ImageViewController.swift in Sources */, 5142192A23522B5500E07E2C /* ImageViewController.swift in Sources */,
516244E3241E19F000B61C47 /* ColorPaletteTableViewController.swift in Sources */,
51C45258226508CF00C03939 /* AppAssets.swift in Sources */, 51C45258226508CF00C03939 /* AppAssets.swift in Sources */,
51FA73A82332BE880090D516 /* ExtractedArticle.swift in Sources */, 51FA73A82332BE880090D516 /* ExtractedArticle.swift in Sources */,
51C4527C2265091600C03939 /* MasterTimelineDefaultCellLayout.swift in Sources */, 51C4527C2265091600C03939 /* MasterTimelineDefaultCellLayout.swift in Sources */,

View File

@ -17,15 +17,23 @@ import Account
struct ArticleRenderer { struct ArticleRenderer {
typealias Rendering = (style: String, html: String, title: String, baseURL: String) typealias Rendering = (style: String, html: String, title: String, baseURL: String)
typealias Page = (url: URL, baseURL: URL)
struct Page {
let url: URL
let baseURL: URL
let html: String
init(name: String) {
url = Bundle.main.url(forResource: name, withExtension: "html")!
baseURL = url.deletingLastPathComponent()
html = try! NSString(contentsOfFile: url.path, encoding: String.Encoding.utf8.rawValue) as String
}
}
static var imageIconScheme = "nnwImageIcon" static var imageIconScheme = "nnwImageIcon"
static var page: Page = { static var blank = Page(name: "blank")
let url = Bundle.main.url(forResource: "page", withExtension: "html")! static var page = Page(name: "page")
let baseURL = url.deletingLastPathComponent()
return Page(url: url, baseURL: baseURL)
}()
private let article: Article? private let article: Article?
private let extractedArticle: ExtractedArticle? private let extractedArticle: ExtractedArticle?

View File

@ -117,25 +117,12 @@ function styleLocalFootnotes() {
} }
} }
function render(data, scrollY) { function processPage() {
document.getElementsByTagName("style")[0].innerHTML = data.style; wrapFrames();
wrapTables();
let title = document.getElementsByTagName("title")[0]; stripStyles();
title.textContent = data.title convertImgSrc();
flattenPreElements();
let base = document.getElementsByTagName("base")[0]; styleLocalFootnotes();
base.href = data.baseURL postRenderProcessing();
document.body.innerHTML = data.body;
window.scrollTo(0, scrollY);
wrapFrames()
wrapTables()
stripStyles()
convertImgSrc()
flattenPreElements()
styleLocalFootnotes()
postRenderProcessing()
} }

View File

@ -8,6 +8,24 @@
import UIKit import UIKit
enum UserInterfaceColorPalette: Int, CustomStringConvertible, CaseIterable {
case automatic = 0
case light = 1
case dark = 2
var description: String {
switch self {
case .automatic:
return NSLocalizedString("Automatic", comment: "Automatic")
case .light:
return NSLocalizedString("Light", comment: "Light")
case .dark:
return NSLocalizedString("Dark", comment: "Dark")
}
}
}
struct AppDefaults { struct AppDefaults {
static var shared: UserDefaults = { static var shared: UserDefaults = {
@ -17,6 +35,7 @@ struct AppDefaults {
}() }()
struct Key { struct Key {
static let userInterfaceColorPalette = "userInterfaceColorPalette"
static let lastImageCacheFlushDate = "lastImageCacheFlushDate" static let lastImageCacheFlushDate = "lastImageCacheFlushDate"
static let firstRunDate = "firstRunDate" static let firstRunDate = "firstRunDate"
static let timelineGroupByFeed = "timelineGroupByFeed" static let timelineGroupByFeed = "timelineGroupByFeed"
@ -41,6 +60,18 @@ struct AppDefaults {
return true return true
}() }()
static var userInterfaceColorPalette: UserInterfaceColorPalette {
get {
if let result = UserInterfaceColorPalette(rawValue: int(for: Key.userInterfaceColorPalette)) {
return result
}
return .automatic
}
set {
setInt(for: Key.userInterfaceColorPalette, newValue.rawValue)
}
}
static var addWebFeedAccountID: String? { static var addWebFeedAccountID: String? {
get { get {
return string(for: Key.addWebFeedAccountID) return string(for: Key.addWebFeedAccountID)
@ -160,7 +191,8 @@ struct AppDefaults {
} }
static func registerDefaults() { static func registerDefaults() {
let defaults: [String : Any] = [Key.timelineGroupByFeed: false, let defaults: [String : Any] = [Key.userInterfaceColorPalette: UserInterfaceColorPalette.automatic.rawValue,
Key.timelineGroupByFeed: false,
Key.refreshClearsReadArticles: false, Key.refreshClearsReadArticles: false,
Key.timelineNumberOfLines: 2, Key.timelineNumberOfLines: 2,
Key.timelineIconSize: IconSize.medium.rawValue, Key.timelineIconSize: IconSize.medium.rawValue,

View File

@ -135,7 +135,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func manualRefresh(errorHandler: @escaping (Error) -> ()) { func manualRefresh(errorHandler: @escaping (Error) -> ()) {
UIApplication.shared.connectedScenes.compactMap( { $0.delegate as? SceneDelegate } ).forEach { UIApplication.shared.connectedScenes.compactMap( { $0.delegate as? SceneDelegate } ).forEach {
$0.refreshInterface() $0.cleanUp()
} }
AccountManager.shared.refreshAll(errorHandler: errorHandler) AccountManager.shared.refreshAll(errorHandler: errorHandler)
} }

View File

@ -103,13 +103,17 @@ class ArticleViewController: UIViewController {
view.bottomAnchor.constraint(equalTo: pageViewController.view.bottomAnchor) view.bottomAnchor.constraint(equalTo: pageViewController.view.bottomAnchor)
]) ])
let controller = createWebViewController(article, updateView: false) let controller: WebViewController
if let state = restoreState { if let state = restoreState {
controller = createWebViewController(article, updateView: false)
controller.extractedArticle = state.extractedArticle controller.extractedArticle = state.extractedArticle
controller.isShowingExtractedArticle = state.isShowingExtractedArticle controller.isShowingExtractedArticle = state.isShowingExtractedArticle
controller.articleExtractorButtonState = state.articleExtractorButtonState controller.articleExtractorButtonState = state.articleExtractorButtonState
controller.windowScrollY = state.windowScrollY controller.windowScrollY = state.windowScrollY
} else {
controller = createWebViewController(article, updateView: true)
} }
articleExtractorButton.buttonState = controller.articleExtractorButtonState articleExtractorButton.buttonState = controller.articleExtractorButtonState
self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil) self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil)

View File

@ -11,10 +11,6 @@ import WebKit
class PreloadedWebView: WKWebView { class PreloadedWebView: WKWebView {
private struct MessageName {
static let domContentLoaded = "domContentLoaded"
}
private var isReady: Bool = false private var isReady: Bool = false
private var readyCompletion: ((PreloadedWebView) -> Void)? private var readyCompletion: ((PreloadedWebView) -> Void)?
@ -38,8 +34,8 @@ class PreloadedWebView: WKWebView {
} }
func preload() { func preload() {
configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.domContentLoaded) navigationDelegate = self
loadFileURL(ArticleRenderer.page.url, allowingReadAccessTo: ArticleRenderer.page.baseURL) loadFileURL(ArticleRenderer.blank.url, allowingReadAccessTo: ArticleRenderer.blank.baseURL)
} }
func ready(completion: @escaping (PreloadedWebView) -> Void) { func ready(completion: @escaping (PreloadedWebView) -> Void) {
@ -54,17 +50,15 @@ class PreloadedWebView: WKWebView {
// MARK: WKScriptMessageHandler // MARK: WKScriptMessageHandler
extension PreloadedWebView: WKScriptMessageHandler { extension PreloadedWebView: WKNavigationDelegate {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
if message.name == MessageName.domContentLoaded {
isReady = true isReady = true
if let completion = readyCompletion { if let completion = readyCompletion {
completeRequest(completion: completion) completeRequest(completion: completion)
readyCompletion = nil readyCompletion = nil
} }
} }
}
} }
@ -74,7 +68,7 @@ private extension PreloadedWebView {
func completeRequest(completion: @escaping (PreloadedWebView) -> Void) { func completeRequest(completion: @escaping (PreloadedWebView) -> Void) {
isReady = false isReady = false
configuration.userContentController.removeScriptMessageHandler(forName: MessageName.domContentLoaded) navigationDelegate = nil
completion(self) completion(self)
} }

View File

@ -393,13 +393,6 @@ extension WebViewController: UIScrollViewDelegate {
// MARK: JSON // MARK: JSON
private struct TemplateData: Codable {
let style: String
let body: String
let title: String
let baseURL: String
}
private struct ImageClickMessage: Codable { private struct ImageClickMessage: Codable {
let x: Float let x: Float
let y: Float let y: Float
@ -416,10 +409,12 @@ private extension WebViewController {
func loadWebView() { func loadWebView() {
guard isViewLoaded else { return } guard isViewLoaded else { return }
coordinator.webViewProvider.dequeueWebView() { webView in if let webView = webView {
self.renderPage(webView)
return
}
let webViewToRecycle = self.webView coordinator.webViewProvider.dequeueWebView() { webView in
self.renderPage(webViewToRecycle)
// Add the webview // Add the webview
webView.translatesAutoresizingMaskIntoConstraints = false webView.translatesAutoresizingMaskIntoConstraints = false
@ -451,9 +446,6 @@ private extension WebViewController {
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown) webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown)
self.renderPage(webView) self.renderPage(webView)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
self.recycleWebView(webViewToRecycle)
}
} }
@ -498,16 +490,16 @@ private extension WebViewController {
rendering = ArticleRenderer.noSelectionHTML(style: style) rendering = ArticleRenderer.noSelectionHTML(style: style)
} }
let templateData = TemplateData(style: rendering.style, body: rendering.html, title: rendering.title, baseURL: rendering.baseURL) let substitutions = [
"title": rendering.title,
"baseURL": rendering.baseURL,
"style": rendering.style,
"body": rendering.html,
"windowScrollY": String(windowScrollY)
]
let encoder = JSONEncoder() let html = try! MacroProcessor.renderedText(withTemplate: ArticleRenderer.page.html, substitutions: substitutions)
var render = "error();" webView.loadHTMLString(html, baseURL: ArticleRenderer.page.baseURL)
if let data = try? encoder.encode(templateData) {
let json = String(data: data, encoding: .utf8)!
render = "render(\(json), \(windowScrollY));"
}
webView.evaluateJavaScript(render)
} }

View File

@ -144,6 +144,9 @@ private extension KeyboardManager {
let markAllAsReadTitle = NSLocalizedString("Mark All as Read", comment: "Mark All as Read") let markAllAsReadTitle = NSLocalizedString("Mark All as Read", comment: "Mark All as Read")
keys.append(KeyboardManager.createKeyCommand(title: markAllAsReadTitle, action: "markAllAsRead:", input: "k", modifiers: [.command])) keys.append(KeyboardManager.createKeyCommand(title: markAllAsReadTitle, action: "markAllAsRead:", input: "k", modifiers: [.command]))
let cleanUp = NSLocalizedString("Clean Up", comment: "Clean Up")
keys.append(KeyboardManager.createKeyCommand(title: cleanUp, action: "cleanUp:", input: "h", modifiers: [.command, .shift]))
return keys return keys
} }

View File

@ -70,7 +70,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
tableView.dataSource = dataSource tableView.dataSource = dataSource
numberOfTextLines = AppDefaults.timelineNumberOfLines numberOfTextLines = AppDefaults.timelineNumberOfLines
iconSize = AppDefaults.timelineIconSize iconSize = AppDefaults.timelineIconSize
resetEstimatedRowHeight() tableView.rowHeight = calculateEstimatedRowHeight(forId: PrototypeFeedContent.feedId, withTitle: PrototypeFeedContent.longTitle, andFeed: PrototypeFeedContent.feedname)
if let titleView = Bundle.main.loadNibNamed("MasterTimelineTitleView", owner: self, options: nil)?[0] as? MasterTimelineTitleView { if let titleView = Bundle.main.loadNibNamed("MasterTimelineTitleView", owner: self, options: nil)?[0] as? MasterTimelineTitleView {
navigationItem.titleView = titleView navigationItem.titleView = titleView
@ -443,7 +443,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
if numberOfTextLines != AppDefaults.timelineNumberOfLines || iconSize != AppDefaults.timelineIconSize { if numberOfTextLines != AppDefaults.timelineNumberOfLines || iconSize != AppDefaults.timelineIconSize {
numberOfTextLines = AppDefaults.timelineNumberOfLines numberOfTextLines = AppDefaults.timelineNumberOfLines
iconSize = AppDefaults.timelineIconSize iconSize = AppDefaults.timelineIconSize
resetEstimatedRowHeight() tableView.rowHeight = calculateEstimatedRowHeight(forId: PrototypeFeedContent.feedId, withTitle: PrototypeFeedContent.longTitle, andFeed: PrototypeFeedContent.feedname)
reloadAllVisibleCells() reloadAllVisibleCells()
} }
} }
@ -487,26 +487,21 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
// MARK: Cell Configuring // MARK: Cell Configuring
private func resetEstimatedRowHeight() { private func calculateEstimatedRowHeight(forId prototypeID: String, withTitle title: String, andFeed feedName: String) -> CGFloat {
let longTitle = "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?"
let prototypeID = "prototype"
let status = ArticleStatus(articleID: prototypeID, read: false, starred: false, userDeleted: false, dateArrived: Date()) let status = ArticleStatus(articleID: prototypeID, read: false, starred: false, userDeleted: false, dateArrived: Date())
let prototypeArticle = Article(accountID: prototypeID, articleID: prototypeID, webFeedID: prototypeID, uniqueID: prototypeID, title: longTitle, contentHTML: nil, contentText: nil, url: nil, externalURL: nil, summary: nil, imageURL: nil, datePublished: nil, dateModified: nil, authors: nil, status: status) let prototypeArticle = Article(accountID: prototypeID, articleID: prototypeID, webFeedID: prototypeID, uniqueID: prototypeID, title: title, contentHTML: nil, contentText: nil, url: nil, externalURL: nil, summary: nil, imageURL: nil, datePublished: nil, dateModified: nil, authors: nil, status: status)
let prototypeCellData = MasterTimelineCellData(article: prototypeArticle, showFeedName: true, feedName: "Prototype Feed Name", iconImage: nil, showIcon: false, featuredImage: nil, numberOfLines: numberOfTextLines, iconSize: iconSize) let prototypeCellData = MasterTimelineCellData(article: prototypeArticle, showFeedName: true, feedName: feedName, iconImage: nil, showIcon: false, featuredImage: nil, numberOfLines: numberOfTextLines, iconSize: iconSize)
if UIApplication.shared.preferredContentSizeCategory.isAccessibilityCategory { if UIApplication.shared.preferredContentSizeCategory.isAccessibilityCategory {
let layout = MasterTimelineAccessibilityCellLayout(width: tableView.bounds.width, insets: tableView.safeAreaInsets, cellData: prototypeCellData) let layout = MasterTimelineAccessibilityCellLayout(width: tableView.bounds.width, insets: tableView.safeAreaInsets, cellData: prototypeCellData)
tableView.estimatedRowHeight = layout.height return layout.height
} else { } else {
let layout = MasterTimelineDefaultCellLayout(width: tableView.bounds.width, insets: tableView.safeAreaInsets, cellData: prototypeCellData) let layout = MasterTimelineDefaultCellLayout(width: tableView.bounds.width, insets: tableView.safeAreaInsets, cellData: prototypeCellData)
tableView.estimatedRowHeight = layout.height return layout.height
} }
} }
} }
// MARK: Searching // MARK: Searching
@ -633,7 +628,9 @@ private extension MasterTimelineViewController {
var snapshot = NSDiffableDataSourceSnapshot<Int, Article>() var snapshot = NSDiffableDataSourceSnapshot<Int, Article>()
snapshot.appendSections([0]) snapshot.appendSections([0])
snapshot.appendItems(coordinator.articles, toSection: 0) snapshot.appendItems(coordinator.articles, toSection: 0)
if coordinator.articles.count == 0 {
tableView.rowHeight = calculateEstimatedRowHeight(forId: PrototypeFeedContent.feedId, withTitle: PrototypeFeedContent.longTitle, andFeed: PrototypeFeedContent.feedname)
}
dataSource.apply(snapshot, animatingDifferences: animated) { [weak self] in dataSource.apply(snapshot, animatingDifferences: animated) { [weak self] in
self?.restoreSelectionIfNecessary(adjustScroll: false) self?.restoreSelectionIfNecessary(adjustScroll: false)
completion?() completion?()
@ -904,3 +901,10 @@ private extension MasterTimelineViewController {
} }
} }
fileprivate struct PrototypeFeedContent {
static let longTitle = "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?"
static let shortTitle = "prototype"
static let feedId = "feedId"
static let feedname = "prototype"
}

View File

@ -164,5 +164,7 @@
</dict> </dict>
</dict> </dict>
</array> </array>
<key>UserAgent</key>
<string>NetNewsWire (RSS Reader; https://ranchero.com/netnewswire/)</string>
</dict> </dict>
</plist> </plist>

11
iOS/Resources/blank.html Normal file
View File

@ -0,0 +1,11 @@
<html>
<head>
<style>
:root {
color-scheme: light dark;
}
</style>
</head>
<body>
</body>
</html>

View File

@ -1,17 +1,22 @@
<html> <html>
<head> <head>
<title></title> <title>[[title]]</title>
<base href="">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<style> <style>
:root { [[style]]
color-scheme: light dark;
}
</style> </style>
<script src="main.js"></script> <script src="main.js"></script>
<script src="main_ios.js"></script> <script src="main_ios.js"></script>
<script src="newsfoot.js" async="async"></script> <script src="newsfoot.js" async="async"></script>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
window.scrollTo(0, [[windowScrollY]]);
processPage();
})
</script>
<base href="[[baseURL]]">
</head> </head>
<body> <body>
[[body]]
</body> </body>
</html> </html>

View File

@ -1,4 +1,5 @@
:root { :root {
color-scheme: light dark;
font: -apple-system-body; font: -apple-system-body;
font-size: [[font-size]]px; font-size: [[font-size]]px;
} }

View File

@ -90,6 +90,10 @@ class RootSplitViewController: UISplitViewController {
coordinator.showAdd(.folder) coordinator.showAdd(.folder)
} }
@objc func cleanUp(_ sender: Any?) {
coordinator.cleanUp()
}
@objc func refresh(_ sender: Any?) { @objc func refresh(_ sender: Any?) {
appDelegate.manualRefresh(errorHandler: ErrorHandler.present(self)) appDelegate.manualRefresh(errorHandler: ErrorHandler.present(self))
} }

View File

@ -572,7 +572,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
fetchRequestQueue.cancelAllRequests() fetchRequestQueue.cancelAllRequests()
} }
func refreshInterface() { func cleanUp() {
if isReadFeedsFiltered { if isReadFeedsFiltered {
rebuildBackingStores() rebuildBackingStores()
} }

View File

@ -21,10 +21,13 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
window = UIWindow(windowScene: scene as! UIWindowScene) window = UIWindow(windowScene: scene as! UIWindowScene)
window!.tintColor = AppAssets.primaryAccentColor window!.tintColor = AppAssets.primaryAccentColor
updateUserInterfaceStyle()
window!.rootViewController = coordinator.start(for: window!.frame.size) window!.rootViewController = coordinator.start(for: window!.frame.size)
coordinator.restoreWindowState(session.stateRestorationActivity) coordinator.restoreWindowState(session.stateRestorationActivity)
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil)
if let shortcutItem = connectionOptions.shortcutItem { if let shortcutItem = connectionOptions.shortcutItem {
window!.makeKeyAndVisible() window!.makeKeyAndVisible()
handleShortcutItem(shortcutItem) handleShortcutItem(shortcutItem)
@ -81,8 +84,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
coordinator.suspend() coordinator.suspend()
} }
func refreshInterface() { func cleanUp() {
coordinator.refreshInterface() coordinator.cleanUp()
} }
} }
@ -102,4 +105,19 @@ private extension SceneDelegate {
} }
} }
@objc func userDefaultsDidChange() {
updateUserInterfaceStyle()
}
func updateUserInterfaceStyle() {
switch AppDefaults.userInterfaceColorPalette {
case .automatic:
window!.overrideUserInterfaceStyle = .unspecified
case .light:
window!.overrideUserInterfaceStyle = .light
case .dark:
window!.overrideUserInterfaceStyle = .dark
}
}
} }

View File

@ -0,0 +1,42 @@
//
// ColorPaletteTableViewController.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 3/15/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import UIKit
class ColorPaletteTableViewController: UITableViewController {
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return UserInterfaceColorPalette.allCases.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let rowColorPalette = UserInterfaceColorPalette.allCases[indexPath.row]
cell.textLabel?.text = String(describing: rowColorPalette)
if rowColorPalette == AppDefaults.userInterfaceColorPalette {
cell.accessoryType = .checkmark
} else {
cell.accessoryType = .none
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let colorPalette = UserInterfaceColorPalette(rawValue: indexPath.row) {
AppDefaults.userInterfaceColorPalette = colorPalette
}
navigationController?.popViewController(animated: true)
}
}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15705" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="9cW-lu-HoC"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="9cW-lu-HoC">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15706"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
<capability name="Named colors" minToolsVersion="9.0"/> <capability name="Named colors" minToolsVersion="9.0"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -19,7 +19,7 @@
<tableViewSection headerTitle="Notifications, Badge, Data, &amp; More" id="Bmb-Oi-RZK"> <tableViewSection headerTitle="Notifications, Badge, Data, &amp; More" id="Bmb-Oi-RZK">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="F9H-Kr-npj" style="IBUITableViewCellStyleDefault" id="zvg-7C-BlH" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="F9H-Kr-npj" style="IBUITableViewCellStyleDefault" id="zvg-7C-BlH" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="55.5" width="374" height="44"/> <rect key="frame" x="20" y="55.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zvg-7C-BlH" id="Tqk-Tu-E6K"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="zvg-7C-BlH" id="Tqk-Tu-E6K">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/> <rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
@ -40,7 +40,7 @@
<tableViewSection headerTitle="Accounts" id="0ac-Ze-Dh4"> <tableViewSection headerTitle="Accounts" id="0ac-Ze-Dh4">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="6sn-wY-hHH" style="IBUITableViewCellStyleDefault" id="XHc-rQ-7FK" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="6sn-wY-hHH" style="IBUITableViewCellStyleDefault" id="XHc-rQ-7FK" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="155.5" width="374" height="44"/> <rect key="frame" x="20" y="155.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="XHc-rQ-7FK" id="nmL-EM-Bsi"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="XHc-rQ-7FK" id="nmL-EM-Bsi">
<rect key="frame" x="0.0" y="0.0" width="343" height="44"/> <rect key="frame" x="0.0" y="0.0" width="343" height="44"/>
@ -61,7 +61,7 @@
<tableViewSection headerTitle="Feeds" id="hAC-uA-RbS"> <tableViewSection headerTitle="Feeds" id="hAC-uA-RbS">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="4Hg-B3-zAE" style="IBUITableViewCellStyleDefault" id="glf-Pg-s3P" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="4Hg-B3-zAE" style="IBUITableViewCellStyleDefault" id="glf-Pg-s3P" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="255.5" width="374" height="44"/> <rect key="frame" x="20" y="255.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="glf-Pg-s3P" id="bPA-43-Oqh"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="glf-Pg-s3P" id="bPA-43-Oqh">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/> <rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
@ -317,136 +317,172 @@
</tableViewCell> </tableViewCell>
</cells> </cells>
</tableViewSection> </tableViewSection>
<tableViewSection headerTitle="Help" id="TkH-4v-yhk"> <tableViewSection headerTitle="Appearance" id="TkH-4v-yhk">
<cells> <cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="40W-2p-ne4" style="IBUITableViewCellStyleDefault" id="Om7-lH-RUh" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target"> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" id="EvG-yE-gDF" customClass="VibrantBasicTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="819.5" width="374" height="44"/> <rect key="frame" x="0.0" y="819.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Om7-lH-RUh" id="vrJ-nE-HMP">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="NetNewsWire Help" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="40W-2p-ne4">
<rect key="frame" x="20" y="0.0" width="334" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="lOk-Dh-GfZ" style="IBUITableViewCellStyleDefault" id="GWZ-jk-qU6" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="863.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="GWZ-jk-qU6" id="ZgS-bo-xDl">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Website" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="lOk-Dh-GfZ">
<rect key="frame" x="20" y="0.0" width="334" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="Pm8-6D-fdE" style="IBUITableViewCellStyleDefault" id="3cU-BG-6kK" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="907.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3cU-BG-6kK" id="Qm0-SY-0vx">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="How To Support NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="Pm8-6D-fdE">
<rect key="frame" x="20" y="0.0" width="334" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="TEA-EG-V6d" style="IBUITableViewCellStyleDefault" id="4yc-ig-I61" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="951.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="4yc-ig-I61" id="uQl-VP-9p9">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="GitHub Repository" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="TEA-EG-V6d">
<rect key="frame" x="20" y="0.0" width="334" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="Q9a-Pi-uCc" style="IBUITableViewCellStyleDefault" id="mSW-A7-8lf" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="995.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="mSW-A7-8lf" id="shF-ro-Zpx">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Bug Tracker" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="Q9a-Pi-uCc">
<rect key="frame" x="20" y="0.0" width="334" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="dWz-1o-EpJ" style="IBUITableViewCellStyleDefault" id="2MG-qn-idJ" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1039.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="2MG-qn-idJ" id="gP9-ry-keC">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Technotes" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="dWz-1o-EpJ">
<rect key="frame" x="20" y="0.0" width="334" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="2o6-8W-nyK" style="IBUITableViewCellStyleDefault" id="he9-Ql-yfa" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1083.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="he9-Ql-yfa" id="q6L-C8-H9a">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="NetNewsWire Slack" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="2o6-8W-nyK">
<rect key="frame" x="20" y="0.0" width="334" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="Uwu-af-31r" style="IBUITableViewCellStyleDefault" id="EvG-yE-gDF" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="1127.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EvG-yE-gDF" id="wBN-zJ-6pN"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="EvG-yE-gDF" id="wBN-zJ-6pN">
<rect key="frame" x="0.0" y="0.0" width="343" height="44"/> <rect key="frame" x="0.0" y="0.0" width="343" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="About NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="Uwu-af-31r"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Color Palette" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2Fp-li-dGP">
<rect key="frame" x="20" y="0.0" width="315" height="44"/> <rect key="frame" x="20" y="11.5" width="99" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Automatic" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="16m-Ns-Y8V">
<rect key="frame" x="257" y="11.5" width="78" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="16m-Ns-Y8V" firstAttribute="trailing" secondItem="wBN-zJ-6pN" secondAttribute="trailingMargin" id="R1e-JV-gyn"/>
<constraint firstItem="16m-Ns-Y8V" firstAttribute="centerY" secondItem="wBN-zJ-6pN" secondAttribute="centerY" id="SXW-Hv-5bK"/>
<constraint firstItem="2Fp-li-dGP" firstAttribute="leading" secondItem="wBN-zJ-6pN" secondAttribute="leadingMargin" id="g40-ob-1IK"/>
<constraint firstItem="2Fp-li-dGP" firstAttribute="centerY" secondItem="wBN-zJ-6pN" secondAttribute="centerY" id="geh-mE-9X7"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="detail" destination="16m-Ns-Y8V" id="Z0c-2s-usf"/>
<outlet property="label" destination="2Fp-li-dGP" id="gyE-t6-U2X"/>
</connections>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection headerTitle="Help" id="CS8-fJ-ghn">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="uGk-2d-oFc" style="IBUITableViewCellStyleDefault" id="Tle-IV-D40" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="919.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Tle-IV-D40" id="IJD-ZB-8Wm">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="NetNewsWire Help" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="uGk-2d-oFc">
<rect key="frame" x="20" y="0.0" width="334" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="6G3-yV-Eyh" style="IBUITableViewCellStyleDefault" id="Tbf-fE-nfx" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="963.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Tbf-fE-nfx" id="beV-vI-g3r">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Website" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="6G3-yV-Eyh">
<rect key="frame" x="20" y="0.0" width="334" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="lfL-bQ-sOp" style="IBUITableViewCellStyleDefault" id="mFn-fE-zqa" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="1007.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="mFn-fE-zqa" id="jTe-mf-MRj">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="How To Support NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="lfL-bQ-sOp">
<rect key="frame" x="15" y="0.0" width="351" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="DDJ-8P-3YY" style="IBUITableViewCellStyleDefault" id="iGs-ze-4gQ" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="1051.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="iGs-ze-4gQ" id="EqZ-rF-N0l">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="GitHub Repository" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="DDJ-8P-3YY">
<rect key="frame" x="15" y="0.0" width="351" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="DsV-Qv-X4K" style="IBUITableViewCellStyleDefault" id="taJ-sg-wnU" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="1095.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="taJ-sg-wnU" id="axB-si-1KM">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Bug Tracker" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="DsV-Qv-X4K">
<rect key="frame" x="15" y="0.0" width="351" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="zMz-hU-UYU" style="IBUITableViewCellStyleDefault" id="OXi-cg-ab9" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="1139.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="OXi-cg-ab9" id="npR-a0-9wv">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Technotes" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="zMz-hU-UYU">
<rect key="frame" x="15" y="0.0" width="351" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="T7x-zl-6Yf" style="IBUITableViewCellStyleDefault" id="VpI-0o-3Px" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="1183.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="VpI-0o-3Px" id="xRH-i4-vne">
<rect key="frame" x="0.0" y="0.0" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="NetNewsWire Slack" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="T7x-zl-6Yf">
<rect key="frame" x="15" y="0.0" width="351" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="NeD-y8-KrM" style="IBUITableViewCellStyleDefault" id="TIX-yK-rC6" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="1227.5" width="374" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="TIX-yK-rC6" id="qr8-EN-Ofg">
<rect key="frame" x="0.0" y="0.0" width="355" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="About NetNewsWire" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontForContentSizeCategory="YES" adjustsFontSizeToFit="NO" id="NeD-y8-KrM">
<rect key="frame" x="15" y="0.0" width="332" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/> <fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<nil key="textColor"/> <nil key="textColor"/>
@ -471,6 +507,7 @@
</barButtonItem> </barButtonItem>
</navigationItem> </navigationItem>
<connections> <connections>
<outlet property="colorPaletteDetailLabel" destination="16m-Ns-Y8V" id="zdj-Ag-ZUw"/>
<outlet property="confirmMarkAllAsReadSwitch" destination="UOo-9z-IuL" id="yLZ-Kf-wDt"/> <outlet property="confirmMarkAllAsReadSwitch" destination="UOo-9z-IuL" id="yLZ-Kf-wDt"/>
<outlet property="groupByFeedSwitch" destination="JNi-Wz-RbU" id="TwH-Kd-o6N"/> <outlet property="groupByFeedSwitch" destination="JNi-Wz-RbU" id="TwH-Kd-o6N"/>
<outlet property="refreshClearsReadArticlesSwitch" destination="duV-CN-JmH" id="xTd-jF-Ei1"/> <outlet property="refreshClearsReadArticlesSwitch" destination="duV-CN-JmH" id="xTd-jF-Ei1"/>
@ -968,6 +1005,45 @@
</objects> </objects>
<point key="canvasLocation" x="3096" y="151"/> <point key="canvasLocation" x="3096" y="151"/>
</scene> </scene>
<!--Color Palette-->
<scene sceneID="1mT-fg-ezs">
<objects>
<tableViewController storyboardIdentifier="ColorPaletteTableViewController" title="Color Palette" id="fO4-og-e41" customClass="ColorPaletteTableViewController" customModule="NetNewsWire" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="insetGrouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="I8p-P9-C8h">
<rect key="frame" x="0.0" y="0.0" width="414" height="808"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="6tf-Xb-7b8" style="IBUITableViewCellStyleDefault" id="BIC-Zk-QMD" customClass="VibrantTableViewCell" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="20" y="55.5" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="BIC-Zk-QMD" id="7xj-8Y-ejz">
<rect key="frame" x="0.0" y="0.0" width="374" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6tf-Xb-7b8">
<rect key="frame" x="20" y="0.0" width="334" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<sections/>
<connections>
<outlet property="dataSource" destination="fO4-og-e41" id="155-KJ-Ps6"/>
<outlet property="delegate" destination="fO4-og-e41" id="zBh-hF-yaJ"/>
</connections>
</tableView>
<simulatedNavigationBarMetrics key="simulatedTopBarMetrics" translucent="NO" prompted="NO"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ODK-C8-FL4" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3743" y="151"/>
</scene>
</scenes> </scenes>
<resources> <resources>
<image name="accountFeedWrangler" width="512" height="512"/> <image name="accountFeedWrangler" width="512" height="512"/>

View File

@ -21,6 +21,7 @@ class SettingsViewController: UITableViewController {
@IBOutlet weak var refreshClearsReadArticlesSwitch: UISwitch! @IBOutlet weak var refreshClearsReadArticlesSwitch: UISwitch!
@IBOutlet weak var confirmMarkAllAsReadSwitch: UISwitch! @IBOutlet weak var confirmMarkAllAsReadSwitch: UISwitch!
@IBOutlet weak var showFullscreenArticlesSwitch: UISwitch! @IBOutlet weak var showFullscreenArticlesSwitch: UISwitch!
@IBOutlet weak var colorPaletteDetailLabel: UILabel!
var scrollToArticlesSection = false var scrollToArticlesSection = false
weak var presentingParentController: UIViewController? weak var presentingParentController: UIViewController?
@ -33,7 +34,6 @@ class SettingsViewController: UITableViewController {
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidAddAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidAddAccount, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidDeleteAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(accountsDidChange), name: .UserDidDeleteAccount, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange), name: .DisplayNameDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange), name: .DisplayNameDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange), name: UserDefaults.didChangeNotification, object: nil)
tableView.register(UINib(nibName: "SettingsAccountTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsAccountTableViewCell") tableView.register(UINib(nibName: "SettingsAccountTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsAccountTableViewCell")
tableView.register(UINib(nibName: "SettingsTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsTableViewCell") tableView.register(UINib(nibName: "SettingsTableViewCell", bundle: nil), forCellReuseIdentifier: "SettingsTableViewCell")
@ -75,6 +75,8 @@ class SettingsViewController: UITableViewController {
showFullscreenArticlesSwitch.isOn = false showFullscreenArticlesSwitch.isOn = false
} }
colorPaletteDetailLabel.text = String(describing: AppDefaults.userInterfaceColorPalette)
let buildLabel = NonIntrinsicLabel(frame: CGRect(x: 32.0, y: 0.0, width: 0.0, height: 0.0)) let buildLabel = NonIntrinsicLabel(frame: CGRect(x: 32.0, y: 0.0, width: 0.0, height: 0.0))
buildLabel.font = UIFont.systemFont(ofSize: 11.0) buildLabel.font = UIFont.systemFont(ofSize: 11.0)
buildLabel.textColor = UIColor.gray buildLabel.textColor = UIColor.gray
@ -192,6 +194,9 @@ class SettingsViewController: UITableViewController {
break break
} }
case 5: case 5:
let colorPalette = UIStoryboard.settings.instantiateController(ofType: ColorPaletteTableViewController.self)
self.navigationController?.pushViewController(colorPalette, animated: true)
case 6:
switch indexPath.row { switch indexPath.row {
case 0: case 0:
openURL("https://ranchero.com/netnewswire/help/ios/5.0/en/") openURL("https://ranchero.com/netnewswire/help/ios/5.0/en/")
@ -305,10 +310,6 @@ class SettingsViewController: UITableViewController {
tableView.reloadData() tableView.reloadData()
} }
@objc func userDefaultsDidChange() {
tableView.reloadData()
}
} }
// MARK: OPML Document Picker // MARK: OPML Document Picker

View File

@ -17,13 +17,13 @@ extension UITableView {
selectRow(at: indexPath, animated: animations.contains(.select), scrollPosition: .none) selectRow(at: indexPath, animated: animations.contains(.select), scrollPosition: .none)
if let visibleIndexPaths = indexPathsForRows(in: safeAreaLayoutGuide.layoutFrame) { if let visibleIndexPaths = indexPathsForRows(in: safeAreaLayoutGuide.layoutFrame) {
if !(visibleIndexPaths.contains(indexPath) && cellCompletelyVisable(indexPath)) { if !(visibleIndexPaths.contains(indexPath) && cellCompletelyVisible(indexPath)) {
scrollToRow(at: indexPath, at: .middle, animated: animations.contains(.scroll)) scrollToRow(at: indexPath, at: .middle, animated: animations.contains(.scroll))
} }
} }
} }
func cellCompletelyVisable(_ indexPath: IndexPath) -> Bool { func cellCompletelyVisible(_ indexPath: IndexPath) -> Bool {
let rect = rectForRow(at: indexPath) let rect = rectForRow(at: indexPath)
return safeAreaLayoutGuide.layoutFrame.contains(rect) return safeAreaLayoutGuide.layoutFrame.contains(rect)
} }

View File

@ -71,6 +71,7 @@ class VibrantTableViewCell: UITableViewCell {
class VibrantBasicTableViewCell: VibrantTableViewCell { class VibrantBasicTableViewCell: VibrantTableViewCell {
@IBOutlet private var label: UILabel! @IBOutlet private var label: UILabel!
@IBOutlet private var detail: UILabel!
@IBOutlet private var icon: UIImageView! @IBOutlet private var icon: UIImageView!
@IBInspectable var imageNormal: UIImage? @IBInspectable var imageNormal: UIImage?
@ -88,6 +89,7 @@ class VibrantBasicTableViewCell: VibrantTableViewCell {
super.updateVibrancy(animated: animated) super.updateVibrancy(animated: animated)
updateIconVibrancy(icon, color: iconTint, image: iconImage, animated: animated) updateIconVibrancy(icon, color: iconTint, image: iconImage, animated: animated)
updateLabelVibrancy(label, color: labelColor, animated: animated) updateLabelVibrancy(label, color: labelColor, animated: animated)
updateLabelVibrancy(detail, color: secondaryLabelColor, animated: animated)
} }
private func updateIconVibrancy(_ icon: UIImageView?, color: UIColor, image: UIImage?, animated: Bool) { private func updateIconVibrancy(_ icon: UIImageView?, color: UIColor, image: UIImage?, animated: Bool) {