diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index afb8c2ca3..be543d33f 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -341,6 +341,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, completionHandler([.alert, .badge, .sound]) } + func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + mainWindowController?.handle(response) + completionHandler() + } + // MARK: Add Feed func addFeed(_ urlString: String?, name: String? = nil, account: Account? = nil, folder: Folder? = nil) { diff --git a/Mac/MainWindow/MainWindowController.swift b/Mac/MainWindow/MainWindowController.swift index f863b0160..7f3861ce2 100644 --- a/Mac/MainWindow/MainWindowController.swift +++ b/Mac/MainWindow/MainWindowController.swift @@ -7,6 +7,7 @@ // import AppKit +import UserNotifications import Articles import Account import RSCore @@ -112,6 +113,12 @@ class MainWindowController : NSWindowController, NSUserInterfaceValidations { return sidebarViewController?.selectedObjects } + func handle(_ response: UNNotificationResponse) { + let userInfo = response.notification.request.content.userInfo + sidebarViewController?.deepLinkRevealAndSelect(for: userInfo) + currentTimelineViewController?.goToDeepLink(for: userInfo) + } + // MARK: - Notifications // func window(_ window: NSWindow, willEncodeRestorableState state: NSCoder) { diff --git a/Mac/MainWindow/Sidebar/SidebarViewController.swift b/Mac/MainWindow/Sidebar/SidebarViewController.swift index 77150e73b..ab588dc5e 100644 --- a/Mac/MainWindow/Sidebar/SidebarViewController.swift +++ b/Mac/MainWindow/Sidebar/SidebarViewController.swift @@ -319,6 +319,13 @@ protocol SidebarDelegate: class { } + func deepLinkRevealAndSelect(for userInfo: [AnyHashable : Any]) { + guard let accountNode = findAccountNode(userInfo), let feedNode = findFeedNode(userInfo, beginningAt: accountNode) else { + return + } + revealAndSelectRepresentedObject(feedNode.representedObject) + } + } // MARK: - NSUserInterfaceValidations @@ -468,6 +475,36 @@ private extension SidebarViewController { return nil } + func findAccountNode(_ userInfo: [AnyHashable : Any]?) -> Node? { + guard let accountID = userInfo?[DeepLinkKey.accountID.rawValue] as? String else { + return nil + } + + if let node = treeController.rootNode.descendantNode(where: { ($0.representedObject as? Account)?.accountID == accountID }) { + return node + } + + guard let accountName = userInfo?[DeepLinkKey.accountName.rawValue] as? String else { + return nil + } + + if let node = treeController.rootNode.descendantNode(where: { ($0.representedObject as? Account)?.name == accountName }) { + return node + } + + return nil + } + + func findFeedNode(_ userInfo: [AnyHashable : Any]?, beginningAt startingNode: Node) -> Node? { + guard let feedID = userInfo?[DeepLinkKey.feedID.rawValue] as? String else { + return nil + } + if let node = startingNode.descendantNode(where: { ($0.representedObject as? Feed)?.feedID == feedID }) { + return node + } + return nil + } + func configure(_ cell: SidebarCell, _ node: Node) { cell.cellAppearance = sidebarCellAppearance cell.name = nameFor(node) diff --git a/Mac/MainWindow/Timeline/TimelineViewController.swift b/Mac/MainWindow/Timeline/TimelineViewController.swift index 4684058ef..144a9369c 100644 --- a/Mac/MainWindow/Timeline/TimelineViewController.swift +++ b/Mac/MainWindow/Timeline/TimelineViewController.swift @@ -387,6 +387,15 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr // MARK: - Navigation + func goToDeepLink(for userInfo: [AnyHashable : Any]) { + guard let articleID = userInfo[DeepLinkKey.articleID.rawValue] as? String else { return } + guard let ix = articles.firstIndex(where: { $0.articleID == articleID }) else { return } + + NSCursor.setHiddenUntilMouseMoves(true) + tableView.rs_selectRow(ix) + tableView.scrollTo(row: ix) + } + func goToNextUnread() { guard let ix = indexOfNextUnreadArticle() else { return