attempt to fix test txs
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
/// [ChangeNotifier] for bank link status, balance, and transactions.
|
||||
library;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import '../models/bank.dart';
|
||||
@@ -36,6 +38,18 @@ class BankController extends ChangeNotifier {
|
||||
_remoteTransactions = [
|
||||
for (final tx in _remoteTransactions) _upgradeLegacyManual(tx),
|
||||
];
|
||||
_pendingRemoteDeletes = _storage?.loadPendingTxDeletes() ?? const [];
|
||||
// Drop anything still queued for remote delete from the local caches.
|
||||
if (_pendingRemoteDeletes.isNotEmpty) {
|
||||
_remoteTransactions = [
|
||||
for (final tx in _remoteTransactions)
|
||||
if (!_isTombstoned(tx)) tx,
|
||||
];
|
||||
_importedTransactions = [
|
||||
for (final tx in _importedTransactions)
|
||||
if (!_isTombstoned(tx)) tx,
|
||||
];
|
||||
}
|
||||
_invalidateMerged();
|
||||
}
|
||||
|
||||
@@ -55,29 +69,66 @@ class BankController extends ChangeNotifier {
|
||||
/// cannot leave the device behind the cloud (or wipe cloud-only history).
|
||||
VoidCallback? onAfterSuccessfulLoad;
|
||||
|
||||
/// Fired when local admin txs are removed/replaced and need a remote delete.
|
||||
VoidCallback? onPendingRemoteDeletesChanged;
|
||||
|
||||
ConnectStatus _status = ConnectStatus.disconnected;
|
||||
BankBalance? _balance;
|
||||
List<BankTransaction> _remoteTransactions = const [];
|
||||
List<BankTransaction> _importedTransactions = const [];
|
||||
List<String> _pendingRemoteDeletes = const [];
|
||||
List<BankTransaction> _pendingRemoteDeletes = const [];
|
||||
|
||||
/// Local tx_ids removed/replaced that should be deleted on the next PB push.
|
||||
List<String> takePendingRemoteDeletes() {
|
||||
final out = List<String>.of(_pendingRemoteDeletes);
|
||||
_pendingRemoteDeletes = const [];
|
||||
return out;
|
||||
}
|
||||
/// Tombstoned txs waiting to be removed from PocketBase (also persisted).
|
||||
List<BankTransaction> get pendingRemoteDeletes =>
|
||||
List<BankTransaction>.unmodifiable(_pendingRemoteDeletes);
|
||||
|
||||
void requeueRemoteDeletes(Iterable<String> txIds) {
|
||||
for (final id in txIds) {
|
||||
_queueRemoteDelete(id);
|
||||
bool get hasPendingRemoteDeletes => _pendingRemoteDeletes.isNotEmpty;
|
||||
|
||||
bool _isTombstoned(BankTransaction tx) {
|
||||
for (final tomb in _pendingRemoteDeletes) {
|
||||
if (tx.matchesForDelete(tomb)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void _queueRemoteDelete(String txId) {
|
||||
if (txId.isEmpty) return;
|
||||
if (_pendingRemoteDeletes.contains(txId)) return;
|
||||
_pendingRemoteDeletes = [..._pendingRemoteDeletes, txId];
|
||||
/// Drop tombstones that match any of [txs] (fingerprint-aware).
|
||||
void clearRemoteDeletesMatching(Iterable<BankTransaction> txs) {
|
||||
final list = txs.toList(growable: false);
|
||||
if (list.isEmpty) return;
|
||||
final next = [
|
||||
for (final tomb in _pendingRemoteDeletes)
|
||||
if (!list.any(tomb.matchesForDelete)) tomb,
|
||||
];
|
||||
if (next.length == _pendingRemoteDeletes.length) return;
|
||||
_pendingRemoteDeletes = next;
|
||||
unawaited(_persistPendingDeletes());
|
||||
}
|
||||
|
||||
void clearAllPendingRemoteDeletes() {
|
||||
if (_pendingRemoteDeletes.isEmpty) return;
|
||||
_pendingRemoteDeletes = const [];
|
||||
unawaited(_persistPendingDeletes());
|
||||
}
|
||||
|
||||
void requeueRemoteDeletes(Iterable<BankTransaction> txs) {
|
||||
for (final tx in txs) {
|
||||
_queueRemoteDelete(tx, notify: false);
|
||||
}
|
||||
unawaited(_persistPendingDeletes());
|
||||
}
|
||||
|
||||
void _queueRemoteDelete(BankTransaction tx, {bool notify = true}) {
|
||||
if (tx.id.isEmpty) return;
|
||||
if (_isTombstoned(tx)) return;
|
||||
_pendingRemoteDeletes = [..._pendingRemoteDeletes, tx];
|
||||
unawaited(_persistPendingDeletes());
|
||||
if (notify) onPendingRemoteDeletesChanged?.call();
|
||||
}
|
||||
|
||||
Future<void> _persistPendingDeletes() async {
|
||||
final storage = _storage;
|
||||
if (storage == null) return;
|
||||
await storage.savePendingTxDeletes(_pendingRemoteDeletes);
|
||||
}
|
||||
|
||||
/// Keep [isManual] when a synced/API row replaces a matching imported test tx.
|
||||
@@ -300,7 +351,7 @@ class BankController extends ChangeNotifier {
|
||||
if (tx.id != previous.id) tx,
|
||||
];
|
||||
if (previous.id != updated.id) {
|
||||
_queueRemoteDelete(previous.id);
|
||||
_queueRemoteDelete(previous);
|
||||
}
|
||||
_importedTransactions = mergeTransactions(_importedTransactions, [updated]);
|
||||
_invalidateMerged();
|
||||
@@ -317,13 +368,13 @@ class BankController extends ChangeNotifier {
|
||||
}
|
||||
_importedTransactions = [
|
||||
for (final row in _importedTransactions)
|
||||
if (row.id != tx.id) row,
|
||||
if (!row.matchesForDelete(tx)) row,
|
||||
];
|
||||
_remoteTransactions = [
|
||||
for (final row in _remoteTransactions)
|
||||
if (row.id != tx.id) row,
|
||||
if (!row.matchesForDelete(tx)) row,
|
||||
];
|
||||
_queueRemoteDelete(tx.id);
|
||||
_queueRemoteDelete(tx);
|
||||
_invalidateMerged();
|
||||
await _persistImported();
|
||||
await _persistCache();
|
||||
@@ -337,17 +388,36 @@ class BankController extends ChangeNotifier {
|
||||
List<BankTransaction> fromCloud, {
|
||||
bool notify = true,
|
||||
}) async {
|
||||
if (fromCloud.isEmpty) return;
|
||||
if (fromCloud.isEmpty && _pendingRemoteDeletes.isEmpty) return;
|
||||
|
||||
final manuals = _collectManualIds(fromCloud.map(_upgradeLegacyManual));
|
||||
_remoteTransactions = mergeTransactions(
|
||||
_remoteTransactions,
|
||||
[for (final tx in fromCloud) _upgradeLegacyManual(tx)],
|
||||
);
|
||||
// Never resurrect rows the user already deleted locally.
|
||||
final incoming = [
|
||||
for (final tx in fromCloud)
|
||||
if (!_isTombstoned(tx)) _upgradeLegacyManual(tx),
|
||||
];
|
||||
if (incoming.isEmpty && _pendingRemoteDeletes.isEmpty) return;
|
||||
|
||||
final manuals = _collectManualIds(incoming);
|
||||
if (incoming.isNotEmpty) {
|
||||
_remoteTransactions = mergeTransactions(_remoteTransactions, incoming);
|
||||
}
|
||||
// Strip any local copies that match a pending delete (defensive).
|
||||
if (_pendingRemoteDeletes.isNotEmpty) {
|
||||
_remoteTransactions = [
|
||||
for (final tx in _remoteTransactions)
|
||||
if (!_isTombstoned(tx)) tx,
|
||||
];
|
||||
}
|
||||
_importedTransactions = filterNewTransactions(
|
||||
_remoteTransactions,
|
||||
_importedTransactions,
|
||||
);
|
||||
if (_pendingRemoteDeletes.isNotEmpty) {
|
||||
_importedTransactions = [
|
||||
for (final tx in _importedTransactions)
|
||||
if (!_isTombstoned(tx)) tx,
|
||||
];
|
||||
}
|
||||
_reapplyManualFlags(manuals);
|
||||
_invalidateMerged();
|
||||
|
||||
@@ -444,6 +514,16 @@ class BankController extends ChangeNotifier {
|
||||
_remoteTransactions,
|
||||
_importedTransactions,
|
||||
);
|
||||
if (_pendingRemoteDeletes.isNotEmpty) {
|
||||
_remoteTransactions = [
|
||||
for (final tx in _remoteTransactions)
|
||||
if (!_isTombstoned(tx)) tx,
|
||||
];
|
||||
_importedTransactions = [
|
||||
for (final tx in _importedTransactions)
|
||||
if (!_isTombstoned(tx)) tx,
|
||||
];
|
||||
}
|
||||
_reapplyManualFlags(manuals);
|
||||
_invalidateMerged();
|
||||
await _persistImported();
|
||||
|
||||
Reference in New Issue
Block a user