mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Add ArticleFetcherType and change state restoration to use new type.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
import Foundation
|
||||
import CoreSpotlight
|
||||
import CoreServices
|
||||
import RSCore
|
||||
import Account
|
||||
import Articles
|
||||
import Intents
|
||||
@@ -37,54 +38,15 @@ class ActivityManager {
|
||||
invalidateNextUnread()
|
||||
}
|
||||
|
||||
func selectingToday() {
|
||||
func selecting(fetcher: ArticleFetcher) {
|
||||
invalidateCurrentActivities()
|
||||
|
||||
let title = NSLocalizedString("See articles for “Today”", comment: "Today")
|
||||
selectingActivity = makeSelectingActivity(type: ActivityType.selectToday, title: title, identifier: "smartfeed.today")
|
||||
donate(selectingActivity!)
|
||||
}
|
||||
|
||||
func selectingAllUnread() {
|
||||
invalidateCurrentActivities()
|
||||
selectingActivity = makeSelectFeedActivity(fetcher: fetcher)
|
||||
|
||||
let title = NSLocalizedString("See articles in “All Unread”", comment: "All Unread")
|
||||
selectingActivity = makeSelectingActivity(type: ActivityType.selectAllUnread, title: title, identifier: "smartfeed.allUnread")
|
||||
donate(selectingActivity!)
|
||||
}
|
||||
|
||||
func selectingStarred() {
|
||||
invalidateCurrentActivities()
|
||||
if let feed = fetcher as? Feed {
|
||||
updateSelectingActivityFeedSearchAttributes(with: feed)
|
||||
}
|
||||
|
||||
let title = NSLocalizedString("See articles in “Starred”", comment: "Starred")
|
||||
selectingActivity = makeSelectingActivity(type: ActivityType.selectStarred, title: title, identifier: "smartfeed.starred")
|
||||
donate(selectingActivity!)
|
||||
}
|
||||
|
||||
func selectingFolder(_ folder: Folder) {
|
||||
invalidateCurrentActivities()
|
||||
|
||||
let localizedText = NSLocalizedString("See articles in “%@”", comment: "See articles in Folder")
|
||||
let title = NSString.localizedStringWithFormat(localizedText as NSString, folder.nameForDisplay) as String
|
||||
selectingActivity = makeSelectingActivity(type: ActivityType.selectFolder, title: title, identifier: ActivityManager.identifer(for: folder))
|
||||
|
||||
let userInfo = folder.deepLinkUserInfo
|
||||
selectingActivity!.userInfo = userInfo
|
||||
selectingActivity!.requiredUserInfoKeys = Set(userInfo.keys.map { $0 as! String })
|
||||
donate(selectingActivity!)
|
||||
}
|
||||
|
||||
func selectingFeed(_ feed: Feed) {
|
||||
invalidateCurrentActivities()
|
||||
|
||||
let localizedText = NSLocalizedString("See articles in “%@”", comment: "See articles in Feed")
|
||||
let title = NSString.localizedStringWithFormat(localizedText as NSString, feed.nameForDisplay) as String
|
||||
selectingActivity = makeSelectingActivity(type: ActivityType.selectFeed, title: title, identifier: ActivityManager.identifer(for: feed))
|
||||
|
||||
let userInfo = feed.deepLinkUserInfo
|
||||
selectingActivity!.userInfo = userInfo
|
||||
selectingActivity!.requiredUserInfoKeys = Set(userInfo.keys.map { $0 as! String })
|
||||
updateSelectingActivityFeedSearchAttributes(with: feed)
|
||||
donate(selectingActivity!)
|
||||
}
|
||||
|
||||
@@ -95,8 +57,17 @@ class ActivityManager {
|
||||
|
||||
func selectingNextUnread() {
|
||||
guard nextUnreadActivity == nil else { return }
|
||||
let title = NSLocalizedString("See first unread article", comment: "First Unread")
|
||||
nextUnreadActivity = makeSelectingActivity(type: ActivityType.nextUnread, title: title, identifier: "action.nextUnread")
|
||||
|
||||
nextUnreadActivity = NSUserActivity(activityType: ActivityType.nextUnread.rawValue)
|
||||
nextUnreadActivity!.title = NSLocalizedString("See first unread article", comment: "First Unread")
|
||||
|
||||
#if os(iOS)
|
||||
nextUnreadActivity!.suggestedInvocationPhrase = nextUnreadActivity!.title
|
||||
nextUnreadActivity!.isEligibleForPrediction = true
|
||||
nextUnreadActivity!.persistentIdentifier = "nextUnread:"
|
||||
nextUnreadActivity!.contentAttributeSet?.relatedUniqueIdentifier = "nextUnread:"
|
||||
#endif
|
||||
|
||||
donate(nextUnreadActivity!)
|
||||
}
|
||||
|
||||
@@ -105,12 +76,12 @@ class ActivityManager {
|
||||
nextUnreadActivity = nil
|
||||
}
|
||||
|
||||
func reading(_ article: Article?) {
|
||||
func reading(fetcher: ArticleFetcher?, article: Article?) {
|
||||
invalidateReading()
|
||||
invalidateNextUnread()
|
||||
|
||||
guard let article = article else { return }
|
||||
readingActivity = makeReadArticleActivity(article)
|
||||
guard let fetcher = fetcher, let article = article else { return }
|
||||
readingActivity = makeReadArticleActivity(fetcher: fetcher, article: article)
|
||||
|
||||
#if os(iOS)
|
||||
updateReadArticleSearchAttributes(with: article)
|
||||
@@ -159,7 +130,7 @@ class ActivityManager {
|
||||
#endif
|
||||
|
||||
@objc func feedIconDidBecomeAvailable(_ note: Notification) {
|
||||
guard let feed = note.userInfo?[UserInfoKey.feed] as? Feed, let activityFeedId = selectingActivity?.userInfo?[DeepLinkKey.feedID.rawValue] as? String else {
|
||||
guard let feed = note.userInfo?[UserInfoKey.feed] as? Feed, let activityFeedId = selectingActivity?.userInfo?[ArticlePathKey.feedID] as? String else {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -180,28 +151,40 @@ class ActivityManager {
|
||||
|
||||
private extension ActivityManager {
|
||||
|
||||
func makeSelectingActivity(type: ActivityType, title: String, identifier: String) -> NSUserActivity {
|
||||
let activity = NSUserActivity(activityType: type.rawValue)
|
||||
func makeSelectFeedActivity(fetcher: ArticleFetcher) -> NSUserActivity {
|
||||
let activity = NSUserActivity(activityType: ActivityType.selectFeed.rawValue)
|
||||
|
||||
let localizedText = NSLocalizedString("See articles in “%@”", comment: "See articles in Folder")
|
||||
let displayName = (fetcher as? DisplayNameProvider)?.nameForDisplay ?? ""
|
||||
let title = NSString.localizedStringWithFormat(localizedText as NSString, displayName) as String
|
||||
activity.title = title
|
||||
|
||||
activity.keywords = Set(makeKeywords(title))
|
||||
activity.isEligibleForSearch = true
|
||||
|
||||
let articleFetcherIdentifierUserInfo = fetcher.articleFetcherType?.userInfo ?? [AnyHashable: Any]()
|
||||
activity.userInfo = [UserInfoKey.feedIdentifier: articleFetcherIdentifierUserInfo]
|
||||
activity.requiredUserInfoKeys = Set(activity.userInfo!.keys.map { $0 as! String })
|
||||
|
||||
#if os(iOS)
|
||||
activity.suggestedInvocationPhrase = title
|
||||
activity.isEligibleForPrediction = true
|
||||
activity.persistentIdentifier = identifier
|
||||
activity.contentAttributeSet?.relatedUniqueIdentifier = identifier
|
||||
activity.persistentIdentifier = fetcher.articleFetcherType?.description ?? ""
|
||||
activity.contentAttributeSet?.relatedUniqueIdentifier = fetcher.articleFetcherType?.description ?? ""
|
||||
#endif
|
||||
|
||||
return activity
|
||||
}
|
||||
|
||||
func makeReadArticleActivity(_ article: Article) -> NSUserActivity {
|
||||
func makeReadArticleActivity(fetcher: ArticleFetcher, article: Article) -> NSUserActivity {
|
||||
let activity = NSUserActivity(activityType: ActivityType.readArticle.rawValue)
|
||||
activity.title = ArticleStringFormatter.truncatedTitle(article)
|
||||
let userInfo = article.deepLinkUserInfo
|
||||
activity.userInfo = userInfo
|
||||
activity.requiredUserInfoKeys = Set(userInfo.keys.map { $0 as! String })
|
||||
|
||||
let articleFetcherIdentifierUserInfo = fetcher.articleFetcherType?.userInfo ?? [AnyHashable: Any]()
|
||||
let articlePathUserInfo = article.pathUserInfo
|
||||
activity.userInfo = [UserInfoKey.feedIdentifier: articleFetcherIdentifierUserInfo, UserInfoKey.articlePath: articlePathUserInfo]
|
||||
activity.requiredUserInfoKeys = Set(activity.userInfo!.keys.map { $0 as! String })
|
||||
|
||||
activity.isEligibleForHandoff = true
|
||||
|
||||
#if os(iOS)
|
||||
|
||||
@@ -9,12 +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 selectFolder = "com.ranchero.NetNewsWire.SelectFolder"
|
||||
case selectFeed = "com.ranchero.NetNewsWire.SelectFeed"
|
||||
case nextUnread = "com.ranchero.NetNewsWire.NextUnread"
|
||||
case readArticle = "com.ranchero.NetNewsWire.ReadArticle"
|
||||
case selectFeed = "SelectFeed"
|
||||
case nextUnread = "NextUnread"
|
||||
case readArticle = "ReadArticle"
|
||||
case addFeedIntent = "AddFeedIntent"
|
||||
}
|
||||
|
||||
@@ -18,18 +18,3 @@ extension Notification.Name {
|
||||
static let WebInspectorEnabledDidChange = Notification.Name("WebInspectorEnabledDidChange")
|
||||
#endif
|
||||
}
|
||||
|
||||
typealias UserInfoDictionary = [AnyHashable: Any]
|
||||
|
||||
struct UserInfoKey {
|
||||
|
||||
static let view = "view"
|
||||
static let article = "article"
|
||||
static let articles = "articles"
|
||||
static let navigationKeyPressed = "navigationKeyPressed"
|
||||
static let objects = "objects"
|
||||
static let feed = "feed"
|
||||
static let url = "url"
|
||||
static let author = "author"
|
||||
}
|
||||
|
||||
|
||||
@@ -94,16 +94,23 @@ extension Article {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: DeepLinkProvider
|
||||
// MARK: Path
|
||||
|
||||
extension Article: DeepLinkProvider {
|
||||
struct ArticlePathKey {
|
||||
static let accountID = "accountID"
|
||||
static let accountName = "accountName"
|
||||
static let feedID = "feedID"
|
||||
static let articleID = "articleID"
|
||||
}
|
||||
|
||||
public var deepLinkUserInfo: [AnyHashable : Any] {
|
||||
extension Article {
|
||||
|
||||
public var pathUserInfo: [AnyHashable : Any] {
|
||||
return [
|
||||
DeepLinkKey.accountID.rawValue: accountID,
|
||||
DeepLinkKey.accountName.rawValue: account?.nameForDisplay ?? "",
|
||||
DeepLinkKey.feedID.rawValue: feedID,
|
||||
DeepLinkKey.articleID.rawValue: articleID
|
||||
ArticlePathKey.accountID: accountID,
|
||||
ArticlePathKey.accountName: account?.nameForDisplay ?? "",
|
||||
ArticlePathKey.feedID: feedID,
|
||||
ArticlePathKey.articleID: articleID
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@ import Articles
|
||||
|
||||
struct SearchFeedDelegate: SmartFeedDelegate {
|
||||
|
||||
var articleFetcherType: ArticleFetcherType? {
|
||||
return ArticleFetcherType.smartFeed(String(describing: SearchFeedDelegate.self))
|
||||
}
|
||||
|
||||
var nameForDisplay: String {
|
||||
return nameForDisplayPrefix + searchString
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ import Articles
|
||||
|
||||
struct SearchTimelineFeedDelegate: SmartFeedDelegate {
|
||||
|
||||
var articleFetcherType: ArticleFetcherType? {
|
||||
return ArticleFetcherType.smartFeed(String(describing: SearchTimelineFeedDelegate.self))
|
||||
}
|
||||
|
||||
var nameForDisplay: String {
|
||||
return nameForDisplayPrefix + searchString
|
||||
}
|
||||
|
||||
@@ -71,6 +71,10 @@ final class SmartFeed: PseudoFeed {
|
||||
}
|
||||
|
||||
extension SmartFeed: ArticleFetcher {
|
||||
|
||||
var articleFetcherType: ArticleFetcherType? {
|
||||
delegate.articleFetcherType
|
||||
}
|
||||
|
||||
func fetchArticles() -> Set<Article> {
|
||||
return delegate.fetchArticles()
|
||||
|
||||
@@ -20,7 +20,20 @@ final class SmartFeedsController: DisplayNameProvider {
|
||||
let starredFeed = SmartFeed(delegate: StarredFeedDelegate())
|
||||
|
||||
private init() {
|
||||
|
||||
self.smartFeeds = [todayFeed, unreadFeed, starredFeed]
|
||||
}
|
||||
|
||||
func find(by identifier: String) -> PseudoFeed? {
|
||||
switch identifier {
|
||||
case String(describing: TodayFeedDelegate.self):
|
||||
return todayFeed
|
||||
case String(describing: UnreadFeed.self):
|
||||
return unreadFeed
|
||||
case String(describing: StarredFeedDelegate.self):
|
||||
return starredFeed
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@ import Account
|
||||
|
||||
struct StarredFeedDelegate: SmartFeedDelegate {
|
||||
|
||||
var articleFetcherType: ArticleFetcherType? {
|
||||
return ArticleFetcherType.smartFeed(String(describing: StarredFeedDelegate.self))
|
||||
}
|
||||
|
||||
let nameForDisplay = NSLocalizedString("Starred", comment: "Starred pseudo-feed title")
|
||||
let fetchType: FetchType = .starred
|
||||
var smallIcon: IconImage? = AppAssets.starredFeedImage
|
||||
|
||||
@@ -13,6 +13,10 @@ import Account
|
||||
|
||||
struct TodayFeedDelegate: SmartFeedDelegate {
|
||||
|
||||
var articleFetcherType: ArticleFetcherType? {
|
||||
return ArticleFetcherType.smartFeed(String(describing: TodayFeedDelegate.self))
|
||||
}
|
||||
|
||||
let nameForDisplay = NSLocalizedString("Today", comment: "Today pseudo-feed title")
|
||||
let fetchType = FetchType.today
|
||||
var smallIcon: IconImage? = AppAssets.todayFeedImage
|
||||
|
||||
@@ -52,6 +52,10 @@ final class UnreadFeed: PseudoFeed {
|
||||
}
|
||||
|
||||
extension UnreadFeed: ArticleFetcher {
|
||||
|
||||
var articleFetcherType: ArticleFetcherType? {
|
||||
return ArticleFetcherType.smartFeed(String(describing: UnreadFeed.self))
|
||||
}
|
||||
|
||||
func fetchArticles() -> Set<Article> {
|
||||
return fetchUnreadArticles()
|
||||
|
||||
26
Shared/UserInfoKey.swift
Normal file
26
Shared/UserInfoKey.swift
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// UserInfoKey.swift
|
||||
// NetNewsWire
|
||||
//
|
||||
// Created by Maurice Parker on 11/14/19.
|
||||
// Copyright © 2019 Ranchero Software. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
typealias UserInfoDictionary = [AnyHashable: Any]
|
||||
|
||||
struct UserInfoKey {
|
||||
|
||||
static let view = "view"
|
||||
static let article = "article"
|
||||
static let articles = "articles"
|
||||
static let navigationKeyPressed = "navigationKeyPressed"
|
||||
static let objects = "objects"
|
||||
static let feed = "feed"
|
||||
static let url = "url"
|
||||
static let author = "author"
|
||||
static let articlePath = "articlePath"
|
||||
static let feedIdentifier = "feedIdentifier"
|
||||
|
||||
}
|
||||
@@ -53,7 +53,7 @@ private extension UserNotificationManager {
|
||||
}
|
||||
|
||||
content.sound = UNNotificationSound.default
|
||||
content.userInfo = article.deepLinkUserInfo
|
||||
content.userInfo = [UserInfoKey.articlePath: article.pathUserInfo]
|
||||
|
||||
let request = UNNotificationRequest.init(identifier: "articleID:\(article.articleID)", content: content, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(request)
|
||||
|
||||
Reference in New Issue
Block a user