Add create Feedbin feed
This commit is contained in:
parent
72c92587d2
commit
43d32b1281
@ -382,26 +382,25 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
}
|
||||
}
|
||||
|
||||
public func createFeed(with name: String?, editedName: String?, url: String) -> Feed {
|
||||
|
||||
// For syncing, this may need to be an async method with a callback,
|
||||
// since it will likely need to call the server.
|
||||
return createFeed(with: name, editedName: editedName, url: url, feedID: url, homePageURL: nil)
|
||||
|
||||
public func createFeed(with name: String?, url: String, completion: @escaping (Result<AccountCreateFeedResult, Error>) -> Void) {
|
||||
delegate.createFeed(for: self, with: name, url: url, completion: completion)
|
||||
}
|
||||
|
||||
func createFeed(with name: String?, editedName: String?, url: String, feedID: String, homePageURL: String?) -> Feed {
|
||||
func createFeed(with name: String?, url: String, feedID: String, homePageURL: String?) -> Feed {
|
||||
|
||||
let metadata = feedMetadata(feedURL: url, feedID: feedID)
|
||||
let feed = Feed(account: self, url: url, metadata: metadata)
|
||||
feed.name = name
|
||||
feed.editedName = editedName
|
||||
feed.homePageURL = homePageURL
|
||||
|
||||
return feed
|
||||
|
||||
}
|
||||
|
||||
public func renameFeed(_ feed: Feed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
delegate.renameFeed(for: self, with: feed, to: name, completion: completion)
|
||||
}
|
||||
|
||||
public func canAddFolder(_ folder: Folder, to containingFolder: Folder?) -> Bool {
|
||||
|
||||
return false // TODO
|
||||
|
@ -21,6 +21,11 @@
|
||||
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 */; };
|
||||
5165D73122837F3400D9D53D /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */; };
|
||||
51D58755227F53BE00900287 /* FeedbinTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D58754227F53BE00900287 /* FeedbinTag.swift */; };
|
||||
51D5875A227F630B00900287 /* tags_delete.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58757227F630B00900287 /* tags_delete.json */; };
|
||||
51D5875B227F630B00900287 /* tags_add.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58758227F630B00900287 /* tags_add.json */; };
|
||||
@ -113,6 +118,11 @@
|
||||
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>"; };
|
||||
5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InitialFeedDownloader.swift; sourceTree = "<group>"; };
|
||||
51D58754227F53BE00900287 /* FeedbinTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinTag.swift; sourceTree = "<group>"; };
|
||||
51D58757227F630B00900287 /* tags_delete.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_delete.json; sourceTree = "<group>"; };
|
||||
51D58758227F630B00900287 /* tags_add.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_add.json; sourceTree = "<group>"; };
|
||||
@ -178,6 +188,16 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
5165D71F22835E9800D9D53D /* FeedFinder */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5165D71C22835E9800D9D53D /* FeedFinder.swift */,
|
||||
5165D71D22835E9800D9D53D /* FeedSpecifier.swift */,
|
||||
5165D71E22835E9800D9D53D /* HTMLFeedFinder.swift */,
|
||||
);
|
||||
path = FeedFinder;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
51D58756227F62E300900287 /* JSON */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -218,6 +238,7 @@
|
||||
children = (
|
||||
8419742C1F6DDE84006346C4 /* LocalAccountDelegate.swift */,
|
||||
8419742D1F6DDE96006346C4 /* LocalAccountRefresher.swift */,
|
||||
5165D73022837F3400D9D53D /* InitialFeedDownloader.swift */,
|
||||
);
|
||||
path = LocalAccount;
|
||||
sourceTree = "<group>";
|
||||
@ -253,6 +274,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
848935101F62486800CEBD24 /* Account.swift */,
|
||||
5165D7192283398700D9D53D /* AccountCreateFeedResult.swift */,
|
||||
841974241F6DDCE4006346C4 /* AccountDelegate.swift */,
|
||||
846E77531F6F00E300A165E2 /* AccountManager.swift */,
|
||||
84AF4EA3222CFDD100F6A800 /* AccountMetadata.swift */,
|
||||
@ -265,6 +287,7 @@
|
||||
84B2D4CE2238C13D00498ADA /* FeedMetadata.swift */,
|
||||
841974001F6DD1EC006346C4 /* Folder.swift */,
|
||||
844B297E210CE37E004020B3 /* UnreadCountProvider.swift */,
|
||||
5165D71F22835E9800D9D53D /* FeedFinder */,
|
||||
8419742B1F6DDE84006346C4 /* LocalAccount */,
|
||||
84245C7D1FDDD2580074AFBB /* Feedbin */,
|
||||
848935031F62484F00CEBD24 /* AccountTests */,
|
||||
@ -485,17 +508,22 @@
|
||||
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 */,
|
||||
5165D72922835F7A00D9D53D /* FeedSpecifier.swift in Sources */,
|
||||
844B297D2106C7EC004020B3 /* Feed.swift in Sources */,
|
||||
84B2D4D02238CD8A00498ADA /* FeedMetadata.swift in Sources */,
|
||||
5144EA49227B497600D19003 /* FeedbinAPICaller.swift in Sources */,
|
||||
84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */,
|
||||
5133231122810EB200C30F19 /* FeedbinIcon.swift in Sources */,
|
||||
846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */,
|
||||
5165D72822835F7800D9D53D /* FeedFinder.swift in Sources */,
|
||||
51D58755227F53BE00900287 /* FeedbinTag.swift in Sources */,
|
||||
84D09623217418DC00D77525 /* FeedbinTagging.swift in Sources */,
|
||||
84CAD7161FDF2E22000F0755 /* FeedbinEntry.swift in Sources */,
|
||||
5165D72A22835F7D00D9D53D /* HTMLFeedFinder.swift in Sources */,
|
||||
841974011F6DD1EC006346C4 /* Folder.swift in Sources */,
|
||||
846E774F1F6EF9C000A165E2 /* LocalAccountDelegate.swift in Sources */,
|
||||
844B297F210CE37E004020B3 /* UnreadCountProvider.swift in Sources */,
|
||||
|
21
Frameworks/Account/AccountCreateFeedResult.swift
Normal file
21
Frameworks/Account/AccountCreateFeedResult.swift
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// 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
|
||||
}
|
@ -19,11 +19,14 @@ protocol AccountDelegate {
|
||||
|
||||
var refreshProgress: DownloadProgress { get }
|
||||
|
||||
func refreshAll(for: Account, completion: (() -> Void)?)
|
||||
func refreshAll(for account: Account, completion: (() -> Void)?)
|
||||
|
||||
func renameFolder(for: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
func deleteFolder(for: Account, with folder: Folder, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
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, with name: String?, url: String, completion: @escaping (Result<AccountCreateFeedResult, Error>) -> Void)
|
||||
func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
|
||||
// Called at the end of account’s init method.
|
||||
func accountDidInitialize(_ account: Account)
|
||||
|
||||
|
@ -10,8 +10,8 @@ import Foundation
|
||||
import RSWeb
|
||||
|
||||
enum CreateSubscriptionResult {
|
||||
case created(String?)
|
||||
case multipleChoice([FeedbinSubscription]?)
|
||||
case created(FeedbinSubscription)
|
||||
case multipleChoice([FeedbinSubscription])
|
||||
case alreadySubscribed
|
||||
case notFound
|
||||
}
|
||||
@ -123,24 +123,46 @@ final class FeedbinAPICaller: NSObject {
|
||||
func createSubscription(url: String, completionHandler completion: @escaping (Result<CreateSubscriptionResult, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("subscriptions.json")
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[AccountMetadata.ConditionalGetKeys.subscriptions]
|
||||
let request = URLRequest(url: callURL, credentials: credentials, conditionalGet: conditionalGet)
|
||||
let payload = FeedbinCreateSubscription(feedURL: url)
|
||||
var request = URLRequest(url: callURL, credentials: credentials)
|
||||
request.httpMethod = HTTPMethod.post
|
||||
request.addValue("application/json; charset=utf-8", forHTTPHeaderField: HTTPRequestHeader.contentType)
|
||||
|
||||
let payload: Data
|
||||
do {
|
||||
payload = try JSONEncoder().encode(FeedbinCreateSubscription(feedURL: url))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
return
|
||||
}
|
||||
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload, resultType: [FeedbinSubscription].self) { [weak self] result in
|
||||
transport.send(request: request, payload: payload) { result in
|
||||
|
||||
switch result {
|
||||
case .success(let (response, subscriptions)):
|
||||
|
||||
self?.storeConditionalGet(metadata: self?.accountMetadata, key: AccountMetadata.ConditionalGetKeys.subscriptions, headers: response.allHeaderFields)
|
||||
case .success(let (response, data)):
|
||||
|
||||
switch response.forcedStatusCode {
|
||||
case 201:
|
||||
let location = response.valueForHTTPHeaderField(HTTPResponseHeader.location)
|
||||
completion(.success(.created(location)))
|
||||
guard let subData = data else {
|
||||
completion(.failure(TransportError.noData))
|
||||
break
|
||||
}
|
||||
do {
|
||||
let subscription = try JSONDecoder().decode(FeedbinSubscription.self, from: subData)
|
||||
completion(.success(.created(subscription)))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
case 300:
|
||||
completion(.success(.multipleChoice(subscriptions)))
|
||||
guard let subData = data else {
|
||||
completion(.failure(TransportError.noData))
|
||||
break
|
||||
}
|
||||
do {
|
||||
let subscriptions = try JSONDecoder().decode([FeedbinSubscription].self, from: subData)
|
||||
completion(.success(.multipleChoice(subscriptions)))
|
||||
} catch {
|
||||
completion(.failure(error))
|
||||
}
|
||||
case 302:
|
||||
completion(.success(.alreadySubscribed))
|
||||
default:
|
||||
@ -166,6 +188,13 @@ final class FeedbinAPICaller: NSObject {
|
||||
|
||||
}
|
||||
|
||||
func renameFeed(feedID: String, newName: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("subscriptions/\(feedID).json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinUpdateSubscription(title: newName)
|
||||
transport.send(request: request, method: HTTPMethod.patch, payload: payload, completion: completion)
|
||||
}
|
||||
|
||||
func retrieveTaggings(completionHandler completion: @escaping (Result<[FeedbinTagging]?, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("taggings.json")
|
||||
|
@ -110,6 +110,59 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
||||
|
||||
}
|
||||
|
||||
func createFeed(for account: Account, with name: String?, url: String, completion: @escaping (Result<AccountCreateFeedResult, Error>) -> Void) {
|
||||
|
||||
caller.createSubscription(url: url) { 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)
|
||||
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 .alreadySubscribed:
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(.alreadySubscribed))
|
||||
}
|
||||
case .notFound:
|
||||
DispatchQueue.main.async {
|
||||
completion(.success(.notFound))
|
||||
}
|
||||
}
|
||||
case .failure(let error):
|
||||
DispatchQueue.main.async {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
caller.renameFeed(feedID: feed.feedID, newName: name) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
DispatchQueue.main.async {
|
||||
feed.editedName = name
|
||||
completion(.success(()))
|
||||
}
|
||||
case .failure(let error):
|
||||
DispatchQueue.main.async {
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func accountDidInitialize(_ account: Account) {
|
||||
credentials = try? account.retrieveBasicCredentials()
|
||||
accountMetadata = account.metadata
|
||||
@ -282,7 +335,7 @@ private extension FeedbinAccountDelegate {
|
||||
feed.name = subscription.name
|
||||
feed.homePageURL = subscription.homePageURL
|
||||
} else {
|
||||
let feed = account.createFeed(with: subscription.name, editedName: nil, url: subscription.url, feedID: subFeedId, homePageURL: subscription.homePageURL)
|
||||
let feed = account.createFeed(with: subscription.name, url: subscription.url, feedID: subFeedId, homePageURL: subscription.homePageURL)
|
||||
account.addFeed(feed, to: nil)
|
||||
}
|
||||
}
|
||||
|
@ -29,11 +29,15 @@ struct FeedbinSubscription: Codable {
|
||||
}
|
||||
|
||||
struct FeedbinCreateSubscription: Codable {
|
||||
|
||||
let feedURL: String
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case feedURL = "feed_url"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct FeedbinUpdateSubscription: Codable {
|
||||
let title: String
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case title
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,10 @@
|
||||
import Foundation
|
||||
import RSWeb
|
||||
|
||||
public enum LocalAccountDelegateError: String, Error {
|
||||
case invalidParameter = "An invalid parameter was used."
|
||||
}
|
||||
|
||||
final class LocalAccountDelegate: AccountDelegate {
|
||||
|
||||
let supportsSubFolders = false
|
||||
@ -16,6 +20,10 @@ final class LocalAccountDelegate: AccountDelegate {
|
||||
var credentials: Credentials?
|
||||
var accountMetadata: AccountMetadata?
|
||||
|
||||
private weak var account: Account?
|
||||
private var feedFinder: FeedFinder?
|
||||
private var createFeedCompletion: ((Result<AccountCreateFeedResult, Error>) -> Void)?
|
||||
|
||||
private let refresher = LocalAccountRefresher()
|
||||
|
||||
var refreshProgress: DownloadProgress {
|
||||
@ -38,6 +46,25 @@ final class LocalAccountDelegate: AccountDelegate {
|
||||
completion(.success(()))
|
||||
}
|
||||
|
||||
func createFeed(for account: Account, with name: String?, url urlString: String, completion: @escaping (Result<AccountCreateFeedResult, Error>) -> Void) {
|
||||
|
||||
guard let url = URL(string: urlString) else {
|
||||
completion(.failure(LocalAccountDelegateError.invalidParameter))
|
||||
return
|
||||
}
|
||||
|
||||
self.account = account
|
||||
createFeedCompletion = completion
|
||||
|
||||
feedFinder = FeedFinder(url: url, delegate: self)
|
||||
|
||||
}
|
||||
|
||||
func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
feed.editedName = name
|
||||
completion(.success(()))
|
||||
}
|
||||
|
||||
func accountDidInitialize(_ account: Account) {
|
||||
}
|
||||
|
||||
@ -46,3 +73,37 @@ final class LocalAccountDelegate: AccountDelegate {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension LocalAccountDelegate: FeedFinderDelegate {
|
||||
|
||||
// MARK: FeedFinderDelegate
|
||||
|
||||
public func feedFinder(_ feedFinder: FeedFinder, didFindFeeds feedSpecifiers: Set<FeedSpecifier>) {
|
||||
|
||||
if let error = feedFinder.initialDownloadError {
|
||||
if feedFinder.initialDownloadStatusCode == 404 {
|
||||
createFeedCompletion!(.success(.notFound))
|
||||
} else {
|
||||
createFeedCompletion!(.failure(error))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers),
|
||||
let url = URL(string: bestFeedSpecifier.urlString),
|
||||
let account = account else {
|
||||
createFeedCompletion!(.success(.notFound))
|
||||
return
|
||||
}
|
||||
|
||||
let feed = account.createFeed(with: nil, url: url.absoluteString, feedID: url.absoluteString, homePageURL: nil)
|
||||
InitialFeedDownloader.download(url) { [weak self] parsedFeed in
|
||||
if let parsedFeed = parsedFeed {
|
||||
account.update(feed, with: parsedFeed, {})
|
||||
}
|
||||
self?.createFeedCompletion!(.success(.created(feed)))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,19 +21,12 @@ import RSParser
|
||||
// Else,
|
||||
// display error sheet.
|
||||
|
||||
class AddFeedController: AddFeedWindowControllerDelegate, FeedFinderDelegate {
|
||||
class AddFeedController: AddFeedWindowControllerDelegate {
|
||||
|
||||
private let hostWindow: NSWindow
|
||||
private var addFeedWindowController: AddFeedWindowController?
|
||||
private var userEnteredURL: URL?
|
||||
private var userEnteredFolder: Folder?
|
||||
private var userEnteredTitle: String?
|
||||
private var userEnteredAccount: Account?
|
||||
private var foundFeedURLString: String?
|
||||
private var titleFromFeed: String?
|
||||
private var feedFinder: FeedFinder?
|
||||
private var isFindingFeed = false
|
||||
private var bestFeedSpecifier: FeedSpecifier?
|
||||
|
||||
init(hostWindow: NSWindow) {
|
||||
|
||||
@ -66,12 +59,30 @@ class AddFeedController: AddFeedWindowControllerDelegate, FeedFinderDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
userEnteredAccount = account
|
||||
userEnteredURL = url
|
||||
userEnteredFolder = folder
|
||||
userEnteredTitle = title
|
||||
|
||||
findFeed()
|
||||
account.createFeed(with: nil, url: url.absoluteString) { [weak self] result in
|
||||
|
||||
self?.endShowingProgress()
|
||||
|
||||
switch result {
|
||||
case .success(let createFeedResult):
|
||||
switch createFeedResult {
|
||||
case .created(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):
|
||||
NSApplication.shared.presentError(error)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
beginShowingProgress()
|
||||
|
||||
}
|
||||
|
||||
func addFeedWindowControllerUserDidCancel(_: AddFeedWindowController) {
|
||||
@ -79,46 +90,8 @@ class AddFeedController: AddFeedWindowControllerDelegate, FeedFinderDelegate {
|
||||
closeAddFeedSheet(NSApplication.ModalResponse.cancel)
|
||||
}
|
||||
|
||||
// MARK: FeedFinderDelegate
|
||||
|
||||
public func feedFinder(_ feedFinder: FeedFinder, didFindFeeds feedSpecifiers: Set<FeedSpecifier>) {
|
||||
|
||||
isFindingFeed = false
|
||||
endShowingProgress()
|
||||
|
||||
if let error = feedFinder.initialDownloadError {
|
||||
if feedFinder.initialDownloadStatusCode == 404 {
|
||||
showNoFeedsErrorMessage()
|
||||
}
|
||||
else {
|
||||
showInitialDownloadError(error)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers) else {
|
||||
showNoFeedsErrorMessage()
|
||||
return
|
||||
}
|
||||
|
||||
self.bestFeedSpecifier = bestFeedSpecifier
|
||||
self.foundFeedURLString = bestFeedSpecifier.urlString
|
||||
|
||||
if let url = URL(string: bestFeedSpecifier.urlString) {
|
||||
|
||||
InitialFeedDownloader.download(url) { (parsedFeed) in
|
||||
self.titleFromFeed = parsedFeed?.title
|
||||
self.addFeedIfPossible(parsedFeed)
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Shouldn't happen.
|
||||
showNoFeedsErrorMessage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private extension AddFeedController {
|
||||
|
||||
var urlStringFromPasteboard: String? {
|
||||
@ -151,50 +124,27 @@ private extension AddFeedController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func addFeedIfPossible(_ parsedFeed: ParsedFeed?) {
|
||||
|
||||
// Add feed if not already subscribed-to.
|
||||
|
||||
guard let account = userEnteredAccount else {
|
||||
assertionFailure("Expected account.")
|
||||
return
|
||||
func processFeed(_ feed: Feed, account: Account, folder: Folder?, url: URL, title: String?) {
|
||||
|
||||
if let title = title {
|
||||
account.renameFeed(feed, to: title) { result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
NSApplication.shared.presentError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
guard let feedURLString = foundFeedURLString else {
|
||||
assertionFailure("Expected feedURLString.")
|
||||
return
|
||||
}
|
||||
|
||||
if account.hasFeed(withURL: feedURLString) {
|
||||
showAlreadySubscribedError(feedURLString)
|
||||
return
|
||||
}
|
||||
|
||||
let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString)
|
||||
|
||||
if let parsedFeed = parsedFeed {
|
||||
account.update(feed, with: parsedFeed, {})
|
||||
}
|
||||
|
||||
account.addFeed(feed, to: userEnteredFolder)
|
||||
|
||||
// TODO: make this async and add to above code
|
||||
account.addFeed(feed, to: folder)
|
||||
|
||||
// Move this into the mess above
|
||||
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
|
||||
}
|
||||
|
||||
// MARK: Find Feeds
|
||||
|
||||
func findFeed() {
|
||||
|
||||
guard let url = userEnteredURL else {
|
||||
assertionFailure("Expected userEnteredURL.")
|
||||
return
|
||||
}
|
||||
|
||||
isFindingFeed = true
|
||||
feedFinder = FeedFinder(url: url, delegate: self)
|
||||
|
||||
beginShowingProgress()
|
||||
}
|
||||
|
||||
|
||||
// MARK: Errors
|
||||
|
||||
func showAlreadySubscribedError(_ urlString: String) {
|
||||
|
@ -59,16 +59,6 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
|
||||
|
||||
// MARK: --- handle NSCreateCommand ---
|
||||
|
||||
class func parsedFeedForURL(_ urlString:String, _ completionHandler: @escaping (_ parsedFeed: ParsedFeed?) -> Void) {
|
||||
guard let url = URL(string: urlString) else {
|
||||
completionHandler(nil)
|
||||
return
|
||||
}
|
||||
InitialFeedDownloader.download(url) { (parsedFeed) in
|
||||
completionHandler(parsedFeed)
|
||||
}
|
||||
}
|
||||
|
||||
class func urlForNewFeed(arguments:[String:Any]) -> String? {
|
||||
var url:String?
|
||||
if let withDataParam = arguments["ObjectData"] {
|
||||
@ -110,23 +100,36 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
|
||||
// suspendExecution(). When we get the callback, we can supply the event result and call resumeExecution()
|
||||
command.suspendExecution()
|
||||
|
||||
self.parsedFeedForURL(url, { (parsedFeedOptional) in
|
||||
if let parsedFeed = parsedFeedOptional {
|
||||
let titleFromFeed = parsedFeed.title
|
||||
|
||||
let feed = account.createFeed(with: titleFromFeed, editedName: titleFromArgs, url: url)
|
||||
account.update(feed, with:parsedFeed, {})
|
||||
|
||||
// add the feed, putting it in a folder if needed
|
||||
account.addFeed(feed, to:folder)
|
||||
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
|
||||
account.createFeed(with: nil, url: url) { result in
|
||||
switch result {
|
||||
case .success(let createFeedResult):
|
||||
|
||||
let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder)
|
||||
command.resumeExecution(withResult:scriptableFeed.objectSpecifier)
|
||||
} else {
|
||||
command.resumeExecution(withResult:nil)
|
||||
}
|
||||
})
|
||||
switch createFeedResult {
|
||||
case .created(let feed):
|
||||
|
||||
if let editedName = titleFromArgs {
|
||||
account.renameFeed(feed, to: editedName) { result in
|
||||
}
|
||||
}
|
||||
|
||||
// add the feed, putting it in a folder if needed
|
||||
account.addFeed(feed, to:folder)
|
||||
|
||||
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
|
||||
|
||||
let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder)
|
||||
command.resumeExecution(withResult:scriptableFeed.objectSpecifier)
|
||||
|
||||
default:
|
||||
command.resumeExecution(withResult:nil)
|
||||
}
|
||||
|
||||
case .failure:
|
||||
command.resumeExecution(withResult:nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,6 @@
|
||||
51C452AC22650FD200C03939 /* AppNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = 842E45CD1ED8C308000A8B52 /* AppNotifications.swift */; };
|
||||
51C452AE2265104D00C03939 /* TimelineStringFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97731ED9EC04007D329B /* TimelineStringFormatter.swift */; };
|
||||
51C452AF2265108300C03939 /* ArticleArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F204DF1FAACBB30076E152 /* ArticleArray.swift */; };
|
||||
51C452B1226510E600C03939 /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A01ED9F180007D329B /* InitialFeedDownloader.swift */; };
|
||||
51C452B42265141B00C03939 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51C452B32265141B00C03939 /* WebKit.framework */; };
|
||||
51C452B82265178500C03939 /* styleSheet.css in Resources */ = {isa = PBXBuildFile; fileRef = 51C452B72265178500C03939 /* styleSheet.css */; };
|
||||
51D5948722668EFA00DFC836 /* MarkStatusCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84702AA31FA27AC0006B8943 /* MarkStatusCommand.swift */; };
|
||||
@ -217,7 +216,6 @@
|
||||
849A978A1ED9ECEF007D329B /* ArticleStylesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */; };
|
||||
849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97971ED9EFAA007D329B /* Node-Extensions.swift */; };
|
||||
849A979F1ED9F130007D329B /* SidebarCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A979E1ED9F130007D329B /* SidebarCell.swift */; };
|
||||
849A97A21ED9F180007D329B /* InitialFeedDownloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A01ED9F180007D329B /* InitialFeedDownloader.swift */; };
|
||||
849A97A31ED9F180007D329B /* FolderTreeControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */; };
|
||||
849C64681ED37A5D003D8FC0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 849C64671ED37A5D003D8FC0 /* Assets.xcassets */; };
|
||||
849EE70F203919360082A1EA /* AppImages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849EE70E203919360082A1EA /* AppImages.swift */; };
|
||||
@ -275,12 +273,6 @@
|
||||
84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */; };
|
||||
84F2D5381FC22FCC00998D64 /* TodayFeedDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5361FC22FCB00998D64 /* TodayFeedDelegate.swift */; };
|
||||
84F2D53A1FC2308B00998D64 /* UnreadFeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F2D5391FC2308B00998D64 /* UnreadFeed.swift */; };
|
||||
84F3EE1620DEC97E003FADEB /* FeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0620DEC97E003FADEB /* FeedFinder.swift */; };
|
||||
84F3EE1720DEC97E003FADEB /* FeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0620DEC97E003FADEB /* FeedFinder.swift */; };
|
||||
84F3EE1820DEC97E003FADEB /* FeedSpecifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */; };
|
||||
84F3EE1920DEC97E003FADEB /* FeedSpecifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */; };
|
||||
84F3EE1A20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */; };
|
||||
84F3EE1B20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */; };
|
||||
84F9EAE5213660A100CF2DE4 /* AppleScriptXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAD1213660A100CF2DE4 /* AppleScriptXCTestCase.swift */; };
|
||||
84F9EAE6213660A100CF2DE4 /* ScriptingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAD2213660A100CF2DE4 /* ScriptingTests.swift */; };
|
||||
84F9EAE7213660A100CF2DE4 /* testNameOfAuthors.applescript in Sources */ = {isa = PBXBuildFile; fileRef = 84F9EAD4213660A100CF2DE4 /* testNameOfAuthors.applescript */; };
|
||||
@ -801,7 +793,6 @@
|
||||
849A97881ED9ECEF007D329B /* ArticleStylesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleStylesManager.swift; sourceTree = "<group>"; };
|
||||
849A97971ED9EFAA007D329B /* Node-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Node-Extensions.swift"; sourceTree = "<group>"; };
|
||||
849A979E1ED9F130007D329B /* SidebarCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SidebarCell.swift; sourceTree = "<group>"; };
|
||||
849A97A01ED9F180007D329B /* InitialFeedDownloader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InitialFeedDownloader.swift; sourceTree = "<group>"; };
|
||||
849A97A11ED9F180007D329B /* FolderTreeControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FolderTreeControllerDelegate.swift; sourceTree = "<group>"; };
|
||||
849C64601ED37A5D003D8FC0 /* NetNewsWire.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NetNewsWire.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
849C64671ED37A5D003D8FC0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@ -859,9 +850,6 @@
|
||||
84F2D5351FC22FCB00998D64 /* PseudoFeed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PseudoFeed.swift; sourceTree = "<group>"; };
|
||||
84F2D5361FC22FCB00998D64 /* TodayFeedDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodayFeedDelegate.swift; sourceTree = "<group>"; };
|
||||
84F2D5391FC2308B00998D64 /* UnreadFeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnreadFeed.swift; sourceTree = "<group>"; };
|
||||
84F3EE0620DEC97E003FADEB /* FeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedFinder.swift; sourceTree = "<group>"; };
|
||||
84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedSpecifier.swift; sourceTree = "<group>"; };
|
||||
84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTMLFeedFinder.swift; sourceTree = "<group>"; };
|
||||
84F9EAD1213660A100CF2DE4 /* AppleScriptXCTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleScriptXCTestCase.swift; sourceTree = "<group>"; };
|
||||
84F9EAD2213660A100CF2DE4 /* ScriptingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScriptingTests.swift; sourceTree = "<group>"; };
|
||||
84F9EAD4213660A100CF2DE4 /* testNameOfAuthors.applescript */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.applescript; path = testNameOfAuthors.applescript; sourceTree = "<group>"; };
|
||||
@ -1117,14 +1105,6 @@
|
||||
path = Timeline;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
51C452B0226510CF00C03939 /* Add Feed */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
849A97A01ED9F180007D329B /* InitialFeedDownloader.swift */,
|
||||
);
|
||||
path = "Add Feed";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
51C452B22265141B00C03939 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1544,7 +1524,6 @@
|
||||
841D4D5E2106B3E100DD04E6 /* ArticlesDatabase.xcodeproj */,
|
||||
842E45CD1ED8C308000A8B52 /* AppNotifications.swift */,
|
||||
51C452AD2265102800C03939 /* Timeline */,
|
||||
51C452B0226510CF00C03939 /* Add Feed */,
|
||||
84702AB31FA27AE8006B8943 /* Commands */,
|
||||
51C452A822650DA100C03939 /* Article Rendering */,
|
||||
849A97861ED9ECEF007D329B /* Article Styles */,
|
||||
@ -1554,7 +1533,6 @@
|
||||
848F6AE31FC29CFA002D422E /* Favicons */,
|
||||
845213211FCA5B10003B6E93 /* Images */,
|
||||
8426119C1FCB6ED40086A189 /* HTMLMetadata */,
|
||||
84F3EE0420DEC97E003FADEB /* FeedFinder */,
|
||||
5183CCEA226F70350010922C /* Refresh */,
|
||||
849A97561ED9EB0D007D329B /* Data */,
|
||||
512E08DD22687FA000BDCFDD /* Tree */,
|
||||
@ -1704,16 +1682,6 @@
|
||||
path = SmartFeeds;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84F3EE0420DEC97E003FADEB /* FeedFinder */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
84F3EE0620DEC97E003FADEB /* FeedFinder.swift */,
|
||||
84F3EE0720DEC97E003FADEB /* FeedSpecifier.swift */,
|
||||
84F3EE0820DEC97E003FADEB /* HTMLFeedFinder.swift */,
|
||||
);
|
||||
path = FeedFinder;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
84F9EACF213660A100CF2DE4 /* NetNewsWireTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -2291,7 +2259,6 @@
|
||||
51F85BF92274AA7B00C787DC /* UIBarButtonItem-Extensions.swift in Sources */,
|
||||
51C45296226509D300C03939 /* OPMLExporter.swift in Sources */,
|
||||
51C4525B226508DA00C03939 /* UIImage-Extensions.swift in Sources */,
|
||||
84F3EE1720DEC97E003FADEB /* FeedFinder.swift in Sources */,
|
||||
51C45291226509C800C03939 /* SmartFeed.swift in Sources */,
|
||||
51C452A722650A3D00C03939 /* RSImage-Extensions.swift in Sources */,
|
||||
51C45269226508F600C03939 /* MasterFeedTableViewCell.swift in Sources */,
|
||||
@ -2320,7 +2287,6 @@
|
||||
51F85BE7227245FC00C787DC /* AboutViewController.swift in Sources */,
|
||||
51C4529E22650A1900C03939 /* ImageDownloader.swift in Sources */,
|
||||
51C45292226509C800C03939 /* TodayFeedDelegate.swift in Sources */,
|
||||
84F3EE1B20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */,
|
||||
51C452A222650A1900C03939 /* RSHTMLMetadata+Extension.swift in Sources */,
|
||||
5183CCE5226F4DFA0010922C /* RefreshInterval.swift in Sources */,
|
||||
51EF0F7C2277919E0050506E /* TimelineNumberOfLinesViewController.swift in Sources */,
|
||||
@ -2336,7 +2302,6 @@
|
||||
51EF0F802277A8330050506E /* MasterTimelineCellLayout.swift in Sources */,
|
||||
51F85BF722749FA100C787DC /* UIFont-Extensions.swift in Sources */,
|
||||
51C452AF2265108300C03939 /* ArticleArray.swift in Sources */,
|
||||
84F3EE1920DEC97E003FADEB /* FeedSpecifier.swift in Sources */,
|
||||
51C4528E2265099C00C03939 /* SmartFeedsController.swift in Sources */,
|
||||
51C4529C22650A1000C03939 /* SingleFaviconDownloader.swift in Sources */,
|
||||
51C45290226509C100C03939 /* PseudoFeed.swift in Sources */,
|
||||
@ -2355,7 +2320,6 @@
|
||||
512E09012268907400BDCFDD /* MasterFeedTableViewSectionHeader.swift in Sources */,
|
||||
51C45268226508F600C03939 /* MasterFeedUnreadCountView.swift in Sources */,
|
||||
5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */,
|
||||
51C452B1226510E600C03939 /* InitialFeedDownloader.swift in Sources */,
|
||||
51C4529F22650A1900C03939 /* AuthorAvatarDownloader.swift in Sources */,
|
||||
51C452A322650A1E00C03939 /* HTMLMetadataDownloader.swift in Sources */,
|
||||
51C4528D2265095F00C03939 /* AddFolderViewController.swift in Sources */,
|
||||
@ -2442,11 +2406,9 @@
|
||||
5144EA51227B8E4500D19003 /* AccountsFeedbinWindowController.swift in Sources */,
|
||||
84AD1EBC2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift in Sources */,
|
||||
845A29241FC9255E007B49E3 /* SidebarCellAppearance.swift in Sources */,
|
||||
84F3EE1620DEC97E003FADEB /* FeedFinder.swift in Sources */,
|
||||
845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */,
|
||||
848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */,
|
||||
84C9FC7722629E1200D921D6 /* AdvancedPreferencesViewController.swift in Sources */,
|
||||
84F3EE1820DEC97E003FADEB /* FeedSpecifier.swift in Sources */,
|
||||
849EE72120391F560082A1EA /* SharingServicePickerDelegate.swift in Sources */,
|
||||
849A97981ED9EFAA007D329B /* Node-Extensions.swift in Sources */,
|
||||
849EE70F203919360082A1EA /* AppImages.swift in Sources */,
|
||||
@ -2472,7 +2434,6 @@
|
||||
841ABA6020145EC100980E11 /* BuiltinSmartFeedInspectorViewController.swift in Sources */,
|
||||
D5E4CC54202C1361009B4FFC /* AppDelegate+Scriptability.swift in Sources */,
|
||||
D5F4EDB5200744A700B9E363 /* ScriptingObject.swift in Sources */,
|
||||
84F3EE1A20DEC97E003FADEB /* HTMLFeedFinder.swift in Sources */,
|
||||
D5F4EDB920074D7C00B9E363 /* Folder+Scriptability.swift in Sources */,
|
||||
842611A01FCB72600086A189 /* FeaturedImageDownloader.swift in Sources */,
|
||||
849A97781ED9EC04007D329B /* TimelineCellLayout.swift in Sources */,
|
||||
@ -2498,7 +2459,6 @@
|
||||
84E185B3203B74E500F69BFA /* SingleLineTextFieldSizer.swift in Sources */,
|
||||
849A977A1ED9EC04007D329B /* TimelineTableCellView.swift in Sources */,
|
||||
849A97761ED9EC04007D329B /* TimelineCellAppearance.swift in Sources */,
|
||||
849A97A21ED9F180007D329B /* InitialFeedDownloader.swift in Sources */,
|
||||
849A977F1ED9EC42007D329B /* ArticleRenderer.swift in Sources */,
|
||||
84C9FC7822629E1200D921D6 /* GeneralPrefencesViewController.swift in Sources */,
|
||||
);
|
||||
|
@ -21,15 +21,6 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh
|
||||
|
||||
private var pickerData: AddFeedFolderPickerData!
|
||||
|
||||
private var feedFinder: FeedFinder?
|
||||
private var userEnteredURL: URL?
|
||||
private var userEnteredFolder: Folder?
|
||||
private var userEnteredTitle: String?
|
||||
private var userEnteredAccount: Account?
|
||||
private var foundFeedURLString: String?
|
||||
private var bestFeedSpecifier: FeedSpecifier?
|
||||
private var titleFromFeed: String?
|
||||
|
||||
private var userCancelled = false
|
||||
|
||||
weak var delegate: AddContainerViewControllerChildDelegate?
|
||||
@ -78,31 +69,50 @@ class AddFeedViewController: UITableViewController, AddContainerViewControllerCh
|
||||
return
|
||||
}
|
||||
|
||||
userEnteredURL = url
|
||||
userEnteredTitle = nameTextField.text
|
||||
|
||||
let container = pickerData.containers[folderPickerView.selectedRow(inComponent: 0)]
|
||||
if let account = container as? Account {
|
||||
userEnteredAccount = account
|
||||
|
||||
var account: Account?
|
||||
var folder: Folder?
|
||||
if let containerAccount = container as? Account {
|
||||
account = containerAccount
|
||||
}
|
||||
if let folder = container as? Folder, let account = folder.account {
|
||||
userEnteredAccount = account
|
||||
userEnteredFolder = folder
|
||||
if let containerFolder = container as? Folder, let containerAccount = containerFolder.account {
|
||||
account = containerAccount
|
||||
folder = containerFolder
|
||||
}
|
||||
|
||||
guard let userEnteredAccount = userEnteredAccount else {
|
||||
assertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
if userEnteredAccount.hasFeed(withURL: url.absoluteString) {
|
||||
if account!.hasFeed(withURL: url.absoluteString) {
|
||||
showAlreadySubscribedError()
|
||||
return
|
||||
}
|
||||
|
||||
let title = nameTextField.text
|
||||
|
||||
delegate?.processingDidBegin()
|
||||
|
||||
feedFinder = FeedFinder(url: url, delegate: self)
|
||||
account!.createFeed(with: nil, url: url.absoluteString) { [weak self] result in
|
||||
|
||||
switch result {
|
||||
case .success(let createFeedResult):
|
||||
switch createFeedResult {
|
||||
case .created(let feed):
|
||||
self?.processFeed(feed, account: account!, folder: folder, url: url, title: title)
|
||||
case .multipleChoice(let feedChoices):
|
||||
print()
|
||||
self?.delegate?.processingDidCancel()
|
||||
case .alreadySubscribed:
|
||||
self?.showAlreadySubscribedError()
|
||||
self?.delegate?.processingDidCancel()
|
||||
case .notFound:
|
||||
self?.showNoFeedsErrorMessage()
|
||||
self?.delegate?.processingDidCancel()
|
||||
}
|
||||
case .failure(let error):
|
||||
self?.presentError(error)
|
||||
self?.delegate?.processingDidCancel()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -132,49 +142,6 @@ extension AddFeedViewController: UIPickerViewDataSource, UIPickerViewDelegate {
|
||||
|
||||
}
|
||||
|
||||
extension AddFeedViewController: FeedFinderDelegate {
|
||||
|
||||
public func feedFinder(_ feedFinder: FeedFinder, didFindFeeds feedSpecifiers: Set<FeedSpecifier>) {
|
||||
|
||||
if userCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
if let error = feedFinder.initialDownloadError {
|
||||
if feedFinder.initialDownloadStatusCode == 404 {
|
||||
showNoFeedsErrorMessage()
|
||||
delegate?.processingDidCancel()
|
||||
} else {
|
||||
showInitialDownloadError(error)
|
||||
delegate?.processingDidCancel()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
guard let bestFeedSpecifier = FeedSpecifier.bestFeed(in: feedSpecifiers) else {
|
||||
showNoFeedsErrorMessage()
|
||||
delegate?.processingDidCancel()
|
||||
return
|
||||
}
|
||||
|
||||
self.bestFeedSpecifier = bestFeedSpecifier
|
||||
self.foundFeedURLString = bestFeedSpecifier.urlString
|
||||
|
||||
if let url = URL(string: bestFeedSpecifier.urlString) {
|
||||
InitialFeedDownloader.download(url) { (parsedFeed) in
|
||||
self.titleFromFeed = parsedFeed?.title
|
||||
self.addFeedIfPossible(parsedFeed)
|
||||
}
|
||||
} else {
|
||||
// Shouldn't happen.
|
||||
showNoFeedsErrorMessage()
|
||||
delegate?.processingDidCancel()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension AddFeedViewController {
|
||||
|
||||
private func showAlreadySubscribedError() {
|
||||
@ -196,41 +163,27 @@ private extension AddFeedViewController {
|
||||
presentError(title: title, message: message as String)
|
||||
}
|
||||
|
||||
func addFeedIfPossible(_ parsedFeed: ParsedFeed?) {
|
||||
func processFeed(_ feed: Feed, account: Account, folder: Folder?, url: URL, title: String?) {
|
||||
|
||||
if userCancelled {
|
||||
return
|
||||
}
|
||||
|
||||
guard let account = userEnteredAccount else {
|
||||
assertionFailure("Expected account.")
|
||||
delegate?.processingDidCancel()
|
||||
return
|
||||
if let title = title {
|
||||
account.renameFeed(feed, to: title) { [weak self] result in
|
||||
switch result {
|
||||
case .success:
|
||||
break
|
||||
case .failure(let error):
|
||||
self?.presentError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard let feedURLString = foundFeedURLString else {
|
||||
assertionFailure("Expected feedURLString.")
|
||||
delegate?.processingDidCancel()
|
||||
return
|
||||
}
|
||||
// TODO: make this async and add to above code
|
||||
account.addFeed(feed, to: folder)
|
||||
|
||||
if account.hasFeed(withURL: feedURLString) {
|
||||
showAlreadySubscribedError()
|
||||
delegate?.processingDidCancel()
|
||||
return
|
||||
}
|
||||
|
||||
let feed = account.createFeed(with: titleFromFeed, editedName: userEnteredTitle, url: feedURLString)
|
||||
|
||||
if let parsedFeed = parsedFeed {
|
||||
account.update(feed, with: parsedFeed, {})
|
||||
}
|
||||
|
||||
account.addFeed(feed, to: userEnteredFolder)
|
||||
// Move this into the mess above
|
||||
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
|
||||
|
||||
delegate?.processingDidEnd()
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user