This commit is contained in:
Brent Simmons 2017-12-05 21:58:29 -08:00
commit a4c59a75ad
29 changed files with 237 additions and 11 deletions

View File

@ -5,7 +5,40 @@
<link>https://ranchero.com/downloads/evergreen-beta.xml</link>
<description>Most recent Evergreen changes with links to updates.</description>
<language>en</language>
<item>
<title>Version 1.0d23</title>
<description><![CDATA[
<p>Decorate the tree!</p>
]]></description>
<pubDate>Tue, 5 Dec 2017 13:00:00 -0800</pubDate>
<enclosure url="https://ranchero.com/downloads/Evergreen1.0d23.zip" sparkle:version="515" sparkle:shortVersionString="1.0d23" length="7306243" type="application/zip" />
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
</item>
<item>
<title>Version 1.0d22</title>
<description><![CDATA[
<p>Refresh all after importing OPML.</p>
<p>Fetch unread counts from database at startup.</p>
<p>Make resizing the timeline view marginally faster.</p>
<p>Make the favicon downloading system use a little less memory.</p>
<p>Read a newly-added feed immediately, instead of waiting for the next refresh-all.</p>
<p>Use 38-pt-wide toolbar icons, a la Mail.</p>
<p>Parse RSS 1.0 (RDF) feeds. Pinboard uses these (I couldnt find them anywhere else).</p>
<p>Make the window title-less. You can bring back the title using a <a href="https://github.com/brentsimmons/Evergreen/blob/master/Technotes/HiddenPrefs.md">hidden pref</a>.</p>
<p>Save feed authors.</p>
<p>Save article authors.</p>
<p>Use updated @2x next-unread icon from Brad.</p>
<p>Fix bug detecting feed type for Dr. Drangs JSON Feed.</p>
<p>Fix bug detecting Macworlds RSS feed as an RSS feed. The feed doesnt start with the standard XML header.</p>
<p>Set user-agent on the detail views webview.</p>
]]></description>
<pubDate>Mon, 4 Dec 2017 13:00:00 -0800</pubDate>
<enclosure url="https://ranchero.com/downloads/Evergreen1.0d22.zip" sparkle:version="514" sparkle:shortVersionString="1.0d22" length="7153185" type="application/zip" />
<sparkle:minimumSystemVersion>10.13</sparkle:minimumSystemVersion>
</item>
<item>
<title>Version 1.0d20</title>
<description><![CDATA[

View File

@ -0,0 +1,68 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "icon_16x16@2x.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "icon_32x32@2x.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "icon_128x128@2x.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "icon_256x256@2x.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "icon_512x512@2x.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 KiB

After

Width:  |  Height:  |  Size: 371 KiB

View File

@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0d21</string>
<string>1.0d23</string>
<key>CFBundleVersion</key>
<string>513</string>
<string>515</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSHumanReadableCopyright</key>

View File

@ -10,14 +10,17 @@ import Foundation
import WebKit
import RSCore
import Data
import RSWeb
class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDelegate {
final class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDelegate {
var webview: WKWebView!
var noSelectionView: NoSelectionView!
var article: Article? {
didSet {
reloadHTML()
showOrHideWebView()
}
}
@ -49,11 +52,16 @@ class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDelegate
webview.uiDelegate = self
webview.navigationDelegate = self
webview.translatesAutoresizingMaskIntoConstraints = false
if let userAgent = UserAgent.fromInfoPlist() {
webview.customUserAgent = userAgent
}
noSelectionView = NoSelectionView(frame: self.view.bounds)
let boxView = self.view as! DetailBox
boxView.contentView = webview
boxView.rs_addFullSizeConstraints(forSubview: webview)
boxView.viewController = self
showOrHideWebView()
}
// MARK: Notifications
@ -88,7 +96,27 @@ class DetailViewController: NSViewController, WKNavigationDelegate, WKUIDelegate
webview.loadHTMLString("", baseURL: nil)
}
}
private func showOrHideWebView() {
if let _ = article {
switchToView(webview)
}
else {
switchToView(noSelectionView)
}
}
private func switchToView(_ view: NSView) {
let boxView = self.view as! DetailBox
if boxView.contentView == view {
return
}
boxView.contentView = view
boxView.rs_addFullSizeConstraints(forSubview: view)
}
// MARK: WKNavigationDelegate
public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
@ -142,7 +170,7 @@ extension DetailViewController: WKScriptMessageHandler {
}
}
class DetailBox: NSBox {
final class DetailBox: NSBox {
weak var viewController: DetailViewController?
@ -156,3 +184,25 @@ class DetailBox: NSBox {
viewController?.viewDidEndLiveResize()
}
}
final class NoSelectionView: NSView {
private var didConfigureLayer = false
override var wantsUpdateLayer: Bool {
return true
}
override func updateLayer() {
guard !didConfigureLayer else {
return
}
if let layer = layer {
let color = appDelegate.currentTheme.color(forKey: "MainWindow.Detail.noSelectionView.backgroundColor")
layer.backgroundColor = color.cgColor
didConfigureLayer = true
}
}
}

View File

@ -108,6 +108,14 @@
<integer>4</integer>
</dict>
</dict>
<key>Detail</key>
<dict>
<key>noSelectionView</key>
<dict>
<key>backgroundColor</key>
<string>FFFFFF</string>
</dict>
</dict>
</dict>
</dict>
</dict>

View File

@ -140,6 +140,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
DispatchQueue.main.async {
self.updateUnreadCount()
self.fetchAllUnreadCounts()
}
}
@ -302,6 +303,10 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
}
importOPMLItems(children, parentFolder: nil)
dirty = true
DispatchQueue.main.async {
self.refreshAll()
}
}
public func updateUnreadCounts(for feeds: Set<Feed>) {
@ -622,6 +627,28 @@ private extension Account {
NotificationCenter.default.post(name: .StatusesDidChange, object: self, userInfo: [UserInfoKey.statuses: statuses, UserInfoKey.articles: articles, UserInfoKey.feeds: feeds])
}
func fetchAllUnreadCounts() {
database.fetchAllNonZeroUnreadCounts { (unreadCountDictionary) in
if unreadCountDictionary.isEmpty {
return
}
self.flattenedFeeds().forEach{ (feed) in
// When the unread count is zero, it wont appear in unreadCountDictionary.
if let unreadCount = unreadCountDictionary[feed] {
feed.unreadCount = unreadCount
}
else {
feed.unreadCount = 0
}
}
}
}
}
// MARK: - Container Overrides

