Implement Feed protocol.

This commit is contained in:
Maurice Parker 2019-11-15 06:19:14 -06:00
parent 3fb1a3b8cc
commit 5283d2efbe
19 changed files with 119 additions and 107 deletions

View File

@ -12,7 +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 /* WebFeedMetadataFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 510BD112232C3E9D002692E4 /* WebFeedMetadataFile.swift */; };
511B9804237CD4270028BCAA /* ArticleFetcherType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9803237CD4270028BCAA /* ArticleFetcherType.swift */; };
511B9804237CD4270028BCAA /* FeedIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 511B9803237CD4270028BCAA /* FeedIdentifier.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 */; };
@ -32,6 +32,7 @@
5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */; };
5170743C232AEDB500A461A3 /* OPMLFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5170743B232AEDB500A461A3 /* OPMLFile.swift */; };
51BB7B84233531BC008E8144 /* AccountBehaviors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BB7B83233531BC008E8144 /* AccountBehaviors.swift */; };
51BC8FCC237EC055004F8B56 /* Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51BC8FCB237EC055004F8B56 /* Feed.swift */; };
51D58755227F53BE00900287 /* FeedbinTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D58754227F53BE00900287 /* FeedbinTag.swift */; };
51D5875A227F630B00900287 /* tags_delete.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58757227F630B00900287 /* tags_delete.json */; };
51D5875B227F630B00900287 /* tags_add.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58758227F630B00900287 /* tags_add.json */; };
@ -212,7 +213,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 /* WebFeedMetadataFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebFeedMetadataFile.swift; sourceTree = "<group>"; };
511B9803237CD4270028BCAA /* ArticleFetcherType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArticleFetcherType.swift; sourceTree = "<group>"; };
511B9803237CD4270028BCAA /* FeedIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedIdentifier.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>"; };
@ -233,6 +234,7 @@
5170743B232AEDB500A461A3 /* OPMLFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OPMLFile.swift; sourceTree = "<group>"; };
518B2EA52351306200400001 /* Account_project_test.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_test.xcconfig; sourceTree = "<group>"; };
51BB7B83233531BC008E8144 /* AccountBehaviors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountBehaviors.swift; sourceTree = "<group>"; };
51BC8FCB237EC055004F8B56 /* Feed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Feed.swift; sourceTree = "<group>"; };
51D58754227F53BE00900287 /* FeedbinTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinTag.swift; sourceTree = "<group>"; };
51D58757227F630B00900287 /* tags_delete.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_delete.json; sourceTree = "<group>"; };
51D58758227F630B00900287 /* tags_add.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_add.json; sourceTree = "<group>"; };
@ -531,16 +533,17 @@
84AF4EA3222CFDD100F6A800 /* AccountMetadata.swift */,
510BD110232C3801002692E4 /* AccountMetadataFile.swift */,
84F73CF0202788D80000BCEF /* ArticleFetcher.swift */,
511B9803237CD4270028BCAA /* ArticleFetcherType.swift */,
84C365491F899F3B001EC85C /* CombinedRefreshProgress.swift */,
8419740D1F6DD25F006346C4 /* Container.swift */,
84B99C9E1FAE8D3200ECDEDB /* ContainerPath.swift */,
84C8B3F31F89DE430053CCA6 /* DataExtensions.swift */,
51BC8FCB237EC055004F8B56 /* Feed.swift */,
511B9803237CD4270028BCAA /* FeedIdentifier.swift */,
841974001F6DD1EC006346C4 /* Folder.swift */,
844B297E210CE37E004020B3 /* UnreadCountProvider.swift */,
844B297C2106C7EC004020B3 /* WebFeed.swift */,
84B2D4CE2238C13D00498ADA /* WebFeedMetadata.swift */,
510BD112232C3E9D002692E4 /* WebFeedMetadataFile.swift */,
841974001F6DD1EC006346C4 /* Folder.swift */,
844B297E210CE37E004020B3 /* UnreadCountProvider.swift */,
5165D71F22835E9800D9D53D /* FeedFinder */,
515E4EB12324FF7D0057B0E7 /* Credentials */,
8419742B1F6DDE84006346C4 /* LocalAccount */,
@ -966,7 +969,7 @@
9E12B0202334696A00ADE5A0 /* FeedlyCreateFeedsForCollectionFoldersOperation.swift in Sources */,
552032FD229D5D5A009559E0 /* ReaderAPITagging.swift in Sources */,
9EAEC62A23331EE70085D7C9 /* FeedlyOrigin.swift in Sources */,
511B9804237CD4270028BCAA /* ArticleFetcherType.swift in Sources */,
511B9804237CD4270028BCAA /* FeedIdentifier.swift in Sources */,
84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */,
9E713653233AD63E00765C84 /* FeedlySetUnreadArticlesOperation.swift in Sources */,
841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */,
@ -1002,6 +1005,7 @@
5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */,
84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */,
9E510D6E234F16A8002E6F1A /* FeedlyAddFeedRequest.swift in Sources */,
51BC8FCC237EC055004F8B56 /* Feed.swift in Sources */,
846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */,
55203300229D5D5A009559E0 /* ReaderAPICaller.swift in Sources */,
9E1D154F233371DD00F4944C /* FeedlyGetCollectionsOperation.swift in Sources */,

