Continue changing webFeed to feed.

This commit is contained in:
Brent Simmons
2023-07-05 08:42:56 -07:00
parent ecd6075bd2
commit 428cb73c34
48 changed files with 522 additions and 526 deletions

View File

@@ -139,7 +139,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
}
}
public var topLevelWebFeeds = Set<WebFeed>()
public var topLevelFeeds = Set<WebFeed>()
public var folders: Set<Folder>? = Set<Folder>()
public var externalID: String? {
@@ -158,24 +158,24 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
return nil
}
private var webFeedDictionariesNeedUpdate = true
private var _idToWebFeedDictionary = [String: WebFeed]()
var idToWebFeedDictionary: [String: WebFeed] {
if webFeedDictionariesNeedUpdate {
rebuildWebFeedDictionaries()
private var feedDictionariesNeedUpdate = true
private var _idToFeedDictionary = [String: WebFeed]()
var idToFeedDictionary: [String: WebFeed] {
if feedDictionariesNeedUpdate {
rebuildFeedDictionaries()
}
return _idToWebFeedDictionary
return _idToFeedDictionary
}
private var _externalIDToWebFeedDictionary = [String: WebFeed]()
var externalIDToWebFeedDictionary: [String: WebFeed] {
if webFeedDictionariesNeedUpdate {
rebuildWebFeedDictionaries()
private var _externalIDToFeedDictionary = [String: WebFeed]()
var externalIDToFeedDictionary: [String: WebFeed] {
if feedDictionariesNeedUpdate {
rebuildFeedDictionaries()
}
return _externalIDToWebFeedDictionary
return _externalIDToFeedDictionary
}
var flattenedWebFeedURLs: Set<String> {
return Set(flattenedWebFeeds().map({ $0.url }))
var flattenedFeedURLs: Set<String> {
return Set(flattenedFeeds().map({ $0.url }))
}
var username: String? {
@@ -210,8 +210,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
private var unreadCounts = [String: Int]() // [feedID: Int]
private var _flattenedWebFeeds = Set<WebFeed>()
private var flattenedWebFeedsNeedUpdate = true
private var _flattenedFeeds = Set<WebFeed>()
private var flattenedFeedsNeedUpdate = true
private lazy var opmlFile = OPMLFile(filename: (dataFolder as NSString).appendingPathComponent("Subscriptions.opml"), account: self)
private lazy var metadataFile = AccountMetadataFile(filename: (dataFolder as NSString).appendingPathComponent("Settings.plist"), account: self)
@@ -331,7 +331,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
self.metadata.performedApril2020RetentionPolicyChange = true
}
self.database.cleanupDatabaseAtStartup(subscribedToFeedIDs: self.flattenedWebFeeds().webFeedIDs())
self.database.cleanupDatabaseAtStartup(subscribedToFeedIDs: self.flattenedFeeds().webFeedIDs())
self.fetchAllUnreadCounts()
}
@@ -496,7 +496,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
func addOPMLItems(_ items: [RSOPMLItem]) {
for item in items {
if let feedSpecifier = item.feedSpecifier {
addWebFeed(newWebFeed(with: feedSpecifier))
addFeed(newFeed(with: feedSpecifier))
} else {
if let title = item.titleFromAttributes, let folder = ensureFolder(with: title) {
folder.externalID = item.attributes?["nnw_externalID"] as? String
@@ -505,7 +505,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
}
item.children?.forEach { itemChild in
if let feedSpecifier = itemChild.feedSpecifier {
folder.addWebFeed(newWebFeed(with: feedSpecifier))
folder.addFeed(newFeed(with: feedSpecifier))
}
}
}
@@ -524,13 +524,13 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
return existingFolder(withExternalID: externalID)
}
func existingContainers(withWebFeed webFeed: WebFeed) -> [Container] {
func existingContainers(withFeed feed: WebFeed) -> [Container] {
var containers = [Container]()
if topLevelWebFeeds.contains(webFeed) {
if topLevelFeeds.contains(feed) {
containers.append(self)
}
folders?.forEach { folder in
if folder.topLevelWebFeeds.contains(webFeed) {
if folder.topLevelFeeds.contains(feed) {
containers.append(folder)
}
}
@@ -575,9 +575,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
return folders?.first(where: { $0.externalID == externalID })
}
func newWebFeed(with opmlFeedSpecifier: RSOPMLFeedSpecifier) -> WebFeed {
func newFeed(with opmlFeedSpecifier: RSOPMLFeedSpecifier) -> WebFeed {
let feedURL = opmlFeedSpecifier.feedURL
let metadata = feedMetadata(feedURL: feedURL, webFeedID: feedURL)
let metadata = feedMetadata(feedURL: feedURL, feedID: feedURL)
let feed = WebFeed(account: self, url: opmlFeedSpecifier.feedURL, metadata: metadata)
if let feedTitle = opmlFeedSpecifier.title {
if feed.name == nil {
@@ -587,36 +587,36 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
return feed
}
public func addWebFeed(_ feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.addWebFeed(for: self, with: feed, to: container, completion: completion)
public func addFeed(_ feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.addFeed(for: self, with: feed, to: container, completion: completion)
}
public func createWebFeed(url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
delegate.createWebFeed(for: self, url: url, name: name, container: container, validateFeed: validateFeed, completion: completion)
public func createFeed(url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
delegate.createFeed(for: self, url: url, name: name, container: container, validateFeed: validateFeed, completion: completion)
}
func createWebFeed(with name: String?, url: String, webFeedID: String, homePageURL: String?) -> WebFeed {
let metadata = feedMetadata(feedURL: url, webFeedID: webFeedID)
func createFeed(with name: String?, url: String, feedID: String, homePageURL: String?) -> WebFeed {
let metadata = feedMetadata(feedURL: url, feedID: feedID)
let feed = WebFeed(account: self, url: url, metadata: metadata)
feed.name = name
feed.homePageURL = homePageURL
return feed
}
public func removeWebFeed(_ feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.removeWebFeed(for: self, with: feed, from: container, completion: completion)
public func removeFeed(_ feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.removeFeed(for: self, with: feed, from: container, completion: completion)
}
public func moveWebFeed(_ feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.moveWebFeed(for: self, with: feed, from: from, to: to, completion: completion)
public func moveFeed(_ feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.moveFeed(for: self, with: feed, from: from, to: to, completion: completion)
}
public func renameWebFeed(_ feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.renameWebFeed(for: self, with: feed, to: name, completion: completion)
public func renameFeed(_ feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.renameFeed(for: self, with: feed, to: name, completion: completion)
}
public func restoreWebFeed(_ feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.restoreWebFeed(for: self, feed: feed, container: container, completion: completion)
public func restoreFeed(_ feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
delegate.restoreFeed(for: self, feed: feed, container: container, completion: completion)
}
public func addFolder(_ name: String, completion: @escaping (Result<Folder, Error>) -> Void) {
@@ -649,8 +649,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
structureDidChange()
}
public func updateUnreadCounts(for webFeeds: Set<WebFeed>, completion: VoidCompletionBlock? = nil) {
fetchUnreadCounts(for: webFeeds, completion: completion)
public func updateUnreadCounts(for feeds: Set<WebFeed>, completion: VoidCompletionBlock? = nil) {
fetchUnreadCounts(for: feeds, completion: completion)
}
public func fetchUnreadArticlesBetween(limit: Int?, before: Date?, after: Date?) throws -> Set<Article> {
@@ -661,8 +661,9 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
return try fetchUnreadArticlesBetween(forContainer: folder, limit: limit, before: before, after: after)
}
public func fetchUnreadArticlesBetween(webFeeds: Set<WebFeed>, limit: Int?, before: Date?, after: Date?) throws -> Set<Article> {
return try fetchUnreadArticlesBetween(feeds: webFeeds, limit: limit, before: before, after: after)
public func fetchUnreadArticlesBetween(feeds: Set<WebFeed>, limit: Int?, before: Date?, after: Date?) throws -> Set<Article> {
let articles = try database.fetchUnreadArticlesBetween(feeds.webFeedIDs(), limit, before, after)
return articles
}
public func fetchArticlesBetween(articleIDs: Set<String>, before: Date?, after: Date?) throws -> Set<Article> {
@@ -684,7 +685,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
return try fetchArticles(folder: folder)
}
case .webFeed(let webFeed):
return try fetchArticles(webFeed: webFeed)
return try fetchArticles(feed: webFeed)
case .articleIDs(let articleIDs):
return try fetchArticles(articleIDs: articleIDs)
case .search(let searchString):
@@ -709,7 +710,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
return fetchArticlesAsync(folder: folder, completion)
}
case .webFeed(let webFeed):
fetchArticlesAsync(webFeed: webFeed, completion)
fetchArticlesAsync(feed: webFeed, completion)
case .articleIDs(let articleIDs):
fetchArticlesAsync(articleIDs: articleIDs, completion)
case .search(let searchString):
@@ -720,15 +721,15 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
}
public func fetchUnreadCountForToday(_ completion: @escaping SingleUnreadCountCompletionBlock) {
database.fetchUnreadCountForToday(for: flattenedWebFeeds().webFeedIDs(), completion: completion)
database.fetchUnreadCountForToday(for: flattenedFeeds().webFeedIDs(), completion: completion)
}
public func fetchUnreadCountForStarredArticles(_ completion: @escaping SingleUnreadCountCompletionBlock) {
database.fetchStarredAndUnreadCount(for: flattenedWebFeeds().webFeedIDs(), completion: completion)
database.fetchStarredAndUnreadCount(for: flattenedFeeds().webFeedIDs(), completion: completion)
}
public func fetchCountForStarredArticles() throws -> Int {
return try database.fetchStarredArticlesCount(flattenedWebFeeds().webFeedIDs())
return try database.fetchStarredArticlesCount(flattenedFeeds().webFeedIDs())
}
public func fetchUnreadArticleIDs(_ completion: @escaping ArticleIDsCompletionBlock) {
@@ -744,8 +745,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
database.fetchArticleIDsForStatusesWithoutArticlesNewerThanCutoffDate(completion)
}
public func unreadCount(for webFeed: WebFeed) -> Int {
return unreadCounts[webFeed.webFeedID] ?? 0
public func unreadCount(for feed: WebFeed) -> Int {
return unreadCounts[feed.webFeedID] ?? 0
}
public func setUnreadCount(_ unreadCount: Int, for webFeed: WebFeed) {
@@ -756,31 +757,31 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
// Feeds were added or deleted. Or folders added or deleted.
// Or feeds inside folders were added or deleted.
opmlFile.markAsDirty()
flattenedWebFeedsNeedUpdate = true
webFeedDictionariesNeedUpdate = true
flattenedFeedsNeedUpdate = true
feedDictionariesNeedUpdate = true
}
func update(_ webFeed: WebFeed, with parsedFeed: ParsedFeed, _ completion: @escaping UpdateArticlesCompletionBlock) {
func update(_ feed: WebFeed, with parsedFeed: ParsedFeed, _ completion: @escaping UpdateArticlesCompletionBlock) {
// Used only by an On My Mac or iCloud account.
precondition(Thread.isMainThread)
precondition(type == .onMyMac || type == .cloudKit)
webFeed.takeSettings(from: parsedFeed)
feed.takeSettings(from: parsedFeed)
let parsedItems = parsedFeed.items
guard !parsedItems.isEmpty else {
completion(.success(ArticleChanges()))
return
}
update(webFeed.webFeedID, with: parsedItems, completion: completion)
update(feed.webFeedID, with: parsedItems, completion: completion)
}
func update(_ webFeedID: String, with parsedItems: Set<ParsedItem>, deleteOlder: Bool = true, completion: @escaping UpdateArticlesCompletionBlock) {
func update(_ feedID: String, with parsedItems: Set<ParsedItem>, deleteOlder: Bool = true, completion: @escaping UpdateArticlesCompletionBlock) {
// Used only by an On My Mac or iCloud account.
precondition(Thread.isMainThread)
precondition(type == .onMyMac || type == .cloudKit)
database.update(with: parsedItems, feedID: webFeedID, deleteOlder: deleteOlder) { updateArticlesResult in
database.update(with: parsedItems, feedID: feedID, deleteOlder: deleteOlder) { updateArticlesResult in
switch updateArticlesResult {
case .success(let articleChanges):
self.sendNotificationAbout(articleChanges)
@@ -791,16 +792,16 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
}
}
func update(webFeedIDsAndItems: [String: Set<ParsedItem>], defaultRead: Bool, completion: @escaping DatabaseCompletionBlock) {
func update(feedIDsAndItems: [String: Set<ParsedItem>], defaultRead: Bool, completion: @escaping DatabaseCompletionBlock) {
// Used only by syncing systems.
precondition(Thread.isMainThread)
precondition(type != .onMyMac && type != .cloudKit)
guard !webFeedIDsAndItems.isEmpty else {
guard !feedIDsAndItems.isEmpty else {
completion(nil)
return
}
database.update(feedIDsAndItems: webFeedIDsAndItems, defaultRead: defaultRead) { updateArticlesResult in
database.update(feedIDsAndItems: feedIDsAndItems, defaultRead: defaultRead) { updateArticlesResult in
switch updateArticlesResult {
case .success(let newAndUpdatedArticles):
self.sendNotificationAbout(newAndUpdatedArticles)
@@ -906,38 +907,38 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
// MARK: - Container
public func flattenedWebFeeds() -> Set<WebFeed> {
public func flattenedFeeds() -> Set<WebFeed> {
assert(Thread.isMainThread)
if flattenedWebFeedsNeedUpdate {
updateFlattenedWebFeeds()
if flattenedFeedsNeedUpdate {
updateFlattenedFeeds()
}
return _flattenedWebFeeds
return _flattenedFeeds
}
public func removeWebFeed(_ webFeed: WebFeed) {
topLevelWebFeeds.remove(webFeed)
public func removeFeed(_ feed: WebFeed) {
topLevelFeeds.remove(feed)
structureDidChange()
postChildrenDidChangeNotification()
}
public func removeFeeds(_ webFeeds: Set<WebFeed>) {
guard !webFeeds.isEmpty else {
public func removeFeeds(_ feeds: Set<WebFeed>) {
guard !feeds.isEmpty else {
return
}
topLevelWebFeeds.subtract(webFeeds)
topLevelFeeds.subtract(feeds)
structureDidChange()
postChildrenDidChangeNotification()
}
public func addWebFeed(_ webFeed: WebFeed) {
topLevelWebFeeds.insert(webFeed)
public func addFeed(_ feed: WebFeed) {
topLevelFeeds.insert(feed)
structureDidChange()
postChildrenDidChangeNotification()
}
func addFeedIfNotInAnyFolder(_ webFeed: WebFeed) {
if !flattenedWebFeeds().contains(webFeed) {
addWebFeed(webFeed)
func addFeedIfNotInAnyFolder(_ feed: WebFeed) {
if !flattenedFeeds().contains(feed) {
addFeed(feed)
}
}
@@ -951,7 +952,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
public func debugDropConditionalGetInfo() {
#if DEBUG
flattenedWebFeeds().forEach{ $0.dropConditionalGetInfo() }
flattenedFeeds().forEach{ $0.dropConditionalGetInfo() }
#endif
}
@@ -983,8 +984,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
}
@objc func batchUpdateDidPerform(_ note: Notification) {
flattenedWebFeedsNeedUpdate = true
rebuildWebFeedDictionaries()
flattenedFeedsNeedUpdate = true
rebuildFeedDictionaries()
updateUnreadCount()
}
@@ -1034,7 +1035,7 @@ extension Account: FeedMetadataDelegate {
func valueDidChange(_ feedMetadata: FeedMetadata, key: FeedMetadata.CodingKeys) {
feedMetadataFile.markAsDirty()
guard let feed = existingWebFeed(withWebFeedID: feedMetadata.feedID) else {
guard let feed = existingFeed(withFeedID: feedMetadata.feedID) else {
return
}
feed.postFeedSettingDidChangeNotification(key)
@@ -1046,11 +1047,11 @@ extension Account: FeedMetadataDelegate {
private extension Account {
func fetchStarredArticles(limit: Int?) throws -> Set<Article> {
return try database.fetchStarredArticles(flattenedWebFeeds().webFeedIDs(), limit)
return try database.fetchStarredArticles(flattenedFeeds().webFeedIDs(), limit)
}
func fetchStarredArticlesAsync(limit: Int?, _ completion: @escaping ArticleSetResultBlock) {
database.fetchedStarredArticlesAsync(flattenedWebFeeds().webFeedIDs(), limit, completion)
database.fetchedStarredArticlesAsync(flattenedFeeds().webFeedIDs(), limit, completion)
}
func fetchUnreadArticles(limit: Int?) throws -> Set<Article> {
@@ -1062,11 +1063,11 @@ private extension Account {
}
func fetchTodayArticles(limit: Int?) throws -> Set<Article> {
return try database.fetchTodayArticles(flattenedWebFeeds().webFeedIDs(), limit)
return try database.fetchTodayArticles(flattenedFeeds().webFeedIDs(), limit)
}
func fetchTodayArticlesAsync(limit: Int?, _ completion: @escaping ArticleSetResultBlock) {
database.fetchTodayArticlesAsync(flattenedWebFeeds().webFeedIDs(), limit, completion)
database.fetchTodayArticlesAsync(flattenedFeeds().webFeedIDs(), limit, completion)
}
func fetchArticles(folder: Folder) throws -> Set<Article> {
@@ -1085,17 +1086,17 @@ private extension Account {
fetchUnreadArticlesAsync(forContainer: folder, limit: nil, completion)
}
func fetchArticles(webFeed: WebFeed) throws -> Set<Article> {
let articles = try database.fetchArticles(webFeed.webFeedID)
validateUnreadCount(webFeed, articles)
func fetchArticles(feed: WebFeed) throws -> Set<Article> {
let articles = try database.fetchArticles(feed.webFeedID)
validateUnreadCount(feed, articles)
return articles
}
func fetchArticlesAsync(webFeed: WebFeed, _ completion: @escaping ArticleSetResultBlock) {
database.fetchArticlesAsync(webFeed.webFeedID) { [weak self] articleSetResult in
func fetchArticlesAsync(feed: WebFeed, _ completion: @escaping ArticleSetResultBlock) {
database.fetchArticlesAsync(feed.webFeedID) { [weak self] articleSetResult in
switch articleSetResult {
case .success(let articles):
self?.validateUnreadCount(webFeed, articles)
self?.validateUnreadCount(feed, articles)
completion(.success(articles))
case .failure(let databaseError):
completion(.failure(databaseError))
@@ -1104,7 +1105,7 @@ private extension Account {
}
func fetchArticlesMatching(_ searchString: String) throws -> Set<Article> {
return try database.fetchArticlesMatching(searchString, flattenedWebFeeds().webFeedIDs())
return try database.fetchArticlesMatching(searchString, flattenedFeeds().webFeedIDs())
}
func fetchArticlesMatchingWithArticleIDs(_ searchString: String, _ articleIDs: Set<String>) throws -> Set<Article> {
@@ -1112,7 +1113,7 @@ private extension Account {
}
func fetchArticlesMatchingAsync(_ searchString: String, _ completion: @escaping ArticleSetResultBlock) {
database.fetchArticlesMatchingAsync(searchString, flattenedWebFeeds().webFeedIDs(), completion)
database.fetchArticlesMatchingAsync(searchString, flattenedFeeds().webFeedIDs(), completion)
}
func fetchArticlesMatchingWithArticleIDsAsync(_ searchString: String, _ articleIDs: Set<String>, _ completion: @escaping ArticleSetResultBlock) {
@@ -1127,25 +1128,25 @@ private extension Account {
return database.fetchArticlesAsync(articleIDs: articleIDs, completion)
}
func fetchUnreadArticles(webFeed: WebFeed) throws -> Set<Article> {
let articles = try database.fetchUnreadArticles(Set([webFeed.webFeedID]), nil)
validateUnreadCount(webFeed, articles)
func fetchUnreadArticles(feed: WebFeed) throws -> Set<Article> {
let articles = try database.fetchUnreadArticles(Set([feed.webFeedID]), nil)
validateUnreadCount(feed, articles)
return articles
}
func fetchArticles(forContainer container: Container) throws -> Set<Article> {
let feeds = container.flattenedWebFeeds()
let feeds = container.flattenedFeeds()
let articles = try database.fetchArticles(feeds.webFeedIDs())
validateUnreadCountsAfterFetchingUnreadArticles(feeds, articles)
return articles
}
func fetchArticlesAsync(forContainer container: Container, _ completion: @escaping ArticleSetResultBlock) {
let webFeeds = container.flattenedWebFeeds()
database.fetchArticlesAsync(webFeeds.webFeedIDs()) { [weak self] (articleSetResult) in
let feeds = container.flattenedFeeds()
database.fetchArticlesAsync(feeds.webFeedIDs()) { [weak self] (articleSetResult) in
switch articleSetResult {
case .success(let articles):
self?.validateUnreadCountsAfterFetchingUnreadArticles(webFeeds, articles)
self?.validateUnreadCountsAfterFetchingUnreadArticles(feeds, articles)
completion(.success(articles))
case .failure(let databaseError):
completion(.failure(databaseError))
@@ -1154,7 +1155,7 @@ private extension Account {
}
func fetchUnreadArticles(forContainer container: Container, limit: Int?) throws -> Set<Article> {
let feeds = container.flattenedWebFeeds()
let feeds = container.flattenedFeeds()
let articles = try database.fetchUnreadArticles(feeds.webFeedIDs(), limit)
// We don't validate limit queries because they, by definition, won't correctly match the
@@ -1167,26 +1168,21 @@ private extension Account {
}
func fetchUnreadArticlesBetween(forContainer container: Container, limit: Int?, before: Date?, after: Date?) throws -> Set<Article> {
let feeds = container.flattenedWebFeeds()
let articles = try database.fetchUnreadArticlesBetween(feeds.webFeedIDs(), limit, before, after)
return articles
}
func fetchUnreadArticlesBetween(feeds: Set<WebFeed>, limit: Int?, before: Date?, after: Date?) throws -> Set<Article> {
let feeds = container.flattenedFeeds()
let articles = try database.fetchUnreadArticlesBetween(feeds.webFeedIDs(), limit, before, after)
return articles
}
func fetchUnreadArticlesAsync(forContainer container: Container, limit: Int?, _ completion: @escaping ArticleSetResultBlock) {
let webFeeds = container.flattenedWebFeeds()
database.fetchUnreadArticlesAsync(webFeeds.webFeedIDs(), limit) { [weak self] (articleSetResult) in
let feeds = container.flattenedFeeds()
database.fetchUnreadArticlesAsync(feeds.webFeedIDs(), limit) { [weak self] (articleSetResult) in
switch articleSetResult {
case .success(let articles):
// We don't validate limit queries because they, by definition, won't correctly match the
// complete unread state for the given container.
if limit == nil {
self?.validateUnreadCountsAfterFetchingUnreadArticles(webFeeds, articles)
self?.validateUnreadCountsAfterFetchingUnreadArticles(feeds, articles)
}
completion(.success(articles))
@@ -1196,7 +1192,7 @@ private extension Account {
}
}
func validateUnreadCountsAfterFetchingUnreadArticles(_ webFeeds: Set<WebFeed>, _ articles: Set<Article>) {
func validateUnreadCountsAfterFetchingUnreadArticles(_ feeds: Set<WebFeed>, _ articles: Set<Article>) {
// Validate unread counts. This was the site of a performance slowdown:
// it was calling going through the entire list of articles once per feed:
// feeds.forEach { validateUnreadCount($0, articles) }
@@ -1206,24 +1202,24 @@ private extension Account {
for article in articles where !article.status.read {
unreadCountStorage[article.feedID, default: 0] += 1
}
webFeeds.forEach { (webFeed) in
let unreadCount = unreadCountStorage[webFeed.webFeedID, default: 0]
webFeed.unreadCount = unreadCount
feeds.forEach { (feed) in
let unreadCount = unreadCountStorage[feed.webFeedID, default: 0]
feed.unreadCount = unreadCount
}
}
func validateUnreadCount(_ webFeed: WebFeed, _ articles: Set<Article>) {
func validateUnreadCount(_ feed: WebFeed, _ articles: Set<Article>) {
// articles must contain all the unread articles for the feed.
// The unread number should match the feeds unread count.
let feedUnreadCount = articles.reduce(0) { (result, article) -> Int in
if article.feed == webFeed && !article.status.read {
if article.feed == feed && !article.status.read {
return result + 1
}
return result
}
webFeed.unreadCount = feedUnreadCount
feed.unreadCount = feedUnreadCount
}
}
@@ -1231,42 +1227,42 @@ private extension Account {
private extension Account {
func feedMetadata(feedURL: String, webFeedID: String) -> FeedMetadata {
func feedMetadata(feedURL: String, feedID: String) -> FeedMetadata {
if let d = feedMetadata[feedURL] {
assert(d.delegate === self)
return d
}
let d = FeedMetadata(webFeedID: webFeedID)
let d = FeedMetadata(feedID: feedID)
d.delegate = self
feedMetadata[feedURL] = d
return d
}
func updateFlattenedWebFeeds() {
func updateFlattenedFeeds() {
var feeds = Set<WebFeed>()
feeds.formUnion(topLevelWebFeeds)
feeds.formUnion(topLevelFeeds)
for folder in folders! {
feeds.formUnion(folder.flattenedWebFeeds())
feeds.formUnion(folder.flattenedFeeds())
}
_flattenedWebFeeds = feeds
flattenedWebFeedsNeedUpdate = false
_flattenedFeeds = feeds
flattenedFeedsNeedUpdate = false
}
func rebuildWebFeedDictionaries() {
func rebuildFeedDictionaries() {
var idDictionary = [String: WebFeed]()
var externalIDDictionary = [String: WebFeed]()
flattenedWebFeeds().forEach { (feed) in
flattenedFeeds().forEach { (feed) in
idDictionary[feed.webFeedID] = feed
if let externalID = feed.externalID {
externalIDDictionary[externalID] = feed
}
}
_idToWebFeedDictionary = idDictionary
_externalIDToWebFeedDictionary = externalIDDictionary
webFeedDictionariesNeedUpdate = false
_idToFeedDictionary = idDictionary
_externalIDToFeedDictionary = externalIDDictionary
feedDictionariesNeedUpdate = false
}
func updateUnreadCount() {
@@ -1274,7 +1270,7 @@ private extension Account {
return
}
var updatedUnreadCount = 0
for feed in flattenedWebFeeds() {
for feed in flattenedFeeds() {
updatedUnreadCount += feed.unreadCount
}
unreadCount = updatedUnreadCount
@@ -1331,8 +1327,8 @@ private extension Account {
}
func fetchUnreadCounts(_ feeds: Set<WebFeed>, _ completion: VoidCompletionBlock?) {
let webFeedIDs = Set(feeds.map { $0.webFeedID })
database.fetchUnreadCounts(for: webFeedIDs) { result in
let feedIDs = Set(feeds.map { $0.webFeedID })
database.fetchUnreadCounts(for: feedIDs) { result in
if let unreadCountDictionary = try? result.get() {
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: feeds)
}
@@ -1347,7 +1343,7 @@ private extension Account {
completion?()
return
}
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: self.flattenedWebFeeds())
self.processUnreadCounts(unreadCountDictionary: unreadCountDictionary, feeds: self.flattenedFeeds())
self.fetchingAllUnreadCounts = false
self.updateUnreadCount()
@@ -1369,13 +1365,13 @@ private extension Account {
}
func sendNotificationAbout(_ articleChanges: ArticleChanges) {
var webFeeds = Set<WebFeed>()
var feeds = Set<WebFeed>()
if let newArticles = articleChanges.newArticles {
webFeeds.formUnion(Set(newArticles.compactMap { $0.feed }))
feeds.formUnion(Set(newArticles.compactMap { $0.feed }))
}
if let updatedArticles = articleChanges.updatedArticles {
webFeeds.formUnion(Set(updatedArticles.compactMap { $0.feed }))
feeds.formUnion(Set(updatedArticles.compactMap { $0.feed }))
}
var shouldSendNotification = false
@@ -1398,11 +1394,11 @@ private extension Account {
}
if shouldUpdateUnreadCounts {
self.updateUnreadCounts(for: webFeeds)
self.updateUnreadCounts(for: feeds)
}
if shouldSendNotification {
userInfo[UserInfoKey.webFeeds] = webFeeds
userInfo[UserInfoKey.webFeeds] = feeds
NotificationCenter.default.post(name: .AccountDidDownloadArticles, object: self, userInfo: userInfo)
}
}
@@ -1412,12 +1408,12 @@ private extension Account {
extension Account {
public func existingWebFeed(withWebFeedID webFeedID: String) -> WebFeed? {
return idToWebFeedDictionary[webFeedID]
public func existingFeed(withFeedID feedID: String) -> WebFeed? {
return idToFeedDictionary[feedID]
}
public func existingWebFeed(withExternalID externalID: String) -> WebFeed? {
return externalIDToWebFeedDictionary[externalID]
public func existingFeed(withExternalID externalID: String) -> WebFeed? {
return externalIDToFeedDictionary[externalID]
}
}
@@ -1428,7 +1424,7 @@ extension Account: OPMLRepresentable {
public func OPMLString(indentLevel: Int, allowCustomAttributes: Bool) -> String {
var s = ""
for feed in topLevelWebFeeds.sorted() {
for feed in topLevelFeeds.sorted() {
s += feed.OPMLString(indentLevel: indentLevel + 1, allowCustomAttributes: allowCustomAttributes)
}
for folder in folders!.sorted() {

View File

@@ -36,13 +36,13 @@ protocol AccountDelegate {
func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void)
func createWebFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void)
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
func addWebFeed(for account: Account, with: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void)
func removeWebFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void)
func moveWebFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void)
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void)
func renameFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
func addFeed(for account: Account, with: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void)
func removeFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void)
func moveFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void)
func restoreWebFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void)
func restoreFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void)
func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result<Void, Error>) -> Void)
func markArticles(for account: Account, articles: Set<Article>, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping (Result<Void, Error>) -> Void)

View File

@@ -208,7 +208,7 @@ public final class AccountManager: UnreadCountProvider {
}
case .feed(let accountID, let webFeedID):
if let account = existingAccount(with: accountID) {
return account.existingWebFeed(withWebFeedID: webFeedID)
return account.existingFeed(withFeedID: webFeedID)
}
default:
break
@@ -307,7 +307,7 @@ public final class AccountManager: UnreadCountProvider {
public func anyAccountHasAtLeastOneFeed() -> Bool {
for account in activeAccounts {
if account.hasAtLeastOneWebFeed() {
if account.hasAtLeastOneFeed() {
return true
}
}
@@ -321,7 +321,7 @@ public final class AccountManager: UnreadCountProvider {
public func anyAccountHasFeedWithURL(_ urlString: String) -> Bool {
for account in activeAccounts {
if let _ = account.existingWebFeed(withURL: urlString) {
if let _ = account.existingFeed(withURL: urlString) {
return true
}
}
@@ -335,7 +335,7 @@ public final class AccountManager: UnreadCountProvider {
for account in accounts {
if account.type == .cloudKit || account.type == .onMyMac {
for webfeed in account.flattenedWebFeeds() {
for webfeed in account.flattenedFeeds() {
if let components = URLComponents(string: webfeed.url), let host = components.host {
if host == "twitter.com" { // Allow, for instance, blog.twitter.com, which might have an actual RSS feed
return true
@@ -355,7 +355,7 @@ public final class AccountManager: UnreadCountProvider {
for account in accounts {
if account.type == .cloudKit || account.type == .onMyMac {
for webfeed in account.flattenedWebFeeds() {
for webfeed in account.flattenedFeeds() {
if feedRequiresRedditAPI(webfeed) {
return true
}

View File

@@ -39,7 +39,7 @@ extension WebFeed: ArticleFetcher {
}
public func fetchUnreadArticlesBetween(before: Date? = nil, after: Date? = nil) throws -> Set<Article> {
return try account?.fetchUnreadArticlesBetween(webFeeds: [self], limit: nil, before: before, after: after) ?? Set<Article>()
return try account?.fetchUnreadArticlesBetween(feeds: [self], limit: nil, before: before, after: after) ?? Set<Article>()
}
public func fetchUnreadArticlesAsync(_ completion: @escaping ArticleSetResultBlock) {

View File

@@ -172,7 +172,7 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
}
func createWebFeed(for account: Account, url urlString: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
func createFeed(for account: Account, url urlString: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
guard let url = URL(string: urlString) else {
completion(.failure(LocalAccountDelegateError.invalidParameter))
return
@@ -181,13 +181,13 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
let editedName = name == nil || name!.isEmpty ? nil : name
// Username should be part of the URL on new feed adds
createRSSWebFeed(for: account, url: url, editedName: editedName, container: container, validateFeed: validateFeed, completion: completion)
createRSSFeed(for: account, url: url, editedName: editedName, container: container, validateFeed: validateFeed, completion: completion)
}
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
func renameFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
let editedName = name.isEmpty ? nil : name
refreshProgress.addToNumberOfTasksAndRemaining(1)
accountZone.renameWebFeed(feed, editedName: editedName) { result in
accountZone.renameFeed(feed, editedName: editedName) { result in
self.refreshProgress.completeTask()
switch result {
case .success:
@@ -200,19 +200,19 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
}
}
func removeWebFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
removeWebFeedFromCloud(for: account, with: feed, from: container) { result in
func removeFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
removeFeedFromCloud(for: account, with: feed, from: container) { result in
switch result {
case .success:
account.clearFeedMetadata(feed)
container.removeWebFeed(feed)
container.removeFeed(feed)
completion(.success(()))
case .failure(let error):
switch error {
case CloudKitZoneError.corruptAccount:
// We got into a bad state and should remove the feed to clear up the bad data
account.clearFeedMetadata(feed)
container.removeWebFeed(feed)
container.removeFeed(feed)
default:
completion(.failure(error))
}
@@ -220,14 +220,14 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
}
}
func moveWebFeed(for account: Account, with feed: WebFeed, from fromContainer: Container, to toContainer: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func moveFeed(for account: Account, with feed: WebFeed, from fromContainer: Container, to toContainer: Container, completion: @escaping (Result<Void, Error>) -> Void) {
refreshProgress.addToNumberOfTasksAndRemaining(1)
accountZone.moveWebFeed(feed, from: fromContainer, to: toContainer) { result in
accountZone.moveFeed(feed, from: fromContainer, to: toContainer) { result in
self.refreshProgress.completeTask()
switch result {
case .success:
fromContainer.removeWebFeed(feed)
toContainer.addWebFeed(feed)
fromContainer.removeFeed(feed)
toContainer.addFeed(feed)
completion(.success(()))
case .failure(let error):
self.processAccountError(account, error)
@@ -236,13 +236,13 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
}
}
func addWebFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func addFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
refreshProgress.addToNumberOfTasksAndRemaining(1)
accountZone.addWebFeed(feed, to: container) { result in
accountZone.addFeed(feed, to: container) { result in
self.refreshProgress.completeTask()
switch result {
case .success:
container.addWebFeed(feed)
container.addFeed(feed)
completion(.success(()))
case .failure(let error):
self.processAccountError(account, error)
@@ -251,8 +251,8 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
}
}
func restoreWebFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
createWebFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in
func restoreFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
createFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in
switch result {
case .success:
completion(.success(()))
@@ -304,13 +304,13 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
switch result {
case .success(let webFeedExternalIDs):
let webFeeds = webFeedExternalIDs.compactMap { account.existingWebFeed(withExternalID: $0) }
let webFeeds = webFeedExternalIDs.compactMap { account.existingFeed(withExternalID: $0) }
let group = DispatchGroup()
var errorOccurred = false
for webFeed in webFeeds {
group.enter()
self.removeWebFeedFromCloud(for: account, with: webFeed, from: folder) { [weak self] result in
self.removeFeedFromCloud(for: account, with: webFeed, from: folder) { [weak self] result in
group.leave()
if case .failure(let error) = result {
self?.logger.error("Remove folder, remove webfeed error: \(error.localizedDescription, privacy: .public)")
@@ -356,7 +356,7 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
return
}
let feedsToRestore = folder.topLevelWebFeeds
let feedsToRestore = folder.topLevelFeeds
refreshProgress.addToNumberOfTasksAndRemaining(1 + feedsToRestore.count)
accountZone.createFolder(name: name) { result in
@@ -369,10 +369,10 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
let group = DispatchGroup()
for feed in feedsToRestore {
folder.topLevelWebFeeds.remove(feed)
folder.topLevelFeeds.remove(feed)
group.enter()
self.restoreWebFeed(for: account, feed: feed, container: folder) { [weak self] result in
self.restoreFeed(for: account, feed: feed, container: folder) { [weak self] result in
self?.refreshProgress.completeTask()
group.leave()
switch result {
@@ -485,7 +485,7 @@ private extension CloudKitAccountDelegate {
accountZone.fetchChangesInZone() { result in
self.refreshProgress.completeTask()
let webFeeds = account.flattenedWebFeeds()
let webFeeds = account.flattenedFeeds()
self.refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
switch result {
@@ -519,7 +519,7 @@ private extension CloudKitAccountDelegate {
func standardRefreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
let intialWebFeedsCount = account.flattenedWebFeeds().count
let intialWebFeedsCount = account.flattenedFeeds().count
refreshProgress.isIndeterminate = true
refreshProgress.addToNumberOfTasksAndRemaining(3 + intialWebFeedsCount)
@@ -534,7 +534,7 @@ private extension CloudKitAccountDelegate {
case .success:
self.refreshProgress.completeTask()
let webFeeds = account.flattenedWebFeeds()
let webFeeds = account.flattenedFeeds()
self.refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count - intialWebFeedsCount)
self.refreshArticleStatus(for: account) { result in
@@ -579,13 +579,13 @@ private extension CloudKitAccountDelegate {
}
}
func createRSSWebFeed(for account: Account, url: URL, editedName: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
func createRSSFeed(for account: Account, url: URL, editedName: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
func addDeadFeed() {
let feed = account.createWebFeed(with: editedName, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
container.addWebFeed(feed)
let feed = account.createFeed(with: editedName, url: url.absoluteString, feedID: url.absoluteString, homePageURL: nil)
container.addFeed(feed)
self.accountZone.createWebFeed(url: url.absoluteString,
self.accountZone.createFeed(url: url.absoluteString,
name: editedName,
editedName: nil,
homePageURL: nil,
@@ -597,7 +597,7 @@ private extension CloudKitAccountDelegate {
feed.externalID = externalID
completion(.success(feed))
case .failure(let error):
container.removeWebFeed(feed)
container.removeFeed(feed)
completion(.failure(error))
}
}
@@ -620,15 +620,15 @@ private extension CloudKitAccountDelegate {
return
}
if account.hasWebFeed(withURL: bestFeedSpecifier.urlString) {
if account.hasFeed(withURL: bestFeedSpecifier.urlString) {
self.refreshProgress.completeTasks(4)
completion(.failure(AccountError.createErrorAlreadySubscribed))
return
}
let feed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
let feed = account.createFeed(with: nil, url: url.absoluteString, feedID: url.absoluteString, homePageURL: nil)
feed.editedName = editedName
container.addWebFeed(feed)
container.addFeed(feed)
InitialFeedDownloader.download(url) { parsedFeed in
self.refreshProgress.completeTask()
@@ -638,7 +638,7 @@ private extension CloudKitAccountDelegate {
switch result {
case .success:
self.accountZone.createWebFeed(url: bestFeedSpecifier.urlString,
self.accountZone.createFeed(url: bestFeedSpecifier.urlString,
name: parsedFeed.title,
editedName: editedName,
homePageURL: parsedFeed.homePageURL,
@@ -651,7 +651,7 @@ private extension CloudKitAccountDelegate {
self.sendNewArticlesToTheCloud(account, feed)
completion(.success(feed))
case .failure(let error):
container.removeWebFeed(feed)
container.removeFeed(feed)
self.refreshProgress.completeTasks(2)
completion(.failure(error))
}
@@ -659,7 +659,7 @@ private extension CloudKitAccountDelegate {
}
case .failure(let error):
container.removeWebFeed(feed)
container.removeFeed(feed)
self.refreshProgress.completeTasks(3)
completion(.failure(error))
}
@@ -667,7 +667,7 @@ private extension CloudKitAccountDelegate {
}
} else {
self.refreshProgress.completeTasks(3)
container.removeWebFeed(feed)
container.removeFeed(feed)
completion(.failure(AccountError.createErrorNotFound))
}
@@ -709,7 +709,7 @@ private extension CloudKitAccountDelegate {
func processAccountError(_ account: Account, _ error: Error) {
if case CloudKitZoneError.userDeletedZone = error {
account.removeFeeds(account.topLevelWebFeeds)
account.removeFeeds(account.topLevelFeeds)
for folder in account.folders ?? Set<Folder>() {
account.removeFolder(folder)
}
@@ -774,9 +774,9 @@ private extension CloudKitAccountDelegate {
}
func removeWebFeedFromCloud(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func removeFeedFromCloud(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
refreshProgress.addToNumberOfTasksAndRemaining(2)
accountZone.removeWebFeed(feed, from: container) { result in
accountZone.removeFeed(feed, from: container) { result in
self.refreshProgress.completeTask()
switch result {
case .success:

View File

@@ -88,7 +88,7 @@ final class CloudKitAccountZone: CloudKitZone {
}
/// Persist a web feed record to iCloud and return the external key
func createWebFeed(url: String, name: String?, editedName: String?, homePageURL: String?, container: Container, completion: @escaping (Result<String, Error>) -> Void) {
func createFeed(url: String, name: String?, editedName: String?, homePageURL: String?, container: Container, completion: @escaping (Result<String, Error>) -> Void) {
let recordID = CKRecord.ID(recordName: url.md5String, zoneID: zoneID)
let record = CKRecord(recordType: CloudKitWebFeed.recordType, recordID: recordID)
record[CloudKitWebFeed.Fields.url] = url
@@ -117,7 +117,7 @@ final class CloudKitAccountZone: CloudKitZone {
}
/// Rename the given web feed
func renameWebFeed(_ webFeed: WebFeed, editedName: String?, completion: @escaping (Result<Void, Error>) -> Void) {
func renameFeed(_ webFeed: WebFeed, editedName: String?, completion: @escaping (Result<Void, Error>) -> Void) {
guard let externalID = webFeed.externalID else {
completion(.failure(CloudKitZoneError.corruptAccount))
return
@@ -138,7 +138,7 @@ final class CloudKitAccountZone: CloudKitZone {
}
/// Removes a web feed from a container and optionally deletes it, calling the completion with true if deleted
func removeWebFeed(_ webFeed: WebFeed, from: Container, completion: @escaping (Result<Bool, Error>) -> Void) {
func removeFeed(_ webFeed: WebFeed, from: Container, completion: @escaping (Result<Bool, Error>) -> Void) {
guard let fromContainerExternalID = from.externalID else {
completion(.failure(CloudKitZoneError.corruptAccount))
return
@@ -187,7 +187,7 @@ final class CloudKitAccountZone: CloudKitZone {
}
}
func moveWebFeed(_ webFeed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func moveFeed(_ webFeed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
guard let fromContainerExternalID = from.externalID, let toContainerExternalID = to.externalID else {
completion(.failure(CloudKitZoneError.corruptAccount))
return
@@ -209,7 +209,7 @@ final class CloudKitAccountZone: CloudKitZone {
}
}
func addWebFeed(_ webFeed: WebFeed, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func addFeed(_ webFeed: WebFeed, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
guard let toContainerExternalID = to.externalID else {
completion(.failure(CloudKitZoneError.corruptAccount))
return

View File

@@ -35,7 +35,7 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
for deletedRecordKey in deleted {
switch deletedRecordKey.recordType {
case CloudKitAccountZone.CloudKitWebFeed.recordType:
removeWebFeed(deletedRecordKey.recordID.externalID)
removeFeed(deletedRecordKey.recordID.externalID)
case CloudKitAccountZone.CloudKitContainer.recordType:
removeContainer(deletedRecordKey.recordID.externalID)
default:
@@ -69,12 +69,12 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
let editedName = record[CloudKitAccountZone.CloudKitWebFeed.Fields.editedName] as? String
let homePageURL = record[CloudKitAccountZone.CloudKitWebFeed.Fields.homePageURL] as? String
if let webFeed = account.existingWebFeed(withExternalID: record.externalID) {
if let webFeed = account.existingFeed(withExternalID: record.externalID) {
updateWebFeed(webFeed, name: name, editedName: editedName, homePageURL: homePageURL, containerExternalIDs: containerExternalIDs)
} else {
for containerExternalID in containerExternalIDs {
if let container = account.existingContainer(withExternalID: containerExternalID) {
createWebFeedIfNecessary(url: url, name: name, editedName: editedName, homePageURL: homePageURL, webFeedExternalID: record.externalID, container: container)
createFeedIfNecessary(url: url, name: name, editedName: editedName, homePageURL: homePageURL, webFeedExternalID: record.externalID, container: container)
} else {
addNewUnclaimedWebFeed(url: url, name: name, editedName: editedName, homePageURL: homePageURL, webFeedExternalID: record.externalID, containerExternalID: containerExternalID)
}
@@ -82,11 +82,11 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
}
}
func removeWebFeed(_ externalID: String) {
if let webFeed = account?.existingWebFeed(withExternalID: externalID), let containers = account?.existingContainers(withWebFeed: webFeed) {
func removeFeed(_ externalID: String) {
if let webFeed = account?.existingFeed(withExternalID: externalID), let containers = account?.existingContainers(withFeed: webFeed) {
containers.forEach {
webFeed.dropConditionalGetInfo()
$0.removeWebFeed(webFeed)
$0.removeFeed(webFeed)
}
}
}
@@ -111,7 +111,7 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
if let newUnclaimedWebFeeds = newUnclaimedWebFeeds[containerExternalID] {
for newUnclaimedWebFeed in newUnclaimedWebFeeds {
createWebFeedIfNecessary(url: newUnclaimedWebFeed.url,
createFeedIfNecessary(url: newUnclaimedWebFeed.url,
name: newUnclaimedWebFeed.name,
editedName: newUnclaimedWebFeed.editedName,
homePageURL: newUnclaimedWebFeed.homePageURL,
@@ -124,7 +124,7 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
if let existingUnclaimedWebFeeds = existingUnclaimedWebFeeds[containerExternalID] {
for existingUnclaimedWebFeed in existingUnclaimedWebFeeds {
container.addWebFeed(existingUnclaimedWebFeed)
container.addFeed(existingUnclaimedWebFeed)
}
self.existingUnclaimedWebFeeds.removeValue(forKey: containerExternalID)
}
@@ -147,7 +147,7 @@ private extension CloudKitAcountZoneDelegate {
webFeed.editedName = editedName
webFeed.homePageURL = homePageURL
let existingContainers = account.existingContainers(withWebFeed: webFeed)
let existingContainers = account.existingContainers(withFeed: webFeed)
let existingContainerExternalIds = existingContainers.compactMap { $0.externalID }
let diff = containerExternalIDs.difference(from: existingContainerExternalIds)
@@ -156,11 +156,11 @@ private extension CloudKitAcountZoneDelegate {
switch change {
case .remove(_, let externalID, _):
if let container = existingContainers.first(where: { $0.externalID == externalID }) {
container.removeWebFeed(webFeed)
container.removeFeed(webFeed)
}
case .insert(_, let externalID, _):
if let container = account.existingContainer(withExternalID: externalID) {
container.addWebFeed(webFeed)
container.addFeed(webFeed)
} else {
addExistingUnclaimedWebFeed(webFeed, containerExternalID: externalID)
}
@@ -168,17 +168,17 @@ private extension CloudKitAcountZoneDelegate {
}
}
func createWebFeedIfNecessary(url: URL, name: String?, editedName: String?, homePageURL: String?, webFeedExternalID: String, container: Container) {
func createFeedIfNecessary(url: URL, name: String?, editedName: String?, homePageURL: String?, webFeedExternalID: String, container: Container) {
guard let account = account else { return }
if account.existingWebFeed(withExternalID: webFeedExternalID) != nil {
if account.existingFeed(withExternalID: webFeedExternalID) != nil {
return
}
let webFeed = account.createWebFeed(with: name, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: homePageURL)
let webFeed = account.createFeed(with: name, url: url.absoluteString, feedID: url.absoluteString, homePageURL: homePageURL)
webFeed.editedName = editedName
webFeed.externalID = webFeedExternalID
container.addWebFeed(webFeed)
container.addFeed(webFeed)
}
func addNewUnclaimedWebFeed(url: URL, name: String?, editedName: String?, homePageURL: String?, webFeedExternalID: String, containerExternalID: String) {

View File

@@ -144,10 +144,10 @@ private extension CloudKitArticlesZoneDelegate {
group.enter()
compressionQueue.async {
let parsedItems = records.compactMap { self.makeParsedItem($0) }
let webFeedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
DispatchQueue.main.async {
for (webFeedID, parsedItems) in webFeedIDsAndItems {
for (webFeedID, parsedItems) in feedIDsAndItems {
group.enter()
self.account?.update(webFeedID, with: parsedItems, deleteOlder: false) { result in
switch result {

View File

@@ -169,7 +169,7 @@ private extension CloudKitSendStatusOperation {
func processAccountError(_ account: Account, _ error: Error) {
if case CloudKitZoneError.userDeletedZone = error {
account.removeFeeds(account.topLevelWebFeeds)
account.removeFeeds(account.topLevelFeeds)
for folder in account.folders ?? Set<Folder>() {
account.removeFolder(folder)
}

View File

@@ -19,27 +19,27 @@ extension Notification.Name {
public protocol Container: AnyObject, ContainerIdentifiable {
var account: Account? { get }
var topLevelWebFeeds: Set<WebFeed> { get set }
var topLevelFeeds: Set<WebFeed> { get set }
var folders: Set<Folder>? { get set }
var externalID: String? { get set }
func hasAtLeastOneWebFeed() -> Bool
func hasAtLeastOneFeed() -> Bool
func objectIsChild(_ object: AnyObject) -> Bool
func hasChildFolder(with: String) -> Bool
func childFolder(with: String) -> Folder?
func removeWebFeed(_ webFeed: WebFeed)
func addWebFeed(_ webFeed: WebFeed)
func removeFeed(_ webFeed: WebFeed)
func addFeed(_ webFeed: WebFeed)
//Recursive  checks subfolders
func flattenedWebFeeds() -> Set<WebFeed>
func flattenedFeeds() -> Set<WebFeed>
func has(_ webFeed: WebFeed) -> Bool
func hasWebFeed(with webFeedID: String) -> Bool
func hasWebFeed(withURL url: String) -> Bool
func existingWebFeed(withWebFeedID: String) -> WebFeed?
func existingWebFeed(withURL url: String) -> WebFeed?
func existingWebFeed(withExternalID externalID: String) -> WebFeed?
func hasFeed(with feedID: String) -> Bool
func hasFeed(withURL url: String) -> Bool
func existingFeed(withFeedID: String) -> WebFeed?
func existingFeed(withURL url: String) -> WebFeed?
func existingFeed(withExternalID externalID: String) -> WebFeed?
func existingFolder(with name: String) -> Folder?
func existingFolder(withID: Int) -> Folder?
@@ -48,8 +48,8 @@ public protocol Container: AnyObject, ContainerIdentifiable {
public extension Container {
func hasAtLeastOneWebFeed() -> Bool {
return topLevelWebFeeds.count > 0
func hasAtLeastOneFeed() -> Bool {
return topLevelFeeds.count > 0
}
func hasChildFolder(with name: String) -> Bool {
@@ -70,7 +70,7 @@ public extension Container {
func objectIsChild(_ object: AnyObject) -> Bool {
if let feed = object as? WebFeed {
return topLevelWebFeeds.contains(feed)
return topLevelFeeds.contains(feed)
}
if let folder = object as? Folder {
return folders?.contains(folder) ?? false
@@ -78,40 +78,40 @@ public extension Container {
return false
}
func flattenedWebFeeds() -> Set<WebFeed> {
func flattenedFeeds() -> Set<WebFeed> {
var feeds = Set<WebFeed>()
feeds.formUnion(topLevelWebFeeds)
feeds.formUnion(topLevelFeeds)
if let folders = folders {
for folder in folders {
feeds.formUnion(folder.flattenedWebFeeds())
feeds.formUnion(folder.flattenedFeeds())
}
}
return feeds
}
func hasWebFeed(with webFeedID: String) -> Bool {
return existingWebFeed(withWebFeedID: webFeedID) != nil
func hasFeed(with feedID: String) -> Bool {
return existingFeed(withFeedID: feedID) != nil
}
func hasWebFeed(withURL url: String) -> Bool {
return existingWebFeed(withURL: url) != nil
func hasFeed(withURL url: String) -> Bool {
return existingFeed(withURL: url) != nil
}
func has(_ webFeed: WebFeed) -> Bool {
return flattenedWebFeeds().contains(webFeed)
func has(_ feed: WebFeed) -> Bool {
return flattenedFeeds().contains(feed)
}
func existingWebFeed(withWebFeedID webFeedID: String) -> WebFeed? {
for feed in flattenedWebFeeds() {
if feed.webFeedID == webFeedID {
func existingFeed(withFeedID feedID: String) -> WebFeed? {
for feed in flattenedFeeds() {
if feed.webFeedID == feedID {
return feed
}
}
return nil
}
func existingWebFeed(withURL url: String) -> WebFeed? {
for feed in flattenedWebFeeds() {
func existingFeed(withURL url: String) -> WebFeed? {
for feed in flattenedFeeds() {
if feed.url == url {
return feed
}
@@ -119,8 +119,8 @@ public extension Container {
return nil
}
func existingWebFeed(withExternalID externalID: String) -> WebFeed? {
for feed in flattenedWebFeeds() {
func existingFeed(withExternalID externalID: String) -> WebFeed? {
for feed in flattenedFeeds() {
if feed.externalID == externalID {
return feed
}

View File

@@ -57,7 +57,7 @@ public extension Article {
}
var feed: WebFeed? {
return account?.existingWebFeed(withWebFeedID: feedID)
return account?.existingFeed(withFeedID: feedID)
}
}

View File

@@ -148,8 +148,8 @@ final class FeedMetadata: Codable {
weak var delegate: FeedMetadataDelegate?
init(webFeedID: String) {
self.feedID = webFeedID
init(feedID: String) {
self.feedID = feedID
}
func valueDidChange(_ key: CodingKeys) {

View File

@@ -70,7 +70,7 @@ private extension FeedMetadataFile {
}
private func metadataForOnlySubscribedToFeeds() -> Account.FeedMetadataDictionary {
let feedIDs = account.idToWebFeedDictionary.keys
let feedIDs = account.idToFeedDictionary.keys
return account.feedMetadata.filter { (feedID: String, metadata: FeedMetadata) -> Bool in
return feedIDs.contains(metadata.feedID)
}

View File

@@ -298,7 +298,7 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
guard folder.hasAtLeastOneWebFeed() else {
guard folder.hasAtLeastOneFeed() else {
folder.name = name
return
}
@@ -326,7 +326,7 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
// Feedbin uses tags and if at least one feed isn't tagged, then the folder doesn't exist on their system
guard folder.hasAtLeastOneWebFeed() else {
guard folder.hasAtLeastOneFeed() else {
account.removeFolder(folder)
completion(.success(()))
return
@@ -334,7 +334,7 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
let group = DispatchGroup()
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
if feed.folderRelationship?.count ?? 0 > 1 {
@@ -386,7 +386,7 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
}
func createWebFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.createSubscription(url: url) { result in
@@ -418,7 +418,7 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
}
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
func renameFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
// This error should never happen
guard let subscriptionID = feed.externalID else {
@@ -445,7 +445,7 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
}
func removeWebFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func removeFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
if feed.folderRelationship?.count ?? 0 > 1 {
deleteTagging(for: account, with: feed, from: container, completion: completion)
} else {
@@ -453,14 +453,14 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
}
}
func moveWebFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func moveFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
if from is Account {
addWebFeed(for: account, with: feed, to: to, completion: completion)
addFeed(for: account, with: feed, to: to, completion: completion)
} else {
deleteTagging(for: account, with: feed, from: from) { result in
switch result {
case .success:
self.addWebFeed(for: account, with: feed, to: to, completion: completion)
self.addFeed(for: account, with: feed, to: to, completion: completion)
case .failure(let error):
completion(.failure(error))
}
@@ -468,7 +468,7 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
}
}
func addWebFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func addFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
if let folder = container as? Folder, let webFeedID = Int(feed.webFeedID) {
refreshProgress.addToNumberOfTasksAndRemaining(1)
@@ -478,8 +478,8 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
case .success(let taggingID):
DispatchQueue.main.async {
self.saveFolderRelationship(for: feed, withFolderName: folder.name ?? "", id: String(taggingID))
account.removeWebFeed(feed)
folder.addWebFeed(feed)
account.removeFeed(feed)
folder.addFeed(feed)
completion(.success(()))
}
case .failure(let error):
@@ -500,10 +500,10 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
}
func restoreWebFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func restoreFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
if let existingFeed = account.existingWebFeed(withURL: feed.url) {
account.addWebFeed(existingFeed, to: container) { result in
if let existingFeed = account.existingFeed(withURL: feed.url) {
account.addFeed(existingFeed, to: container) { result in
switch result {
case .success:
completion(.success(()))
@@ -512,7 +512,7 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
}
}
} else {
createWebFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in
createFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in
switch result {
case .success:
completion(.success(()))
@@ -528,12 +528,12 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
let group = DispatchGroup()
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
folder.topLevelWebFeeds.remove(feed)
folder.topLevelFeeds.remove(feed)
group.enter()
restoreWebFeed(for: account, feed: feed, container: folder) { result in
restoreFeed(for: account, feed: feed, container: folder) { result in
group.leave()
switch result {
case .success:
@@ -779,8 +779,8 @@ private extension FeedbinAccountDelegate {
if let folders = account.folders {
folders.forEach { folder in
if !tagNames.contains(folder.name ?? "") {
for feed in folder.topLevelWebFeeds {
account.addWebFeed(feed)
for feed in folder.topLevelFeeds {
account.addFeed(feed)
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
}
account.removeFolder(folder)
@@ -817,17 +817,17 @@ private extension FeedbinAccountDelegate {
// Remove any feeds that are no longer in the subscriptions
if let folders = account.folders {
for folder in folders {
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
if !subFeedIds.contains(feed.webFeedID) {
folder.removeWebFeed(feed)
folder.removeFeed(feed)
}
}
}
}
for feed in account.topLevelWebFeeds {
for feed in account.topLevelFeeds {
if !subFeedIds.contains(feed.webFeedID) {
account.removeWebFeed(feed)
account.removeFeed(feed)
}
}
@@ -837,7 +837,7 @@ private extension FeedbinAccountDelegate {
let subFeedId = String(subscription.feedID)
if let feed = account.existingWebFeed(withWebFeedID: subFeedId) {
if let feed = account.existingFeed(withFeedID: subFeedId) {
feed.name = subscription.name
// If the name has been changed on the server remove the locally edited name
feed.editedName = nil
@@ -853,9 +853,9 @@ private extension FeedbinAccountDelegate {
// Actually add subscriptions all in one go, so we dont trigger various rebuilding things that Account does.
subscriptionsToAdd.forEach { subscription in
let feed = account.createWebFeed(with: subscription.name, url: subscription.url, webFeedID: String(subscription.feedID), homePageURL: subscription.homePageURL)
let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: String(subscription.feedID), homePageURL: subscription.homePageURL)
feed.externalID = String(subscription.subscriptionID)
account.addWebFeed(feed)
account.addFeed(feed)
}
}
@@ -887,25 +887,25 @@ private extension FeedbinAccountDelegate {
let taggingFeedIDs = groupedTaggings.map { String($0.feedID) }
// Move any feeds not in the folder to the account
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
if !taggingFeedIDs.contains(feed.webFeedID) {
folder.removeWebFeed(feed)
folder.removeFeed(feed)
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
account.addWebFeed(feed)
account.addFeed(feed)
}
}
// Add any feeds not in the folder
let folderFeedIds = folder.topLevelWebFeeds.map { $0.webFeedID }
let folderFeedIds = folder.topLevelFeeds.map { $0.webFeedID }
for tagging in groupedTaggings {
let taggingFeedID = String(tagging.feedID)
if !folderFeedIds.contains(taggingFeedID) {
guard let feed = account.existingWebFeed(withWebFeedID: taggingFeedID) else {
guard let feed = account.existingFeed(withFeedID: taggingFeedID) else {
continue
}
saveFolderRelationship(for: feed, withFolderName: folderName, id: String(tagging.taggingID))
folder.addWebFeed(feed)
folder.addFeed(feed)
}
}
@@ -914,9 +914,9 @@ private extension FeedbinAccountDelegate {
let taggedFeedIDs = Set(taggings.map { String($0.feedID) })
// Remove all feeds from the account container that have a tag
for feed in account.topLevelWebFeeds {
for feed in account.topLevelFeeds {
if taggedFeedIDs.contains(feed.webFeedID) {
account.removeWebFeed(feed)
account.removeFeed(feed)
}
}
}
@@ -979,7 +979,7 @@ private extension FeedbinAccountDelegate {
}
func renameFolderRelationship(for account: Account, fromName: String, toName: String) {
for feed in account.flattenedWebFeeds() {
for feed in account.flattenedFeeds() {
if var folderRelationship = feed.folderRelationship {
let relationship = folderRelationship[fromName]
folderRelationship[fromName] = nil
@@ -1016,7 +1016,7 @@ private extension FeedbinAccountDelegate {
}
if let bestSpecifier = FeedSpecifier.bestFeed(in: Set(feedSpecifiers)) {
createWebFeed(for: account, url: bestSpecifier.urlString, name: name, container: container, validateFeed: true, completion: completion)
createFeed(for: account, url: bestSpecifier.urlString, name: name, container: container, validateFeed: true, completion: completion)
} else {
DispatchQueue.main.async {
completion(.failure(FeedbinAccountDelegateError.invalidParameter))
@@ -1028,16 +1028,16 @@ private extension FeedbinAccountDelegate {
DispatchQueue.main.async {
let feed = account.createWebFeed(with: sub.name, url: sub.url, webFeedID: String(sub.feedID), homePageURL: sub.homePageURL)
let feed = account.createFeed(with: sub.name, url: sub.url, feedID: String(sub.feedID), homePageURL: sub.homePageURL)
feed.externalID = String(sub.subscriptionID)
feed.iconURL = sub.jsonFeed?.icon
feed.faviconURL = sub.jsonFeed?.favicon
account.addWebFeed(feed, to: container) { result in
account.addFeed(feed, to: container) { result in
switch result {
case .success:
if let name = name {
account.renameWebFeed(feed, to: name) { result in
account.renameFeed(feed, to: name) { result in
switch result {
case .success:
self.initialFeedDownload(account: account, feed: feed, completion: completion)
@@ -1247,8 +1247,8 @@ private extension FeedbinAccountDelegate {
func processEntries(account: Account, entries: [FeedbinEntry]?, completion: @escaping DatabaseCompletionBlock) {
let parsedItems = mapEntriesToParsedItems(entries: entries)
let webFeedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
account.update(webFeedIDsAndItems: webFeedIDsAndItems, defaultRead: true, completion: completion)
let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true, completion: completion)
}
func mapEntriesToParsedItems(entries: [FeedbinEntry]?) -> Set<ParsedItem> {
@@ -1380,7 +1380,7 @@ private extension FeedbinAccountDelegate {
case .success:
DispatchQueue.main.async {
self.clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
folder.removeWebFeed(feed)
folder.removeFeed(feed)
account.addFeedIfNotInAnyFolder(feed)
completion(.success(()))
}
@@ -1393,7 +1393,7 @@ private extension FeedbinAccountDelegate {
}
} else {
if let account = container as? Account {
account.removeWebFeed(feed)
account.removeFeed(feed)
}
completion(.success(()))
}
@@ -1411,10 +1411,10 @@ private extension FeedbinAccountDelegate {
func complete() {
DispatchQueue.main.async {
account.clearFeedMetadata(feed)
account.removeWebFeed(feed)
account.removeFeed(feed)
if let folders = account.folders {
for folder in folders {
folder.removeWebFeed(feed)
folder.removeFeed(feed)
}
}
completion(.success(()))

View File

@@ -312,7 +312,7 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
}
}
func createWebFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
do {
guard let credentials = credentials else {
@@ -344,7 +344,7 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
}
}
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
func renameFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
let folderCollectionIds = account.folders?.filter { $0.has(feed) }.compactMap { $0.externalID }
guard let collectionIds = folderCollectionIds, let collectionId = collectionIds.first else {
completion(.failure(FeedlyAccountDelegateError.unableToRenameFeed(feed.nameForDisplay, name)))
@@ -371,7 +371,7 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
feed.editedName = name
}
func addWebFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func addFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
do {
guard let credentials = credentials else {
@@ -401,7 +401,7 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
}
}
func removeWebFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func removeFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
guard let folder = container as? Folder, let collectionId = folder.externalID else {
return DispatchQueue.main.async {
completion(.failure(FeedlyAccountDelegateError.unableToRemoveFeed(feed)))
@@ -413,50 +413,50 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
case .success:
completion(.success(()))
case .failure(let error):
folder.addWebFeed(feed)
folder.addFeed(feed)
completion(.failure(error))
}
}
folder.removeWebFeed(feed)
folder.removeFeed(feed)
}
func moveWebFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func moveFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
guard let from = from as? Folder, let to = to as? Folder else {
return DispatchQueue.main.async {
completion(.failure(FeedlyAccountDelegateError.addFeedChooseFolder))
}
}
addWebFeed(for: account, with: feed, to: to) { [weak self] addResult in
addFeed(for: account, with: feed, to: to) { [weak self] addResult in
switch addResult {
// now that we have added the feed, remove it from the other collection
case .success:
self?.removeWebFeed(for: account, with: feed, from: from) { removeResult in
self?.removeFeed(for: account, with: feed, from: from) { removeResult in
switch removeResult {
case .success:
completion(.success(()))
case .failure:
from.addWebFeed(feed)
from.addFeed(feed)
completion(.failure(FeedlyAccountDelegateError.unableToMoveFeedBetweenFolders(feed, from, to)))
}
}
case .failure(let error):
from.addWebFeed(feed)
to.removeWebFeed(feed)
from.addFeed(feed)
to.removeFeed(feed)
completion(.failure(error))
}
}
// optimistically move the feed, undoing as appropriate to the failure
from.removeWebFeed(feed)
to.addWebFeed(feed)
from.removeFeed(feed)
to.addFeed(feed)
}
func restoreWebFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
if let existingFeed = account.existingWebFeed(withURL: feed.url) {
account.addWebFeed(existingFeed, to: container) { result in
func restoreFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
if let existingFeed = account.existingFeed(withURL: feed.url) {
account.addFeed(existingFeed, to: container) { result in
switch result {
case .success:
completion(.success(()))
@@ -465,7 +465,7 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
}
}
} else {
createWebFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in
createFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in
switch result {
case .success:
completion(.success(()))
@@ -479,12 +479,12 @@ final class FeedlyAccountDelegate: AccountDelegate, Logging {
func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
let group = DispatchGroup()
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
folder.topLevelWebFeeds.remove(feed)
folder.topLevelFeeds.remove(feed)
group.enter()
restoreWebFeed(for: account, feed: feed, container: folder) { result in
restoreFeed(for: account, feed: feed, container: folder) { result in
group.leave()
switch result {
case .success:

View File

@@ -134,7 +134,7 @@ class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, Feedl
guard let handler = addCompletionHandler else {
return
}
if let feedResource = feedResourceId, let feed = folder.existingWebFeed(withWebFeedID: feedResource.id) {
if let feedResource = feedResourceId, let feed = folder.existingFeed(withFeedID: feedResource.id) {
handler(.success(feed))
}
else {

View File

@@ -29,11 +29,11 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation, Log
let feedsBefore = Set(pairs
.map { $0.1 }
.flatMap { $0.topLevelWebFeeds })
.flatMap { $0.topLevelFeeds })
// Remove feeds in a folder which are not in the corresponding collection.
for (collectionFeeds, folder) in pairs {
let feedsInFolder = folder.topLevelWebFeeds
let feedsInFolder = folder.topLevelFeeds
let feedsInCollection = Set(collectionFeeds.map { $0.id })
let feedsToRemove = feedsInFolder.filter { !feedsInCollection.contains($0.webFeedID) }
if !feedsToRemove.isEmpty {
@@ -55,7 +55,7 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation, Log
.compactMap { (collectionFeed, folder) -> (WebFeed, Folder) in
// find an existing feed previously added to the account
if let feed = account.existingWebFeed(withWebFeedID: collectionFeed.id) {
if let feed = account.existingFeed(withFeedID: collectionFeed.id) {
// If the feed was renamed on Feedly, ensure we ingest the new name.
if feed.nameForDisplay != collectionFeed.title {
@@ -80,9 +80,9 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation, Log
// no existing feed, create a new one
let parser = FeedlyFeedParser(feed: collectionFeed)
let feed = account.createWebFeed(with: parser.title,
let feed = account.createFeed(with: parser.title,
url: parser.url,
webFeedID: parser.webFeedID,
feedID: parser.webFeedID,
homePageURL: parser.homePageURL)
// So the same feed isn't created more than once.
@@ -94,7 +94,7 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation, Log
logger.debug("Processing \(feedsAndFolders.count, privacy: .public) feeds.")
feedsAndFolders.forEach { (feed, folder) in
if !folder.has(feed) {
folder.addWebFeed(feed)
folder.addFeed(feed)
}
}

View File

@@ -22,15 +22,15 @@ final class FeedlyUpdateAccountFeedsWithItemsOperation: FeedlyOperation, Logging
}
override func run() {
let webFeedIDsAndItems = organisedItemsProvider.parsedItemsKeyedByFeedId
let feedIDsAndItems = organisedItemsProvider.parsedItemsKeyedByFeedId
account.update(webFeedIDsAndItems: webFeedIDsAndItems, defaultRead: true) { databaseError in
account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true) { databaseError in
if let error = databaseError {
self.didFinish(with: error)
return
}
self.logger.debug("Updated \(webFeedIDsAndItems.count, privacy: .public) feeds for \(self.organisedItemsProvider.parsedItemsByFeedProviderName, privacy: .public).")
self.logger.debug("Updated \(feedIDsAndItems.count, privacy: .public) feeds for \(self.organisedItemsProvider.parsedItemsByFeedProviderName, privacy: .public).")
self.didFinish()
}
}

View File

@@ -33,7 +33,7 @@ public final class Folder: FeedProtocol, Renamable, Container, Hashable {
}
public weak var account: Account?
public var topLevelWebFeeds: Set<WebFeed> = Set<WebFeed>()
public var topLevelFeeds: Set<WebFeed> = Set<WebFeed>()
public var folders: Set<Folder>? = nil // subfolders are not supported, so this is always nil
public var name: String? {
@@ -101,9 +101,9 @@ public final class Folder: FeedProtocol, Renamable, Container, Hashable {
// MARK: Container
public func flattenedWebFeeds() -> Set<WebFeed> {
public func flattenedFeeds() -> Set<WebFeed> {
// Since sub-folders are not supported, its always the top-level feeds.
return topLevelWebFeeds
return topLevelFeeds
}
public func objectIsChild(_ object: AnyObject) -> Bool {
@@ -111,11 +111,11 @@ public final class Folder: FeedProtocol, Renamable, Container, Hashable {
guard let feed = object as? WebFeed else {
return false
}
return topLevelWebFeeds.contains(feed)
return topLevelFeeds.contains(feed)
}
public func addWebFeed(_ feed: WebFeed) {
topLevelWebFeeds.insert(feed)
public func addFeed(_ feed: WebFeed) {
topLevelFeeds.insert(feed)
postChildrenDidChangeNotification()
}
@@ -123,12 +123,12 @@ public final class Folder: FeedProtocol, Renamable, Container, Hashable {
guard !feeds.isEmpty else {
return
}
topLevelWebFeeds.formUnion(feeds)
topLevelFeeds.formUnion(feeds)
postChildrenDidChangeNotification()
}
public func removeWebFeed(_ feed: WebFeed) {
topLevelWebFeeds.remove(feed)
public func removeFeed(_ feed: WebFeed) {
topLevelFeeds.remove(feed)
postChildrenDidChangeNotification()
}
@@ -136,7 +136,7 @@ public final class Folder: FeedProtocol, Renamable, Container, Hashable {
guard !feeds.isEmpty else {
return
}
topLevelWebFeeds.subtract(feeds)
topLevelFeeds.subtract(feeds)
postChildrenDidChangeNotification()
}
@@ -159,14 +159,14 @@ private extension Folder {
func updateUnreadCount() {
var updatedUnreadCount = 0
for feed in topLevelWebFeeds {
for feed in topLevelFeeds {
updatedUnreadCount += feed.unreadCount
}
unreadCount = updatedUnreadCount
}
func childrenContain(_ feed: WebFeed) -> Bool {
return topLevelWebFeeds.contains(feed)
return topLevelFeeds.contains(feed)
}
}
@@ -198,7 +198,7 @@ extension Folder: OPMLRepresentable {
var hasAtLeastOneChild = false
for feed in topLevelWebFeeds.sorted() {
for feed in topLevelFeeds.sorted() {
s += feed.OPMLString(indentLevel: indentLevel + 1, allowCustomAttributes: allowCustomAttributes)
hasAtLeastOneChild = true
}

View File

@@ -47,7 +47,7 @@ final class LocalAccountDelegate: AccountDelegate, Logging {
return
}
let webFeeds = account.flattenedWebFeeds()
let webFeeds = account.flattenedFeeds()
refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
let group = DispatchGroup()
@@ -119,39 +119,39 @@ final class LocalAccountDelegate: AccountDelegate, Logging {
}
func createWebFeed(for account: Account, url urlString: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
func createFeed(for account: Account, url urlString: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
guard let url = URL(string: urlString) else {
completion(.failure(LocalAccountDelegateError.invalidParameter))
return
}
// Username should be part of the URL on new feed adds
createRSSWebFeed(for: account, url: url, editedName: name, container: container, completion: completion)
createRSSFeed(for: account, url: url, editedName: name, container: container, completion: completion)
}
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
func renameFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
feed.editedName = name
completion(.success(()))
}
func removeWebFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
container.removeWebFeed(feed)
func removeFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
container.removeFeed(feed)
completion(.success(()))
}
func moveWebFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
from.removeWebFeed(feed)
to.addWebFeed(feed)
func moveFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
from.removeFeed(feed)
to.addFeed(feed)
completion(.success(()))
}
func addWebFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
container.addWebFeed(feed)
func addFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
container.addFeed(feed)
completion(.success(()))
}
func restoreWebFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
container.addWebFeed(feed)
func restoreFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
container.addFeed(feed)
completion(.success(()))
}
@@ -231,7 +231,7 @@ extension LocalAccountDelegate: LocalAccountRefresherDelegate {
private extension LocalAccountDelegate {
func createRSSWebFeed(for account: Account, url: URL, editedName: String?, container: Container, completion: @escaping (Result<WebFeed, Error>) -> Void) {
func createRSSFeed(for account: Account, url: URL, editedName: String?, container: Container, completion: @escaping (Result<WebFeed, Error>) -> Void) {
// We need to use a batch update here because we need to assign add the feed to the
// container before the name has been downloaded. This will put it in the sidebar
@@ -250,7 +250,7 @@ private extension LocalAccountDelegate {
return
}
if account.hasWebFeed(withURL: bestFeedSpecifier.urlString) {
if account.hasFeed(withURL: bestFeedSpecifier.urlString) {
self.refreshProgress.completeTask()
BatchUpdate.shared.end()
completion(.failure(AccountError.createErrorAlreadySubscribed))
@@ -261,9 +261,9 @@ private extension LocalAccountDelegate {
self.refreshProgress.completeTask()
if let parsedFeed = parsedFeed {
let feed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
let feed = account.createFeed(with: nil, url: url.absoluteString, feedID: url.absoluteString, homePageURL: nil)
feed.editedName = editedName
container.addWebFeed(feed)
container.addFeed(feed)
account.update(feed, with: parsedFeed, {_ in
BatchUpdate.shared.end()

View File

@@ -47,8 +47,8 @@ extension NewsBlurAccountDelegate {
if let folders = account.folders {
folders.forEach { folder in
if !folderNames.contains(folder.name ?? "") {
for feed in folder.topLevelWebFeeds {
account.addWebFeed(feed)
for feed in folder.topLevelFeeds {
account.addFeed(feed)
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
}
account.removeFolder(folder)
@@ -84,17 +84,17 @@ extension NewsBlurAccountDelegate {
// Remove any feeds that are no longer in the subscriptions
if let folders = account.folders {
for folder in folders {
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
if !newsBlurFeedIds.contains(feed.webFeedID) {
folder.removeWebFeed(feed)
folder.removeFeed(feed)
}
}
}
}
for feed in account.topLevelWebFeeds {
for feed in account.topLevelFeeds {
if !newsBlurFeedIds.contains(feed.webFeedID) {
account.removeWebFeed(feed)
account.removeFeed(feed)
}
}
@@ -103,7 +103,7 @@ extension NewsBlurAccountDelegate {
feeds.forEach { feed in
let subFeedId = String(feed.feedID)
if let webFeed = account.existingWebFeed(withWebFeedID: subFeedId) {
if let webFeed = account.existingFeed(withFeedID: subFeedId) {
webFeed.name = feed.name
// If the name has been changed on the server remove the locally edited name
webFeed.editedName = nil
@@ -118,9 +118,9 @@ extension NewsBlurAccountDelegate {
// Actually add feeds all in one go, so we dont trigger various rebuilding things that Account does.
feedsToAdd.forEach { feed in
let webFeed = account.createWebFeed(with: feed.name, url: feed.feedURL, webFeedID: String(feed.feedID), homePageURL: feed.homePageURL)
let webFeed = account.createFeed(with: feed.name, url: feed.feedURL, feedID: String(feed.feedID), homePageURL: feed.homePageURL)
webFeed.externalID = String(feed.feedID)
account.addWebFeed(webFeed)
account.addFeed(webFeed)
}
}
@@ -155,25 +155,25 @@ extension NewsBlurAccountDelegate {
guard let folder = folderDict[folderName] else { return }
// Move any feeds not in the folder to the account
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
if !newsBlurFolderFeedIDs.contains(feed.webFeedID) {
folder.removeWebFeed(feed)
folder.removeFeed(feed)
clearFolderRelationship(for: feed, withFolderName: folder.name ?? "")
account.addWebFeed(feed)
account.addFeed(feed)
}
}
// Add any feeds not in the folder
let folderFeedIds = folder.topLevelWebFeeds.map { $0.webFeedID }
let folderFeedIds = folder.topLevelFeeds.map { $0.webFeedID }
for relationship in folderRelationships {
let folderFeedID = String(relationship.feedID)
if !folderFeedIds.contains(folderFeedID) {
guard let feed = account.existingWebFeed(withWebFeedID: folderFeedID) else {
guard let feed = account.existingFeed(withFeedID: folderFeedID) else {
continue
}
saveFolderRelationship(for: feed, withFolderName: folderName, id: relationship.folderName)
folder.addWebFeed(feed)
folder.addFeed(feed)
}
}
}
@@ -182,14 +182,14 @@ extension NewsBlurAccountDelegate {
// in folders and we need to remove them all from the account level.
if let folderRelationships = newsBlurFolderDict[" "] {
let newsBlurFolderFeedIDs = folderRelationships.map { String($0.feedID) }
for feed in account.topLevelWebFeeds {
for feed in account.topLevelFeeds {
if !newsBlurFolderFeedIDs.contains(feed.webFeedID) {
account.removeWebFeed(feed)
account.removeFeed(feed)
}
}
} else {
for feed in account.topLevelWebFeeds {
account.removeWebFeed(feed)
for feed in account.topLevelFeeds {
account.removeFeed(feed)
}
}
@@ -419,15 +419,15 @@ extension NewsBlurAccountDelegate {
}
DispatchQueue.main.async {
let webFeed = account.createWebFeed(with: feed.name, url: feed.feedURL, webFeedID: String(feed.feedID), homePageURL: feed.homePageURL)
let webFeed = account.createFeed(with: feed.name, url: feed.feedURL, feedID: String(feed.feedID), homePageURL: feed.homePageURL)
webFeed.externalID = String(feed.feedID)
webFeed.faviconURL = feed.faviconURL
account.addWebFeed(webFeed, to: container) { result in
account.addFeed(webFeed, to: container) { result in
switch result {
case .success:
if let name = name {
account.renameWebFeed(webFeed, to: name) { result in
account.renameFeed(webFeed, to: name) { result in
switch result {
case .success:
self.initialFeedDownload(account: account, feed: webFeed, completion: completion)
@@ -532,16 +532,16 @@ extension NewsBlurAccountDelegate {
let feedID = feed.webFeedID
if folderName == nil {
account.removeWebFeed(feed)
account.removeFeed(feed)
}
if let folders = account.folders {
for folder in folders where folderName != nil && folder.name == folderName {
folder.removeWebFeed(feed)
folder.removeFeed(feed)
}
}
if account.existingWebFeed(withWebFeedID: feedID) != nil {
if account.existingFeed(withFeedID: feedID) != nil {
account.clearFeedMetadata(feed)
}

View File

@@ -328,17 +328,17 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
return datePublished >= since
}
let webFeedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL }).mapValues {
let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL }).mapValues {
Set($0)
}
account.update(webFeedIDsAndItems: webFeedIDsAndItems, defaultRead: true) { error in
account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true) { error in
if let error = error {
completion(.failure(error))
return
}
completion(.success(!webFeedIDsAndItems.isEmpty))
completion(.success(!feedIDsAndItems.isEmpty))
}
}
@@ -397,7 +397,7 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
}
var feedIDs: [String] = []
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
if (feed.folderRelationship?.count ?? 0) > 1 {
clearFolderRelationship(for: feed, withFolderName: folderToRemove)
} else if let feedID = feed.externalID {
@@ -420,7 +420,7 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
}
}
func createWebFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> ()) {
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> ()) {
refreshProgress.addToNumberOfTasksAndRemaining(1)
let folderName = (container as? Folder)?.name
@@ -439,7 +439,7 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
}
}
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> ()) {
func renameFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> ()) {
guard let feedID = feed.externalID else {
completion(.failure(NewsBlurError.invalidParameter))
return
@@ -466,11 +466,11 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
}
}
func addWebFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> ()) {
func addFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> ()) {
guard let folder = container as? Folder else {
DispatchQueue.main.async {
if let account = container as? Account {
account.addWebFeed(feed)
account.addFeed(feed)
}
completion(.success(()))
}
@@ -480,16 +480,16 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
let folderName = folder.name ?? ""
saveFolderRelationship(for: feed, withFolderName: folderName, id: folderName)
folder.addWebFeed(feed)
folder.addFeed(feed)
completion(.success(()))
}
func removeWebFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> ()) {
func removeFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> ()) {
deleteFeed(for: account, with: feed, from: container, completion: completion)
}
func moveWebFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> ()) {
func moveFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> ()) {
guard let feedID = feed.externalID else {
completion(.failure(NewsBlurError.invalidParameter))
return
@@ -506,8 +506,8 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
switch result {
case .success:
from.removeWebFeed(feed)
to.addWebFeed(feed)
from.removeFeed(feed)
to.addFeed(feed)
completion(.success(()))
case .failure(let error):
@@ -516,9 +516,9 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
}
}
func restoreWebFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> ()) {
if let existingFeed = account.existingWebFeed(withURL: feed.url) {
account.addWebFeed(existingFeed, to: container) { result in
func restoreFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> ()) {
if let existingFeed = account.existingFeed(withURL: feed.url) {
account.addFeed(existingFeed, to: container) { result in
switch result {
case .success:
completion(.success(()))
@@ -527,7 +527,7 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
}
}
} else {
createWebFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in
createFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in
switch result {
case .success:
completion(.success(()))
@@ -545,9 +545,9 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
}
var feedsToRestore: [WebFeed] = []
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
feedsToRestore.append(feed)
folder.topLevelWebFeeds.remove(feed)
folder.topLevelFeeds.remove(feed)
}
let group = DispatchGroup()
@@ -559,7 +559,7 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
case .success(let folder):
for feed in feedsToRestore {
group.enter()
self.restoreWebFeed(for: account, feed: feed, container: folder) { result in
self.restoreFeed(for: account, feed: feed, container: folder) { result in
group.leave()
switch result {
case .success:

View File

@@ -324,7 +324,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
let group = DispatchGroup()
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
if feed.folderRelationship?.count ?? 0 > 1 {
@@ -388,7 +388,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
}
func createWebFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<WebFeed, Error>) -> Void) {
guard let url = URL(string: url) else {
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
return
@@ -437,7 +437,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
}
func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
func renameFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
// This error should never happen
guard let subscriptionID = feed.externalID else {
@@ -464,7 +464,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
}
func removeWebFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func removeFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
guard let subscriptionID = feed.externalID else {
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
return
@@ -477,10 +477,10 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
case .success:
DispatchQueue.main.async {
account.clearFeedMetadata(feed)
account.removeWebFeed(feed)
account.removeFeed(feed)
if let folders = account.folders {
for folder in folders {
folder.removeWebFeed(feed)
folder.removeFeed(feed)
}
}
completion(.success(()))
@@ -494,9 +494,9 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
}
}
func moveWebFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func moveFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
if from is Account {
addWebFeed(for: account, with: feed, to: to, completion: completion)
addFeed(for: account, with: feed, to: to, completion: completion)
} else {
guard
let subscriptionId = feed.externalID,
@@ -512,8 +512,8 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
self.refreshProgress.completeTask()
switch result {
case .success:
from.removeWebFeed(feed)
to.addWebFeed(feed)
from.removeFeed(feed)
to.addFeed(feed)
completion(.success(()))
case .failure(let error):
completion(.failure(error))
@@ -522,7 +522,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
}
}
func addWebFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func addFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
if let folder = container as? Folder, let feedExternalID = feed.externalID {
refreshProgress.addToNumberOfTasksAndRemaining(1)
caller.createTagging(subscriptionID: feedExternalID, tagName: folder.name ?? "") { result in
@@ -531,8 +531,8 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
case .success:
DispatchQueue.main.async {
self.saveFolderRelationship(for: feed, folderExternalID: folder.externalID, feedExternalID: feedExternalID)
account.removeWebFeed(feed)
folder.addWebFeed(feed)
account.removeFeed(feed)
folder.addFeed(feed)
completion(.success(()))
}
case .failure(let error):
@@ -552,10 +552,10 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
}
}
func restoreWebFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
func restoreFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
if let existingFeed = account.existingWebFeed(withURL: feed.url) {
account.addWebFeed(existingFeed, to: container) { result in
if let existingFeed = account.existingFeed(withURL: feed.url) {
account.addFeed(existingFeed, to: container) { result in
switch result {
case .success:
completion(.success(()))
@@ -564,7 +564,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
}
}
} else {
createWebFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in
createFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in
switch result {
case .success:
completion(.success(()))
@@ -580,12 +580,12 @@ final class ReaderAPIAccountDelegate: AccountDelegate, Logging {
let group = DispatchGroup()
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
folder.topLevelWebFeeds.remove(feed)
folder.topLevelFeeds.remove(feed)
group.enter()
restoreWebFeed(for: account, feed: feed, container: folder) { result in
restoreFeed(for: account, feed: feed, container: folder) { result in
group.leave()
switch result {
case .success:
@@ -718,8 +718,8 @@ private extension ReaderAPIAccountDelegate {
if let folders = account.folders {
folders.forEach { folder in
if !readerFolderExternalIDs.contains(folder.externalID ?? "") {
for feed in folder.topLevelWebFeeds {
account.addWebFeed(feed)
for feed in folder.topLevelFeeds {
account.addFeed(feed)
clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
}
account.removeFolder(folder)
@@ -757,32 +757,32 @@ private extension ReaderAPIAccountDelegate {
// Remove any feeds that are no longer in the subscriptions
if let folders = account.folders {
for folder in folders {
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
if !subFeedIds.contains(feed.webFeedID) {
folder.removeWebFeed(feed)
folder.removeFeed(feed)
}
}
}
}
for feed in account.topLevelWebFeeds {
for feed in account.topLevelFeeds {
if !subFeedIds.contains(feed.webFeedID) {
account.clearFeedMetadata(feed)
account.removeWebFeed(feed)
account.removeFeed(feed)
}
}
// Add any feeds we don't have and update any we do
subscriptions.forEach { subscription in
if let feed = account.existingWebFeed(withWebFeedID: subscription.feedID) {
if let feed = account.existingFeed(withFeedID: subscription.feedID) {
feed.name = subscription.name
feed.editedName = nil
feed.homePageURL = subscription.homePageURL
} else {
let feed = account.createWebFeed(with: subscription.name, url: subscription.url, webFeedID: subscription.feedID, homePageURL: subscription.homePageURL)
let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: subscription.feedID, homePageURL: subscription.homePageURL)
feed.externalID = subscription.feedID
account.addWebFeed(feed)
account.addFeed(feed)
}
}
@@ -817,25 +817,25 @@ private extension ReaderAPIAccountDelegate {
let taggingFeedIDs = groupedTaggings.map { $0.feedID }
// Move any feeds not in the folder to the account
for feed in folder.topLevelWebFeeds {
for feed in folder.topLevelFeeds {
if !taggingFeedIDs.contains(feed.webFeedID) {
folder.removeWebFeed(feed)
folder.removeFeed(feed)
clearFolderRelationship(for: feed, folderExternalID: folder.externalID)
account.addWebFeed(feed)
account.addFeed(feed)
}
}
// Add any feeds not in the folder
let folderFeedIds = folder.topLevelWebFeeds.map { $0.webFeedID }
let folderFeedIds = folder.topLevelFeeds.map { $0.webFeedID }
for subscription in groupedTaggings {
let taggingFeedID = subscription.feedID
if !folderFeedIds.contains(taggingFeedID) {
guard let feed = account.existingWebFeed(withWebFeedID: taggingFeedID) else {
guard let feed = account.existingFeed(withFeedID: taggingFeedID) else {
continue
}
saveFolderRelationship(for: feed, folderExternalID: folderExternalID, feedExternalID: subscription.feedID)
folder.addWebFeed(feed)
folder.addFeed(feed)
}
}
@@ -844,9 +844,9 @@ private extension ReaderAPIAccountDelegate {
let taggedFeedIDs = Set(subscriptions.filter({ !$0.categories.isEmpty }).map { String($0.feedID) })
// Remove all feeds from the account container that have a tag
for feed in account.topLevelWebFeeds {
for feed in account.topLevelFeeds {
if taggedFeedIDs.contains(feed.webFeedID) {
account.removeWebFeed(feed)
account.removeFeed(feed)
}
}
}
@@ -920,14 +920,14 @@ private extension ReaderAPIAccountDelegate {
DispatchQueue.main.async {
let feed = account.createWebFeed(with: sub.name, url: sub.url, webFeedID: String(sub.feedID), homePageURL: sub.homePageURL)
let feed = account.createFeed(with: sub.name, url: sub.url, feedID: String(sub.feedID), homePageURL: sub.homePageURL)
feed.externalID = String(sub.feedID)
account.addWebFeed(feed, to: container) { result in
account.addFeed(feed, to: container) { result in
switch result {
case .success:
if let name = name {
self.renameWebFeed(for: account, with: feed, to: name) { result in
self.renameFeed(for: account, with: feed, to: name) { result in
switch result {
case .success:
self.initialFeedDownload(account: account, feed: feed, completion: completion)
@@ -1031,8 +1031,8 @@ private extension ReaderAPIAccountDelegate {
func processEntries(account: Account, entries: [ReaderAPIEntry]?, completion: @escaping VoidCompletionBlock) {
let parsedItems = mapEntriesToParsedItems(account: account, entries: entries)
let webFeedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
account.update(webFeedIDsAndItems: webFeedIDsAndItems, defaultRead: true) { _ in
let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
account.update(feedIDsAndItems: feedIDsAndItems, defaultRead: true) { _ in
completion()
}
}

View File

@@ -212,7 +212,7 @@ public final class WebFeed: FeedProtocol, Renamable, Hashable, ObservableObject
public func rename(to newName: String, completion: @escaping (Result<Void, Error>) -> Void) {
guard let account = account else { return }
account.renameWebFeed(self, to: newName, completion: completion)
account.renameFeed(self, to: newName, completion: completion)
}
// MARK: - UnreadCountProvider

View File

@@ -33,8 +33,8 @@ class AccountFeedbinFolderContentsSyncTest: XCTestCase {
waitForExpectations(timeout: 5, handler: nil)
let folder = account.folders?.filter { $0.name == "Developers" } .first!
XCTAssertEqual(156, folder?.topLevelWebFeeds.count ?? 0)
XCTAssertEqual(2, account.topLevelWebFeeds.count)
XCTAssertEqual(156, folder?.topLevelFeeds.count ?? 0)
XCTAssertEqual(2, account.topLevelFeeds.count)
// Test Adding a Feed to the folder
testTransport.testFiles["https://api.feedbin.com/v2/taggings.json"] = "JSON/taggings_add.json"
@@ -45,8 +45,8 @@ class AccountFeedbinFolderContentsSyncTest: XCTestCase {
}
waitForExpectations(timeout: 5, handler: nil)
XCTAssertEqual(157, folder?.topLevelWebFeeds.count ?? 0)
XCTAssertEqual(1, account.topLevelWebFeeds.count)
XCTAssertEqual(157, folder?.topLevelFeeds.count ?? 0)
XCTAssertEqual(1, account.topLevelFeeds.count)
// Test Deleting some Feeds from the folder
testTransport.testFiles["https://api.feedbin.com/v2/taggings.json"] = "JSON/taggings_delete.json"
@@ -57,8 +57,8 @@ class AccountFeedbinFolderContentsSyncTest: XCTestCase {
}
waitForExpectations(timeout: 5, handler: nil)
XCTAssertEqual(153, folder?.topLevelWebFeeds.count ?? 0)
XCTAssertEqual(5, account.topLevelWebFeeds.count)
XCTAssertEqual(153, folder?.topLevelFeeds.count ?? 0)
XCTAssertEqual(5, account.topLevelFeeds.count)
TestAccountManager.shared.deleteAccount(account)

View File

@@ -36,9 +36,9 @@ class AccountFeedbinSyncTest: XCTestCase {
}
waitForExpectations(timeout: 5, handler: nil)
XCTAssertEqual(224, account.flattenedWebFeeds().count)
XCTAssertEqual(224, account.flattenedFeeds().count)
let daringFireball = account.idToWebFeedDictionary["1296379"]
let daringFireball = account.idToFeedDictionary["1296379"]
XCTAssertEqual("Daring Fireball", daringFireball!.name)
XCTAssertEqual("https://daringfireball.net/feeds/json", daringFireball!.url)
XCTAssertEqual("https://daringfireball.net/", daringFireball!.homePageURL)
@@ -57,9 +57,9 @@ class AccountFeedbinSyncTest: XCTestCase {
}
waitForExpectations(timeout: 5, handler: nil)
XCTAssertEqual(225, account.flattenedWebFeeds().count)
XCTAssertEqual(225, account.flattenedFeeds().count)
let bPixels = account.idToWebFeedDictionary["1096623"]
let bPixels = account.idToFeedDictionary["1096623"]
XCTAssertEqual("Beautiful Pixels", bPixels?.name)
XCTAssertEqual("https://feedpress.me/beautifulpixels", bPixels?.url)
XCTAssertEqual("https://beautifulpixels.com/", bPixels?.homePageURL)

View File

@@ -59,7 +59,7 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
completionExpectation.fulfill()
}
XCTAssertTrue(account.flattenedWebFeeds().isEmpty, "Expected empty account.")
XCTAssertTrue(account.flattenedFeeds().isEmpty, "Expected empty account.")
MainThreadOperationQueue.shared.add(createFeeds)
@@ -73,7 +73,7 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
.flatMap { $0 }
.map { $0.title })
let accountFeeds = account.flattenedWebFeeds()
let accountFeeds = account.flattenedFeeds()
let ingestedIds = Set(accountFeeds.map { $0.webFeedID })
let ingestedTitles = Set(accountFeeds.map { $0.nameForDisplay })
@@ -92,7 +92,7 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
let ingestedFolderAndFeedIds = (account.folders ?? Set())
.sorted { $0.externalID! < $1.externalID! }
.compactMap { folder -> [String: [String]]? in
return [folder.externalID!: folder.topLevelWebFeeds.map { $0.webFeedID }.sorted(by: <)]
return [folder.externalID!: folder.topLevelFeeds.map { $0.webFeedID }.sorted(by: <)]
}
XCTAssertEqual(expectedFolderAndFeedIds, ingestedFolderAndFeedIds, "Did not ingest feeds in their corresponding folders.")
@@ -130,7 +130,7 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
completionExpectation.fulfill()
}
XCTAssertTrue(account.flattenedWebFeeds().isEmpty, "Expected empty account.")
XCTAssertTrue(account.flattenedFeeds().isEmpty, "Expected empty account.")
MainThreadOperationQueue.shared.add(createFeeds)
@@ -166,7 +166,7 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
.flatMap { $0 }
.map { $0.title })
let accountFeeds = account.flattenedWebFeeds()
let accountFeeds = account.flattenedFeeds()
let ingestedIds = Set(accountFeeds.map { $0.webFeedID })
let ingestedTitles = Set(accountFeeds.map { $0.nameForDisplay })
@@ -188,7 +188,7 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
let ingestedFolderAndFeedIds = (account.folders ?? Set())
.sorted { $0.externalID! < $1.externalID! }
.compactMap { folder -> [String: [String]]? in
return [folder.externalID!: folder.topLevelWebFeeds.map { $0.webFeedID }.sorted(by: <)]
return [folder.externalID!: folder.topLevelFeeds.map { $0.webFeedID }.sorted(by: <)]
}
XCTAssertEqual(expectedFolderAndFeedIds, ingestedFolderAndFeedIds, "Did not ingest feeds to their corresponding folders.")

View File

@@ -180,7 +180,7 @@ class FeedlyMirrorCollectionsAsFoldersOperationTests: XCTestCase {
waitForExpectations(timeout: 2)
XCTAssertFalse(account.flattenedWebFeeds().isEmpty, "Expected account to have feeds.")
XCTAssertFalse(account.flattenedFeeds().isEmpty, "Expected account to have feeds.")
}
// Now that the folders are added, remove them all.
@@ -197,7 +197,7 @@ class FeedlyMirrorCollectionsAsFoldersOperationTests: XCTestCase {
waitForExpectations(timeout: 2)
let feeds = account.flattenedWebFeeds()
let feeds = account.flattenedFeeds()
XCTAssertTrue(feeds.isEmpty)
}

View File

@@ -134,7 +134,7 @@ class FeedlyTestSupport {
return
}
let collectionFeeds = collection["feeds"] as! [[String: Any]]
let folderFeeds = folder.topLevelWebFeeds
let folderFeeds = folder.topLevelFeeds
XCTAssertEqual(collectionFeeds.count, folderFeeds.count)

View File

@@ -549,7 +549,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
}
// MARK: Add Feed
@MainActor func addWebFeed(_ urlString: String?, name: String? = nil, account: Account? = nil, folder: Folder? = nil) {
@MainActor func addFeed(_ urlString: String?, name: String? = nil, account: Account? = nil, folder: Folder? = nil) {
createAndShowMainWindowIfNecessary()
if mainWindowController!.isDisplayingSheet {
@@ -588,7 +588,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
}
@MainActor @IBAction func showAddFeedWindow(_ sender: Any?) {
addWebFeed(nil)
addFeed(nil)
}
@MainActor @IBAction func showAddFolderWindow(_ sender: Any?) {
@@ -661,7 +661,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations,
if AccountManager.shared.anyAccountHasNetNewsWireNewsSubscription() {
return
}
addWebFeed(AccountManager.netNewsWireNewsURL, name: "NetNewsWire News")
addFeed(AccountManager.netNewsWireNewsURL, name: "NetNewsWire News")
}
@MainActor @IBAction func openWebsite(_ sender: Any?) {

View File

@@ -35,7 +35,7 @@ import UserNotifications
let isFallbackInspector = false
var objects: [Any]? {
didSet {
renameWebFeedIfNecessary()
renameFeedIfNecessary()
updateFeed()
}
}
@@ -58,7 +58,7 @@ import UserNotifications
}
override func viewDidDisappear() {
renameWebFeedIfNecessary()
renameFeedIfNecessary()
}
// MARK: Actions
@@ -115,7 +115,7 @@ import UserNotifications
extension WebFeedInspectorViewController: NSTextFieldDelegate {
func controlTextDidEndEditing(_ note: Notification) {
renameWebFeedIfNecessary()
renameFeedIfNecessary()
}
}
@@ -204,7 +204,7 @@ private extension WebFeedInspectorViewController {
}
}
func renameWebFeedIfNecessary() {
func renameFeedIfNecessary() {
guard let feed = feed,
let account = feed.account,
let nameTextField = nameTextField,
@@ -212,7 +212,7 @@ private extension WebFeedInspectorViewController {
return
}
account.renameWebFeed(feed, to: nameTextField.stringValue) { [weak self] result in
account.renameFeed(feed, to: nameTextField.stringValue) { [weak self] result in
if case .failure(let error) = result {
self?.presentError(error)
} else {

View File

@@ -60,12 +60,12 @@ import RSParser
}
let account = accountAndFolderSpecifier.account
if account.hasWebFeed(withURL: url.absoluteString) {
if account.hasFeed(withURL: url.absoluteString) {
showAlreadySubscribedError(url.absoluteString)
return
}
account.createWebFeed(url: url.absoluteString, name: title, container: container, validateFeed: true) { result in
account.createFeed(url: url.absoluteString, name: title, container: container, validateFeed: true) { result in
DispatchQueue.main.async {
self.endShowingProgress()

View File

@@ -316,7 +316,7 @@ private extension SidebarOutlineDataSource {
}
func copyWebFeedInAccount(_ feed: WebFeed, _ destination: Container ) {
destination.account?.addWebFeed(feed, to: destination) { result in
destination.account?.addFeed(feed, to: destination) { result in
switch result {
case .success:
break
@@ -326,9 +326,9 @@ private extension SidebarOutlineDataSource {
}
}
func moveWebFeedInAccount(_ feed: WebFeed, _ source: Container, _ destination: Container) {
func moveFeedInAccount(_ feed: WebFeed, _ source: Container, _ destination: Container) {
BatchUpdate.shared.start()
source.account?.moveWebFeed(feed, from: source, to: destination) { result in
source.account?.moveFeed(feed, from: source, to: destination) { result in
BatchUpdate.shared.end()
switch result {
case .success:
@@ -344,8 +344,8 @@ private extension SidebarOutlineDataSource {
return
}
if let existingFeed = destinationAccount.existingWebFeed(withURL: feed.url) {
destinationAccount.addWebFeed(existingFeed, to: destinationContainer) { result in
if let existingFeed = destinationAccount.existingFeed(withURL: feed.url) {
destinationAccount.addFeed(existingFeed, to: destinationContainer) { result in
switch result {
case .success:
break
@@ -354,7 +354,7 @@ private extension SidebarOutlineDataSource {
}
}
} else {
destinationAccount.createWebFeed(url: feed.url, name: feed.nameForDisplay, container: destinationContainer, validateFeed: false) { result in
destinationAccount.createFeed(url: feed.url, name: feed.nameForDisplay, container: destinationContainer, validateFeed: false) { result in
switch result {
case .success:
break
@@ -374,7 +374,7 @@ private extension SidebarOutlineDataSource {
guard let sourceAccountID = pasteboardFeed.accountID,
let sourceAccount = AccountManager.shared.existingAccount(with: sourceAccountID),
let webFeedID = pasteboardFeed.feedID,
let feed = sourceAccount.existingWebFeed(withWebFeedID: webFeedID),
let feed = sourceAccount.existingFeed(withFeedID: webFeedID),
let destinationContainer = parentNode.representedObject as? Container
else {
return
@@ -393,7 +393,7 @@ private extension SidebarOutlineDataSource {
if NSApplication.shared.currentEvent?.modifierFlags.contains(.option) ?? false {
copyWebFeedInAccount(feed, destinationContainer)
} else {
moveWebFeedInAccount(feed, sourceContainer, destinationContainer)
moveFeedInAccount(feed, sourceContainer, destinationContainer)
}
} else {
copyWebFeedBetweenAccounts(feed, destinationContainer)
@@ -443,9 +443,9 @@ private extension SidebarOutlineDataSource {
destinationAccount.addFolder(folder.name ?? "") { result in
switch result {
case .success(let destinationFolder):
for feed in folder.topLevelWebFeeds {
if let existingFeed = destinationAccount.existingWebFeed(withURL: feed.url) {
destinationAccount.addWebFeed(existingFeed, to: destinationFolder) { result in
for feed in folder.topLevelFeeds {
if let existingFeed = destinationAccount.existingFeed(withURL: feed.url) {
destinationAccount.addFeed(existingFeed, to: destinationFolder) { result in
switch result {
case .success:
break
@@ -454,7 +454,7 @@ private extension SidebarOutlineDataSource {
}
}
} else {
destinationAccount.createWebFeed(url: feed.url, name: feed.nameForDisplay, container: destinationFolder, validateFeed: false) { result in
destinationAccount.createFeed(url: feed.url, name: feed.nameForDisplay, container: destinationFolder, validateFeed: false) { result in
switch result {
case .success:
break
@@ -499,11 +499,11 @@ private extension SidebarOutlineDataSource {
// Show the add-feed sheet.
if let account = parentNode.representedObject as? Account {
appDelegate.addWebFeed(draggedFeed.url, name: draggedFeed.editedName ?? draggedFeed.name, account: account, folder: nil)
appDelegate.addFeed(draggedFeed.url, name: draggedFeed.editedName ?? draggedFeed.name, account: account, folder: nil)
} else {
let account = parentNode.parent?.representedObject as? Account
let folder = parentNode.representedObject as? Folder
appDelegate.addWebFeed(draggedFeed.url, name: draggedFeed.editedName ?? draggedFeed.name, account: account, folder: folder)
appDelegate.addFeed(draggedFeed.url, name: draggedFeed.editedName ?? draggedFeed.name, account: account, folder: folder)
}
return true
@@ -635,7 +635,7 @@ private extension SidebarOutlineDataSource {
return true
}
} else {
if dropTargetAccount.hasWebFeed(withURL: draggedFeed.url) {
if dropTargetAccount.hasFeed(withURL: draggedFeed.url) {
return true
}
}

View File

@@ -552,7 +552,7 @@ private extension SidebarViewController {
treeControllerDelegate.addFilterException(itemID)
}
} else if let webFeed = feed as? WebFeed {
if webFeed.account?.existingWebFeed(withWebFeedID: webFeed.webFeedID) != nil {
if webFeed.account?.existingFeed(withFeedID: webFeed.webFeedID) != nil {
treeControllerDelegate.addFilterException(itemID)
addParentFolderToFilterExceptions(webFeed)
}

View File

@@ -1333,7 +1333,7 @@ private extension TimelineViewController {
}
else if let folder = representedObject as? Folder {
for oneFeed in webFeeds {
if folder.hasWebFeed(with: oneFeed.webFeedID) || folder.hasWebFeed(withURL: oneFeed.url) {
if folder.hasFeed(with: oneFeed.webFeedID) || folder.hasFeed(withURL: oneFeed.url) {
return true
}
}

View File

@@ -81,7 +81,7 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
} else {
container = account
}
account.removeWebFeed(scriptableFeed.feed, from: container!) { result in
account.removeFeed(scriptableFeed.feed, from: container!) { result in
}
}
}
@@ -96,18 +96,18 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
@objc(webFeeds)
var webFeeds:NSArray {
return account.topLevelWebFeeds.map { ScriptableFeed($0, container:self) } as NSArray
return account.topLevelFeeds.map { ScriptableFeed($0, container:self) } as NSArray
}
@objc(valueInWebFeedsWithUniqueID:)
func valueInWebFeeds(withUniqueID id:String) -> ScriptableFeed? {
guard let feed = account.existingWebFeed(withWebFeedID: id) else { return nil }
guard let feed = account.existingFeed(withFeedID: id) else { return nil }
return ScriptableFeed(feed, container:self)
}
@objc(valueInWebFeedsWithName:)
func valueInWebFeeds(withName name:String) -> ScriptableFeed? {
let feeds = Array(account.flattenedWebFeeds())
let feeds = Array(account.flattenedFeeds())
guard let feed = feeds.first(where:{$0.name == name}) else { return nil }
return ScriptableFeed(feed, container:self)
}
@@ -133,13 +133,13 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
@objc(allWebFeeds)
var allWebFeeds: NSArray {
var webFeeds = [ScriptableFeed]()
for webFeed in account.topLevelWebFeeds {
for webFeed in account.topLevelFeeds {
webFeeds.append(ScriptableFeed(webFeed, container: self))
}
if let folders = account.folders {
for folder in folders {
let scriptableFolder = ScriptableFolder(folder, container: self)
for webFeed in folder.topLevelWebFeeds {
for webFeed in folder.topLevelFeeds {
webFeeds.append(ScriptableFeed(webFeed, container: scriptableFolder))
}
}

View File

@@ -89,7 +89,7 @@ extension AppDelegate : AppDelegateAppleEvents {
DispatchQueue.main.async {
self.addWebFeed(normalizedURLString)
self.addFeed(normalizedURLString)
}
}
}

View File

@@ -53,7 +53,7 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
func deleteElement(_ element:ScriptingObject) {
if let scriptableFeed = element as? ScriptableFeed {
BatchUpdate.shared.perform {
folder.account?.removeWebFeed(scriptableFeed.feed, from: folder) { result in }
folder.account?.removeFeed(scriptableFeed.feed, from: folder) { result in }
}
}
}
@@ -97,7 +97,7 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
@objc(webFeeds)
var webFeeds:NSArray {
let feeds = Array(folder.topLevelWebFeeds)
let feeds = Array(folder.topLevelFeeds)
return feeds.map { ScriptableFeed($0, container:self) } as NSArray
}

View File

@@ -77,7 +77,7 @@ extension NSApplication : ScriptingObjectContainer {
let accounts = AccountManager.shared.activeAccounts
let emptyFeeds:[WebFeed] = []
return accounts.reduce(emptyFeeds) { (result, nthAccount) -> [WebFeed] in
let accountFeeds = Array(nthAccount.topLevelWebFeeds)
let accountFeeds = Array(nthAccount.topLevelFeeds)
return result + accountFeeds
}
}

View File

@@ -88,7 +88,7 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
let (account, folder) = command.accountAndFolderForNewChild()
guard let url = self.urlForNewFeed(arguments:arguments) else {return nil}
if let existingFeed = account.existingWebFeed(withURL:url) {
if let existingFeed = account.existingFeed(withURL:url) {
return scriptableFeed(existingFeed, account:account, folder:folder).objectSpecifier
}
@@ -102,7 +102,7 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
// suspendExecution(). When we get the callback, we supply the event result and call resumeExecution().
command.suspendExecution()
account.createWebFeed(url: url, name: titleFromArgs, container: container, validateFeed: true) { result in
account.createFeed(url: url, name: titleFromArgs, container: container, validateFeed: true) { result in
switch result {
case .success(let feed):
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.webFeed: feed])

View File

@@ -117,7 +117,7 @@ class ActivityManager {
}
}
for webFeed in account.flattenedWebFeeds() {
for webFeed in account.flattenedFeeds() {
ids.append(contentsOf: identifiers(for: webFeed))
}
@@ -128,7 +128,7 @@ class ActivityManager {
var ids = [String]()
ids.append(identifier(for: folder))
for webFeed in folder.flattenedWebFeeds() {
for webFeed in folder.flattenedFeeds() {
ids.append(contentsOf: identifiers(for: webFeed))
}

View File

@@ -152,7 +152,7 @@ private struct SidebarItemSpecifier {
}
BatchUpdate.shared.start()
account?.removeWebFeed(webFeed, from: container) { result in
account?.removeFeed(webFeed, from: container) { result in
BatchUpdate.shared.end()
completion()
self.checkResult(result)
@@ -173,21 +173,21 @@ private struct SidebarItemSpecifier {
func restore() {
if let _ = webFeed {
restoreWebFeed()
restoreFeed()
}
else if let _ = folder {
restoreFolder()
}
}
private func restoreWebFeed() {
private func restoreFeed() {
guard let account = account, let feed = webFeed, let container = path.resolveContainer() else {
return
}
BatchUpdate.shared.start()
account.restoreWebFeed(feed, container: container) { result in
account.restoreFeed(feed, container: container) { result in
BatchUpdate.shared.end()
self.checkResult(result)
}

View File

@@ -152,7 +152,7 @@ private extension ExtensionFeedAddRequestFile {
guard let container = destinationContainer else { return }
account.createWebFeed(url: request.feedURL.absoluteString, name: request.name, container: container, validateFeed: true) { _ in }
account.createFeed(url: request.feedURL.absoluteString, name: request.name, container: container, validateFeed: true) { _ in }
}
}

View File

@@ -66,7 +66,7 @@ private extension FeedTreeControllerDelegate {
var children = [AnyObject]()
for webFeed in container.topLevelWebFeeds {
for webFeed in container.topLevelFeeds {
if let itemID = webFeed.itemID, !(!filterExceptions.contains(itemID) && isReadFiltered && webFeed.unreadCount == 0) {
children.append(webFeed)
}

View File

@@ -97,7 +97,7 @@ class AddFeedViewController: UITableViewController {
account = containerAccount
}
if account!.hasWebFeed(withURL: url.absoluteString) {
if account!.hasFeed(withURL: url.absoluteString) {
presentError(AccountError.createErrorAlreadySubscribed)
return
}
@@ -110,7 +110,7 @@ class AddFeedViewController: UITableViewController {
BatchUpdate.shared.start()
account!.createWebFeed(url: url.absoluteString, name: feedName, container: container, validateFeed: true) { result in
account!.createFeed(url: url.absoluteString, name: feedName, container: container, validateFeed: true) { result in
BatchUpdate.shared.end()

View File

@@ -58,7 +58,7 @@ extension MasterFeedViewController: UITableViewDropDelegate {
// Validate account specific behaviors...
if correctDestAccount.behaviors.contains(.disallowFeedInMultipleFolders),
sourceWebFeed.account?.accountID != correctDestAccount.accountID && correctDestAccount.hasWebFeed(withURL: sourceWebFeed.url) {
sourceWebFeed.account?.accountID != correctDestAccount.accountID && correctDestAccount.hasFeed(withURL: sourceWebFeed.url) {
return UITableViewDropProposal(operation: .forbidden)
}
@@ -117,7 +117,7 @@ extension MasterFeedViewController: UITableViewDropDelegate {
guard let destination = destinationContainer, let webFeed = dragNode.representedObject as? WebFeed else { return }
if source.account == destination.account {
moveWebFeedInAccount(feed: webFeed, sourceContainer: source, destinationContainer: destination)
moveFeedInAccount(feed: webFeed, sourceContainer: source, destinationContainer: destination)
} else {
copyWebFeedBetweenAccounts(feed: webFeed, sourceContainer: source, destinationContainer: destination)
}
@@ -138,11 +138,11 @@ private extension MasterFeedViewController {
return correctDestination
}
func moveWebFeedInAccount(feed: WebFeed, sourceContainer: Container, destinationContainer: Container) {
func moveFeedInAccount(feed: WebFeed, sourceContainer: Container, destinationContainer: Container) {
guard sourceContainer !== destinationContainer else { return }
BatchUpdate.shared.start()
sourceContainer.account?.moveWebFeed(feed, from: sourceContainer, to: destinationContainer) { result in
sourceContainer.account?.moveFeed(feed, from: sourceContainer, to: destinationContainer) { result in
BatchUpdate.shared.end()
switch result {
case .success:
@@ -155,10 +155,10 @@ private extension MasterFeedViewController {
func copyWebFeedBetweenAccounts(feed: WebFeed, sourceContainer: Container, destinationContainer: Container) {
if let existingFeed = destinationContainer.account?.existingWebFeed(withURL: feed.url) {
if let existingFeed = destinationContainer.account?.existingFeed(withURL: feed.url) {
BatchUpdate.shared.start()
destinationContainer.account?.addWebFeed(existingFeed, to: destinationContainer) { result in
destinationContainer.account?.addFeed(existingFeed, to: destinationContainer) { result in
switch result {
case .success:
BatchUpdate.shared.end()
@@ -171,7 +171,7 @@ private extension MasterFeedViewController {
} else {
BatchUpdate.shared.start()
destinationContainer.account?.createWebFeed(url: feed.url, name: feed.editedName, container: destinationContainer, validateFeed: false) { result in
destinationContainer.account?.createFeed(url: feed.url, name: feed.editedName, container: destinationContainer, validateFeed: false) { result in
switch result {
case .success:
BatchUpdate.shared.end()
@@ -190,7 +190,7 @@ private extension MasterFeedViewController {
private extension Container {
func hasChildWebFeed(withURL url: String) -> Bool {
return topLevelWebFeeds.contains(where: { $0.url == url })
return topLevelFeeds.contains(where: { $0.url == url })
}
}

View File

@@ -1495,7 +1495,7 @@ private extension SceneCoordinator {
treeControllerDelegate.addFilterException(feedID)
}
} else if let webFeed = feed as? WebFeed {
if webFeed.account?.existingWebFeed(withWebFeedID: webFeed.webFeedID) != nil {
if webFeed.account?.existingFeed(withWebFeedID: webFeed.webFeedID) != nil {
treeControllerDelegate.addFilterException(feedID)
addParentFolderToFilterExceptions(webFeed)
}
@@ -2016,7 +2016,7 @@ private extension SceneCoordinator {
if !unsortedArticleIDs.contains(article.articleID) {
updatedArticles.insert(article)
}
if article.account?.existingWebFeed(withWebFeedID: article.webFeedID) == nil {
if article.account?.existingFeed(withWebFeedID: article.webFeedID) == nil {
updatedArticles.remove(article)
}
}
@@ -2101,7 +2101,7 @@ private extension SceneCoordinator {
}
} else if let folder = timelineFeed as? Folder {
for oneFeed in feeds {
if folder.hasWebFeed(with: oneFeed.webFeedID) || folder.hasWebFeed(withURL: oneFeed.url) {
if folder.hasFeed(with: oneFeed.webFeedID) || folder.hasFeed(withURL: oneFeed.url) {
return true
}
}
@@ -2175,7 +2175,7 @@ private extension SceneCoordinator {
case .webFeed(let accountID, let webFeedID):
guard let accountNode = findAccountNode(accountID: accountID),
let account = accountNode.representedObject as? Account,
let webFeed = account.existingWebFeed(withWebFeedID: webFeedID) else {
let webFeed = account.existingFeed(withWebFeedID: webFeedID) else {
return
}
@@ -2204,7 +2204,7 @@ private extension SceneCoordinator {
return
}
guard let webFeed = account.existingWebFeed(withWebFeedID: webFeedID) else {
guard let webFeed = account.existingFeed(withWebFeedID: webFeedID) else {
return
}

View File

@@ -34,7 +34,7 @@ struct NewArticleNotificationsView: View, Logging {
return
}
activeAccounts.forEach { account in
for feed in Array(account.flattenedWebFeeds()) {
for feed in Array(account.flattenedFeeds()) {
if let feedURLHost = URL(string: feed.url)?.host {
if faviconHost == feedURLHost {
feed.objectWillChange.send()
@@ -50,7 +50,7 @@ struct NewArticleNotificationsView: View, Logging {
}
private func sortedWebFeedsForAccount(_ account: Account) -> [WebFeed] {
return Array(account.flattenedWebFeeds()).sorted(by: { $0.nameForDisplay.caseInsensitiveCompare($1.nameForDisplay) == .orderedAscending })
return Array(account.flattenedFeeds()).sorted(by: { $0.nameForDisplay.caseInsensitiveCompare($1.nameForDisplay) == .orderedAscending })
}