Make handling of programmatic feed selection when filtered or collapsed more consistent. Issues #1788 and #1792
This commit is contained in:
parent
6c5f0cc8b6
commit
03c1ed2625
@ -15,6 +15,13 @@ final class FetchRequestQueue {
|
|||||||
private var pendingRequests = [FetchRequestOperation]()
|
private var pendingRequests = [FetchRequestOperation]()
|
||||||
private var currentRequest: FetchRequestOperation? = nil
|
private var currentRequest: FetchRequestOperation? = nil
|
||||||
|
|
||||||
|
var isAnyCurrentRequest: Bool {
|
||||||
|
if let currentRequest = currentRequest {
|
||||||
|
return !currentRequest.isCanceled
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func cancelAllRequests() {
|
func cancelAllRequests() {
|
||||||
precondition(Thread.isMainThread)
|
precondition(Thread.isMainThread)
|
||||||
pendingRequests.forEach { $0.isCanceled = true }
|
pendingRequests.forEach { $0.isCanceled = true }
|
||||||
|
@ -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(faviconDidBecomeAvailable(_:)), name: .FaviconDidBecomeAvailable, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(webFeedIconDidBecomeAvailable(_:)), name: .WebFeedIconDidBecomeAvailable, 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(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(contentSizeCategoryDidChange), name: UIContentSizeCategory.didChangeNotification, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
|
||||||
|
|
||||||
@ -149,13 +148,6 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
|||||||
reloadAllVisibleCells()
|
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) {
|
@objc func contentSizeCategoryDidChange(_ note: Notification) {
|
||||||
resetEstimatedRowHeight()
|
resetEstimatedRowHeight()
|
||||||
applyChanges(animated: false)
|
applyChanges(animated: false)
|
||||||
@ -335,7 +327,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
|||||||
|
|
||||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||||
becomeFirstResponder()
|
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 {
|
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()
|
updateUI()
|
||||||
|
|
||||||
// We have to reload all the visible cells because if we got here by doing a table cell move,
|
// We have to reload all the visible cells because if we got here by doing a table cell move,
|
||||||
@ -553,73 +545,11 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
|
|||||||
// drops on a "folder" that should cause the dropped cell to disappear.
|
// drops on a "folder" that should cause the dropped cell to disappear.
|
||||||
applyChanges(animated: !initialLoad) { [weak self] in
|
applyChanges(animated: !initialLoad) { [weak self] in
|
||||||
if !initialLoad {
|
if !initialLoad {
|
||||||
self?.reloadAllVisibleCells()
|
self?.reloadAllVisibleCells(completion: 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 {
|
} else {
|
||||||
completion?()
|
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() {
|
func focus() {
|
||||||
@ -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) }
|
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()
|
var snapshot = dataSource.snapshot()
|
||||||
snapshot.reloadItems(nodes)
|
snapshot.reloadItems(nodes)
|
||||||
dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
|
dataSource.apply(snapshot, animatingDifferences: false) { [weak self] in
|
||||||
self?.restoreSelectionIfNecessary(adjustScroll: false)
|
self?.restoreSelectionIfNecessary(adjustScroll: false)
|
||||||
|
completion?()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1224,7 +1155,7 @@ private extension MasterFeedViewController {
|
|||||||
deleteCommand.perform()
|
deleteCommand.perform()
|
||||||
|
|
||||||
if indexPath == coordinator.currentFeedIndexPath {
|
if indexPath == coordinator.currentFeedIndexPath {
|
||||||
coordinator.selectFeed(nil)
|
coordinator.selectFeed(indexPath: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -774,7 +774,7 @@ private extension MasterTimelineViewController {
|
|||||||
|
|
||||||
let title = NSLocalizedString("Go to Feed", comment: "Go to Feed")
|
let title = NSLocalizedString("Go to Feed", comment: "Go to Feed")
|
||||||
let action = UIAction(title: title, image: AppAssets.openInSidebarImage) { [weak self] action in
|
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
|
return action
|
||||||
}
|
}
|
||||||
@ -785,7 +785,7 @@ private extension MasterTimelineViewController {
|
|||||||
|
|
||||||
let title = NSLocalizedString("Go to Feed", comment: "Go to Feed")
|
let title = NSLocalizedString("Go to Feed", comment: "Go to Feed")
|
||||||
let action = UIAlertAction(title: title, style: .default) { [weak self] action in
|
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)
|
completion(true)
|
||||||
}
|
}
|
||||||
return action
|
return action
|
||||||
|
@ -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(accountStateDidChange(_:)), name: .AccountStateDidChange, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(userDidAddAccount(_:)), name: .UserDidAddAccount, 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(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(userDefaultsDidChange(_:)), name: UserDefaults.didChangeNotification, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(downloadArticlesDidUpdateUnreadCounts(_:)), name: .DownloadArticlesDidUpdateUnreadCounts, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(downloadArticlesDidUpdateUnreadCounts(_:)), name: .DownloadArticlesDidUpdateUnreadCounts, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(accountDidDownloadArticles(_:)), name: .AccountDidDownloadArticles, 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) {
|
func handle(_ activity: NSUserActivity) {
|
||||||
selectFeed(nil) {
|
selectFeed(indexPath: nil) {
|
||||||
guard let activityType = ActivityType(rawValue: activity.activityType) else { return }
|
guard let activityType = ActivityType(rawValue: activity.activityType) else { return }
|
||||||
switch activityType {
|
switch activityType {
|
||||||
case .restoration:
|
case .restoration:
|
||||||
break
|
break
|
||||||
case .selectFeed:
|
case .selectFeed:
|
||||||
self.handleSelectFeed(activity.userInfo, initialLoad: initialLoad)
|
self.handleSelectFeed(activity.userInfo)
|
||||||
case .nextUnread:
|
case .nextUnread:
|
||||||
self.selectFirstUnreadInAllUnread()
|
self.selectFirstUnreadInAllUnread()
|
||||||
case .readArticle:
|
case .readArticle:
|
||||||
self.handleReadArticle(activity.userInfo, initialLoad: initialLoad)
|
self.handleReadArticle(activity.userInfo)
|
||||||
case .addFeedIntent:
|
case .addFeedIntent:
|
||||||
self.showAdd(.feed)
|
self.showAdd(.feed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle(_ response: UNNotificationResponse, initialLoad: Bool) {
|
func handle(_ response: UNNotificationResponse) {
|
||||||
let userInfo = response.notification.request.content.userInfo
|
let userInfo = response.notification.request.content.userInfo
|
||||||
handleReadArticle(userInfo, initialLoad: initialLoad)
|
handleReadArticle(userInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func configurePanelMode(for size: CGSize) {
|
func configurePanelMode(for size: CGSize) {
|
||||||
@ -404,15 +405,16 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func selectFirstUnreadInAllUnread() {
|
func selectFirstUnreadInAllUnread() {
|
||||||
masterFeedViewController.ensureSectionIsExpanded(0) {
|
expand(SmartFeedsController.shared)
|
||||||
self.selectFeed(IndexPath(row: 1, section: 0)) {
|
self.ensureFeedIsAvailableToSelect(SmartFeedsController.shared.unreadFeed) {
|
||||||
|
self.selectFeed(SmartFeedsController.shared.unreadFeed) {
|
||||||
self.selectFirstUnreadArticleInTimeline()
|
self.selectFirstUnreadArticleInTimeline()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func showSearch() {
|
func showSearch() {
|
||||||
selectFeed(nil) {
|
selectFeed(indexPath: nil) {
|
||||||
self.installTimelineControllerIfNecessary(animated: false)
|
self.installTimelineControllerIfNecessary(animated: false)
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
DispatchQueue.main.asyncAfter(deadline: .now()) {
|
||||||
self.masterTimelineViewController!.showSearchAll()
|
self.masterTimelineViewController!.showSearchAll()
|
||||||
@ -443,14 +445,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for section in shadowTable {
|
addShadowTableToFilterExceptions()
|
||||||
for node in section {
|
|
||||||
if let feed = node.representedObject as? Feed, let feedID = feed.feedID {
|
|
||||||
treeControllerDelegate.addFilterException(feedID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rebuildBackingStores()
|
rebuildBackingStores()
|
||||||
treeControllerDelegate.resetFilterExceptions()
|
treeControllerDelegate.resetFilterExceptions()
|
||||||
}
|
}
|
||||||
@ -490,14 +485,10 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||||||
if timelineFetcherContainsAnyPseudoFeed() {
|
if timelineFetcherContainsAnyPseudoFeed() {
|
||||||
fetchAndMergeArticlesAsync(animated: true) {
|
fetchAndMergeArticlesAsync(animated: true) {
|
||||||
self.masterTimelineViewController?.reinitializeArticles(resetScroll: false)
|
self.masterTimelineViewController?.reinitializeArticles(resetScroll: false)
|
||||||
self.rebuildBackingStores() {
|
self.rebuildBackingStores(updateExpandedNodes: expandNewlyActivatedAccount)
|
||||||
expandNewlyActivatedAccount()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rebuildBackingStores() {
|
self.rebuildBackingStores(updateExpandedNodes: expandNewlyActivatedAccount)
|
||||||
expandNewlyActivatedAccount()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -513,14 +504,10 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||||||
if timelineFetcherContainsAnyPseudoFeed() {
|
if timelineFetcherContainsAnyPseudoFeed() {
|
||||||
fetchAndMergeArticlesAsync(animated: true) {
|
fetchAndMergeArticlesAsync(animated: true) {
|
||||||
self.masterTimelineViewController?.reinitializeArticles(resetScroll: false)
|
self.masterTimelineViewController?.reinitializeArticles(resetScroll: false)
|
||||||
self.rebuildBackingStores() {
|
self.rebuildBackingStores(updateExpandedNodes: expandNewAccount)
|
||||||
expandNewAccount()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rebuildBackingStores() {
|
self.rebuildBackingStores(updateExpandedNodes: expandNewAccount)
|
||||||
expandNewAccount()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -535,15 +522,18 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||||||
if timelineFetcherContainsAnyPseudoFeed() {
|
if timelineFetcherContainsAnyPseudoFeed() {
|
||||||
fetchAndMergeArticlesAsync(animated: true) {
|
fetchAndMergeArticlesAsync(animated: true) {
|
||||||
self.masterTimelineViewController?.reinitializeArticles(resetScroll: false)
|
self.masterTimelineViewController?.reinitializeArticles(resetScroll: false)
|
||||||
self.rebuildBackingStores() {
|
self.rebuildBackingStores(updateExpandedNodes: cleanupAccount)
|
||||||
cleanupAccount()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rebuildBackingStores() {
|
self.rebuildBackingStores(updateExpandedNodes: cleanupAccount)
|
||||||
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) {
|
@objc func userDefaultsDidChange(_ note: Notification) {
|
||||||
@ -567,8 +557,13 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func willEnterForeground(_ note: Notification) {
|
@objc func willEnterForeground(_ note: Notification) {
|
||||||
|
// 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()
|
queueFetchAndMergeArticles()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: API
|
// MARK: API
|
||||||
|
|
||||||
@ -637,20 +632,35 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||||||
refreshTimeline(resetScroll: false)
|
refreshTimeline(resetScroll: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isExpanded(_ node: Node) -> Bool {
|
func isExpanded(_ containerIdentifiable: ContainerIdentifiable) -> Bool {
|
||||||
if let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID {
|
if let containerID = containerIdentifiable.containerID {
|
||||||
return expandedTable.contains(containerID)
|
return expandedTable.contains(containerID)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func expand(_ node: Node) {
|
func isExpanded(_ node: Node) -> Bool {
|
||||||
markExpanded(node)
|
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
|
animatingChanges = true
|
||||||
rebuildShadowTable()
|
rebuildShadowTable()
|
||||||
animatingChanges = false
|
animatingChanges = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func expand(_ node: Node) {
|
||||||
|
if let containerIdentifiable = node.representedObject as? ContainerIdentifiable {
|
||||||
|
expand(containerIdentifiable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func expandAllSectionsAndFolders() {
|
func expandAllSectionsAndFolders() {
|
||||||
for sectionNode in treeController.rootNode.childNodes {
|
for sectionNode in treeController.rootNode.childNodes {
|
||||||
markExpanded(sectionNode)
|
markExpanded(sectionNode)
|
||||||
@ -695,7 +705,18 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||||||
return indexPathFor(node)
|
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 {
|
guard indexPath != currentFeedIndexPath else {
|
||||||
completion?()
|
completion?()
|
||||||
return
|
return
|
||||||
@ -732,31 +753,34 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
|||||||
|
|
||||||
func selectPrevFeed() {
|
func selectPrevFeed() {
|
||||||
if let indexPath = prevFeedIndexPath {
|
if let indexPath = prevFeedIndexPath {
|
||||||
selectFeed(indexPath, animations: [.navigation, .scroll])
|
selectFeed(indexPath: indexPath, animations: [.navigation, .scroll])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectNextFeed() {
|
func selectNextFeed() {
|
||||||
if let indexPath = nextFeedIndexPath {
|
if let indexPath = nextFeedIndexPath {
|
||||||
selectFeed(indexPath, animations: [.navigation, .scroll])
|
selectFeed(indexPath: indexPath, animations: [.navigation, .scroll])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectTodayFeed() {
|
func selectTodayFeed() {
|
||||||
masterFeedViewController?.ensureSectionIsExpanded(0) {
|
expand(SmartFeedsController.shared)
|
||||||
self.selectFeed(IndexPath(row: 0, section: 0), animations: [.navigation, .scroll])
|
self.ensureFeedIsAvailableToSelect(SmartFeedsController.shared.todayFeed) {
|
||||||
|
self.selectFeed(SmartFeedsController.shared.todayFeed, animations: [.navigation, .scroll])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectAllUnreadFeed() {
|
func selectAllUnreadFeed() {
|
||||||
masterFeedViewController?.ensureSectionIsExpanded(0) {
|
expand(SmartFeedsController.shared)
|
||||||
self.selectFeed(IndexPath(row: 1, section: 0), animations: [.navigation, .scroll])
|
self.ensureFeedIsAvailableToSelect(SmartFeedsController.shared.unreadFeed) {
|
||||||
|
self.selectFeed(SmartFeedsController.shared.unreadFeed, animations: [.navigation, .scroll])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectStarredFeed() {
|
func selectStarredFeed() {
|
||||||
masterFeedViewController?.ensureSectionIsExpanded(0) {
|
expand(SmartFeedsController.shared)
|
||||||
self.selectFeed(IndexPath(row: 2, section: 0), animations: [.navigation, .scroll])
|
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
|
return timelineFeed == feed
|
||||||
}
|
}
|
||||||
|
|
||||||
func discloseFeed(_ feed: WebFeed, animations: Animations = [], completion: (() -> Void)? = nil) {
|
func discloseWebFeed(_ webFeed: WebFeed, animations: Animations = [], completion: (() -> Void)? = nil) {
|
||||||
if isSearching {
|
if isSearching {
|
||||||
masterTimelineViewController?.hideSearch()
|
masterTimelineViewController?.hideSearch()
|
||||||
}
|
}
|
||||||
|
|
||||||
masterFeedViewController.discloseFeed(feed, animations: animations) {
|
guard let account = webFeed.account else {
|
||||||
completion?()
|
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() {
|
func showStatusBar() {
|
||||||
@ -1259,29 +1304,25 @@ private extension SceneCoordinator {
|
|||||||
articleDictionaryNeedsUpdate = false
|
articleDictionaryNeedsUpdate = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func rebuildBackingStores(initialLoad: Bool = false, updateExpandedNodes: (() -> Void)? = nil) {
|
func ensureFeedIsAvailableToSelect(_ feed: Feed, completion: @escaping () -> Void) {
|
||||||
if !animatingChanges && !BatchUpdate.shared.isPerforming {
|
addToFilterExeptionsIfNecessary(feed)
|
||||||
|
addShadowTableToFilterExceptions()
|
||||||
addCurrentFeedToFilterExeptionsIfNecessary()
|
|
||||||
treeController.rebuild()
|
|
||||||
treeControllerDelegate.resetFilterExceptions()
|
|
||||||
|
|
||||||
updateExpandedNodes?()
|
|
||||||
rebuildShadowTable()
|
|
||||||
masterFeedViewController.reloadFeeds(initialLoad: initialLoad)
|
|
||||||
|
|
||||||
|
rebuildBackingStores() {
|
||||||
|
self.treeControllerDelegate.resetFilterExceptions()
|
||||||
|
completion()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addCurrentFeedToFilterExeptionsIfNecessary() {
|
func addToFilterExeptionsIfNecessary(_ feed: Feed?) {
|
||||||
if isReadFeedsFiltered, let feedID = timelineFeed?.feedID {
|
if isReadFeedsFiltered, let feedID = feed?.feedID {
|
||||||
if timelineFeed is SmartFeed {
|
if feed is SmartFeed {
|
||||||
treeControllerDelegate.addFilterException(feedID)
|
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 {
|
if folderFeed.account?.existingFolder(withID: folderFeed.folderID) != nil {
|
||||||
treeControllerDelegate.addFilterException(feedID)
|
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 {
|
if webFeed.account?.existingWebFeed(withWebFeedID: webFeed.webFeedID) != nil {
|
||||||
treeControllerDelegate.addFilterException(feedID)
|
treeControllerDelegate.addFilterException(feedID)
|
||||||
addParentFolderToFilterExceptions(webFeed)
|
addParentFolderToFilterExceptions(webFeed)
|
||||||
@ -1300,6 +1341,30 @@ private extension SceneCoordinator {
|
|||||||
treeControllerDelegate.addFilterException(folderFeedID)
|
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() {
|
func rebuildShadowTable() {
|
||||||
shadowTable = [[Node]]()
|
shadowTable = [[Node]]()
|
||||||
|
|
||||||
@ -1405,18 +1470,30 @@ private extension SceneCoordinator {
|
|||||||
self.showIcons = false
|
self.showIcons = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func markExpanded(_ node: Node) {
|
func markExpanded(_ containerIdentifiable: ContainerIdentifiable) {
|
||||||
if let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID {
|
if let containerID = containerIdentifiable.containerID {
|
||||||
expandedTable.insert(containerID)
|
expandedTable.insert(containerID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func unmarkExpanded(_ node: Node) {
|
func markExpanded(_ node: Node) {
|
||||||
if let containerID = (node.representedObject as? ContainerIdentifiable)?.containerID {
|
if let containerIdentifiable = node.representedObject as? ContainerIdentifiable {
|
||||||
|
markExpanded(containerIdentifiable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarkExpanded(_ containerIdentifiable: ContainerIdentifiable) {
|
||||||
|
if let containerID = containerIdentifiable.containerID {
|
||||||
expandedTable.remove(containerID)
|
expandedTable.remove(containerID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func unmarkExpanded(_ node: Node) {
|
||||||
|
if let containerIdentifiable = node.representedObject as? ContainerIdentifiable {
|
||||||
|
unmarkExpanded(containerIdentifiable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Select Prev Unread
|
// MARK: Select Prev Unread
|
||||||
|
|
||||||
@discardableResult
|
@discardableResult
|
||||||
@ -1507,7 +1584,7 @@ private extension SceneCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if unreadCountProvider.unreadCount > 0 {
|
if unreadCountProvider.unreadCount > 0 {
|
||||||
selectFeed(prevIndexPath, animations: [.scroll, .navigation])
|
selectFeed(indexPath: prevIndexPath, animations: [.scroll, .navigation])
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1618,7 +1695,7 @@ private extension SceneCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if unreadCountProvider.unreadCount > 0 {
|
if unreadCountProvider.unreadCount > 0 {
|
||||||
selectFeed(nextIndexPath, animations: [.scroll, .navigation], deselectArticle: false) {
|
selectFeed(indexPath: nextIndexPath, animations: [.scroll, .navigation], deselectArticle: false) {
|
||||||
self.currentArticle = nil
|
self.currentArticle = nil
|
||||||
completion(true)
|
completion(true)
|
||||||
}
|
}
|
||||||
@ -1920,7 +1997,7 @@ private extension SceneCoordinator {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSelectFeed(_ userInfo: [AnyHashable : Any]?, initialLoad: Bool) {
|
func handleSelectFeed(_ userInfo: [AnyHashable : Any]?) {
|
||||||
guard let userInfo = userInfo,
|
guard let userInfo = userInfo,
|
||||||
let feedIdentifierUserInfo = userInfo[UserInfoKey.feedIdentifier] as? [AnyHashable : AnyHashable],
|
let feedIdentifierUserInfo = userInfo[UserInfoKey.feedIdentifier] as? [AnyHashable : AnyHashable],
|
||||||
let feedIdentifier = FeedIdentifier(userInfo: feedIdentifierUserInfo) else {
|
let feedIdentifier = FeedIdentifier(userInfo: feedIdentifierUserInfo) else {
|
||||||
@ -1930,40 +2007,41 @@ private extension SceneCoordinator {
|
|||||||
treeControllerDelegate.addFilterException(feedIdentifier)
|
treeControllerDelegate.addFilterException(feedIdentifier)
|
||||||
masterFeedViewController.restoreSelection = true
|
masterFeedViewController.restoreSelection = true
|
||||||
|
|
||||||
func rebuildIfNecessary() {
|
|
||||||
if !initialLoad && isReadFeedsFiltered {
|
|
||||||
rebuildBackingStores()
|
|
||||||
treeControllerDelegate.resetFilterExceptions()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch feedIdentifier {
|
switch feedIdentifier {
|
||||||
|
|
||||||
case .smartFeed:
|
case .smartFeed:
|
||||||
guard let smartFeed = SmartFeedsController.shared.find(by: feedIdentifier) else { return }
|
guard let smartFeed = SmartFeedsController.shared.find(by: feedIdentifier) else { return }
|
||||||
|
|
||||||
rebuildIfNecessary()
|
expand(SmartFeedsController.shared)
|
||||||
|
rebuildBackingStores() {
|
||||||
if let indexPath = indexPathFor(smartFeed) {
|
self.treeControllerDelegate.resetFilterExceptions()
|
||||||
selectFeed(indexPath) {
|
if let indexPath = self.indexPathFor(smartFeed) {
|
||||||
|
self.selectFeed(indexPath: indexPath) {
|
||||||
self.masterFeedViewController.focus()
|
self.masterFeedViewController.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case .script:
|
case .script:
|
||||||
break
|
break
|
||||||
|
|
||||||
case .folder(let accountID, let folderName):
|
case .folder(let accountID, let folderName):
|
||||||
rebuildIfNecessary()
|
guard let accountNode = self.findAccountNode(accountID: accountID),
|
||||||
|
let account = accountNode.representedObject as? Account else {
|
||||||
guard let accountNode = findAccountNode(accountID: accountID), let folderNode = findFolderNode(folderName: folderName, beginningAt: accountNode) else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if let indexPath = indexPathFor(folderNode) {
|
|
||||||
selectFeed(indexPath) {
|
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()
|
self.masterFeedViewController.focus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case .webFeed(let accountID, let webFeedID):
|
case .webFeed(let accountID, let webFeedID):
|
||||||
guard let accountNode = findAccountNode(accountID: accountID),
|
guard let accountNode = findAccountNode(accountID: accountID),
|
||||||
@ -1972,23 +2050,13 @@ private extension SceneCoordinator {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for folder in account.sortedFolders ?? [Folder]() {
|
self.discloseWebFeed(webFeed) {
|
||||||
if folder.objectIsChild(webFeed), let folderFeedID = folder.feedID {
|
|
||||||
treeControllerDelegate.addFilterException(folderFeedID)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rebuildIfNecessary()
|
|
||||||
|
|
||||||
discloseFeed(webFeed) {
|
|
||||||
self.masterFeedViewController.focus()
|
self.masterFeedViewController.focus()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleReadArticle(_ userInfo: [AnyHashable : Any]?, initialLoad: Bool) {
|
func handleReadArticle(_ userInfo: [AnyHashable : Any]?) {
|
||||||
guard let userInfo = userInfo else { return }
|
guard let userInfo = userInfo else { return }
|
||||||
|
|
||||||
guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable : Any],
|
guard let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable : Any],
|
||||||
@ -2007,25 +2075,11 @@ private extension SceneCoordinator {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let webFeed = account.existingWebFeed(withWebFeedID: webFeedID),
|
guard let webFeed = account.existingWebFeed(withWebFeedID: webFeedID) else {
|
||||||
let webFeedFeedID = webFeed.feedID else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
treeControllerDelegate.addFilterException(webFeedFeedID)
|
discloseWebFeed(webFeed) {
|
||||||
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) {
|
|
||||||
self.selectArticleInCurrentFeed(articleID)
|
self.selectArticleInCurrentFeed(articleID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2043,7 +2097,7 @@ private extension SceneCoordinator {
|
|||||||
case .smartFeed:
|
case .smartFeed:
|
||||||
guard let smartFeed = SmartFeedsController.shared.find(by: feedIdentifier) else { return false }
|
guard let smartFeed = SmartFeedsController.shared.find(by: feedIdentifier) else { return false }
|
||||||
if let indexPath = indexPathFor(smartFeed) {
|
if let indexPath = indexPathFor(smartFeed) {
|
||||||
selectFeed(indexPath) {
|
selectFeed(indexPath: indexPath) {
|
||||||
self.selectArticleInCurrentFeed(articleID)
|
self.selectArticleInCurrentFeed(articleID)
|
||||||
}
|
}
|
||||||
treeControllerDelegate.addFilterException(feedIdentifier)
|
treeControllerDelegate.addFilterException(feedIdentifier)
|
||||||
@ -2110,7 +2164,7 @@ private extension SceneCoordinator {
|
|||||||
|
|
||||||
func selectFeedAndArticle(feedNode: Node, articleID: String) -> Bool {
|
func selectFeedAndArticle(feedNode: Node, articleID: String) -> Bool {
|
||||||
if let feedIndexPath = indexPathFor(feedNode) {
|
if let feedIndexPath = indexPathFor(feedNode) {
|
||||||
selectFeed(feedIndexPath) {
|
selectFeed(indexPath: feedIndexPath) {
|
||||||
self.selectArticleInCurrentFeed(articleID)
|
self.selectArticleInCurrentFeed(articleID)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -33,12 +33,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
|
|
||||||
if let notificationResponse = connectionOptions.notificationResponse {
|
if let notificationResponse = connectionOptions.notificationResponse {
|
||||||
window!.makeKeyAndVisible()
|
window!.makeKeyAndVisible()
|
||||||
coordinator.handle(notificationResponse, initialLoad: true)
|
coordinator.handle(notificationResponse)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
|
if let userActivity = connectionOptions.userActivities.first ?? session.stateRestorationActivity {
|
||||||
coordinator.handle(userActivity, initialLoad: true)
|
coordinator.handle(userActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
window!.makeKeyAndVisible()
|
window!.makeKeyAndVisible()
|
||||||
@ -52,7 +52,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
|
|
||||||
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
|
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
|
||||||
appDelegate.resumeDatabaseProcessingIfNecessary()
|
appDelegate.resumeDatabaseProcessingIfNecessary()
|
||||||
coordinator.handle(userActivity, initialLoad: false)
|
coordinator.handle(userActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sceneDidEnterBackground(_ scene: UIScene) {
|
func sceneDidEnterBackground(_ scene: UIScene) {
|
||||||
@ -74,7 +74,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|||||||
|
|
||||||
func handle(_ response: UNNotificationResponse) {
|
func handle(_ response: UNNotificationResponse) {
|
||||||
appDelegate.resumeDatabaseProcessingIfNecessary()
|
appDelegate.resumeDatabaseProcessingIfNecessary()
|
||||||
coordinator.handle(response, initialLoad: false)
|
coordinator.handle(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
func suspend() {
|
func suspend() {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user