diff --git a/Frameworks/Account/Feed.swift b/Frameworks/Account/Feed.swift
index f9f6be0a3..0ca76adcd 100644
--- a/Frameworks/Account/Feed.swift
+++ b/Frameworks/Account/Feed.swift
@@ -183,7 +183,7 @@ public final class Feed: DisplayNameProvider, Renamable, UnreadCountProvider, De
public var deepLinkUserInfo: [AnyHashable : Any] {
return [
DeepLinkKey.accountID.rawValue: account?.accountID ?? "",
- DeepLinkKey.accountName.rawValue: account?.name ?? "",
+ DeepLinkKey.accountName.rawValue: account?.nameForDisplay ?? "",
DeepLinkKey.feedID.rawValue: feedID
]
}
diff --git a/Frameworks/Account/Folder.swift b/Frameworks/Account/Folder.swift
index cb5713ec3..c341e110a 100644
--- a/Frameworks/Account/Folder.swift
+++ b/Frameworks/Account/Folder.swift
@@ -37,7 +37,7 @@ public final class Folder: DisplayNameProvider, Renamable, Container, UnreadCoun
public var deepLinkUserInfo: [AnyHashable : Any] {
return [
DeepLinkKey.accountID.rawValue: account?.accountID ?? "",
- DeepLinkKey.accountName.rawValue: account?.name ?? "",
+ DeepLinkKey.accountName.rawValue: account?.nameForDisplay ?? "",
DeepLinkKey.folderName.rawValue: nameForDisplay
]
}
diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift
index be543d33f..ec7cc7595 100644
--- a/Mac/AppDelegate.swift
+++ b/Mac/AppDelegate.swift
@@ -211,6 +211,14 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
}
#endif
}
+
+ func application(_ application: NSApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([NSUserActivityRestoring]) -> Void) -> Bool {
+ guard let mainWindowController = mainWindowController else {
+ return false
+ }
+ mainWindowController.handle(userActivity)
+ return true
+ }
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
// https://github.com/brentsimmons/NetNewsWire/issues/522
diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift
index 7f3861ce2..d4212211b 100644
--- a/Mac/MainWindow/MainWindowController.swift
+++ b/Mac/MainWindow/MainWindowController.swift
@@ -18,6 +18,8 @@ enum TimelineSourceMode {
class MainWindowController : NSWindowController, NSUserInterfaceValidations {
+ private var activityManager = ActivityManager()
+
private var isShowingExtractedArticle = false
private var articleExtractor: ArticleExtractor? = nil
private var sharingServicePickerDelegate: NSSharingServicePickerDelegate?
@@ -119,6 +121,12 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations {
currentTimelineViewController?.goToDeepLink(for: userInfo)
}
+ func handle(_ activity: NSUserActivity) {
+ guard let userInfo = activity.userInfo else { return }
+ sidebarViewController?.deepLinkRevealAndSelect(for: userInfo)
+ currentTimelineViewController?.goToDeepLink(for: userInfo)
+ }
+
// MARK: - Notifications
// func window(_ window: NSWindow, willEncodeRestorableState state: NSCoder) {
@@ -456,6 +464,8 @@ extension MainWindowController: SidebarDelegate {
extension MainWindowController: TimelineContainerViewControllerDelegate {
func timelineSelectionDidChange(_: TimelineContainerViewController, articles: [Article]?, mode: TimelineSourceMode) {
+ activityManager.invalidateReading()
+
articleExtractor?.cancel()
articleExtractor = nil
isShowingExtractedArticle = false
@@ -464,6 +474,7 @@ extension MainWindowController: TimelineContainerViewControllerDelegate {
let detailState: DetailState
if let articles = articles {
if articles.count == 1 {
+ activityManager.reading(articles.first!)
if articles.first?.feed?.isArticleExtractorAlwaysOn ?? false {
detailState = .loading
startArticleExtractorForCurrentLink()
diff --git a/Mac/MainWindow/Sidebar/SidebarViewController.swift b/Mac/MainWindow/Sidebar/SidebarViewController.swift
index ab588dc5e..9924d5de5 100644
--- a/Mac/MainWindow/Sidebar/SidebarViewController.swift
+++ b/Mac/MainWindow/Sidebar/SidebarViewController.swift
@@ -488,7 +488,7 @@ private extension SidebarViewController {
return nil
}
- if let node = treeController.rootNode.descendantNode(where: { ($0.representedObject as? Account)?.name == accountName }) {
+ if let node = treeController.rootNode.descendantNode(where: { ($0.representedObject as? Account)?.nameForDisplay == accountName }) {
return node
}
diff --git a/Mac/Resources/Info.plist b/Mac/Resources/Info.plist
index 31bd46543..a9c54ee1d 100644
--- a/Mac/Resources/Info.plist
+++ b/Mac/Resources/Info.plist
@@ -43,6 +43,10 @@
NSAllowsArbitraryLoads
+ NSUserActivityTypes
+
+ com.ranchero.NetNewsWire.ReadArticle
+
NSAppleEventsUsageDescription
NetNewsWire communicates with other apps on your Mac when you choose to share an article.
NSAppleScriptEnabled
diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj
index 8d9ac8097..10fec3605 100644
--- a/NetNewsWire.xcodeproj/project.pbxproj
+++ b/NetNewsWire.xcodeproj/project.pbxproj
@@ -219,6 +219,8 @@
51FD413B2342BD0500880194 /* MasterTimelineUnreadCountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FD413A2342BD0500880194 /* MasterTimelineUnreadCountView.swift */; };
51FE10032345529D0056195D /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; };
51FE10042345529D0056195D /* UserNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE10022345529D0056195D /* UserNotificationManager.swift */; };
+ 51FE10092346739D0056195D /* ActivityType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D87EE02311D34700E63F03 /* ActivityType.swift */; };
+ 51FE100A234673A00056195D /* ActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51934CCD2310792F006127BE /* ActivityManager.swift */; };
55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */ = {isa = PBXBuildFile; fileRef = 55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */; };
55E15BCC229D65A900D6602A /* AccountsReaderAPIWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */; };
5F323809231DF9F000706F6B /* NNWTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F323808231DF9F000706F6B /* NNWTableViewCell.swift */; };
@@ -2947,6 +2949,7 @@
51E595A5228CC36500FCC42B /* ArticleStatusSyncTimer.swift in Sources */,
849A97651ED9EB96007D329B /* FeedTreeControllerDelegate.swift in Sources */,
849A97671ED9EB96007D329B /* UnreadCountView.swift in Sources */,
+ 51FE10092346739D0056195D /* ActivityType.swift in Sources */,
840BEE4121D70E64009BBAFA /* CrashReportWindowController.swift in Sources */,
8426118A1FCB67AA0086A189 /* FeedIconDownloader.swift in Sources */,
84C9FC7B22629E1200D921D6 /* AccountsControlsBackgroundView.swift in Sources */,
@@ -2973,6 +2976,7 @@
84E185C3203BB12600F69BFA /* MultilineTextFieldSizer.swift in Sources */,
8477ACBE22238E9500DF7F37 /* SearchFeedDelegate.swift in Sources */,
51E3EB33229AB02C00645299 /* ErrorHandler.swift in Sources */,
+ 51FE100A234673A00056195D /* ActivityManager.swift in Sources */,
8472058120142E8900AD578B /* FeedInspectorViewController.swift in Sources */,
55E15BCC229D65A900D6602A /* AccountsReaderAPIWindowController.swift in Sources */,
5144EA382279FC6200D19003 /* AccountsAddLocalWindowController.swift in Sources */,
diff --git a/Shared/Activity/ActivityManager.swift b/Shared/Activity/ActivityManager.swift
index a7772ca79..c663fcbc5 100644
--- a/Shared/Activity/ActivityManager.swift
+++ b/Shared/Activity/ActivityManager.swift
@@ -113,6 +113,7 @@ class ActivityManager {
readingActivity = nil
}
+ #if os(iOS)
static func cleanUp(_ account: Account) {
var ids = [String]()
@@ -143,7 +144,8 @@ class ActivityManager {
static func cleanUp(_ feed: Feed) {
NSUserActivity.deleteSavedUserActivities(withPersistentIdentifiers: identifers(for: feed)) {}
}
-
+ #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 {
return
@@ -162,28 +164,33 @@ private extension ActivityManager {
func makeSelectingActivity(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
+
+ #if os(iOS)
+ activity.suggestedInvocationPhrase = title
+ activity.isEligibleForPrediction = true
activity.persistentIdentifier = identifier
+ #endif
+
return activity
}
func makeReadArticleActivity(_ article: Article) -> NSUserActivity {
let activity = NSUserActivity(activityType: ActivityType.readArticle.rawValue)
-
activity.title = article.title
+ activity.userInfo = article.deepLinkUserInfo
+ activity.isEligibleForHandoff = true
+
+ #if os(iOS)
let feedNameKeywords = makeKeywords(article.feed?.nameForDisplay)
let articleTitleKeywords = makeKeywords(article.title)
let keywords = feedNameKeywords + articleTitleKeywords
activity.keywords = Set(keywords)
- activity.userInfo = article.deepLinkUserInfo
activity.isEligibleForSearch = true
activity.isEligibleForPrediction = false
- activity.isEligibleForHandoff = true
activity.persistentIdentifier = ActivityManager.identifer(for: article)
// CoreSpotlight
@@ -197,7 +204,8 @@ private extension ActivityManager {
}
activity.contentAttributeSet = attributeSet
-
+ #endif
+
return activity
}
@@ -211,9 +219,17 @@ private extension ActivityManager {
attributeSet.title = feed.nameForDisplay
attributeSet.keywords = makeKeywords(feed.nameForDisplay)
if let image = appDelegate.feedIconDownloader.icon(for: feed) {
+ #if os(iOS)
attributeSet.thumbnailData = image.pngData()
+ #else
+ attributeSet.thumbnailData = image.tiffRepresentation
+ #endif
} else if let image = appDelegate.faviconDownloader.faviconAsAvatar(for: feed) {
+ #if os(iOS)
attributeSet.thumbnailData = image.pngData()
+ #else
+ attributeSet.thumbnailData = image.tiffRepresentation
+ #endif
}
selectingActivity!.contentAttributeSet = attributeSet
diff --git a/Shared/Data/ArticleUtilities.swift b/Shared/Data/ArticleUtilities.swift
index e33a56e33..e3b2eb88e 100644
--- a/Shared/Data/ArticleUtilities.swift
+++ b/Shared/Data/ArticleUtilities.swift
@@ -98,7 +98,7 @@ extension Article: DeepLinkProvider {
public var deepLinkUserInfo: [AnyHashable : Any] {
return [
DeepLinkKey.accountID.rawValue: accountID,
- DeepLinkKey.accountName.rawValue: account?.name ?? "",
+ DeepLinkKey.accountName.rawValue: account?.nameForDisplay ?? "",
DeepLinkKey.feedID.rawValue: feedID,
DeepLinkKey.articleID.rawValue: articleID
]
diff --git a/iOS/SceneCoordinator.swift b/iOS/SceneCoordinator.swift
index 5f3e5e5d8..e78178057 100644
--- a/iOS/SceneCoordinator.swift
+++ b/iOS/SceneCoordinator.swift
@@ -1658,7 +1658,7 @@ private extension SceneCoordinator {
return nil
}
- if let node = treeController.rootNode.descendantNode(where: { ($0.representedObject as? Account)?.name == accountName }) {
+ if let node = treeController.rootNode.descendantNode(where: { ($0.representedObject as? Account)?.nameForDisplay == accountName }) {
return node
}