mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Convert FeedFinder public API to async/await.
This commit is contained in:
@@ -275,25 +275,26 @@ private extension LocalAccountDelegate {
|
||||
// with an Untitled name if we don't delay it being added to the sidebar.
|
||||
BatchUpdate.shared.start()
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(1)
|
||||
FeedFinder.find(url: url) { result in
|
||||
|
||||
switch result {
|
||||
case .success(let feedSpecifiers):
|
||||
|
||||
Task { @MainActor in
|
||||
|
||||
do {
|
||||
let feedSpecifiers = try await FeedFinder.find(url: url)
|
||||
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers),
|
||||
let url = URL(string: bestFeedSpecifier.urlString) else {
|
||||
self.refreshProgress.completeTask()
|
||||
BatchUpdate.shared.end()
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
return
|
||||
let url = URL(string: bestFeedSpecifier.urlString) else {
|
||||
self.refreshProgress.completeTask()
|
||||
BatchUpdate.shared.end()
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if account.hasFeed(withURL: bestFeedSpecifier.urlString) {
|
||||
self.refreshProgress.completeTask()
|
||||
BatchUpdate.shared.end()
|
||||
completion(.failure(AccountError.createErrorAlreadySubscribed))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
InitialFeedDownloader.download(url) { parsedFeed in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
@@ -310,10 +311,9 @@ private extension LocalAccountDelegate {
|
||||
BatchUpdate.shared.end()
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
case .failure:
|
||||
} catch {
|
||||
BatchUpdate.shared.end()
|
||||
self.refreshProgress.completeTask()
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
|
||||
@@ -389,19 +389,20 @@ public enum ReaderAPIAccountDelegateError: LocalizedError {
|
||||
}
|
||||
|
||||
func createFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result<Feed, Error>) -> Void) {
|
||||
guard let url = URL(string: url) else {
|
||||
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
|
||||
return
|
||||
}
|
||||
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(2)
|
||||
|
||||
FeedFinder.find(url: url) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
switch result {
|
||||
case .success(let feedSpecifiers):
|
||||
let feedSpecifiers = feedSpecifiers.filter { !$0.urlString.contains("json") }
|
||||
Task { @MainActor in
|
||||
|
||||
guard let url = URL(string: url) else {
|
||||
completion(.failure(ReaderAPIAccountDelegateError.invalidParameter))
|
||||
return
|
||||
}
|
||||
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(2)
|
||||
|
||||
do {
|
||||
var feedSpecifiers = try await FeedFinder.find(url: url)
|
||||
self.refreshProgress.completeTask()
|
||||
feedSpecifiers = feedSpecifiers.filter { !$0.urlString.contains("json") }
|
||||
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers) else {
|
||||
self.refreshProgress.clear()
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
@@ -409,32 +410,24 @@ public enum ReaderAPIAccountDelegateError: LocalizedError {
|
||||
}
|
||||
|
||||
self.caller.createSubscription(url: bestFeedSpecifier.urlString, name: name) { result in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success(let subResult):
|
||||
switch subResult {
|
||||
case .created(let subscription):
|
||||
self.createFeed(account: account, subscription: subscription, name: name, container: container, completion: completion)
|
||||
case .notFound:
|
||||
DispatchQueue.main.async {
|
||||
Task { @MainActor in
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success(let subResult):
|
||||
switch subResult {
|
||||
case .created(let subscription):
|
||||
self.createFeed(account: account, subscription: subscription, name: name, container: container, completion: completion)
|
||||
case .notFound:
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
DispatchQueue.main.async {
|
||||
case .failure(let error):
|
||||
let wrappedError = WrappedAccountError(accountID: account.accountID, accountNameForDisplay: account.nameForDisplay, underlyingError: error)
|
||||
completion(.failure(wrappedError))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
case .failure:
|
||||
self.refreshProgress.clear()
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func renameFeed(for account: Account, feed: Feed, name: String) async throws {
|
||||
|
||||
@@ -629,11 +629,12 @@ private extension CloudKitAccountDelegate {
|
||||
}
|
||||
|
||||
refreshProgress.addToNumberOfTasksAndRemaining(5)
|
||||
FeedFinder.find(url: url) { result in
|
||||
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
case .success(let feedSpecifiers):
|
||||
|
||||
Task { @MainActor in
|
||||
do {
|
||||
let feedSpecifiers = try await FeedFinder.find(url: url)
|
||||
self.refreshProgress.completeTask()
|
||||
|
||||
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers), let url = URL(string: bestFeedSpecifier.urlString) else {
|
||||
self.refreshProgress.completeTasks(3)
|
||||
if validateFeed {
|
||||
@@ -644,13 +645,13 @@ private extension CloudKitAccountDelegate {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if account.hasFeed(withURL: bestFeedSpecifier.urlString) {
|
||||
self.refreshProgress.completeTasks(4)
|
||||
completion(.failure(AccountError.createErrorAlreadySubscribed))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
let feed = account.createFeed(with: nil, url: url.absoluteString, feedID: url.absoluteString, homePageURL: nil)
|
||||
feed.editedName = editedName
|
||||
container.addFeed(feed)
|
||||
@@ -662,12 +663,12 @@ private extension CloudKitAccountDelegate {
|
||||
account.update(feed, with: parsedFeed) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
|
||||
|
||||
self.accountZone.createFeed(url: bestFeedSpecifier.urlString,
|
||||
name: parsedFeed.title,
|
||||
editedName: editedName,
|
||||
homePageURL: parsedFeed.homePageURL,
|
||||
container: container) { result in
|
||||
name: parsedFeed.title,
|
||||
editedName: editedName,
|
||||
homePageURL: parsedFeed.homePageURL,
|
||||
container: container) { result in
|
||||
|
||||
self.refreshProgress.completeTask()
|
||||
switch result {
|
||||
@@ -680,7 +681,7 @@ private extension CloudKitAccountDelegate {
|
||||
self.refreshProgress.completeTasks(2)
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
case .failure(let error):
|
||||
@@ -688,18 +689,17 @@ private extension CloudKitAccountDelegate {
|
||||
self.refreshProgress.completeTasks(3)
|
||||
completion(.failure(error))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
self.refreshProgress.completeTasks(3)
|
||||
container.removeFeed(feed)
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
case .failure:
|
||||
self.refreshProgress.completeTasks(3)
|
||||
} catch {
|
||||
self.refreshProgress.completeTasks(4)
|
||||
if validateFeed {
|
||||
self.refreshProgress.completeTask()
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
|
||||
@@ -14,9 +14,27 @@ import AccountError
|
||||
|
||||
public final class FeedFinder {
|
||||
|
||||
public static func find(url: URL, completion: @escaping (Result<Set<FeedSpecifier>, Error>) -> Void) {
|
||||
@MainActor public static func find(url: URL) async throws -> Set<FeedSpecifier> {
|
||||
|
||||
try await withCheckedThrowingContinuation { continuation in
|
||||
|
||||
find(url: url) { result in
|
||||
switch result {
|
||||
case .success(let feedSpecifiers):
|
||||
continuation.resume(returning: feedSpecifiers)
|
||||
case .failure(let error):
|
||||
continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension FeedFinder {
|
||||
|
||||
@MainActor static func find(url: URL, completion: @escaping (Result<Set<FeedSpecifier>, Error>) -> Void) {
|
||||
downloadAddingToCache(url) { (data, response, error) in
|
||||
|
||||
|
||||
if response?.forcedStatusCode == 404 {
|
||||
if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), urlComponents.host == "micro.blog" {
|
||||
urlComponents.path = "\(urlComponents.path).json"
|
||||
@@ -29,39 +47,36 @@ public final class FeedFinder {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if let error = error {
|
||||
completion(.failure(error))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
guard let data = data, let response = response else {
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if !response.statusIsOK || data.isEmpty {
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if FeedFinder.isFeed(data, url.absoluteString) {
|
||||
let feedSpecifier = FeedSpecifier(title: nil, urlString: url.absoluteString, source: .UserEntered, orderFound: 1)
|
||||
completion(.success(Set([feedSpecifier])))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if !FeedFinder.isHTML(data) {
|
||||
completion(.failure(AccountError.createErrorNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
FeedFinder.findFeedsInHTMLPage(htmlData: data, urlString: url.absoluteString, completion: completion)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension FeedFinder {
|
||||
|
||||
static func addFeedSpecifier(_ feedSpecifier: FeedSpecifier, feedSpecifiers: inout [String: FeedSpecifier]) {
|
||||
// If there’s an existing feed specifier, merge the two so that we have the best data. If one has a title and one doesn’t, use that non-nil title. Use the better source.
|
||||
|
||||
Reference in New Issue
Block a user