From d6ae740305da9295f64197d5a5a77c288a3e6e46 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 6 May 2019 17:34:41 -0500 Subject: [PATCH] Added folder (tag) delete for Feedbin --- Frameworks/Account/Account.swift | 10 ++++--- .../Account/Account.xcodeproj/project.pbxproj | 4 --- Frameworks/Account/AccountDelegate.swift | 5 ++-- .../Account/AccountTests/TestTransport.swift | 2 +- Frameworks/Account/Container.swift | 2 +- .../Account/Feedbin/FeedbinAPICaller.swift | 26 ++++++++++++++++--- .../Feedbin/FeedbinAccountDelegate.swift | 24 ++++++++++++++++- .../Account/Feedbin/FeedbinRenameTag.swift | 21 --------------- Frameworks/Account/Feedbin/FeedbinTag.swift | 22 ++++++++++++++++ Frameworks/Account/Folder.swift | 4 +-- .../LocalAccount/LocalAccountDelegate.swift | 7 ++++- Mac/Scriptability/Account+Scriptability.swift | 23 ++++++++-------- Mac/Scriptability/Folder+Scriptability.swift | 2 +- Shared/Commands/DeleteCommand.swift | 13 +++++++++- submodules/RSWeb | 2 +- 15 files changed, 114 insertions(+), 53 deletions(-) delete mode 100644 Frameworks/Account/Feedbin/FeedbinRenameTag.swift diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index 0c8a19279..f72af1db2 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -419,7 +419,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, } public func renameFolder(_ folder: Folder, to name: String, completion: @escaping (Result) -> Void) { - delegate.renameFolder(folder, to: name, completion: completion) + delegate.renameFolder(for: self, with: folder, to: name, completion: completion) } public func importOPML(_ opmlDocument: RSOPMLDocument) { @@ -597,12 +597,16 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, postChildrenDidChangeNotification() } - public func deleteFolder(_ folder: Folder) { + public func deleteFolder(_ folder: Folder, completion: @escaping (Result) -> Void) { + delegate.deleteFolder(for: self, with: folder, completion: completion) + } + + func deleteFolder(_ folder: Folder) { folders?.remove(folder) structureDidChange() postChildrenDidChangeNotification() } - + // MARK: - Debug public func debugDropConditionalGetInfo() { diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index 5e39f32d4..879eeffa5 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -17,7 +17,6 @@ 51D5875B227F630B00900287 /* tags_add.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58758227F630B00900287 /* tags_add.json */; }; 51D5875C227F630B00900287 /* tags_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58759227F630B00900287 /* tags_initial.json */; }; 51D5875E227F643C00900287 /* AccountFolderSyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D5875D227F643C00900287 /* AccountFolderSyncTest.swift */; }; - 51D587642280594700900287 /* FeedbinRenameTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D5875F22804FD200900287 /* FeedbinRenameTag.swift */; }; 841973FE1F6DD1BC006346C4 /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841973EF1F6DD19E006346C4 /* RSCore.framework */; }; 841973FF1F6DD1C5006346C4 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841973FA1F6DD1AC006346C4 /* RSParser.framework */; }; 841974011F6DD1EC006346C4 /* Folder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841974001F6DD1EC006346C4 /* Folder.swift */; }; @@ -103,7 +102,6 @@ 51D58758227F630B00900287 /* tags_add.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_add.json; sourceTree = ""; }; 51D58759227F630B00900287 /* tags_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_initial.json; sourceTree = ""; }; 51D5875D227F643C00900287 /* AccountFolderSyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFolderSyncTest.swift; sourceTree = ""; }; - 51D5875F22804FD200900287 /* FeedbinRenameTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinRenameTag.swift; sourceTree = ""; }; 841973E81F6DD19E006346C4 /* RSCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSCore.xcodeproj; path = ../RSCore/RSCore.xcodeproj; sourceTree = ""; }; 841973F41F6DD1AC006346C4 /* RSParser.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSParser.xcodeproj; path = ../RSParser/RSParser.xcodeproj; sourceTree = ""; }; 841974001F6DD1EC006346C4 /* Folder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Folder.swift; sourceTree = ""; }; @@ -214,7 +212,6 @@ 84245C841FDDD8CB0074AFBB /* FeedbinFeed.swift */, 84D0962421741B8500D77525 /* FeedbinSavedSearch.swift */, 51D58754227F53BE00900287 /* FeedbinTag.swift */, - 51D5875F22804FD200900287 /* FeedbinRenameTag.swift */, 84D09622217418DC00D77525 /* FeedbinTagging.swift */, ); path = Feedbin; @@ -457,7 +454,6 @@ files = ( 84C8B3F41F89DE430053CCA6 /* DataExtensions.swift in Sources */, 84C3654A1F899F3B001EC85C /* CombinedRefreshProgress.swift in Sources */, - 51D587642280594700900287 /* FeedbinRenameTag.swift in Sources */, 8469F81C1F6DD15E0084783E /* Account.swift in Sources */, 84D096212174169100D77525 /* FeedbinArticleIDArray.swift in Sources */, 5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */, diff --git a/Frameworks/Account/AccountDelegate.swift b/Frameworks/Account/AccountDelegate.swift index a00c757b6..5cbf903d1 100644 --- a/Frameworks/Account/AccountDelegate.swift +++ b/Frameworks/Account/AccountDelegate.swift @@ -21,8 +21,9 @@ protocol AccountDelegate { func refreshAll(for: Account, completion: (() -> Void)?) - func renameFolder(_ folder: Folder, to name: String, completion: @escaping (Result) -> Void) - + func renameFolder(for: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) + func deleteFolder(for: Account, with folder: Folder, completion: @escaping (Result) -> Void) + // Called at the end of account’s init method. func accountDidInitialize(_ account: Account) diff --git a/Frameworks/Account/AccountTests/TestTransport.swift b/Frameworks/Account/AccountTests/TestTransport.swift index c84655916..2f6e1c4bc 100644 --- a/Frameworks/Account/AccountTests/TestTransport.swift +++ b/Frameworks/Account/AccountTests/TestTransport.swift @@ -31,7 +31,7 @@ final class TestTransport: Transport { } - func send(request: URLRequest, data: Data, completion: @escaping (Result<(HTTPHeaders, Data?), Error>) -> Void) { + func send(request: URLRequest, payload: Data, completion: @escaping (Result<(HTTPHeaders, Data?), Error>) -> Void) { } diff --git a/Frameworks/Account/Container.swift b/Frameworks/Account/Container.swift index 0ecd7f6d7..16c2df398 100644 --- a/Frameworks/Account/Container.swift +++ b/Frameworks/Account/Container.swift @@ -28,7 +28,7 @@ public protocol Container: class { func childFolder(with: String) -> Folder? func deleteFeed(_ feed: Feed) - func deleteFolder(_ folder: Folder) + func deleteFolder(_ folder: Folder, completion: @escaping (Result) -> Void) func addFeed(_ feed: Feed) func addFeeds(_ feeds: Set) diff --git a/Frameworks/Account/Feedbin/FeedbinAPICaller.swift b/Frameworks/Account/Feedbin/FeedbinAPICaller.swift index 838734166..f5a5f55ea 100644 --- a/Frameworks/Account/Feedbin/FeedbinAPICaller.swift +++ b/Frameworks/Account/Feedbin/FeedbinAPICaller.swift @@ -52,8 +52,9 @@ final class FeedbinAPICaller: NSObject { let callURL = feedbinBaseURL.appendingPathComponent("tags.json") let conditionalGet = accountMetadata?.conditionalGetInfo[AccountMetadata.ConditionalGetKeys.tags] let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet) - - transport.getJSON(request: request, resultType: [FeedbinTag].self) { [weak self] result in + + transport.send(request: request, resultType: [FeedbinTag].self) { [weak self] result in + switch result { case .success(let (headers, tags)): self?.storeConditionalGet(metadata: self?.accountMetadata, key: AccountMetadata.ConditionalGetKeys.tags, headers: headers) @@ -70,7 +71,26 @@ final class FeedbinAPICaller: NSObject { let callURL = feedbinBaseURL.appendingPathComponent("tags.json") let request = URLRequest(url: callURL, credentials: credentials) let payload = FeedbinRenameTag(oldName: oldName, newName: newName) - transport.postJSON(request: request, payload: payload, completion: completion) + transport.send(request: request, method: HTTPMethod.post, payload: payload, completion: completion) + } + + func deleteTag(name: String, completion: @escaping (Result<[FeedbinTagging]?, Error>) -> Void) { + + let callURL = feedbinBaseURL.appendingPathComponent("tags.json") + let request = URLRequest(url: callURL, credentials: credentials) + let payload = FeedbinDeleteTag(name: name) + + transport.send(request: request, method: HTTPMethod.delete, payload: payload, resultType: [FeedbinTagging].self) { result in + + switch result { + case .success(let (_, taggings)): + completion(.success(taggings)) + case .failure(let error): + completion(.failure(error)) + } + + } + } } diff --git a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift index 39ae79b30..19368b4d6 100644 --- a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift +++ b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift @@ -49,7 +49,7 @@ final class FeedbinAccountDelegate: AccountDelegate { } } - func renameFolder(_ folder: Folder, to name: String, completion: @escaping (Result) -> Void) { + func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) { caller.renameTag(oldName: folder.name ?? "", newName: name) { result in switch result { @@ -62,6 +62,28 @@ final class FeedbinAccountDelegate: AccountDelegate { } } + + func deleteFolder(for account: Account, with folder: Folder, completion: @escaping (Result) -> Void) { + + // Feedbin uses tags and if at least one feed isn't tagged, then the folder doesn't exist on their system + guard folder.hasAtLeastOneFeed() else { + account.deleteFolder(folder) + return + } + + caller.deleteTag(name: folder.name ?? "") { result in + switch result { + case .success: + account.deleteFolder(folder) + // TODO: Take the serialized taggings and reestablish the folder to feed relationships. Deleting + // a tag on Feedbin doesn't any feeds. + completion(.success(())) + case .failure(let error): + completion(.failure(error)) + } + } + + } func accountDidInitialize(_ account: Account) { credentials = try? account.retrieveBasicCredentials() diff --git a/Frameworks/Account/Feedbin/FeedbinRenameTag.swift b/Frameworks/Account/Feedbin/FeedbinRenameTag.swift deleted file mode 100644 index 49768c096..000000000 --- a/Frameworks/Account/Feedbin/FeedbinRenameTag.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// FeedbinRenameTag.swift -// Account -// -// Created by Maurice Parker on 5/6/19. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -struct FeedbinRenameTag: Codable { - - let oldName: String - let newName: String - - enum CodingKeys: String, CodingKey { - case oldName = "old_name" - case newName = "new_name" - } - -} diff --git a/Frameworks/Account/Feedbin/FeedbinTag.swift b/Frameworks/Account/Feedbin/FeedbinTag.swift index e603465f9..398228bb1 100644 --- a/Frameworks/Account/Feedbin/FeedbinTag.swift +++ b/Frameworks/Account/Feedbin/FeedbinTag.swift @@ -19,3 +19,25 @@ struct FeedbinTag: Codable, Equatable, Hashable { } } + +struct FeedbinRenameTag: Codable { + + let oldName: String + let newName: String + + enum CodingKeys: String, CodingKey { + case oldName = "old_name" + case newName = "new_name" + } + +} + +struct FeedbinDeleteTag: Codable { + + let name: String + + enum CodingKeys: String, CodingKey { + case name + } + +} diff --git a/Frameworks/Account/Folder.swift b/Frameworks/Account/Folder.swift index bcb912d51..3a979dbf3 100644 --- a/Frameworks/Account/Folder.swift +++ b/Frameworks/Account/Folder.swift @@ -100,8 +100,8 @@ public final class Folder: DisplayNameProvider, Renamable, Container, UnreadCoun postChildrenDidChangeNotification() } - public func deleteFolder(_ folder: Folder) { - // Nothing to do + public func deleteFolder(_ folder: Folder, completion: @escaping (Result) -> Void) { + completion(.success(())) } // MARK: - Hashable diff --git a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift index 2f05729eb..56f727c06 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift @@ -28,11 +28,16 @@ final class LocalAccountDelegate: AccountDelegate { completion?() } - func renameFolder(_ folder: Folder, to name: String, completion: @escaping (Result) -> Void) { + func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) { folder.name = name completion(.success(())) } + func deleteFolder(for account: Account, with folder: Folder, completion: @escaping (Result) -> Void) { + account.deleteFolder(folder) + completion(.success(())) + } + func accountDidInitialize(_ account: Account) { } diff --git a/Mac/Scriptability/Account+Scriptability.swift b/Mac/Scriptability/Account+Scriptability.swift index fa56d8dbf..364b6e342 100644 --- a/Mac/Scriptability/Account+Scriptability.swift +++ b/Mac/Scriptability/Account+Scriptability.swift @@ -47,17 +47,18 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta return self.classDescription as! NSScriptClassDescription } - func deleteElement(_ element:ScriptingObject) { - if let scriptableFolder = element as? ScriptableFolder { - BatchUpdate.shared.perform { - account.deleteFolder(scriptableFolder.folder) - } - } else if let scriptableFeed = element as? ScriptableFeed { - BatchUpdate.shared.perform { - account.deleteFeed(scriptableFeed.feed) - } - } - } + func deleteElement(_ element:ScriptingObject) { + if let scriptableFolder = element as? ScriptableFolder { + BatchUpdate.shared.perform { + account.deleteFolder(scriptableFolder.folder) { result in + } + } + } else if let scriptableFeed = element as? ScriptableFeed { + BatchUpdate.shared.perform { + account.deleteFeed(scriptableFeed.feed) + } + } + } @objc(isLocationRequiredToCreateForKey:) func isLocationRequiredToCreate(forKey key:String) -> Bool { diff --git a/Mac/Scriptability/Folder+Scriptability.swift b/Mac/Scriptability/Folder+Scriptability.swift index 5ef9352ce..b71535e1b 100644 --- a/Mac/Scriptability/Folder+Scriptability.swift +++ b/Mac/Scriptability/Folder+Scriptability.swift @@ -53,7 +53,7 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai func deleteElement(_ element:ScriptingObject) { if let scriptableFolder = element as? ScriptableFolder { BatchUpdate.shared.perform { - folder.deleteFolder(scriptableFolder.folder) + folder.deleteFolder(scriptableFolder.folder) { result in } } } else if let scriptableFeed = element as? ScriptableFeed { BatchUpdate.shared.perform { diff --git a/Shared/Commands/DeleteCommand.swift b/Shared/Commands/DeleteCommand.swift index 995b9dfec..61f621da7 100644 --- a/Shared/Commands/DeleteCommand.swift +++ b/Shared/Commands/DeleteCommand.swift @@ -142,7 +142,18 @@ private struct SidebarItemSpecifier { container.deleteFeed(feed) } else if let folder = folder { - container.deleteFolder(folder) + container.deleteFolder(folder) { result in + switch result { + case .success(): + break + case .failure(let error): + #if os(macOS) + NSApplication.shared.presentError(error) + #else + UIApplication.shared.presentError(error) + #endif + } + } } } diff --git a/submodules/RSWeb b/submodules/RSWeb index 72c45431f..d1d5eba95 160000 --- a/submodules/RSWeb +++ b/submodules/RSWeb @@ -1 +1 @@ -Subproject commit 72c45431fbba0bf1c17d11555c8f75ea963bcb5b +Subproject commit d1d5eba957eefec54b9a8c8648024a389c2271f0