Make handling of programmatic feed selection when filtered or collapsed more consistent. Issues #1788 and #1792

This commit is contained in:
Maurice Parker 2020-02-17 17:40:40 -08:00
parent 6c5f0cc8b6
commit 03c1ed2625
5 changed files with 204 additions and 212 deletions

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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

View File

@ -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

View File

@ -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() {