Enable adding feeds to folders.
This commit is contained in:
parent
6d3e6914df
commit
203b83d64d
@ -511,6 +511,13 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
return delegate.markArticles(for: self, articles: articles, statusKey: statusKey, flag: flag)
|
||||
}
|
||||
|
||||
func existingContainer(withExternalID externalID: String) -> Container? {
|
||||
guard self.externalID != externalID else {
|
||||
return self
|
||||
}
|
||||
return existingFolder(withExternalID: externalID)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func ensureFolder(with name: String) -> Folder? {
|
||||
// TODO: support subfolders, maybe, some day
|
||||
@ -561,10 +568,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
return feed
|
||||
}
|
||||
|
||||
public func existingWebFeed(withExternalID externalID: String) -> WebFeed? {
|
||||
return externalIDToWebFeedDictionary[externalID]
|
||||
}
|
||||
|
||||
public func addWebFeed(_ feed: WebFeed, to container: Container, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
delegate.addWebFeed(for: self, with: feed, to: container, completion: completion)
|
||||
}
|
||||
@ -1315,6 +1318,11 @@ extension Account {
|
||||
public func existingWebFeed(withWebFeedID webFeedID: String) -> WebFeed? {
|
||||
return idToWebFeedDictionary[webFeedID]
|
||||
}
|
||||
|
||||
public func existingWebFeed(withExternalID externalID: String) -> WebFeed? {
|
||||
return externalIDToWebFeedDictionary[externalID]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - OPMLRepresentable
|
||||
|
@ -155,9 +155,9 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
self.accountZone.createWebFeed(url: bestFeedSpecifier.urlString, editedName: name) { result in
|
||||
self.accountZone.createWebFeed(url: bestFeedSpecifier.urlString, editedName: name, container: container) { result in
|
||||
switch result {
|
||||
case .success(let externalID):
|
||||
case .success(let containerWebFeed):
|
||||
|
||||
let feed = account.createWebFeed(with: nil, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
|
||||
|
||||
@ -168,7 +168,8 @@ final class CloudKitAccountDelegate: AccountDelegate {
|
||||
account.update(feed, with: parsedFeed, {_ in
|
||||
|
||||
feed.editedName = name
|
||||
feed.externalID = externalID
|
||||
feed.externalID = containerWebFeed.webFeedExternalID
|
||||
feed.folderRelationship?[containerWebFeed.containerWebFeedExternalID] = containerWebFeed.containerExternalID
|
||||
|
||||
container.addWebFeed(feed)
|
||||
completion(.success(feed))
|
||||
|
@ -13,6 +13,8 @@ import CloudKit
|
||||
|
||||
final class CloudKitAccountZone: CloudKitZone {
|
||||
|
||||
typealias ContainerWebFeed = (webFeedExternalID: String, containerWebFeedExternalID: String, containerExternalID: String)
|
||||
|
||||
static var zoneID: CKRecordZone.ID {
|
||||
return CKRecordZone.ID(zoneName: "Account", ownerName: CKCurrentUserDefaultName)
|
||||
}
|
||||
@ -40,29 +42,51 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
}
|
||||
}
|
||||
|
||||
struct CloudKitContainerWebFeed {
|
||||
static let recordType = "ContainerWebFeed"
|
||||
struct Fields {
|
||||
static let container = "container"
|
||||
static let webFeed = "webFeed"
|
||||
}
|
||||
}
|
||||
|
||||
init(container: CKContainer) {
|
||||
self.container = container
|
||||
self.database = container.privateCloudDatabase
|
||||
}
|
||||
|
||||
/// Persist a web feed record to iCloud and return the external key
|
||||
func createWebFeed(url: String, editedName: String?, completion: @escaping (Result<String, Error>) -> Void) {
|
||||
let record = CKRecord(recordType: CloudKitWebFeed.recordType, recordID: generateRecordID())
|
||||
record[CloudKitWebFeed.Fields.url] = url
|
||||
func createWebFeed(url: String, editedName: String?, container: Container, completion: @escaping (Result<ContainerWebFeed, Error>) -> Void) {
|
||||
let webFeedRecord = CKRecord(recordType: CloudKitWebFeed.recordType, recordID: generateRecordID())
|
||||
webFeedRecord[CloudKitWebFeed.Fields.url] = url
|
||||
if let editedName = editedName {
|
||||
record[CloudKitWebFeed.Fields.editedName] = editedName
|
||||
webFeedRecord[CloudKitWebFeed.Fields.editedName] = editedName
|
||||
}
|
||||
|
||||
save(record: record) { result in
|
||||
guard let containerExternalID = container.externalID else {
|
||||
completion(.failure(CloudKitZoneError.invalidParameter))
|
||||
return
|
||||
}
|
||||
|
||||
let containerRecordID = CKRecord.ID(recordName: containerExternalID, zoneID: Self.zoneID)
|
||||
let containerWebFeedRecord = CKRecord(recordType: CloudKitContainerWebFeed.recordType, recordID: generateRecordID())
|
||||
containerWebFeedRecord[CloudKitContainerWebFeed.Fields.container] = CKRecord.Reference(recordID: containerRecordID, action: .deleteSelf)
|
||||
containerWebFeedRecord[CloudKitContainerWebFeed.Fields.webFeed] = CKRecord.Reference(record: webFeedRecord, action: .deleteSelf)
|
||||
|
||||
save([webFeedRecord, containerWebFeedRecord]) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(record.externalID))
|
||||
let cwf = ContainerWebFeed(webFeedExternalID: webFeedRecord.externalID,
|
||||
containerWebFeedExternalID: containerWebFeedRecord.externalID,
|
||||
containerExternalID: containerExternalID)
|
||||
completion(.success(cwf))
|
||||
case .failure(let error):
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Rename the given web feed
|
||||
func renameWebFeed(_ webFeed: WebFeed, editedName: String?, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
guard let externalID = webFeed.externalID else {
|
||||
completion(.failure(CloudKitZoneError.invalidParameter))
|
||||
@ -73,7 +97,7 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
let record = CKRecord(recordType: CloudKitWebFeed.recordType, recordID: recordID)
|
||||
record[CloudKitWebFeed.Fields.editedName] = editedName
|
||||
|
||||
save(record: record) { result in
|
||||
save([record]) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
@ -116,7 +140,7 @@ final class CloudKitAccountZone: CloudKitZone {
|
||||
let record = CKRecord(recordType: CloudKitContainer.recordType, recordID: recordID)
|
||||
record[CloudKitContainer.Fields.name] = name
|
||||
|
||||
save(record: record) { result in
|
||||
save([record]) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(()))
|
||||
@ -139,7 +163,7 @@ private extension CloudKitAccountZone {
|
||||
record[CloudKitContainer.Fields.name] = name
|
||||
record[CloudKitContainer.Fields.isAccount] = isAccount ? "true" : "false"
|
||||
|
||||
save(record: record) { result in
|
||||
save([record]) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
completion(.success(record.externalID))
|
||||
|
@ -13,6 +13,9 @@ import CloudKit
|
||||
|
||||
class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
|
||||
private typealias UnclaimedWebFeed = (url: String, editedName: String?)
|
||||
private var unclaimedWebFeeds = [String: UnclaimedWebFeed]()
|
||||
|
||||
private var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "CloudKit")
|
||||
|
||||
weak var account: Account?
|
||||
@ -29,6 +32,8 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
addOrUpdateWebFeed(record)
|
||||
case CloudKitAccountZone.CloudKitContainer.recordType:
|
||||
addOrUpdateContainer(record)
|
||||
case CloudKitAccountZone.CloudKitContainerWebFeed.recordType:
|
||||
addOrUpdateContainerWebFeed(record)
|
||||
default:
|
||||
assertionFailure("Unknown record type: \(record.recordType)")
|
||||
}
|
||||
@ -37,9 +42,11 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
func cloudKitDidDelete(recordType: CKRecord.RecordType, recordID: CKRecord.ID) {
|
||||
switch recordType {
|
||||
case CloudKitAccountZone.CloudKitWebFeed.recordType:
|
||||
removeWebFeed(recordID.externalID)
|
||||
break
|
||||
case CloudKitAccountZone.CloudKitContainer.recordType:
|
||||
removeContainer(recordID.externalID)
|
||||
case CloudKitAccountZone.CloudKitContainerWebFeed.recordType:
|
||||
removeContainerWebFeed(recordID.externalID)
|
||||
default:
|
||||
assertionFailure("Unknown record type: \(recordID.externalID)")
|
||||
}
|
||||
@ -53,20 +60,12 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
if let webFeed = account.existingWebFeed(withExternalID: record.externalID) {
|
||||
webFeed.editedName = editedName
|
||||
} else {
|
||||
if let urlString = record[CloudKitAccountZone.CloudKitWebFeed.Fields.url] as? String, let url = URL(string: urlString) {
|
||||
downloadAndAddWebFeed(url: url, editedName: editedName, externalID: record.externalID)
|
||||
} else {
|
||||
os_log(.error, log: self.log, "Failed to add or update web feed.")
|
||||
if let urlString = record[CloudKitAccountZone.CloudKitWebFeed.Fields.url] as? String {
|
||||
unclaimedWebFeeds[record.externalID] = UnclaimedWebFeed(url: urlString, editedName: editedName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeWebFeed(_ externalID: String) {
|
||||
if let webFeed = account?.existingWebFeed(withExternalID: externalID) {
|
||||
account?.removeWebFeed(webFeed)
|
||||
}
|
||||
}
|
||||
|
||||
func addOrUpdateContainer(_ record: CKRecord) {
|
||||
guard let account = account,
|
||||
let name = record[CloudKitAccountZone.CloudKitContainer.Fields.name] as? String,
|
||||
@ -87,17 +86,34 @@ class CloudKitAcountZoneDelegate: CloudKitZoneDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension CloudKitAcountZoneDelegate {
|
||||
|
||||
func downloadAndAddWebFeed(url: URL, editedName: String?, externalID: String) {
|
||||
guard let account = account else { return }
|
||||
func addOrUpdateContainerWebFeed(_ record: CKRecord) {
|
||||
guard let account = account,
|
||||
let containerReference = record[CloudKitAccountZone.CloudKitContainerWebFeed.Fields.container] as? CKRecord.Reference,
|
||||
let webFeedReference = record[CloudKitAccountZone.CloudKitContainerWebFeed.Fields.webFeed] as? CKRecord.Reference else { return }
|
||||
|
||||
let webFeed = account.createWebFeed(with: editedName, url: url.absoluteString, webFeedID: url.absoluteString, homePageURL: nil)
|
||||
webFeed.editedName = editedName
|
||||
webFeed.externalID = externalID
|
||||
account.addWebFeed(webFeed)
|
||||
let containerWebFeedExternalID = record.externalID
|
||||
let containerExternalID = containerReference.recordID.externalID
|
||||
let webFeedExternalID = webFeedReference.recordID.externalID
|
||||
|
||||
guard let container = account.existingContainer(withExternalID: containerExternalID) else { return }
|
||||
|
||||
if let webFeed = account.existingWebFeed(withExternalID: webFeedExternalID) {
|
||||
webFeed.folderRelationship?[containerWebFeedExternalID] = containerExternalID
|
||||
container.addWebFeed(webFeed)
|
||||
return
|
||||
}
|
||||
|
||||
guard let unclaimedWebFeed = unclaimedWebFeeds[webFeedExternalID] else { return }
|
||||
unclaimedWebFeeds.removeValue(forKey: webFeedExternalID)
|
||||
|
||||
let webFeed = account.createWebFeed(with: nil, url: unclaimedWebFeed.url, webFeedID: unclaimedWebFeed.url, homePageURL: nil)
|
||||
webFeed.editedName = unclaimedWebFeed.editedName
|
||||
webFeed.externalID = webFeedExternalID
|
||||
webFeed.folderRelationship = [String: String]()
|
||||
webFeed.folderRelationship![containerWebFeedExternalID] = containerExternalID
|
||||
container.addWebFeed(webFeed)
|
||||
|
||||
guard let url = URL(string: unclaimedWebFeed.url) else { return }
|
||||
|
||||
refreshProgress?.addToNumberOfTasksAndRemaining(1)
|
||||
InitialFeedDownloader.download(url) { parsedFeed in
|
||||
@ -106,7 +122,22 @@ private extension CloudKitAcountZoneDelegate {
|
||||
account.update(webFeed, with: parsedFeed, {_ in })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func removeContainerWebFeed(_ containerWebFeedExternalID: String) {
|
||||
guard let account = account,
|
||||
let webFeed = account.flattenedWebFeeds().first(where: { $0.folderRelationship?.keys.contains(containerWebFeedExternalID) ?? false }),
|
||||
let containerExternalId = webFeed.folderRelationship?[containerWebFeedExternalID] else { return }
|
||||
|
||||
webFeed.folderRelationship?.removeValue(forKey: containerWebFeedExternalID)
|
||||
|
||||
guard account.externalID != containerExternalId else {
|
||||
account.removeWebFeed(webFeed)
|
||||
return
|
||||
}
|
||||
|
||||
guard let folder = account.existingFolder(withExternalID: containerExternalId) else { return }
|
||||
folder.removeWebFeed(webFeed)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -96,8 +96,8 @@ extension CloudKitZone {
|
||||
}
|
||||
}
|
||||
|
||||
func save(record: CKRecord, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
modify(recordsToSave: [record], recordIDsToDelete: [], completion: completion)
|
||||
func save(_ records: [CKRecord], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
modify(recordsToSave: records, recordIDsToDelete: [], completion: completion)
|
||||
}
|
||||
|
||||
func delete(externalID: String?, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
@ -39,6 +39,7 @@ public protocol Container: class, ContainerIdentifiable {
|
||||
func hasWebFeed(withURL url: String) -> Bool
|
||||
func existingWebFeed(withWebFeedID: String) -> WebFeed?
|
||||
func existingWebFeed(withURL url: String) -> WebFeed?
|
||||
func existingWebFeed(withExternalID externalID: String) -> WebFeed?
|
||||
func existingFolder(with name: String) -> Folder?
|
||||
func existingFolder(withID: Int) -> Folder?
|
||||
|
||||
@ -117,6 +118,15 @@ public extension Container {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func existingWebFeed(withExternalID externalID: String) -> WebFeed? {
|
||||
for feed in flattenedWebFeeds() {
|
||||
if feed.externalID == externalID {
|
||||
return feed
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func existingFolder(with name: String) -> Folder? {
|
||||
guard let folders = folders else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user