Refactoring create feed
This commit is contained in:
parent
7cac3e6238
commit
d1e4c84a9e
|
@ -91,6 +91,7 @@
|
||||||
9E1D15572334355900F4944C /* FeedlyRequestStreamsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D15562334355900F4944C /* FeedlyRequestStreamsOperation.swift */; };
|
9E1D15572334355900F4944C /* FeedlyRequestStreamsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D15562334355900F4944C /* FeedlyRequestStreamsOperation.swift */; };
|
||||||
9E1D155B2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D155A2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift */; };
|
9E1D155B2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D155A2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift */; };
|
||||||
9E1D155D233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D155C233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift */; };
|
9E1D155D233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1D155C233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift */; };
|
||||||
|
9E510D6E234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E510D6D234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift */; };
|
||||||
9E713653233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E713652233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift */; };
|
9E713653233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E713652233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift */; };
|
||||||
9E7F15072341E96700F860D1 /* AccountFeedlySyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7F15062341E96700F860D1 /* AccountFeedlySyncTest.swift */; };
|
9E7F15072341E96700F860D1 /* AccountFeedlySyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7F15062341E96700F860D1 /* AccountFeedlySyncTest.swift */; };
|
||||||
9E7F150A2341EF5A00F860D1 /* feedly_collections_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 9E7F15092341EF5A00F860D1 /* feedly_collections_initial.json */; };
|
9E7F150A2341EF5A00F860D1 /* feedly_collections_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 9E7F15092341EF5A00F860D1 /* feedly_collections_initial.json */; };
|
||||||
|
@ -115,7 +116,9 @@
|
||||||
9EC688EC232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */; };
|
9EC688EC232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */; };
|
||||||
9EC688EE232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */; };
|
9EC688EE232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */; };
|
||||||
9ECC9A85234DC16E009B5144 /* FeedlyAccountDelegateError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */; };
|
9ECC9A85234DC16E009B5144 /* FeedlyAccountDelegateError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */; };
|
||||||
|
9EE4CCFA234F106600FBAE4B /* FeedlyFeedContainerValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EE4CCF9234F106600FBAE4B /* FeedlyFeedContainerValidator.swift */; };
|
||||||
9EF35F7A234E830E003AE2AE /* FeedlyCompoundOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */; };
|
9EF35F7A234E830E003AE2AE /* FeedlyCompoundOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */; };
|
||||||
|
9EF35F7C234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF35F7B234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -264,6 +267,7 @@
|
||||||
9E1D15562334355900F4944C /* FeedlyRequestStreamsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRequestStreamsOperation.swift; sourceTree = "<group>"; };
|
9E1D15562334355900F4944C /* FeedlyRequestStreamsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRequestStreamsOperation.swift; sourceTree = "<group>"; };
|
||||||
9E1D155A2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyOrganiseParsedItemsByFeedOperation.swift; sourceTree = "<group>"; };
|
9E1D155A2334423300F4944C /* FeedlyOrganiseParsedItemsByFeedOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyOrganiseParsedItemsByFeedOperation.swift; sourceTree = "<group>"; };
|
||||||
9E1D155C233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyUpdateAccountFeedsWithItemsOperation.swift; sourceTree = "<group>"; };
|
9E1D155C233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyUpdateAccountFeedsWithItemsOperation.swift; sourceTree = "<group>"; };
|
||||||
|
9E510D6D234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCreateFeedRequest.swift; sourceTree = "<group>"; };
|
||||||
9E713652233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRefreshStreamEntriesStatusOperation.swift; sourceTree = "<group>"; };
|
9E713652233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRefreshStreamEntriesStatusOperation.swift; sourceTree = "<group>"; };
|
||||||
9E7F15062341E96700F860D1 /* AccountFeedlySyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFeedlySyncTest.swift; sourceTree = "<group>"; };
|
9E7F15062341E96700F860D1 /* AccountFeedlySyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFeedlySyncTest.swift; sourceTree = "<group>"; };
|
||||||
9E7F15092341EF5A00F860D1 /* feedly_collections_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = feedly_collections_initial.json; sourceTree = "<group>"; };
|
9E7F15092341EF5A00F860D1 /* feedly_collections_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = feedly_collections_initial.json; sourceTree = "<group>"; };
|
||||||
|
@ -288,7 +292,9 @@
|
||||||
9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FeedlyAccountDelegate+OAuth.swift"; sourceTree = "<group>"; };
|
9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FeedlyAccountDelegate+OAuth.swift"; sourceTree = "<group>"; };
|
||||||
9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthAuthorizationCodeGranting.swift; sourceTree = "<group>"; };
|
9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthAuthorizationCodeGranting.swift; sourceTree = "<group>"; };
|
||||||
9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyAccountDelegateError.swift; sourceTree = "<group>"; };
|
9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyAccountDelegateError.swift; sourceTree = "<group>"; };
|
||||||
|
9EE4CCF9234F106600FBAE4B /* FeedlyFeedContainerValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFeedContainerValidator.swift; sourceTree = "<group>"; };
|
||||||
9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCompoundOperation.swift; sourceTree = "<group>"; };
|
9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCompoundOperation.swift; sourceTree = "<group>"; };
|
||||||
|
9EF35F7B234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlySubscribeToFeedOperation.swift; sourceTree = "<group>"; };
|
||||||
D511EEB5202422BB00712EC3 /* Account_project_debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_debug.xcconfig; sourceTree = "<group>"; };
|
D511EEB5202422BB00712EC3 /* Account_project_debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_debug.xcconfig; sourceTree = "<group>"; };
|
||||||
D511EEB6202422BB00712EC3 /* Account_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_target.xcconfig; sourceTree = "<group>"; };
|
D511EEB6202422BB00712EC3 /* Account_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_target.xcconfig; sourceTree = "<group>"; };
|
||||||
D511EEB7202422BB00712EC3 /* Account_project_release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_release.xcconfig; sourceTree = "<group>"; };
|
D511EEB7202422BB00712EC3 /* Account_project_release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_release.xcconfig; sourceTree = "<group>"; };
|
||||||
|
@ -542,12 +548,15 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
9EA3133A231E368100268BA0 /* FeedlyAccountDelegate.swift */,
|
9EA3133A231E368100268BA0 /* FeedlyAccountDelegate.swift */,
|
||||||
|
9EE4CCF9234F106600FBAE4B /* FeedlyFeedContainerValidator.swift */,
|
||||||
9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */,
|
9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */,
|
||||||
9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */,
|
9EC688EB232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift */,
|
||||||
9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */,
|
9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */,
|
||||||
9EC688E9232B973C00A8D0A2 /* FeedlyAPICaller.swift */,
|
9EC688E9232B973C00A8D0A2 /* FeedlyAPICaller.swift */,
|
||||||
|
9E510D6D234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift */,
|
||||||
9E1D1554233431A600F4944C /* FeedlyOperation.swift */,
|
9E1D1554233431A600F4944C /* FeedlyOperation.swift */,
|
||||||
9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */,
|
9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */,
|
||||||
|
9EF35F7B234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift */,
|
||||||
9EBC31B6233987C1002A567B /* FeedlyArticleStatusCoordinator.swift */,
|
9EBC31B6233987C1002A567B /* FeedlyArticleStatusCoordinator.swift */,
|
||||||
9EBC31B32338AC2E002A567B /* Models */,
|
9EBC31B32338AC2E002A567B /* Models */,
|
||||||
9EBC31B22338AC0F002A567B /* Refresh */,
|
9EBC31B22338AC0F002A567B /* Refresh */,
|
||||||
|
@ -812,9 +821,11 @@
|
||||||
515E4EB62324FF8C0057B0E7 /* URLRequest+RSWeb.swift in Sources */,
|
515E4EB62324FF8C0057B0E7 /* URLRequest+RSWeb.swift in Sources */,
|
||||||
5154367B228EEB28005E1CDF /* FeedbinImportResult.swift in Sources */,
|
5154367B228EEB28005E1CDF /* FeedbinImportResult.swift in Sources */,
|
||||||
84B2D4D02238CD8A00498ADA /* FeedMetadata.swift in Sources */,
|
84B2D4D02238CD8A00498ADA /* FeedMetadata.swift in Sources */,
|
||||||
|
9EF35F7C234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift in Sources */,
|
||||||
9EAEC624233315F60085D7C9 /* FeedlyEntry.swift in Sources */,
|
9EAEC624233315F60085D7C9 /* FeedlyEntry.swift in Sources */,
|
||||||
5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */,
|
5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */,
|
||||||
84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */,
|
84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */,
|
||||||
|
9E510D6E234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift in Sources */,
|
||||||
5133231122810EB200C30F19 /* FeedbinIcon.swift in Sources */,
|
5133231122810EB200C30F19 /* FeedbinIcon.swift in Sources */,
|
||||||
846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */,
|
846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */,
|
||||||
55203300229D5D5A009559E0 /* ReaderAPICaller.swift in Sources */,
|
55203300229D5D5A009559E0 /* ReaderAPICaller.swift in Sources */,
|
||||||
|
@ -833,6 +844,7 @@
|
||||||
5170743C232AEDB500A461A3 /* OPMLFile.swift in Sources */,
|
5170743C232AEDB500A461A3 /* OPMLFile.swift in Sources */,
|
||||||
51BB7B84233531BC008E8144 /* AccountBehaviors.swift in Sources */,
|
51BB7B84233531BC008E8144 /* AccountBehaviors.swift in Sources */,
|
||||||
9E1773D923458D590056A5A8 /* FeedlyResourceId.swift in Sources */,
|
9E1773D923458D590056A5A8 /* FeedlyResourceId.swift in Sources */,
|
||||||
|
9EE4CCFA234F106600FBAE4B /* FeedlyFeedContainerValidator.swift in Sources */,
|
||||||
552032FC229D5D5A009559E0 /* ReaderAPIUnreadEntry.swift in Sources */,
|
552032FC229D5D5A009559E0 /* ReaderAPIUnreadEntry.swift in Sources */,
|
||||||
51FE1008234635A20056195D /* DeepLinkProvider.swift in Sources */,
|
51FE1008234635A20056195D /* DeepLinkProvider.swift in Sources */,
|
||||||
9EC688EA232B973C00A8D0A2 /* FeedlyAPICaller.swift in Sources */,
|
9EC688EA232B973C00A8D0A2 /* FeedlyAPICaller.swift in Sources */,
|
||||||
|
|
|
@ -238,75 +238,21 @@ final class FeedlyAccountDelegate: AccountDelegate {
|
||||||
return (folder, collectionId)
|
return (folder, collectionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFeed(for account: Account, url: String, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void) {
|
var createFeedRequest: FeedlyCreateFeedRequest?
|
||||||
let (folder, collectionId): (Folder, String)
|
|
||||||
do {
|
|
||||||
(folder, collectionId) = try isValidContainer(for: account, container: container)
|
|
||||||
} catch {
|
|
||||||
return DispatchQueue.main.async {
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let resourceId = FeedlyFeedResourceId(url: url)
|
func createFeed(for account: Account, url: String, name: String?, container: Container, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||||
|
|
||||||
let progress = refreshProgress
|
let progress = refreshProgress
|
||||||
progress.addToNumberOfTasksAndRemaining(1)
|
progress.addToNumberOfTasksAndRemaining(1)
|
||||||
|
|
||||||
caller.addFeed(with: resourceId, title: name, toCollectionWith: collectionId) { [weak self] result in
|
let createFeedRequest = FeedlyCreateFeedRequest(account: account, caller: caller, container: container, log: log)
|
||||||
defer { progress.completeTask() }
|
|
||||||
|
|
||||||
switch result {
|
self.createFeedRequest = createFeedRequest
|
||||||
case .success(let feedlyFeeds):
|
|
||||||
let feedsBefore = folder.flattenedFeeds()
|
|
||||||
for feedlyFeed in feedlyFeeds where !account.hasFeed(with: feedlyFeed.feedId) {
|
|
||||||
let resourceId = FeedlyFeedResourceId(id: feedlyFeed.id)
|
|
||||||
let feed = account.createFeed(with: feedlyFeed.title,
|
|
||||||
url: resourceId.url,
|
|
||||||
feedID: feedlyFeed.id,
|
|
||||||
homePageURL: feedlyFeed.website)
|
|
||||||
folder.addFeed(feed)
|
|
||||||
}
|
|
||||||
|
|
||||||
let feedsAfter = folder.flattenedFeeds()
|
createFeedRequest.start(url: url, name: name) { [weak self] result in
|
||||||
let added = feedsAfter.subtracting(feedsBefore)
|
|
||||||
|
|
||||||
guard let first = added.first else {
|
|
||||||
return completion(.failure(AccountError.createErrorNotFound))
|
|
||||||
}
|
|
||||||
|
|
||||||
let group = DispatchGroup()
|
|
||||||
progress.addToNumberOfTasksAndRemaining(1)
|
|
||||||
|
|
||||||
if let self = self {
|
|
||||||
for feed in added {
|
|
||||||
group.enter()
|
|
||||||
let resourceId = FeedlyFeedResourceId(id: feed.feedID)
|
|
||||||
self.caller.getStream(for: resourceId, newerThan: nil, unreadOnly: nil) { result in
|
|
||||||
switch result {
|
|
||||||
case .success(let stream):
|
|
||||||
let items = Set(stream.items.map { FeedlyEntryParser(entry: $0).parsedItemRepresentation })
|
|
||||||
|
|
||||||
account.update(feed, parsedItems: items, defaultRead: false) {
|
|
||||||
group.leave()
|
|
||||||
}
|
|
||||||
|
|
||||||
case .failure:
|
|
||||||
// Feed will remain empty until new articles appear.
|
|
||||||
group.leave()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
group.notify(queue: .main) {
|
|
||||||
progress.completeTask()
|
progress.completeTask()
|
||||||
completion(.success(first))
|
self?.createFeedRequest = nil
|
||||||
}
|
completion(result)
|
||||||
|
|
||||||
case .failure(let error):
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
/// An operation with a queue of its own.
|
||||||
final class FeedlyCompoundOperation: FeedlyOperation {
|
final class FeedlyCompoundOperation: FeedlyOperation {
|
||||||
private let operationQueue = OperationQueue()
|
private let operationQueue = OperationQueue()
|
||||||
private let operations: [Operation]
|
private let operations: [Operation]
|
||||||
|
@ -31,6 +32,9 @@ final class FeedlyCompoundOperation: FeedlyOperation {
|
||||||
finishOperation.addDependency(operation)
|
finishOperation.addDependency(operation)
|
||||||
}
|
}
|
||||||
|
|
||||||
operationQueue.addOperations(operations, waitUntilFinished: false)
|
var operationsWithFinish = operations
|
||||||
|
operationsWithFinish.append(finishOperation)
|
||||||
|
|
||||||
|
operationQueue.addOperations(operationsWithFinish, waitUntilFinished: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
//
|
||||||
|
// FeedlyCreateFeedRequest.swift
|
||||||
|
// Account
|
||||||
|
//
|
||||||
|
// Created by Kiel Gillard on 10/10/19.
|
||||||
|
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import os.log
|
||||||
|
|
||||||
|
final class FeedlyCreateFeedRequest {
|
||||||
|
let account: Account
|
||||||
|
let caller: FeedlyAPICaller
|
||||||
|
let container: Container
|
||||||
|
let log: OSLog
|
||||||
|
|
||||||
|
init(account: Account, caller: FeedlyAPICaller, container: Container, log: OSLog) {
|
||||||
|
self.account = account
|
||||||
|
self.caller = caller
|
||||||
|
self.container = container
|
||||||
|
self.log = log
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Delegate: FeedlyOperationDelegate {
|
||||||
|
let resourceProvider: FeedlyResourceProviding
|
||||||
|
|
||||||
|
init(resourceProvider: FeedlyResourceProviding) {
|
||||||
|
self.resourceProvider = resourceProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
var completionHandler: ((Result<Feed, Error>) -> ())?
|
||||||
|
var error: Error?
|
||||||
|
|
||||||
|
func feedlyOperation(_ operation: FeedlyOperation, didFailWith error: Error) {
|
||||||
|
self.error = error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func start(url: String, name: String?, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||||
|
|
||||||
|
let (folder, collectionId): (Folder, String)
|
||||||
|
do {
|
||||||
|
let validator = FeedlyFeedContainerValidator(container: container, userId: caller.credentials?.username)
|
||||||
|
(folder, collectionId) = try validator.getValidContainer()
|
||||||
|
} catch {
|
||||||
|
return DispatchQueue.main.async {
|
||||||
|
completion(.failure(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let subscribeRequest = FeedlySubscribeToFeedOperation(account: account, folder: folder, url: url, feedName: name, collectionId: collectionId, caller: caller)
|
||||||
|
|
||||||
|
let delegate = Delegate(resourceProvider: subscribeRequest)
|
||||||
|
delegate.completionHandler = completion
|
||||||
|
|
||||||
|
let createFeed = FeedlyCompoundOperation() {
|
||||||
|
let createFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: subscribeRequest, log: log)
|
||||||
|
let getStream = FeedlyGetStreamOperation(account: account, resourceProvider: subscribeRequest, caller: caller, newerThan: nil)
|
||||||
|
let organiseByFeed = FeedlyOrganiseParsedItemsByFeedOperation(account: account, entryProvider: getStream, log: log)
|
||||||
|
let updateAccount = FeedlyUpdateAccountFeedsWithItemsOperation(account: account, organisedItemsProvider: organiseByFeed, log: log)
|
||||||
|
|
||||||
|
createFeeds.addDependency(subscribeRequest)
|
||||||
|
getStream.addDependency(createFeeds)
|
||||||
|
organiseByFeed.addDependency(getStream)
|
||||||
|
updateAccount.addDependency(organiseByFeed)
|
||||||
|
|
||||||
|
let operations = [subscribeRequest, createFeeds, getStream, organiseByFeed, updateAccount]
|
||||||
|
|
||||||
|
for operation in operations {
|
||||||
|
operation.delegate = delegate
|
||||||
|
}
|
||||||
|
|
||||||
|
return operations
|
||||||
|
}
|
||||||
|
|
||||||
|
let callback = BlockOperation() {
|
||||||
|
guard let handler = delegate.completionHandler else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer { delegate.completionHandler = nil }
|
||||||
|
|
||||||
|
if let error = delegate.error {
|
||||||
|
handler(.failure(error))
|
||||||
|
|
||||||
|
} else if let feed = folder.existingFeed(withFeedID: subscribeRequest.resource.id) {
|
||||||
|
handler(.success(feed))
|
||||||
|
|
||||||
|
} else {
|
||||||
|
handler(.failure(AccountError.createErrorNotFound))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback.addDependency(createFeed)
|
||||||
|
|
||||||
|
OperationQueue.main.addOperations([createFeed, callback], waitUntilFinished: false)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
//
|
||||||
|
// FeedlyFeedContainerValidator.swift
|
||||||
|
// Account
|
||||||
|
//
|
||||||
|
// Created by Kiel Gillard on 10/10/19.
|
||||||
|
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct FeedlyFeedContainerValidator {
|
||||||
|
var container: Container
|
||||||
|
var userId: String?
|
||||||
|
|
||||||
|
func getValidContainer() throws -> (Folder, String) {
|
||||||
|
guard let folder = container as? Folder else {
|
||||||
|
throw FeedlyAccountDelegateError.addFeedChooseFolder
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let collectionId = folder.externalID else {
|
||||||
|
throw FeedlyAccountDelegateError.addFeedInvalidFolder(folder)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let userId = userId else {
|
||||||
|
throw FeedlyAccountDelegateError.notLoggedIn
|
||||||
|
}
|
||||||
|
|
||||||
|
let uncategorized = FeedlyCategoryResourceId.uncategorized(for: userId)
|
||||||
|
|
||||||
|
guard collectionId != uncategorized.id else {
|
||||||
|
throw FeedlyAccountDelegateError.addFeedInvalidFolder(folder)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (folder, collectionId)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
//
|
||||||
|
// FeedlyCreateFeedOperation.swift
|
||||||
|
// Account
|
||||||
|
//
|
||||||
|
// Created by Kiel Gillard on 10/10/19.
|
||||||
|
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol FeedlyResourceProviding {
|
||||||
|
var resource: FeedlyResourceId { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
final class FeedlySubscribeToFeedOperation: FeedlyOperation, FeedlyFeedsAndFoldersProviding, FeedlyResourceProviding {
|
||||||
|
let url: String
|
||||||
|
let feedName: String?
|
||||||
|
let collectionId: String
|
||||||
|
let caller: FeedlyAPICaller
|
||||||
|
let account: Account
|
||||||
|
let folder: Folder
|
||||||
|
let feedResource: FeedlyFeedResourceId
|
||||||
|
|
||||||
|
init(account: Account, folder: Folder, url: String, feedName: String?, collectionId: String, caller: FeedlyAPICaller) {
|
||||||
|
self.account = account
|
||||||
|
self.folder = folder
|
||||||
|
self.url = url
|
||||||
|
self.feedResource = FeedlyFeedResourceId(url: url)
|
||||||
|
self.feedName = feedName
|
||||||
|
self.collectionId = collectionId
|
||||||
|
self.caller = caller
|
||||||
|
}
|
||||||
|
|
||||||
|
private(set) var feedsAndFolders = [([FeedlyFeed], Folder)]()
|
||||||
|
|
||||||
|
var resource: FeedlyResourceId {
|
||||||
|
return feedResource
|
||||||
|
}
|
||||||
|
|
||||||
|
override func main() {
|
||||||
|
guard !isCancelled else {
|
||||||
|
return didFinish()
|
||||||
|
}
|
||||||
|
|
||||||
|
caller.addFeed(with: feedResource, title: feedName, toCollectionWith: collectionId) { [weak self] result in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard !self.isCancelled else { return self.didFinish() }
|
||||||
|
self.didCompleteRequest(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func didCompleteRequest(_ result: Result<[FeedlyFeed], Error>) {
|
||||||
|
switch result {
|
||||||
|
case .success(let feedlyFeeds):
|
||||||
|
feedsAndFolders = [(feedlyFeeds, folder)]
|
||||||
|
|
||||||
|
let feedsWithCreatedFeedId = feedlyFeeds.filter { $0.feedId == resource.id }
|
||||||
|
|
||||||
|
if feedsWithCreatedFeedId.isEmpty {
|
||||||
|
didFinish(AccountError.createErrorNotFound)
|
||||||
|
} else {
|
||||||
|
didFinish()
|
||||||
|
}
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
didFinish(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,10 +27,12 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
|
||||||
|
|
||||||
guard !isCancelled else { return }
|
guard !isCancelled else { return }
|
||||||
|
|
||||||
var localFeeds = account.flattenedFeeds()
|
|
||||||
let feedsBefore = localFeeds
|
|
||||||
let pairs = feedsAndFoldersProvider.feedsAndFolders
|
let pairs = feedsAndFoldersProvider.feedsAndFolders
|
||||||
|
|
||||||
|
let feedsBefore = Set(pairs
|
||||||
|
.map { $0.1 }
|
||||||
|
.flatMap { $0.topLevelFeeds })
|
||||||
|
|
||||||
// Remove feeds in a folder which are not in the corresponding collection.
|
// Remove feeds in a folder which are not in the corresponding collection.
|
||||||
for (collectionFeeds, folder) in pairs {
|
for (collectionFeeds, folder) in pairs {
|
||||||
let feedsInFolder = folder.topLevelFeeds
|
let feedsInFolder = folder.topLevelFeeds
|
||||||
|
@ -44,6 +46,8 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pair each Feed with its Folder.
|
// Pair each Feed with its Folder.
|
||||||
|
var feedsAdded = Set<Feed>()
|
||||||
|
|
||||||
let feedsAndFolders = pairs
|
let feedsAndFolders = pairs
|
||||||
.map({ (collectionFeeds, folder) -> [(FeedlyFeed, Folder)] in
|
.map({ (collectionFeeds, folder) -> [(FeedlyFeed, Folder)] in
|
||||||
return collectionFeeds.map { feed -> (FeedlyFeed, Folder) in
|
return collectionFeeds.map { feed -> (FeedlyFeed, Folder) in
|
||||||
|
@ -53,9 +57,12 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
|
||||||
.flatMap { $0 }
|
.flatMap { $0 }
|
||||||
.compactMap { (collectionFeed, folder) -> (Feed, Folder) in
|
.compactMap { (collectionFeed, folder) -> (Feed, Folder) in
|
||||||
|
|
||||||
// find an existing feed
|
// find an existing feed previously added to the account
|
||||||
for feed in localFeeds {
|
if let feed = account.existingFeed(withFeedID: collectionFeed.id) {
|
||||||
if feed.feedID == collectionFeed.id {
|
return (feed, folder)
|
||||||
|
} else {
|
||||||
|
// find an existing feed we created below in an earlier value
|
||||||
|
for feed in feedsAdded where feed.feedID == collectionFeed.id {
|
||||||
return (feed, folder)
|
return (feed, folder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +73,7 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
|
||||||
let feed = account.createFeed(with: collectionFeed.title, url: url, feedID: id, homePageURL: collectionFeed.website)
|
let feed = account.createFeed(with: collectionFeed.title, url: url, feedID: id, homePageURL: collectionFeed.website)
|
||||||
|
|
||||||
// So the same feed isn't created more than once.
|
// So the same feed isn't created more than once.
|
||||||
localFeeds.insert(feed)
|
feedsAdded.insert(feed)
|
||||||
|
|
||||||
return (feed, folder)
|
return (feed, folder)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,15 @@ protocol FeedlyEntryProviding: class {
|
||||||
/// Single responsibility is to get the stream content of a Collection from Feedly.
|
/// Single responsibility is to get the stream content of a Collection from Feedly.
|
||||||
final class FeedlyGetStreamOperation: FeedlyOperation, FeedlyEntryProviding {
|
final class FeedlyGetStreamOperation: FeedlyOperation, FeedlyEntryProviding {
|
||||||
|
|
||||||
private(set) var resource: FeedlyResourceId
|
struct ResourceProvider: FeedlyResourceProviding {
|
||||||
|
var resource: FeedlyResourceId
|
||||||
|
}
|
||||||
|
|
||||||
|
let resourceProvider: FeedlyResourceProviding
|
||||||
|
|
||||||
|
var resource: FeedlyResourceId {
|
||||||
|
return resourceProvider.resource
|
||||||
|
}
|
||||||
|
|
||||||
var entries: [FeedlyEntry] {
|
var entries: [FeedlyEntry] {
|
||||||
guard let entries = storedStream?.items else {
|
guard let entries = storedStream?.items else {
|
||||||
|
@ -55,19 +63,23 @@ final class FeedlyGetStreamOperation: FeedlyOperation, FeedlyEntryProviding {
|
||||||
|
|
||||||
init(account: Account, resource: FeedlyResourceId, caller: FeedlyAPICaller, newerThan: Date?, unreadOnly: Bool? = nil) {
|
init(account: Account, resource: FeedlyResourceId, caller: FeedlyAPICaller, newerThan: Date?, unreadOnly: Bool? = nil) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.resource = resource
|
self.resourceProvider = ResourceProvider(resource: resource)
|
||||||
self.caller = caller
|
self.caller = caller
|
||||||
self.unreadOnly = unreadOnly
|
self.unreadOnly = unreadOnly
|
||||||
self.newerThan = newerThan
|
self.newerThan = newerThan
|
||||||
}
|
}
|
||||||
|
|
||||||
|
convenience init(account: Account, resourceProvider: FeedlyResourceProviding, caller: FeedlyAPICaller, newerThan: Date?, unreadOnly: Bool? = nil) {
|
||||||
|
self.init(account: account, resource: resourceProvider.resource, caller: caller, newerThan: newerThan, unreadOnly: unreadOnly)
|
||||||
|
}
|
||||||
|
|
||||||
override func main() {
|
override func main() {
|
||||||
guard !isCancelled else {
|
guard !isCancelled else {
|
||||||
didFinish()
|
didFinish()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
caller.getStream(for: resource, newerThan: newerThan, unreadOnly: unreadOnly) { result in
|
caller.getStream(for: resourceProvider.resource, newerThan: newerThan, unreadOnly: unreadOnly) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let stream):
|
case .success(let stream):
|
||||||
self.storedStream = stream
|
self.storedStream = stream
|
||||||
|
|
Loading…
Reference in New Issue