View File

@ -11,8 +11,6 @@ import Articles
public protocol ArticleFetcher {
var articleFetcherType: ArticleFetcherType? { get }
func fetchArticles() -> Set<Article>
func fetchArticlesAsync(_ callback: @escaping ArticleSetBlock)
func fetchUnreadArticles() -> Set<Article>
@ -21,14 +19,6 @@ public protocol ArticleFetcher {
extension WebFeed: ArticleFetcher {
public var articleFetcherType: ArticleFetcherType? {
guard let accountID = account?.accountID else {
assertionFailure("Expected feed.account, but got nil.")
return nil
}
return ArticleFetcherType.webFeed(accountID, webFeedID)
}
public func fetchArticles() -> Set<Article> {
return account?.fetchArticles(.webFeed(self)) ?? Set<Article>()
}
@ -57,14 +47,6 @@ extension WebFeed: 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()

View File

@ -0,0 +1,14 @@
//
// Feed.swift
// Account
//
// Created by Maurice Parker on 11/15/19.
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
//
import Foundation
import RSCore
public protocol Feed: FeedIdentifiable, ArticleFetcher, DisplayNameProvider, UnreadCountProvider {
}

View File

@ -8,7 +8,11 @@
import Foundation
public enum ArticleFetcherType: CustomStringConvertible {
public protocol FeedIdentifiable {
var feedID: FeedIdentifier? { get }
}
public enum FeedIdentifier: CustomStringConvertible {
case smartFeed(String) // String is a unique identifier
case script(String) // String is a unique identifier
@ -61,16 +65,16 @@ public enum ArticleFetcherType: CustomStringConvertible {
switch type {
case "smartFeed":
guard let id = userInfo["id"] as? String else { return nil }
self = ArticleFetcherType.smartFeed(id)
self = FeedIdentifier.smartFeed(id)
case "script":
guard let id = userInfo["id"] as? String else { return nil }
self = ArticleFetcherType.script(id)
self = FeedIdentifier.script(id)
case "feed":
guard let accountID = userInfo["accountID"] as? String, let webFeedID = userInfo["webFeedID"] as? String else { return nil }
self = ArticleFetcherType.webFeed(accountID, webFeedID)
self = FeedIdentifier.webFeed(accountID, webFeedID)
case "folder":
guard let accountID = userInfo["accountID"] as? String, let folderName = userInfo["folderName"] as? String else { return nil }
self = ArticleFetcherType.folder(accountID, folderName)
self = FeedIdentifier.folder(accountID, folderName)
default:
return nil
}

View File

@ -10,7 +10,15 @@ import Foundation
import Articles
import RSCore
public final class Folder: DisplayNameProvider, Renamable, Container, UnreadCountProvider, Hashable {
public final class Folder: Feed, Renamable, Container, Hashable {
public var feedID: FeedIdentifier? {
guard let accountID = account?.accountID else {
assertionFailure("Expected feed.account, but got nil.")
return nil
}
return FeedIdentifier.folder(accountID, nameForDisplay)
}
public weak var account: Account?
public var topLevelWebFeeds: Set<WebFeed> = Set<WebFeed>()

View File

@ -11,7 +11,15 @@ import RSCore
import RSWeb
import Articles
public final class WebFeed: DisplayNameProvider, Renamable, UnreadCountProvider, Hashable {
public final class WebFeed: Feed, Renamable, Hashable {
public var feedID: FeedIdentifier? {
guard let accountID = account?.accountID else {
assertionFailure("Expected feed.account, but got nil.")
return nil
}
return FeedIdentifier.webFeed(accountID, webFeedID)
}
public weak var account: Account?
public let url: String

View File

@ -481,7 +481,7 @@ extension MainWindowController: TimelineContainerViewControllerDelegate {
let detailState: DetailState
if let articles = articles {
if articles.count == 1 {
activityManager.reading(fetcher: nil, article: articles.first)
activityManager.reading(feed: nil, article: articles.first)
if articles.first?.webFeed?.isArticleExtractorAlwaysOn ?? false {
detailState = .loading
startArticleExtractorForCurrentLink()

View File

@ -38,13 +38,13 @@ class ActivityManager {
invalidateNextUnread()
}
func selecting(fetcher: ArticleFetcher) {
func selecting(feed: Feed) {
invalidateCurrentActivities()
selectingActivity = makeSelectFeedActivity(fetcher: fetcher)
selectingActivity = makeSelectFeedActivity(feed: feed)
if let feed = fetcher as? WebFeed {
updateSelectingActivityFeedSearchAttributes(with: feed)
if let webFeed = feed as? WebFeed {
updateSelectingActivityFeedSearchAttributes(with: webFeed)
}
donate(selectingActivity!)
@ -76,12 +76,12 @@ class ActivityManager {
nextUnreadActivity = nil
}
func reading(fetcher: ArticleFetcher?, article: Article?) {
func reading(feed: Feed?, article: Article?) {
invalidateReading()
invalidateNextUnread()
guard let article = article else { return }
readingActivity = makeReadArticleActivity(fetcher: fetcher, article: article)
readingActivity = makeReadArticleActivity(feed: feed, article: article)
#if os(iOS)
updateReadArticleSearchAttributes(with: article)
@ -151,37 +151,36 @@ class ActivityManager {
private extension ActivityManager {
func makeSelectFeedActivity(fetcher: ArticleFetcher) -> NSUserActivity {
func makeSelectFeedActivity(feed: Feed) -> 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
let title = NSString.localizedStringWithFormat(localizedText as NSString, feed.nameForDisplay) as String
activity.title = title
activity.keywords = Set(makeKeywords(title))
activity.isEligibleForSearch = true
let articleFetcherIdentifierUserInfo = fetcher.articleFetcherType?.userInfo ?? [AnyHashable: Any]()
let articleFetcherIdentifierUserInfo = feed.feedID?.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 = fetcher.articleFetcherType?.description ?? ""
activity.contentAttributeSet?.relatedUniqueIdentifier = fetcher.articleFetcherType?.description ?? ""
activity.persistentIdentifier = feed.feedID?.description ?? ""
activity.contentAttributeSet?.relatedUniqueIdentifier = feed.feedID?.description ?? ""
#endif
return activity
}
func makeReadArticleActivity(fetcher: ArticleFetcher?, article: Article) -> NSUserActivity {
func makeReadArticleActivity(feed: Feed?, article: Article) -> NSUserActivity {
let activity = NSUserActivity(activityType: ActivityType.readArticle.rawValue)
activity.title = ArticleStringFormatter.truncatedTitle(article)
if let fetcher = fetcher {
let articleFetcherIdentifierUserInfo = fetcher.articleFetcherType?.userInfo ?? [AnyHashable: Any]()
if let feed = feed {
let articleFetcherIdentifierUserInfo = feed.feedID?.userInfo ?? [AnyHashable: Any]()
let articlePathUserInfo = article.pathUserInfo
activity.userInfo = [UserInfoKey.feedIdentifier: articleFetcherIdentifierUserInfo, UserInfoKey.articlePath: articlePathUserInfo]
} else {

View File

@ -13,12 +13,11 @@ import Articles
import Account
import RSCore
protocol PseudoFeed: class, DisplayNameProvider, UnreadCountProvider, SmallIconProvider, PasteboardWriterOwner {
protocol PseudoFeed: class, Feed, SmallIconProvider, PasteboardWriterOwner {
}
private var smartFeedIcon: RSImage = {
return RSImage(named: NSImage.smartBadgeTemplateName)!
}()
@ -35,7 +34,7 @@ import Articles
import Account
import RSCore
protocol PseudoFeed: class, DisplayNameProvider, UnreadCountProvider, SmallIconProvider {
protocol PseudoFeed: class, Feed, SmallIconProvider {
}

View File

@ -13,8 +13,8 @@ import Articles
struct SearchFeedDelegate: SmartFeedDelegate {
var articleFetcherType: ArticleFetcherType? {
return ArticleFetcherType.smartFeed(String(describing: SearchFeedDelegate.self))
var feedID: FeedIdentifier? {
return FeedIdentifier.smartFeed(String(describing: SearchFeedDelegate.self))
}
var nameForDisplay: String {

View File

@ -13,8 +13,8 @@ import Articles
struct SearchTimelineFeedDelegate: SmartFeedDelegate {
var articleFetcherType: ArticleFetcherType? {
return ArticleFetcherType.smartFeed(String(describing: SearchTimelineFeedDelegate.self))
var feedID: FeedIdentifier? {
return FeedIdentifier.smartFeed(String(describing: SearchTimelineFeedDelegate.self))
}
var nameForDisplay: String {

View File

@ -13,6 +13,10 @@ import Account
final class SmartFeed: PseudoFeed {
var feedID: FeedIdentifier? {
delegate.feedID
}
var nameForDisplay: String {
return delegate.nameForDisplay
}
@ -71,10 +75,6 @@ final class SmartFeed: PseudoFeed {
}
extension SmartFeed: ArticleFetcher {
var articleFetcherType: ArticleFetcherType? {
delegate.articleFetcherType
}
func fetchArticles() -> Set<Article> {
return delegate.fetchArticles()

View File

@ -11,10 +11,8 @@ import Account
import Articles
import RSCore
protocol SmartFeedDelegate: DisplayNameProvider, ArticleFetcher, SmallIconProvider {
protocol SmartFeedDelegate: FeedIdentifiable, DisplayNameProvider, ArticleFetcher, SmallIconProvider {
var fetchType: FetchType { get }
func fetchUnreadCount(for: Account, callback: @escaping (Int) -> Void)
}

View File

@ -15,8 +15,8 @@ import Account
struct StarredFeedDelegate: SmartFeedDelegate {
var articleFetcherType: ArticleFetcherType? {
return ArticleFetcherType.smartFeed(String(describing: StarredFeedDelegate.self))
var feedID: FeedIdentifier? {
return FeedIdentifier.smartFeed(String(describing: StarredFeedDelegate.self))
}
let nameForDisplay = NSLocalizedString("Starred", comment: "Starred pseudo-feed title")

View File

@ -13,8 +13,8 @@ import Account
struct TodayFeedDelegate: SmartFeedDelegate {
var articleFetcherType: ArticleFetcherType? {
return ArticleFetcherType.smartFeed(String(describing: TodayFeedDelegate.self))
var feedID: FeedIdentifier? {
return FeedIdentifier.smartFeed(String(describing: TodayFeedDelegate.self))
}
let nameForDisplay = NSLocalizedString("Today", comment: "Today pseudo-feed title")

View File

@ -19,6 +19,10 @@ import Articles
final class UnreadFeed: PseudoFeed {
var feedID: FeedIdentifier? {
return FeedIdentifier.smartFeed(String(describing: UnreadFeed.self))
}
let nameForDisplay = NSLocalizedString("All Unread", comment: "All Unread pseudo-feed title")
let fetchType = FetchType.unread
@ -53,10 +57,6 @@ final class UnreadFeed: PseudoFeed {
extension UnreadFeed: ArticleFetcher {
var articleFetcherType: ArticleFetcherType? {
return ArticleFetcherType.smartFeed(String(describing: UnreadFeed.self))
}
func fetchArticles() -> Set<Article> {
return fetchUnreadArticles()
}

View File

@ -93,7 +93,7 @@ class MasterFeedViewController: UITableViewController, UndoableCommandRunner {
}
var node: Node? = nil
if let coordinator = representedObject as? SceneCoordinator, let fetcher = coordinator.timelineFetcher {
if let coordinator = representedObject as? SceneCoordinator, let fetcher = coordinator.timelineFeed {
node = coordinator.rootNode.descendantNodeRepresentingObject(fetcher as AnyObject)
} else {
node = coordinator.rootNode.descendantNodeRepresentingObject(representedObject as AnyObject)

View File

@ -368,7 +368,7 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
}
@objc func displayNameDidChange(_ note: Notification) {
titleView?.label.text = coordinator.timelineName
titleView?.label.text = coordinator.timelineFeed?.nameForDisplay
}
@objc func scrollPositionDidChange() {
@ -455,16 +455,16 @@ extension MasterTimelineViewController: UISearchBarDelegate {
private extension MasterTimelineViewController {
func resetUI() {
title = coordinator.timelineName
title = coordinator.timelineFeed?.nameForDisplay
if let titleView = Bundle.main.loadNibNamed("MasterTimelineTitleView", owner: self, options: nil)?[0] as? MasterTimelineTitleView {
self.titleView = titleView
titleView.iconView.iconImage = coordinator.timelineIconImage
titleView.label.text = coordinator.timelineName
titleView.label.text = coordinator.timelineFeed?.nameForDisplay
updateTitleUnreadCount()
if coordinator.timelineFetcher is WebFeed {
if coordinator.timelineFeed is WebFeed {
titleView.heightAnchor.constraint(equalToConstant: 44.0).isActive = true
let tap = UITapGestureRecognizer(target: self, action:#selector(showFeedInspector(_:)))
titleView.addGestureRecognizer(tap)
@ -494,7 +494,7 @@ private extension MasterTimelineViewController {
}
func updateTitleUnreadCount() {
if let unreadCountProvider = coordinator.timelineFetcher as? UnreadCountProvider {
if let unreadCountProvider = coordinator.timelineFeed as? UnreadCountProvider {
self.titleView?.unreadCountView.unreadCount = unreadCountProvider.unreadCount
}
}

View File

@ -110,7 +110,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
private(set) var currentFeedIndexPath: IndexPath?
var timelineIconImage: IconImage? {
if let feed = timelineFetcher as? WebFeed {
if let feed = timelineFeed as? WebFeed {
let feedIconImage = appDelegate.webFeedIconDownloader.icon(for: feed)
if feedIconImage != nil {
@ -123,19 +123,15 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
return (timelineFetcher as? SmallIconProvider)?.smallIcon
return (timelineFeed as? SmallIconProvider)?.smallIcon
}
var timelineName: String? {
return (timelineFetcher as? DisplayNameProvider)?.nameForDisplay
}
var timelineFetcher: ArticleFetcher? {
var timelineFeed: Feed? {
didSet {
timelineMiddleIndexPath = nil
if timelineFetcher is WebFeed {
if timelineFeed is WebFeed {
showFeedNames = false
} else {
showFeedNames = true
@ -259,7 +255,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
var isTimelineUnreadAvailable: Bool {
if let unreadProvider = timelineFetcher as? UnreadCountProvider {
if let unreadProvider = timelineFeed as? UnreadCountProvider {
return unreadProvider.unreadCount > 0
}
return false
@ -519,7 +515,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
func masterFeedIndexPathForCurrentTimeline() -> IndexPath? {
guard let node = treeController.rootNode.descendantNodeRepresentingObject(timelineFetcher as AnyObject) else {
guard let node = treeController.rootNode.descendantNodeRepresentingObject(timelineFeed as AnyObject) else {
return nil
}
return indexPathFor(node)
@ -533,12 +529,12 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
masterFeedViewController.updateFeedSelection(animated: animated)
if let ip = indexPath, let node = nodeFor(ip), let fetcher = node.representedObject as? ArticleFetcher {
timelineFetcher = fetcher
activityManager.selecting(fetcher: fetcher)
if let ip = indexPath, let node = nodeFor(ip), let feed = node.representedObject as? Feed {
timelineFeed = feed
activityManager.selecting(feed: feed)
installTimelineControllerIfNecessary(animated: animated)
} else {
timelineFetcher = nil
timelineFeed = nil
activityManager.invalidateSelecting()
if rootSplitViewController.isCollapsed && navControllerForTimeline().viewControllers.last is MasterTimelineViewController {
navControllerForTimeline().popViewController(animated: animated)
@ -582,7 +578,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
stopArticleExtractor()
currentArticle = article
activityManager.reading(fetcher: timelineFetcher, article: article)
activityManager.reading(feed: timelineFeed, article: article)
if article == nil {
if rootSplitViewController.isCollapsed {
@ -621,7 +617,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
func beginSearching() {
isSearching = true
searchArticleIds = Set(articles.map { $0.articleID })
timelineFetcher = nil
timelineFeed = nil
}
func endSearching() {
@ -630,10 +626,10 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
lastSearchScope = nil
searchArticleIds = nil
if let ip = currentFeedIndexPath, let node = nodeFor(ip), let fetcher = node.representedObject as? ArticleFetcher {
timelineFetcher = fetcher
if let ip = currentFeedIndexPath, let node = nodeFor(ip), let feed = node.representedObject as? Feed {
timelineFeed = feed
} else {
timelineFetcher = nil
timelineFeed = nil
}
selectArticle(nil)
@ -644,7 +640,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
guard isSearching else { return }
if searchString.count < 3 {
timelineFetcher = nil
timelineFeed = nil
return
}
@ -652,9 +648,9 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
switch searchScope {
case .global:
timelineFetcher = SmartFeed(delegate: SearchFeedDelegate(searchString: searchString))
timelineFeed = SmartFeed(delegate: SearchFeedDelegate(searchString: searchString))
case .timeline:
timelineFetcher = SmartFeed(delegate: SearchTimelineFeedDelegate(searchString: searchString, articleIDs: searchArticleIds!))
timelineFeed = SmartFeed(delegate: SearchTimelineFeedDelegate(searchString: searchString, articleIDs: searchArticleIds!))
}
lastSearchString = searchString
@ -806,7 +802,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
func showFeedInspector() {
guard let feed = timelineFetcher as? WebFeed else {
guard let feed = timelineFeed as? WebFeed else {
return
}
showFeedInspector(for: feed)
@ -1362,11 +1358,11 @@ private extension SceneCoordinator {
@objc func fetchAndMergeArticles() {
guard let timelineFetcher = timelineFetcher else {
guard let timelineFeed = timelineFeed else {
return
}
fetchUnsortedArticlesAsync(for: [timelineFetcher]) { [weak self] (unsortedArticles) in
fetchUnsortedArticlesAsync(for: [timelineFeed]) { [weak self] (unsortedArticles) in
// Merge articles by articleID. For any unique articleID in current articles, add to unsortedArticles.
guard let strongSelf = self else {
return
@ -1395,7 +1391,7 @@ private extension SceneCoordinator {
// so that the entire display refreshes at once.
// Its a better user experience this way.
cancelPendingAsyncFetches()
guard let timelineFetcher = timelineFetcher else {
guard let timelineFetcher = timelineFeed else {
emptyTheTimeline()
return
}
@ -1407,7 +1403,7 @@ private extension SceneCoordinator {
// To be called when we need to do an entire fetch, but an async delay is okay.
// Example: we have the Today feed selected, and the calendar day just changed.
cancelPendingAsyncFetches()
guard let timelineFetcher = timelineFetcher else {
guard let timelineFetcher = timelineFeed else {
emptyTheTimeline()
return
}
@ -1447,14 +1443,14 @@ private extension SceneCoordinator {
}
func timelineFetcherContainsAnyPseudoFeed() -> Bool {
if timelineFetcher is PseudoFeed {
if timelineFeed is PseudoFeed {
return true
}
return false
}
func timelineFetcherContainsAnyFolder() -> Bool {
if timelineFetcher is Folder {
if timelineFeed is Folder {
return true
}
return false
@ -1464,13 +1460,13 @@ private extension SceneCoordinator {
// Return true if theres a match or if a folder contains (recursively) one of feeds
if let feed = timelineFetcher as? WebFeed {
if let feed = timelineFeed as? WebFeed {
for oneFeed in feeds {
if feed.webFeedID == oneFeed.webFeedID || feed.url == oneFeed.url {
return true
}
}
} else if let folder = timelineFetcher as? Folder {
} else if let folder = timelineFeed as? Folder {
for oneFeed in feeds {
if folder.hasWebFeed(with: oneFeed.webFeedID) || folder.hasWebFeed(withURL: oneFeed.url) {
return true
@ -1625,7 +1621,7 @@ private extension SceneCoordinator {
func handleSelectFeed(_ userInfo: [AnyHashable : Any]?) {
guard let userInfo = userInfo,
let feedIdentifierUserInfo = userInfo[UserInfoKey.feedIdentifier] as? [AnyHashable : Any],
let articleFetcherType = ArticleFetcherType(userInfo: feedIdentifierUserInfo) else {
let articleFetcherType = FeedIdentifier(userInfo: feedIdentifierUserInfo) else {
return
}