Use best feed specifier code to determine which Feedbin option to use when creating a feed
This commit is contained in:
parent
a7d1014d5b
commit
fe70723eb4
@ -36,6 +36,11 @@ public enum AccountType: Int {
|
||||
// TODO: more
|
||||
}
|
||||
|
||||
public enum AccountError: Error {
|
||||
case createErrorNotFound
|
||||
case createErrorAlreadySubscribed
|
||||
}
|
||||
|
||||
public final class Account: DisplayNameProvider, UnreadCountProvider, Container, Hashable {
|
||||
|
||||
public struct UserInfoKey {
|
||||
@ -362,7 +367,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
delegate.removeFeed(for: self, from: container, with: feed, completion: completion)
|
||||
}
|
||||
|
||||
public func createFeed(url: String, completion: @escaping (Result<AccountCreateFeedResult, Error>) -> Void) {
|
||||
public func createFeed(url: String, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||
delegate.createFeed(for: self, url: url, completion: completion)
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
5165D71622821C2400D9D53D /* taggings_delete.json in Resources */ = {isa = PBXBuildFile; fileRef = 5165D71322821C2400D9D53D /* taggings_delete.json */; };
|
||||
5165D71722821C2400D9D53D /* taggings_add.json in Resources */ = {isa = PBXBuildFile; fileRef = 5165D71422821C2400D9D53D /* taggings_add.json */; };
|
||||
5165D71822821C2400D9D53D /* taggings_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 5165D71522821C2400D9D53D /* taggings_initial.json */; };
|
||||
5165D71B22833A7500D9D53D /* AccountCreateFeedResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D7192283398700D9D53D /* AccountCreateFeedResult.swift */; };
|
||||
5165D72822835F7800D9D53D /* FeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D71C22835E9800D9D53D /* FeedFinder.swift */; };
|
||||
5165D72922835F7A00D9D53D /* FeedSpecifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D71D22835E9800D9D53D /* FeedSpecifier.swift */; };
|
||||
5165D72A22835F7D00D9D53D /* HTMLFeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D71E22835E9800D9D53D /* HTMLFeedFinder.swift */; };
|
||||
@ -118,7 +117,6 @@
|
||||
5165D71322821C2400D9D53D /* taggings_delete.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = taggings_delete.json; sourceTree = "<group>"; };
|
||||
5165D71422821C2400D9D53D /* taggings_add.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = taggings_add.json; sourceTree = "<group>"; };
|
||||
5165D71522821C2400D9D53D /* taggings_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = taggings_initial.json; sourceTree = "<group>"; };
|
||||
5165D7192283398700D9D53D /* AccountCreateFeedResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCreateFeedResult.swift; sourceTree = "<group>"; };
|
||||
5165D71C22835E9800D9D53D /* FeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedFinder.swift; sourceTree = "<group>"; };
|
||||
5165D71D22835E9800D9D53D /* FeedSpecifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedSpecifier.swift; sourceTree = "<group>"; };
|
||||
5165D71E22835E9800D9D53D /* HTMLFeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLFeedFinder.swift; sourceTree = "<group>"; };
|
||||
@ -274,7 +272,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
848935101F62486800CEBD24 /* Account.swift */,
|
||||
5165D7192283398700D9D53D /* AccountCreateFeedResult.swift */,
|
||||
841974241F6DDCE4006346C4 /* AccountDelegate.swift */,
|
||||
846E77531F6F00E300A165E2 /* AccountManager.swift */,
|
||||
84AF4EA3222CFDD100F6A800 /* AccountMetadata.swift */,
|
||||
@ -508,7 +505,6 @@
|
||||
5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */,
|
||||
846E77451F6EF9B900A165E2 /* Container.swift in Sources */,
|
||||
84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */,
|
||||
5165D71B22833A7500D9D53D /* AccountCreateFeedResult.swift in Sources */,
|
||||
841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */,
|
||||
5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */,
|
||||
846E77541F6F00E300A165E2 /* AccountManager.swift in Sources */,
|
||||
|
@ -1,21 +0,0 @@
|
||||
//
|
||||
// AccountCreateFeedResult.swift
|
||||
// AccountTests
|
||||
//
|
||||
// Created by Maurice Parker on 5/8/19.
|
||||
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum AccountCreateFeedResult {
|
||||
case created(Feed)
|
||||
case multipleChoice([AccountCreateFeedChoice])
|
||||
case alreadySubscribed
|
||||
case notFound
|
||||
}
|
||||
|
||||
public struct AccountCreateFeedChoice {
|
||||
let name: String
|
||||
let url: String
|
||||
}
|
@ -24,7 +24,7 @@ protocol AccountDelegate {
|
||||
func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
func deleteFolder(for account: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
|
||||
func createFeed(for account: Account, url: String, completion: @escaping (Result<AccountCreateFeedResult, Error>) -> Void)
|
||||
func createFeed(for account: Account, url: String, completion: @escaping (Result<Feed, Error>) -> Void)
|
||||
func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
func deleteFeed(for account: Account, with feed: Feed, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
|
||||
|
@ -11,7 +11,7 @@ import RSWeb
|
||||
|
||||
enum CreateSubscriptionResult {
|
||||
case created(FeedbinSubscription)
|
||||
case multipleChoice([FeedbinSubscription])
|
||||
case multipleChoice([FeedbinSubscriptionChoice])
|
||||
case alreadySubscribed
|
||||
case notFound
|
||||
}
|
||||
@ -157,7 +157,7 @@ final class FeedbinAPICaller: NSObject {
|
||||
break
|
||||
}
|
||||
do {
|
||||
let subscriptions = try JSONDecoder().decode([FeedbinSubscription].self, from: subData)
|
||||
let subscriptions = try JSONDecoder().decode([FeedbinSubscriptionChoice].self, from: subData)
|
||||
completion(.success(.multipleChoice(subscriptions)))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
|
@ -18,7 +18,6 @@ import os.log
|
||||
|
||||
public enum FeedbinAccountDelegateError: String, Error {
|
||||
case invalidParameter = "There was an invalid parameter passed."
|
||||
case invalidResponse = "An invalid response was received from the service."
|
||||
}
|
||||
|
||||
final class FeedbinAccountDelegate: AccountDelegate {
|
||||
@ -115,30 +114,23 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
||||
|
||||
}
|
||||
|
||||
func createFeed(for account: Account, url: String, completion: @escaping (Result<AccountCreateFeedResult, Error>) -> Void) {
|
||||
func createFeed(for account: Account, url: String, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||
|
||||
caller.createSubscription(url: url) { result in
|
||||
caller.createSubscription(url: url) { [weak self] result in
|
||||
switch result {
|
||||
case .success(let subResult):
|
||||
switch subResult {
|
||||
case .created(let sub):
|
||||
DispatchQueue.main.async {
|
||||
let feed = account.createFeed(with: sub.name, url: sub.url, feedID: String(sub.feedID), homePageURL: sub.homePageURL)
|
||||
feed.subscriptionID = String(sub.subscriptionID)
|
||||
completion(.success(.created(feed)))
|
||||
}
|
||||
case .multipleChoice(let subs):
|
||||
let resultSubs = subs.map { sub in return AccountCreateFeedChoice(name: sub.name ?? "", url: sub.url) }
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(.multipleChoice(resultSubs)))
|
||||
}
|
||||
case .created(let subscription):
|
||||
self?.createFeed(account: account, subscription: subscription, completion: completion)
|
||||
case .multipleChoice(let choices):
|
||||
self?.decideBestFeedChoice(account: account, url: url, choices: choices, completion: completion)
|
||||
case .alreadySubscribed:
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(.alreadySubscribed))
|
||||
completion(.failure(AccountError.createErrorAlreadySubscribed))
|
||||
}
|
||||
case .notFound:
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(.notFound))
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
@ -262,25 +254,14 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
||||
let editedName = feed.editedName
|
||||
|
||||
createFeed(for: account, url: feed.url) { [weak self] result in
|
||||
|
||||
switch result {
|
||||
case .success(let createResult):
|
||||
|
||||
switch createResult {
|
||||
case .created(let feed):
|
||||
case .success(let feed):
|
||||
self?.processRestoredFeed(for: account, feed: feed, editedName: editedName, folder: folder, completion: completion)
|
||||
default:
|
||||
DispatchQueue.main.async {
|
||||
completion(.failure(FeedbinAccountDelegateError.invalidResponse))
|
||||
}
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
DispatchQueue.main.async {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -662,4 +643,36 @@ private extension FeedbinAccountDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func decideBestFeedChoice(account: Account, url: String, choices: [FeedbinSubscriptionChoice], completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||
|
||||
let feedSpecifiers: [FeedSpecifier] = choices.map { choice in
|
||||
let source = url == choice.url ? FeedSpecifier.Source.UserEntered : FeedSpecifier.Source.HTMLLink
|
||||
let specifier = FeedSpecifier(title: choice.name, urlString: choice.url, source: source)
|
||||
return specifier
|
||||
}
|
||||
|
||||
if let bestSpecifier = FeedSpecifier.bestFeed(in: Set(feedSpecifiers)) {
|
||||
if let bestSubscription = choices.filter({ bestSpecifier.urlString == $0.url }).first {
|
||||
createFeed(for: account, url: bestSubscription.url, completion: completion)
|
||||
} else {
|
||||
DispatchQueue.main.async {
|
||||
completion(.failure(FeedbinAccountDelegateError.invalidParameter))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DispatchQueue.main.async {
|
||||
completion(.failure(FeedbinAccountDelegateError.invalidParameter))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func createFeed( account: Account, subscription sub: FeedbinSubscription, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||
DispatchQueue.main.async {
|
||||
let feed = account.createFeed(with: sub.name, url: sub.url, feedID: String(sub.feedID), homePageURL: sub.homePageURL)
|
||||
feed.subscriptionID = String(sub.subscriptionID)
|
||||
completion(.success(feed))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -41,3 +41,15 @@ struct FeedbinUpdateSubscription: Codable {
|
||||
case title
|
||||
}
|
||||
}
|
||||
|
||||
struct FeedbinSubscriptionChoice: Codable {
|
||||
|
||||
let name: String?
|
||||
let url: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case name = "title"
|
||||
case url = "feed_url"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ final class LocalAccountDelegate: AccountDelegate {
|
||||
|
||||
private weak var account: Account?
|
||||
private var feedFinder: FeedFinder?
|
||||
private var createFeedCompletion: ((Result<AccountCreateFeedResult, Error>) -> Void)?
|
||||
private var createFeedCompletion: ((Result<Feed, Error>) -> Void)?
|
||||
|
||||
private let refresher = LocalAccountRefresher()
|
||||
|
||||
@ -46,7 +46,7 @@ final class LocalAccountDelegate: AccountDelegate {
|
||||
completion(.success(()))
|
||||
}
|
||||
|
||||
func createFeed(for account: Account, url urlString: String, completion: @escaping (Result<AccountCreateFeedResult, Error>) -> Void) {
|
||||
func createFeed(for account: Account, url urlString: String, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||
|
||||
guard let url = URL(string: urlString) else {
|
||||
completion(.failure(LocalAccountDelegateError.invalidParameter))
|
||||
@ -138,7 +138,7 @@ extension LocalAccountDelegate: FeedFinderDelegate {
|
||||
|
||||
if let error = feedFinder.initialDownloadError {
|
||||
if feedFinder.initialDownloadStatusCode == 404 {
|
||||
createFeedCompletion!(.success(.notFound))
|
||||
createFeedCompletion!(.failure(AccountError.createErrorNotFound))
|
||||
} else {
|
||||
createFeedCompletion!(.failure(error))
|
||||
}
|
||||
@ -148,7 +148,7 @@ extension LocalAccountDelegate: FeedFinderDelegate {
|
||||
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers),
|
||||
let url = URL(string: bestFeedSpecifier.urlString),
|
||||
let account = account else {
|
||||
createFeedCompletion!(.success(.notFound))
|
||||
createFeedCompletion!(.failure(AccountError.createErrorNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
@ -157,7 +157,7 @@ extension LocalAccountDelegate: FeedFinderDelegate {
|
||||
if let parsedFeed = parsedFeed {
|
||||
account.update(feed, with: parsedFeed, {})
|
||||
}
|
||||
self?.createFeedCompletion!(.success(.created(feed)))
|
||||
self?.createFeedCompletion!(.success(feed))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -64,20 +64,18 @@ class AddFeedController: AddFeedWindowControllerDelegate {
|
||||
self?.endShowingProgress()
|
||||
|
||||
switch result {
|
||||
case .success(let createFeedResult):
|
||||
switch createFeedResult {
|
||||
case .created(let feed):
|
||||
case .success(let feed):
|
||||
self?.processFeed(feed, account: account, folder: folder, url: url, title: title)
|
||||
case .multipleChoice(let feedChoices):
|
||||
print()
|
||||
case .alreadySubscribed:
|
||||
self?.showAlreadySubscribedError(url.absoluteString)
|
||||
case .notFound:
|
||||
self?.showNoFeedsErrorMessage()
|
||||
}
|
||||
case .failure(let error):
|
||||
switch error {
|
||||
case AccountError.createErrorAlreadySubscribed:
|
||||
self?.showAlreadySubscribedError(url.absoluteString)
|
||||
case AccountError.createErrorNotFound:
|
||||
self?.showNoFeedsErrorMessage()
|
||||
default:
|
||||
NSApplication.shared.presentError(error)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -102,10 +102,7 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
|
||||
|
||||
account.createFeed(url: url) { result in
|
||||
switch result {
|
||||
case .success(let createFeedResult):
|
||||
|
||||
switch createFeedResult {
|
||||
case .created(let feed):
|
||||
case .success(let feed):
|
||||
|
||||
if let editedName = titleFromArgs {
|
||||
account.renameFeed(feed, to: editedName) { result in
|
||||
@ -124,10 +121,6 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
command.resumeExecution(withResult:nil)
|
||||
}
|
||||
|
||||
case .failure:
|
||||
command.resumeExecution(withResult:nil)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user