From a0636b8f4700f1f330d2acc0d35650c8a5efae7f Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sun, 25 Aug 2019 17:04:15 -0500 Subject: [PATCH] Add Smart Feeds as User Activities --- Shared/Activity/ActivityManager.swift | 55 ++++++++++++++++++++++++++- Shared/Activity/ActivityType.swift | 3 ++ iOS/AppCoordinator.swift | 55 +++++++++++++++++++++------ iOS/Resources/Info.plist | 3 ++ 4 files changed, 103 insertions(+), 13 deletions(-) diff --git a/Shared/Activity/ActivityManager.swift b/Shared/Activity/ActivityManager.swift index 582983d15..f5a6c7f4b 100644 --- a/Shared/Activity/ActivityManager.swift +++ b/Shared/Activity/ActivityManager.swift @@ -10,18 +10,65 @@ import Foundation import CoreSpotlight import CoreServices import Articles +import Intents class ActivityManager { public static var shared = ActivityManager() + private var selectingActivity: NSUserActivity? = nil + private var readingActivity: NSUserActivity? = nil + + func selectingToday() { + let title = NSLocalizedString("See articles for Today", comment: "Today") + selectingActivity = makeSmartFeedActivity(type: ActivityType.selectToday, title: title, identifier: "smartfeed.today") + selectingActivity!.becomeCurrent() + } + + func selectingAllUnread() { + let title = NSLocalizedString("See articles in All Unread", comment: "All Unread") + selectingActivity = makeSmartFeedActivity(type: ActivityType.selectAllUnread, title: title, identifier: "smartfeed.allUnread") + selectingActivity!.becomeCurrent() + } + + func selectingStarred() { + let title = NSLocalizedString("See articles in Starred", comment: "Starred") + selectingActivity = makeSmartFeedActivity(type: ActivityType.selectStarred, title: title, identifier: "smartfeed.starred") + selectingActivity!.becomeCurrent() + } + + func reading(_ article: Article?) { + readingActivity?.invalidate() + readingActivity = nil + guard let article = article else { return } + readingActivity = makeReadArticleActivity(article) + readingActivity?.becomeCurrent() + } + +} + +// MARK: Private + +private extension ActivityManager { + + func makeSmartFeedActivity(type: ActivityType, title: String, identifier: String) -> NSUserActivity { + let activity = NSUserActivity(activityType: type.rawValue) + activity.title = title + activity.suggestedInvocationPhrase = title + activity.keywords = Set(makeKeywords(title)) + activity.isEligibleForPrediction = true + activity.isEligibleForSearch = true + activity.persistentIdentifier = identifier + return activity + } + func makeReadArticleActivity(_ article: Article) -> NSUserActivity { let activity = NSUserActivity(activityType: ActivityType.readArticle.rawValue) activity.title = article.title - let feedNameKeywords = article.feed?.nameForDisplay.components(separatedBy: " ").filter { $0.count > 2 } ?? [] - let articleTitleKeywords = article.title?.components(separatedBy: " ").filter { $0.count > 2 } ?? [] + let feedNameKeywords = makeKeywords(article.feed?.nameForDisplay) + let articleTitleKeywords = makeKeywords(article.title) let keywords = feedNameKeywords + articleTitleKeywords activity.keywords = Set(keywords) @@ -51,4 +98,8 @@ class ActivityManager { return activity } + func makeKeywords(_ value: String?) -> [String] { + return value?.components(separatedBy: " ").filter { $0.count > 2 } ?? [] + } + } diff --git a/Shared/Activity/ActivityType.swift b/Shared/Activity/ActivityType.swift index 26000f44a..3e2874441 100644 --- a/Shared/Activity/ActivityType.swift +++ b/Shared/Activity/ActivityType.swift @@ -9,5 +9,8 @@ import Foundation enum ActivityType: String { + case selectToday = "com.ranchero.NetNewsWire.SelectToday" + case selectAllUnread = "com.ranchero.NetNewsWire.SelectAllUnread" + case selectStarred = "com.ranchero.NetNewsWire.SelectStarred" case readArticle = "com.ranchero.NetNewsWire.ReadArticle" } diff --git a/iOS/AppCoordinator.swift b/iOS/AppCoordinator.swift index 8b93ff480..fe55ec63e 100644 --- a/iOS/AppCoordinator.swift +++ b/iOS/AppCoordinator.swift @@ -90,6 +90,7 @@ class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { timelineFetcher = fetcher } masterFeedViewController.updateFeedSelection() + updateSelectingActivity(with: node) } } @@ -203,8 +204,6 @@ class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { } } - private(set) var readActivity: NSUserActivity? = nil - override init() { super.init() @@ -248,6 +247,12 @@ class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { func handle(_ activity: NSUserActivity) { guard let activityType = ActivityType(rawValue: activity.activityType) else { return } switch activityType { + case .selectToday: + handleSelectToday() + case .selectAllUnread: + handleSelectAllUnread() + case .selectStarred: + handleSelectStarred() case .readArticle: handleReadArticle(activity) } @@ -361,6 +366,13 @@ class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { return nil } + func indexPath(for object: AnyObject) -> IndexPath? { + guard let node = treeController.rootNode.descendantNodeRepresentingObject(object) else { + return nil + } + return indexPathFor(node) + } + func unreadCountFor(_ node: Node) -> Int { // The coordinator supplies the unread count for the currently selected feed node if let indexPath = currentMasterIndexPath, let selectedNode = nodeFor(indexPath), selectedNode == node { @@ -510,7 +522,7 @@ class AppCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider { func selectArticle(_ indexPath: IndexPath?) { currentArticleIndexPath = indexPath - updateReadArticleUserActivity() + ActivityManager.shared.reading(currentArticle) if indexPath == nil { if !rootSplitViewController.isCollapsed { @@ -1173,14 +1185,35 @@ private extension AppCoordinator { // MARK: NSUserActivity - func updateReadArticleUserActivity() { - readActivity?.invalidate() - readActivity = nil - - guard let article = currentArticle else { return } - - readActivity = ActivityManager.shared.makeReadArticleActivity(article) - readActivity?.becomeCurrent() + func updateSelectingActivity(with node: Node) { + switch true { + case node.representedObject === SmartFeedsController.shared.todayFeed: + ActivityManager.shared.selectingToday() + case node.representedObject === SmartFeedsController.shared.unreadFeed: + ActivityManager.shared.selectingAllUnread() + case node.representedObject === SmartFeedsController.shared.starredFeed: + ActivityManager.shared.selectingStarred() + default: + break + } + } + + func handleSelectToday() { + if let indexPath = indexPath(for: SmartFeedsController.shared.todayFeed) { + selectFeed(indexPath) + } + } + + func handleSelectAllUnread() { + if let indexPath = indexPath(for: SmartFeedsController.shared.unreadFeed) { + selectFeed(indexPath) + } + } + + func handleSelectStarred() { + if let indexPath = indexPath(for: SmartFeedsController.shared.starredFeed) { + selectFeed(indexPath) + } } func handleReadArticle(_ activity: NSUserActivity) { diff --git a/iOS/Resources/Info.plist b/iOS/Resources/Info.plist index fcfc523d3..1594130c6 100644 --- a/iOS/Resources/Info.plist +++ b/iOS/Resources/Info.plist @@ -49,6 +49,9 @@ NSUserActivityTypes + com.ranchero.NetNewsWire.SelectAllUnread + com.ranchero.NetNewsWire.SelectStarred + com.ranchero.NetNewsWire.SelectToday com.ranchero.NetNewsWire.ReadArticle NSAppTransportSecurity