diff --git a/iOS/AppCoordinator.swift b/iOS/AppCoordinator.swift index 514e6260e..72b5973d6 100644 --- a/iOS/AppCoordinator.swift +++ b/iOS/AppCoordinator.swift @@ -21,7 +21,7 @@ public extension Notification.Name { static let ArticleSelectionDidChange = Notification.Name(rawValue: "ArticleSelectionDidChange") } -class AppCoordinator: NSObject, UndoableCommandRunner { +class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { var undoableCommands = [UndoableCommand]() var undoManager: UndoManager? { @@ -107,6 +107,7 @@ class AppCoordinator: NSObject, UndoableCommandRunner { var timelineFetcher: ArticleFetcher? { didSet { + unreadCount = 0 currentArticleIndexPath = nil if timelineFetcher is Feed { showFeedNames = false @@ -181,11 +182,13 @@ class AppCoordinator: NSObject, UndoableCommandRunner { if articles.representSameArticlesInSameOrder(as: oldValue) { articleRowMap = [String: Int]() NotificationCenter.default.post(name: .ArticleDataDidChange, object: self, userInfo: nil) + updateUnreadCount() return } updateShowAvatars() articleRowMap = [String: Int]() NotificationCenter.default.post(name: .ArticlesDidChange, object: self, userInfo: nil) + updateUnreadCount() } } @@ -200,6 +203,14 @@ class AppCoordinator: NSObject, UndoableCommandRunner { return appDelegate.unreadCount > 0 } + var unreadCount: Int = 0 { + didSet { + if unreadCount != oldValue { + postUnreadCountDidChangeNotification() + } + } + } + override init() { super.init() @@ -210,6 +221,7 @@ class AppCoordinator: NSObject, UndoableCommandRunner { rebuildShadowTable() + NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(containerChildrenDidChange(_:)), name: .ChildrenDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(batchUpdateDidPerform(_:)), name: .BatchUpdateDidPerform, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(displayNameDidChange(_:)), name: .DisplayNameDidChange, object: nil) @@ -240,6 +252,10 @@ class AppCoordinator: NSObject, UndoableCommandRunner { // MARK: Notifications + @objc func statusesDidChange(_ note: Notification) { + updateUnreadCount() + } + @objc func containerChildrenDidChange(_ note: Notification) { rebuildBackingStores() } @@ -335,7 +351,18 @@ class AppCoordinator: NSObject, UndoableCommandRunner { } return nil } - + + func unreadCountFor(_ node: Node) -> Int { + // The coordinator supplies the unread count for the currently selected feed node + if let indexPath = currentMasterIndexPath, let selectedNode = nodeFor(indexPath), selectedNode == node { + return unreadCount + } + if let unreadCountProvider = node.representedObject as? UnreadCountProvider { + return unreadCountProvider.unreadCount + } + return 0 + } + func expand(section: Int, completion: ([IndexPath]) -> ()) { guard let expandNode = treeController.rootNode.childAtIndex(section) else { @@ -713,6 +740,16 @@ extension AppCoordinator: UISplitViewControllerDelegate { private extension AppCoordinator { + func updateUnreadCount() { + var count = 0 + for article in articles { + if !article.status.read { + count += 1 + } + } + unreadCount = count + } + func rebuildBackingStores() { if !animatingChanges && !BatchUpdate.shared.isPerforming { treeController.rebuild() diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index bb4c91b14..75c587f08 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -93,9 +93,15 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { return } - guard let node = coordinator.rootNode.descendantNodeRepresentingObject(representedObject as AnyObject), - let indexPath = coordinator.indexPathFor(node) else { - return + var node: Node? = nil + if let coordinator = representedObject as? AppCoordinator, let fetcher = coordinator.timelineFetcher { + node = coordinator.rootNode.descendantNodeRepresentingObject(fetcher as AnyObject) + } else { + node = coordinator.rootNode.descendantNodeRepresentingObject(representedObject as AnyObject) + } + + guard let unwrappedNode = node, let indexPath = coordinator.indexPathFor(unwrappedNode) else { + return } performBlockAndRestoreSelection { @@ -559,7 +565,7 @@ private extension MasterFeedViewController { cell.allowDisclosureSelection = node.canHaveChildNodes cell.name = nameFor(node) - cell.unreadCount = unreadCountFor(node) + cell.unreadCount = coordinator.unreadCountFor(node) configureFavicon(cell, node) cell.shouldShowImage = node.representedObject is SmallIconProvider @@ -582,14 +588,7 @@ private extension MasterFeedViewController { } return "" } - - func unreadCountFor(_ node: Node) -> Int { - if let unreadCountProvider = node.representedObject as? UnreadCountProvider { - return unreadCountProvider.unreadCount - } - return 0 - } - + func configureCellsForRepresentedObject(_ representedObject: AnyObject) { applyToCellsForRepresentedObject(representedObject, configure) }