look ahead

This commit is contained in:
davmar
2026-09-10 00:35:54 +02:00
parent 91d6dc9e54
commit 5b22fc8694
20 changed files with 2942 additions and 799 deletions
+54 -9
View File
@@ -51,6 +51,8 @@ class SyncController extends ChangeNotifier {
bool _queuedFullSync = false;
Timer? _pushDebounce;
Timer? _progressHold;
Timer? _uiNotifyThrottle;
var _uiNotifyQueued = false;
String? _lastError;
DateTime? _lastSyncedAt;
String _lastSyncSummary = '';
@@ -64,6 +66,7 @@ class SyncController extends ChangeNotifier {
double _syncProgress = 0;
bool _showProgressRing = false;
bool _progressSuccess = false;
int _lastNotifiedProgressPct = -1;
bool get isSyncing => _syncing;
String? get lastError => _lastError;
@@ -86,6 +89,25 @@ class SyncController extends ChangeNotifier {
/// Ring turns green when this is true (sync finished successfully).
bool get progressSuccess => _progressSuccess;
void _notifyUiNow() {
_uiNotifyThrottle?.cancel();
_uiNotifyQueued = false;
_lastNotifiedProgressPct = (_syncProgress * 100).round();
notifyListeners();
}
/// Coalesce high-frequency progress/status into ~10 Hz UI updates.
void _notifyUiThrottled() {
if (_uiNotifyQueued) return;
_uiNotifyQueued = true;
_uiNotifyThrottle?.cancel();
_uiNotifyThrottle = Timer(const Duration(milliseconds: 100), () {
_uiNotifyQueued = false;
_lastNotifiedProgressPct = (_syncProgress * 100).round();
notifyListeners();
});
}
void _announce(String message, {bool isProgress = false, bool isError = false}) {
_feedbackEpoch++;
_feedback = SyncFeedback(
@@ -94,24 +116,38 @@ class SyncController extends ChangeNotifier {
isProgress: isProgress,
isError: isError,
);
notifyListeners();
_notifyUiNow();
}
void _setProgress(double value, {bool success = false}) {
_syncProgress = value.clamp(0.0, 1.0);
final next = value.clamp(0.0, 1.0);
final pct = (next * 100).round();
_syncProgress = next;
_showProgressRing = true;
_progressSuccess = success;
notifyListeners();
// Skip tiny progress moves — avatar only shows whole percents.
if (!success &&
pct == _lastNotifiedProgressPct &&
_showProgressRing) {
return;
}
if (success) {
_notifyUiNow();
} else {
_notifyUiThrottled();
}
}
void _setStatus(String message) {
if (_statusDetail == message) return;
_statusDetail = message;
notifyListeners();
_notifyUiThrottled();
}
void _finishProgress({required bool success}) {
_progressHold?.cancel();
_uiNotifyThrottle?.cancel();
_uiNotifyQueued = false;
_statusDetail = '';
if (success) {
_setProgress(1, success: true);
@@ -119,13 +155,15 @@ class SyncController extends ChangeNotifier {
_showProgressRing = false;
_progressSuccess = false;
_syncProgress = 0;
notifyListeners();
_lastNotifiedProgressPct = -1;
_notifyUiNow();
});
} else {
_showProgressRing = false;
_progressSuccess = false;
_syncProgress = 0;
notifyListeners();
_lastNotifiedProgressPct = -1;
_notifyUiNow();
}
}
@@ -138,12 +176,15 @@ class SyncController extends ChangeNotifier {
if (!loggedIn) {
_pushDebounce?.cancel();
_progressHold?.cancel();
_uiNotifyThrottle?.cancel();
_uiNotifyQueued = false;
_lastError = null;
_statusDetail = '';
_showProgressRing = false;
_progressSuccess = false;
_syncProgress = 0;
notifyListeners();
_lastNotifiedProgressPct = -1;
_notifyUiNow();
}
}
@@ -191,8 +232,11 @@ class SyncController extends ChangeNotifier {
_setProgress(0.5);
_setStatus('Merging…');
final mergedMeta = mergeTxMetaStores(_meta.exportStore(), remoteMeta);
await _meta.replaceStore(mergedMeta);
await _bank.applySyncedTransactions(remoteTxs);
// Apply without notifying — one rebuild after both stores are ready.
await _meta.replaceStore(mergedMeta, notify: false);
await _bank.applySyncedTransactions(remoteTxs, notify: false);
_meta.notifyListeners();
_bank.notifyListeners();
_setProgress(0.55);
_setStatus('Pushing…');
final metaSummary = await service.push(
@@ -283,6 +327,7 @@ class SyncController extends ChangeNotifier {
void dispose() {
_pushDebounce?.cancel();
_progressHold?.cancel();
_uiNotifyThrottle?.cancel();
_auth.removeListener(_onAuthChanged);
_meta.onPersisted = null;
super.dispose();