diff --git a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift index 6b3e45e2d..c349a48d3 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift @@ -86,17 +86,34 @@ final class CloudKitAccountDelegate: AccountDelegate { return } - self.articlesZone.sendArticleStatus(syncStatuses) { result in - switch result { - case .success: - self.database.deleteSelectedForProcessing(syncStatuses.map({ $0.articleID }) ) - os_log(.debug, log: self.log, "Done sending article statuses.") - completion(.success(())) - case .failure(let error): - self.database.resetSelectedForProcessing(syncStatuses.map({ $0.articleID }) ) - completion(.failure(error)) + let starredArticleIDs = syncStatuses.filter({ $0.key == .starred && $0.flag == true }).map({ $0.articleID }) + account.fetchArticlesAsync(.articleIDs(Set(starredArticleIDs))) { result in + + func processWithArticles(_ starredArticles: Set
) { + + self.articlesZone.sendArticleStatus(syncStatuses, starredArticles: starredArticles) { result in + switch result { + case .success: + self.database.deleteSelectedForProcessing(syncStatuses.map({ $0.articleID }) ) + os_log(.debug, log: self.log, "Done sending article statuses.") + completion(.success(())) + case .failure(let error): + self.database.resetSelectedForProcessing(syncStatuses.map({ $0.articleID }) ) + completion(.failure(error)) + } + } + } + + switch result { + case .success(let starredArticles): + processWithArticles(starredArticles) + case .failure(let databaseError): + completion(.failure(databaseError)) + } + } + } switch result { diff --git a/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift b/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift index af34f8f6f..a50cc4911 100644 --- a/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift +++ b/Frameworks/Account/CloudKit/CloudKitArticlesZone.swift @@ -10,6 +10,7 @@ import Foundation import os.log import RSWeb import CloudKit +import Articles import SyncDatabase final class CloudKitArticlesZone: CloudKitZone { @@ -24,6 +25,37 @@ final class CloudKitArticlesZone: CloudKitZone { weak var database: CKDatabase? var delegate: CloudKitZoneDelegate? = nil + struct CloudKitArticle { + static let recordType = "Article" + struct Fields { + static let articleStatus = "articleStatus" + static let webFeedID = "webFeedID" + static let uniqueID = "uniqueID" + static let title = "title" + static let contentHTML = "contentHTML" + static let contentText = "contentText" + static let url = "url" + static let externalURL = "externalURL" + static let summary = "summary" + static let imageURL = "imageURL" + static let datePublished = "datePublished" + static let dateModified = "dateModified" + static let authors = "authors" + } + } + + struct CloudKitAuthor { + static let recordType = "Author" + struct Fields { + static let article = "article" + static let authorID = "authorID" + static let name = "name" + static let url = "url" + static let avatarURL = "avatarURL" + static let emailAddress = "emailAddress" + } + } + struct CloudKitArticleStatus { static let recordType = "ArticleStatus" struct Fields { @@ -38,7 +70,17 @@ final class CloudKitArticlesZone: CloudKitZone { self.database = container.privateCloudDatabase } - func sendArticleStatus(_ syncStatuses: [SyncStatus], completion: @escaping ((Result) -> Void)) { + func sendArticleStatus(_ syncStatuses: [SyncStatus], starredArticles: Set
, completion: @escaping ((Result) -> Void)) { + var records = makeStatusRecords(syncStatuses) + records.append(contentsOf: makeArticleRecords(starredArticles)) + modify(recordsToSave: records, recordIDsToDelete: [], completion: completion) + } + +} + +private extension CloudKitArticlesZone { + + func makeStatusRecords(_ syncStatuses: [SyncStatus]) -> [CKRecord] { var records = [String: CKRecord]() for status in syncStatuses { @@ -60,7 +102,53 @@ final class CloudKitArticlesZone: CloudKitZone { } } - modify(recordsToSave: Array(records.values), recordIDsToDelete: [], completion: completion) + return Array(records.values) + } + + func makeArticleRecords(_ articles: Set
) -> [CKRecord] { + var records = [CKRecord]() + + for article in articles { + + let record = CKRecord(recordType: CloudKitArticle.recordType, recordID: generateRecordID()) + + let articleStatusRecordID = CKRecord.ID(recordName: article.articleID, zoneID: Self.zoneID) + record[CloudKitArticle.Fields.articleStatus] = CKRecord.Reference(recordID: articleStatusRecordID, action: .deleteSelf) + record[CloudKitArticle.Fields.webFeedID] = article.webFeedID + record[CloudKitArticle.Fields.uniqueID] = article.uniqueID + record[CloudKitArticle.Fields.title] = article.title + record[CloudKitArticle.Fields.contentHTML] = article.contentHTML + record[CloudKitArticle.Fields.contentText] = article.contentText + record[CloudKitArticle.Fields.url] = article.url + record[CloudKitArticle.Fields.externalURL] = article.externalURL + record[CloudKitArticle.Fields.summary] = article.summary + record[CloudKitArticle.Fields.imageURL] = article.imageURL + record[CloudKitArticle.Fields.datePublished] = article.datePublished + record[CloudKitArticle.Fields.dateModified] = article.dateModified + + records.append(record) + + if let authors = article.authors { + for author in authors { + records.append(makeAuthorRecord(record, author)) + } + } + } + + return records } + func makeAuthorRecord(_ articleRecord: CKRecord, _ author: Author) -> CKRecord { + let record = CKRecord(recordType: CloudKitAuthor.recordType, recordID: generateRecordID()) + + record[CloudKitAuthor.Fields.article] = CKRecord.Reference(record: articleRecord, action: .deleteSelf) + record[CloudKitAuthor.Fields.authorID] = author.authorID + record[CloudKitAuthor.Fields.name] = author.name + record[CloudKitAuthor.Fields.url] = author.url + record[CloudKitAuthor.Fields.avatarURL] = author.avatarURL + record[CloudKitAuthor.Fields.emailAddress] = author.emailAddress + + return record + } + }