diff --git a/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift b/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift index 16d333340..812085fbe 100644 --- a/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift +++ b/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift @@ -473,10 +473,23 @@ final class ReaderAPIAccountDelegate: AccountDelegate { if from is Account { addWebFeed(for: account, with: feed, to: to, completion: completion) } else { - deleteTagging(for: account, with: feed, from: from) { result in + guard + let subscriptionId = feed.externalID, + let fromTag = (from as? Folder)?.name, + let toTag = (to as? Folder)?.name + else { + completion(.failure(ReaderAPIAccountDelegateError.invalidParameter)) + return + } + + refreshProgress.addToNumberOfTasksAndRemaining(1) + caller.moveSubscription(subscriptionID: subscriptionId, fromTag: fromTag, toTag: toTag) { result in + self.refreshProgress.completeTask() switch result { case .success: - self.addWebFeed(for: account, with: feed, to: to, completion: completion) + from.removeWebFeed(feed) + to.addWebFeed(feed) + completion(.success(())) case .failure(let error): completion(.failure(error)) } @@ -1008,8 +1021,13 @@ private extension ReaderAPIAccountDelegate { guard let streamID = entry.origin.streamId else { return nil } - // let authors = Set([ParsedAuthor(name: entry.authorName, url: entry.jsonFeed?.jsonFeedAuthor?.url, avatarURL: entry.jsonFeed?.jsonFeedAuthor?.avatarURL, emailAddress: nil)]) - // let feed = account.idToFeedDictionary[entry.origin.streamId!]! // TODO clean this up + + var authors: Set? { + guard let name = entry.author else { + return nil + } + return Set([ParsedAuthor(name: name, url: nil, avatarURL: nil, emailAddress: nil)]) + } return ParsedItem(syncServiceID: entry.uniqueID(variant: variant), uniqueID: entry.uniqueID(variant: variant), @@ -1025,7 +1043,7 @@ private extension ReaderAPIAccountDelegate { bannerImageURL: nil, datePublished: entry.parseDatePublished(), dateModified: nil, - authors: nil, + authors: authors, tags: nil, attachments: nil) } @@ -1132,32 +1150,4 @@ private extension ReaderAPIAccountDelegate { } - func deleteTagging(for account: Account, with feed: WebFeed, from container: Container?, completion: @escaping (Result) -> Void) { - - if let folder = container as? Folder, let feedName = feed.externalID { - caller.deleteTagging(subscriptionID: feedName, tagName: folder.name ?? "") { result in - switch result { - case .success: - DispatchQueue.main.async { - self.clearFolderRelationship(for: feed, folderExternalID: folder.externalID) - folder.removeWebFeed(feed) - account.addFeedIfNotInAnyFolder(feed) - completion(.success(())) - } - case .failure(let error): - DispatchQueue.main.async { - let wrappedError = AccountError.wrappedError(error: error, account: account) - completion(.failure(wrappedError)) - } - } - } - } else { - if let account = container as? Account { - account.removeWebFeed(feed) - } - completion(.success(())) - } - - } - } diff --git a/Account/Sources/Account/ReaderAPI/ReaderAPICaller.swift b/Account/Sources/Account/ReaderAPI/ReaderAPICaller.swift index 805fd3b8d..a769d956c 100644 --- a/Account/Sources/Account/ReaderAPI/ReaderAPICaller.swift +++ b/Account/Sources/Account/ReaderAPI/ReaderAPICaller.swift @@ -358,21 +358,19 @@ final class ReaderAPICaller: NSObject { self.requestAuthorizationToken(endpoint: baseURL) { (result) in switch result { case .success(let token): - let url = baseURL + let callURL = baseURL .appendingPathComponent(ReaderAPIEndpoints.subscriptionAdd.rawValue) - .appendingQueryItem(URLQueryItem(name: "quickadd", value: url)) - - guard let callURL = url else { - completion(.failure(TransportError.noURL)) - return - } - + var request = URLRequest(url: callURL, credentials: self.credentials) self.addVariantHeaders(&request) request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") request.httpMethod = "POST" - - let postData = "T=\(token)".data(using: String.Encoding.utf8) + + guard let encodedFeedURL = self.encodeForURLPath(url) else { + completion(.failure(ReaderAPIAccountDelegateError.invalidParameter)) + return + } + let postData = "T=\(token)&quickadd=\(encodedFeedURL)".data(using: String.Encoding.utf8) self.transport.send(request: request, method: HTTPMethod.post, data: postData!, resultType: ReaderAPIQuickAddResult.self, completion: { (result) in switch result { @@ -387,34 +385,7 @@ final class ReaderAPICaller: NSObject { return } - if name == nil && folder == nil { - findSubscription(streamID: streamId, completion: completion) - } else { - let callURL = baseURL.appendingPathComponent(ReaderAPIEndpoints.subscriptionEdit.rawValue) - var request = URLRequest(url: callURL, credentials: self.credentials) - self.addVariantHeaders(&request) - request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") - request.httpMethod = "POST" - - var postString = "T=\(token)&ac=subscribe&s=\(streamId)" - if let folderName = self.encodeForURLPath(folder?.nameForDisplay) { - postString += "&a=user/-/label/\(folderName)" - } - if let name = self.encodeForURLPath(name) { - postString += "&t=\(name)" - } - - let postData = postString.data(using: String.Encoding.utf8) - self.transport.send(request: request, method: HTTPMethod.post, payload: postData!, completion: { (result) in - switch result { - case .success: - findSubscription(streamID: streamId, completion: completion) - case .failure: - completion(.failure(AccountError.createErrorAlreadySubscribed)) - } - }) - } - + findSubscription(streamID: streamId, completion: completion) } case .failure(let error): @@ -432,42 +403,7 @@ final class ReaderAPICaller: NSObject { } func renameSubscription(subscriptionID: String, newName: String, completion: @escaping (Result) -> Void) { - guard let baseURL = apiBaseURL else { - completion(.failure(CredentialsError.incompleteCredentials)) - return - } - - self.requestAuthorizationToken(endpoint: baseURL) { (result) in - switch result { - case .success(let token): - var request = URLRequest(url: baseURL.appendingPathComponent(ReaderAPIEndpoints.subscriptionEdit.rawValue), credentials: self.credentials) - self.addVariantHeaders(&request) - request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") - request.httpMethod = "POST" - - guard let encodedNewName = self.encodeForURLPath(newName) else { - completion(.failure(ReaderAPIAccountDelegateError.invalidParameter)) - return - } - - let postData = "T=\(token)&s=\(subscriptionID)&ac=edit&t=\(encodedNewName)".data(using: String.Encoding.utf8) - - self.transport.send(request: request, method: HTTPMethod.post, payload: postData!, completion: { (result) in - switch result { - case .success: - completion(.success(())) - break - case .failure(let error): - completion(.failure(error)) - break - } - }) - - - case .failure(let error): - completion(.failure(error)) - } - } + changeSubscription(subscriptionID: subscriptionID, title: newName, completion: completion) } func deleteSubscription(subscriptionID: String, completion: @escaping (Result) -> Void) { @@ -504,46 +440,23 @@ final class ReaderAPICaller: NSObject { } func createTagging(subscriptionID: String, tagName: String, completion: @escaping (Result) -> Void) { - - guard let baseURL = apiBaseURL else { - completion(.failure(CredentialsError.incompleteCredentials)) - return - } - - self.requestAuthorizationToken(endpoint: baseURL) { (result) in - switch result { - case .success(let token): - var request = URLRequest(url: baseURL.appendingPathComponent(ReaderAPIEndpoints.subscriptionEdit.rawValue), credentials: self.credentials) - self.addVariantHeaders(&request) - request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") - request.httpMethod = "POST" - - guard let tagName = self.encodeForURLPath(tagName) else { - completion(.failure(ReaderAPIAccountDelegateError.invalidParameter)) - return - } - - let postData = "T=\(token)&s=\(subscriptionID)&ac=edit&a=user/-/label/\(tagName)".data(using: String.Encoding.utf8) - - self.transport.send(request: request, method: HTTPMethod.post, payload: postData!, completion: { (result) in - switch result { - case .success: - completion(.success(())) - break - case .failure(let error): - completion(.failure(error)) - break - } - }) - - case .failure(let error): - completion(.failure(error)) - } - } - + changeSubscription(subscriptionID: subscriptionID, addTagName: tagName, completion: completion) } - + func deleteTagging(subscriptionID: String, tagName: String, completion: @escaping (Result) -> Void) { + changeSubscription(subscriptionID: subscriptionID, removeTagName: tagName, completion: completion) + } + + func moveSubscription(subscriptionID: String, fromTag: String, toTag: String, completion: @escaping (Result) -> Void) { + changeSubscription(subscriptionID: subscriptionID, removeTagName: fromTag, addTagName: toTag, completion: completion) + } + + private func changeSubscription(subscriptionID: String, removeTagName: String? = nil, addTagName: String? = nil, title: String? = nil, completion: @escaping (Result) -> Void) { + guard removeTagName != nil || addTagName != nil || title != nil else { + completion(.failure(ReaderAPIAccountDelegateError.invalidParameter)) + return + } + guard let baseURL = apiBaseURL else { completion(.failure(CredentialsError.incompleteCredentials)) return @@ -557,8 +470,17 @@ final class ReaderAPICaller: NSObject { request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") request.httpMethod = "POST" - let tagName = "user/-/label/\(tagName)" - let postData = "T=\(token)&s=\(subscriptionID)&ac=edit&r=\(tagName)".data(using: String.Encoding.utf8) + var postString = "T=\(token)&s=\(subscriptionID)&ac=edit" + if let fromLabel = self.encodeForURLPath(removeTagName) { + postString += "&r=user/-/label/\(fromLabel)" + } + if let toLabel = self.encodeForURLPath(addTagName) { + postString += "&a=user/-/label/\(toLabel)" + } + if let encodedTitle = self.encodeForURLPath(title) { + postString += "&t=\(encodedTitle)" + } + let postData = postString.data(using: String.Encoding.utf8) self.transport.send(request: request, method: HTTPMethod.post, payload: postData!, completion: { (result) in switch result { diff --git a/Account/Sources/Account/ReaderAPI/ReaderAPIEntry.swift b/Account/Sources/Account/ReaderAPI/ReaderAPIEntry.swift index 41abfe4be..5d8575945 100644 --- a/Account/Sources/Account/ReaderAPI/ReaderAPIEntry.swift +++ b/Account/Sources/Account/ReaderAPI/ReaderAPIEntry.swift @@ -51,6 +51,7 @@ struct ReaderAPIEntry: Codable { let articleID: String let title: String? + let author: String? let publishedTimestamp: Double? let crawledTimestamp: String? @@ -64,6 +65,7 @@ struct ReaderAPIEntry: Codable { enum CodingKeys: String, CodingKey { case articleID = "id" case title = "title" + case author = "author" case summary = "summary" case alternates = "alternate" case categories = "categories"