diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index e59c109e3..eeefbb373 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -93,6 +93,9 @@ 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 */; }; + 9E7299D523505DC700DAEFB7 /* FeedlyAddFeedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7299D423505DC700DAEFB7 /* FeedlyAddFeedRequest.swift */; }; + 9E7299D723505E9600DAEFB7 /* FeedlyAddFeedOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7299D623505E9600DAEFB7 /* FeedlyAddFeedOperation.swift */; }; + 9E7299D9235062A200DAEFB7 /* FeedlyResourceProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7299D8235062A200DAEFB7 /* FeedlyResourceProviding.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 */; }; 9E7F150D2341F32000F860D1 /* macintosh_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 9E7F150C2341F32000F860D1 /* macintosh_initial.json */; }; @@ -118,7 +121,6 @@ 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 */; }; - 9EF35F7C234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF35F7B234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -269,6 +271,9 @@ 9E1D155C233447F000F4944C /* FeedlyUpdateAccountFeedsWithItemsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyUpdateAccountFeedsWithItemsOperation.swift; sourceTree = ""; }; 9E510D6D234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCreateFeedRequest.swift; sourceTree = ""; }; 9E713652233AD63E00765C84 /* FeedlyRefreshStreamEntriesStatusOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRefreshStreamEntriesStatusOperation.swift; sourceTree = ""; }; + 9E7299D423505DC700DAEFB7 /* FeedlyAddFeedRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyAddFeedRequest.swift; sourceTree = ""; }; + 9E7299D623505E9600DAEFB7 /* FeedlyAddFeedOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyAddFeedOperation.swift; sourceTree = ""; }; + 9E7299D8235062A200DAEFB7 /* FeedlyResourceProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyResourceProviding.swift; sourceTree = ""; }; 9E7F15062341E96700F860D1 /* AccountFeedlySyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFeedlySyncTest.swift; sourceTree = ""; }; 9E7F15092341EF5A00F860D1 /* feedly_collections_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = feedly_collections_initial.json; sourceTree = ""; }; 9E7F150C2341F32000F860D1 /* macintosh_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = macintosh_initial.json; sourceTree = ""; }; @@ -294,7 +299,6 @@ 9ECC9A84234DC16E009B5144 /* FeedlyAccountDelegateError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyAccountDelegateError.swift; sourceTree = ""; }; 9EE4CCF9234F106600FBAE4B /* FeedlyFeedContainerValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFeedContainerValidator.swift; sourceTree = ""; }; 9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCompoundOperation.swift; sourceTree = ""; }; - 9EF35F7B234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlySubscribeToFeedOperation.swift; sourceTree = ""; }; D511EEB5202422BB00712EC3 /* Account_project_debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_debug.xcconfig; sourceTree = ""; }; D511EEB6202422BB00712EC3 /* Account_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_target.xcconfig; sourceTree = ""; }; D511EEB7202422BB00712EC3 /* Account_project_release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_release.xcconfig; sourceTree = ""; }; @@ -554,9 +558,11 @@ 9EC688ED232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift */, 9EC688E9232B973C00A8D0A2 /* FeedlyAPICaller.swift */, 9E510D6D234F16A8002E6F1A /* FeedlyCreateFeedRequest.swift */, + 9E7299D423505DC700DAEFB7 /* FeedlyAddFeedRequest.swift */, 9E1D1554233431A600F4944C /* FeedlyOperation.swift */, 9EF35F79234E830E003AE2AE /* FeedlyCompoundOperation.swift */, - 9EF35F7B234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift */, + 9E7299D8235062A200DAEFB7 /* FeedlyResourceProviding.swift */, + 9E7299D623505E9600DAEFB7 /* FeedlyAddFeedOperation.swift */, 9EBC31B6233987C1002A567B /* FeedlyArticleStatusCoordinator.swift */, 9EBC31B32338AC2E002A567B /* Models */, 9EBC31B22338AC0F002A567B /* Refresh */, @@ -814,6 +820,7 @@ 552032F9229D5D5A009559E0 /* ReaderAPISubscription.swift in Sources */, 84C3654A1F899F3B001EC85C /* CombinedRefreshProgress.swift in Sources */, 9EC688EE232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift in Sources */, + 9E7299D9235062A200DAEFB7 /* FeedlyResourceProviding.swift in Sources */, 9EC688EC232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift in Sources */, 8469F81C1F6DD15E0084783E /* Account.swift in Sources */, 9EAEC60E2332FEC20085D7C9 /* FeedlyFeed.swift in Sources */, @@ -841,9 +848,9 @@ 9E1D15512334282100F4944C /* FeedlyMirrorCollectionsAsFoldersOperation.swift in Sources */, 9E1773D7234575AB0056A5A8 /* FeedlyTag.swift in Sources */, 515E4EB62324FF8C0057B0E7 /* URLRequest+RSWeb.swift in Sources */, + 9E7299D723505E9600DAEFB7 /* FeedlyAddFeedOperation.swift in Sources */, 5154367B228EEB28005E1CDF /* FeedbinImportResult.swift in Sources */, 84B2D4D02238CD8A00498ADA /* FeedMetadata.swift in Sources */, - 9EF35F7C234E9383003AE2AE /* FeedlySubscribeToFeedOperation.swift in Sources */, 9EAEC624233315F60085D7C9 /* FeedlyEntry.swift in Sources */, 5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */, 84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */, @@ -884,6 +891,7 @@ 9E1D1555233431A600F4944C /* FeedlyOperation.swift in Sources */, 84F1F06E2243524700DA0616 /* AccountMetadata.swift in Sources */, 9EBC31B7233987C1002A567B /* FeedlyArticleStatusCoordinator.swift in Sources */, + 9E7299D523505DC700DAEFB7 /* FeedlyAddFeedRequest.swift in Sources */, 84245C851FDDD8CB0074AFBB /* FeedbinSubscription.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Frameworks/Account/Feedly/FeedlySubscribeToFeedOperation.swift b/Frameworks/Account/Feedly/FeedlyAddFeedOperation.swift similarity index 65% rename from Frameworks/Account/Feedly/FeedlySubscribeToFeedOperation.swift rename to Frameworks/Account/Feedly/FeedlyAddFeedOperation.swift index 50744d694..60d2fd7b7 100644 --- a/Frameworks/Account/Feedly/FeedlySubscribeToFeedOperation.swift +++ b/Frameworks/Account/Feedly/FeedlyAddFeedOperation.swift @@ -1,19 +1,14 @@ // -// FeedlyCreateFeedOperation.swift +// FeedlyAddFeedOperation.swift // Account // -// Created by Kiel Gillard on 10/10/19. +// Created by Kiel Gillard on 11/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 +final class FeedlyAddFeedOperation: FeedlyOperation, FeedlyFeedsAndFoldersProviding, FeedlyResourceProviding { let feedName: String? let collectionId: String let caller: FeedlyAPICaller @@ -21,11 +16,15 @@ final class FeedlySubscribeToFeedOperation: FeedlyOperation, FeedlyFeedsAndFolde let folder: Folder let feedResource: FeedlyFeedResourceId - init(account: Account, folder: Folder, url: String, feedName: String?, collectionId: String, caller: FeedlyAPICaller) { + convenience init(account: Account, folder: Folder, url: String, feedName: String? = nil, collectionId: String, caller: FeedlyAPICaller) { + let resource = FeedlyFeedResourceId(url: url) + self.init(account: account, folder: folder, feedResource: resource, feedName: feedName, collectionId: collectionId, caller: caller) + } + + init(account: Account, folder: Folder, feedResource: FeedlyFeedResourceId, feedName: String? = nil, collectionId: String, caller: FeedlyAPICaller) { self.account = account self.folder = folder - self.url = url - self.feedResource = FeedlyFeedResourceId(url: url) + self.feedResource = feedResource self.feedName = feedName self.collectionId = collectionId self.caller = caller diff --git a/Frameworks/Account/Feedly/FeedlyAddFeedRequest.swift b/Frameworks/Account/Feedly/FeedlyAddFeedRequest.swift new file mode 100644 index 000000000..3bb53b827 --- /dev/null +++ b/Frameworks/Account/Feedly/FeedlyAddFeedRequest.swift @@ -0,0 +1,95 @@ +// +// FeedlyAddFeedRequest.swift +// Account +// +// Created by Kiel Gillard on 11/10/19. +// Copyright © 2019 Ranchero Software, LLC. All rights reserved. +// + +import Foundation +import os.log + +final class FeedlyAddFeedRequest { + 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) -> ())? + var error: Error? + + func feedlyOperation(_ operation: FeedlyOperation, didFailWith error: Error) { + self.error = error + } + } + + func start(adding feed: Feed, to container: Container, completion: @escaping (Result) -> 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 resource = FeedlyFeedResourceId(id: feed.feedID) + + let delegate = Delegate(resourceProvider: resource) + delegate.completionHandler = completion + + let addFeed = FeedlyCompoundOperation() { + let addRequest = FeedlyAddFeedOperation(account: account, folder: folder, feedResource: resource, collectionId: collectionId, caller: caller) + + let createFeeds = FeedlyCreateFeedsForCollectionFoldersOperation(account: account, feedsAndFoldersProvider: addRequest, log: log) + + createFeeds.addDependency(addRequest) + + let operations = [addRequest, createFeeds] + + 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: resource.id) { + handler(.success(feed)) + + } else { + handler(.failure(AccountError.createErrorNotFound)) + } + } + + callback.addDependency(addFeed) + + OperationQueue.main.addOperations([addFeed, callback], waitUntilFinished: false) + } +} diff --git a/Frameworks/Account/Feedly/FeedlyCreateFeedRequest.swift b/Frameworks/Account/Feedly/FeedlyCreateFeedRequest.swift index 3bc26684a..b22adf945 100644 --- a/Frameworks/Account/Feedly/FeedlyCreateFeedRequest.swift +++ b/Frameworks/Account/Feedly/FeedlyCreateFeedRequest.swift @@ -49,7 +49,7 @@ final class FeedlyCreateFeedRequest { } } - let subscribeRequest = FeedlySubscribeToFeedOperation(account: account, folder: folder, url: url, feedName: name, collectionId: collectionId, caller: caller) + let subscribeRequest = FeedlyAddFeedOperation(account: account, folder: folder, url: url, feedName: name, collectionId: collectionId, caller: caller) let delegate = Delegate(resourceProvider: subscribeRequest) delegate.completionHandler = completion diff --git a/Frameworks/Account/Feedly/FeedlyResourceProviding.swift b/Frameworks/Account/Feedly/FeedlyResourceProviding.swift new file mode 100644 index 000000000..c58d25a28 --- /dev/null +++ b/Frameworks/Account/Feedly/FeedlyResourceProviding.swift @@ -0,0 +1,20 @@ +// +// FeedlyResourceProviding.swift +// Account +// +// Created by Kiel Gillard on 11/10/19. +// Copyright © 2019 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +protocol FeedlyResourceProviding { + var resource: FeedlyResourceId { get } +} + +extension FeedlyFeedResourceId: FeedlyResourceProviding { + + var resource: FeedlyResourceId { + return self + } +}