diff --git a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift index b77d4af29..8c8c2a7de 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountDelegate.swift @@ -123,16 +123,13 @@ final class CloudKitAccountDelegate: AccountDelegate { return } - guard let children = loadDocument.children else { + guard let opmlItems = loadDocument.children, let rootExternalID = account.externalID else { return } - BatchUpdate.shared.perform { - account.loadOPMLItems(children) - } + let normalizedItems = OPMLNormalizer.normalize(opmlItems) - completion(.success(())) - + accountZone.importOPML(rootExternalID: rootExternalID, items: normalizedItems, completion: completion) } func createWebFeed(for account: Account, url urlString: String, name: String?, container: Container, completion: @escaping (Result) -> Void) { @@ -163,19 +160,16 @@ final class CloudKitAccountDelegate: AccountDelegate { case .success(let externalID): let feed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil) - + feed.editedName = name + feed.externalID = externalID + container.addWebFeed(feed) + InitialFeedDownloader.download(url) { parsedFeed in self.refreshProgress.completeTask() if let parsedFeed = parsedFeed { account.update(feed, with: parsedFeed, {_ in - - feed.editedName = name - feed.externalID = externalID - - container.addWebFeed(feed) completion(.success(feed)) - }) } diff --git a/Frameworks/Account/CloudKit/CloudKitAccountZone.swift b/Frameworks/Account/CloudKit/CloudKitAccountZone.swift index ede217556..532577e64 100644 --- a/Frameworks/Account/CloudKit/CloudKitAccountZone.swift +++ b/Frameworks/Account/CloudKit/CloudKitAccountZone.swift @@ -9,6 +9,7 @@ import Foundation import os.log import RSWeb +import RSParser import CloudKit final class CloudKitAccountZone: CloudKitZone { @@ -31,7 +32,7 @@ final class CloudKitAccountZone: CloudKitZone { struct Fields { static let url = "url" static let editedName = "editedName" - static let containerExternalIDs = "folderExternalIDs" + static let containerExternalIDs = "containerExternalIDs" } } @@ -47,6 +48,40 @@ final class CloudKitAccountZone: CloudKitZone { self.container = container self.database = container.privateCloudDatabase } + + func importOPML(rootExternalID: String, items: [RSOPMLItem], completion: @escaping (Result) -> Void) { + var records = [CKRecord]() + var feedRecords = [String: CKRecord]() + + func processFeed(feedSpecifier: RSOPMLFeedSpecifier, containerExternalID: String) { + if let webFeedRecord = feedRecords[feedSpecifier.feedURL], var containerExternalIDs = webFeedRecord[CloudKitWebFeed.Fields.containerExternalIDs] as? [String] { + containerExternalIDs.append(containerExternalID) + webFeedRecord[CloudKitWebFeed.Fields.containerExternalIDs] = containerExternalIDs + } else { + let webFeedRecord = newWebFeedCKRecord(feedSpecifier: feedSpecifier, containerExternalID: containerExternalID) + records.append(webFeedRecord) + feedRecords[feedSpecifier.feedURL] = webFeedRecord + } + } + + for item in items { + if let feedSpecifier = item.feedSpecifier { + processFeed(feedSpecifier: feedSpecifier, containerExternalID: rootExternalID) + } else { + if let title = item.titleFromAttributes { + let containerRecord = newContainerCKRecord(name: title) + records.append(containerRecord) + item.children?.forEach { itemChild in + if let feedSpecifier = itemChild.feedSpecifier { + processFeed(feedSpecifier: feedSpecifier, containerExternalID: containerRecord.externalID) + } + } + } + } + } + + modify(recordsToSave: records, recordIDsToDelete: [], completion: completion) + } /// Persist a web feed record to iCloud and return the external key func createWebFeed(url: String, editedName: String?, container: Container, completion: @escaping (Result) -> Void) { @@ -208,6 +243,23 @@ 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 + if let editedName = feedSpecifier.title { + record[CloudKitWebFeed.Fields.editedName] = editedName + } + record[CloudKitWebFeed.Fields.containerExternalIDs] = [containerExternalID] + return record + } + + func newContainerCKRecord(name: String) -> CKRecord { + let record = CKRecord(recordType: CloudKitContainer.recordType, recordID: generateRecordID()) + record[CloudKitContainer.Fields.name] = name + record[CloudKitContainer.Fields.isAccount] = "false" + return record + } + func createContainer(name: String, isAccount: Bool, completion: @escaping (Result) -> Void) { let record = CKRecord(recordType: CloudKitContainer.recordType, recordID: generateRecordID()) record[CloudKitContainer.Fields.name] = name