diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index d0f787da3..c2a3b76b9 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -226,6 +226,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, self.delegate = ReaderAPIAccountDelegate(dataFolder: dataFolder, transport: transport) case .feedly: self.delegate = FeedlyAccountDelegate(dataFolder: dataFolder, transport: transport) + case .feedWrangler: + self.delegate = FeedWranglerAccountDelegate(dataFolder: dataFolder, transport: transport) default: return nil } @@ -302,6 +304,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, FeedbinAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion) case .freshRSS: ReaderAPIAccountDelegate.validateCredentials(transport: transport, credentials: credentials, endpoint: endpoint, completion: completion) + case .feedWrangler: + FeedWranglerAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion) default: break } diff --git a/Frameworks/Account/Account.xcodeproj/project.pbxproj b/Frameworks/Account/Account.xcodeproj/project.pbxproj index a27cac954..12b3b737f 100644 --- a/Frameworks/Account/Account.xcodeproj/project.pbxproj +++ b/Frameworks/Account/Account.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 3B29BC6E233EA83C002A346D /* FeedWranglerAPICaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B29BC6C233EA83C002A346D /* FeedWranglerAPICaller.swift */; }; + 3B29BC6F233EA83C002A346D /* FeedWranglerAccountDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B29BC6D233EA83C002A346D /* FeedWranglerAccountDelegate.swift */; }; + 3B90F51F233F0EF800D481CC /* FeedWranglerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B90F51E233F0EF800D481CC /* FeedWranglerConfig.swift */; }; 5107A099227DE42E00C7C3C5 /* AccountCredentialsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5107A098227DE42E00C7C3C5 /* AccountCredentialsTest.swift */; }; 5107A09B227DE49500C7C3C5 /* TestAccountManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5107A09A227DE49500C7C3C5 /* TestAccountManager.swift */; }; 5107A09D227DE77700C7C3C5 /* TestTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5107A09C227DE77700C7C3C5 /* TestTransport.swift */; }; @@ -144,6 +147,9 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 3B29BC6C233EA83C002A346D /* FeedWranglerAPICaller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerAPICaller.swift; sourceTree = ""; }; + 3B29BC6D233EA83C002A346D /* FeedWranglerAccountDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedWranglerAccountDelegate.swift; sourceTree = ""; }; + 3B90F51E233F0EF800D481CC /* FeedWranglerConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedWranglerConfig.swift; sourceTree = ""; }; 5107A098227DE42E00C7C3C5 /* AccountCredentialsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountCredentialsTest.swift; sourceTree = ""; }; 5107A09A227DE49500C7C3C5 /* TestAccountManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestAccountManager.swift; sourceTree = ""; }; 5107A09C227DE77700C7C3C5 /* TestTransport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestTransport.swift; sourceTree = ""; }; @@ -268,6 +274,16 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3B29BC6B233EA83C002A346D /* FeedWrangler */ = { + isa = PBXGroup; + children = ( + 3B29BC6C233EA83C002A346D /* FeedWranglerAPICaller.swift */, + 3B29BC6D233EA83C002A346D /* FeedWranglerAccountDelegate.swift */, + 3B90F51E233F0EF800D481CC /* FeedWranglerConfig.swift */, + ); + path = FeedWrangler; + sourceTree = ""; + }; 515E4EB12324FF7D0057B0E7 /* Credentials */ = { isa = PBXGroup; children = ( @@ -405,6 +421,7 @@ 84245C7D1FDDD2580074AFBB /* Feedbin */, 552032EA229D5D5A009559E0 /* ReaderAPI */, 9EA31339231E368100268BA0 /* Feedly */, + 3B29BC6B233EA83C002A346D /* FeedWrangler */, 848935031F62484F00CEBD24 /* AccountTests */, 848934F71F62484F00CEBD24 /* Products */, 8469F80F1F6DC3C10084783E /* Frameworks */, @@ -665,14 +682,17 @@ 84C8B3F41F89DE430053CCA6 /* DataExtensions.swift in Sources */, 552032F9229D5D5A009559E0 /* ReaderAPISubscription.swift in Sources */, 84C3654A1F899F3B001EC85C /* CombinedRefreshProgress.swift in Sources */, + 3B29BC6F233EA83C002A346D /* FeedWranglerAccountDelegate.swift in Sources */, 9EC688EE232C58E800A8D0A2 /* OAuthAuthorizationCodeGranting.swift in Sources */, 9EC688EC232C583300A8D0A2 /* FeedlyAccountDelegate+OAuth.swift in Sources */, 8469F81C1F6DD15E0084783E /* Account.swift in Sources */, 9EAEC60E2332FEC20085D7C9 /* FeedlyFeed.swift in Sources */, 5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */, 9EA3133B231E368100268BA0 /* FeedlyAccountDelegate.swift in Sources */, + 3B29BC6E233EA83C002A346D /* FeedWranglerAPICaller.swift in Sources */, 51E5959B228C781500FCC42B /* FeedbinStarredEntry.swift in Sources */, 9E1D155923343B2A00F4944C /* FeedlyGetStreamParsedItemsOperation.swift in Sources */, + 3B90F51F233F0EF800D481CC /* FeedWranglerConfig.swift in Sources */, 846E77451F6EF9B900A165E2 /* Container.swift in Sources */, 9E1D15532334304B00F4944C /* FeedlyGetCollectionStreamOperation.swift in Sources */, 9E12B0202334696A00ADE5A0 /* FeedlyCreateFeedsForCollectionFoldersOperation.swift in Sources */, diff --git a/Frameworks/Account/Credentials/Credentials.swift b/Frameworks/Account/Credentials/Credentials.swift index 22a99e8a3..3a6cf06bd 100644 --- a/Frameworks/Account/Credentials/Credentials.swift +++ b/Frameworks/Account/Credentials/Credentials.swift @@ -15,6 +15,8 @@ public enum CredentialsError: Error { public enum CredentialsType: String { case basic = "password" + case feedWranglerBasic = "feedWranglerBasic" + case feedWranglerToken = "feedWranglerToken" case readerBasic = "readerBasic" case readerAPIKey = "readerAPIKey" case oauthAccessToken = "oauthAccessToken" diff --git a/Frameworks/Account/Credentials/URLRequest+RSWeb.swift b/Frameworks/Account/Credentials/URLRequest+RSWeb.swift index 4374f285b..da7b04a49 100755 --- a/Frameworks/Account/Credentials/URLRequest+RSWeb.swift +++ b/Frameworks/Account/Credentials/URLRequest+RSWeb.swift @@ -25,6 +25,19 @@ public extension URLRequest { let base64 = data?.base64EncodedString() let auth = "Basic \(base64 ?? "")" setValue(auth, forHTTPHeaderField: HTTPRequestHeader.authorization) + case .feedWranglerBasic: + + guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { + return + } + components.queryItems = [ + URLQueryItem(name: "email", value: credentials.username), + URLQueryItem(name: "password", value: credentials.secret), + URLQueryItem(name: "client_key", value: FeedWranglerConfig.clientKey) + ] + self.url = components.url + case .feedWranglerToken: + fatalError() // TODO: implement case .readerBasic: setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") httpMethod = "POST" diff --git a/Frameworks/Account/FeedWrangler/FeedWranglerAPICaller.swift b/Frameworks/Account/FeedWrangler/FeedWranglerAPICaller.swift new file mode 100644 index 000000000..110ec5d8b --- /dev/null +++ b/Frameworks/Account/FeedWrangler/FeedWranglerAPICaller.swift @@ -0,0 +1,59 @@ +// +// FeedWranglerAPICaller.swift +// Account +// +// Created by Jonathan Bennett on 2019-08-29. +// Copyright © 2019 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +import Foundation +import RSWeb + +final class FeedWranglerAPICaller: NSObject { + + private var transport: Transport! + + var credentials: Credentials? + weak var accountMetadata: AccountMetadata? + + init(transport: Transport) { + super.init() + self.transport = transport + } + + func validateCredentials(completion: @escaping (Result) -> Void) { + let callURL = FeedWranglerConfig.clientURL.appendingPathComponent("users/authorize") + let request = URLRequest(url: callURL, credentials: credentials) + let username = self.credentials?.username ?? "" + + transport.send(request: request) { result in + switch result { + case .success(let (_, data)): + guard let data = data else { + completion(.success(nil)) + return + } + + do { + if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] { + if let accessToken = json["access_token"] as? String { + let authCredentials = Credentials(type: .feedWranglerToken, username: username, secret: accessToken) + completion(.success(authCredentials)) + return + } + } + + completion(.success(nil)) + } catch let error { + completion(.failure(error)) + } + case .failure(let error): + completion(.failure(error)) + } + + } + } + +} diff --git a/Frameworks/Account/FeedWrangler/FeedWranglerAccountDelegate.swift b/Frameworks/Account/FeedWrangler/FeedWranglerAccountDelegate.swift new file mode 100644 index 000000000..5829fcc3f --- /dev/null +++ b/Frameworks/Account/FeedWrangler/FeedWranglerAccountDelegate.swift @@ -0,0 +1,156 @@ +// +// 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 + +final class FeedWranglerAccountDelegate: AccountDelegate { + + var behaviors: AccountBehaviors = [] + + var isOPMLImportInProgress = false + var server: String? = FeedWranglerConfig.clientPath + var 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("/Sync.sqlite")) + } + + func refreshAll(for account: Account, completion: @escaping (Result) -> Void) { + refreshProgress.addToNumberOfTasksAndRemaining(6) + + self.sendArticleStatus(for: account) { + self.refreshArticleStatus(for: account) { + self.refreshArticles(for: account) { + self.refreshMissingArticles(for: account) { + self.refreshProgress.clear() + DispatchQueue.main.async { + completion(.success(())) + } + } + } + } + } + } + + func refreshArticles(for account: Account, completion: @escaping (() -> Void)) { + os_log(.debug, log: log, "Refreshing articles...") + } + + func refreshMissingArticles(for account: Account, completion: @escaping (() -> Void)) { + os_log(.debug, log: log, "Refreshing missing articles...") + completion() + } + + func sendArticleStatus(for account: Account, completion: @escaping (() -> Void)) { + os_log(.debug, log: log, "Sending article status...") + completion() + } + + func refreshArticleStatus(for account: Account, completion: @escaping (() -> Void)) { + os_log(.debug, log: log, "Refreshing article status...") + completion() + } + + func importOPML(for account: Account, opmlFile: URL, completion: @escaping (Result) -> Void) { + fatalError() + } + + func addFolder(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 createFeed(for account: Account, url: String, name: String?, container: Container, completion: @escaping (Result) -> Void) { + fatalError() + } + + func renameFeed(for account: Account, with feed: Feed, to name: String, completion: @escaping (Result) -> Void) { + fatalError() + } + + func addFeed(for account: Account, with: Feed, to container: Container, completion: @escaping (Result) -> Void) { + fatalError() + } + + func removeFeed(for account: Account, with feed: Feed, from container: Container, completion: @escaping (Result) -> Void) { + fatalError() + } + + func moveFeed(for account: Account, with feed: Feed, from: Container, to: Container, completion: @escaping (Result) -> Void) { + fatalError() + } + + func restoreFeed(for account: Account, feed: Feed, container: Container, completion: @escaping (Result) -> Void) { + fatalError() + } + + func restoreFolder(for account: Account, folder: Folder, completion: @escaping (Result) -> Void) { + fatalError() + } + + func markArticles(for account: Account, articles: Set
, statusKey: ArticleStatus.Key, flag: Bool) -> Set
? { + fatalError() + } + + 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: Private +private extension FeedWranglerAccountDelegate { + +} diff --git a/Frameworks/Account/FeedWrangler/FeedWranglerConfig.swift b/Frameworks/Account/FeedWrangler/FeedWranglerConfig.swift new file mode 100644 index 000000000..6e594b80f --- /dev/null +++ b/Frameworks/Account/FeedWrangler/FeedWranglerConfig.swift @@ -0,0 +1,17 @@ +// +// FeedWranglerConfig.swift +// NetNewsWire +// +// Created by Jonathan Bennett on 9/27/19. +// Copyright © 2019 Ranchero Software, LLC. All rights reserved. +// + +import Foundation + +enum FeedWranglerConfig { + static let clientKey = "{FEEDWRANGLERKEY}" + static let clientPath = "https://feedwrangler.net/api/v2/" + static let clientURL = { + URL(string: FeedWranglerConfig.clientPath)! + }() +} diff --git a/Mac/AppAssets.swift b/Mac/AppAssets.swift index b67f10559..3eec27951 100644 --- a/Mac/AppAssets.swift +++ b/Mac/AppAssets.swift @@ -38,6 +38,10 @@ struct AppAssets { return RSImage(named: "accountFeedly") }() + static var accountFeedWrangler: RSImage! = { + return RSImage(named: "accountFeedWrangler") + }() + static var accountFreshRSS: RSImage! = { return RSImage(named: "accountFreshRSS") }() diff --git a/Mac/Preferences/Accounts/AccountsAddViewController.swift b/Mac/Preferences/Accounts/AccountsAddViewController.swift index 02b87bd38..dababea22 100644 --- a/Mac/Preferences/Accounts/AccountsAddViewController.swift +++ b/Mac/Preferences/Accounts/AccountsAddViewController.swift @@ -15,7 +15,7 @@ class AccountsAddViewController: NSViewController { private var accountsAddWindowController: NSWindowController? - private let addableAccountTypes: [AccountType] = [.onMyMac, .feedbin, .feedly, .freshRSS] + private let addableAccountTypes: [AccountType] = [.onMyMac, .feedbin, .feedly, .feedWrangler, .freshRSS] init() { super.init(nibName: "AccountsAdd", bundle: nil) @@ -65,6 +65,9 @@ extension AccountsAddViewController: NSTableViewDelegate { case .feedbin: cell.accountNameLabel?.stringValue = NSLocalizedString("Feedbin", comment: "Feedbin") cell.accountImageView?.image = AppAssets.accountFeedbin + case .feedWrangler: + cell.accountNameLabel?.stringValue = NSLocalizedString("Feed Wrangler", comment: "Feed Wrangler") + cell.accountImageView?.image = AppAssets.accountFeedWrangler case .freshRSS: cell.accountNameLabel?.stringValue = NSLocalizedString("FreshRSS", comment: "FreshRSS") cell.accountImageView?.image = AppAssets.accountFreshRSS @@ -95,6 +98,10 @@ extension AccountsAddViewController: NSTableViewDelegate { let accountsFeedbinWindowController = AccountsFeedbinWindowController() accountsFeedbinWindowController.runSheetOnWindow(self.view.window!) accountsAddWindowController = accountsFeedbinWindowController + case .feedWrangler: + let accountsFeedWranglerWindowController = AccountsFeedWranglerWindowController() + accountsFeedWranglerWindowController.runSheetOnWindow(self.view.window!) + accountsAddWindowController = accountsFeedWranglerWindowController case .freshRSS: let accountsReaderAPIWindowController = AccountsReaderAPIWindowController() accountsReaderAPIWindowController.accountType = .freshRSS diff --git a/Mac/Preferences/Accounts/AccountsFeedWrangler.xib b/Mac/Preferences/Accounts/AccountsFeedWrangler.xib new file mode 100644 index 000000000..684cdb225 --- /dev/null +++ b/Mac/Preferences/Accounts/AccountsFeedWrangler.xib @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NSAllRomanInputSourcesLocaleIdentifier + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mac/Preferences/Accounts/AccountsFeedWranglerWindowController.swift b/Mac/Preferences/Accounts/AccountsFeedWranglerWindowController.swift new file mode 100644 index 000000000..c4b4f6e1e --- /dev/null +++ b/Mac/Preferences/Accounts/AccountsFeedWranglerWindowController.swift @@ -0,0 +1,110 @@ +// +// AccountsFeedWranglerWindowController.swift +// NetNewsWire +// +// Created by Jonathan Bennett on 2019-08-29. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import AppKit +import Account +import RSWeb + +class AccountsFeedWranglerWindowController: NSWindowController { + @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") + } else { + actionButton.title = NSLocalizedString("Create", comment: "Create") + } + } + + // MARK: API + + func runSheetOnWindow(_ hostWindow: NSWindow, completionHandler handler: ((NSApplication.ModalResponse) -> Void)? = nil) { + self.hostWindow = hostWindow + hostWindow.beginSheet(window!, completionHandler: handler) + } + + // 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 + } + + 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 + } + var newAccount = false + if self.account == nil { + self.account = AccountManager.shared.createAccount(type: .feedWrangler) + newAccount = true + } + + do { + try self.account?.removeCredentials(type: .feedWranglerBasic) + try self.account?.removeCredentials(type: .feedWranglerToken) + try self.account?.storeCredentials(credentials) + try self.account?.storeCredentials(validatedCredentials) + if newAccount { + 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") + + } + } + } +} diff --git a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/Contents.json b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/Contents.json new file mode 100644 index 000000000..b2c62a136 --- /dev/null +++ b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "accountFreshRSS.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/accountFreshRSS.pdf b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/accountFreshRSS.pdf new file mode 100644 index 000000000..1ff98e115 Binary files /dev/null and b/Mac/Resources/Assets.xcassets/accountFeedWrangler.imageset/accountFreshRSS.pdf differ diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index 8bdb83bab..cec9860f8 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 3B29BC69233EA81F002A346D /* AccountsFeedWranglerWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B29BC5F233EA81F002A346D /* AccountsFeedWranglerWindowController.swift */; }; + 3B29BC6A233EA81F002A346D /* AccountsFeedWrangler.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3B29BC68233EA81F002A346D /* AccountsFeedWrangler.xib */; }; 49F40DF82335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; }; 49F40DF92335B71000552BF4 /* newsfoot.js in Resources */ = {isa = PBXBuildFile; fileRef = 49F40DEF2335B71000552BF4 /* newsfoot.js */; }; 510BD15D232D765D002692E4 /* SettingsReaderAPIAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557EE1A522B6F4E1004206FA /* SettingsReaderAPIAccountView.swift */; }; @@ -777,6 +779,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 3B29BC5F233EA81F002A346D /* AccountsFeedWranglerWindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AccountsFeedWranglerWindowController.swift; sourceTree = ""; }; + 3B29BC68233EA81F002A346D /* AccountsFeedWrangler.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AccountsFeedWrangler.xib; sourceTree = ""; }; 49F40DEF2335B71000552BF4 /* newsfoot.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = newsfoot.js; sourceTree = ""; }; 510D707322B028E1004E8F65 /* SettingsAddAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAddAccountView.swift; sourceTree = ""; }; 510D707D22B02A4B004E8F65 /* SettingsLocalAccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLocalAccountView.swift; sourceTree = ""; }; @@ -1930,6 +1934,8 @@ 5144EA4F227B8E4500D19003 /* AccountsFeedbinWindowController.swift */, 9EA33BB82318F8C10097B644 /* AccountsFeedlyWeb.xib */, 9EA33BB72318F8C10097B644 /* AccountsFeedlyWebWindowController.swift */, + 3B29BC68233EA81F002A346D /* AccountsFeedWrangler.xib */, + 3B29BC5F233EA81F002A346D /* AccountsFeedWranglerWindowController.swift */, 55E15BC1229D65A900D6602A /* AccountsReaderAPI.xib */, 55E15BCA229D65A900D6602A /* AccountsReaderAPIWindowController.swift */, 5144EA352279FC3D00D19003 /* AccountsAddLocal.xib */, @@ -2266,16 +2272,16 @@ TargetAttributes = { 513C5CE5232571C2003D4054 = { CreatedOnToolsVersion = 11.0; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = 8EQFQ9RY84; ProvisioningStyle = Automatic; }; 6581C73220CED60000F4AD34 = { - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = 8EQFQ9RY84; ProvisioningStyle = Automatic; }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = 8EQFQ9RY84; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { @@ -2285,7 +2291,7 @@ }; 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = 8EQFQ9RY84; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.HardenedRuntime = { @@ -2295,7 +2301,7 @@ }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = SHJK2V3AJG; + DevelopmentTeam = 8EQFQ9RY84; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; @@ -2588,6 +2594,7 @@ 84C9FC8E22629E8F00D921D6 /* Credits.rtf in Resources */, 84BBB12D20142A4700F054F5 /* Inspector.storyboard in Resources */, 848363022262A3BD00DA1D35 /* AddFeedSheet.xib in Resources */, + 3B29BC6A233EA81F002A346D /* AccountsFeedWrangler.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2897,6 +2904,7 @@ 847CD6CA232F4CBF00FAC46D /* TimelineAvatarView.swift in Sources */, 51FA73AA2332C2FD0090D516 /* ArticleExtractorConfig.swift in Sources */, 84BBB12E20142A4700F054F5 /* InspectorWindowController.swift in Sources */, + 3B29BC69233EA81F002A346D /* AccountsFeedWranglerWindowController.swift in Sources */, 51EF0F7A22771B890050506E /* ColorHash.swift in Sources */, 84E46C7D1F75EF7B005ECFB3 /* AppDefaults.swift in Sources */, D5907D972004B7EB005947E5 /* Account+Scriptability.swift in Sources */,