Add send sync database contents to Feedbin
This commit is contained in:
parent
f5f8d67411
commit
6be6c6a682
@ -286,10 +286,18 @@ public final class Account: DisplayNameProvider, UnreadCountProvider, Container,
|
||||
}
|
||||
}
|
||||
|
||||
public func refreshAll(completionHandler completion: (() -> Void)? = nil) {
|
||||
delegate.refreshAll(for: self, completion: completion)
|
||||
public func refreshAll(completion: (() -> Void)? = nil) {
|
||||
self.delegate.refreshAll(for: self, completion: completion)
|
||||
}
|
||||
|
||||
public func syncArticleStatus(completion: (() -> Void)? = nil) {
|
||||
delegate.sendArticleStatus(for: self) { [unowned self] in
|
||||
self.delegate.refreshArticleStatus(for: self) {
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func importOPML(_ opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
delegate.importOPML(for: self, opmlFile: opmlFile) { [weak self] result in
|
||||
switch result {
|
||||
|
@ -31,6 +31,8 @@
|
||||
51D5875C227F630B00900287 /* tags_initial.json in Resources */ = {isa = PBXBuildFile; fileRef = 51D58759227F630B00900287 /* tags_initial.json */; };
|
||||
51D5875E227F643C00900287 /* AccountFolderSyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51D5875D227F643C00900287 /* AccountFolderSyncTest.swift */; };
|
||||
51E490362288C37100C791F0 /* FeedbinDate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E490352288C37100C791F0 /* FeedbinDate.swift */; };
|
||||
51E59599228C77BC00FCC42B /* FeedbinUnreadEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E59598228C77BC00FCC42B /* FeedbinUnreadEntry.swift */; };
|
||||
51E5959B228C781500FCC42B /* FeedbinStarredEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E5959A228C781500FCC42B /* FeedbinStarredEntry.swift */; };
|
||||
841973FE1F6DD1BC006346C4 /* RSCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841973EF1F6DD19E006346C4 /* RSCore.framework */; };
|
||||
841973FF1F6DD1C5006346C4 /* RSParser.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 841973FA1F6DD1AC006346C4 /* RSParser.framework */; };
|
||||
841974011F6DD1EC006346C4 /* Folder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 841974001F6DD1EC006346C4 /* Folder.swift */; };
|
||||
@ -128,6 +130,8 @@
|
||||
51D58759227F630B00900287 /* tags_initial.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = tags_initial.json; sourceTree = "<group>"; };
|
||||
51D5875D227F643C00900287 /* AccountFolderSyncTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountFolderSyncTest.swift; sourceTree = "<group>"; };
|
||||
51E490352288C37100C791F0 /* FeedbinDate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinDate.swift; sourceTree = "<group>"; };
|
||||
51E59598228C77BC00FCC42B /* FeedbinUnreadEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinUnreadEntry.swift; sourceTree = "<group>"; };
|
||||
51E5959A228C781500FCC42B /* FeedbinStarredEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinStarredEntry.swift; sourceTree = "<group>"; };
|
||||
841973E81F6DD19E006346C4 /* RSCore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSCore.xcodeproj; path = ../RSCore/RSCore.xcodeproj; sourceTree = "<group>"; };
|
||||
841973F41F6DD1AC006346C4 /* RSParser.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RSParser.xcodeproj; path = ../RSParser/RSParser.xcodeproj; sourceTree = "<group>"; };
|
||||
841974001F6DD1EC006346C4 /* Folder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Folder.swift; sourceTree = "<group>"; };
|
||||
@ -251,9 +255,11 @@
|
||||
51E490352288C37100C791F0 /* FeedbinDate.swift */,
|
||||
84CAD7151FDF2E22000F0755 /* FeedbinEntry.swift */,
|
||||
5133230F22810E5700C30F19 /* FeedbinIcon.swift */,
|
||||
51E5959A228C781500FCC42B /* FeedbinStarredEntry.swift */,
|
||||
84245C841FDDD8CB0074AFBB /* FeedbinSubscription.swift */,
|
||||
51D58754227F53BE00900287 /* FeedbinTag.swift */,
|
||||
84D09622217418DC00D77525 /* FeedbinTagging.swift */,
|
||||
51E59598228C77BC00FCC42B /* FeedbinUnreadEntry.swift */,
|
||||
);
|
||||
path = Feedbin;
|
||||
sourceTree = "<group>";
|
||||
@ -506,6 +512,7 @@
|
||||
84C3654A1F899F3B001EC85C /* CombinedRefreshProgress.swift in Sources */,
|
||||
8469F81C1F6DD15E0084783E /* Account.swift in Sources */,
|
||||
5144EA4E227B829A00D19003 /* FeedbinAccountDelegate.swift in Sources */,
|
||||
51E5959B228C781500FCC42B /* FeedbinStarredEntry.swift in Sources */,
|
||||
846E77451F6EF9B900A165E2 /* Container.swift in Sources */,
|
||||
84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */,
|
||||
841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */,
|
||||
@ -519,6 +526,7 @@
|
||||
84B99C9F1FAE8D3200ECDEDB /* ContainerPath.swift in Sources */,
|
||||
5133231122810EB200C30F19 /* FeedbinIcon.swift in Sources */,
|
||||
846E77501F6EF9C400A165E2 /* LocalAccountRefresher.swift in Sources */,
|
||||
51E59599228C77BC00FCC42B /* FeedbinUnreadEntry.swift in Sources */,
|
||||
5165D72822835F7800D9D53D /* FeedFinder.swift in Sources */,
|
||||
51D58755227F53BE00900287 /* FeedbinTag.swift in Sources */,
|
||||
84D09623217418DC00D77525 /* FeedbinTagging.swift in Sources */,
|
||||
|
@ -21,6 +21,9 @@ protocol AccountDelegate {
|
||||
var refreshProgress: DownloadProgress { get }
|
||||
|
||||
func refreshAll(for account: Account, completion: (() -> Void)?)
|
||||
func sendArticleStatus(for account: Account, completion: @escaping (() -> Void))
|
||||
func refreshArticleStatus(for account: Account, completion: @escaping (() -> Void))
|
||||
|
||||
func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
|
||||
func renameFolder(for account: Account, with folder: Folder, to name: String, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
|
@ -42,7 +42,7 @@ final class FeedbinAPICaller: NSObject {
|
||||
self.transport = transport
|
||||
}
|
||||
|
||||
func validateCredentials(completionHandler completion: @escaping (Result<Bool, Error>) -> Void) {
|
||||
func validateCredentials(completion: @escaping (Result<Bool, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("authentication.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
@ -67,7 +67,7 @@ final class FeedbinAPICaller: NSObject {
|
||||
|
||||
}
|
||||
|
||||
func retrieveTags(completionHandler completion: @escaping (Result<[FeedbinTag]?, Error>) -> Void) {
|
||||
func retrieveTags(completion: @escaping (Result<[FeedbinTag]?, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("tags.json")
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.tags]
|
||||
@ -113,7 +113,7 @@ final class FeedbinAPICaller: NSObject {
|
||||
|
||||
}
|
||||
|
||||
func retrieveSubscriptions(completionHandler completion: @escaping (Result<[FeedbinSubscription]?, Error>) -> Void) {
|
||||
func retrieveSubscriptions(completion: @escaping (Result<[FeedbinSubscription]?, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("subscriptions.json")
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.subscriptions]
|
||||
@ -133,7 +133,7 @@ final class FeedbinAPICaller: NSObject {
|
||||
|
||||
}
|
||||
|
||||
func createSubscription(url: String, completionHandler completion: @escaping (Result<CreateSubscriptionResult, Error>) -> Void) {
|
||||
func createSubscription(url: String, completion: @escaping (Result<CreateSubscriptionResult, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("subscriptions.json")
|
||||
var request = URLRequest(url: callURL, credentials: credentials)
|
||||
@ -218,7 +218,7 @@ final class FeedbinAPICaller: NSObject {
|
||||
transport.send(request: request, method: HTTPMethod.delete, completion: completion)
|
||||
}
|
||||
|
||||
func retrieveTaggings(completionHandler completion: @escaping (Result<[FeedbinTagging]?, Error>) -> Void) {
|
||||
func retrieveTaggings(completion: @escaping (Result<[FeedbinTagging]?, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("taggings.json")
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.taggings]
|
||||
@ -279,7 +279,7 @@ final class FeedbinAPICaller: NSObject {
|
||||
transport.send(request: request, method: HTTPMethod.delete, completion: completion)
|
||||
}
|
||||
|
||||
func retrieveIcons(completionHandler completion: @escaping (Result<[FeedbinIcon]?, Error>) -> Void) {
|
||||
func retrieveIcons(completion: @escaping (Result<[FeedbinIcon]?, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("icons.json")
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.icons]
|
||||
@ -403,7 +403,7 @@ final class FeedbinAPICaller: NSObject {
|
||||
|
||||
}
|
||||
|
||||
func retrieveUnreadEntries(completionHandler completion: @escaping (Result<[Int]?, Error>) -> Void) {
|
||||
func retrieveUnreadEntries(completion: @escaping (Result<[Int]?, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json")
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.unreadEntries]
|
||||
@ -423,7 +423,21 @@ final class FeedbinAPICaller: NSObject {
|
||||
|
||||
}
|
||||
|
||||
func retrieveStarredEntries(completionHandler completion: @escaping (Result<[Int]?, Error>) -> Void) {
|
||||
func createUnreadEntries(entries: [Int], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinUnreadEntry(unreadEntries: entries)
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload, completion: completion)
|
||||
}
|
||||
|
||||
func deleteUnreadEntries(entries: [Int], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("unread_entries.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinUnreadEntry(unreadEntries: entries)
|
||||
transport.send(request: request, method: HTTPMethod.delete, payload: payload, completion: completion)
|
||||
}
|
||||
|
||||
func retrieveStarredEntries(completion: @escaping (Result<[Int]?, Error>) -> Void) {
|
||||
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json")
|
||||
let conditionalGet = accountMetadata?.conditionalGetInfo[ConditionalGetKeys.starredEntries]
|
||||
@ -443,6 +457,20 @@ final class FeedbinAPICaller: NSObject {
|
||||
|
||||
}
|
||||
|
||||
func createStarredEntries(entries: [Int], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinStarredEntry(starredEntries: entries)
|
||||
transport.send(request: request, method: HTTPMethod.post, payload: payload, completion: completion)
|
||||
}
|
||||
|
||||
func deleteStarredEntries(entries: [Int], completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
let callURL = feedbinBaseURL.appendingPathComponent("starred_entries.json")
|
||||
let request = URLRequest(url: callURL, credentials: credentials)
|
||||
let payload = FeedbinStarredEntry(starredEntries: entries)
|
||||
transport.send(request: request, method: HTTPMethod.delete, payload: payload, completion: completion)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
@ -104,8 +104,48 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
||||
|
||||
}
|
||||
|
||||
func refreshArticleStatus(for account: Account, completion: (() -> Void)? = nil) {
|
||||
func sendArticleStatus(for account: Account, completion: @escaping (() -> Void)) {
|
||||
|
||||
os_log(.debug, log: log, "Sending article statuses...")
|
||||
|
||||
let syncStatuses = database.selectForProcessing()
|
||||
let createUnreadStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.read && $0.flag == false }
|
||||
let deleteUnreadStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.read && $0.flag == true }
|
||||
let createStarredStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.starred && $0.flag == true }
|
||||
let deleteStarredStatuses = syncStatuses.filter { $0.key == ArticleStatus.Key.starred && $0.flag == false }
|
||||
|
||||
let group = DispatchGroup()
|
||||
|
||||
group.enter()
|
||||
sendArticleStatuses(createUnreadStatuses, apiCall: caller.createUnreadEntries) {
|
||||
group.leave()
|
||||
}
|
||||
|
||||
group.enter()
|
||||
sendArticleStatuses(deleteUnreadStatuses, apiCall: caller.deleteUnreadEntries) {
|
||||
group.leave()
|
||||
}
|
||||
|
||||
group.enter()
|
||||
sendArticleStatuses(createStarredStatuses, apiCall: caller.createStarredEntries) {
|
||||
group.leave()
|
||||
}
|
||||
|
||||
group.enter()
|
||||
sendArticleStatuses(deleteStarredStatuses, apiCall: caller.deleteStarredEntries) {
|
||||
group.leave()
|
||||
}
|
||||
|
||||
group.notify(queue: DispatchQueue.main) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
os_log(.debug, log: self.log, "Done sending article statuses.")
|
||||
completion()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func refreshArticleStatus(for account: Account, completion: @escaping (() -> Void)) {
|
||||
|
||||
os_log(.debug, log: log, "Refreshing article statuses...")
|
||||
|
||||
let group = DispatchGroup()
|
||||
@ -139,7 +179,7 @@ final class FeedbinAccountDelegate: AccountDelegate {
|
||||
group.notify(queue: DispatchQueue.main) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
os_log(.debug, log: self.log, "Done refreshing article statuses.")
|
||||
completion?()
|
||||
completion()
|
||||
}
|
||||
|
||||
}
|
||||
@ -701,6 +741,36 @@ private extension FeedbinAccountDelegate {
|
||||
|
||||
}
|
||||
|
||||
|
||||
func sendArticleStatuses(_ statuses: [SyncStatus],
|
||||
apiCall: ([Int], @escaping (Result<Void, Error>) -> Void) -> Void,
|
||||
completion: @escaping (() -> Void)) {
|
||||
|
||||
guard !statuses.isEmpty else {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
|
||||
let articleIDs = statuses.compactMap { Int($0.articleID) }
|
||||
let articleIDGroups = articleIDs.chunked(into: 1000)
|
||||
for articleIDGroup in articleIDGroups {
|
||||
|
||||
apiCall(articleIDGroup) { [weak self] result in
|
||||
switch result {
|
||||
case .success:
|
||||
self?.database.deleteSelectedForProcessing(articleIDGroup.map { String($0) } )
|
||||
completion()
|
||||
case .failure(let error):
|
||||
guard let self = self else { return }
|
||||
os_log(.error, log: self.log, "Article status sync call failed: %@.", error.localizedDescription)
|
||||
self.database.resetSelectedForProcessing(articleIDGroup.map { String($0) } )
|
||||
completion()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func importOPMLItems(_ account: Account, items: [RSOPMLItem], parentFolder: Folder?) {
|
||||
|
||||
items.forEach { (item) in
|
||||
@ -1058,14 +1128,14 @@ private extension FeedbinAccountDelegate {
|
||||
let deltaUnreadArticleIDs = feedbinUnreadArticleIDs.subtracting(currentUnreadArticleIDs)
|
||||
let markUnreadArticles = account.fetchArticles(forArticleIDs: deltaUnreadArticleIDs)
|
||||
DispatchQueue.main.async {
|
||||
_ = account.markArticles(markUnreadArticles, statusKey: .read, flag: false)
|
||||
_ = account.update(markUnreadArticles, statusKey: .read, flag: false)
|
||||
}
|
||||
|
||||
// Mark articles as read
|
||||
let deltaReadArticleIDs = currentUnreadArticleIDs.subtracting(feedbinUnreadArticleIDs)
|
||||
let markReadArticles = account.fetchArticles(forArticleIDs: deltaReadArticleIDs)
|
||||
DispatchQueue.main.async {
|
||||
_ = account.markArticles(markReadArticles, statusKey: .read, flag: true)
|
||||
_ = account.update(markReadArticles, statusKey: .read, flag: true)
|
||||
}
|
||||
|
||||
// Save any unread statuses for articles we haven't yet received
|
||||
@ -1092,14 +1162,14 @@ private extension FeedbinAccountDelegate {
|
||||
let deltaStarredArticleIDs = feedbinStarredArticleIDs.subtracting(currentStarredArticleIDs)
|
||||
let markStarredArticles = account.fetchArticles(forArticleIDs: deltaStarredArticleIDs)
|
||||
DispatchQueue.main.async {
|
||||
_ = account.markArticles(markStarredArticles, statusKey: .starred, flag: true)
|
||||
_ = account.update(markStarredArticles, statusKey: .starred, flag: true)
|
||||
}
|
||||
|
||||
// Mark articles as unstarred
|
||||
let deltaUnstarredArticleIDs = currentStarredArticleIDs.subtracting(feedbinStarredArticleIDs)
|
||||
let markUnstarredArticles = account.fetchArticles(forArticleIDs: deltaUnstarredArticleIDs)
|
||||
DispatchQueue.main.async {
|
||||
_ = account.markArticles(markUnstarredArticles, statusKey: .starred, flag: false)
|
||||
_ = account.update(markUnstarredArticles, statusKey: .starred, flag: false)
|
||||
}
|
||||
|
||||
// Save any starred statuses for articles we haven't yet received
|
||||
|
19
Frameworks/Account/Feedbin/FeedbinStarredEntry.swift
Normal file
19
Frameworks/Account/Feedbin/FeedbinStarredEntry.swift
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// FeedbinStarredEntry.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Maurice Parker on 5/15/19.
|
||||
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FeedbinStarredEntry: Codable {
|
||||
|
||||
let starredEntries: [Int]
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case starredEntries = "starred_entries"
|
||||
}
|
||||
|
||||
}
|
19
Frameworks/Account/Feedbin/FeedbinUnreadEntry.swift
Normal file
19
Frameworks/Account/Feedbin/FeedbinUnreadEntry.swift
Normal file
@ -0,0 +1,19 @@
|
||||
//
|
||||
// FeedbinUnreadEntry.swift
|
||||
// Account
|
||||
//
|
||||
// Created by Maurice Parker on 5/15/19.
|
||||
// Copyright © 2019 Ranchero Software, LLC. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FeedbinUnreadEntry: Codable {
|
||||
|
||||
let unreadEntries: [Int]
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case unreadEntries = "unread_entries"
|
||||
}
|
||||
|
||||
}
|
@ -37,6 +37,14 @@ final class LocalAccountDelegate: AccountDelegate {
|
||||
refresher.refreshFeeds(account.flattenedFeeds())
|
||||
completion?()
|
||||
}
|
||||
|
||||
func sendArticleStatus(for account: Account, completion: @escaping (() -> Void)) {
|
||||
completion()
|
||||
}
|
||||
|
||||
func refreshArticleStatus(for account: Account, completion: @escaping (() -> Void)) {
|
||||
completion()
|
||||
}
|
||||
|
||||
func importOPML(for account:Account, opmlFile: URL, completion: @escaping (Result<Void, Error>) -> Void) {
|
||||
|
||||
|
@ -27,6 +27,18 @@ public final class SyncDatabase {
|
||||
syncStatusTable.insertStatuses(statuses)
|
||||
}
|
||||
|
||||
public func selectForProcessing() -> [SyncStatus] {
|
||||
return syncStatusTable.selectForProcessing()
|
||||
}
|
||||
|
||||
public func resetSelectedForProcessing(_ articleIDs: [String]) {
|
||||
syncStatusTable.resetSelectedForProcessing(articleIDs)
|
||||
}
|
||||
|
||||
public func deleteSelectedForProcessing(_ articleIDs: [String]) {
|
||||
syncStatusTable.deleteSelectedForProcessing(articleIDs)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
@ -10,7 +10,7 @@ import Foundation
|
||||
import Articles
|
||||
import RSDatabase
|
||||
|
||||
public struct SyncStatus {
|
||||
public struct SyncStatus: Hashable, Equatable {
|
||||
|
||||
public let articleID: String
|
||||
public let key: ArticleStatus.Key
|
||||
@ -24,7 +24,7 @@ public struct SyncStatus {
|
||||
self.selected = selected
|
||||
}
|
||||
|
||||
public func databaseDictionary() -> DatabaseDictionary? {
|
||||
public func databaseDictionary() -> DatabaseDictionary {
|
||||
return [DatabaseKey.articleID: articleID, DatabaseKey.key: key.rawValue, DatabaseKey.flag: flag, DatabaseKey.selected: selected]
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Articles
|
||||
import RSDatabase
|
||||
|
||||
final class SyncStatusTable: DatabaseTable {
|
||||
@ -18,11 +19,67 @@ final class SyncStatusTable: DatabaseTable {
|
||||
self.queue = queue
|
||||
}
|
||||
|
||||
func selectForProcessing() -> [SyncStatus] {
|
||||
|
||||
var statuses: Set<SyncStatus>? = nil
|
||||
|
||||
self.queue.updateSync { database in
|
||||
|
||||
let updateSQL = "update syncStatus set selected = true"
|
||||
database.executeUpdate(updateSQL, withArgumentsIn: nil)
|
||||
|
||||
let selectSQL = "select * from syncStatus where selected == true"
|
||||
if let resultSet = database.executeQuery(selectSQL, withArgumentsIn: nil) {
|
||||
statuses = resultSet.mapToSet(self.statusWithRow)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return statuses != nil ? Array(statuses!) : [SyncStatus]()
|
||||
|
||||
}
|
||||
|
||||
func resetSelectedForProcessing(_ articleIDs: [String]) {
|
||||
self.queue.update { database in
|
||||
let parameters = articleIDs.map { $0 as AnyObject }
|
||||
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(articleIDs.count))!
|
||||
let updateSQL = "update syncStatus set selected = false where articleID in \(placeholders)"
|
||||
database.executeUpdate(updateSQL, withArgumentsIn: parameters)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteSelectedForProcessing(_ articleIDs: [String]) {
|
||||
self.queue.update { database in
|
||||
let parameters = articleIDs.map { $0 as AnyObject }
|
||||
let placeholders = NSString.rs_SQLValueList(withPlaceholders: UInt(articleIDs.count))!
|
||||
let deleteSQL = "delete from syncStatus where articleID in \(placeholders)"
|
||||
database.executeUpdate(deleteSQL, withArgumentsIn: parameters)
|
||||
}
|
||||
}
|
||||
|
||||
func insertStatuses(_ statuses: [SyncStatus]) {
|
||||
self.queue.update { database in
|
||||
let statusArray = statuses.map { $0.databaseDictionary()! }
|
||||
let statusArray = statuses.map { $0.databaseDictionary() }
|
||||
self.insertRows(statusArray, insertType: .orReplace, in: database)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private extension SyncStatusTable {
|
||||
|
||||
func statusWithRow(_ row: FMResultSet) -> SyncStatus? {
|
||||
|
||||
guard let articleID = row.string(forColumn: DatabaseKey.articleID),
|
||||
let rawKey = row.string(forColumn: DatabaseKey.key),
|
||||
let key = ArticleStatus.Key(rawValue: rawKey) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let flag = row.bool(forColumn: DatabaseKey.flag)
|
||||
let selected = row.bool(forColumn: DatabaseKey.selected)
|
||||
|
||||
return SyncStatus(articleID: articleID, key: key, flag: flag, selected: selected)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 9c268f00e93f758a79dae04dd8f18d27449221b0
|
||||
Subproject commit 4682329b70eba64f94500885307f99ed8cbcc938
|
Loading…
x
Reference in New Issue
Block a user