mirror of
https://github.com/Ranchero-Software/NetNewsWire.git
synced 2024-12-18 12:28:37 +01:00
Add ArticleFetcherType and change state restoration to use new type.
This commit is contained in:
parent
48fef65bc4
commit
0d66259650
@ -12,6 +12,7 @@
|
||||
5107A09D227DE77700C7C3C5 /* TestTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5107A09C227DE77700C7C3C5 /* TestTransport.swift */; };
|
||||
510BD111232C3801002692E4 /* AccountMetadataFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510BD110232C3801002692E4 /* AccountMetadataFile.swift */; };
|
||||
510BD113232C3E9D002692E4 /* FeedMetadataFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510BD112232C3E9D002692E4 /* FeedMetadataFile.swift */; };
|
||||
511B9804237CD4270028BCAA /* ArticleFetcherType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9803237CD4270028BCAA /* ArticleFetcherType.swift */; };
|
||||
513323082281070D00C30F19 /* AccountFeedbinSyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 513323072281070C00C30F19 /* AccountFeedbinSyncTest.swift */; };
|
||||
5133230A2281082F00C30F19 /* subscriptions_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 513323092281082F00C30F19 /* subscriptions_initial.json */; };
|
||||
5133230C2281088A00C30F19 /* subscriptions_add.json in Resources */ = {isa = PBXBuildFile; fileRef = 5133230B2281088A00C30F19 /* subscriptions_add.json */; };
|
||||
@ -41,7 +42,6 @@
|
||||
51E490362288C37100C791F0 /* FeedbinDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E490352288C37100C791F0 /* FeedbinDate.swift */; };
|
||||
51E59599228C77BC00FCC42B /* FeedbinUnreadEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E59598228C77BC00FCC42B /* FeedbinUnreadEntry.swift */; };
|
||||
51E5959B228C781500FCC42B /* FeedbinStarredEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E5959A228C781500FCC42B /* FeedbinStarredEntry.swift */; };
|
||||
51FE1008234635A20056195D /* DeepLinkProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51FE1007234635A20056195D /* DeepLinkProvider.swift */; };
|
||||
552032F8229D5D5A009559E0 /* ReaderAPIEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552032ED229D5D5A009559E0 /* ReaderAPIEntry.swift */; };
|
||||
552032F9229D5D5A009559E0 /* ReaderAPISubscription.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552032EE229D5D5A009559E0 /* ReaderAPISubscription.swift */; };
|
||||
552032FB229D5D5A009559E0 /* ReaderAPITag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552032F0229D5D5A009559E0 /* ReaderAPITag.swift */; };
|
||||
@ -210,6 +210,7 @@
|
||||
5107A09C227DE77700C7C3C5 /* TestTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestTransport.swift; sourceTree = "<group>"; };
|
||||
510BD110232C3801002692E4 /* AccountMetadataFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountMetadataFile.swift; sourceTree = "<group>"; };
|
||||
510BD112232C3E9D002692E4 /* FeedMetadataFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedMetadataFile.swift; sourceTree = "<group>"; };
|
||||
511B9803237CD4270028BCAA /* ArticleFetcherType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleFetcherType.swift; sourceTree = "<group>"; };
|
||||
513323072281070C00C30F19 /* AccountFeedbinSyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFeedbinSyncTest.swift; sourceTree = "<group>"; };
|
||||
513323092281082F00C30F19 /* subscriptions_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = subscriptions_initial.json; sourceTree = "<group>"; };
|
||||
5133230B2281088A00C30F19 /* subscriptions_add.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = subscriptions_add.json; sourceTree = "<group>"; };
|
||||
@ -240,7 +241,6 @@
|
||||
51E490352288C37100C791F0 /* FeedbinDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinDate.swift; sourceTree = "<group>"; };
|
||||
51E59598228C77BC00FCC42B /* FeedbinUnreadEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinUnreadEntry.swift; sourceTree = "<group>"; };
|
||||
51E5959A228C781500FCC42B /* FeedbinStarredEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinStarredEntry.swift; sourceTree = "<group>"; };
|
||||
51FE1007234635A20056195D /* DeepLinkProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinkProvider.swift; sourceTree = "<group>"; };
|
||||
552032ED229D5D5A009559E0 /* ReaderAPIEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReaderAPIEntry.swift; sourceTree = "<group>"; };
|
||||
552032EE229D5D5A009559E0 /* ReaderAPISubscription.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReaderAPISubscription.swift; sourceTree = "<group>"; };
|
||||
552032F0229D5D5A009559E0 /* ReaderAPITag.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReaderAPITag.swift; sourceTree = "<group>"; };
|
||||
@ -527,6 +527,7 @@
|
||||
84AF4EA3222CFDD100F6A800 /* AccountMetadata.swift */,
|
||||
510BD110232C3801002692E4 /* AccountMetadataFile.swift */,
|
||||
84F73CF0202788D80000BCEF /* ArticleFetcher.swift */,
|
||||
511B9803237CD4270028BCAA /* ArticleFetcherType.swift */,
|
||||
84C365491F899F3B001EC85C /* CombinedRefreshProgress.swift */,
|
||||
8419740D1F6DD25F006346C4 /* Container.swift */,
|
||||
84B99C9E1FAE8D3200ECDEDB /* ContainerPath.swift */,
|
||||
@ -536,7 +537,6 @@
|
||||
510BD112232C3E9D002692E4 /* FeedMetadataFile.swift */,
|
||||
841974001F6DD1EC006346C4 /* Folder.swift */,
|
||||
844B297E210CE37E004020B3 /* UnreadCountProvider.swift */,
|
||||
51FE1007234635A20056195D /* DeepLinkProvider.swift */,
|
||||
5165D71F22835E9800D9D53D /* FeedFinder */,
|
||||
515E4EB12324FF7D0057B0E7 /* Credentials */,
|
||||
8419742B1F6DDE84006346C4 /* LocalAccount */,
|
||||
@ -960,6 +960,7 @@
|
||||
9E12B0202334696A00ADE5A0 /* FeedlyCreateFeedsForCollectionFoldersOperation.swift in Sources */,
|
||||
552032FD229D5D5A009559E0 /* ReaderAPITagging.swift in Sources */,
|
||||
9EAEC62A23331EE70085D7C9 /* FeedlyOrigin.swift in Sources */,
|
||||
511B9804237CD4270028BCAA /* ArticleFetcherType.swift in Sources */,
|
||||
84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */,
|
||||
9E713653233AD63E00765C84 /* FeedlySetUnreadArticlesOperation.swift in Sources */,
|
||||
841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */,
|
||||
@ -1013,7 +1014,6 @@
|
||||
9E1773D923458D590056A5A8 /* FeedlyResourceId.swift in Sources */,
|
||||
9EE4CCFA234F106600FBAE4B /* FeedlyFeedContainerValidator.swift in Sources */,
|
||||
552032FC229D5D5A009559E0 /* ReaderAPIUnreadEntry.swift in Sources */,
|
||||
51FE1008234635A20056195D /* DeepLinkProvider.swift in Sources */,
|
||||
9EC688EA232B973C00A8D0A2 /* FeedlyAPICaller.swift in Sources */,
|
||||
9E1773D32345700F0056A5A8 /* FeedlyLink.swift in Sources */,
|
||||
9EAEC62823331C350085D7C9 /* FeedlyCategory.swift in Sources */,
|
||||
|
@ -11,6 +11,8 @@ import Articles
|
||||
|
||||
public protocol ArticleFetcher {
|
||||
|
||||
var articleFetcherType: ArticleFetcherType? { get }
|
||||
|
||||
func fetchArticles() -> Set<Article>
|
||||
func fetchArticlesAsync(_ callback: @escaping ArticleSetBlock)
|
||||
func fetchUnreadArticles() -> Set<Article>
|
||||
@ -18,7 +20,15 @@ public protocol ArticleFetcher {
|
||||
}
|
||||
|
||||
extension Feed: ArticleFetcher {
|
||||
|
||||
|
||||
public var articleFetcherType: ArticleFetcherType? {
|
||||
guard let accountID = account?.accountID else {
|
||||
assertionFailure("Expected feed.account, but got nil.")
|
||||
return nil
|
||||
}
|
||||
return ArticleFetcherType.feed(accountID, feedID)
|
||||
}
|
||||
|
||||
public func fetchArticles() -> Set<Article> {
|
||||
return account?.fetchArticles(.feed(self)) ?? Set<Article>()
|
||||
}
|
||||
@ -48,6 +58,14 @@ extension Feed: ArticleFetcher {
|
||||
|
||||
extension Folder: ArticleFetcher {
|
||||
|
||||
public var articleFetcherType: ArticleFetcherType? {
|
||||
guard let accountID = account?.accountID else {
|
||||
assertionFailure("Expected feed.account, but got nil.")
|
||||
return nil
|
||||
}
|
||||
return ArticleFetcherType.folder(accountID, nameForDisplay)
|
||||
}
|
||||
|
||||
public func fetchArticles() -> Set<Article> {
|
||||
return fetchUnreadArticles()
|
||||
}
|
||||
|
79
Frameworks/Account/ArticleFetcherType.swift
Normal file
79
Frameworks/Account/ArticleFetcherType.swift
Normal file
@ -0,0 +1,79 @@
|
||||
//
|
||||
// ArticleFetcherType.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Maurice Parker on 11/13/19.
|
||||
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum ArticleFetcherType: CustomStringConvertible {
|
||||
|
||||
case smartFeed(String) // String is a unique identifier
|
||||
case script(String) // String is a unique identifier
|
||||
case feed(String, String) // accountID, feedID
|
||||
case folder(String, String) // accountID, folderName
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .smartFeed(let id):
|
||||
return "smartFeed: \(id)"
|
||||
case .script(let id):
|
||||
return "script: \(id)"
|
||||
case .feed(let accountID, let feedID):
|
||||
return "feed: \(accountID)_\(feedID)"
|
||||
case .folder(let accountID, let folderName):
|
||||
return "folder: \(accountID)_\(folderName)"
|
||||
}
|
||||
}
|
||||
|
||||
public var userInfo: [AnyHashable: Any] {
|
||||
switch self {
|
||||
case .smartFeed(let id):
|
||||
return [
|
||||
"type": "smartFeed",
|
||||
"id": id
|
||||
]
|
||||
case .script(let id):
|
||||
return [
|
||||
"type": "script",
|
||||
"id": id
|
||||
]
|
||||
case .feed(let accountID, let feedID):
|
||||
return [
|
||||
"type": "feed",
|
||||
"accountID": accountID,
|
||||
"feedID": feedID
|
||||
]
|
||||
case .folder(let accountID, let folderName):
|
||||
return [
|
||||
"type": "folder",
|
||||
"accountID": accountID,
|
||||
"folderName": folderName
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
public init?(userInfo: [AnyHashable: Any]) {
|
||||
guard let type = userInfo["type"] as? String else { return nil }
|
||||
|
||||
switch type {
|
||||
case "smartFeed":
|
||||
guard let id = userInfo["id"] as? String else { return nil }
|
||||
self = ArticleFetcherType.smartFeed(id)
|
||||
case "script":
|
||||
guard let id = userInfo["id"] as? String else { return nil }
|
||||
self = ArticleFetcherType.script(id)
|
||||
case "feed":
|
||||
guard let accountID = userInfo["accountID"] as? String, let feedID = userInfo["feedID"] as? String else { return nil }
|
||||
self = ArticleFetcherType.feed(accountID, feedID)
|
||||
case "folder":
|
||||
guard let accountID = userInfo["accountID"] as? String, let folderName = userInfo["folderName"] as? String else { return nil }
|
||||
self = ArticleFetcherType.folder(accountID, folderName)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
//
|
||||
// DeepLinkProvider.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Maurice Parker on 10/3/19.
|
||||
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum DeepLinkKey: String {
|
||||
case accountID = "accountID"
|
||||
case accountName = "accountName"
|
||||
case feedID = "feedID"
|
||||
case articleID = "articleID"
|
||||
case folderName = "folderName"
|
||||
}
|
||||
|
||||
public protocol DeepLinkProvider {
|
||||
var deepLinkUserInfo: [AnyHashable : Any] { get }
|
||||
}
|
@ -11,7 +11,7 @@ import RSCore
|
||||
import RSWeb
|
||||
import Articles
|
||||
|
||||
public final class Feed: DisplayNameProvider, Renamable, UnreadCountProvider, DeepLinkProvider, Hashable {
|
||||
public final class Feed: DisplayNameProvider, Renamable, UnreadCountProvider, Hashable {
|
||||
|
||||
public weak var account: Account?
|
||||
public let url: String
|
||||
@ -179,16 +179,6 @@ public final class Feed: DisplayNameProvider, Renamable, UnreadCountProvider, De
|
||||
account.renameFeed(self, to: newName, completion: completion)
|
||||
}
|
||||
|
||||
// MARK: - DeepLinkProvider
|
||||
|
||||
public var deepLinkUserInfo: [AnyHashable : Any] {
|
||||
return [
|
||||
DeepLinkKey.accountID.rawValue: account?.accountID ?? "",
|
||||
DeepLinkKey.accountName.rawValue: account?.nameForDisplay ?? "",
|
||||
DeepLinkKey.feedID.rawValue: feedID
|
||||
]
|
||||
}
|
||||
|
||||
// MARK: - UnreadCountProvider
|
||||
|
||||
public var unreadCount: Int {
|
||||
|
@ -10,7 +10,7 @@ import Foundation
|
||||
import Articles
|
||||
import RSCore
|
||||
|
||||
public final class Folder: DisplayNameProvider, Renamable, Container, UnreadCountProvider, DeepLinkProvider, Hashable {
|
||||
public final class Folder: DisplayNameProvider, Renamable, Container, UnreadCountProvider, Hashable {
|
||||
|
||||
public weak var account: Account?
|
||||
public var topLevelFeeds: Set<Feed> = Set<Feed>()
|
||||
@ -33,16 +33,6 @@ public final class Folder: DisplayNameProvider, Renamable, Container, UnreadCoun
|
||||
return name ?? Folder.untitledName
|
||||
}
|
||||
|
||||
// MARK: - DeepLinkProvider
|
||||
|
||||
public var deepLinkUserInfo: [AnyHashable : Any] {
|
||||
return [
|
||||
DeepLinkKey.accountID.rawValue: account?.accountID ?? "",
|
||||
DeepLinkKey.accountName.rawValue: account?.nameForDisplay ?? "",
|
||||
DeepLinkKey.folderName.rawValue: nameForDisplay
|
||||
]
|
||||
}
|
||||
|
||||
// MARK: - UnreadCountProvider
|
||||
|
||||
public var unreadCount = 0 {
|
||||
|
@ -19,6 +19,8 @@
|
||||
5110C37D2373A8D100A9C04F /* InspectorIconHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5110C37C2373A8D100A9C04F /* InspectorIconHeaderView.swift */; };
|
||||
51126DA4225FDE2F00722696 /* RSImage-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */; };
|
||||
5115CAF42266301400B21BCE /* AddContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */; };
|
||||
511B9806237DCAC90028BCAA /* UserInfoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9805237DCAC90028BCAA /* UserInfoKey.swift */; };
|
||||
511B9807237DCAC90028BCAA /* UserInfoKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9805237DCAC90028BCAA /* UserInfoKey.swift */; };
|
||||
511D43CF231FA62200FB1562 /* DetailKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5127B237222B4849006D641D /* DetailKeyboardShortcuts.plist */; };
|
||||
511D43D0231FA62500FB1562 /* TimelineKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 845479871FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist */; };
|
||||
511D43D1231FA62800FB1562 /* SidebarKeyboardShortcuts.plist in Resources */ = {isa = PBXBuildFile; fileRef = 844B5B681FEA20DF00C7C76A /* SidebarKeyboardShortcuts.plist */; };
|
||||
@ -1210,6 +1212,7 @@
|
||||
51121AA12265430A00BC0EC1 /* NetNewsWire_iOSapp_target.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = NetNewsWire_iOSapp_target.xcconfig; sourceTree = "<group>"; };
|
||||
51121B5A22661FEF00BC0EC1 /* AddContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContainerViewController.swift; sourceTree = "<group>"; };
|
||||
51126DA3225FDE2F00722696 /* RSImage-Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RSImage-Extensions.swift"; sourceTree = "<group>"; };
|
||||
511B9805237DCAC90028BCAA /* UserInfoKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInfoKey.swift; sourceTree = "<group>"; };
|
||||
511D43EE231FBDE800FB1562 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreenPad.storyboard; sourceTree = "<group>"; };
|
||||
511D4410231FC02D00FB1562 /* KeyboardManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardManager.swift; sourceTree = "<group>"; };
|
||||
512363372369155100951F16 /* RoundedProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedProgressView.swift; sourceTree = "<group>"; };
|
||||
@ -2193,8 +2196,8 @@
|
||||
children = (
|
||||
849A97731ED9EC04007D329B /* ArticleStringFormatter.swift */,
|
||||
849A97581ED9EB0D007D329B /* ArticleUtilities.swift */,
|
||||
84411E701FE5FBFA004B527F /* SmallIconProvider.swift */,
|
||||
5108F6B52375E612001ABC45 /* CacheCleaner.swift */,
|
||||
84411E701FE5FBFA004B527F /* SmallIconProvider.swift */,
|
||||
);
|
||||
path = Data;
|
||||
sourceTree = "<group>";
|
||||
@ -2424,6 +2427,7 @@
|
||||
841D4D5E2106B3E100DD04E6 /* ArticlesDatabase.xcodeproj */,
|
||||
51554BFC228B6EB50055115A /* SyncDatabase.xcodeproj */,
|
||||
842E45CD1ED8C308000A8B52 /* AppNotifications.swift */,
|
||||
511B9805237DCAC90028BCAA /* UserInfoKey.swift */,
|
||||
51C452AD2265102800C03939 /* Timeline */,
|
||||
84702AB31FA27AE8006B8943 /* Commands */,
|
||||
51934CCC231078DC006127BE /* Activity */,
|
||||
@ -3881,6 +3885,7 @@
|
||||
51C45296226509D300C03939 /* OPMLExporter.swift in Sources */,
|
||||
51C45291226509C800C03939 /* SmartFeed.swift in Sources */,
|
||||
51C452A722650A3D00C03939 /* RSImage-Extensions.swift in Sources */,
|
||||
511B9807237DCAC90028BCAA /* UserInfoKey.swift in Sources */,
|
||||
51C45269226508F600C03939 /* MasterFeedTableViewCell.swift in Sources */,
|
||||
51F85BFD2275DCA800C787DC /* SingleLineUILabelSizer.swift in Sources */,
|
||||
517630232336657E00E15FFF /* ArticleViewControllerWebViewProvider.swift in Sources */,
|
||||
@ -4074,6 +4079,7 @@
|
||||
845A29241FC9255E007B49E3 /* SidebarCellAppearance.swift in Sources */,
|
||||
845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */,
|
||||
848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */,
|
||||
511B9806237DCAC90028BCAA /* UserInfoKey.swift in Sources */,
|
||||
84C9FC7722629E1200D921D6 /* AdvancedPreferencesViewController.swift in Sources */,
|
||||
849EE72120391F560082A1EA /* SharingServicePickerDelegate.swift in Sources */,
|
||||
5108F6B62375E612001ABC45 /* CacheCleaner.swift in Sources */,
|
||||
|
@ -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)
|
||||
|
@ -61,13 +61,9 @@
|
||||
<key>NSUserActivityTypes</key>
|
||||
<array>
|
||||
<string>AddFeedIntent</string>
|
||||
<string>com.ranchero.NetNewsWire.NextUnread</string>
|
||||
<string>com.ranchero.NetNewsWire.ReadArticle</string>
|
||||
<string>com.ranchero.NetNewsWire.SelectAllUnread</string>
|
||||
<string>com.ranchero.NetNewsWire.SelectFeed</string>
|
||||
<string>com.ranchero.NetNewsWire.SelectFolder</string>
|
||||
<string>com.ranchero.NetNewsWire.SelectStarred</string>
|
||||
<string>com.ranchero.NetNewsWire.SelectToday</string>
|
||||
<string>NextUnread</string>
|
||||
<string>ReadArticle</string>
|
||||
<string>SelectFeed</string>
|
||||
</array>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
|
@ -332,14 +332,6 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
|
||||
guard let activityType = ActivityType(rawValue: activity.activityType) else { return }
|
||||
switch activityType {
|
||||
case .selectToday:
|
||||
handleSelectToday()
|
||||
case .selectAllUnread:
|
||||
handleSelectAllUnread()
|
||||
case .selectStarred:
|
||||
handleSelectStarred()
|
||||
case .selectFolder:
|
||||
handleSelectFolder(activity.userInfo)
|
||||
case .selectFeed:
|
||||
handleSelectFeed(activity.userInfo)
|
||||
case .nextUnread:
|
||||
@ -543,7 +535,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
|
||||
if let ip = indexPath, let node = nodeFor(ip), let fetcher = node.representedObject as? ArticleFetcher {
|
||||
timelineFetcher = fetcher
|
||||
updateSelectingActivity(with: node)
|
||||
activityManager.selecting(fetcher: fetcher)
|
||||
installTimelineControllerIfNecessary(animated: animated)
|
||||
} else {
|
||||
timelineFetcher = nil
|
||||
@ -590,7 +582,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
|
||||
|
||||
stopArticleExtractor()
|
||||
currentArticle = article
|
||||
activityManager.reading(currentArticle)
|
||||
activityManager.reading(fetcher: timelineFetcher, article: article)
|
||||
|
||||
if article == nil {
|
||||
if rootSplitViewController.isCollapsed {
|
||||
@ -1630,108 +1622,84 @@ private extension SceneCoordinator {
|
||||
|
||||
// MARK: NSUserActivity
|
||||
|
||||
func updateSelectingActivity(with node: Node) {
|
||||
switch true {
|
||||
case node.representedObject === SmartFeedsController.shared.todayFeed:
|
||||
activityManager.selectingToday()
|
||||
case node.representedObject === SmartFeedsController.shared.unreadFeed:
|
||||
activityManager.selectingAllUnread()
|
||||
case node.representedObject === SmartFeedsController.shared.starredFeed:
|
||||
activityManager.selectingStarred()
|
||||
case node.representedObject is Folder:
|
||||
activityManager.selectingFolder(node.representedObject as! Folder)
|
||||
case node.representedObject is Feed:
|
||||
activityManager.selectingFeed(node.representedObject as! Feed)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func handleSelectToday() {
|
||||
if let indexPath = indexPathFor(SmartFeedsController.shared.todayFeed) {
|
||||
selectFeed(indexPath, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
func handleSelectAllUnread() {
|
||||
if let indexPath = indexPathFor(SmartFeedsController.shared.unreadFeed) {
|
||||
selectFeed(indexPath, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
func handleSelectStarred() {
|
||||
if let indexPath = indexPathFor(SmartFeedsController.shared.starredFeed) {
|
||||
selectFeed(indexPath, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
func handleSelectFolder(_ userInfo: [AnyHashable : Any]?) {
|
||||
guard let accountNode = findAccountNode(userInfo), let folderNode = findFolderNode(userInfo, beginningAt: accountNode) else {
|
||||
return
|
||||
}
|
||||
if let indexPath = indexPathFor(folderNode) {
|
||||
selectFeed(indexPath, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
func handleSelectFeed(_ userInfo: [AnyHashable : Any]?) {
|
||||
guard let accountNode = findAccountNode(userInfo), let feedNode = findFeedNode(userInfo, beginningAt: accountNode) else {
|
||||
return
|
||||
}
|
||||
if let feed = feedNode.representedObject as? Feed {
|
||||
discloseFeed(feed, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
func handleReadArticle(_ userInfo: [AnyHashable : Any]?) {
|
||||
guard let accountNode = findAccountNode(userInfo), let feedNode = findFeedNode(userInfo, beginningAt: accountNode) else {
|
||||
return
|
||||
guard let userInfo = userInfo,
|
||||
let feedIdentifierUserInfo = userInfo[UserInfoKey.feedIdentifier] as? [AnyHashable : Any],
|
||||
let articleFetcherType = ArticleFetcherType(userInfo: feedIdentifierUserInfo) else {
|
||||
return
|
||||
}
|
||||
|
||||
switch articleFetcherType {
|
||||
|
||||
discloseFeed(feedNode.representedObject as! Feed, animated: false) {
|
||||
case .smartFeed(let identifier):
|
||||
guard let smartFeed = SmartFeedsController.shared.find(by: identifier) else { return }
|
||||
if let indexPath = indexPathFor(smartFeed) {
|
||||
selectFeed(indexPath, animated: false)
|
||||
}
|
||||
|
||||
guard let articleID = userInfo?[DeepLinkKey.articleID.rawValue] as? String else { return }
|
||||
if let article = self.articles.first(where: { $0.articleID == articleID }) {
|
||||
self.selectArticle(article)
|
||||
case .script:
|
||||
break
|
||||
|
||||
case .folder(let accountID, let folderName):
|
||||
guard let accountNode = findAccountNode(accountID: accountID), let folderNode = findFolderNode(folderName: folderName, beginningAt: accountNode) else {
|
||||
return
|
||||
}
|
||||
if let indexPath = indexPathFor(folderNode) {
|
||||
selectFeed(indexPath, animated: false)
|
||||
}
|
||||
|
||||
case .feed(let accountID, let feedID):
|
||||
guard let accountNode = findAccountNode(accountID: accountID), let feedNode = findFeedNode(feedID: feedID, beginningAt: accountNode) else {
|
||||
return
|
||||
}
|
||||
if let feed = feedNode.representedObject as? Feed {
|
||||
discloseFeed(feed, animated: false)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func findAccountNode(_ userInfo: [AnyHashable : Any]?) -> Node? {
|
||||
guard let accountID = userInfo?[DeepLinkKey.accountID.rawValue] as? String else {
|
||||
return nil
|
||||
func handleReadArticle(_ userInfo: [AnyHashable : Any]?) {
|
||||
guard let userInfo = userInfo,
|
||||
let articlePathUserInfo = userInfo[UserInfoKey.articlePath] as? [AnyHashable : Any],
|
||||
let accountID = articlePathUserInfo[ArticlePathKey.accountID] as? String,
|
||||
let accountName = articlePathUserInfo[ArticlePathKey.accountName] as? String,
|
||||
let feedID = articlePathUserInfo[ArticlePathKey.feedID] as? String,
|
||||
let articleID = articlePathUserInfo[ArticlePathKey.articleID] as? String else {
|
||||
return
|
||||
}
|
||||
|
||||
guard let accountNode = findAccountNode(accountID: accountID, accountName: accountName), let feedNode = findFeedNode(feedID: feedID, beginningAt: accountNode) else {
|
||||
return
|
||||
}
|
||||
|
||||
discloseFeed(feedNode.representedObject as! Feed, animated: false) {
|
||||
if let article = self.articles.first(where: { $0.articleID == articleID }) {
|
||||
self.selectArticle(article)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findAccountNode(accountID: String, accountName: String? = nil) -> Node? {
|
||||
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)?.nameForDisplay == accountName }) {
|
||||
if let accountName = accountName, let node = treeController.rootNode.descendantNode(where: { ($0.representedObject as? Account)?.nameForDisplay == accountName }) {
|
||||
return node
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findFolderNode(_ userInfo: [AnyHashable : Any]?, beginningAt startingNode: Node) -> Node? {
|
||||
guard let folderName = userInfo?[DeepLinkKey.folderName.rawValue] as? String else {
|
||||
return nil
|
||||
}
|
||||
func findFolderNode(folderName: String, beginningAt startingNode: Node) -> Node? {
|
||||
if let node = startingNode.descendantNode(where: { ($0.representedObject as? Folder)?.nameForDisplay == folderName }) {
|
||||
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
|
||||
}
|
||||
func findFeedNode(feedID: String, beginningAt startingNode: Node) -> Node? {
|
||||
if let node = startingNode.descendantNode(where: { ($0.representedObject as? Feed)?.feedID == feedID }) {
|
||||
return node
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user