diff --git a/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift b/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift index 7eabf2e41..de674b1b0 100644 --- a/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift +++ b/Account/Sources/Account/Feedbin/FeedbinAccountDelegate.swift @@ -1268,87 +1268,63 @@ private extension FeedbinAccountDelegate { } - func syncArticleReadState(account: Account, articleIDs: [Int]?, completion: @escaping (() -> Void)) { - guard let articleIDs = articleIDs else { - completion() - return - } + func syncArticleReadState(account: Account, articleIDs: [Int]?, completion: @escaping (() -> Void)) { - database.selectPendingReadStatusArticleIDs() { result in - - func process(_ pendingArticleIDs: Set) { - - Task { @MainActor in - let feedbinUnreadArticleIDs = Set(articleIDs.map { String($0) } ) - let updatableFeedbinUnreadArticleIDs = feedbinUnreadArticleIDs.subtracting(pendingArticleIDs) - - do { - let currentUnreadArticleIDs = try await account.fetchUnreadArticleIDs() - - // Mark articles as unread - let deltaUnreadArticleIDs = updatableFeedbinUnreadArticleIDs.subtracting(currentUnreadArticleIDs) - account.markAsUnread(deltaUnreadArticleIDs) - - // Mark articles as read - let deltaReadArticleIDs = currentUnreadArticleIDs.subtracting(updatableFeedbinUnreadArticleIDs) - account.markAsRead(deltaReadArticleIDs) - - } catch let error { - self.logger.error("Sync Articles Read Status failed: \(error.localizedDescription, privacy: .public)") - } - - completion() - } + Task { @MainActor in + guard let articleIDs else { + completion() + return } - switch result { - case .success(let pendingArticleIDs): - process(pendingArticleIDs) - case .failure(let error): + do { + let pendingArticleIDs = try await database.selectPendingReadArticleIDs() + + let feedbinUnreadArticleIDs = Set(articleIDs.map { String($0) } ) + let updatableFeedbinUnreadArticleIDs = feedbinUnreadArticleIDs.subtracting(pendingArticleIDs) + + let currentUnreadArticleIDs = try await account.fetchUnreadArticleIDs() + + // Mark articles as unread + let deltaUnreadArticleIDs = updatableFeedbinUnreadArticleIDs.subtracting(currentUnreadArticleIDs) + account.markAsUnread(deltaUnreadArticleIDs) + + // Mark articles as read + let deltaReadArticleIDs = currentUnreadArticleIDs.subtracting(updatableFeedbinUnreadArticleIDs) + account.markAsRead(deltaReadArticleIDs) + } catch let error { self.logger.error("Sync Articles Read Status failed: \(error.localizedDescription, privacy: .public)") - } - } - } + } + + completion() + } + } func syncArticleStarredState(account: Account, articleIDs: [Int]?, completion: @escaping (() -> Void)) { - guard let articleIDs = articleIDs else { - completion() - return - } - database.selectPendingStarredStatusArticleIDs() { result in - - func process(_ pendingArticleIDs: Set) { - - Task { @MainActor in - let feedbinStarredArticleIDs = Set(articleIDs.map { String($0) } ) - let updatableFeedbinStarredArticleIDs = feedbinStarredArticleIDs.subtracting(pendingArticleIDs) - - do { - let currentStarredArticleIDs = try await account.fetchStarredArticleIDs() - - // Mark articles as starred - let deltaStarredArticleIDs = updatableFeedbinStarredArticleIDs.subtracting(currentStarredArticleIDs) - account.markAsStarred(deltaStarredArticleIDs) - - // Mark articles as unstarred - let deltaUnstarredArticleIDs = currentStarredArticleIDs.subtracting(updatableFeedbinStarredArticleIDs) - account.markAsUnstarred(deltaUnstarredArticleIDs) - - } catch let error { - self.logger.error("Sync Article Starred Status failed: \(error.localizedDescription, privacy: .public)") - } - - completion() - } + Task { @MainActor in + guard let articleIDs = articleIDs else { + completion() + return } - switch result { - case .success(let pendingArticleIDs): - process(pendingArticleIDs) - case .failure(let error): + do { + let currentStarredArticleIDs = try await database.selectPendingStarredArticleIDs() + + let feedbinStarredArticleIDs = Set(articleIDs.map { String($0) } ) + let updatableFeedbinStarredArticleIDs = feedbinStarredArticleIDs.subtracting(currentStarredArticleIDs) + + // Mark articles as starred + let deltaStarredArticleIDs = updatableFeedbinStarredArticleIDs.subtracting(currentStarredArticleIDs) + account.markAsStarred(deltaStarredArticleIDs) + + // Mark articles as unstarred + let deltaUnstarredArticleIDs = currentStarredArticleIDs.subtracting(updatableFeedbinStarredArticleIDs) + account.markAsUnstarred(deltaUnstarredArticleIDs) + } catch let error { self.logger.error("Sync Article Starred Status failed: \(error.localizedDescription, privacy: .public)") } + + completion() } } diff --git a/Account/Sources/Account/NewsBlur/Internals/NewsBlurAccountDelegate+Internal.swift b/Account/Sources/Account/NewsBlur/Internals/NewsBlurAccountDelegate+Internal.swift index 1583ecaa2..cf982ba8c 100644 --- a/Account/Sources/Account/NewsBlur/Internals/NewsBlurAccountDelegate+Internal.swift +++ b/Account/Sources/Account/NewsBlur/Internals/NewsBlurAccountDelegate+Internal.swift @@ -317,87 +317,68 @@ extension NewsBlurAccountDelegate { } func syncStoryReadState(account: Account, hashes: [NewsBlurStoryHash]?, completion: @escaping (() -> Void)) { - guard let hashes = hashes else { - completion() - return - } - database.selectPendingReadStatusArticleIDs() { result in - @MainActor func process(_ pendingStoryHashes: Set) { - - Task { @MainActor in - let newsBlurUnreadStoryHashes = Set(hashes.map { $0.hash } ) - let updatableNewsBlurUnreadStoryHashes = newsBlurUnreadStoryHashes.subtracting(pendingStoryHashes) - - do { - let currentUnreadArticleIDs = try await account.fetchUnreadArticleIDs() - - // Mark articles as unread - let deltaUnreadArticleIDs = updatableNewsBlurUnreadStoryHashes.subtracting(currentUnreadArticleIDs) - account.markAsUnread(deltaUnreadArticleIDs) - - // Mark articles as read - let deltaReadArticleIDs = currentUnreadArticleIDs.subtracting(updatableNewsBlurUnreadStoryHashes) - account.markAsRead(deltaReadArticleIDs) - } catch let error { - self.logger.error("Sync story read status failed: \(error.localizedDescription, privacy: .public)") - } - - completion() - } - - switch result { - case .success(let pendingArticleIDs): - process(pendingArticleIDs) - case .failure(let error): - self.logger.error("Sync story read status failed: \(error.localizedDescription, privacy: .public)") - } + Task { @MainActor in + guard let hashes = hashes else { + completion() + return } + + do { + let pendingStoryHashes = try await database.selectPendingReadArticleIDs() + + let newsBlurUnreadStoryHashes = Set(hashes.map { $0.hash } ) + let updatableNewsBlurUnreadStoryHashes = newsBlurUnreadStoryHashes.subtracting(pendingStoryHashes) + + let currentUnreadArticleIDs = try await account.fetchUnreadArticleIDs() + + // Mark articles as unread + let deltaUnreadArticleIDs = updatableNewsBlurUnreadStoryHashes.subtracting(currentUnreadArticleIDs) + account.markAsUnread(deltaUnreadArticleIDs) + + // Mark articles as read + let deltaReadArticleIDs = currentUnreadArticleIDs.subtracting(updatableNewsBlurUnreadStoryHashes) + account.markAsRead(deltaReadArticleIDs) + } catch let error { + self.logger.error("Sync story read status failed: \(error.localizedDescription, privacy: .public)") + } + + completion() } } - func syncStoryStarredState(account: Account, hashes: [NewsBlurStoryHash]?, completion: @escaping (() -> Void)) { - guard let hashes = hashes else { - completion() - return - } + func syncStoryStarredState(account: Account, hashes: [NewsBlurStoryHash]?, completion: @escaping (() -> Void)) { - database.selectPendingStarredStatusArticleIDs() { result in - - func process(_ pendingStoryHashes: Set) { - - Task { @MainActor in - let newsBlurStarredStoryHashes = Set(hashes.map { $0.hash } ) - let updatableNewsBlurUnreadStoryHashes = newsBlurStarredStoryHashes.subtracting(pendingStoryHashes) - - do { - let currentStarredArticleIDs = try await account.fetchStarredArticleIDs() - - // Mark articles as starred - let deltaStarredArticleIDs = updatableNewsBlurUnreadStoryHashes.subtracting(currentStarredArticleIDs) - account.markAsStarred(deltaStarredArticleIDs) - - // Mark articles as unstarred - let deltaUnstarredArticleIDs = currentStarredArticleIDs.subtracting(updatableNewsBlurUnreadStoryHashes) - account.markAsUnstarred(deltaUnstarredArticleIDs) - } catch let error { - self.logger.error("Sync story starred status failed: \(error.localizedDescription, privacy: .public)") - } - - completion() - } + Task { @MainActor in + guard let hashes = hashes else { + completion() + return } - switch result { - case .success(let pendingArticleIDs): - process(pendingArticleIDs) - case .failure(let error): - self.logger.error("Sync story starred status failed: \(error.localizedDescription, privacy: .public)") - } - } - } + do { + let pendingStoryHashes = try await database.selectPendingStarredArticleIDs() - func createFeed(account: Account, newsBlurFeed: NewsBlurFeed?, name: String?, container: Container, completion: @escaping (Result) -> Void) { + let newsBlurStarredStoryHashes = Set(hashes.map { $0.hash } ) + let updatableNewsBlurUnreadStoryHashes = newsBlurStarredStoryHashes.subtracting(pendingStoryHashes) + + let currentStarredArticleIDs = try await account.fetchStarredArticleIDs() + + // Mark articles as starred + let deltaStarredArticleIDs = updatableNewsBlurUnreadStoryHashes.subtracting(currentStarredArticleIDs) + account.markAsStarred(deltaStarredArticleIDs) + + // Mark articles as unstarred + let deltaUnstarredArticleIDs = currentStarredArticleIDs.subtracting(updatableNewsBlurUnreadStoryHashes) + account.markAsUnstarred(deltaUnstarredArticleIDs) + } catch let error { + self.logger.error("Sync story starred status failed: \(error.localizedDescription, privacy: .public)") + } + + completion() + } + } + + func createFeed(account: Account, newsBlurFeed: NewsBlurFeed?, name: String?, container: Container, completion: @escaping (Result) -> Void) { guard let newsBlurFeed = newsBlurFeed else { completion(.failure(NewsBlurError.invalidParameter)) return diff --git a/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift b/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift index 5b975e4c3..6a64d2d08 100644 --- a/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift +++ b/Account/Sources/Account/ReaderAPI/ReaderAPIAccountDelegate.swift @@ -1079,81 +1079,62 @@ private extension ReaderAPIAccountDelegate { } func syncArticleReadState(account: Account, articleIDs: [String]?, completion: @escaping (() -> Void)) { - guard let articleIDs = articleIDs else { - completion() - return - } - database.selectPendingReadStatusArticleIDs() { result in - - @MainActor func process(_ pendingArticleIDs: Set) { - Task { @MainActor in - let updatableReaderUnreadArticleIDs = Set(articleIDs).subtracting(pendingArticleIDs) - - do { - let currentUnreadArticleIDs = try await account.fetchUnreadArticleIDs() - - // Mark articles as unread - let deltaUnreadArticleIDs = updatableReaderUnreadArticleIDs.subtracting(currentUnreadArticleIDs) - account.markAsUnread(deltaUnreadArticleIDs) - - // Mark articles as read - let deltaReadArticleIDs = currentUnreadArticleIDs.subtracting(updatableReaderUnreadArticleIDs) - account.markAsRead(deltaReadArticleIDs) - } catch let error { - self.logger.error("Sync Article Read Status failed: \(error.localizedDescription, privacy: .public)") - } - - completion() - } - - switch result { - case .success(let pendingArticleIDs): - process(pendingArticleIDs) - case .failure(let error): - self.logger.error("Sync Article Read Status failed: \(error.localizedDescription, privacy: .public)") - } + Task { @MainActor in + guard let articleIDs = articleIDs else { + completion() + return } + + do { + let pendingArticleIDs = try await database.selectPendingReadArticleIDs() + + let updatableReaderUnreadArticleIDs = Set(articleIDs).subtracting(pendingArticleIDs) + + let currentUnreadArticleIDs = try await account.fetchUnreadArticleIDs() + + // Mark articles as unread + let deltaUnreadArticleIDs = updatableReaderUnreadArticleIDs.subtracting(currentUnreadArticleIDs) + account.markAsUnread(deltaUnreadArticleIDs) + + // Mark articles as read + let deltaReadArticleIDs = currentUnreadArticleIDs.subtracting(updatableReaderUnreadArticleIDs) + account.markAsRead(deltaReadArticleIDs) + } catch let error { + self.logger.error("Sync Article Read Status failed: \(error.localizedDescription, privacy: .public)") + } + + completion() } } func syncArticleStarredState(account: Account, articleIDs: [String]?, completion: @escaping (() -> Void)) { - guard let articleIDs = articleIDs else { - completion() - return - } - database.selectPendingStarredStatusArticleIDs() { result in - - func process(_ pendingArticleIDs: Set) { - - Task { @MainActor in - do { - let updatableReaderUnreadArticleIDs = Set(articleIDs).subtracting(pendingArticleIDs) - - let currentStarredArticleIDs = try await account.fetchStarredArticleIDs() - - // Mark articles as starred - let deltaStarredArticleIDs = updatableReaderUnreadArticleIDs.subtracting(currentStarredArticleIDs) - account.markAsStarred(deltaStarredArticleIDs) - - // Mark articles as unstarred - let deltaUnstarredArticleIDs = currentStarredArticleIDs.subtracting(updatableReaderUnreadArticleIDs) - account.markAsUnstarred(deltaUnstarredArticleIDs) - } catch let error { - self.logger.error("Sync Article Starred Status failed: \(error.localizedDescription, privacy: .public)") - } - - completion() - } + Task { @MainActor in + guard let articleIDs = articleIDs else { + completion() + return } - switch result { - case .success(let pendingArticleIDs): - process(pendingArticleIDs) - case .failure(let error): + do { + let pendingArticleIDs = try await database.selectPendingStarredArticleIDs() + + let updatableReaderUnreadArticleIDs = Set(articleIDs).subtracting(pendingArticleIDs) + + let currentStarredArticleIDs = try await account.fetchStarredArticleIDs() + + // Mark articles as starred + let deltaStarredArticleIDs = updatableReaderUnreadArticleIDs.subtracting(currentStarredArticleIDs) + account.markAsStarred(deltaStarredArticleIDs) + + // Mark articles as unstarred + let deltaUnstarredArticleIDs = currentStarredArticleIDs.subtracting(updatableReaderUnreadArticleIDs) + account.markAsUnstarred(deltaUnstarredArticleIDs) + } catch let error { self.logger.error("Sync Article Starred Status failed: \(error.localizedDescription, privacy: .public)") } + + completion() } } } diff --git a/SyncDatabase/Sources/SyncDatabase/SyncDatabase.swift b/SyncDatabase/Sources/SyncDatabase/SyncDatabase.swift index 93726a113..c0fe7d068 100644 --- a/SyncDatabase/Sources/SyncDatabase/SyncDatabase.swift +++ b/SyncDatabase/Sources/SyncDatabase/SyncDatabase.swift @@ -44,10 +44,18 @@ public struct SyncDatabase { syncStatusTable.selectPendingCount(completion) } + public func selectPendingReadArticleIDs() async throws -> Set { + try await syncStatusTable.selectPendingReadArticleIDs() + } + public func selectPendingReadStatusArticleIDs(completion: @escaping SyncStatusArticleIDsCompletionBlock) { syncStatusTable.selectPendingReadStatusArticleIDs(completion: completion) } - + + public func selectPendingStarredArticleIDs() async throws -> Set { + try await syncStatusTable.selectPendingStarredArticleIDs() + } + public func selectPendingStarredStatusArticleIDs(completion: @escaping SyncStatusArticleIDsCompletionBlock) { syncStatusTable.selectPendingStarredStatusArticleIDs(completion: completion) } diff --git a/SyncDatabase/Sources/SyncDatabase/SyncStatusTable.swift b/SyncDatabase/Sources/SyncDatabase/SyncStatusTable.swift index 0ab0d80cb..560926793 100644 --- a/SyncDatabase/Sources/SyncDatabase/SyncStatusTable.swift +++ b/SyncDatabase/Sources/SyncDatabase/SyncStatusTable.swift @@ -87,10 +87,18 @@ struct SyncStatusTable: DatabaseTable { } } + func selectPendingReadArticleIDs() async throws -> Set { + return try await selectPendingArticleIDs(.read) + } + func selectPendingReadStatusArticleIDs(completion: @escaping SyncStatusArticleIDsCompletionBlock) { selectPendingArticleIDsAsync(.read, completion) } - + + func selectPendingStarredArticleIDs() async throws -> Set { + return try await selectPendingArticleIDs(.starred) + } + func selectPendingStarredStatusArticleIDs(completion: @escaping SyncStatusArticleIDsCompletionBlock) { selectPendingArticleIDsAsync(.starred, completion) } @@ -197,7 +205,22 @@ private extension SyncStatusTable { return SyncStatus(articleID: articleID, key: key, flag: flag, selected: selected) } - + + func selectPendingArticleIDs(_ statusKey: ArticleStatus.Key) async throws -> Set { + return try await withCheckedThrowingContinuation() { continuation in + Task { @MainActor in + selectPendingArticleIDsAsync(statusKey) { result in + switch result { + case .success(let articleIDs): + continuation.resume(returning: articleIDs) + case .failure(let databaseError): + continuation.resume(throwing: databaseError) + } + } + } + } + } + func selectPendingArticleIDsAsync(_ statusKey: ArticleStatus.Key, _ completion: @escaping SyncStatusArticleIDsCompletionBlock) { queue.runInDatabase { databaseResult in