diff --git a/Shared/Tree/WebFeedTreeControllerDelegate.swift b/Shared/Tree/WebFeedTreeControllerDelegate.swift index 6f572581d..34a0373e1 100644 --- a/Shared/Tree/WebFeedTreeControllerDelegate.swift +++ b/Shared/Tree/WebFeedTreeControllerDelegate.swift @@ -13,8 +13,17 @@ import Account final class WebFeedTreeControllerDelegate: TreeControllerDelegate { + private var filterExceptions = Set() var isReadFiltered = false + func addFilterException(_ feedID: FeedIdentifier) { + filterExceptions.insert(feedID) + } + + func resetFilterExceptions() { + filterExceptions = Set() + } + func treeController(treeController: TreeController, childNodesFor node: Node) -> [Node]? { if node.isRoot { return childNodesForRootNode(node) @@ -50,7 +59,7 @@ private extension WebFeedTreeControllerDelegate { func childNodesForSmartFeeds(_ parentNode: Node) -> [Node] { return SmartFeedsController.shared.smartFeeds.compactMap { (feed) -> Node? in - if isReadFiltered && feed.unreadCount == 0 { + if let feedID = feed.feedID, !filterExceptions.contains(feedID) && isReadFiltered && feed.unreadCount == 0 { return nil } return parentNode.existingOrNewChildNode(with: feed as AnyObject) @@ -63,14 +72,14 @@ private extension WebFeedTreeControllerDelegate { var children = [AnyObject]() for webFeed in container.topLevelWebFeeds { - if !(isReadFiltered && webFeed.unreadCount == 0) { + if let feedID = webFeed.feedID, !(!filterExceptions.contains(feedID) && isReadFiltered && webFeed.unreadCount == 0) { children.append(webFeed) } } if let folders = container.folders { for folder in folders { - if !(isReadFiltered && folder.unreadCount == 0) { + if let feedID = folder.feedID, !(!filterExceptions.contains(feedID) && isReadFiltered && folder.unreadCount == 0) { children.append(folder) } } @@ -129,10 +138,21 @@ private extension WebFeedTreeControllerDelegate { } func sortedAccountNodes(_ parent: Node) -> [Node] { + + var accountFilterException: String? = nil + switch filterExceptions.first { + case .folder(let accountID, _), .webFeed(let accountID, _): + accountFilterException = accountID + default: + break + } + let nodes = AccountManager.shared.sortedActiveAccounts.compactMap { (account) -> Node? in - if isReadFiltered && account.unreadCount == 0 { + + if isReadFiltered && account.accountID != accountFilterException && account.unreadCount == 0 { return nil } + let accountNode = parent.existingOrNewChildNode(with: account) accountNode.canHaveChildNodes = true accountNode.isGroupItem = true diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index 6cb867491..294b6584e 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -426,6 +426,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { if isReadFeedsFiltered { rebuildBackingStores() } + treeControllerDelegate.resetFilterExceptions() } @objc func statusesDidChange(_ note: Notification) { @@ -1819,6 +1820,8 @@ private extension SceneCoordinator { return } + treeControllerDelegate.addFilterException(feedIdentifier) + switch feedIdentifier { case .smartFeed: @@ -1842,6 +1845,9 @@ private extension SceneCoordinator { guard let accountNode = findAccountNode(accountID: accountID), let feedNode = findWebFeedNode(webFeedID: webFeedID, beginningAt: accountNode) else { return } + if let folder = feedNode.parent?.representedObject as? Folder, let folderFeedID = folder.feedID { + treeControllerDelegate.addFilterException(folderFeedID) + } if let feed = feedNode.representedObject as? WebFeed { discloseFeed(feed, animated: false) } @@ -1860,20 +1866,26 @@ private extension SceneCoordinator { return } - if restoreFeed(userInfo, accountID: accountID, webFeedID: webFeedID, articleID: articleID) { + if restoreFeedSelection(userInfo, accountID: accountID, webFeedID: webFeedID, articleID: articleID) { return } - guard let accountNode = findAccountNode(accountID: accountID, accountName: accountName), let feedNode = findWebFeedNode(webFeedID: webFeedID, beginningAt: accountNode) else { - return + guard let accountNode = findAccountNode(accountID: accountID, accountName: accountName), + let webFeedNode = findWebFeedNode(webFeedID: webFeedID, beginningAt: accountNode), + let webFeed = webFeedNode.representedObject as? WebFeed, + let webFeedFeedID = webFeed.feedID else { + return } - discloseFeed(feedNode.representedObject as! WebFeed, animated: false) { + treeControllerDelegate.addFilterException(webFeedFeedID) + addParentFolderToFilterExceptions(webFeedNode) + + discloseFeed(webFeed, animated: false) { self.selectArticleInCurrentFeed(articleID) } } - func restoreFeed(_ userInfo: [AnyHashable : Any], accountID: String, webFeedID: String, articleID: String) -> Bool { + func restoreFeedSelection(_ userInfo: [AnyHashable : Any], accountID: String, webFeedID: String, articleID: String) -> Bool { guard let feedIdentifierUserInfo = userInfo[UserInfoKey.feedIdentifier] as? [AnyHashable : AnyHashable], let feedIdentifier = FeedIdentifier(userInfo: feedIdentifierUserInfo) else { return false @@ -1883,13 +1895,12 @@ private extension SceneCoordinator { case .smartFeed: guard let smartFeed = SmartFeedsController.shared.find(by: feedIdentifier) else { return false } - if smartFeed.fetchArticles().contains(accountID: accountID, articleID: articleID) { - if let indexPath = indexPathFor(smartFeed) { - selectFeed(indexPath, animated: false) { - self.selectArticleInCurrentFeed(articleID) - } - return true + if let indexPath = indexPathFor(smartFeed) { + selectFeed(indexPath, animated: false) { + self.selectArticleInCurrentFeed(articleID) } + treeControllerDelegate.addFilterException(feedIdentifier) + return true } case .script: @@ -1897,20 +1908,26 @@ private extension SceneCoordinator { case .folder(let accountID, let folderName): guard let accountNode = findAccountNode(accountID: accountID), - let folderNode = findFolderNode(folderName: folderName, beginningAt: accountNode), - let folderFeed = folderNode.representedObject as? Feed else { + let folderNode = findFolderNode(folderName: folderName, beginningAt: accountNode) else { return false } - if folderFeed.fetchArticles().contains(accountID: accountID, articleID: articleID) { - return selectFeedAndArticle(feedNode: folderNode, articleID: articleID) + let found = selectFeedAndArticle(feedNode: folderNode, articleID: articleID) + if found { + treeControllerDelegate.addFilterException(feedIdentifier) } + return found case .webFeed: guard let accountNode = findAccountNode(accountID: accountID), let webFeedNode = findWebFeedNode(webFeedID: webFeedID, beginningAt: accountNode) else { return false } - return selectFeedAndArticle(feedNode: webFeedNode, articleID: articleID) - + let found = selectFeedAndArticle(feedNode: webFeedNode, articleID: articleID) + if found { + treeControllerDelegate.addFilterException(feedIdentifier) + addParentFolderToFilterExceptions(webFeedNode) + } + return found + } return false @@ -1958,4 +1975,10 @@ private extension SceneCoordinator { } } + func addParentFolderToFilterExceptions(_ feedNode: Node) { + if let folder = feedNode.parent?.representedObject as? Folder, let folderFeedID = folder.feedID { + treeControllerDelegate.addFilterException(folderFeedID) + } + } + }