View File

@ -174,6 +174,37 @@ final class ArticlesTable: DatabaseTable {
}
}
func fetchAllUnreadCounts(_ completion: @escaping UnreadCountCompletionBlock) {
// Returns only where unreadCount > 0.
let cutoffDate = articleCutoffDate
queue.fetch { (database) in
let sql = "select distinct feedID, count(*) from articles natural join statuses where read=0 and userDeleted=0 and (starred=1 or dateArrived>?) group by feedID;"
guard let resultSet = database.executeQuery(sql, withArgumentsIn: [cutoffDate]) else {
DispatchQueue.main.async() {
completion(UnreadCountDictionary())
}
return
}
var d = UnreadCountDictionary()
while resultSet.next() {
let unreadCount = resultSet.long(forColumnIndex: 1)
if let feedID = resultSet.string(forColumnIndex: 0) {
d[feedID] = unreadCount
}
}
DispatchQueue.main.async() {
completion(d)
}
}
}
func fetchStarredAndUnreadCount(_ feeds: Set<Feed>, _ callback: @escaping (Int) -> Void) {
if feeds.isEmpty {

View File

@ -57,7 +57,7 @@ public final class Database {
// MARK: - Unread Counts
public func fetchUnreadCounts(for feeds: Set<Feed>, _ completion: @escaping UnreadCountCompletionBlock) {
articlesTable.fetchUnreadCounts(feeds, completion)
}
@ -71,6 +71,11 @@ public final class Database {
articlesTable.fetchStarredAndUnreadCount(feeds, callback)
}
public func fetchAllNonZeroUnreadCounts(_ completion: @escaping UnreadCountCompletionBlock) {
articlesTable.fetchAllUnreadCounts(completion)
}
// MARK: - Saving and Updating Articles
public func update(feed: Feed, parsedFeed: ParsedFeed, completion: @escaping UpdateArticlesWithFeedCompletionBlock) {

View File

@ -12,6 +12,10 @@ import Data
public struct UnreadCountDictionary {
private var dictionary = [String: Int]()
public var isEmpty: Bool {
return dictionary.count < 1
}
subscript(_ feedID: String) -> Int? {
get {