diff --git a/Shared/Timeline/FetchRequestQueue.swift b/Shared/Timeline/FetchRequestQueue.swift index 729813f35..4fd9ff093 100644 --- a/Shared/Timeline/FetchRequestQueue.swift +++ b/Shared/Timeline/FetchRequestQueue.swift @@ -14,6 +14,13 @@ final class FetchRequestQueue { private var pendingRequests = [FetchRequestOperation]() private var currentRequest: FetchRequestOperation? = nil + + var isAnyCurrentRequest: Bool { + if let currentRequest = currentRequest { + return !currentRequest.isCanceled + } + return false + } func cancelAllRequests() { precondition(Thread.isMainThread) diff --git a/iOS/MasterFeed/MasterFeedViewController.swift b/iOS/MasterFeed/MasterFeedViewController.swift index e7273a6fe..078d9a617 100644 --- a/iOS/MasterFeed/MasterFeedViewController.swift +++ b/iOS/MasterFeed/MasterFeedViewController.swift @@ -58,7 +58,6 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { NotificationCenter.default.addObserver(self, selector: #selector(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(webFeedIconDidBecomeAvailable(_:)), name: .WebFeedIconDidBecomeAvailable, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(webFeedSettingDidChange(_:)), name: .WebFeedSettingDidChange, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(userDidAddFeed(_:)), name: .UserDidAddFeed, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil) @@ -149,13 +148,6 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { reloadAllVisibleCells() } - @objc func userDidAddFeed(_ notification: Notification) { - guard let webFeed = notification.userInfo?[UserInfoKey.webFeed] as? WebFeed else { - return - } - discloseFeed(webFeed, animations: [.scroll, .navigation]) - } - @objc func contentSizeCategoryDidChange(_ note: Notification) { resetEstimatedRowHeight() applyChanges(animated: false) @@ -335,7 +327,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { becomeFirstResponder() - coordinator.selectFeed(indexPath, animations: [.navigation, .select, .scroll]) + coordinator.selectFeed(indexPath: indexPath, animations: [.navigation, .select, .scroll]) } override func tableView(_ tableView: UITableView, targetIndexPathForMoveFromRowAt sourceIndexPath: IndexPath, toProposedIndexPath proposedDestinationIndexPath: IndexPath) -> IndexPath { @@ -545,7 +537,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { } } - func reloadFeeds(initialLoad: Bool) { + func reloadFeeds(initialLoad: Bool, completion: (() -> Void)? = nil) { updateUI() // We have to reload all the visible cells because if we got here by doing a table cell move, @@ -553,75 +545,13 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner { // drops on a "folder" that should cause the dropped cell to disappear. applyChanges(animated: !initialLoad) { [weak self] in if !initialLoad { - self?.reloadAllVisibleCells() + self?.reloadAllVisibleCells(completion: completion) + } else { + completion?() } } } - func ensureSectionIsExpanded(_ sectionIndex: Int, completion: (() -> Void)? = nil) { - guard let sectionNode = coordinator.rootNode.childAtIndex(sectionIndex) else { - return - } - - if !coordinator.isExpanded(sectionNode) { - coordinator.expand(sectionNode) - self.applyChanges(animated: true) { - completion?() - } - } else { - completion?() - } - } - - func discloseFeed(_ webFeed: WebFeed, animations: Animations, completion: (() -> Void)? = nil) { - - func discloseFeedInAccount() { - guard let node = coordinator.rootNode.descendantNodeRepresentingObject(webFeed as AnyObject) else { - completion?() - return - } - - if let indexPath = dataSource.indexPath(for: node) { - coordinator.selectFeed(indexPath, animations: animations) { - completion?() - } - return - } - - // It wasn't already visable, so expand its folder and try again - guard let parent = node.parent, parent.representedObject is Folder else { - completion?() - return - } - - coordinator.expand(parent) - reloadNode(parent) - - applyChanges(animated: true, adjustScroll: true) { [weak self] in - if let indexPath = self?.dataSource.indexPath(for: node) { - self?.coordinator.selectFeed(indexPath, animations: animations) { - completion?() - } - } - } - } - - // If the account for the feed is collapsed, expand it - if let account = webFeed.account, - let accountNode = coordinator.rootNode.childNodeRepresentingObject(account as AnyObject), - !coordinator.isExpanded(accountNode) { - - coordinator.expand(accountNode) - applyChanges(animated: true) { - discloseFeedInAccount() - } - - } else { - discloseFeedInAccount() - } - - } - func focus() { becomeFirstResponder() } @@ -836,16 +766,17 @@ private extension MasterFeedViewController { } } - private func reloadAllVisibleCells() { + private func reloadAllVisibleCells(completion: (() -> Void)? = nil) { let visibleNodes = tableView.indexPathsForVisibleRows!.compactMap { return dataSource.itemIdentifier(for: $0) } - reloadCells(visibleNodes) + reloadCells(visibleNodes, completion: completion) } - private func reloadCells(_ nodes: [Node]) { + private func reloadCells(_ nodes: [Node], completion: (() -> Void)? = nil) { var snapshot = dataSource.snapshot() snapshot.reloadItems(nodes) dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in self?.restoreSelectionIfNecessary(adjustScroll: false) + completion?() } } @@ -1224,7 +1155,7 @@ private extension MasterFeedViewController { deleteCommand.perform() if indexPath == coordinator.currentFeedIndexPath { - coordinator.selectFeed(nil) + coordinator.selectFeed(indexPath: nil) } } diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index 9e7f83da0..4caa62a57 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -774,7 +774,7 @@ private extension MasterTimelineViewController { let title = NSLocalizedString("Go to Feed", comment: "Go to Feed") let action = UIAction(title: title, image: AppAssets.openInSidebarImage) { [weak self] action in - self?.coordinator.discloseFeed(webFeed, animations: [.scroll, .navigation]) + self?.coordinator.discloseWebFeed(webFeed, animations: [.scroll, .navigation]) } return action } @@ -785,7 +785,7 @@ private extension MasterTimelineViewController { let title = NSLocalizedString("Go to Feed", comment: "Go to Feed") let action = UIAlertAction(title: title, style: .default) { [weak self] action in - self?.coordinator.discloseFeed(webFeed, animations: [.scroll, .navigation]) + self?.coordinator.discloseWebFeed(webFeed, animations: [.scroll, .navigation]) completion(true) } return action diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift index 5636a2c37..d09446d4f 100644 --- a/iOS/SceneCoordinator.swift +++ b/iOS/SceneCoordinator.swift @@ -298,6 +298,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { NotificationCenter.default.addObserver(self, selector: #selector(accountStateDidChange(_:)), name: .AccountStateDidChange, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(userDidAddAccount(_:)), name: .UserDidAddAccount, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(userDidDeleteAccount(_:)), name: .UserDidDeleteAccount, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(userDidAddFeed(_:)), name: .UserDidAddFeed, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(downloadArticlesDidUpdateUnreadCounts(_:)), name: .DownloadArticlesDidUpdateUnreadCounts, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(accountDidDownloadArticles(_:)), name: .AccountDidDownloadArticles, object: nil) @@ -360,27 +361,27 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { } } - func handle(_ activity: NSUserActivity, initialLoad: Bool) { - selectFeed(nil) { + func handle(_ activity: NSUserActivity) { + selectFeed(indexPath: nil) { guard let activityType = ActivityType(rawValue: activity.activityType) else { return } switch activityType { case .restoration: break case .selectFeed: - self.handleSelectFeed(activity.userInfo, initialLoad: initialLoad) + self.handleSelectFeed(activity.userInfo) case .nextUnread: self.selectFirstUnreadInAllUnread() case .readArticle: - self.handleReadArticle(activity.userInfo, initialLoad: initialLoad) + self.handleReadArticle(activity.userInfo) case .addFeedIntent: self.showAdd(.feed) } } } - func handle(_ response: UNNotificationResponse, initialLoad: Bool) { + func handle(_ response: UNNotificationResponse) { let userInfo = response.notification.request.content.userInfo - handleReadArticle(userInfo, initialLoad: initialLoad) + handleReadArticle(userInfo) } func configurePanelMode(for size: CGSize) { @@ -404,15 +405,16 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { } func selectFirstUnreadInAllUnread() { - masterFeedViewController.ensureSectionIsExpanded(0) { - self.selectFeed(IndexPath(row: 1, section: 0)) { + expand(SmartFeedsController.shared) + self.ensureFeedIsAvailableToSelect(SmartFeedsController.shared.unreadFeed) { + self.selectFeed(SmartFeedsController.shared.unreadFeed) { self.selectFirstUnreadArticleInTimeline() } } } func showSearch() { - selectFeed(nil) { + selectFeed(indexPath: nil) { self.installTimelineControllerIfNecessary(animated: false) DispatchQueue.main.asyncAfter(deadline: .now()) { self.masterTimelineViewController!.showSearchAll() @@ -443,14 +445,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { return } - for section in shadowTable { - for node in section { - if let feed = node.representedObject as? Feed, let feedID = feed.feedID { - treeControllerDelegate.addFilterException(feedID) - } - } - } - + addShadowTableToFilterExceptions() rebuildBackingStores() treeControllerDelegate.resetFilterExceptions() } @@ -490,14 +485,10 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { if timelineFetcherContainsAnyPseudoFeed() { fetchAndMergeArticlesAsync(animated: true) { self.masterTimelineViewController?.reinitializeArticles(resetScroll: false) - self.rebuildBackingStores() { - expandNewlyActivatedAccount() - } + self.rebuildBackingStores(updateExpandedNodes: expandNewlyActivatedAccount) } } else { - rebuildBackingStores() { - expandNewlyActivatedAccount() - } + self.rebuildBackingStores(updateExpandedNodes: expandNewlyActivatedAccount) } } @@ -513,14 +504,10 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { if timelineFetcherContainsAnyPseudoFeed() { fetchAndMergeArticlesAsync(animated: true) { self.masterTimelineViewController?.reinitializeArticles(resetScroll: false) - self.rebuildBackingStores() { - expandNewAccount() - } + self.rebuildBackingStores(updateExpandedNodes: expandNewAccount) } } else { - rebuildBackingStores() { - expandNewAccount() - } + self.rebuildBackingStores(updateExpandedNodes: expandNewAccount) } } @@ -535,17 +522,20 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { if timelineFetcherContainsAnyPseudoFeed() { fetchAndMergeArticlesAsync(animated: true) { self.masterTimelineViewController?.reinitializeArticles(resetScroll: false) - self.rebuildBackingStores() { - cleanupAccount() - } + self.rebuildBackingStores(updateExpandedNodes: cleanupAccount) } } else { - rebuildBackingStores() { - cleanupAccount() - } + self.rebuildBackingStores(updateExpandedNodes: cleanupAccount) } } + @objc func userDidAddFeed(_ notification: Notification) { + guard let webFeed = notification.userInfo?[UserInfoKey.webFeed] as? WebFeed else { + return + } + discloseWebFeed(webFeed, animations: [.scroll, .navigation]) + } + @objc func userDefaultsDidChange(_ note: Notification) { self.sortDirection = AppDefaults.timelineSortDirection self.groupByFeed = AppDefaults.timelineGroupByFeed @@ -567,7 +557,12 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { } @objc func willEnterForeground(_ note: Notification) { - queueFetchAndMergeArticles() + // Don't interfere with any fetch requests that we may have initiated before the app was returned to the foreground. + // For example if you select Next Unread from the Home Screen Quick actions, you can start a request before we are + // in the foreground. + if !fetchRequestQueue.isAnyCurrentRequest { + queueFetchAndMergeArticles() + } } // MARK: API @@ -637,20 +632,35 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { refreshTimeline(resetScroll: false) } - func isExpanded(_ node: Node) -> Bool { - if let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID { + func isExpanded(_ containerIdentifiable: ContainerIdentifiable) -> Bool { + if let containerID = containerIdentifiable.containerID { return expandedTable.contains(containerID) } return false } - func expand(_ node: Node) { - markExpanded(node) + func isExpanded(_ node: Node) -> Bool { + if let containerIdentifiable = node.representedObject as? ContainerIdentifiable { + return isExpanded(containerIdentifiable) + } + return false + } + + func expand(_ containerIdentifiable: ContainerIdentifiable) { + guard !isExpanded(containerIdentifiable) else { return } + + markExpanded(containerIdentifiable) animatingChanges = true rebuildShadowTable() animatingChanges = false } + func expand(_ node: Node) { + if let containerIdentifiable = node.representedObject as? ContainerIdentifiable { + expand(containerIdentifiable) + } + } + func expandAllSectionsAndFolders() { for sectionNode in treeController.rootNode.childNodes { markExpanded(sectionNode) @@ -695,7 +705,18 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { return indexPathFor(node) } - func selectFeed(_ indexPath: IndexPath?, animations: Animations = [], deselectArticle: Bool = true, completion: (() -> Void)? = nil) { + func selectFeed(_ feed: Feed?, animations: Animations = [], deselectArticle: Bool = true, completion: (() -> Void)? = nil) { + let indexPath: IndexPath? = { + if let feed = feed, let indexPath = indexPathFor(feed as AnyObject) { + return indexPath + } else { + return nil + } + }() + selectFeed(indexPath: indexPath, animations: animations, deselectArticle: deselectArticle, completion: completion) + } + + func selectFeed(indexPath: IndexPath?, animations: Animations = [], deselectArticle: Bool = true, completion: (() -> Void)? = nil) { guard indexPath != currentFeedIndexPath else { completion?() return @@ -732,31 +753,34 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { func selectPrevFeed() { if let indexPath = prevFeedIndexPath { - selectFeed(indexPath, animations: [.navigation, .scroll]) + selectFeed(indexPath: indexPath, animations: [.navigation, .scroll]) } } func selectNextFeed() { if let indexPath = nextFeedIndexPath { - selectFeed(indexPath, animations: [.navigation, .scroll]) + selectFeed(indexPath: indexPath, animations: [.navigation, .scroll]) } } func selectTodayFeed() { - masterFeedViewController?.ensureSectionIsExpanded(0) { - self.selectFeed(IndexPath(row: 0, section: 0), animations: [.navigation, .scroll]) + expand(SmartFeedsController.shared) + self.ensureFeedIsAvailableToSelect(SmartFeedsController.shared.todayFeed) { + self.selectFeed(SmartFeedsController.shared.todayFeed, animations: [.navigation, .scroll]) } } func selectAllUnreadFeed() { - masterFeedViewController?.ensureSectionIsExpanded(0) { - self.selectFeed(IndexPath(row: 1, section: 0), animations: [.navigation, .scroll]) + expand(SmartFeedsController.shared) + self.ensureFeedIsAvailableToSelect(SmartFeedsController.shared.unreadFeed) { + self.selectFeed(SmartFeedsController.shared.unreadFeed, animations: [.navigation, .scroll]) } } func selectStarredFeed() { - masterFeedViewController?.ensureSectionIsExpanded(0) { - self.selectFeed(IndexPath(row: 2, section: 0), animations: [.navigation, .scroll]) + expand(SmartFeedsController.shared) + self.ensureFeedIsAvailableToSelect(SmartFeedsController.shared.starredFeed) { + self.selectFeed(SmartFeedsController.shared.starredFeed, animations: [.navigation, .scroll]) } } @@ -1009,14 +1033,35 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { return timelineFeed == feed } - func discloseFeed(_ feed: WebFeed, animations: Animations = [], completion: (() -> Void)? = nil) { + func discloseWebFeed(_ webFeed: WebFeed, animations: Animations = [], completion: (() -> Void)? = nil) { if isSearching { masterTimelineViewController?.hideSearch() } - masterFeedViewController.discloseFeed(feed, animations: animations) { + guard let account = webFeed.account else { completion?() + return } + + let parentFolder = account.sortedFolders?.first(where: { $0.objectIsChild(webFeed) }) + + expand(account) + if let parentFolder = parentFolder { + expand(parentFolder) + } + + if let webFeedFeedID = webFeed.feedID { + self.treeControllerDelegate.addFilterException(webFeedFeedID) + } + if let parentFolderFeedID = parentFolder?.feedID { + self.treeControllerDelegate.addFilterException(parentFolderFeedID) + } + + rebuildBackingStores() { + self.treeControllerDelegate.resetFilterExceptions() + self.selectFeed(webFeed, animations: animations, completion: completion) + } + } func showStatusBar() { @@ -1258,30 +1303,26 @@ private extension SceneCoordinator { _idToArticleDictionary = idDictionary articleDictionaryNeedsUpdate = false } - - func rebuildBackingStores(initialLoad: Bool = false, updateExpandedNodes: (() -> Void)? = nil) { - if !animatingChanges && !BatchUpdate.shared.isPerforming { - - addCurrentFeedToFilterExeptionsIfNecessary() - treeController.rebuild() - treeControllerDelegate.resetFilterExceptions() - - updateExpandedNodes?() - rebuildShadowTable() - masterFeedViewController.reloadFeeds(initialLoad: initialLoad) - + + func ensureFeedIsAvailableToSelect(_ feed: Feed, completion: @escaping () -> Void) { + addToFilterExeptionsIfNecessary(feed) + addShadowTableToFilterExceptions() + + rebuildBackingStores() { + self.treeControllerDelegate.resetFilterExceptions() + completion() } } - - func addCurrentFeedToFilterExeptionsIfNecessary() { - if isReadFeedsFiltered, let feedID = timelineFeed?.feedID { - if timelineFeed is SmartFeed { + + func addToFilterExeptionsIfNecessary(_ feed: Feed?) { + if isReadFeedsFiltered, let feedID = feed?.feedID { + if feed is SmartFeed { treeControllerDelegate.addFilterException(feedID) - } else if let folderFeed = timelineFeed as? Folder { + } else if let folderFeed = feed as? Folder { if folderFeed.account?.existingFolder(withID: folderFeed.folderID) != nil { treeControllerDelegate.addFilterException(feedID) } - } else if let webFeed = timelineFeed as? WebFeed { + } else if let webFeed = feed as? WebFeed { if webFeed.account?.existingWebFeed(withWebFeedID: webFeed.webFeedID) != nil { treeControllerDelegate.addFilterException(feedID) addParentFolderToFilterExceptions(webFeed) @@ -1299,7 +1340,31 @@ private extension SceneCoordinator { treeControllerDelegate.addFilterException(folderFeedID) } + + func addShadowTableToFilterExceptions() { + for section in shadowTable { + for node in section { + if let feed = node.representedObject as? Feed, let feedID = feed.feedID { + treeControllerDelegate.addFilterException(feedID) + } + } + } + } + func rebuildBackingStores(initialLoad: Bool = false, updateExpandedNodes: (() -> Void)? = nil, completion: (() -> Void)? = nil) { + if !animatingChanges && !BatchUpdate.shared.isPerforming { + + addToFilterExeptionsIfNecessary(timelineFeed) + treeController.rebuild() + treeControllerDelegate.resetFilterExceptions() + + updateExpandedNodes?() + rebuildShadowTable() + masterFeedViewController.reloadFeeds(initialLoad: initialLoad, completion: completion) + + } + } + func rebuildShadowTable() { shadowTable = [[Node]]() @@ -1405,18 +1470,30 @@ private extension SceneCoordinator { self.showIcons = false } - func markExpanded(_ node: Node) { - if let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID { + func markExpanded(_ containerIdentifiable: ContainerIdentifiable) { + if let containerID = containerIdentifiable.containerID { expandedTable.insert(containerID) } } - func unmarkExpanded(_ node: Node) { - if let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID { + func markExpanded(_ node: Node) { + if let containerIdentifiable = node.representedObject as? ContainerIdentifiable { + markExpanded(containerIdentifiable) + } + } + + func unmarkExpanded(_ containerIdentifiable: ContainerIdentifiable) { + if let containerID = containerIdentifiable.containerID { expandedTable.remove(containerID) } } + func unmarkExpanded(_ node: Node) { + if let containerIdentifiable = node.representedObject as? ContainerIdentifiable { + unmarkExpanded(containerIdentifiable) + } + } + // MARK: Select Prev Unread @discardableResult @@ -1507,7 +1584,7 @@ private extension SceneCoordinator { } if unreadCountProvider.unreadCount > 0 { - selectFeed(prevIndexPath, animations: [.scroll, .navigation]) + selectFeed(indexPath: prevIndexPath, animations: [.scroll, .navigation]) return true } @@ -1618,7 +1695,7 @@ private extension SceneCoordinator { } if unreadCountProvider.unreadCount > 0 { - selectFeed(nextIndexPath, animations: [.scroll, .navigation], deselectArticle: false) { + selectFeed(indexPath: nextIndexPath, animations: [.scroll, .navigation], deselectArticle: false) { self.currentArticle = nil completion(true) } @@ -1660,7 +1737,7 @@ private extension SceneCoordinator { } func queueFetchAndMergeArticles() { - fetchAndMergeArticlesQueue.add(self, #selector(fetchAndMergeArticlesAsync)) + fetchAndMergeArticlesQueue.add(self, #selector(fetchAndMergeArticlesAsync)) } @objc func fetchAndMergeArticlesAsync() { @@ -1920,7 +1997,7 @@ private extension SceneCoordinator { ] } - func handleSelectFeed(_ userInfo: [AnyHashable : Any]?, initialLoad: Bool) { + func handleSelectFeed(_ userInfo: [AnyHashable : Any]?) { guard let userInfo = userInfo, let feedIdentifierUserInfo = userInfo[UserInfoKey.feedIdentifier] as? [AnyHashable : AnyHashable], let feedIdentifier = FeedIdentifier(userInfo: feedIdentifierUserInfo) else { @@ -1930,23 +2007,18 @@ private extension SceneCoordinator { treeControllerDelegate.addFilterException(feedIdentifier) masterFeedViewController.restoreSelection = true - func rebuildIfNecessary() { - if !initialLoad && isReadFeedsFiltered { - rebuildBackingStores() - treeControllerDelegate.resetFilterExceptions() - } - } - switch feedIdentifier { case .smartFeed: guard let smartFeed = SmartFeedsController.shared.find(by: feedIdentifier) else { return } - - rebuildIfNecessary() - - if let indexPath = indexPathFor(smartFeed) { - selectFeed(indexPath) { - self.masterFeedViewController.focus() + + expand(SmartFeedsController.shared) + rebuildBackingStores() { + self.treeControllerDelegate.resetFilterExceptions() + if let indexPath = self.indexPathFor(smartFeed) { + self.selectFeed(indexPath: indexPath) { + self.masterFeedViewController.focus() + } } } @@ -1954,14 +2026,20 @@ private extension SceneCoordinator { break case .folder(let accountID, let folderName): - rebuildIfNecessary() - - guard let accountNode = findAccountNode(accountID: accountID), let folderNode = findFolderNode(folderName: folderName, beginningAt: accountNode) else { + guard let accountNode = self.findAccountNode(accountID: accountID), + let account = accountNode.representedObject as? Account else { return } - if let indexPath = indexPathFor(folderNode) { - selectFeed(indexPath) { - self.masterFeedViewController.focus() + + expand(account) + + rebuildBackingStores() { + self.treeControllerDelegate.resetFilterExceptions() + + if let folderNode = self.findFolderNode(folderName: folderName, beginningAt: accountNode), let indexPath = self.indexPathFor(folderNode) { + self.selectFeed(indexPath: indexPath) { + self.masterFeedViewController.focus() + } } } @@ -1972,23 +2050,13 @@ private extension SceneCoordinator { return } - for folder in account.sortedFolders ?? [Folder]() { - if folder.objectIsChild(webFeed), let folderFeedID = folder.feedID { - treeControllerDelegate.addFilterException(folderFeedID) - break - } - } - - rebuildIfNecessary() - - discloseFeed(webFeed) { + self.discloseWebFeed(webFeed) { self.masterFeedViewController.focus() } - } } - func handleReadArticle(_ userInfo: [AnyHashable : Any]?, initialLoad: Bool) { + func handleReadArticle(_ userInfo: [AnyHashable : Any]?) { guard let userInfo = userInfo else { return } guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable : Any], @@ -2007,25 +2075,11 @@ private extension SceneCoordinator { return } - guard let webFeed = account.existingWebFeed(withWebFeedID: webFeedID), - let webFeedFeedID = webFeed.feedID else { - return + guard let webFeed = account.existingWebFeed(withWebFeedID: webFeedID) else { + return } - treeControllerDelegate.addFilterException(webFeedFeedID) - for folder in account.sortedFolders ?? [Folder]() { - if folder.objectIsChild(webFeed), let folderFeedID = folder.feedID { - treeControllerDelegate.addFilterException(folderFeedID) - break - } - } - - if !initialLoad && isReadFeedsFiltered { - rebuildBackingStores() - treeControllerDelegate.resetFilterExceptions() - } - - discloseFeed(webFeed) { + discloseWebFeed(webFeed) { self.selectArticleInCurrentFeed(articleID) } } @@ -2043,7 +2097,7 @@ private extension SceneCoordinator { case .smartFeed: guard let smartFeed = SmartFeedsController.shared.find(by: feedIdentifier) else { return false } if let indexPath = indexPathFor(smartFeed) { - selectFeed(indexPath) { + selectFeed(indexPath: indexPath) { self.selectArticleInCurrentFeed(articleID) } treeControllerDelegate.addFilterException(feedIdentifier) @@ -2110,7 +2164,7 @@ private extension SceneCoordinator { func selectFeedAndArticle(feedNode: Node, articleID: String) -> Bool { if let feedIndexPath = indexPathFor(feedNode) { - selectFeed(feedIndexPath) { + selectFeed(indexPath: feedIndexPath) { self.selectArticleInCurrentFeed(articleID) } return true diff --git a/iOS/SceneDelegate.swift b/iOS/SceneDelegate.swift index dd00e8f4b..6c6447e5d 100644 --- a/iOS/SceneDelegate.swift +++ b/iOS/SceneDelegate.swift @@ -33,12 +33,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { if let notificationResponse = connectionOptions.notificationResponse { window!.makeKeyAndVisible() - coordinator.handle(notificationResponse, initialLoad: true) + coordinator.handle(notificationResponse) return } if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity { - coordinator.handle(userActivity, initialLoad: true) + coordinator.handle(userActivity) } window!.makeKeyAndVisible() @@ -52,7 +52,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { appDelegate.resumeDatabaseProcessingIfNecessary() - coordinator.handle(userActivity, initialLoad: false) + coordinator.handle(userActivity) } func sceneDidEnterBackground(_ scene: UIScene) { @@ -74,7 +74,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func handle(_ response: UNNotificationResponse) { appDelegate.resumeDatabaseProcessingIfNecessary() - coordinator.handle(response, initialLoad: false) + coordinator.handle(response) } func suspend() {