Merge remote-tracking branch 'upstream/master'

# Conflicts:
#	iOS/AppAssets.swift
This commit is contained in:
Stuart Breckenridge 2019-06-21 15:27:44 +08:00
commit 7d226c46a4
25 changed files with 181 additions and 138 deletions

View File

@ -33,7 +33,7 @@ public enum AccountType: Int {
case feedbin = 17
case feedWrangler = 18
case newsBlur = 19
case readerAPI = 20
case freshRSS = 20
// TODO: more
}
@ -221,7 +221,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
self.delegate = LocalAccountDelegate()
case .feedbin:
self.delegate = FeedbinAccountDelegate(dataFolder: dataFolder, transport: transport)
case .readerAPI:
case .freshRSS:
self.delegate = ReaderAPIAccountDelegate(dataFolder: dataFolder, transport: transport)
default:
fatalError("Only Local and Feedbin accounts are supported")
@ -250,8 +250,8 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
defaultName = "FeedWrangler"
case .newsBlur:
defaultName = "NewsBlur"
case .readerAPI:
defaultName = "Reader API"
case .freshRSS:
defaultName = "FreshRSS"
}
NotificationCenter.default.addObserver(self, selector: #selector(downloadProgressDidChange(_:)), name: .DownloadProgressDidChange, object: nil)
@ -295,34 +295,40 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
}
public func retrieveBasicCredentials() throws -> Credentials? {
guard let username = self.username, let server = delegate.server else {
public func retrieveCredentials() throws -> Credentials? {
switch type {
case .feedbin:
guard let username = self.username, let server = delegate.server else {
return nil
}
return try CredentialsManager.retrieveBasicCredentials(server: server, username: username)
case .freshRSS:
guard let username = self.username, let server = delegate.server else {
return nil
}
return try CredentialsManager.retrieveReaderAPIAuthCredentials(server: server, username: username)
default:
return nil
}
return try CredentialsManager.retrieveBasicCredentials(server: server, username: username)
}
public func removeBasicCredentials() throws {
guard let username = self.username, let server = delegate.server else {
return
public func removeCredentials() throws {
switch type {
case .feedbin:
guard let username = self.username, let server = delegate.server else {
return
}
try CredentialsManager.removeBasicCredentials(server: server, username: username)
self.username = nil
case .freshRSS:
guard let username = self.username, let server = delegate.server else {
return
}
try CredentialsManager.removeReaderAPIAuthCredentials(server: server, username: username)
self.username = nil
default:
break
}
try CredentialsManager.removeBasicCredentials(server: server, username: username)
self.username = nil
}
public func retrieveReaderAPIAuthCredentials() throws -> Credentials? {
guard let username = self.username, let server = delegate.server else {
return nil
}
return try CredentialsManager.retrieveReaderAPIAuthCredentials(server: server, username: username)
}
public func removeReaderAPIAuthCredentials() throws {
guard let username = self.username, let server = delegate.server else {
return
}
try CredentialsManager.removeReaderAPIAuthCredentials(server: server, username: username)
self.username = nil
}
public static func validateCredentials(transport: Transport = URLSession.webserviceTransport(), type: AccountType, credentials: Credentials, endpoint: URL? = nil, completion: @escaping (Result<Credentials?, Error>) -> Void) {
@ -331,7 +337,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
LocalAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion)
case .feedbin:
FeedbinAccountDelegate.validateCredentials(transport: transport, credentials: credentials, completion: completion)
case .readerAPI:
case .freshRSS:
ReaderAPIAccountDelegate.validateCredentials(transport: transport, credentials: credentials, endpoint: endpoint, completion: completion)
default:
break

View File

@ -55,7 +55,7 @@ final class FeedbinAPICaller: NSObject {
switch error {
case TransportError.httpError(let status):
if status == 401 {
completion(.success(self.credentials))
completion(.success(nil))
} else {
completion(.failure(error))
}

View File

@ -510,7 +510,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
}
func accountDidInitialize(_ account: Account) {
credentials = try? account.retrieveBasicCredentials()
credentials = try? account.retrieveCredentials()
accountMetadata = account.metadata
}

View File

@ -411,7 +411,7 @@ final class ReaderAPIAccountDelegate: AccountDelegate {
func accountDidInitialize(_ account: Account) {
accountMetadata = account.metadata
credentials = try? account.retrieveReaderAPIAuthCredentials()
credentials = try? account.retrieveCredentials()
}
static func validateCredentials(transport: Transport, credentials: Credentials, endpoint: URL?, completion: @escaping (Result<Credentials?, Error>) -> Void) {

View File

@ -34,8 +34,8 @@ struct AppAssets {
return RSImage(named: "accountFeedbin")
}()
static var accountReader: RSImage! = {
return RSImage(named: "accountReader")
static var accountFreshRSS: RSImage! = {
return RSImage(named: "accountFreshRSS")
}()
static var faviconTemplateImage: RSImage = {

View File

@ -64,8 +64,8 @@ extension AccountsAddViewController: NSTableViewDelegate {
cell.accountNameLabel?.stringValue = NSLocalizedString("Feedbin", comment: "Feedbin")
cell.accountImageView?.image = AppAssets.accountFeedbin
case 2:
cell.accountNameLabel?.stringValue = NSLocalizedString("Reader API", comment: "Reader API")
cell.accountImageView?.image = AppAssets.accountReader
cell.accountNameLabel?.stringValue = NSLocalizedString("FreshRSS", comment: "FreshRSS")
cell.accountImageView?.image = AppAssets.accountFreshRSS
default:
break
}
@ -92,6 +92,7 @@ extension AccountsAddViewController: NSTableViewDelegate {
accountsAddWindowController = accountsFeedbinWindowController
case 2:
let accountsReaderAPIWindowController = AccountsReaderAPIWindowController()
accountsReaderAPIWindowController.accountType = .freshRSS
accountsReaderAPIWindowController.runSheetOnWindow(self.view.window!)
accountsAddWindowController = accountsReaderAPIWindowController
default:

View File

@ -27,10 +27,9 @@ class AccountsFeedbinWindowController: NSWindowController {
}
override func windowDidLoad() {
if let account = account, let credentials = try? account.retrieveBasicCredentials() {
if case .basic(let username, let password) = credentials {
if let account = account, let credentials = try? account.retrieveCredentials() {
if case .basic(let username, _) = credentials {
usernameTextField.stringValue = username
passwordTextField.stringValue = password
}
actionButton.title = NSLocalizedString("Update", comment: "Update")
} else {
@ -87,7 +86,7 @@ class AccountsFeedbinWindowController: NSWindowController {
}
do {
try self.account?.removeBasicCredentials()
try self.account?.removeCredentials()
try self.account?.storeCredentials(validatedCredentials)
if newAccount {
self.account?.refreshAll() { result in

View File

@ -107,8 +107,8 @@ extension AccountsPreferencesViewController: NSTableViewDelegate {
cell.imageView?.image = AppAssets.accountLocal
case .feedbin:
cell.imageView?.image = NSImage(named: "accountFeedbin")
case .readerAPI:
cell.imageView?.image = AppAssets.accountReader
case .freshRSS:
cell.imageView?.image = AppAssets.accountFreshRSS
default:
break
}

View File

@ -13,6 +13,8 @@
<outlet property="errorMessageLabel" destination="byK-Sd-r7F" id="8zt-9d-dWQ"/>
<outlet property="passwordTextField" destination="JSa-LY-zNQ" id="E9W-0F-69m"/>
<outlet property="progressIndicator" destination="B0W-bh-Evv" id="Tiq-gx-s3F"/>
<outlet property="titleImageView" destination="Ssh-Dh-xbg" id="8Iy-jf-EYR"/>
<outlet property="titleLabel" destination="lti-yM-8LV" id="AP0-ds-6tS"/>
<outlet property="usernameTextField" destination="78p-Cf-f55" id="RWd-0q-oAL"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
@ -37,7 +39,7 @@
<constraint firstAttribute="height" constant="36" id="Ern-Kk-8LX"/>
<constraint firstAttribute="width" constant="36" id="PLS-68-NMc"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="accountReader" id="y38-YL-woC"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="y38-YL-woC"/>
</imageView>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="lti-yM-8LV">
<rect key="frame" x="53" y="0.0" width="156" height="38"/>
@ -205,7 +207,4 @@ Gw
<point key="canvasLocation" x="116.5" y="136.5"/>
</window>
</objects>
<resources>
<image name="accountReader" width="73" height="73"/>
</resources>
</document>

View File

@ -12,6 +12,9 @@ import RSWeb
class AccountsReaderAPIWindowController: NSWindowController {
@IBOutlet weak var titleImageView: NSImageView!
@IBOutlet weak var titleLabel: NSTextField!
@IBOutlet weak var progressIndicator: NSProgressIndicator!
@IBOutlet weak var usernameTextField: NSTextField!
@IBOutlet weak var apiURLTextField: NSTextField!
@ -20,6 +23,7 @@ class AccountsReaderAPIWindowController: NSWindowController {
@IBOutlet weak var actionButton: NSButton!
var account: Account?
var accountType: AccountType?
private weak var hostWindow: NSWindow?
@ -28,10 +32,19 @@ class AccountsReaderAPIWindowController: NSWindowController {
}
override func windowDidLoad() {
if let account = account, let credentials = try? account.retrieveBasicCredentials() {
if case .basic(let username, let password) = credentials {
if let accountType = accountType {
switch accountType {
case .freshRSS:
titleImageView.image = AppAssets.accountFreshRSS
titleLabel.stringValue = NSLocalizedString("FreshRSS", comment: "FreshRSS")
default:
break
}
}
if let account = account, let credentials = try? account.retrieveCredentials() {
if case .basic(let username, _) = credentials {
usernameTextField.stringValue = username
passwordTextField.stringValue = password
}
actionButton.title = NSLocalizedString("Update", comment: "Update")
} else {
@ -71,7 +84,7 @@ class AccountsReaderAPIWindowController: NSWindowController {
}
let credentials = Credentials.readerAPIBasicLogin(username: usernameTextField.stringValue, password: passwordTextField.stringValue)
Account.validateCredentials(type: .readerAPI, credentials: credentials, endpoint: apiURL) { [weak self] result in
Account.validateCredentials(type: accountType!, credentials: credentials, endpoint: apiURL) { [weak self] result in
guard let self = self else { return }
@ -89,14 +102,14 @@ class AccountsReaderAPIWindowController: NSWindowController {
var newAccount = false
if self.account == nil {
self.account = AccountManager.shared.createAccount(type: .readerAPI)
self.account = AccountManager.shared.createAccount(type: self.accountType!)
newAccount = true
}
do {
self.account?.endpointURL = apiURL
try self.account?.removeReaderAPIAuthCredentials()
try self.account?.removeCredentials()
try self.account?.storeCredentials(validatedCredentials)
if newAccount {

View File

@ -2,7 +2,7 @@
"images" : [
{
"idiom" : "universal",
"filename" : "accountReader.pdf"
"filename" : "accountFreshRSS.pdf"
}
],
"info" : {

View File

@ -142,8 +142,8 @@ class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
osType = "FWrg"
case .newsBlur:
osType = "NBlr"
case .readerAPI:
osType = "Grdr"
case .freshRSS:
osType = "Frsh"
}
return osType.fourCharCode()
}

View File

@ -51,7 +51,7 @@ private extension RSImage {
let rect = CGRect(x: 0, y: 0, width: avatarSize, height: avatarSize)
UIGraphicsBeginImageContext(rect.size)
if let context = UIGraphicsGetCurrentContext() {
context.setFillColor(AppAssets.avatarLightBackgroundColor.cgColor)
context.setFillColor(AppAssets.avatarBackgroundColor.cgColor)
context.fill(rect)
context.translateBy(x: 0.0, y: CGFloat(integerLiteral: avatarSize));
context.scaleBy(x: 1.0, y: -1.0)

View File

@ -10,34 +10,30 @@ import RSCore
struct AppAssets {
static var avatarDarkBackgroundColor: UIColor {
return UIColor(named: "avatarDarkBackgroundColor")!
}
static var avatarBackgroundColor: UIColor = {
return UIColor(named: "avatarBackgroundColor")!
}()
static var avatarLightBackgroundColor: UIColor {
return UIColor(named: "avatarLightBackgroundColor")!
}
static var circleClosedImage: UIImage = {
return UIImage(systemName: "circle.fill")!
static var circleClosedImage: RSImage = {
return RSImage(named: "circleClosedImage")!
}()
static var circleOpenImage: UIImage = {
return UIImage(systemName: "circle")!
static var circleOpenImage: RSImage = {
return RSImage(named: "circleOpenImage")!
}()
static var chevronDisclosureColor: UIColor = {
return UIColor(named: "chevronDisclosureColor")!
}()
static var chevronDownImage: UIImage = {
let image = UIImage(systemName: "chevron.down")!
return image.withTintColor(AppAssets.chevronDisclosureColor, renderingMode: .alwaysOriginal)
static var chevronDownImage: RSImage = {
let image = RSImage(named: "chevronDownImage")!
return image.maskWithColor(color: AppAssets.chevronDisclosureColor.cgColor)!
}()
static var chevronRightImage: UIImage = {
let image = UIImage(systemName: "chevron.right")!
return image.withTintColor(AppAssets.chevronDisclosureColor, renderingMode: .alwaysOriginal)
static var chevronRightImage: RSImage = {
let image = RSImage(named: "chevronRightImage")!
return image.maskWithColor(color: AppAssets.chevronDisclosureColor.cgColor)!
}()
static var faviconTemplateImage: RSImage = {
@ -52,9 +48,9 @@ struct AppAssets {
return UIColor(named: "masterFolderColor")!
}()
static var masterFolderImage: UIImage = {
let image = UIImage(systemName: "folder.fill")!
return image.withTintColor(AppAssets.masterFolderColor, renderingMode: .alwaysOriginal)
static var masterFolderImage: RSImage = {
let image = RSImage(systemName: "folder.fill")!
return image.maskWithColor(color: AppAssets.masterFolderColor.cgColor)!
}()
static var netNewsWireBlueColor: UIColor = {
@ -65,26 +61,26 @@ struct AppAssets {
return UIColor(named: "smartFeedColor")!
}()
static var smartFeedImage: UIImage = {
let image = UIImage(systemName: "gear")!
return image.withTintColor(AppAssets.smartFeedColor, renderingMode: .alwaysOriginal)
static var smartFeedImage: RSImage = {
let image = RSImage(named: "smartFeedImage")!
return image.maskWithColor(color: AppAssets.smartFeedColor.cgColor)!
}()
static var starColor: UIColor = {
return UIColor(named: "starColor")!
}()
static var starClosedImage: UIImage = {
return UIImage(systemName: "star.fill")!
static var starClosedImage: RSImage = {
return RSImage(named: "starClosedImage")!
}()
static var starOpenImage: UIImage = {
return UIImage(systemName: "star")!
static var starOpenImage: RSImage = {
return RSImage(named: "starOpenImage")!
}()
static var timelineStarImage: UIImage = {
let image = UIImage(systemName: "star.fill")!
return image.withTintColor(AppAssets.starColor, renderingMode: .alwaysOriginal)
static var timelineStarImage: RSImage = {
let image = RSImage(named: "starClosedImage")!
return image.maskWithColor(color: AppAssets.starColor.cgColor)!
}()
static var timelineUnreadCircleColor: UIColor = {

View File

@ -73,6 +73,19 @@ class MasterTimelineViewController: ProgressTableViewController, UndoableCommand
}
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.userInterfaceStyle != previousTraitCollection?.userInterfaceStyle {
appDelegate.authorAvatarDownloader.resetCache()
appDelegate.feedIconDownloader.resetCache()
appDelegate.faviconDownloader.resetCache()
performBlockAndRestoreSelection {
tableView.reloadData()
}
}
}
// MARK Actions
@IBAction func markAllAsRead(_ sender: Any) {

View File

@ -24,7 +24,7 @@ public final class NavigationProgressView: UIView {
internal let bar = UIView()
@objc public dynamic var progressTintColor: UIColor? = UIColor(red: 0, green: 122/255, blue: 1, alpha: 1) {
@objc public dynamic var progressTintColor: UIColor? = AppAssets.netNewsWireBlueColor {
didSet {
bar.backgroundColor = progressTintColor
}

View File

@ -0,0 +1,15 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "accountFreshRSS.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@ -0,0 +1,38 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"red" : "242",
"alpha" : "1.000",
"blue" : "242",
"green" : "242"
}
}
},
{
"idiom" : "universal",
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"red" : "56",
"alpha" : "1.000",
"blue" : "56",
"green" : "56"
}
}
}
]
}

View File

@ -1,20 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"red" : "56",
"alpha" : "1.000",
"blue" : "56",
"green" : "56"
}
}
}
]
}

View File

@ -1,20 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
},
"colors" : [
{
"idiom" : "universal",
"color" : {
"color-space" : "srgb",
"components" : {
"red" : "242",
"alpha" : "1.000",
"blue" : "242",
"green" : "242"
}
}
}
]
}

View File

@ -93,7 +93,7 @@ struct SettingsFeedbinAccountView : View {
do {
do {
try workAccount.removeBasicCredentials()
try workAccount.removeCredentials()
} catch {}
try workAccount.storeCredentials(credentials)
@ -132,7 +132,7 @@ struct SettingsFeedbinAccountView : View {
init(account: Account) {
self.account = account
if case .basic(let username, let password) = try? account.retrieveBasicCredentials() {
if case .basic(let username, let password) = try? account.retrieveCredentials() {
self.email = username
self.password = password
}

View File

@ -21,7 +21,7 @@ struct SettingsReaderAPIAccountView : View {
NavigationView {
List {
Section(header:
SettingsAccountLabelView(accountImage: "accountLocal", accountLabel: "Google Reader Compatible").padding()
SettingsAccountLabelView(accountImage: "accountFreshRSS", accountLabel: "FreshRSS").padding()
) {
HStack {
Text("Email:")
@ -83,7 +83,7 @@ struct SettingsReaderAPIAccountView : View {
return
}
Account.validateCredentials(type: .readerAPI, credentials: credentials, endpoint: apiURL) { result in
Account.validateCredentials(type: viewModel.accountType, credentials: credentials, endpoint: apiURL) { result in
self.busy = false
@ -95,7 +95,7 @@ struct SettingsReaderAPIAccountView : View {
var newAccount = false
let workAccount: Account
if self.viewModel.account == nil {
workAccount = AccountManager.shared.createAccount(type: .readerAPI)
workAccount = AccountManager.shared.createAccount(type: self.viewModel.accountType)
newAccount = true
} else {
workAccount = self.viewModel.account!
@ -104,7 +104,7 @@ struct SettingsReaderAPIAccountView : View {
do {
do {
try workAccount.removeBasicCredentials()
try workAccount.removeCredentials()
} catch {}
workAccount.endpointURL = apiURL
@ -139,14 +139,17 @@ struct SettingsReaderAPIAccountView : View {
class ViewModel: BindableObject {
let didChange = PassthroughSubject<ViewModel, Never>()
var accountType: AccountType
var account: Account? = nil
init() {
init(accountType: AccountType) {
self.accountType = accountType
}
init(account: Account) {
init(accountType: AccountType, account: Account) {
self.account = account
if case .basic(let username, let password) = try? account.retrieveBasicCredentials() {
self.accountType = accountType
if case .basic(let username, let password) = try? account.retrieveCredentials() {
self.email = username
self.password = password
}
@ -181,7 +184,7 @@ struct SettingsReaderAPIAccountView : View {
#if DEBUG
struct SettingsReaderAPIAccountView_Previews : PreviewProvider {
static var previews: some View {
SettingsReaderAPIAccountView(viewModel: SettingsReaderAPIAccountView.ViewModel())
SettingsReaderAPIAccountView(viewModel: SettingsReaderAPIAccountView.ViewModel(accountType: .freshRSS))
}
}
#endif

View File

@ -30,7 +30,7 @@ class FeedbinAccountViewController: UIViewController {
emailTextField.delegate = self
passwordTextField.delegate = self
if let account = account, let credentials = try? account.retrieveBasicCredentials() {
if let account = account, let credentials = try? account.retrieveCredentials() {
actionButton.setTitle(NSLocalizedString("Update Credentials", comment: "Update Credentials"), for: .normal)
if case .basic(let username, let password) = credentials {
emailTextField.text = username
@ -77,7 +77,7 @@ class FeedbinAccountViewController: UIViewController {
do {
do {
try self.account?.removeBasicCredentials()
try self.account?.removeCredentials()
} catch {}
try self.account?.storeCredentials(credentials)