diff --git a/Account/Sources/Account/Account.swift b/Account/Sources/Account/Account.swift index db7f23fd3..305186116 100644 --- a/Account/Sources/Account/Account.swift +++ b/Account/Sources/Account/Account.swift @@ -39,7 +39,6 @@ public enum AccountType: Int, Codable { case cloudKit = 2 case feedly = 16 case feedbin = 17 - case feedWrangler = 18 case newsBlur = 19 case freshRSS = 20 case inoreader = 21 @@ -47,7 +46,7 @@ public enum AccountType: Int, Codable { case theOldReader = 23 public var isDeveloperRestricted: Bool { - return self == .cloudKit || self == .feedbin || self == .feedly || self == .feedWrangler || self == .inoreader + return self == .cloudKit || self == .feedbin || self == .feedly || self == .inoreader } } @@ -269,8 +268,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, self.delegate = FeedbinAccountDelegate(dataFolder: dataFolder, transport: transport) case .feedly: self.delegate = FeedlyAccountDelegate(dataFolder: dataFolder, transport: transport, api: FeedlyAccountDelegate.environment) - case .feedWrangler: - self.delegate = FeedWranglerAccountDelegate(dataFolder: dataFolder, transport: transport) case .newsBlur: self.delegate = NewsBlurAccountDelegate(dataFolder: dataFolder, transport: transport) case .freshRSS: @@ -302,8 +299,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, defaultName = NSLocalizedString("Feedly", comment: "Feedly") case .feedbin: defaultName = NSLocalizedString("Feedbin", comment: "Feedbin") - case .feedWrangler: - defaultName = NSLocalizedString("FeedWrangler", comment: "FeedWrangler") case .newsBlur: defaultName = NSLocalizedString("NewsBlur", comment: "NewsBlur") case .freshRSS: @@ -376,8 +371,6 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, switch type { case .feedbin: FeedbinAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion) - case .feedWrangler: - FeedWranglerAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion) case .newsBlur: NewsBlurAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion) case .freshRSS, .inoreader, .bazQux, .theOldReader: diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerAPICaller.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerAPICaller.swift deleted file mode 100644 index 06e880298..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerAPICaller.swift +++ /dev/null @@ -1,280 +0,0 @@ -// -// FeedWranglerAPICaller.swift -// Account -// -// Created by Jonathan Bennett on 2019-08-29. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -import Foundation -import SyncDatabase -import RSWeb -import Secrets - -enum FeedWranglerError : Error { - case general(message: String) -} - -final class FeedWranglerAPICaller: NSObject { - - private var transport: Transport! - - var credentials: Credentials? - weak var accountMetadata: AccountMetadata? - - init(transport: Transport) { - super.init() - self.transport = transport - } - - func cancelAll() { - transport.cancelAll() - } - - func logout(completion: @escaping (Result) -> Void) { - let url = FeedWranglerConfig.clientURL.appendingPathComponent("users/logout") - let request = URLRequest(url: url, credentials: credentials) - - transport.send(request: request) { result in - switch result { - case .success: - completion(.success(())) - - case .failure(let error): - completion(.failure(error)) - } - } - } - - func validateCredentials(completion: @escaping (Result) -> Void) { - let url = FeedWranglerConfig.clientURL.appendingPathComponent("users/authorize") - let username = self.credentials?.username ?? "" - - standardSend(url: url, resultType: FeedWranglerAuthorizationResult.self) { result in - switch result { - case .success(let (_, results)): - if let accessToken = results?.accessToken { - let authCredentials = Credentials(type: .feedWranglerToken, username: username, secret: accessToken) - completion(.success(authCredentials)) - } else { - completion(.success(nil)) - } - case .failure(let error): - completion(.failure(error)) - } - } - } - - func retrieveSubscriptions(completion: @escaping (Result<[FeedWranglerSubscription], Error>) -> Void) { - let url = FeedWranglerConfig.clientURL.appendingPathComponent("subscriptions/list") - - standardSend(url: url, resultType: FeedWranglerSubscriptionsRequest.self) { result in - switch result { - case .success(let (_, results)): - completion(.success(results?.feeds ?? [])) - - case .failure(let error): - completion(.failure(error)) - } - } - } - - func addSubscription(url: String, completion: @escaping (Result) -> Void) { - let url = FeedWranglerConfig - .clientURL - .appendingPathComponent("subscriptions/add_feed_and_wait") - .appendingQueryItems([ - URLQueryItem(name: "feed_url", value: url), - URLQueryItem(name: "choose_first", value: "true") - ]) - - standardSend(url: url, resultType: FeedWranglerSubscriptionResult.self) { result in - switch result { - case .success(let (_, results)): - if let results = results { - if let error = results.error { - completion(.failure(FeedWranglerError.general(message: error))) - } else { - completion(.success(results.feed)) - } - } else { - completion(.failure(FeedWranglerError.general(message: "No feed found"))) - } - - - case .failure(let error): - completion(.failure(error)) - } - } - } - - func renameSubscription(feedID: String, newName: String, completion: @escaping (Result) -> Void) { - var postData = URLComponents(url: FeedWranglerConfig.clientURL, resolvingAgainstBaseURL: false) - postData?.path += "subscriptions/rename_feed" - postData?.queryItems = [ - URLQueryItem(name: "feed_id", value: feedID), - URLQueryItem(name: "feed_name", value: newName), - ] - - guard let url = postData?.urlWithEnhancedPercentEncodedQuery else { - completion(.failure(FeedWranglerError.general(message: "Could not encode name"))) - return - } - - standardSend(url: url, resultType: FeedWranglerSubscriptionsRequest.self) { result in - switch result { - case .success: - completion(.success(())) - - case .failure(let error): - completion(.failure(error)) - } - } - } - - func removeSubscription(feedID: String, completion: @escaping (Result) -> Void) { - let url = FeedWranglerConfig.clientURL - .appendingPathComponent("subscriptions/remove_feed") - .appendingQueryItem(URLQueryItem(name: "feed_id", value: feedID)) - - standardSend(url: url, resultType: FeedWranglerGenericResult.self) { result in - switch result { - case .success: - completion(.success(())) - - case .failure(let error): - completion(.failure(error)) - } - } - } - - // MARK: FeedItems - func retrieveEntries(articleIDs: [String], completion: @escaping (Result<[FeedWranglerFeedItem], Error>) -> Void) { - let IDs = articleIDs.joined(separator: ",") - let url = FeedWranglerConfig.clientURL - .appendingPathComponent("feed_items/get") - .appendingQueryItem(URLQueryItem(name: "feed_item_ids", value: IDs)) - - standardSend(url: url, resultType: FeedWranglerFeedItemsRequest.self) { result in - switch result { - case .success(let (_, results)): - completion(.success(results?.feedItems ?? [])) - - case .failure(let error): - completion(.failure(error)) - } - } - - } - - func retrieveFeedItems(page: Int = 0, feed: WebFeed? = nil, completion: @escaping (Result<[FeedWranglerFeedItem], Error>) -> Void) { - let queryItems = [ - URLQueryItem(name: "read", value: "false"), - URLQueryItem(name: "offset", value: String(page * FeedWranglerConfig.pageSize)), - feed.map { URLQueryItem(name: "feed_id", value: $0.webFeedID) } - ].compactMap { $0 } - let url = FeedWranglerConfig.clientURL - .appendingPathComponent("feed_items/list") - .appendingQueryItems(queryItems) - - standardSend(url: url, resultType: FeedWranglerFeedItemsRequest.self) { result in - switch result { - case .success(let (_, results)): - completion(.success(results?.feedItems ?? [])) - - case .failure(let error): - completion(.failure(error)) - } - } - } - - func retrieveUnreadFeedItemIds(completion: @escaping (Result<[FeedWranglerFeedItemId], Error>) -> Void) { - retrieveAllFeedItemIds(filters: [URLQueryItem(name: "read", value: "false")], completion: completion) - } - - func retrieveStarredFeedItemIds(completion: @escaping (Result<[FeedWranglerFeedItemId], Error>) -> Void) { - retrieveAllFeedItemIds(filters: [URLQueryItem(name: "starred", value: "true")], completion: completion) - } - - private func retrieveAllFeedItemIds(filters: [URLQueryItem] = [], foundItems: [FeedWranglerFeedItemId] = [], page: Int = 0, completion: @escaping (Result<[FeedWranglerFeedItemId], Error>) -> Void) { - retrieveFeedItemIds(filters: filters, page: page) { result in - switch result { - case .success(let newItems): - if newItems.count > 0 { - self.retrieveAllFeedItemIds(filters: filters, foundItems: foundItems + newItems, page: (page + 1), completion: completion) - } else { - completion(.success(foundItems + newItems)) - } - - case .failure(let error): - completion(.failure(error)) - } - } - } - - private func retrieveFeedItemIds(filters: [URLQueryItem] = [], page: Int = 0, completion: @escaping (Result<[FeedWranglerFeedItemId], Error>) -> Void) { - let url = FeedWranglerConfig.clientURL - .appendingPathComponent("feed_items/list_ids") - .appendingQueryItems(filters + [URLQueryItem(name: "offset", value: String(page * FeedWranglerConfig.idsPageSize))]) - - standardSend(url: url, resultType: FeedWranglerFeedItemIdsRequest.self) { result in - switch result { - case .success(let (_, results)): - completion(.success(results?.feedItems ?? [])) - - case .failure(let error): - completion(.failure(error)) - } - } - } - - func updateArticleStatus(_ articleID: String, _ statuses: [SyncStatus], completion: @escaping () -> Void) { - - var queryItems = statuses.compactMap { status -> URLQueryItem? in - switch status.key { - case .read: - return URLQueryItem(name: "read", value: status.flag.description) - case .starred: - return URLQueryItem(name: "starred", value: status.flag.description) - case .deleted: - return nil - case .new: - return nil - } - } - queryItems.append(URLQueryItem(name: "feed_item_id", value: articleID)) - let url = FeedWranglerConfig.clientURL - .appendingPathComponent("feed_items/update") - .appendingQueryItems(queryItems) - - standardSend(url: url, resultType: FeedWranglerGenericResult.self) { result in - completion() - } - } - - private func standardSend(url: URL?, resultType: R.Type, completion: @escaping (Result<(HTTPURLResponse, R?), Error>) -> Void) { - guard let callURL = url else { - completion(.failure(TransportError.noURL)) - return - } - let request = URLRequest(url: callURL, credentials: credentials) - - transport.send(request: request, resultType: resultType, completion: completion) - } - -} - -private extension URLComponents { - - var urlWithEnhancedPercentEncodedQuery: URL? { - guard let tempQueryItems = self.queryItems, !tempQueryItems.isEmpty else { - return self.url - } - - var tempComponents = self - tempComponents.percentEncodedQuery = self.enhancedPercentEncodedQuery - return tempComponents.url - } -} diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerAccountDelegate.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerAccountDelegate.swift deleted file mode 100644 index 1be0ec832..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerAccountDelegate.swift +++ /dev/null @@ -1,608 +0,0 @@ -// -// FeedWranglerAccountDelegate.swift -// Account -// -// Created by Jonathan Bennett on 2019-08-29. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Articles -import RSCore -import RSParser -import RSWeb -import SyncDatabase -import os.log -import Secrets - -final class FeedWranglerAccountDelegate: AccountDelegate { - - var behaviors: AccountBehaviors = [.disallowFolderManagement] - - var isOPMLImportInProgress = false - var server: String? = FeedWranglerConfig.clientPath - var credentials: Credentials? { - didSet { - caller.credentials = credentials - } - } - - var accountMetadata: AccountMetadata? - var refreshProgress = DownloadProgress(numberOfTasks: 0) - - private let caller: FeedWranglerAPICaller - private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Feed Wrangler") - private let database: SyncDatabase - - init(dataFolder: String, transport: Transport?) { - if let transport = transport { - caller = FeedWranglerAPICaller(transport: transport) - } else { - let sessionConfiguration = URLSessionConfiguration.default - sessionConfiguration.requestCachePolicy = .reloadIgnoringLocalCacheData - sessionConfiguration.timeoutIntervalForRequest = 60.0 - sessionConfiguration.httpShouldSetCookies = false - sessionConfiguration.httpCookieAcceptPolicy = .never - sessionConfiguration.httpMaximumConnectionsPerHost = 1 - sessionConfiguration.httpCookieStorage = nil - sessionConfiguration.urlCache = nil - - if let userAgentHeaders = UserAgent.headers() { - sessionConfiguration.httpAdditionalHeaders = userAgentHeaders - } - - let session = URLSession(configuration: sessionConfiguration) - caller = FeedWranglerAPICaller(transport: session) - } - - database = SyncDatabase(databaseFilePath: dataFolder.appending("/DB.sqlite3")) - } - - func accountWillBeDeleted(_ account: Account) { - caller.logout() { _ in } - } - - func receiveRemoteNotification(for account: Account, userInfo: [AnyHashable : Any], completion: @escaping () -> Void) { - completion() - } - - func refreshAll(for account: Account, completion: @escaping (Result) -> Void) { - refreshProgress.addToNumberOfTasksAndRemaining(6) - - self.refreshCredentials(for: account) { - self.refreshProgress.completeTask() - self.refreshSubscriptions(for: account) { result in - self.refreshProgress.completeTask() - - switch result { - case .success: - self.sendArticleStatus(for: account) { result in - self.refreshProgress.completeTask() - - switch result { - case .success: - self.refreshArticleStatus(for: account) { result in - self.refreshProgress.completeTask() - - switch result { - case .success: - self.refreshArticles(for: account) { result in - self.refreshProgress.completeTask() - - switch result { - case .success: - self.refreshMissingArticles(for: account) { result in - self.refreshProgress.completeTask() - - switch result { - case .success: - DispatchQueue.main.async { - account.metadata.lastArticleFetchEndTime = Date() - completion(.success(())) - } - - case .failure(let error): - completion(.failure(error)) - } - } - - case .failure(let error): - completion(.failure(error)) - } - } - - case .failure(let error): - completion(.failure(error)) - } - } - - case .failure(let error): - completion(.failure(error)) - } - } - - case .failure(let error): - completion(.failure(error)) - } - } - } - } - - func refreshCredentials(for account: Account, completion: @escaping (() -> Void)) { - os_log(.debug, log: log, "Refreshing credentials...") - // MARK: TODO - credentials = try? account.retrieveCredentials(type: .feedWranglerToken) - completion() - } - - func refreshSubscriptions(for account: Account, completion: @escaping ((Result) -> Void)) { - os_log(.debug, log: log, "Refreshing subscriptions...") - caller.retrieveSubscriptions { result in - switch result { - case .success(let subscriptions): - self.syncFeeds(account, subscriptions) - completion(.success(())) - - case .failure(let error): - os_log(.debug, log: self.log, "Failed to refresh subscriptions: %@", error.localizedDescription) - completion(.failure(error)) - } - - } - } - - func syncArticleStatus(for account: Account, completion: ((Result) -> Void)? = nil) { - sendArticleStatus(for: account) { result in - switch result { - case .success: - self.refreshArticleStatus(for: account) { result in - switch result { - case .success: - completion?(.success(())) - case .failure(let error): - completion?(.failure(error)) - } - } - case .failure(let error): - completion?(.failure(error)) - } - } - } - - func refreshArticles(for account: Account, page: Int = 0, completion: @escaping ((Result) -> Void)) { - os_log(.debug, log: log, "Refreshing articles, page: %d...", page) - - caller.retrieveFeedItems(page: page) { result in - switch result { - case .success(let items): - self.syncFeedItems(account, items) { - if items.count == 0 { - completion(.success(())) - } else { - self.refreshArticles(for: account, page: (page + 1), completion: completion) - } - } - - case .failure(let error): - completion(.failure(error)) - } - } - } - - func refreshMissingArticles(for account: Account, completion: @escaping ((Result)-> Void)) { - account.fetchArticleIDsForStatusesWithoutArticlesNewerThanCutoffDate { articleIDsResult in - - func process(_ fetchedArticleIDs: Set) { - os_log(.debug, log: self.log, "Refreshing missing articles...") - let group = DispatchGroup() - - let articleIDs = Array(fetchedArticleIDs) - let chunkedArticleIDs = articleIDs.chunked(into: 100) - - for chunk in chunkedArticleIDs { - group.enter() - self.caller.retrieveEntries(articleIDs: chunk) { result in - switch result { - case .success(let entries): - self.syncFeedItems(account, entries) { - group.leave() - } - - case .failure(let error): - os_log(.error, log: self.log, "Refresh missing articles failed: %@", error.localizedDescription) - group.leave() - } - } - } - - group.notify(queue: DispatchQueue.main) { - self.refreshProgress.completeTask() - os_log(.debug, log: self.log, "Done refreshing missing articles.") - completion(.success(())) - } - } - - switch articleIDsResult { - case .success(let articleIDs): - process(articleIDs) - case .failure(let databaseError): - self.refreshProgress.completeTask() - completion(.failure(databaseError)) - } - } - } - - func sendArticleStatus(for account: Account, completion: @escaping VoidResultCompletionBlock) { - os_log(.debug, log: log, "Sending article status...") - - database.selectForProcessing { result in - - func processStatuses(_ syncStatuses: [SyncStatus]) { - let articleStatuses = Dictionary(grouping: syncStatuses, by: { $0.articleID }) - let group = DispatchGroup() - - articleStatuses.forEach { articleID, statuses in - group.enter() - self.caller.updateArticleStatus(articleID, statuses) { - group.leave() - } - } - - group.notify(queue: DispatchQueue.main) { - os_log(.debug, log: self.log, "Done sending article statuses.") - completion(.success(())) - } - } - - switch result { - case .success(let syncStatuses): - processStatuses(syncStatuses) - case .failure(let databaseError): - completion(.failure(databaseError)) - } - } - } - - func refreshArticleStatus(for account: Account, completion: @escaping ((Result) -> Void)) { - os_log(.debug, log: log, "Refreshing article status...") - let group = DispatchGroup() - - group.enter() - caller.retrieveUnreadFeedItemIds { result in - switch result { - case .success(let items): - self.syncArticleReadState(account, items) - group.leave() - - case .failure(let error): - os_log(.info, log: self.log, "Retrieving unread entries failed: %@.", error.localizedDescription) - group.leave() - } - } - - // starred - group.enter() - caller.retrieveStarredFeedItemIds { result in - switch result { - case .success(let items): - self.syncArticleStarredState(account, items) - group.leave() - - case .failure(let error): - os_log(.info, log: self.log, "Retrieving starred entries failed: %@.", error.localizedDescription) - group.leave() - } - } - - group.notify(queue: DispatchQueue.main) { - os_log(.debug, log: self.log, "Done refreshing article statuses.") - completion(.success(())) - } - } - - func importOPML(for account: Account, opmlFile: URL, completion: @escaping (Result) -> Void) { - fatalError() - } - - func createFolder(for account: Account, name: String, completion: @escaping (Result) -> Void) { - fatalError() - } - - func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) { - fatalError() - } - - func removeFolder(for account: Account, with folder: Folder, completion: @escaping (Result) -> Void) { - fatalError() - } - - func createWebFeed(for account: Account, url: String, name: String?, container: Container, validateFeed: Bool, completion: @escaping (Result) -> Void) { - refreshProgress.addToNumberOfTasksAndRemaining(2) - - self.refreshCredentials(for: account) { - self.refreshProgress.completeTask() - self.caller.addSubscription(url: url) { result in - self.refreshProgress.completeTask() - - switch result { - case .success(let subscription): - self.addFeedWranglerSubscription(account: account, subscription: subscription, name: name, container: container, completion: completion) - - case .failure(let error): - DispatchQueue.main.async { - completion(.failure(error)) - } - } - } - } - } - - private func addFeedWranglerSubscription(account: Account, subscription sub: FeedWranglerSubscription, name: String?, container: Container, completion: @escaping (Result) -> Void) { - DispatchQueue.main.async { - let feed = account.createWebFeed(with: sub.title, url: sub.feedURL, webFeedID: String(sub.feedID), homePageURL: sub.siteURL) - - account.addWebFeed(feed, to: container) { result in - switch result { - case .success: - if let name = name { - account.renameWebFeed(feed, to: name) { result in - switch result { - case .success: - self.initialFeedDownload(account: account, feed: feed, completion: completion) - - case .failure(let error): - completion(.failure(error)) - } - } - } else { - self.initialFeedDownload(account: account, feed: feed, completion: completion) - } - - case .failure(let error): - completion(.failure(error)) - } - } - } - } - - private func initialFeedDownload(account: Account, feed: WebFeed, completion: @escaping (Result) -> Void) { - - self.caller.retrieveFeedItems(page: 0, feed: feed) { results in - switch results { - case .success(let entries): - self.syncFeedItems(account, entries) { - DispatchQueue.main.async { - completion(.success(feed)) - } - } - - case .failure(let error): - DispatchQueue.main.async { - completion(.failure(error)) - } - } - } - } - - func renameWebFeed(for account: Account, with feed: WebFeed, to name: String, completion: @escaping (Result) -> Void) { - refreshProgress.addToNumberOfTasksAndRemaining(2) - - self.refreshCredentials(for: account) { - self.refreshProgress.completeTask() - self.caller.renameSubscription(feedID: feed.webFeedID, newName: name) { result in - self.refreshProgress.completeTask() - - switch result { - case .success: - DispatchQueue.main.async { - feed.editedName = name - completion(.success(())) - } - - case .failure(let error): - DispatchQueue.main.async { - let wrappedError = AccountError.wrappedError(error: error, account: account) - completion(.failure(wrappedError)) - } - } - } - } - } - - func addWebFeed(for account: Account, with feed: WebFeed, to container: Container, completion: @escaping (Result) -> Void) { - // just add to account, folders are not supported - DispatchQueue.main.async { - account.addFeedIfNotInAnyFolder(feed) - completion(.success(())) - } - } - - func removeWebFeed(for account: Account, with feed: WebFeed, from container: Container, completion: @escaping (Result) -> Void) { - refreshProgress.addToNumberOfTasksAndRemaining(2) - - self.refreshCredentials(for: account) { - self.refreshProgress.completeTask() - self.caller.removeSubscription(feedID: feed.webFeedID) { result in - self.refreshProgress.completeTask() - - switch result { - case .success: - DispatchQueue.main.async { - account.clearWebFeedMetadata(feed) - account.removeWebFeed(feed) - completion(.success(())) - } - - case .failure(let error): - DispatchQueue.main.async { - let wrappedError = AccountError.wrappedError(error: error, account: account) - completion(.failure(wrappedError)) - } - } - } - } - } - - func moveWebFeed(for account: Account, with feed: WebFeed, from: Container, to: Container, completion: @escaping (Result) -> Void) { - fatalError() - } - - func restoreWebFeed(for account: Account, feed: WebFeed, container: Container, completion: @escaping (Result) -> Void) { - if let existingFeed = account.existingWebFeed(withURL: feed.url) { - account.addWebFeed(existingFeed, to: container) { result in - switch result { - case .success: - completion(.success(())) - case .failure(let error): - completion(.failure(error)) - } - } - } else { - createWebFeed(for: account, url: feed.url, name: feed.editedName, container: container, validateFeed: true) { result in - switch result { - case .success: - completion(.success(())) - case .failure(let error): - completion(.failure(error)) - } - } - } - } - - func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result) -> Void) { - fatalError() - } - - func markArticles(for account: Account, articles: Set
, statusKey: ArticleStatus.Key, flag: Bool, completion: @escaping (Result) -> Void) { - account.update(articles, statusKey: statusKey, flag: flag) { result in - switch result { - case .success(let articles): - let syncStatuses = articles.map { article in - return SyncStatus(articleID: article.articleID, key: SyncStatus.Key(statusKey), flag: flag) - } - - self.database.insertStatuses(syncStatuses) { _ in - self.database.selectPendingCount { result in - if let count = try? result.get(), count > 100 { - self.sendArticleStatus(for: account) { _ in } - } - completion(.success(())) - } - } - case .failure(let error): - completion(.failure(error)) - } - } - } - - func accountDidInitialize(_ account: Account) { - credentials = try? account.retrieveCredentials(type: .feedWranglerToken) - } - - static func validateCredentials(transport: Transport, credentials: Credentials, endpoint: URL? = nil, completion: @escaping (Result) -> Void) { - let caller = FeedWranglerAPICaller(transport: transport) - caller.credentials = credentials - caller.validateCredentials() { result in - DispatchQueue.main.async { - completion(result) - } - } - } - - // MARK: Suspend and Resume (for iOS) - - /// Suspend all network activity - func suspendNetwork() { - caller.cancelAll() - } - - /// Suspend the SQLLite databases - func suspendDatabase() { - database.suspend() - } - - /// Make sure no SQLite databases are open and we are ready to issue network requests. - func resume() { - database.resume() - } -} - -// MARK: Private -private extension FeedWranglerAccountDelegate { - - func syncFeeds(_ account: Account, _ subscriptions: [FeedWranglerSubscription]) { - assert(Thread.isMainThread) - let feedIds = subscriptions.map { String($0.feedID) } - - let feedsToRemove = account.topLevelWebFeeds.filter { !feedIds.contains($0.webFeedID) } - account.removeFeeds(feedsToRemove) - - var subscriptionsToAdd = Set() - subscriptions.forEach { subscription in - let subscriptionId = String(subscription.feedID) - - if let feed = account.existingWebFeed(withWebFeedID: subscriptionId) { - feed.name = subscription.title - feed.editedName = nil - feed.homePageURL = subscription.siteURL - feed.externalID = nil // MARK: TODO What should this be? - } else { - subscriptionsToAdd.insert(subscription) - } - } - - subscriptionsToAdd.forEach { subscription in - let feedId = String(subscription.feedID) - let feed = account.createWebFeed(with: subscription.title, url: subscription.feedURL, webFeedID: feedId, homePageURL: subscription.siteURL) - feed.externalID = nil - account.addWebFeed(feed) - } - } - - func syncFeedItems(_ account: Account, _ feedItems: [FeedWranglerFeedItem], completion: @escaping VoidCompletionBlock) { - let parsedItems = feedItems.map { (item: FeedWranglerFeedItem) -> ParsedItem in - let itemID = String(item.feedItemID) - // let authors = ... - let parsedItem = ParsedItem(syncServiceID: itemID, uniqueID: itemID, feedURL: String(item.feedID), url: nil, externalURL: item.url, title: item.title, language: nil, contentHTML: item.body, contentText: nil, summary: nil, imageURL: nil, bannerImageURL: nil, datePublished: item.publishedDate, dateModified: item.updatedDate, authors: nil, tags: nil, attachments: nil) - - return parsedItem - } - - let feedIDsAndItems = Dictionary(grouping: parsedItems, by: { $0.feedURL }).mapValues { Set($0) } - account.update(webFeedIDsAndItems: feedIDsAndItems, defaultRead: true) { _ in - completion() - } - } - - func syncArticleReadState(_ account: Account, _ unreadFeedItems: [FeedWranglerFeedItemId]) { - let unreadServerItemIDs = Set(unreadFeedItems.map { String($0.feedItemID) }) - account.fetchUnreadArticleIDs { articleIDsResult in - guard let unreadLocalItemIDs = try? articleIDsResult.get() else { - return - } - account.markAsUnread(unreadServerItemIDs) - - let readItemIDs = unreadLocalItemIDs.subtracting(unreadServerItemIDs) - account.markAsRead(readItemIDs) - } - } - - func syncArticleStarredState(_ account: Account, _ starredFeedItems: [FeedWranglerFeedItemId]) { - let starredServerItemIDs = Set(starredFeedItems.map { String($0.feedItemID) }) - account.fetchStarredArticleIDs { articleIDsResult in - guard let starredLocalItemIDs = try? articleIDsResult.get() else { - return - } - - account.markAsStarred(starredServerItemIDs) - - let unstarredItemIDs = starredLocalItemIDs.subtracting(starredServerItemIDs) - account.markAsUnstarred(unstarredItemIDs) - } - } - - func syncArticleState(_ account: Account, key: ArticleStatus.Key, flag: Bool, serverFeedItems: [FeedWranglerFeedItem]) { - let _ /*serverFeedItemIDs*/ = serverFeedItems.map { String($0.feedID) } - - // todo generalize this logic - } -} diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerAuthorizationResult.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerAuthorizationResult.swift deleted file mode 100644 index 5055b4a0b..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerAuthorizationResult.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// FeedWranglerAuthorizationResult.swift -// Account -// -// Created by Jonathan Bennett on 2019-11-20. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -struct FeedWranglerAuthorizationResult: Hashable, Codable { - - let accessToken: String? - let error: String? - let result: String - - - enum CodingKeys: String, CodingKey { - case accessToken = "access_token" - case error = "error" - case result = "result" - } -} diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerConfig.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerConfig.swift deleted file mode 100644 index eca64e64a..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerConfig.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// FeedWranglerConfig.swift -// NetNewsWire -// -// Created by Jonathan Bennett on 9/27/19. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Foundation -import Secrets - -enum FeedWranglerConfig { - static let pageSize = 100 - static let idsPageSize = 1000 - static let clientPath = "https://feedwrangler.net/api/v2/" - static let clientURL = { - URL(string: FeedWranglerConfig.clientPath)! - }() -} diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItem.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItem.swift deleted file mode 100644 index 28389b292..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItem.swift +++ /dev/null @@ -1,62 +0,0 @@ -// -// FeedWranglerFeedItem.swift -// Account -// -// Created by Jonathan Bennett on 2019-10-16.4// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -struct FeedWranglerFeedItem: Hashable, Codable { - - let feedItemID: Int - let publishedAt: Int - let createdAt: Int - let versionKey: Int - let updatedAt: Int - let url: String - let title: String - let starred: Bool - let read: Bool - let readLater: Bool - let body: String - let author: String? - let feedID: Int - let feedName: String - - var publishedDate: Date { - get { - Date(timeIntervalSince1970: Double(publishedAt)) - } - } - - var createdDate: Date { - get { - Date(timeIntervalSince1970: Double(createdAt)) - } - } - - var updatedDate: Date { - get { - Date(timeIntervalSince1970: Double(updatedAt)) - } - } - - enum CodingKeys: String, CodingKey { - case feedItemID = "feed_item_id" - case publishedAt = "published_at" - case createdAt = "created_at" - case versionKey = "version_key" - case updatedAt = "updated_at" - case url = "url" - case title = "title" - case starred = "starred" - case read = "read" - case readLater = "read_later" - case body = "body" - case author = "author" - case feedID = "feed_id" - case feedName = "feed_name" - } - -} diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItemId.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItemId.swift deleted file mode 100644 index 856131b13..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItemId.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// File.swift -// -// -// Created by Jonathan Bennett on 2021-01-14. -// - - -import Foundation - -struct FeedWranglerFeedItemId: Hashable, Codable { - - let feedItemID: Int - - enum CodingKeys: String, CodingKey { - case feedItemID = "feed_item_id" - } - -} diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItemIdsRequest.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItemIdsRequest.swift deleted file mode 100644 index dda666285..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItemIdsRequest.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// FeedWranglerFeedItemsRequest.swift -// Account -// -// Created by Jonathan Bennett on 2021-01-14. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -struct FeedWranglerFeedItemIdsRequest: Hashable, Codable { - - let count: Int - let feedItems: [FeedWranglerFeedItemId] - let error: String? - let result: String - - enum CodingKeys: String, CodingKey { - case count = "count" - case feedItems = "feed_items" - case error = "error" - case result = "result" - } - -} diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItemsRequest.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItemsRequest.swift deleted file mode 100644 index 426aae6d3..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerFeedItemsRequest.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// FeedWranglerFeedItemsRequest.swift -// Account -// -// Created by Jonathan Bennett on 2019-10-16. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -struct FeedWranglerFeedItemsRequest: Hashable, Codable { - - let count: Int - let feedItems: [FeedWranglerFeedItem] - let error: String? - let result: String - - enum CodingKeys: String, CodingKey { - case count = "count" - case feedItems = "feed_items" - case error = "error" - case result = "result" - } - -} diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerGenericResult.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerGenericResult.swift deleted file mode 100644 index 817fe9c8b..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerGenericResult.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// FeedWranglerGenericResult.swift -// Account -// -// Created by Jonathan Bennett on 2019-10-16. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -struct FeedWranglerGenericResult: Hashable, Codable { - - let error: String? - let result: String - -} diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerSubscription.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerSubscription.swift deleted file mode 100644 index 821dd41ad..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerSubscription.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// FeedWranglerSubscription.swift -// Account -// -// Created by Jonathan Bennett on 2019-10-16. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// -import Foundation -import RSCore -import RSParser - -struct FeedWranglerSubscription: Hashable, Codable { - - let title: String - let feedID: Int - let feedURL: String - let siteURL: String? - - enum CodingKeys: String, CodingKey { - case title = "title" - case feedID = "feed_id" - case feedURL = "feed_url" - case siteURL = "site_url" - } - -} diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerSubscriptionResult.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerSubscriptionResult.swift deleted file mode 100644 index 2bd21bbee..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerSubscriptionResult.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// FeedWranglerSubscriptionResult.swift -// Account -// -// Created by Jonathan Bennett on 2019-11-20. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -struct FeedWranglerSubscriptionResult: Hashable, Codable { - - let feed: FeedWranglerSubscription - let error: String? - let result: String - -} - diff --git a/Account/Sources/Account/FeedWrangler/FeedWranglerSubscriptionsRequest.swift b/Account/Sources/Account/FeedWrangler/FeedWranglerSubscriptionsRequest.swift deleted file mode 100644 index 66fb6a20b..000000000 --- a/Account/Sources/Account/FeedWrangler/FeedWranglerSubscriptionsRequest.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// FeedWranglerSubscriptionsRequest.swift -// Account -// -// Created by Jonathan Bennett on 2019-10-16. -// Copyright © 2019 Ranchero Software, LLC. All rights reserved. -// - -import Foundation - -struct FeedWranglerSubscriptionsRequest: Hashable, Codable { - - let feeds: [FeedWranglerSubscription] - let error: String? - let result: String - -} diff --git a/Account/Sources/Account/URLRequest+Account.swift b/Account/Sources/Account/URLRequest+Account.swift index 18f0ee5c0..035106a7c 100755 --- a/Account/Sources/Account/URLRequest+Account.swift +++ b/Account/Sources/Account/URLRequest+Account.swift @@ -26,14 +26,6 @@ public extension URLRequest { let base64 = data?.base64EncodedString() let auth = "Basic \(base64 ?? "")" setValue(auth, forHTTPHeaderField: HTTPRequestHeader.authorization) - case .feedWranglerBasic: - self.url = url.appendingQueryItems([ - URLQueryItem(name: "email", value: credentials.username), - URLQueryItem(name: "password", value: credentials.secret), - URLQueryItem(name: "client_key", value: SecretsManager.provider.feedWranglerKey) - ]) - case .feedWranglerToken: - self.url = url.appendingQueryItem(URLQueryItem(name: "access_token", value: credentials.secret)) case .newsBlurBasic: setValue("application/x-www-form-urlencoded", forHTTPHeaderField: HTTPRequestHeader.contentType) httpMethod = "POST" diff --git a/Account/Tests/AccountTests/Feedly/FeedlyTestSecrets.swift b/Account/Tests/AccountTests/Feedly/FeedlyTestSecrets.swift index f55e71043..9b92c02af 100644 --- a/Account/Tests/AccountTests/Feedly/FeedlyTestSecrets.swift +++ b/Account/Tests/AccountTests/Feedly/FeedlyTestSecrets.swift @@ -9,7 +9,6 @@ import Foundation import Secrets struct FeedlyTestSecrets: SecretsProvider { - var feedWranglerKey = "" var mercuryClientId = "" var mercuryClientSecret = "" var feedlyClientId = "" diff --git a/Mac/AppAssets.swift b/Mac/AppAssets.swift index 94725f96d..d679f248e 100644 --- a/Mac/AppAssets.swift +++ b/Mac/AppAssets.swift @@ -28,10 +28,6 @@ struct AppAssets { return RSImage(named: "accountFeedly") }() - static var accountFeedWrangler: RSImage! = { - return RSImage(named: "accountFeedWrangler") - }() - static var accountFreshRSS: RSImage! = { return RSImage(named: "accountFreshRSS") }() @@ -342,8 +338,6 @@ struct AppAssets { return AppAssets.accountFeedbin case .feedly: return AppAssets.accountFeedly - case .feedWrangler: - return AppAssets.accountFeedWrangler case .freshRSS: return AppAssets.accountFreshRSS case .inoreader: diff --git a/Mac/Preferences/Accounts/AccountsDetailViewController.swift b/Mac/Preferences/Accounts/AccountsDetailViewController.swift index 390fb3c18..5386dd67e 100644 --- a/Mac/Preferences/Accounts/AccountsDetailViewController.swift +++ b/Mac/Preferences/Accounts/AccountsDetailViewController.swift @@ -79,11 +79,6 @@ final class AccountsDetailViewController: NSViewController, NSTextFieldDelegate accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!) accountsWindowController = accountsReaderAPIWindowController break - case .feedWrangler: - let accountsFeedWranglerWindowController = AccountsFeedWranglerWindowController() - accountsFeedWranglerWindowController.account = account - accountsFeedWranglerWindowController.runSheetOnWindow(self.view.window!) - accountsWindowController = accountsFeedWranglerWindowController case .newsBlur: let accountsNewsBlurWindowController = AccountsNewsBlurWindowController() accountsNewsBlurWindowController.account = account diff --git a/Mac/Preferences/Accounts/AccountsFeedWrangler.xib b/Mac/Preferences/Accounts/AccountsFeedWrangler.xib deleted file mode 100644 index e07cd2ef6..000000000 --- a/Mac/Preferences/Accounts/AccountsFeedWrangler.xib +++ /dev/null @@ -1,234 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NSAllRomanInputSourcesLocaleIdentifier - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Mac/Preferences/Accounts/AccountsFeedWranglerWindowController.swift b/Mac/Preferences/Accounts/AccountsFeedWranglerWindowController.swift deleted file mode 100644 index d7d185c9d..000000000 --- a/Mac/Preferences/Accounts/AccountsFeedWranglerWindowController.swift +++ /dev/null @@ -1,137 +0,0 @@ -// -// AccountsFeedWranglerWindowController.swift -// NetNewsWire -// -// Created by Jonathan Bennett on 2019-08-29. -// Copyright © 2019 Ranchero Software. All rights reserved. -// - -import AppKit -import Account -import RSWeb -import Secrets - -class AccountsFeedWranglerWindowController: NSWindowController { - - @IBOutlet weak var signInTextField: NSTextField! - @IBOutlet weak var noAccountTextField: NSTextField! - @IBOutlet weak var createNewAccountButton: NSButton! - @IBOutlet weak var progressIndicator: NSProgressIndicator! - @IBOutlet weak var usernameTextField: NSTextField! - @IBOutlet weak var passwordTextField: NSSecureTextField! - @IBOutlet weak var errorMessageLabel: NSTextField! - @IBOutlet weak var actionButton: NSButton! - - var account: Account? - - private weak var hostWindow: NSWindow? - - convenience init() { - self.init(windowNibName: NSNib.Name("AccountsFeedWrangler")) - } - - override func windowDidLoad() { - if let account = account, let credentials = try? account.retrieveCredentials(type: .basic) { - usernameTextField.stringValue = credentials.username - actionButton.title = NSLocalizedString("Update", comment: "Update") - signInTextField.stringValue = NSLocalizedString("Update your Feed Wrangler account credentials.", comment: "SignIn") - noAccountTextField.isHidden = true - createNewAccountButton.isHidden = true - } else { - actionButton.title = NSLocalizedString("Create", comment: "Create") - signInTextField.stringValue = NSLocalizedString("Sign in to your Feed Wrangler account.", comment: "SignIn") - } - enableAutofill() - usernameTextField.becomeFirstResponder() - } - - // MARK: API - - func runSheetOnWindow(_ hostWindow: NSWindow, completion: ((NSApplication.ModalResponse) -> Void)? = nil) { - self.hostWindow = hostWindow - hostWindow.beginSheet(window!, completionHandler: completion) - } - - // MARK: Actions - - @IBAction func cancel(_ sender: Any) { - hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.cancel) - } - - @IBAction func action(_ sender: Any) { - self.errorMessageLabel.stringValue = "" - - guard !usernameTextField.stringValue.isEmpty && !passwordTextField.stringValue.isEmpty else { - self.errorMessageLabel.stringValue = NSLocalizedString("Username & password required.", comment: "Credentials Error") - return - } - - guard account != nil || !AccountManager.shared.duplicateServiceAccount(type: .feedWrangler, username: usernameTextField.stringValue) else { - self.errorMessageLabel.stringValue = NSLocalizedString("There is already a FeedWrangler account with that username created.", comment: "Duplicate Error") - return - } - - actionButton.isEnabled = false - progressIndicator.isHidden = false - progressIndicator.startAnimation(self) - - let credentials = Credentials(type: .feedWranglerBasic, username: usernameTextField.stringValue, secret: passwordTextField.stringValue) - Account.validateCredentials(type: .feedWrangler, credentials: credentials) { [weak self] result in - - guard let self = self else { return } - - self.actionButton.isEnabled = true - self.progressIndicator.isHidden = true - self.progressIndicator.stopAnimation(self) - - switch result { - case .success(let validatedCredentials): - guard let validatedCredentials = validatedCredentials else { - self.errorMessageLabel.stringValue = NSLocalizedString("Invalid email/password combination.", comment: "Credentials Error") - return - } - if self.account == nil { - self.account = AccountManager.shared.createAccount(type: .feedWrangler) - } - - do { - try self.account?.removeCredentials(type: .feedWranglerBasic) - try self.account?.removeCredentials(type: .feedWranglerToken) - try self.account?.storeCredentials(credentials) - try self.account?.storeCredentials(validatedCredentials) - - self.account?.refreshAll() { result in - switch result { - case .success: - break - case .failure(let error): - NSApplication.shared.presentError(error) - } - } - - self.hostWindow?.endSheet(self.window!, returnCode: NSApplication.ModalResponse.OK) - } catch { - self.errorMessageLabel.stringValue = NSLocalizedString("Keychain error while storing credentials.", comment: "Credentials Error") - } - - case .failure: - - self.errorMessageLabel.stringValue = NSLocalizedString("Network error. Try again later.", comment: "Credentials Error") - - } - } - } - - @IBAction func createAccountWithProvider(_ sender: Any) { - NSWorkspace.shared.open(URL(string: "https://feedwrangler.net/users/new")!) - } - - // MARK: Autofill - func enableAutofill() { - if #available(macOS 11, *) { - usernameTextField.contentType = .username - passwordTextField.contentType = .password - } - } - -} diff --git a/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift b/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift index ce61cc8a7..d1a81e611 100644 --- a/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift +++ b/Mac/Preferences/Accounts/AccountsPreferencesViewController.swift @@ -168,10 +168,6 @@ extension AccountsPreferencesViewController: AccountsPreferencesAddAccountDelega let accountsFeedbinWindowController = AccountsFeedbinWindowController() accountsFeedbinWindowController.runSheetOnWindow(self.view.window!) addAccountWindowController = accountsFeedbinWindowController - case .feedWrangler: - let accountsFeedWranglerWindowController = AccountsFeedWranglerWindowController() - accountsFeedWranglerWindowController.runSheetOnWindow(self.view.window!) - addAccountWindowController = accountsFeedWranglerWindowController case .freshRSS, .inoreader, .bazQux, .theOldReader: let accountsReaderAPIWindowController = AccountsReaderAPIWindowController() accountsReaderAPIWindowController.accountType = accountType diff --git a/Mac/Resources/Assets.xcassets/Account icon colors/feedwranglerColor.colorset/Contents.json b/Mac/Resources/Assets.xcassets/Account icon colors/feedwranglerColor.colorset/Contents.json deleted file mode 100644 index bdaabb8f8..000000000 --- a/Mac/Resources/Assets.xcassets/Account icon colors/feedwranglerColor.colorset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "colors" : [ - { - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.737", - "green" : "0.569", - "red" : "0.118" - } - }, - "idiom" : "universal" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "color" : { - "color-space" : "srgb", - "components" : { - "alpha" : "1.000", - "blue" : "0.698", - "green" : "0.529", - "red" : "0.078" - } - }, - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/Contents.json b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/Contents.json deleted file mode 100644 index f7b68151c..000000000 --- a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/Contents.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "images" : [ - { - "filename" : "feedwranger-any-slice.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "feedwranger-dark-slice.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "filename" : "feedwranger-any-slice@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "feedwranger-dark-slice@2x.png", - "idiom" : "universal", - "scale" : "2x" - }, - { - "filename" : "feedwranger-any-slice@3x.png", - "idiom" : "universal", - "scale" : "3x" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "filename" : "feedwranger-dark-slice@3x.png", - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "template-rendering-intent" : "original" - } -} diff --git a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice.png b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice.png deleted file mode 100644 index a04e07f9a..000000000 Binary files a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice@2x.png b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice@2x.png deleted file mode 100644 index dd25a60ae..000000000 Binary files a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice@2x.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice@3x.png b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice@3x.png deleted file mode 100644 index 1fceca03d..000000000 Binary files a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-any-slice@3x.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice.png b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice.png deleted file mode 100644 index ff1990102..000000000 Binary files a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice@2x.png b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice@2x.png deleted file mode 100644 index e2e52edb5..000000000 Binary files a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice@2x.png and /dev/null differ diff --git a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice@3x.png b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice@3x.png deleted file mode 100644 index e1640465a..000000000 Binary files a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/feedwranger-dark-slice@3x.png and /dev/null differ diff --git a/Mac/Resources/NetNewsWire.sdef b/Mac/Resources/NetNewsWire.sdef index 2c6c0e3c6..58fa1004a 100644 --- a/Mac/Resources/NetNewsWire.sdef +++ b/Mac/Resources/NetNewsWire.sdef @@ -69,7 +69,6 @@ - diff --git a/Mac/Scriptability/Account+Scriptability.swift b/Mac/Scriptability/Account+Scriptability.swift index 7e97dd88b..895beeed8 100644 --- a/Mac/Scriptability/Account+Scriptability.swift +++ b/Mac/Scriptability/Account+Scriptability.swift @@ -164,8 +164,6 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta osType = "Fdly" case .feedbin: osType = "Fdbn" - case .feedWrangler: - osType = "FWrg" case .newsBlur: osType = "NBlr" case .freshRSS: diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 783dcd93f..dd65d016c 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -58,10 +58,6 @@ 17EF6A2225C4E5B4002C9F81 /* RSWeb in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 17EF6A2025C4E5B4002C9F81 /* RSWeb */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 27B86EEB25A53AAB00264340 /* Account in Frameworks */ = {isa = PBXBuildFile; productRef = 51BC2F4A24D343A500E90810 /* Account */; }; 3B3A32A5238B820900314204 /* FeedWranglerAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */; }; - 3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */; }; - 3B826DCC2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */; }; - 3B826DCD2385C89600FC1ADB /* AccountsFeedWrangler.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */; }; - 3B826DCE2385C89600FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */; }; 4679674625E599C100844E8D /* Articles in Frameworks */ = {isa = PBXBuildFile; productRef = 4679674525E599C100844E8D /* Articles */; }; 4679674725E599C100844E8D /* Articles in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 4679674525E599C100844E8D /* Articles */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 4679674925E599C100844E8D /* RSCore in Frameworks */ = {isa = PBXBuildFile; productRef = 4679674825E599C100844E8D /* RSCore */; }; @@ -1111,8 +1107,6 @@ 17D7586E2679C21800B17787 /* OnePasswordExtension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OnePasswordExtension.m; sourceTree = ""; }; 17E0084525941887000C23F0 /* SizeCategories.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SizeCategories.swift; sourceTree = ""; }; 3B3A328B238B820900314204 /* FeedWranglerAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerAccountViewController.swift; sourceTree = ""; }; - 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsFeedWrangler.xib; sourceTree = ""; }; - 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsFeedWranglerWindowController.swift; sourceTree = ""; }; 49F40DEF2335B71000552BF4 /* newsfoot.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = newsfoot.js; sourceTree = ""; }; 510289CC24519A1D00426DDF /* SelectComboTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectComboTableViewCell.swift; sourceTree = ""; }; 5103A9972421643300410853 /* blank.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = blank.html; sourceTree = ""; }; @@ -2532,8 +2526,6 @@ 5144EA2E2279FAB600D19003 /* AccountsDetailViewController.swift */, 5144EA50227B8E4500D19003 /* AccountsFeedbin.xib */, 5144EA4F227B8E4500D19003 /* AccountsFeedbinWindowController.swift */, - 3B826DB02385C84800FC1ADB /* AccountsFeedWrangler.xib */, - 3B826DCA2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift */, BDCB514D24282C8A00102A80 /* AccountsNewsBlur.xib */, 179DBBA2B22A659F81EED6F9 /* AccountsNewsBlurWindowController.swift */, 84C9FC7222629E1200D921D6 /* AccountsPreferencesViewController.swift */, @@ -3279,7 +3271,6 @@ 65ED4066235DEF6C0081F399 /* TimelineTableView.xib in Resources */, 65ED4067235DEF6C0081F399 /* page.html in Resources */, BDCB516824282C8A00102A80 /* AccountsNewsBlur.xib in Resources */, - 3B826DCD2385C89600FC1ADB /* AccountsFeedWrangler.xib in Resources */, 65ED4069235DEF6C0081F399 /* AccountsReaderAPI.xib in Resources */, 65ED406A235DEF6C0081F399 /* newsfoot.js in Resources */, 5103A9992421643300410853 /* blank.html in Resources */, @@ -3381,7 +3372,6 @@ 8405DDA222168920008CE1BF /* TimelineTableView.xib in Resources */, B528F81E23333C7E00E735DD /* page.html in Resources */, DFD6AACE27ADE86E00463FAD /* NewsFax.nnwtheme in Resources */, - 3B826DCB2385C84800FC1ADB /* AccountsFeedWrangler.xib in Resources */, 55E15BCB229D65A900D6602A /* AccountsReaderAPI.xib in Resources */, 49F40DF82335B71000552BF4 /* newsfoot.js in Resources */, BDCB516724282C8A00102A80 /* AccountsNewsBlur.xib in Resources */, @@ -3824,7 +3814,6 @@ 65ED3FFF235DEF6C0081F399 /* SidebarOutlineDataSource.swift in Sources */, 65ED4000235DEF6C0081F399 /* SidebarCellAppearance.swift in Sources */, 65ED4001235DEF6C0081F399 /* StarredFeedDelegate.swift in Sources */, - 3B826DCE2385C89600FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */, 65ED4002235DEF6C0081F399 /* FaviconDownloader.swift in Sources */, 65ED4003235DEF6C0081F399 /* AdvancedPreferencesViewController.swift in Sources */, 65ED4004235DEF6C0081F399 /* SharingServicePickerDelegate.swift in Sources */, @@ -4209,7 +4198,6 @@ 84CAFCAF22BC8C35007694F0 /* FetchRequestOperation.swift in Sources */, 8426119E1FCB6ED40086A189 /* HTMLMetadataDownloader.swift in Sources */, 849A976E1ED9EBC8007D329B /* TimelineViewController.swift in Sources */, - 3B826DCC2385C84800FC1ADB /* AccountsFeedWranglerWindowController.swift in Sources */, 5154368B229404D1005E1CDF /* FaviconGenerator.swift in Sources */, 5183CCE6226F4E110010922C /* RefreshInterval.swift in Sources */, 849A97771ED9EC04007D329B /* TimelineCellData.swift in Sources */, diff --git a/Secrets/Sources/Secrets/Credentials.swift b/Secrets/Sources/Secrets/Credentials.swift index 512012b1c..e7d0f0262 100644 --- a/Secrets/Sources/Secrets/Credentials.swift +++ b/Secrets/Sources/Secrets/Credentials.swift @@ -15,8 +15,6 @@ public enum CredentialsError: Error { public enum CredentialsType: String { case basic = "password" - case feedWranglerBasic = "feedWranglerBasic" - case feedWranglerToken = "feedWranglerToken" case newsBlurBasic = "newsBlurBasic" case newsBlurSessionId = "newsBlurSessionId" case readerBasic = "readerBasic" diff --git a/Secrets/Sources/Secrets/SecretsProvider.swift b/Secrets/Sources/Secrets/SecretsProvider.swift index 24fc53fee..26ed84410 100644 --- a/Secrets/Sources/Secrets/SecretsProvider.swift +++ b/Secrets/Sources/Secrets/SecretsProvider.swift @@ -8,7 +8,6 @@ import Foundation public protocol SecretsProvider { - var feedWranglerKey: String { get } var mercuryClientId: String { get } var mercuryClientSecret: String { get } var feedlyClientId: String { get } diff --git a/Shared/AccountType+Helpers.swift b/Shared/AccountType+Helpers.swift index 4b7ccb3cc..dd5d04083 100644 --- a/Shared/AccountType+Helpers.swift +++ b/Shared/AccountType+Helpers.swift @@ -38,8 +38,6 @@ extension AccountType { return NSLocalizedString("BazQux", comment: "Account name") case .cloudKit: return NSLocalizedString("iCloud", comment: "Account name") - case .feedWrangler: - return NSLocalizedString("FeedWrangler", comment: "Account name") case .feedbin: return NSLocalizedString("Feedbin", comment: "Account name") case .feedly: @@ -73,8 +71,6 @@ extension AccountType { return Image("accountBazQux") case .cloudKit: return Image("accountCloudKit") - case .feedWrangler: - return Image("accountFeedWrangler") case .feedbin: return Image("accountFeedbin") case .feedly: diff --git a/Shared/Secrets.swift.gyb b/Shared/Secrets.swift.gyb index 819bcac95..aa17c57e7 100644 --- a/Shared/Secrets.swift.gyb +++ b/Shared/Secrets.swift.gyb @@ -2,7 +2,7 @@ %{ import os -secrets = ['FEED_WRANGLER_KEY', 'MERCURY_CLIENT_ID', 'MERCURY_CLIENT_SECRET', 'FEEDLY_CLIENT_ID', 'FEEDLY_CLIENT_SECRET', 'INOREADER_APP_ID', 'INOREADER_APP_KEY'] +secrets = ['MERCURY_CLIENT_ID', 'MERCURY_CLIENT_SECRET', 'FEEDLY_CLIENT_ID', 'FEEDLY_CLIENT_SECRET', 'INOREADER_APP_ID', 'INOREADER_APP_KEY'] def chunks(seq, size): return (seq[i:(i + size)] for i in range(0, len(seq), size)) diff --git a/iOS/Account/Account.storyboard b/iOS/Account/Account.storyboard index 9f9b4def0..0b702fad4 100644 --- a/iOS/Account/Account.storyboard +++ b/iOS/Account/Account.storyboard @@ -1,9 +1,8 @@ - + - - + @@ -14,7 +13,7 @@ - + @@ -30,7 +29,7 @@ - + @@ -41,187 +40,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -282,7 +100,7 @@ Don’t have a Feed Wrangler account?