Add UnreadFeed pseudo-feed.

This commit is contained in:
Brent Simmons 2017-11-19 13:57:42 -08:00
parent 81ff62ad0f
commit 1ed48090dd
6 changed files with 169 additions and 30 deletions

View File

@ -13,7 +13,6 @@
842E45E51ED8C6B7000A8B52 /* MainWindowSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45E41ED8C6B7000A8B52 /* MainWindowSplitView.swift */; }; 842E45E51ED8C6B7000A8B52 /* MainWindowSplitView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45E41ED8C6B7000A8B52 /* MainWindowSplitView.swift */; };
842E45E71ED8C747000A8B52 /* DB5.plist in Resources */ = {isa = PBXBuildFile; fileRef = 842E45E61ED8C747000A8B52 /* DB5.plist */; }; 842E45E71ED8C747000A8B52 /* DB5.plist in Resources */ = {isa = PBXBuildFile; fileRef = 842E45E61ED8C747000A8B52 /* DB5.plist */; };
84513F901FAA63950023A1A9 /* FeedListControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */; }; 84513F901FAA63950023A1A9 /* FeedListControlsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */; };
845B14921FC2028A0013CF92 /* PseudoFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845B14911FC2028A0013CF92 /* PseudoFeed.swift */; };
845F52ED1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */; }; 845F52ED1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */; };
846E773D1F6EF67A00A165E2 /* Account.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846E773A1F6EF5D700A165E2 /* Account.framework */; }; 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, ); }; }; 846E773E1F6EF67A00A165E2 /* Account.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 846E773A1F6EF5D700A165E2 /* Account.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -99,7 +98,9 @@
84F204CE1FAACB660076E152 /* FeedListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204CD1FAACB660076E152 /* FeedListViewController.swift */; }; 84F204CE1FAACB660076E152 /* FeedListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204CD1FAACB660076E152 /* FeedListViewController.swift */; };
84F204DE1FAACB8B0076E152 /* FeedListTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DD1FAACB8B0076E152 /* FeedListTimelineViewController.swift */; }; 84F204DE1FAACB8B0076E152 /* FeedListTimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DD1FAACB8B0076E152 /* FeedListTimelineViewController.swift */; };
84F204E01FAACBB30076E152 /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; }; 84F204E01FAACBB30076E152 /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; };
84F2D5251FC22D8100998D64 /* TodayFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5241FC22D8100998D64 /* TodayFeed.swift */; }; 84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */; };
84F2D5381FC22FCC00998D64 /* TodayFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5361FC22FCB00998D64 /* TodayFeed.swift */; };
84F2D53A1FC2308B00998D64 /* UnreadFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5391FC2308B00998D64 /* UnreadFeed.swift */; };
84FB9A2F1EDCD6C4003D53B9 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; }; 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, ); }; }; 84FB9A301EDCD6C4003D53B9 /* Sparkle.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@ -398,7 +399,6 @@
842E45E41ED8C6B7000A8B52 /* MainWindowSplitView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowSplitView.swift; sourceTree = "<group>"; }; 842E45E41ED8C6B7000A8B52 /* MainWindowSplitView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowSplitView.swift; sourceTree = "<group>"; };
842E45E61ED8C747000A8B52 /* DB5.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = DB5.plist; path = Evergreen/Resources/DB5.plist; sourceTree = "<group>"; }; 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>"; }; 84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListControlsView.swift; sourceTree = "<group>"; };
845B14911FC2028A0013CF92 /* PseudoFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PseudoFeed.swift; sourceTree = "<group>"; };
845B14A51FC2299E0013CF92 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; }; 845B14A51FC2299E0013CF92 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedPasteboardWriter.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>"; }; 846E77161F6EF5D000A165E2 /* Database.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Database.xcodeproj; path = Frameworks/Database/Database.xcodeproj; sourceTree = "<group>"; };
@ -478,7 +478,9 @@
84F204CD1FAACB660076E152 /* FeedListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListViewController.swift; sourceTree = "<group>"; }; 84F204CD1FAACB660076E152 /* FeedListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListViewController.swift; sourceTree = "<group>"; };
84F204DD1FAACB8B0076E152 /* FeedListTimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListTimelineViewController.swift; sourceTree = "<group>"; }; 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>"; }; 84F204DF1FAACBB30076E152 /* ArticleArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleArray.swift; sourceTree = "<group>"; };
84F2D5241FC22D8100998D64 /* TodayFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayFeed.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>"; };
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; }; 84FB9A2D1EDCD6B8003D53B9 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Frameworks/Vendor/Sparkle.framework; sourceTree = SOURCE_ROOT; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -592,8 +594,6 @@
849A97561ED9EB0D007D329B /* Data */ = { 849A97561ED9EB0D007D329B /* Data */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
845B14911FC2028A0013CF92 /* PseudoFeed.swift */,
84F2D5241FC22D8100998D64 /* TodayFeed.swift */,
849A97581ED9EB0D007D329B /* ArticleUtilities.swift */, 849A97581ED9EB0D007D329B /* ArticleUtilities.swift */,
); );
name = Data; name = Data;
@ -744,6 +744,7 @@
849A978B1ED9EE4D007D329B /* Feed List */, 849A978B1ED9EE4D007D329B /* Feed List */,
849A97901ED9EF65007D329B /* Progress Window */, 849A97901ED9EF65007D329B /* Progress Window */,
84DAEE201F86CAE00058304B /* Importers */, 84DAEE201F86CAE00058304B /* Importers */,
84F2D5341FC22FCB00998D64 /* PseudoFeeds */,
849A97561ED9EB0D007D329B /* Data */, 849A97561ED9EB0D007D329B /* Data */,
849A97961ED9EFAA007D329B /* Extensions */, 849A97961ED9EFAA007D329B /* Extensions */,
849A97991ED9EFB6007D329B /* Resources */, 849A97991ED9EFB6007D329B /* Resources */,
@ -895,6 +896,17 @@
path = Evergreen/Resources/KeyboardShortcuts; path = Evergreen/Resources/KeyboardShortcuts;
sourceTree = SOURCE_ROOT; sourceTree = SOURCE_ROOT;
}; };
84F2D5341FC22FCB00998D64 /* PseudoFeeds */ = {
isa = PBXGroup;
children = (
84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */,
84F2D5361FC22FCB00998D64 /* TodayFeed.swift */,
84F2D5391FC2308B00998D64 /* UnreadFeed.swift */,
);
name = PseudoFeeds;
path = Evergreen/PseudoFeeds;
sourceTree = "<group>";
};
84FB9A2C1EDCD6A4003D53B9 /* Frameworks */ = { 84FB9A2C1EDCD6A4003D53B9 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -1269,8 +1281,10 @@
845F52ED1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift in Sources */, 845F52ED1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift in Sources */,
849A975E1ED9EB72007D329B /* MainWindowController.swift in Sources */, 849A975E1ED9EB72007D329B /* MainWindowController.swift in Sources */,
842E45E51ED8C6B7000A8B52 /* MainWindowSplitView.swift in Sources */, 842E45E51ED8C6B7000A8B52 /* MainWindowSplitView.swift in Sources */,
84F2D53A1FC2308B00998D64 /* UnreadFeed.swift in Sources */,
849A97661ED9EB96007D329B /* SidebarViewController.swift in Sources */, 849A97661ED9EB96007D329B /* SidebarViewController.swift in Sources */,
849A97641ED9EB96007D329B /* SidebarOutlineView.swift in Sources */, 849A97641ED9EB96007D329B /* SidebarOutlineView.swift in Sources */,
84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */,
84702AA41FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift in Sources */, 84702AA41FA27AC0006B8943 /* MarkReadOrUnreadCommand.swift in Sources */,
849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */, 849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */,
849A97651ED9EB96007D329B /* SidebarTreeControllerDelegate.swift in Sources */, 849A97651ED9EB96007D329B /* SidebarTreeControllerDelegate.swift in Sources */,
@ -1288,7 +1302,7 @@
849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */, 849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */,
849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */, 849A97531ED9EAC0007D329B /* AddFeedController.swift in Sources */,
849A97831ED9EC63007D329B /* StatusBarView.swift in Sources */, 849A97831ED9EC63007D329B /* StatusBarView.swift in Sources */,
84F2D5251FC22D8100998D64 /* TodayFeed.swift in Sources */, 84F2D5381FC22FCC00998D64 /* TodayFeed.swift in Sources */,
849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */, 849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */,
849A97921ED9EF65007D329B /* IndeterminateProgressWindowController.swift in Sources */, 849A97921ED9EF65007D329B /* IndeterminateProgressWindowController.swift in Sources */,
849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */, 849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */,
@ -1306,7 +1320,6 @@
84B99C691FAE36B800ECDEDB /* FeedListFolder.swift in Sources */, 84B99C691FAE36B800ECDEDB /* FeedListFolder.swift in Sources */,
84F204DE1FAACB8B0076E152 /* FeedListTimelineViewController.swift in Sources */, 84F204DE1FAACB8B0076E152 /* FeedListTimelineViewController.swift in Sources */,
849A97A31ED9F180007D329B /* FolderTreeControllerDelegate.swift in Sources */, 849A97A31ED9F180007D329B /* FolderTreeControllerDelegate.swift in Sources */,
845B14921FC2028A0013CF92 /* PseudoFeed.swift in Sources */,
849A97851ED9ECCD007D329B /* PreferencesWindowController.swift in Sources */, 849A97851ED9ECCD007D329B /* PreferencesWindowController.swift in Sources */,
849A977A1ED9EC04007D329B /* TimelineTableCellView.swift in Sources */, 849A977A1ED9EC04007D329B /* TimelineTableCellView.swift in Sources */,
849A97761ED9EC04007D329B /* TimelineCellAppearance.swift in Sources */, 849A97761ED9EC04007D329B /* TimelineCellAppearance.swift in Sources */,

View File

@ -20,7 +20,7 @@ var currentTheme: VSTheme!
var appDelegate: AppDelegate! var appDelegate: AppDelegate!
@NSApplicationMain @NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations { class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, UnreadCountProvider {
let windowControllers = NSMutableArray() let windowControllers = NSMutableArray()
var preferencesWindowController: NSWindowController? var preferencesWindowController: NSWindowController?
@ -46,6 +46,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations {
didSet { didSet {
if unreadCount != oldValue { if unreadCount != oldValue {
dockBadge.update() dockBadge.update()
postUnreadCountDidChangeNotification()
} }
} }
} }
@ -96,7 +97,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations {
currentTheme = themeLoader.defaultTheme currentTheme = themeLoader.defaultTheme
pseudoFeeds += [TodayFeed()] pseudoFeeds = [TodayFeed(), UnreadFeed()]
createAndShowMainWindow() createAndShowMainWindow()

View File

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

View File

@ -0,0 +1,18 @@
//
// PseudoFeed.swift
// Evergreen
//
// Created by Brent Simmons on 11/19/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
import Data
import RSCore
protocol PseudoFeed: class, DisplayNameProvider, UnreadCountProvider {
}

View File

@ -0,0 +1,91 @@
//
// TodayFeed.swift
// Evergreen
//
// Created by Brent Simmons on 11/19/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
import Data
import Account
final class TodayFeed: PseudoFeed {
let nameForDisplay = NSLocalizedString("Today", comment: "Today pseudo-feed title")
var unreadCount = 0 {
didSet {
if unreadCount != oldValue {
postUnreadCountDidChangeNotification()
}
}
}
private var timer: Timer?
private var unreadCounts = [Account: Int]()
init() {
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
startTimer() // Fetch unread count at startup
}
@objc func unreadCountDidChange(_ note: Notification) {
if note.object is Account {
startTimer()
}
}
}
private extension TodayFeed {
// MARK: - Unread Counts
private func fetchUnreadCount(for account: Account) {
account.fetchUnreadCountForToday { (accountUnreadCount) in
self.unreadCounts[account] = accountUnreadCount
self.updateUnreadCount()
}
}
private func fetchUnreadCounts() {
AccountManager.shared.accounts.forEach { self.fetchUnreadCount(for: $0) }
}
private func updateUnreadCount() {
unreadCount = AccountManager.shared.accounts.reduce(0) { (result, account) -> Int in
if let oneUnreadCount = unreadCounts[account] {
return result + oneUnreadCount
}
return result
}
}
// MARK: - Timer
func stopTimer() {
if let timer = timer {
timer.rs_invalidateIfValid()
}
timer = nil
}
private static let fetchCoalescingDelay: TimeInterval = 0.2
func startTimer() {
stopTimer()
timer = Timer.scheduledTimer(withTimeInterval: TodayFeed.fetchCoalescingDelay, repeats: false, block: { (timer) in
self.fetchUnreadCounts()
self.stopTimer()
})
}
}

View File

@ -0,0 +1,36 @@
//
// UnreadFeed.swift
// Evergreen
//
// Created by Brent Simmons on 11/19/17.
// Copyright © 2017 Ranchero Software. All rights reserved.
//
import Foundation
// This just shows the global unread count, which appDelegate already has. Easy.
final class UnreadFeed: PseudoFeed {
let nameForDisplay = NSLocalizedString("All Unread", comment: "All Unread pseudo-feed title")
var unreadCount = 0 {
didSet {
if unreadCount != oldValue {
postUnreadCountDidChangeNotification()
}
}
}
init() {
self.unreadCount = appDelegate.unreadCount
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: appDelegate)
}
@objc func unreadCountDidChange(_ note: Notification) {
assert(note.object is AppDelegate)
unreadCount = appDelegate.unreadCount
}
}