mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Continue renaming webFeed to just feed.
This commit is contained in:
@@ -55,7 +55,7 @@ public enum FetchType {
|
||||
case unread(_: Int? = nil)
|
||||
case today(_: Int? = nil)
|
||||
case folder(Folder, Bool)
|
||||
case webFeed(Feed)
|
||||
case feed(Feed)
|
||||
case articleIDs(Set<String>)
|
||||
case search(String)
|
||||
case searchWithArticleIDs(String, Set<String>)
|
||||
@@ -72,7 +72,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
public static let articleIDs = "articleIDs" // StatusesDidChange
|
||||
public static let statusKey = "statusKey" // StatusesDidChange
|
||||
public static let statusFlag = "statusFlag" // StatusesDidChange
|
||||
public static let webFeeds = "webFeeds" // AccountDidDownloadArticles, StatusesDidChange
|
||||
public static let feeds = "feeds" // AccountDidDownloadArticles, StatusesDidChange
|
||||
public static let syncErrors = "syncErrors" // AccountsDidFailToSyncWithErrors
|
||||
}
|
||||
|
||||
@@ -684,8 +684,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
} else {
|
||||
return try fetchArticles(folder: folder)
|
||||
}
|
||||
case .webFeed(let webFeed):
|
||||
return try fetchArticles(feed: webFeed)
|
||||
case .feed(let feed):
|
||||
return try fetchArticles(feed: feed)
|
||||
case .articleIDs(let articleIDs):
|
||||
return try fetchArticles(articleIDs: articleIDs)
|
||||
case .search(let searchString):
|
||||
@@ -709,8 +709,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
} else {
|
||||
return fetchArticlesAsync(folder: folder, completion)
|
||||
}
|
||||
case .webFeed(let webFeed):
|
||||
fetchArticlesAsync(feed: webFeed, completion)
|
||||
case .feed(let feed):
|
||||
fetchArticlesAsync(feed:feed, completion)
|
||||
case .articleIDs(let articleIDs):
|
||||
fetchArticlesAsync(articleIDs: articleIDs, completion)
|
||||
case .search(let searchString):
|
||||
@@ -749,8 +749,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
return unreadCounts[feed.feedID] ?? 0
|
||||
}
|
||||
|
||||
public func setUnreadCount(_ unreadCount: Int, for webFeed: Feed) {
|
||||
unreadCounts[webFeed.feedID] = unreadCount
|
||||
public func setUnreadCount(_ unreadCount: Int, for feed: Feed) {
|
||||
unreadCounts[feed.feedID] = unreadCount
|
||||
}
|
||||
|
||||
public func structureDidChange() {
|
||||
@@ -1285,7 +1285,7 @@ private extension Account {
|
||||
// which will update their own unread counts.
|
||||
updateUnreadCounts(for: feeds)
|
||||
|
||||
NotificationCenter.default.post(name: .StatusesDidChange, object: self, userInfo: [UserInfoKey.statuses: statuses, UserInfoKey.articles: articles, UserInfoKey.articleIDs: articleIDs, UserInfoKey.webFeeds: feeds])
|
||||
NotificationCenter.default.post(name: .StatusesDidChange, object: self, userInfo: [UserInfoKey.statuses: statuses, UserInfoKey.articles: articles, UserInfoKey.articleIDs: articleIDs, UserInfoKey.feeds: feeds])
|
||||
}
|
||||
|
||||
func noteStatusesForArticleIDsDidChange(articleIDs: Set<String>, statusKey: ArticleStatus.Key, flag: Bool) {
|
||||
@@ -1398,7 +1398,7 @@ private extension Account {
|
||||
}
|
||||
|
||||
if shouldSendNotification {
|
||||
userInfo[UserInfoKey.webFeeds] = feeds
|
||||
userInfo[UserInfoKey.feeds] = feeds
|
||||
NotificationCenter.default.post(name: .AccountDidDownloadArticles, object: self, userInfo: userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,15 +200,15 @@ public final class AccountManager: UnreadCountProvider {
|
||||
return nil
|
||||
}
|
||||
|
||||
public func existingFeed(with feedID: ItemIdentifier) -> FeedProtocol? {
|
||||
switch feedID {
|
||||
public func existingFeed(with itemID: ItemIdentifier) -> FeedProtocol? {
|
||||
switch itemID {
|
||||
case .folder(let accountID, let folderName):
|
||||
if let account = existingAccount(with: accountID) {
|
||||
return account.existingFolder(with: folderName)
|
||||
}
|
||||
case .feed(let accountID, let webFeedID):
|
||||
case .feed(let accountID, let feedID):
|
||||
if let account = existingAccount(with: accountID) {
|
||||
return account.existingFeed(withFeedID: webFeedID)
|
||||
return account.existingFeed(withFeedID: feedID)
|
||||
}
|
||||
default:
|
||||
break
|
||||
@@ -335,8 +335,8 @@ public final class AccountManager: UnreadCountProvider {
|
||||
|
||||
for account in accounts {
|
||||
if account.type == .cloudKit || account.type == .onMyMac {
|
||||
for webfeed in account.flattenedFeeds() {
|
||||
if let components = URLComponents(string: webfeed.url), let host = components.host {
|
||||
for feed in account.flattenedFeeds() {
|
||||
if let components = URLComponents(string: feed.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,8 +355,8 @@ public final class AccountManager: UnreadCountProvider {
|
||||
|
||||
for account in accounts {
|
||||
if account.type == .cloudKit || account.type == .onMyMac {
|
||||
for webfeed in account.flattenedFeeds() {
|
||||
if feedRequiresRedditAPI(webfeed) {
|
||||
for feed in account.flattenedFeeds() {
|
||||
if feedRequiresRedditAPI(feed) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public protocol ArticleFetcher {
|
||||
extension Feed: ArticleFetcher {
|
||||
|
||||
public func fetchArticles() throws -> Set<Article> {
|
||||
return try account?.fetchArticles(.webFeed(self)) ?? Set<Article>()
|
||||
return try account?.fetchArticles(.feed(self)) ?? Set<Article>()
|
||||
}
|
||||
|
||||
public func fetchArticlesAsync(_ completion: @escaping ArticleSetResultBlock) {
|
||||
@@ -31,7 +31,7 @@ extension Feed: ArticleFetcher {
|
||||
completion(.success(Set<Article>()))
|
||||
return
|
||||
}
|
||||
account.fetchArticlesAsync(.webFeed(self), completion)
|
||||
account.fetchArticlesAsync(.feed(self), completion)
|
||||
}
|
||||
|
||||
public func fetchUnreadArticles() throws -> Set<Article> {
|
||||
@@ -48,7 +48,7 @@ extension Feed: ArticleFetcher {
|
||||
completion(.success(Set<Article>()))
|
||||
return
|
||||
}
|
||||
account.fetchArticlesAsync(.webFeed(self)) { articleSetResult in
|
||||
account.fetchArticlesAsync(.feed(self)) { articleSetResult in
|
||||
switch articleSetResult {
|
||||
case .success(let articles):
|
||||
completion(.success(articles.unreadArticles()))
|
||||
|
||||
@@ -299,21 +299,21 @@ final class CloudKitAccountDelegate: AccountDelegate, Logging {
|
||||
func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(2)
|
||||
accountZone.findWebFeedExternalIDs(for: folder) { result in
|
||||
accountZone.findFeedExternalIDs(for: folder) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success(let webFeedExternalIDs):
|
||||
case .success(let feedExternalIDs):
|
||||
|
||||
let webFeeds = webFeedExternalIDs.compactMap { account.existingFeed(withExternalID: $0) }
|
||||
let feeds = feedExternalIDs.compactMap { account.existingFeed(withExternalID: $0) }
|
||||
let group = DispatchGroup()
|
||||
var errorOccurred = false
|
||||
|
||||
for webFeed in webFeeds {
|
||||
for feed in feeds {
|
||||
group.enter()
|
||||
self.removeFeedFromCloud(for: account, with: webFeed, from: folder) { [weak self] result in
|
||||
self.removeFeedFromCloud(for: account, with: feed, 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)")
|
||||
self?.logger.error("Remove folder, remove feed error: \(error.localizedDescription, privacy: .public)")
|
||||
errorOccurred = true
|
||||
}
|
||||
}
|
||||
@@ -485,8 +485,8 @@ private extension CloudKitAccountDelegate {
|
||||
accountZone.fetchChangesInZone() { result in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
let webFeeds = account.flattenedFeeds()
|
||||
self.refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
|
||||
let feeds = account.flattenedFeeds()
|
||||
self.refreshProgress.addToNumberOfTasksAndRemaining(feeds.count)
|
||||
|
||||
switch result {
|
||||
case .success:
|
||||
@@ -496,7 +496,7 @@ private extension CloudKitAccountDelegate {
|
||||
switch result {
|
||||
case .success:
|
||||
|
||||
self.combinedRefresh(account, webFeeds) { result in
|
||||
self.combinedRefresh(account, feeds) { result in
|
||||
self.refreshProgress.clear()
|
||||
switch result {
|
||||
case .success:
|
||||
@@ -519,9 +519,9 @@ private extension CloudKitAccountDelegate {
|
||||
|
||||
func standardRefreshAll(for account: Account, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
let intialWebFeedsCount = account.flattenedFeeds().count
|
||||
let intialFeedsCount = account.flattenedFeeds().count
|
||||
refreshProgress.isIndeterminate = true
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(3 + intialWebFeedsCount)
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(3 + intialFeedsCount)
|
||||
|
||||
func fail(_ error: Error) {
|
||||
self.processAccountError(account, error)
|
||||
@@ -534,15 +534,15 @@ private extension CloudKitAccountDelegate {
|
||||
case .success:
|
||||
|
||||
self.refreshProgress.completeTask()
|
||||
let webFeeds = account.flattenedFeeds()
|
||||
self.refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count - intialWebFeedsCount)
|
||||
let feeds = account.flattenedFeeds()
|
||||
self.refreshProgress.addToNumberOfTasksAndRemaining(feeds.count - intialFeedsCount)
|
||||
|
||||
self.refreshArticleStatus(for: account) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
self.refreshProgress.completeTask()
|
||||
self.refreshProgress.isIndeterminate = false
|
||||
self.combinedRefresh(account, webFeeds) { result in
|
||||
self.combinedRefresh(account, feeds) { result in
|
||||
self.sendArticleStatus(for: account, showProgress: true) { _ in
|
||||
self.refreshProgress.clear()
|
||||
if case .failure(let error) = result {
|
||||
@@ -565,12 +565,12 @@ private extension CloudKitAccountDelegate {
|
||||
|
||||
}
|
||||
|
||||
func combinedRefresh(_ account: Account, _ webFeeds: Set<Feed>, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
func combinedRefresh(_ account: Account, _ feeds: Set<Feed>, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
group.enter()
|
||||
refresher.refreshFeeds(webFeeds) {
|
||||
refresher.refreshFeeds(feeds) {
|
||||
group.leave()
|
||||
}
|
||||
|
||||
@@ -687,7 +687,7 @@ private extension CloudKitAccountDelegate {
|
||||
}
|
||||
|
||||
func sendNewArticlesToTheCloud(_ account: Account, _ feed: Feed) {
|
||||
account.fetchArticlesAsync(.webFeed(feed)) { result in
|
||||
account.fetchArticlesAsync(.feed(feed)) { result in
|
||||
switch result {
|
||||
case .success(let articles):
|
||||
self.storeArticleChanges(new: articles, updated: Set<Article>(), deleted: Set<Article>()) {
|
||||
@@ -780,11 +780,11 @@ private extension CloudKitAccountDelegate {
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success:
|
||||
guard let webFeedExternalID = feed.externalID else {
|
||||
guard let feedExternalID = feed.externalID else {
|
||||
completion(.success(()))
|
||||
return
|
||||
}
|
||||
self.articlesZone.deleteArticles(webFeedExternalID) { result in
|
||||
self.articlesZone.deleteArticles(feedExternalID) { result in
|
||||
feed.dropConditionalGetInfo()
|
||||
self.refreshProgress.completeTask()
|
||||
completion(result)
|
||||
|
||||
@@ -27,7 +27,7 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
weak var database: CKDatabase?
|
||||
var delegate: CloudKitZoneDelegate?
|
||||
|
||||
struct CloudKitWebFeed {
|
||||
struct CloudKitFeed {
|
||||
static let recordType = "AccountWebFeed"
|
||||
struct Fields {
|
||||
static let url = "url"
|
||||
@@ -58,13 +58,13 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
var feedRecords = [String: CKRecord]()
|
||||
|
||||
func processFeed(feedSpecifier: RSOPMLFeedSpecifier, containerExternalID: String) {
|
||||
if let webFeedRecord = feedRecords[feedSpecifier.feedURL], var containerExternalIDs = webFeedRecord[CloudKitWebFeed.Fields.containerExternalIDs] as? [String] {
|
||||
if let feedRecord = feedRecords[feedSpecifier.feedURL], var containerExternalIDs = feedRecord[CloudKitFeed.Fields.containerExternalIDs] as? [String] {
|
||||
containerExternalIDs.append(containerExternalID)
|
||||
webFeedRecord[CloudKitWebFeed.Fields.containerExternalIDs] = containerExternalIDs
|
||||
feedRecord[CloudKitFeed.Fields.containerExternalIDs] = containerExternalIDs
|
||||
} else {
|
||||
let webFeedRecord = newWebFeedCKRecord(feedSpecifier: feedSpecifier, containerExternalID: containerExternalID)
|
||||
records.append(webFeedRecord)
|
||||
feedRecords[feedSpecifier.feedURL] = webFeedRecord
|
||||
let feedRecord = newFeedCKRecord(feedSpecifier: feedSpecifier, containerExternalID: containerExternalID)
|
||||
records.append(feedRecord)
|
||||
feedRecords[feedSpecifier.feedURL] = feedRecord
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,21 +90,21 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
/// Persist a web feed record to iCloud and return the external key
|
||||
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
|
||||
record[CloudKitWebFeed.Fields.name] = name
|
||||
let record = CKRecord(recordType: CloudKitFeed.recordType, recordID: recordID)
|
||||
record[CloudKitFeed.Fields.url] = url
|
||||
record[CloudKitFeed.Fields.name] = name
|
||||
if let editedName = editedName {
|
||||
record[CloudKitWebFeed.Fields.editedName] = editedName
|
||||
record[CloudKitFeed.Fields.editedName] = editedName
|
||||
}
|
||||
if let homePageURL = homePageURL {
|
||||
record[CloudKitWebFeed.Fields.homePageURL] = homePageURL
|
||||
record[CloudKitFeed.Fields.homePageURL] = homePageURL
|
||||
}
|
||||
|
||||
guard let containerExternalID = container.externalID else {
|
||||
completion(.failure(CloudKitZoneError.corruptAccount))
|
||||
return
|
||||
}
|
||||
record[CloudKitWebFeed.Fields.containerExternalIDs] = [containerExternalID]
|
||||
record[CloudKitFeed.Fields.containerExternalIDs] = [containerExternalID]
|
||||
|
||||
save(record) { result in
|
||||
switch result {
|
||||
@@ -117,15 +117,15 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
}
|
||||
|
||||
/// Rename the given web feed
|
||||
func renameFeed(_ webFeed: Feed, editedName: String?, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
guard let externalID = webFeed.externalID else {
|
||||
func renameFeed(_ feed: Feed, editedName: String?, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
guard let externalID = feed.externalID else {
|
||||
completion(.failure(CloudKitZoneError.corruptAccount))
|
||||
return
|
||||
}
|
||||
|
||||
let recordID = CKRecord.ID(recordName: externalID, zoneID: zoneID)
|
||||
let record = CKRecord(recordType: CloudKitWebFeed.recordType, recordID: recordID)
|
||||
record[CloudKitWebFeed.Fields.editedName] = editedName
|
||||
let record = CKRecord(recordType: CloudKitFeed.recordType, recordID: recordID)
|
||||
record[CloudKitFeed.Fields.editedName] = editedName
|
||||
|
||||
save(record) { result in
|
||||
switch result {
|
||||
@@ -138,22 +138,22 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
}
|
||||
|
||||
/// Removes a web feed from a container and optionally deletes it, calling the completion with true if deleted
|
||||
func removeFeed(_ webFeed: Feed, from: Container, completion: @escaping (Result<Bool, Error>) -> Void) {
|
||||
func removeFeed(_ feed: Feed, from: Container, completion: @escaping (Result<Bool, Error>) -> Void) {
|
||||
guard let fromContainerExternalID = from.externalID else {
|
||||
completion(.failure(CloudKitZoneError.corruptAccount))
|
||||
return
|
||||
}
|
||||
|
||||
fetch(externalID: webFeed.externalID) { result in
|
||||
fetch(externalID: feed.externalID) { result in
|
||||
switch result {
|
||||
case .success(let record):
|
||||
|
||||
if let containerExternalIDs = record[CloudKitWebFeed.Fields.containerExternalIDs] as? [String] {
|
||||
if let containerExternalIDs = record[CloudKitFeed.Fields.containerExternalIDs] as? [String] {
|
||||
var containerExternalIDSet = Set(containerExternalIDs)
|
||||
containerExternalIDSet.remove(fromContainerExternalID)
|
||||
|
||||
if containerExternalIDSet.isEmpty {
|
||||
self.delete(externalID: webFeed.externalID) { result in
|
||||
self.delete(externalID: feed.externalID) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(true))
|
||||
@@ -164,7 +164,7 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
|
||||
} else {
|
||||
|
||||
record[CloudKitWebFeed.Fields.containerExternalIDs] = Array(containerExternalIDSet)
|
||||
record[CloudKitFeed.Fields.containerExternalIDs] = Array(containerExternalIDSet)
|
||||
self.save(record) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
@@ -187,20 +187,20 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
}
|
||||
}
|
||||
|
||||
func moveFeed(_ webFeed: Feed, from: Container, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
func moveFeed(_ feed: Feed, 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
|
||||
}
|
||||
|
||||
fetch(externalID: webFeed.externalID) { result in
|
||||
fetch(externalID: feed.externalID) { result in
|
||||
switch result {
|
||||
case .success(let record):
|
||||
if let containerExternalIDs = record[CloudKitWebFeed.Fields.containerExternalIDs] as? [String] {
|
||||
if let containerExternalIDs = record[CloudKitFeed.Fields.containerExternalIDs] as? [String] {
|
||||
var containerExternalIDSet = Set(containerExternalIDs)
|
||||
containerExternalIDSet.remove(fromContainerExternalID)
|
||||
containerExternalIDSet.insert(toContainerExternalID)
|
||||
record[CloudKitWebFeed.Fields.containerExternalIDs] = Array(containerExternalIDSet)
|
||||
record[CloudKitFeed.Fields.containerExternalIDs] = Array(containerExternalIDSet)
|
||||
self.save(record, completion: completion)
|
||||
}
|
||||
case .failure(let error):
|
||||
@@ -209,19 +209,19 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
}
|
||||
}
|
||||
|
||||
func addFeed(_ webFeed: Feed, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
func addFeed(_ feed: Feed, to: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
guard let toContainerExternalID = to.externalID else {
|
||||
completion(.failure(CloudKitZoneError.corruptAccount))
|
||||
return
|
||||
}
|
||||
|
||||
fetch(externalID: webFeed.externalID) { result in
|
||||
fetch(externalID: feed.externalID) { result in
|
||||
switch result {
|
||||
case .success(let record):
|
||||
if let containerExternalIDs = record[CloudKitWebFeed.Fields.containerExternalIDs] as? [String] {
|
||||
if let containerExternalIDs = record[CloudKitFeed.Fields.containerExternalIDs] as? [String] {
|
||||
var containerExternalIDSet = Set(containerExternalIDs)
|
||||
containerExternalIDSet.insert(toContainerExternalID)
|
||||
record[CloudKitWebFeed.Fields.containerExternalIDs] = Array(containerExternalIDSet)
|
||||
record[CloudKitFeed.Fields.containerExternalIDs] = Array(containerExternalIDSet)
|
||||
self.save(record, completion: completion)
|
||||
}
|
||||
case .failure(let error):
|
||||
@@ -230,20 +230,20 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
}
|
||||
}
|
||||
|
||||
func findWebFeedExternalIDs(for folder: Folder, completion: @escaping (Result<[String], Error>) -> Void) {
|
||||
func findFeedExternalIDs(for folder: Folder, completion: @escaping (Result<[String], Error>) -> Void) {
|
||||
guard let folderExternalID = folder.externalID else {
|
||||
completion(.failure(CloudKitAccountZoneError.unknown))
|
||||
return
|
||||
}
|
||||
|
||||
let predicate = NSPredicate(format: "containerExternalIDs CONTAINS %@", folderExternalID)
|
||||
let ckQuery = CKQuery(recordType: CloudKitWebFeed.recordType, predicate: predicate)
|
||||
let ckQuery = CKQuery(recordType: CloudKitFeed.recordType, predicate: predicate)
|
||||
|
||||
query(ckQuery) { result in
|
||||
switch result {
|
||||
case .success(let records):
|
||||
let webFeedExternalIds = records.map { $0.externalID }
|
||||
completion(.success(webFeedExternalIds))
|
||||
let feedExternalIds = records.map { $0.externalID }
|
||||
completion(.success(feedExternalIds))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
@@ -320,16 +320,16 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
|
||||
private extension CloudKitAccountZone {
|
||||
|
||||
func newWebFeedCKRecord(feedSpecifier: RSOPMLFeedSpecifier, containerExternalID: String) -> CKRecord {
|
||||
let record = CKRecord(recordType: CloudKitWebFeed.recordType, recordID: generateRecordID())
|
||||
record[CloudKitWebFeed.Fields.url] = feedSpecifier.feedURL
|
||||
func newFeedCKRecord(feedSpecifier: RSOPMLFeedSpecifier, containerExternalID: String) -> CKRecord {
|
||||
let record = CKRecord(recordType: CloudKitFeed.recordType, recordID: generateRecordID())
|
||||
record[CloudKitFeed.Fields.url] = feedSpecifier.feedURL
|
||||
if let editedName = feedSpecifier.title {
|
||||
record[CloudKitWebFeed.Fields.editedName] = editedName
|
||||
record[CloudKitFeed.Fields.editedName] = editedName
|
||||
}
|
||||
if let homePageURL = feedSpecifier.homePageURL {
|
||||
record[CloudKitWebFeed.Fields.homePageURL] = homePageURL
|
||||
record[CloudKitFeed.Fields.homePageURL] = homePageURL
|
||||
}
|
||||
record[CloudKitWebFeed.Fields.containerExternalIDs] = [containerExternalID]
|
||||
record[CloudKitFeed.Fields.containerExternalIDs] = [containerExternalID]
|
||||
return record
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@ import Articles
|
||||
|
||||
class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
|
||||
private typealias UnclaimedWebFeed = (url: URL, name: String?, editedName: String?, homePageURL: String?, webFeedExternalID: String)
|
||||
private var newUnclaimedWebFeeds = [String: [UnclaimedWebFeed]]()
|
||||
private var existingUnclaimedWebFeeds = [String: [Feed]]()
|
||||
private typealias UnclaimedFeed = (url: URL, name: String?, editedName: String?, homePageURL: String?, feedExternalID: String)
|
||||
private var newUnclaimedFeeds = [String: [UnclaimedFeed]]()
|
||||
private var existingUnclaimedFeeds = [String: [Feed]]()
|
||||
|
||||
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
|
||||
|
||||
@@ -34,7 +34,7 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
func cloudKitWasChanged(updated: [CKRecord], deleted: [CloudKitRecordKey], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
for deletedRecordKey in deleted {
|
||||
switch deletedRecordKey.recordType {
|
||||
case CloudKitAccountZone.CloudKitWebFeed.recordType:
|
||||
case CloudKitAccountZone.CloudKitFeed.recordType:
|
||||
removeFeed(deletedRecordKey.recordID.externalID)
|
||||
case CloudKitAccountZone.CloudKitContainer.recordType:
|
||||
removeContainer(deletedRecordKey.recordID.externalID)
|
||||
@@ -45,8 +45,8 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
|
||||
for changedRecord in updated {
|
||||
switch changedRecord.recordType {
|
||||
case CloudKitAccountZone.CloudKitWebFeed.recordType:
|
||||
addOrUpdateWebFeed(changedRecord)
|
||||
case CloudKitAccountZone.CloudKitFeed.recordType:
|
||||
addOrUpdateFeed(changedRecord)
|
||||
case CloudKitAccountZone.CloudKitContainer.recordType:
|
||||
addOrUpdateContainer(changedRecord)
|
||||
default:
|
||||
@@ -57,36 +57,36 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
completion(.success(()))
|
||||
}
|
||||
|
||||
func addOrUpdateWebFeed(_ record: CKRecord) {
|
||||
func addOrUpdateFeed(_ record: CKRecord) {
|
||||
guard let account = account,
|
||||
let urlString = record[CloudKitAccountZone.CloudKitWebFeed.Fields.url] as? String,
|
||||
let containerExternalIDs = record[CloudKitAccountZone.CloudKitWebFeed.Fields.containerExternalIDs] as? [String],
|
||||
let urlString = record[CloudKitAccountZone.CloudKitFeed.Fields.url] as? String,
|
||||
let containerExternalIDs = record[CloudKitAccountZone.CloudKitFeed.Fields.containerExternalIDs] as? [String],
|
||||
let url = URL(string: urlString) else {
|
||||
return
|
||||
}
|
||||
|
||||
let name = record[CloudKitAccountZone.CloudKitWebFeed.Fields.name] as? String
|
||||
let editedName = record[CloudKitAccountZone.CloudKitWebFeed.Fields.editedName] as? String
|
||||
let homePageURL = record[CloudKitAccountZone.CloudKitWebFeed.Fields.homePageURL] as? String
|
||||
let name = record[CloudKitAccountZone.CloudKitFeed.Fields.name] as? String
|
||||
let editedName = record[CloudKitAccountZone.CloudKitFeed.Fields.editedName] as? String
|
||||
let homePageURL = record[CloudKitAccountZone.CloudKitFeed.Fields.homePageURL] as? String
|
||||
|
||||
if let webFeed = account.existingFeed(withExternalID: record.externalID) {
|
||||
updateWebFeed(webFeed, name: name, editedName: editedName, homePageURL: homePageURL, containerExternalIDs: containerExternalIDs)
|
||||
if let feed = account.existingFeed(withExternalID: record.externalID) {
|
||||
updateFeed(feed, name: name, editedName: editedName, homePageURL: homePageURL, containerExternalIDs: containerExternalIDs)
|
||||
} else {
|
||||
for containerExternalID in containerExternalIDs {
|
||||
if let container = account.existingContainer(withExternalID: containerExternalID) {
|
||||
createFeedIfNecessary(url: url, name: name, editedName: editedName, homePageURL: homePageURL, webFeedExternalID: record.externalID, container: container)
|
||||
createFeedIfNecessary(url: url, name: name, editedName: editedName, homePageURL: homePageURL, feedExternalID: record.externalID, container: container)
|
||||
} else {
|
||||
addNewUnclaimedWebFeed(url: url, name: name, editedName: editedName, homePageURL: homePageURL, webFeedExternalID: record.externalID, containerExternalID: containerExternalID)
|
||||
addNewUnclaimedFeed(url: url, name: name, editedName: editedName, homePageURL: homePageURL, feedExternalID: record.externalID, containerExternalID: containerExternalID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeFeed(_ externalID: String) {
|
||||
if let webFeed = account?.existingFeed(withExternalID: externalID), let containers = account?.existingContainers(withFeed: webFeed) {
|
||||
if let feed = account?.existingFeed(withExternalID: externalID), let containers = account?.existingContainers(withFeed: feed) {
|
||||
containers.forEach {
|
||||
webFeed.dropConditionalGetInfo()
|
||||
$0.removeFeed(webFeed)
|
||||
feed.dropConditionalGetInfo()
|
||||
$0.removeFeed(feed)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,24 +109,24 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
|
||||
guard let container = folder, let containerExternalID = container.externalID else { return }
|
||||
|
||||
if let newUnclaimedWebFeeds = newUnclaimedWebFeeds[containerExternalID] {
|
||||
for newUnclaimedWebFeed in newUnclaimedWebFeeds {
|
||||
createFeedIfNecessary(url: newUnclaimedWebFeed.url,
|
||||
name: newUnclaimedWebFeed.name,
|
||||
editedName: newUnclaimedWebFeed.editedName,
|
||||
homePageURL: newUnclaimedWebFeed.homePageURL,
|
||||
webFeedExternalID: newUnclaimedWebFeed.webFeedExternalID,
|
||||
if let newUnclaimedFeeds = newUnclaimedFeeds[containerExternalID] {
|
||||
for newUnclaimedFeed in newUnclaimedFeeds {
|
||||
createFeedIfNecessary(url: newUnclaimedFeed.url,
|
||||
name: newUnclaimedFeed.name,
|
||||
editedName: newUnclaimedFeed.editedName,
|
||||
homePageURL: newUnclaimedFeed.homePageURL,
|
||||
feedExternalID: newUnclaimedFeed.feedExternalID,
|
||||
container: container)
|
||||
}
|
||||
|
||||
self.newUnclaimedWebFeeds.removeValue(forKey: containerExternalID)
|
||||
self.newUnclaimedFeeds.removeValue(forKey: containerExternalID)
|
||||
}
|
||||
|
||||
if let existingUnclaimedWebFeeds = existingUnclaimedWebFeeds[containerExternalID] {
|
||||
for existingUnclaimedWebFeed in existingUnclaimedWebFeeds {
|
||||
container.addFeed(existingUnclaimedWebFeed)
|
||||
if let existingUnclaimedFeeds = existingUnclaimedFeeds[containerExternalID] {
|
||||
for existingUnclaimedFeed in existingUnclaimedFeeds {
|
||||
container.addFeed(existingUnclaimedFeed)
|
||||
}
|
||||
self.existingUnclaimedWebFeeds.removeValue(forKey: containerExternalID)
|
||||
self.existingUnclaimedFeeds.removeValue(forKey: containerExternalID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,14 +140,14 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
|
||||
private extension CloudKitAcountZoneDelegate {
|
||||
|
||||
func updateWebFeed(_ webFeed: Feed, name: String?, editedName: String?, homePageURL: String?, containerExternalIDs: [String]) {
|
||||
func updateFeed(_ feed: Feed, name: String?, editedName: String?, homePageURL: String?, containerExternalIDs: [String]) {
|
||||
guard let account = account else { return }
|
||||
|
||||
webFeed.name = name
|
||||
webFeed.editedName = editedName
|
||||
webFeed.homePageURL = homePageURL
|
||||
feed.name = name
|
||||
feed.editedName = editedName
|
||||
feed.homePageURL = homePageURL
|
||||
|
||||
let existingContainers = account.existingContainers(withFeed: webFeed)
|
||||
let existingContainers = account.existingContainers(withFeed: feed)
|
||||
let existingContainerExternalIds = existingContainers.compactMap { $0.externalID }
|
||||
|
||||
let diff = containerExternalIDs.difference(from: existingContainerExternalIds)
|
||||
@@ -156,50 +156,50 @@ private extension CloudKitAcountZoneDelegate {
|
||||
switch change {
|
||||
case .remove(_, let externalID, _):
|
||||
if let container = existingContainers.first(where: { $0.externalID == externalID }) {
|
||||
container.removeFeed(webFeed)
|
||||
container.removeFeed(feed)
|
||||
}
|
||||
case .insert(_, let externalID, _):
|
||||
if let container = account.existingContainer(withExternalID: externalID) {
|
||||
container.addFeed(webFeed)
|
||||
container.addFeed(feed)
|
||||
} else {
|
||||
addExistingUnclaimedWebFeed(webFeed, containerExternalID: externalID)
|
||||
addExistingUnclaimedFeed(feed, containerExternalID: externalID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createFeedIfNecessary(url: URL, name: String?, editedName: String?, homePageURL: String?, webFeedExternalID: String, container: Container) {
|
||||
func createFeedIfNecessary(url: URL, name: String?, editedName: String?, homePageURL: String?, feedExternalID: String, container: Container) {
|
||||
guard let account = account else { return }
|
||||
|
||||
if account.existingFeed(withExternalID: webFeedExternalID) != nil {
|
||||
if account.existingFeed(withExternalID: feedExternalID) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
let webFeed = account.createFeed(with: name, url: url.absoluteString, feedID: url.absoluteString, homePageURL: homePageURL)
|
||||
webFeed.editedName = editedName
|
||||
webFeed.externalID = webFeedExternalID
|
||||
container.addFeed(webFeed)
|
||||
let feed = account.createFeed(with: name, url: url.absoluteString, feedID: url.absoluteString, homePageURL: homePageURL)
|
||||
feed.editedName = editedName
|
||||
feed.externalID = feedExternalID
|
||||
container.addFeed(feed)
|
||||
}
|
||||
|
||||
func addNewUnclaimedWebFeed(url: URL, name: String?, editedName: String?, homePageURL: String?, webFeedExternalID: String, containerExternalID: String) {
|
||||
if var unclaimedWebFeeds = self.newUnclaimedWebFeeds[containerExternalID] {
|
||||
unclaimedWebFeeds.append(UnclaimedWebFeed(url: url, name: name, editedName: editedName, homePageURL: homePageURL, webFeedExternalID: webFeedExternalID))
|
||||
self.newUnclaimedWebFeeds[containerExternalID] = unclaimedWebFeeds
|
||||
func addNewUnclaimedFeed(url: URL, name: String?, editedName: String?, homePageURL: String?, feedExternalID: String, containerExternalID: String) {
|
||||
if var unclaimedFeeds = self.newUnclaimedFeeds[containerExternalID] {
|
||||
unclaimedFeeds.append(UnclaimedFeed(url: url, name: name, editedName: editedName, homePageURL: homePageURL, feedExternalID: feedExternalID))
|
||||
self.newUnclaimedFeeds[containerExternalID] = unclaimedFeeds
|
||||
} else {
|
||||
var unclaimedWebFeeds = [UnclaimedWebFeed]()
|
||||
unclaimedWebFeeds.append(UnclaimedWebFeed(url: url, name: name, editedName: editedName, homePageURL: homePageURL, webFeedExternalID: webFeedExternalID))
|
||||
self.newUnclaimedWebFeeds[containerExternalID] = unclaimedWebFeeds
|
||||
var unclaimedFeeds = [UnclaimedFeed]()
|
||||
unclaimedFeeds.append(UnclaimedFeed(url: url, name: name, editedName: editedName, homePageURL: homePageURL, feedExternalID: feedExternalID))
|
||||
self.newUnclaimedFeeds[containerExternalID] = unclaimedFeeds
|
||||
}
|
||||
}
|
||||
|
||||
func addExistingUnclaimedWebFeed(_ webFeed: Feed, containerExternalID: String) {
|
||||
if var unclaimedWebFeeds = self.existingUnclaimedWebFeeds[containerExternalID] {
|
||||
unclaimedWebFeeds.append(webFeed)
|
||||
self.existingUnclaimedWebFeeds[containerExternalID] = unclaimedWebFeeds
|
||||
func addExistingUnclaimedFeed(_ feed: Feed, containerExternalID: String) {
|
||||
if var unclaimedFeeds = self.existingUnclaimedFeeds[containerExternalID] {
|
||||
unclaimedFeeds.append(feed)
|
||||
self.existingUnclaimedFeeds[containerExternalID] = unclaimedFeeds
|
||||
} else {
|
||||
var unclaimedWebFeeds = [Feed]()
|
||||
unclaimedWebFeeds.append(webFeed)
|
||||
self.existingUnclaimedWebFeeds[containerExternalID] = unclaimedWebFeeds
|
||||
var unclaimedFeeds = [Feed]()
|
||||
unclaimedFeeds.append(feed)
|
||||
self.existingUnclaimedFeeds[containerExternalID] = unclaimedFeeds
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ final class CloudKitArticlesZone: CloudKitZone {
|
||||
static let recordType = "Article"
|
||||
struct Fields {
|
||||
static let articleStatus = "articleStatus"
|
||||
static let webFeedURL = "webFeedURL"
|
||||
static let feedURL = "webFeedURL"
|
||||
static let uniqueID = "uniqueID"
|
||||
static let title = "title"
|
||||
static let contentHTML = "contentHTML"
|
||||
@@ -49,7 +49,7 @@ final class CloudKitArticlesZone: CloudKitZone {
|
||||
struct CloudKitArticleStatus {
|
||||
static let recordType = "ArticleStatus"
|
||||
struct Fields {
|
||||
static let webFeedExternalID = "webFeedExternalID"
|
||||
static let feedExternalID = "webFeedExternalID"
|
||||
static let read = "read"
|
||||
static let starred = "starred"
|
||||
}
|
||||
@@ -82,8 +82,8 @@ final class CloudKitArticlesZone: CloudKitZone {
|
||||
}
|
||||
}
|
||||
|
||||
func deleteArticles(_ webFeedExternalID: String, completion: @escaping ((Result<Void, Error>) -> Void)) {
|
||||
let predicate = NSPredicate(format: "webFeedExternalID = %@", webFeedExternalID)
|
||||
func deleteArticles(_ feedExternalID: String, completion: @escaping ((Result<Void, Error>) -> Void)) {
|
||||
let predicate = NSPredicate(format: "webFeedExternalID = %@", feedExternalID)
|
||||
let ckQuery = CKQuery(recordType: CloudKitArticleStatus.recordType, predicate: predicate)
|
||||
delete(ckQuery: ckQuery, completion: completion)
|
||||
}
|
||||
@@ -166,8 +166,8 @@ private extension CloudKitArticlesZone {
|
||||
func makeStatusRecord(_ article: Article) -> CKRecord {
|
||||
let recordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: zoneID)
|
||||
let record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID)
|
||||
if let webFeedExternalID = article.feed?.externalID {
|
||||
record[CloudKitArticleStatus.Fields.webFeedExternalID] = webFeedExternalID
|
||||
if let feedExternalID = article.feed?.externalID {
|
||||
record[CloudKitArticleStatus.Fields.feedExternalID] = feedExternalID
|
||||
}
|
||||
record[CloudKitArticleStatus.Fields.read] = article.status.read ? "1" : "0"
|
||||
record[CloudKitArticleStatus.Fields.starred] = article.status.starred ? "1" : "0"
|
||||
@@ -178,8 +178,8 @@ private extension CloudKitArticlesZone {
|
||||
let recordID = CKRecord.ID(recordName: statusID(statusUpdate.articleID), zoneID: zoneID)
|
||||
let record = CKRecord(recordType: CloudKitArticleStatus.recordType, recordID: recordID)
|
||||
|
||||
if let webFeedExternalID = statusUpdate.article?.feed?.externalID {
|
||||
record[CloudKitArticleStatus.Fields.webFeedExternalID] = webFeedExternalID
|
||||
if let feedExternalID = statusUpdate.article?.feed?.externalID {
|
||||
record[CloudKitArticleStatus.Fields.feedExternalID] = feedExternalID
|
||||
}
|
||||
|
||||
record[CloudKitArticleStatus.Fields.read] = statusUpdate.isRead ? "1" : "0"
|
||||
@@ -194,7 +194,7 @@ private extension CloudKitArticlesZone {
|
||||
|
||||
let articleStatusRecordID = CKRecord.ID(recordName: statusID(article.articleID), zoneID: zoneID)
|
||||
record[CloudKitArticle.Fields.articleStatus] = CKRecord.Reference(recordID: articleStatusRecordID, action: .deleteSelf)
|
||||
record[CloudKitArticle.Fields.webFeedURL] = article.feed?.url
|
||||
record[CloudKitArticle.Fields.feedURL] = article.feed?.url
|
||||
record[CloudKitArticle.Fields.uniqueID] = article.uniqueID
|
||||
record[CloudKitArticle.Fields.title] = article.title
|
||||
record[CloudKitArticle.Fields.contentHTML] = article.contentHTML
|
||||
|
||||
@@ -147,9 +147,9 @@ private extension CloudKitArticlesZoneDelegate {
|
||||
let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { item in item.feedURL } ).mapValues { Set($0) }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
for (webFeedID, parsedItems) in feedIDsAndItems {
|
||||
for (feedID, parsedItems) in feedIDsAndItems {
|
||||
group.enter()
|
||||
self.account?.update(webFeedID, with: parsedItems, deleteOlder: false) { result in
|
||||
self.account?.update(feedID, with: parsedItems, deleteOlder: false) { result in
|
||||
switch result {
|
||||
case .success(let articleChanges):
|
||||
guard let deletes = articleChanges.deletedArticles, !deletes.isEmpty else {
|
||||
@@ -203,7 +203,7 @@ private extension CloudKitArticlesZoneDelegate {
|
||||
}
|
||||
|
||||
guard let uniqueID = articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.uniqueID] as? String,
|
||||
let webFeedURL = articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.webFeedURL] as? String else {
|
||||
let feedURL = articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.feedURL] as? String else {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ private extension CloudKitArticlesZoneDelegate {
|
||||
|
||||
let parsedItem = ParsedItem(syncServiceID: nil,
|
||||
uniqueID: uniqueID,
|
||||
feedURL: webFeedURL,
|
||||
feedURL: feedURL,
|
||||
url: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.url] as? String,
|
||||
externalURL: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.externalURL] as? String,
|
||||
title: articleRecord[CloudKitArticlesZone.CloudKitArticle.Fields.title] as? String,
|
||||
|
||||
@@ -357,7 +357,7 @@ final class FeedbinAPICaller: NSObject {
|
||||
|
||||
}
|
||||
|
||||
func createTagging(webFeedID: Int, name: String, completion: @escaping (Result<Int, Error>) -> Void) {
|
||||
func createTagging(feedID: Int, name: String, completion: @escaping (Result<Int, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("taggings.json")
|
||||
var request = URLRequest(url: callURL, credentials: credentials)
|
||||
@@ -365,7 +365,7 @@ final class FeedbinAPICaller: NSObject {
|
||||
|
||||
let payload: Data
|
||||
do {
|
||||
payload = try JSONEncoder().encode(FeedbinCreateTagging(feedID: webFeedID, name: name))
|
||||
payload = try JSONEncoder().encode(FeedbinCreateTagging(feedID: feedID, name: name))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
return
|
||||
|
||||
@@ -470,9 +470,9 @@ final class FeedbinAccountDelegate: AccountDelegate, Logging {
|
||||
|
||||
func addFeed(for account: Account, with feed: Feed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
if let folder = container as? Folder, let webFeedID = Int(feed.feedID) {
|
||||
if let folder = container as? Folder, let feedID = Int(feed.feedID) {
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||
caller.createTagging(webFeedID: webFeedID, name: folder.name ?? "") { result in
|
||||
caller.createTagging(feedID: feedID, name: folder.name ?? "") { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success(let taggingID):
|
||||
|
||||
@@ -19,7 +19,7 @@ struct FeedlyEntryParser {
|
||||
return entry.id
|
||||
}
|
||||
|
||||
/// When ingesting articles, the feedURL must match a feed's `webFeedID` for the article to be reachable between it and its matching feed. It reminds me of a foreign key.
|
||||
/// When ingesting articles, the feedURL must match a feed's `feedID` for the article to be reachable between it and its matching feed. It reminds me of a foreign key.
|
||||
var feedUrl: String? {
|
||||
guard let id = entry.origin?.streamId else {
|
||||
// At this point, check Feedly's API isn't glitching or the response has not changed structure.
|
||||
|
||||
@@ -17,7 +17,7 @@ struct FeedlyFeedParser {
|
||||
return rightToLeftTextSantizer.sanitize(feed.title) ?? ""
|
||||
}
|
||||
|
||||
var webFeedID: String {
|
||||
var feedID: String {
|
||||
return feed.id
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation, Log
|
||||
let parser = FeedlyFeedParser(feed: collectionFeed)
|
||||
let feed = account.createFeed(with: parser.title,
|
||||
url: parser.url,
|
||||
feedID: parser.webFeedID,
|
||||
feedID: parser.feedID,
|
||||
homePageURL: parser.homePageURL)
|
||||
|
||||
// So the same feed isn't created more than once.
|
||||
|
||||
@@ -70,8 +70,8 @@ public enum ItemIdentifier: CustomStringConvertible, Hashable, Equatable {
|
||||
guard let id = userInfo["id"] as? String else { return nil }
|
||||
self = ItemIdentifier.script(id)
|
||||
case "feed":
|
||||
guard let accountID = userInfo["accountID"] as? String, let webFeedID = userInfo["feedID"] as? String else { return nil }
|
||||
self = ItemIdentifier.feed(accountID, webFeedID)
|
||||
guard let accountID = userInfo["accountID"] as? String, let feedID = userInfo["feedID"] as? String else { return nil }
|
||||
self = ItemIdentifier.feed(accountID, feedID)
|
||||
case "folder":
|
||||
guard let accountID = userInfo["accountID"] as? String, let folderName = userInfo["folderName"] as? String else { return nil }
|
||||
self = ItemIdentifier.folder(accountID, folderName)
|
||||
|
||||
@@ -47,13 +47,13 @@ final class LocalAccountDelegate: AccountDelegate, Logging {
|
||||
return
|
||||
}
|
||||
|
||||
let webFeeds = account.flattenedFeeds()
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(webFeeds.count)
|
||||
let feeds = account.flattenedFeeds()
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(feeds.count)
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
group.enter()
|
||||
refresher?.refreshFeeds(webFeeds) {
|
||||
refresher?.refreshFeeds(feeds) {
|
||||
group.leave()
|
||||
}
|
||||
|
||||
|
||||
@@ -100,27 +100,27 @@ extension NewsBlurAccountDelegate {
|
||||
|
||||
// Add any feeds we don't have and update any we do
|
||||
var feedsToAdd = Set<NewsBlurFeed>()
|
||||
feeds.forEach { feed in
|
||||
let subFeedId = String(feed.feedID)
|
||||
feeds.forEach { newsBlurFeed in
|
||||
let subFeedID = String(newsBlurFeed.feedID)
|
||||
|
||||
if let webFeed = account.existingFeed(withFeedID: subFeedId) {
|
||||
webFeed.name = feed.name
|
||||
if let feed = account.existingFeed(withFeedID: subFeedID) {
|
||||
feed.name = newsBlurFeed.name
|
||||
// If the name has been changed on the server remove the locally edited name
|
||||
webFeed.editedName = nil
|
||||
webFeed.homePageURL = feed.homePageURL
|
||||
webFeed.externalID = String(feed.feedID)
|
||||
webFeed.faviconURL = feed.faviconURL
|
||||
feed.editedName = nil
|
||||
feed.homePageURL = newsBlurFeed.homePageURL
|
||||
feed.externalID = String(newsBlurFeed.feedID)
|
||||
feed.faviconURL = newsBlurFeed.faviconURL
|
||||
}
|
||||
else {
|
||||
feedsToAdd.insert(feed)
|
||||
feedsToAdd.insert(newsBlurFeed)
|
||||
}
|
||||
}
|
||||
|
||||
// Actually add feeds all in one go, so we don’t trigger various rebuilding things that Account does.
|
||||
feedsToAdd.forEach { feed in
|
||||
let webFeed = account.createFeed(with: feed.name, url: feed.feedURL, feedID: String(feed.feedID), homePageURL: feed.homePageURL)
|
||||
webFeed.externalID = String(feed.feedID)
|
||||
account.addFeed(webFeed)
|
||||
feedsToAdd.forEach { newsBlurFeed in
|
||||
let feed = account.createFeed(with: newsBlurFeed.name, url: newsBlurFeed.feedURL, feedID: String(newsBlurFeed.feedID), homePageURL: newsBlurFeed.homePageURL)
|
||||
feed.externalID = String(newsBlurFeed.feedID)
|
||||
account.addFeed(feed)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -412,31 +412,31 @@ extension NewsBlurAccountDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func createFeed(account: Account, feed: NewsBlurFeed?, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||
guard let feed = feed else {
|
||||
func createFeed(account: Account, newsBlurFeed: NewsBlurFeed?, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||
guard let newsBlurFeed = newsBlurFeed else {
|
||||
completion(.failure(NewsBlurError.invalidParameter))
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
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
|
||||
let feed = account.createFeed(with: newsBlurFeed.name, url: newsBlurFeed.feedURL, feedID: String(newsBlurFeed.feedID), homePageURL: newsBlurFeed.homePageURL)
|
||||
feed.externalID = String(newsBlurFeed.feedID)
|
||||
feed.faviconURL = newsBlurFeed.faviconURL
|
||||
|
||||
account.addFeed(webFeed, to: container) { result in
|
||||
account.addFeed(feed, to: container) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
if let name = name {
|
||||
account.renameFeed(webFeed, to: name) { result in
|
||||
account.renameFeed(feed, to: name) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
self.initialFeedDownload(account: account, feed: webFeed, completion: completion)
|
||||
self.initialFeedDownload(account: account, feed: feed, completion: completion)
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.initialFeedDownload(account: account, feed: webFeed, completion: completion)
|
||||
self.initialFeedDownload(account: account, feed: feed, completion: completion)
|
||||
}
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
|
||||
@@ -429,7 +429,7 @@ final class NewsBlurAccountDelegate: AccountDelegate, Logging {
|
||||
|
||||
switch result {
|
||||
case .success(let feed):
|
||||
self.createFeed(account: account, feed: feed, name: name, container: container, completion: completion)
|
||||
self.createFeed(account: account, newsBlurFeed: feed, name: name, container: container, completion: completion)
|
||||
case .failure(let error):
|
||||
DispatchQueue.main.async {
|
||||
let wrappedError = AccountError.wrappedError(error: error, account: account)
|
||||
|
||||
@@ -951,7 +951,7 @@ private extension ReaderAPIAccountDelegate {
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(5)
|
||||
|
||||
// Download the initial articles
|
||||
self.caller.retrieveItemIDs(type: .allForFeed, webFeedID: feed.feedID) { result in
|
||||
self.caller.retrieveItemIDs(type: .allForFeed, feedID: feed.feedID) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success(let articleIDs):
|
||||
|
||||
@@ -554,7 +554,7 @@ final class ReaderAPICaller: NSObject {
|
||||
|
||||
}
|
||||
|
||||
func retrieveItemIDs(type: ItemIDType, webFeedID: String? = nil, completion: @escaping ((Result<[String], Error>) -> Void)) {
|
||||
func retrieveItemIDs(type: ItemIDType, feedID: String? = nil, completion: @escaping ((Result<[String], Error>) -> Void)) {
|
||||
guard let baseURL = apiBaseURL else {
|
||||
completion(.failure(CredentialsError.incompleteCredentials))
|
||||
return
|
||||
@@ -579,13 +579,13 @@ final class ReaderAPICaller: NSObject {
|
||||
queryItems.append(URLQueryItem(name: "ot", value: String(Int(sinceTimeInterval))))
|
||||
queryItems.append(URLQueryItem(name: "s", value: ReaderStreams.readingList.rawValue))
|
||||
case .allForFeed:
|
||||
guard let webFeedID = webFeedID else {
|
||||
guard let feedID = feedID else {
|
||||
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
|
||||
return
|
||||
}
|
||||
let sinceTimeInterval = (Calendar.current.date(byAdding: .month, value: -3, to: Date()) ?? Date()).timeIntervalSince1970
|
||||
queryItems.append(URLQueryItem(name: "ot", value: String(Int(sinceTimeInterval))))
|
||||
queryItems.append(URLQueryItem(name: "s", value: webFeedID))
|
||||
queryItems.append(URLQueryItem(name: "s", value: feedID))
|
||||
case .unread:
|
||||
queryItems.append(URLQueryItem(name: "s", value: ReaderStreams.readingList.rawValue))
|
||||
queryItems.append(URLQueryItem(name: "xt", value: ReaderState.read.rawValue))
|
||||
|
||||
@@ -74,7 +74,7 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
|
||||
.map { $0.title })
|
||||
|
||||
let accountFeeds = account.flattenedFeeds()
|
||||
let ingestedIds = Set(accountFeeds.map { $0.webFeedID })
|
||||
let ingestedIds = Set(accountFeeds.map { $0.feedID })
|
||||
let ingestedTitles = Set(accountFeeds.map { $0.nameForDisplay })
|
||||
|
||||
let missingIds = feedIds.subtracting(ingestedIds)
|
||||
@@ -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.topLevelFeeds.map { $0.webFeedID }.sorted(by: <)]
|
||||
return [folder.externalID!: folder.topLevelFeeds.map { $0.feedID }.sorted(by: <)]
|
||||
}
|
||||
|
||||
XCTAssertEqual(expectedFolderAndFeedIds, ingestedFolderAndFeedIds, "Did not ingest feeds in their corresponding folders.")
|
||||
@@ -167,7 +167,7 @@ class FeedlyCreateFeedsForCollectionFoldersOperationTests: XCTestCase {
|
||||
.map { $0.title })
|
||||
|
||||
let accountFeeds = account.flattenedFeeds()
|
||||
let ingestedIds = Set(accountFeeds.map { $0.webFeedID })
|
||||
let ingestedIds = Set(accountFeeds.map { $0.feedID })
|
||||
let ingestedTitles = Set(accountFeeds.map { $0.nameForDisplay })
|
||||
|
||||
XCTAssertEqual(ingestedIds.count, feedIds.count)
|
||||
@@ -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.topLevelFeeds.map { $0.webFeedID }.sorted(by: <)]
|
||||
return [folder.externalID!: folder.topLevelFeeds.map { $0.feedID }.sorted(by: <)]
|
||||
}
|
||||
|
||||
XCTAssertEqual(expectedFolderAndFeedIds, ingestedFolderAndFeedIds, "Did not ingest feeds to their corresponding folders.")
|
||||
|
||||
@@ -55,7 +55,7 @@ class FeedlyEntryParserTests: XCTestCase {
|
||||
XCTAssertEqual(item.uniqueID, entry.id)
|
||||
|
||||
// The following is not an error.
|
||||
// The feedURL must match the webFeedID for the article to be connected to its matching feed.
|
||||
// The feedURL must match the feedID for the article to be connected to its matching feed.
|
||||
XCTAssertEqual(item.feedURL, origin.streamId)
|
||||
XCTAssertEqual(item.title, entry.title)
|
||||
XCTAssertEqual(item.contentHTML, content.content)
|
||||
|
||||
@@ -22,7 +22,7 @@ class FeedlyFeedParserTests: XCTestCase {
|
||||
XCTAssertEqual(parser.title, name)
|
||||
XCTAssertEqual(parser.homePageURL, website)
|
||||
XCTAssertEqual(parser.url, url)
|
||||
XCTAssertEqual(parser.webFeedID, id)
|
||||
XCTAssertEqual(parser.feedID, id)
|
||||
}
|
||||
|
||||
func testSanitization() {
|
||||
@@ -36,6 +36,6 @@ class FeedlyFeedParserTests: XCTestCase {
|
||||
XCTAssertEqual(parser.title, name)
|
||||
XCTAssertEqual(parser.homePageURL, website)
|
||||
XCTAssertEqual(parser.url, url)
|
||||
XCTAssertEqual(parser.webFeedID, id)
|
||||
XCTAssertEqual(parser.feedID, id)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ class FeedlyTestSupport {
|
||||
XCTAssertEqual(collectionFeeds.count, folderFeeds.count)
|
||||
|
||||
let collectionFeedIds = Set(collectionFeeds.map { $0["id"] as! String })
|
||||
let folderFeedIds = Set(folderFeeds.map { $0.webFeedID })
|
||||
let folderFeedIds = Set(folderFeeds.map { $0.feedID })
|
||||
let missingFeedIds = collectionFeedIds.subtracting(folderFeedIds)
|
||||
|
||||
XCTAssertTrue(missingFeedIds.isEmpty, "Feeds with these ids were not found in the \"\(label)\" folder.")
|
||||
@@ -210,7 +210,7 @@ class FeedlyTestSupport {
|
||||
for item in articleItems where item.id == article.articleID {
|
||||
XCTAssertEqual(article.uniqueID, item.id)
|
||||
XCTAssertEqual(article.contentHTML, item.content)
|
||||
XCTAssertEqual(article.webFeedID, item.feedId)
|
||||
XCTAssertEqual(article.feedID, item.feedId)
|
||||
XCTAssertEqual(article.rawExternalLink, item.externalUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,7 +692,7 @@ final class TimelineViewController: NSViewController, UndoableCommandRunner, Unr
|
||||
}
|
||||
|
||||
@objc func accountDidDownloadArticles(_ note: Notification) {
|
||||
guard let feeds = note.userInfo?[Account.UserInfoKey.webFeeds] as? Set<Feed> else {
|
||||
guard let feeds = note.userInfo?[Account.UserInfoKey.feeds] as? Set<Feed> else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -525,7 +525,7 @@ final class SceneCoordinator: NSObject, UndoableCommandRunner, Logging {
|
||||
}
|
||||
|
||||
@objc func accountDidDownloadArticles(_ note: Notification) {
|
||||
guard let feeds = note.userInfo?[Account.UserInfoKey.webFeeds] as? Set<Feed> else {
|
||||
guard let feeds = note.userInfo?[Account.UserInfoKey.feeds] as? Set<Feed> else {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user