Create SmartFeed class. It’s for Unread and Starred pseudo-feeds — and will also be used later on by predicate-based smart feeds.

This commit is contained in:
Brent Simmons 2017-11-19 15:40:02 -08:00
parent 9f415dfab7
commit 9c571271e6
9 changed files with 108 additions and 13 deletions

View File

@ -13,6 +13,8 @@
842E45E51ED8C6B7000A8B52 /* MainWindowSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45E41ED8C6B7000A8B52 /* MainWindowSplitView.swift */; };
842E45E71ED8C747000A8B52 /* DB5.plist in Resources */ = {isa = PBXBuildFile; fileRef = 842E45E61ED8C747000A8B52 /* DB5.plist */; };
84513F901FAA63950023A1A9 /* FeedListControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */; };
845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845EE7B01FC2366500854A1F /* StarredFeedDelegate.swift */; };
845EE7C11FC2488C00854A1F /* SmartFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845EE7C01FC2488C00854A1F /* SmartFeed.swift */; };
845F52ED1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */; };
846E773D1F6EF67A00A165E2 /* Account.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846E773A1F6EF5D700A165E2 /* Account.framework */; };
846E773E1F6EF67A00A165E2 /* Account.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 846E773A1F6EF5D700A165E2 /* Account.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -99,7 +101,7 @@
84F204DE1FAACB8B0076E152 /* FeedListTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DD1FAACB8B0076E152 /* FeedListTimelineViewController.swift */; };
84F204E01FAACBB30076E152 /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; };
84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */; };
84F2D5381FC22FCC00998D64 /* TodayFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5361FC22FCB00998D64 /* TodayFeed.swift */; };
84F2D5381FC22FCC00998D64 /* TodayFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5361FC22FCB00998D64 /* TodayFeedDelegate.swift */; };
84F2D53A1FC2308B00998D64 /* UnreadFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5391FC2308B00998D64 /* UnreadFeed.swift */; };
84FB9A2F1EDCD6C4003D53B9 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; };
84FB9A301EDCD6C4003D53B9 /* Sparkle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -400,6 +402,8 @@
842E45E61ED8C747000A8B52 /* DB5.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = DB5.plist; path = Evergreen/Resources/DB5.plist; sourceTree = "<group>"; };
84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListControlsView.swift; sourceTree = "<group>"; };
845B14A51FC2299E0013CF92 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
845EE7B01FC2366500854A1F /* StarredFeedDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarredFeedDelegate.swift; sourceTree = "<group>"; };
845EE7C01FC2488C00854A1F /* SmartFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmartFeed.swift; sourceTree = "<group>"; };
845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedPasteboardWriter.swift; sourceTree = "<group>"; };
846E77161F6EF5D000A165E2 /* Database.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Database.xcodeproj; path = Frameworks/Database/Database.xcodeproj; sourceTree = "<group>"; };
846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; sourceTree = "<group>"; };
@ -479,7 +483,7 @@
84F204DD1FAACB8B0076E152 /* FeedListTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListTimelineViewController.swift; sourceTree = "<group>"; };
84F204DF1FAACBB30076E152 /* ArticleArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleArray.swift; sourceTree = "<group>"; };
84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PseudoFeed.swift; sourceTree = "<group>"; };
84F2D5361FC22FCB00998D64 /* TodayFeed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodayFeed.swift; sourceTree = "<group>"; };
84F2D5361FC22FCB00998D64 /* TodayFeedDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodayFeedDelegate.swift; sourceTree = "<group>"; };
84F2D5391FC2308B00998D64 /* UnreadFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnreadFeed.swift; sourceTree = "<group>"; };
84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Vendor/Sparkle.framework; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */
@ -900,8 +904,10 @@
isa = PBXGroup;
children = (
84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */,
84F2D5361FC22FCB00998D64 /* TodayFeed.swift */,
84F2D5391FC2308B00998D64 /* UnreadFeed.swift */,
845EE7C01FC2488C00854A1F /* SmartFeed.swift */,
84F2D5361FC22FCB00998D64 /* TodayFeedDelegate.swift */,
845EE7B01FC2366500854A1F /* StarredFeedDelegate.swift */,
);
name = PseudoFeeds;
path = Evergreen/PseudoFeeds;
@ -1285,6 +1291,7 @@
849A97661ED9EB96007D329B /* SidebarViewController.swift in Sources */,
849A97641ED9EB96007D329B /* SidebarOutlineView.swift in Sources */,
84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */,
845EE7C11FC2488C00854A1F /* SmartFeed.swift in Sources */,
84702AA41FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift in Sources */,
849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */,
849A97651ED9EB96007D329B /* SidebarTreeControllerDelegate.swift in Sources */,
@ -1299,10 +1306,11 @@
849A978A1ED9ECEF007D329B /* ArticleStylesManager.swift in Sources */,
849A97791ED9EC04007D329B /* TimelineStringUtilities.swift in Sources */,
84F204CE1FAACB660076E152 /* FeedListViewController.swift in Sources */,
845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */,
849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */,
849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */,
849A97831ED9EC63007D329B /* StatusBarView.swift in Sources */,
84F2D5381FC22FCC00998D64 /* TodayFeed.swift in Sources */,
84F2D5381FC22FCC00998D64 /* TodayFeedDelegate.swift in Sources */,
849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */,
849A97921ED9EF65007D329B /* IndeterminateProgressWindowController.swift in Sources */,
849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */,

View File

