215 lines
7.3 KiB
Swift
215 lines
7.3 KiB
Swift
//
|
|
// AccountServiceHistory.swift
|
|
// Wallet
|
|
//
|
|
// Created by Saveliy Stavitsky on 1/12/21.
|
|
// Copyright © 2021 List. All rights reserved.
|
|
//
|
|
|
|
import RealmSwift
|
|
import Foundation
|
|
import WalletFoundation
|
|
import WalletNetwork
|
|
|
|
let historyFetchTypeIsNode = false
|
|
|
|
extension Notification.Name {
|
|
static let didUpdateHistory = Notification.Name("Account.Service.History.didUpdateHistory")
|
|
}
|
|
|
|
extension Account.Service {
|
|
|
|
final class AccountHistory {
|
|
|
|
private let realm: Realm
|
|
private let username: String
|
|
|
|
init(username: String) throws {
|
|
self.username = username
|
|
|
|
var config = Realm.Configuration()
|
|
config.objectTypes = [AccountAction.self]
|
|
config.deleteRealmIfMigrationNeeded = true
|
|
|
|
config.fileURL >>- { url in
|
|
|
|
let nodeUrl = url
|
|
.deletingLastPathComponent()
|
|
.appendingPathComponent("\(username)_actions.realm")
|
|
let hyperionUrl = url
|
|
.deletingLastPathComponent()
|
|
.appendingPathComponent("\(username)_actions_hyperion.realm")
|
|
|
|
config.fileURL = historyFetchTypeIsNode ? nodeUrl : hyperionUrl
|
|
|
|
let fileManager = FileManager.default
|
|
|
|
if historyFetchTypeIsNode {
|
|
|
|
if !fileManager.fileExists(atPath: nodeUrl.relativePath),
|
|
fileManager.fileExists(atPath: hyperionUrl.relativePath) {
|
|
try? fileManager.moveItem(at: hyperionUrl, to: nodeUrl)
|
|
}
|
|
|
|
} else {
|
|
|
|
if fileManager.fileExists(atPath: nodeUrl.relativePath),
|
|
!fileManager.fileExists(atPath: hyperionUrl.relativePath) {
|
|
try? fileManager.moveItem(at: nodeUrl, to: hyperionUrl)
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.realm = try Realm(configuration: config)
|
|
}
|
|
|
|
var actions: Results<AccountAction> {
|
|
self.realm
|
|
.objects(AccountAction.self)
|
|
.where { $0.username == self.username }
|
|
}
|
|
|
|
func actions(in trxId: String, ascending: Bool = true) -> Results<AccountAction> {
|
|
self.actions
|
|
.filter("trxId = '\(trxId)'")
|
|
.sorted(by: \.accountActionId, ascending: ascending)
|
|
}
|
|
|
|
func fetchUntil(trxId: String, ascending: Bool = true, _ completion: ((Results<AccountAction>) -> Void)? = nil) {
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
|
|
let quantityOfActionsBefore = self.actions.count
|
|
|
|
self.fetch { [weak self] in
|
|
|
|
guard let self else { return }
|
|
|
|
if !self.actions.where({ $0.trxId == trxId }).isEmpty || (self.actions.count == quantityOfActionsBefore) {
|
|
completion?(self.actions(in: trxId, ascending: ascending))
|
|
} else {
|
|
self.fetchUntil(trxId: trxId, completion)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func fetch(_ completion: (() -> Void)? = nil) {
|
|
|
|
self.completions.append(completion)
|
|
|
|
self.fetchHyperion(for: self.username)
|
|
}
|
|
|
|
private(set) var isLoading = false
|
|
|
|
private var loadedThisSession = Set<Int>()
|
|
|
|
// MARK: - Hyperion loading
|
|
private func fetchHyperion(for username: String, skip: Int = 0) {
|
|
|
|
guard !self.isLoading else { return }
|
|
|
|
self.isLoading = true
|
|
|
|
Network.actions.fetch(
|
|
account: username,
|
|
limit: self.loadedThisSession.count == 0 ? 1000 : 100,
|
|
skip: skip > 0 ? skip : self.loadedThisSession.count,
|
|
type: NodeData.self
|
|
) { [weak self] actions, total, error in
|
|
|
|
guard let self else { return }
|
|
|
|
guard !error.isExist else {
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
|
self.isLoading = false
|
|
self.fetchHyperion(for: username, skip: skip)
|
|
}
|
|
return
|
|
|
|
}
|
|
|
|
self.add(
|
|
actions
|
|
.filter {
|
|
let identity = AccountAction.createIdentity(username: self.username,
|
|
actionId: $0.global_sequence)
|
|
return !self.realm.object(ofType: AccountAction.self,
|
|
forPrimaryKey: identity).isExist
|
|
}
|
|
.map { AccountAction(action: $0, receiver: self.username) }
|
|
)
|
|
|
|
if actions.contains(where: { self.loadedThisSession.contains($0.global_sequence) }) || actions.count == 0 {
|
|
|
|
actions
|
|
.map { $0.global_sequence }
|
|
.forEach { self.loadedThisSession.insert($0) }
|
|
|
|
self.isLoading = false
|
|
self.notifyEveryone()
|
|
|
|
} else {
|
|
|
|
let newSkip = skip + actions.count
|
|
|
|
actions
|
|
.map { $0.global_sequence }
|
|
.forEach { self.loadedThisSession.insert($0) }
|
|
|
|
self.isLoading = false
|
|
self.fetchHyperion(for: username, skip: newSkip)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func add(_ actions: [AccountAction]) {
|
|
|
|
guard actions.count > 0 else { return }
|
|
|
|
self.write(block: { realm in
|
|
actions.forEach { realm.add($0, update: .modified) }
|
|
}) { _ in
|
|
// self.notifyEveryone()
|
|
}
|
|
|
|
}
|
|
|
|
private func remove(_ actions: [AccountAction]) {
|
|
|
|
guard actions.count > 0 else { return }
|
|
|
|
self.write(block: { realm in
|
|
actions.forEach { realm.delete($0) }
|
|
}) { _ in
|
|
// self.notifyEveryone()
|
|
}
|
|
}
|
|
|
|
private var completions = [(() -> Void)?]()
|
|
private func notifyEveryone() {
|
|
|
|
let completions = self.completions
|
|
self.completions = []
|
|
|
|
Notification.post(name: .didUpdateHistory, userInfo: ["username": username])
|
|
|
|
completions
|
|
.compactMap { $0 }
|
|
.forEach { $0() }
|
|
}
|
|
|
|
private func write(block: @escaping ((Realm) -> Void), completion: ((Error?) -> Void)? = nil) {
|
|
DispatchQueue.main.async {
|
|
let realm = self.realm
|
|
realm.writeAsync({ block(realm) }, onComplete: completion)
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|