From bad21330f3061d68bd31e787a34db6bc813cd764 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Tue, 22 Sep 2020 17:39:29 -0500 Subject: [PATCH 01/13] Revert to requesting notifications on application launch so that the badge can be configured. Issue #2447 --- iOS/AppDelegate.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iOS/AppDelegate.swift b/iOS/AppDelegate.swift index b5c7244b8..0dd99bad3 100644 --- a/iOS/AppDelegate.swift +++ b/iOS/AppDelegate.swift @@ -90,9 +90,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD DispatchQueue.main.async { self.unreadCount = AccountManager.shared.unreadCount } - - UNUserNotificationCenter.current().getNotificationSettings { (settings) in - if settings.authorizationStatus == .authorized { + + UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .sound, .alert]) { (granted, error) in + if granted { DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } From 0708ffcec8d5c3caa28e104173423cfcf6a7ca3f Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Wed, 23 Sep 2020 20:36:30 -0500 Subject: [PATCH 02/13] Don't allow duplicate accounts on launch. Issue #2448 --- Frameworks/Account/AccountManager.swift | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Frameworks/Account/AccountManager.swift b/Frameworks/Account/AccountManager.swift index fd357b8cc..b0c8dca0b 100644 --- a/Frameworks/Account/AccountManager.swift +++ b/Frameworks/Account/AccountManager.swift @@ -165,6 +165,15 @@ public final class AccountManager: UnreadCountProvider { NotificationCenter.default.post(name: .UserDidDeleteAccount, object: self, userInfo: userInfo) } + public func duplicateServiceAccount(type: AccountType, username: String?) -> Bool { + for account in accounts { + if account.type == type && username == account.username { + return true + } + } + return false + } + public func existingAccount(with accountID: String) -> Account? { return accountsDictionary[accountID] } @@ -350,16 +359,24 @@ private extension AccountManager { print("Error reading Accounts folder: \(error)") return } + + filenames = filenames?.sorted() filenames?.forEach { (oneFilename) in guard oneFilename != defaultAccountFolderName else { return } if let oneAccount = loadAccount(oneFilename) { - accountsDictionary[oneAccount.accountID] = oneAccount + if !duplicateServiceAccount(oneAccount) { + accountsDictionary[oneAccount.accountID] = oneAccount + } } } } + + func duplicateServiceAccount(_ account: Account) -> Bool { + return duplicateServiceAccount(type: account.type, username: account.username) + } func sortByName(_ accounts: [Account]) -> [Account] { // LocalAccount is first. From 59d427d62644ff8b51ea8d3dd76eecf83b15447b Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Thu, 24 Sep 2020 18:02:12 -0500 Subject: [PATCH 03/13] Don't allow duplicate Feedly or FeedWrangler accounts. Issue #2448 --- .../FeedWranglerAccountViewController.swift | 11 ++++++++--- iOS/Account/FeedbinAccountViewController.swift | 14 ++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/iOS/Account/FeedWranglerAccountViewController.swift b/iOS/Account/FeedWranglerAccountViewController.swift index 39d1e2bbd..92e12db32 100644 --- a/iOS/Account/FeedWranglerAccountViewController.swift +++ b/iOS/Account/FeedWranglerAccountViewController.swift @@ -72,17 +72,22 @@ class FeedWranglerAccountViewController: UITableViewController { } @IBAction func action(_ sender: Any) { - guard let email = emailTextField.text, let password = passwordTextField.text else { showError(NSLocalizedString("Username & password required.", comment: "Credentials Error")) return } + // When you fill in the email address via auto-complete it adds extra whitespace + let trimmedEmail = email.trimmingCharacters(in: .whitespaces) + + guard !AccountManager.shared.duplicateServiceAccount(type: .feedWrangler, username: trimmedEmail) else { + showError(NSLocalizedString("There is already a FeedWrangler account with that username created.", comment: "Duplicate Error")) + return + } + resignFirstResponder() toggleActivityIndicatorAnimation(visible: true) setNavigationEnabled(to: false) - // When you fill in the email address via auto-complete it adds extra whitespace - let trimmedEmail = email.trimmingCharacters(in: .whitespaces) let credentials = Credentials(type: .feedWranglerBasic, username: trimmedEmail, secret: password) Account.validateCredentials(type: .feedWrangler, credentials: credentials) { result in diff --git a/iOS/Account/FeedbinAccountViewController.swift b/iOS/Account/FeedbinAccountViewController.swift index b33638b5d..c9c60d5e0 100644 --- a/iOS/Account/FeedbinAccountViewController.swift +++ b/iOS/Account/FeedbinAccountViewController.swift @@ -73,17 +73,23 @@ class FeedbinAccountViewController: UITableViewController { } @IBAction func action(_ sender: Any) { - guard let email = emailTextField.text, let password = passwordTextField.text else { showError(NSLocalizedString("Username & password required.", comment: "Credentials Error")) return } - resignFirstResponder() - toggleActivityIndicatorAnimation(visible: true) - setNavigationEnabled(to: false) // When you fill in the email address via auto-complete it adds extra whitespace let trimmedEmail = email.trimmingCharacters(in: .whitespaces) + + guard !AccountManager.shared.duplicateServiceAccount(type: .feedbin, username: trimmedEmail) else { + showError(NSLocalizedString("There is already a Feedbin account with that username created.", comment: "Duplicate Error")) + return + } + + resignFirstResponder() + toggleActivityIndicatorAnimation(visible: true) + setNavigationEnabled(to: false) + let credentials = Credentials(type: .basic, username: trimmedEmail, secret: password) Account.validateCredentials(type: .feedbin, credentials: credentials) { result in self.toggleActivityIndicatorAnimation(visible: false) From cc7e89215c3c688435852797930ff7a3c327b90b Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Thu, 24 Sep 2020 18:02:33 -0500 Subject: [PATCH 04/13] Don't allow duplicate Feedly Accounts. Issue #2448 --- .../Feedly/OAuthAccountAuthorizationOperation.swift | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Frameworks/Account/Feedly/OAuthAccountAuthorizationOperation.swift b/Frameworks/Account/Feedly/OAuthAccountAuthorizationOperation.swift index 1b78797da..7c72d34d1 100644 --- a/Frameworks/Account/Feedly/OAuthAccountAuthorizationOperation.swift +++ b/Frameworks/Account/Feedly/OAuthAccountAuthorizationOperation.swift @@ -15,6 +15,13 @@ public protocol OAuthAccountAuthorizationOperationDelegate: class { func oauthAccountAuthorizationOperation(_ operation: OAuthAccountAuthorizationOperation, didFailWith error: Error) } +public enum OAuthAccountAuthorizationOperationError: LocalizedError { + case duplicateAccount + + public var errorDescription: String? { + return NSLocalizedString("There is already a Feedly account with that username created.", comment: "Duplicate Error") + } +} @objc public final class OAuthAccountAuthorizationOperation: NSObject, MainThreadOperation, ASWebAuthenticationPresentationContextProviding { public var isCanceled: Bool = false { @@ -122,7 +129,11 @@ public protocol OAuthAccountAuthorizationOperationDelegate: class { } private func saveAccount(for grant: OAuthAuthorizationGrant) { - // TODO: Find an already existing account for this username? + guard !AccountManager.shared.duplicateServiceAccount(type: .feedly, username: grant.accessToken.username) else { + didFinish(OAuthAccountAuthorizationOperationError.duplicateAccount) + return + } + let account = AccountManager.shared.createAccount(type: .feedly) do { From 8685380552d0bacf2b29943f4b9bccda70a104f2 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Thu, 24 Sep 2020 18:27:54 -0700 Subject: [PATCH 05/13] Bump version and build number. --- xcconfig/common/NetNewsWire_ios_target_common.xcconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig index 24e4615db..811e17027 100644 --- a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig +++ b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig @@ -1,7 +1,7 @@ // High Level Settings common to both the iOS application and any extensions we bundle with it -MARKETING_VERSION = 5.0.3 -CURRENT_PROJECT_VERSION = 50 +MARKETING_VERSION = 5.0.4 +CURRENT_PROJECT_VERSION = 51 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon From 3345c058086a2427f86901d3850854d9e5e93a53 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 26 Sep 2020 15:11:59 -0500 Subject: [PATCH 06/13] Don't count OnMyMac accounts as potential duplicates. --- Frameworks/Account/AccountManager.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Frameworks/Account/AccountManager.swift b/Frameworks/Account/AccountManager.swift index b0c8dca0b..80aae85f6 100644 --- a/Frameworks/Account/AccountManager.swift +++ b/Frameworks/Account/AccountManager.swift @@ -166,6 +166,9 @@ public final class AccountManager: UnreadCountProvider { } public func duplicateServiceAccount(type: AccountType, username: String?) -> Bool { + guard type != .onMyMac else { + return false + } for account in accounts { if account.type == type && username == account.username { return true From e651b1e17bedfc59ec9034b0272a36e37186d524 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Sun, 27 Sep 2020 12:41:10 -0700 Subject: [PATCH 07/13] Bump build number. --- xcconfig/common/NetNewsWire_ios_target_common.xcconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig index 811e17027..70cfe2f6c 100644 --- a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig +++ b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig @@ -1,7 +1,7 @@ // High Level Settings common to both the iOS application and any extensions we bundle with it MARKETING_VERSION = 5.0.4 -CURRENT_PROJECT_VERSION = 51 +CURRENT_PROJECT_VERSION = 52 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon From 4280895fa6b1ac59ec09bfa8a0e2aabdabc77186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kiel=20Gillard=20=F0=9F=A4=AA?= Date: Thu, 1 Oct 2020 14:09:41 +1000 Subject: [PATCH 08/13] Ensure the add new feedly feed completion handler is called when a feed is not found. Fixes #2470 --- .../Feedly/Operations/FeedlyAddNewFeedOperation.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Frameworks/Account/Feedly/Operations/FeedlyAddNewFeedOperation.swift b/Frameworks/Account/Feedly/Operations/FeedlyAddNewFeedOperation.swift index d4852c2f4..93eb20902 100644 --- a/Frameworks/Account/Feedly/Operations/FeedlyAddNewFeedOperation.swift +++ b/Frameworks/Account/Feedly/Operations/FeedlyAddNewFeedOperation.swift @@ -66,6 +66,13 @@ class FeedlyAddNewFeedOperation: FeedlyOperation, FeedlyOperationDelegate, Feedl addCompletionHandler = nil super.didCancel() } + + override func didFinish(with error: Error) { + assert(Thread.isMainThread) + addCompletionHandler?(.failure(error)) + addCompletionHandler = nil + super.didFinish(with: error) + } func feedlySearchOperation(_ operation: FeedlySearchOperation, didGet response: FeedlyFeedsSearchResponse) { guard !isCanceled else { From dcdefc39317300d5d8b47cfffed61ddb39f8e7e1 Mon Sep 17 00:00:00 2001 From: Brent Simmons Date: Thu, 1 Oct 2020 20:01:16 -0700 Subject: [PATCH 09/13] Bump build number. --- xcconfig/common/NetNewsWire_ios_target_common.xcconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig index 70cfe2f6c..5069c364b 100644 --- a/xcconfig/common/NetNewsWire_ios_target_common.xcconfig +++ b/xcconfig/common/NetNewsWire_ios_target_common.xcconfig @@ -1,7 +1,7 @@ // High Level Settings common to both the iOS application and any extensions we bundle with it MARKETING_VERSION = 5.0.4 -CURRENT_PROJECT_VERSION = 52 +CURRENT_PROJECT_VERSION = 53 ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon From af7fa62996c31658791e3bb4262baa5259ce6157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kiel=20Gillard=20=F0=9F=A4=AA?= Date: Mon, 5 Oct 2020 22:12:45 +1100 Subject: [PATCH 10/13] Encode the + in Feedly collection URIs. Fixes #2443. --- Frameworks/Account/Feedly/FeedlyAPICaller.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Frameworks/Account/Feedly/FeedlyAPICaller.swift b/Frameworks/Account/Feedly/FeedlyAPICaller.swift index 9b1421df7..af7019e75 100644 --- a/Frameworks/Account/Feedly/FeedlyAPICaller.swift +++ b/Frameworks/Account/Feedly/FeedlyAPICaller.swift @@ -47,10 +47,15 @@ final class FeedlyAPICaller { private let transport: Transport private let baseUrlComponents: URLComponents + private let uriComponentAllowed: CharacterSet init(transport: Transport, api: API) { self.transport = transport self.baseUrlComponents = api.baseUrlComponents + + var urlHostAllowed = CharacterSet.urlHostAllowed + urlHostAllowed.remove("+") + uriComponentAllowed = urlHostAllowed } weak var delegate: FeedlyAPICallerDelegate? @@ -271,7 +276,7 @@ final class FeedlyAPICaller { } private func encodeForURLPath(_ pathComponent: String) -> String? { - return pathComponent.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) + return pathComponent.addingPercentEncoding(withAllowedCharacters: uriComponentAllowed) } func deleteCollection(with id: String, completion: @escaping (Result) -> ()) { From 8ab762c860ed9330dd4dfc7fde75e424630f0259 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 5 Oct 2020 17:28:03 -0500 Subject: [PATCH 11/13] FIx slider layering issue on iOS 14. Issue #2482 --- iOS/UIKit Extensions/TickMarkSlider.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/UIKit Extensions/TickMarkSlider.swift b/iOS/UIKit Extensions/TickMarkSlider.swift index a6b8f7b2c..5a7aa73ba 100644 --- a/iOS/UIKit Extensions/TickMarkSlider.swift +++ b/iOS/UIKit Extensions/TickMarkSlider.swift @@ -37,7 +37,7 @@ class TickMarkSlider: UISlider { let tick = UIView() tick.translatesAutoresizingMaskIntoConstraints = false tick.backgroundColor = AppAssets.tickMarkColor - insertSubview(tick, belowSubview: self) + insertSubview(tick, at: 0) tick.widthAnchor.constraint(equalToConstant: 3).isActive = true tick.heightAnchor.constraint(equalToConstant: 10).isActive = true From 3431a45d22bdbf523aa7e6029449b5c80ecf5cc8 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 17 Oct 2020 20:00:58 -0500 Subject: [PATCH 12/13] Remove test for URL's that might not be feeds since it is running on the main thread. Issue #2499 --- Frameworks/Account/LocalAccount/LocalAccountRefresher.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift b/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift index 870113d3d..3ed75b47a 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountRefresher.swift @@ -110,11 +110,6 @@ extension LocalAccountRefresher: DownloadSessionDelegate { return false } - if data.count > 4096 { - let parserData = ParserData(url: feed.url, data: data) - return FeedParser.mightBeAbleToParseBasedOnPartialData(parserData) - } - return true } From d1b8ac06afb3f58c68f83a5063067ee2dd200631 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Sat, 17 Oct 2020 20:06:53 -0500 Subject: [PATCH 13/13] Ensure that the snapshot has items in it before testing section zero. --- iOS/MasterTimeline/MasterTimelineViewController.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/iOS/MasterTimeline/MasterTimelineViewController.swift b/iOS/MasterTimeline/MasterTimelineViewController.swift index 224c1db09..5945dcf47 100644 --- a/iOS/MasterTimeline/MasterTimelineViewController.swift +++ b/iOS/MasterTimeline/MasterTimelineViewController.swift @@ -611,12 +611,14 @@ private extension MasterTimelineViewController { } tableView.selectRow(at: nil, animated: false, scrollPosition: .top) - if resetScroll && dataSource.snapshot().itemIdentifiers(inSection: 0).count > 0 { - tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false) + if resetScroll { + let snapshot = dataSource.snapshot() + if snapshot.sectionIdentifiers.count > 0 && snapshot.itemIdentifiers(inSection: 0).count > 0 { + tableView.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: false) + } } updateToolbar() - } func updateToolbar() {