@ -97,7 +97,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
currentTheme = themeLoader.defaultTheme
pseudoFeeds = [TodayFeed(), UnreadFeed()]
let todayFeed = SmartFeed(delegate: TodayFeedDelegate())
let unreadFeed = UnreadFeed()
let starredFeed = SmartFeed(delegate: StarredFeedDelegate())
pseudoFeeds = [todayFeed, unreadFeed, starredFeed]
createAndShowMainWindow()

View File

@ -1,5 +1,5 @@
//
// TodayFeed.swift
// SmartFeed.swift
// Evergreen
//
// Created by Brent Simmons on 11/19/17.
@ -7,12 +7,22 @@
//
import Foundation
import RSCore
import Data
import Account
final class TodayFeed: PseudoFeed {
protocol SmartFeedDelegate: DisplayNameProvider {
let nameForDisplay = NSLocalizedString("Today", comment: "Today pseudo-feed title")
func fetchUnreadCount(for: Account, callback: @escaping (Int) -> Void)
}
final class SmartFeed: PseudoFeed {
var nameForDisplay: String {
get {
return delegate.nameForDisplay
}
}
var unreadCount = 0 {
didSet {
@ -22,11 +32,13 @@ final class TodayFeed: PseudoFeed {
}
}
private let delegate: SmartFeedDelegate
private var timer: Timer?
private var unreadCounts = [Account: Int]()
init() {
init(delegate: SmartFeedDelegate) {
self.delegate = delegate
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
startTimer() // Fetch unread count at startup
}
@ -39,13 +51,13 @@ final class TodayFeed: PseudoFeed {
}
}
private extension TodayFeed {
private extension SmartFeed {
// MARK: - Unread Counts
private func fetchUnreadCount(for account: Account) {
account.fetchUnreadCountForToday { (accountUnreadCount) in
delegate.fetchUnreadCount(for: account) { (accountUnreadCount) in
self.unreadCounts[account] = accountUnreadCount
self.updateUnreadCount()
}
@ -82,10 +94,9 @@ private extension TodayFeed {
stopTimer()
timer = Timer.scheduledTimer(withTimeInterval: TodayFeed.fetchCoalescingDelay, repeats: false, block: { (timer) in
timer = Timer.scheduledTimer(withTimeInterval: SmartFeed.fetchCoalescingDelay, repeats: false, block: { (timer) in
self.fetchUnreadCounts()
self.stopTimer()
})
}
}

View File

@ -0,0 +1,22 @@
//
// StarredFeedDelegate.swift
// Evergreen
//
// Created by Brent Simmons on 11/19/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
import Data
import Account
struct StarredFeedDelegate: SmartFeedDelegate {
let nameForDisplay = NSLocalizedString("Starred", comment: "Starred pseudo-feed title")
func fetchUnreadCount(for account: Account, callback: @escaping (Int) -> Void) {
account.fetchUnreadCountForStarredArticles(callback)
}
}

View File

@ -0,0 +1,22 @@
//
// TodayFeedDelegate.swift
// Evergreen
//
// Created by Brent Simmons on 11/19/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
import Data
import Account
struct TodayFeedDelegate: SmartFeedDelegate {
let nameForDisplay = NSLocalizedString("Today", comment: "Today pseudo-feed title")
func fetchUnreadCount(for account: Account, callback: @escaping (Int) -> Void) {
account.fetchUnreadCountForToday(callback)
}
}

View File

@ -330,6 +330,11 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
database.fetchUnreadCount(for: flattenedFeeds(), since: startOfToday, callback: callback)
}
public func fetchUnreadCountForStarredArticles(_ callback: @escaping (Int) -> Void) {
database.fetchStarredAndUnreadCount(for: flattenedFeeds(), callback: callback)
}
// MARK: - Notifications
@objc func downloadProgressDidChange(_ note: Notification) {

View File

@ -164,6 +164,23 @@ final class ArticlesTable: DatabaseTable {
}
}
func fetchStarredAndUnreadCount(_ feeds: Set<Feed>, _ callback: @escaping (Int) -> Void) {
let feedIDs = feeds.feedIDs()
queue.fetch { (database) in
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(feedIDs.count))!
let sql = "select count(*) from articles natural join statuses where feedID in \(placeholders) and read=0 and starred=1 and userDeleted=0;"
let parameters = Array(feedIDs) as [Any]
let unreadCount = self.numberWithSQLAndParameters(sql, parameters, in: database)
DispatchQueue.main.async() {
callback(unreadCount)
}
}
}
// MARK: Status
func mark(_ articles: Set<Article>, _ statusKey: ArticleStatus.Key, _ flag: Bool) -> Set<ArticleStatus>? {

View File

@ -15,3 +15,5 @@ CREATE INDEX if not EXISTS articles_feedID_index on articles (feedID);
CREATE INDEX if not EXISTS tags_tagName_index on tags (tagName COLLATE NOCASE);
CREATE INDEX if not EXISTS statuses_read_index on statuses (read);
CREATE INDEX if not EXISTS statuses_starred_index on statuses (starred);

View File

@ -66,6 +66,11 @@ public final class Database {
articlesTable.fetchUnreadCount(feeds, since, callback)
}
public func fetchStarredAndUnreadCount(for feeds: Set<Feed>, callback: @escaping (Int) -> Void) {
articlesTable.fetchStarredAndUnreadCount(feeds, callback)
}
// MARK: - Saving and Updating Articles
public func update(feed: Feed, parsedFeed: ParsedFeed, completion: @escaping UpdateArticlesWithFeedCompletionBlock) {