Stop a slow chain refresh from showing the wrong wallet balance
This commit is contained in:
@@ -23,6 +23,9 @@ final class AppModel: ObservableObject {
|
|||||||
private var refreshTask: Task<Void, Never>?
|
private var refreshTask: Task<Void, Never>?
|
||||||
private var lastSample: (height: UInt64, date: Date)?
|
private var lastSample: (height: UInt64, date: Date)?
|
||||||
private var lastCheckpoint = Date.distantPast
|
private var lastCheckpoint = Date.distantPast
|
||||||
|
/// Invalidates in-flight refreshes: switching chains (or starting a newer
|
||||||
|
/// refresh) must stop an older request from writing its result to the UI.
|
||||||
|
private var refreshToken = UUID()
|
||||||
|
|
||||||
var wallet: WalletRecord? {
|
var wallet: WalletRecord? {
|
||||||
chain == .xrp ? xrpWallet : xmrWallet
|
chain == .xrp ? xrpWallet : xmrWallet
|
||||||
@@ -93,6 +96,7 @@ final class AppModel: ObservableObject {
|
|||||||
|
|
||||||
func select(_ newChain: Chain) async {
|
func select(_ newChain: Chain) async {
|
||||||
chain = newChain
|
chain = newChain
|
||||||
|
refreshToken = UUID()
|
||||||
errorMessage = nil
|
errorMessage = nil
|
||||||
statusMessage = nil
|
statusMessage = nil
|
||||||
balance = Balance(total: "0", unlocked: "", funded: false)
|
balance = Balance(total: "0", unlocked: "", funded: false)
|
||||||
@@ -200,14 +204,21 @@ final class AppModel: ObservableObject {
|
|||||||
|
|
||||||
func refresh(force: Bool = false) async {
|
func refresh(force: Bool = false) async {
|
||||||
guard hasWallet else { return }
|
guard hasWallet else { return }
|
||||||
|
let token = UUID()
|
||||||
|
refreshToken = token
|
||||||
|
let requestChain = chain
|
||||||
errorMessage = nil
|
errorMessage = nil
|
||||||
if chain == .xrp, let wallet = xrpWallet {
|
if chain == .xrp, let wallet = xrpWallet {
|
||||||
do {
|
do {
|
||||||
async let balanceCall = XRPWalletService.shared.balance(address: wallet.address)
|
async let balanceCall = XRPWalletService.shared.balance(address: wallet.address)
|
||||||
async let txCall = XRPWalletService.shared.transactions(address: wallet.address)
|
async let txCall = XRPWalletService.shared.transactions(address: wallet.address)
|
||||||
balance = try await balanceCall
|
let fetchedBalance = try await balanceCall
|
||||||
transactions = try await txCall
|
let fetchedTransactions = try await txCall
|
||||||
|
guard !isStale(token, requestChain) else { return }
|
||||||
|
balance = fetchedBalance
|
||||||
|
transactions = fetchedTransactions
|
||||||
} catch {
|
} catch {
|
||||||
|
guard !isStale(token, requestChain) else { return }
|
||||||
errorMessage = error.localizedDescription
|
errorMessage = error.localizedDescription
|
||||||
}
|
}
|
||||||
// The Monero wallet keeps scanning while XRP is on screen, so keep its
|
// The Monero wallet keeps scanning while XRP is on screen, so keep its
|
||||||
@@ -223,22 +234,36 @@ final class AppModel: ObservableObject {
|
|||||||
do {
|
do {
|
||||||
// Sync progress is cheap and polled every tick; the balance and transfer
|
// Sync progress is cheap and polled every tick; the balance and transfer
|
||||||
// list touch the wallet's data files, so they are refreshed less often.
|
// list touch the wallet's data files, so they are refreshed less often.
|
||||||
updateSyncStatus(try await MoneroService.shared.syncStatus())
|
let status = try await MoneroService.shared.syncStatus()
|
||||||
|
if !isStale(token, requestChain) {
|
||||||
|
updateSyncStatus(status)
|
||||||
|
}
|
||||||
refreshTick += 1
|
refreshTick += 1
|
||||||
await checkpointWalletIfSyncing()
|
await checkpointWalletIfSyncing()
|
||||||
if force || refreshTick == 1 || refreshTick % 4 == 0 {
|
if force || refreshTick == 1 || refreshTick % 4 == 0 {
|
||||||
async let balanceCall = MoneroService.shared.balance()
|
async let balanceCall = MoneroService.shared.balance()
|
||||||
async let txCall = MoneroService.shared.transactions()
|
async let txCall = MoneroService.shared.transactions()
|
||||||
async let accountsCall = MoneroService.shared.accounts()
|
async let accountsCall = MoneroService.shared.accounts()
|
||||||
balance = try await balanceCall
|
let fetchedBalance = try await balanceCall
|
||||||
transactions = try await txCall
|
let fetchedTransactions = try await txCall
|
||||||
moneroAccounts = try await accountsCall
|
let fetchedAccounts = try await accountsCall
|
||||||
|
guard !isStale(token, requestChain) else { return }
|
||||||
|
balance = fetchedBalance
|
||||||
|
transactions = fetchedTransactions
|
||||||
|
moneroAccounts = fetchedAccounts
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
guard !isStale(token, requestChain) else { return }
|
||||||
errorMessage = error.localizedDescription
|
errorMessage = error.localizedDescription
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// True when a newer refresh has started or the user moved to another chain
|
||||||
|
/// while this request was in flight.
|
||||||
|
private func isStale(_ token: UUID, _ requestChain: Chain) -> Bool {
|
||||||
|
token != refreshToken || requestChain != chain
|
||||||
|
}
|
||||||
|
|
||||||
/// Tracks how fast blocks are being scanned so the UI can show an estimate.
|
/// Tracks how fast blocks are being scanned so the UI can show an estimate.
|
||||||
private func updateSyncStatus(_ status: SyncStatus) {
|
private func updateSyncStatus(_ status: SyncStatus) {
|
||||||
syncStatus = status
|
syncStatus = status
|
||||||
|
|||||||
Reference in New Issue
Block a user