From 0a9bf2aef0e399bb53f8048fa13cb9b56f00b961 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Fri, 17 May 2019 10:44:22 -0500 Subject: [PATCH] Prevent more than one OPML import from being run at the same time. --- Frameworks/Account/Account.swift | 25 +++++- Frameworks/Account/AccountDelegate.swift | 2 + .../Feedbin/FeedbinAccountDelegate.swift | 80 ++++++++++--------- .../LocalAccount/LocalAccountDelegate.swift | 2 + 4 files changed, 71 insertions(+), 38 deletions(-) diff --git a/Frameworks/Account/Account.swift b/Frameworks/Account/Account.swift index d555a5152..b25c60103 100644 --- a/Frameworks/Account/Account.swift +++ b/Frameworks/Account/Account.swift @@ -36,9 +36,25 @@ public enum AccountType: Int { // TODO: more } -public enum AccountError: Error { +public enum AccountError: LocalizedError { + case createErrorNotFound case createErrorAlreadySubscribed + case opmlImportInProgress + + public var errorDescription: String? { + switch self { + case .opmlImportInProgress: + return NSLocalizedString("An OPML import for this account is already running.", comment: "Import running") + default: + return NSLocalizedString("An unknown error occurred.", comment: "Unknown error") + } + } + + public var recoverySuggestion: String? { + return NSLocalizedString("Please try again later.", comment: "Try later") + } + } public final class Account: DisplayNameProvider, UnreadCountProvider, Container, Hashable { @@ -299,6 +315,12 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, } public func importOPML(_ opmlFile: URL, completion: @escaping (Result) -> Void) { + + guard !delegate.opmlImportInProgress else { + completion(.failure(AccountError.opmlImportInProgress)) + return + } + delegate.importOPML(for: self, opmlFile: opmlFile) { [weak self] result in switch result { case .success: @@ -312,6 +334,7 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container, completion(.failure(error)) } } + } public func markArticles(_ articles: Set
, statusKey: ArticleStatus.Key, flag: Bool) -> Set
? { diff --git a/Frameworks/Account/AccountDelegate.swift b/Frameworks/Account/AccountDelegate.swift index ffdc6e0c0..aaffefc16 100644 --- a/Frameworks/Account/AccountDelegate.swift +++ b/Frameworks/Account/AccountDelegate.swift @@ -14,6 +14,8 @@ protocol AccountDelegate { // Local account does not; some synced accounts might. var supportsSubFolders: Bool { get } + var opmlImportInProgress: Bool { get } + var server: String? { get } var credentials: Credentials? { get set } var accountMetadata: AccountMetadata? { get set } diff --git a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift index 7ab706a6d..b2a49406b 100644 --- a/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift +++ b/Frameworks/Account/Feedbin/FeedbinAccountDelegate.swift @@ -32,6 +32,7 @@ final class FeedbinAccountDelegate: AccountDelegate { let supportsSubFolders = false let server: String? = "api.feedbin.com" + var opmlImportInProgress = false var credentials: Credentials? { didSet { @@ -185,7 +186,7 @@ final class FeedbinAccountDelegate: AccountDelegate { } func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result) -> Void) { - + var fileData: Data? do { @@ -201,13 +202,15 @@ final class FeedbinAccountDelegate: AccountDelegate { } os_log(.debug, log: log, "Begin importing OPML...") - + opmlImportInProgress = true + caller.importOPML(opmlData: opmlData) { [weak self] result in switch result { case .success(let importResult): if importResult.complete { guard let self = self else { return } os_log(.debug, log: self.log, "Import OPML done.") + self.opmlImportInProgress = false DispatchQueue.main.async { completion(.success(())) } @@ -217,6 +220,7 @@ final class FeedbinAccountDelegate: AccountDelegate { case .failure(let error): guard let self = self else { return } os_log(.debug, log: self.log, "Import OPML failed.") + self.opmlImportInProgress = false DispatchQueue.main.async { completion(.failure(error)) } @@ -225,41 +229,6 @@ final class FeedbinAccountDelegate: AccountDelegate { } - private func checkImportResult(opmlImportResultID: Int, completion: @escaping (Result) -> Void) { - - DispatchQueue.main.async { - - Timer.scheduledTimer(withTimeInterval: 15, repeats: true) { [weak self] timer in - - guard let self = self else { return } - - os_log(.debug, log: self.log, "Checking status of OPML import...") - - self.caller.retrieveOPMLImportResult(importID: opmlImportResultID) { result in - switch result { - case .success(let importResult): - if let result = importResult, result.complete { - os_log(.debug, log: self.log, "Checking status of OPML import successfully completed.") - timer.invalidate() - DispatchQueue.main.async { - completion(.success(())) - } - } - case .failure(let error): - os_log(.debug, log: self.log, "Import OPML check failed.") - timer.invalidate() - DispatchQueue.main.async { - completion(.failure(error)) - } - } - } - - } - - } - - } - func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result) -> Void) { caller.renameTag(oldName: folder.name ?? "", newName: name) { result in @@ -546,6 +515,43 @@ private extension FeedbinAccountDelegate { } + func checkImportResult(opmlImportResultID: Int, completion: @escaping (Result) -> Void) { + + DispatchQueue.main.async { + + Timer.scheduledTimer(withTimeInterval: 15, repeats: true) { [weak self] timer in + + guard let self = self else { return } + + os_log(.debug, log: self.log, "Checking status of OPML import...") + + self.caller.retrieveOPMLImportResult(importID: opmlImportResultID) { result in + switch result { + case .success(let importResult): + if let result = importResult, result.complete { + os_log(.debug, log: self.log, "Checking status of OPML import successfully completed.") + timer.invalidate() + self.opmlImportInProgress = false + DispatchQueue.main.async { + completion(.success(())) + } + } + case .failure(let error): + os_log(.debug, log: self.log, "Import OPML check failed.") + timer.invalidate() + self.opmlImportInProgress = false + DispatchQueue.main.async { + completion(.failure(error)) + } + } + } + + } + + } + + } + func syncFolders(_ account: Account, _ tags: [FeedbinTag]?) { guard let tags = tags else { return } diff --git a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift index 150fbf3ea..3e9a97d81 100644 --- a/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift +++ b/Frameworks/Account/LocalAccount/LocalAccountDelegate.swift @@ -18,6 +18,8 @@ public enum LocalAccountDelegateError: String, Error { final class LocalAccountDelegate: AccountDelegate { let supportsSubFolders = false + let opmlImportInProgress = false + let server: String? = nil var credentials: Credentials? var accountMetadata: AccountMetadata?