diff --git a/Evergreen.xcodeproj/project.pbxproj b/Evergreen.xcodeproj/project.pbxproj index 01bf30dfb..2144407c6 100644 --- a/Evergreen.xcodeproj/project.pbxproj +++ b/Evergreen.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 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 */; }; + 845B14921FC2028A0013CF92 /* PseudoFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 845B14911FC2028A0013CF92 /* PseudoFeed.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, ); }; }; @@ -396,6 +397,7 @@ 842E45E41ED8C6B7000A8B52 /* MainWindowSplitView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainWindowSplitView.swift; sourceTree = ""; }; 842E45E61ED8C747000A8B52 /* DB5.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = DB5.plist; path = Evergreen/Resources/DB5.plist; sourceTree = ""; }; 84513F8F1FAA63950023A1A9 /* FeedListControlsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedListControlsView.swift; sourceTree = ""; }; + 845B14911FC2028A0013CF92 /* PseudoFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PseudoFeed.swift; sourceTree = ""; }; 845F52EC1FB2B9FC00C10BF0 /* FeedPasteboardWriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedPasteboardWriter.swift; sourceTree = ""; }; 846E77161F6EF5D000A165E2 /* Database.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Database.xcodeproj; path = Frameworks/Database/Database.xcodeproj; sourceTree = ""; }; 846E77301F6EF5D600A165E2 /* Account.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Account.xcodeproj; path = Frameworks/Account/Account.xcodeproj; sourceTree = ""; }; @@ -587,6 +589,7 @@ 849A97561ED9EB0D007D329B /* Data */ = { isa = PBXGroup; children = ( + 845B14911FC2028A0013CF92 /* PseudoFeed.swift */, 849A97581ED9EB0D007D329B /* ArticleUtilities.swift */, ); name = Data; @@ -1297,6 +1300,7 @@ 84B99C691FAE36B800ECDEDB /* FeedListFolder.swift in Sources */, 84F204DE1FAACB8B0076E152 /* FeedListTimelineViewController.swift in Sources */, 849A97A31ED9F180007D329B /* FolderTreeControllerDelegate.swift in Sources */, + 845B14921FC2028A0013CF92 /* PseudoFeed.swift in Sources */, 849A97851ED9ECCD007D329B /* PreferencesWindowController.swift in Sources */, 849A977A1ED9EC04007D329B /* TimelineTableCellView.swift in Sources */, 849A97761ED9EC04007D329B /* TimelineCellAppearance.swift in Sources */, diff --git a/Evergreen/Data/PseudoFeed.swift b/Evergreen/Data/PseudoFeed.swift new file mode 100644 index 000000000..c4fe286bc --- /dev/null +++ b/Evergreen/Data/PseudoFeed.swift @@ -0,0 +1,92 @@ +// +// PseudoFeed.swift +// Evergreen +// +// Created by Brent Simmons on 11/19/17. +// Copyright © 2017 Ranchero Software. All rights reserved. +// + +import Foundation +import Data +import RSCore +import Account + +protocol PseudoFeedDelegate: DisplayNameProvider { + + func fetchUnreadCount(for: Account, callback: (Int) -> Void) +} + +final class PseudoFeed: UnreadCountProvider, DisplayNameProvider { + + private static let fetchCoalescingDelay: TimeInterval = 0.1 + private var timer: Timer? + private var unreadCounts = [Account: Int]() + private let delegate: PseudoFeedDelegate + + var nameForDisplay: String { + get { + return delegate.nameForDisplay + } + } + + var unreadCount = 0 { + didSet { + if unreadCount != oldValue { + postUnreadCountDidChangeNotification() + } + } + } + + init(delegate: PseudoFeedDelegate) { + + self.delegate = delegate + } + + @objc func unreadCountDidChange(_ note: Notification) { + + if let object = note.object, object is Account { + startCoalescingTimer() + } + } + + private func fetchUnreadCount(for account: Account) { + + delegate.fetchUnreadCount(for: account) { (accountUnreadCount) in + unreadCounts[account] = accountUnreadCount + } + } + + 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 + + private func stopTimer() { + + if let timer = timer { + timer.rs_invalidateIfValid() + } + timer = nil + } + + private func startCoalescingTimer() { + + stopTimer() + timer = Timer(timeInterval: PseudoFeed.fetchCoalescingDelay, repeats: false, block: { (_) in + self.fetchUnreadCounts() + self.stopTimer() + }) + } +}