diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 816833cc15..7a0fecd3e7 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -2657,9 +2657,8 @@ abstract class ElectrumWalletBase break; case electrum.ConnectionStatus.disconnected: - if (syncStatus is! NotConnectedSyncStatus && - syncStatus is! ConnectingSyncStatus && - syncStatus is! SyncronizingSyncStatus) { + // Always show disconnected status when connection is lost, regardless of current sync state + if (syncStatus is! NotConnectedSyncStatus) { syncStatus = NotConnectedSyncStatus(); } break; diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index 0126ea7f6d..e85db9a0f9 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -21,6 +21,7 @@ class StartingScanSyncStatus extends SyncStatus { class SyncingSyncStatus extends SyncStatus { SyncingSyncStatus(this.blocksLeft, this.ptc) { updateEtaHistory(blocksLeft); + _globalSyncStartTime ??= DateTime.now(); } double ptc; @@ -32,6 +33,19 @@ class SyncingSyncStatus extends SyncStatus { @override String toString() => '$blocksLeft'; + /// Returns true if we should show blocks remaining instead of percentage + /// Shows blocks remaining for the first 15 seconds of syncing + bool shouldShowBlocksRemaining() { + if (_globalSyncStartTime == null) return true; + final elapsed = DateTime.now().difference(_globalSyncStartTime!); + return elapsed.inSeconds < 15; + } + + /// Reset the global sync start time (call when sync completes or fails) + static void resetSyncStartTime() { + _globalSyncStartTime = null; + } + factory SyncingSyncStatus.fromHeightValues(int chainTip, int initialSyncHeight, int syncHeight) { final track = chainTip - initialSyncHeight; final diff = track - (chainTip - syncHeight); @@ -45,21 +59,14 @@ class SyncingSyncStatus extends SyncStatus { static void updateEtaHistory(int blocksLeft) { blockHistory[DateTime.now()] = blocksLeft; - - // keep only the last 30 entries (gives us better statistical accuracy) - while (blockHistory.length > 30) { - blockHistory.remove(blockHistory.keys.first); - } } static Map blockHistory = {}; static Duration? lastEtaDuration; static const int _minDataPoints = 3; - static const int _maxDataAgeMinutes = 2; + static DateTime? _globalSyncStartTime; String? getFormattedEtaWithPlaceholder() { - _cleanOldEntries(); - // If we have enough data, show actual ETA if (blockHistory.length >= _minDataPoints) { final eta = getFormattedEta(); @@ -70,11 +77,6 @@ class SyncingSyncStatus extends SyncStatus { return '--:--'; } - void _cleanOldEntries() { - final cutoffTime = DateTime.now().subtract(Duration(minutes: _maxDataAgeMinutes)); - blockHistory.removeWhere((key, value) => key.isBefore(cutoffTime)); - } - String? getFormattedEta() { Duration? duration = getEtaDuration(); diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index 44628ccdc6..1cfc66137f 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -4,6 +4,20 @@ import 'package:cw_core/sync_status.dart'; String syncStatusTitle(SyncStatus syncStatus, SyncStatusDisplayMode syncStatusDisplayMode) { if (syncStatus is SyncingSyncStatus) { + // Show blocks remaining for the first 3 seconds, then switch to percentage + if (syncStatus.shouldShowBlocksRemaining()) { + if (syncStatus.blocksLeft == 1) { + return S.current.block_remaining; + } + return S.current.Blocks_remaining('${syncStatus.blocksLeft}'); + } + + // After 3 seconds, show percentage-based display + // Don't show ETA for very few blocks (less than 100) to avoid inconsistency + if (syncStatus.blocksLeft < 100) { + return S.current.Blocks_remaining('${syncStatus.blocksLeft}'); + } + if (syncStatus.blocksLeft == 1) { return S.current.block_remaining; } diff --git a/lib/reactions/check_connection.dart b/lib/reactions/check_connection.dart index 4591bb9ae4..e7e62c8738 100644 --- a/lib/reactions/check_connection.dart +++ b/lib/reactions/check_connection.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/utils/tor.dart'; import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:cw_core/utils/print_verbose.dart'; @@ -8,8 +7,6 @@ import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/sync_status.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/store/settings_store.dart'; -import 'package:flutter/material.dart'; -import 'package:get_it/get_it.dart'; Timer? _checkConnectionTimer; @@ -29,7 +26,7 @@ void startCheckConnectionReaction(WalletBase wallet, SettingsStore settingsStore try { final connectivityResult = await (Connectivity().checkConnectivity()); - if (connectivityResult == ConnectivityResult.none) { + if (connectivityResult.contains(ConnectivityResult.none)) { wallet.syncStatus = FailedSyncStatus(); return; } diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index d6c872c052..4b862d1805 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -11,6 +11,7 @@ import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/utils/tor.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/sync_status.dart'; import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/balance.dart'; import 'package:cw_core/transaction_info.dart'; @@ -92,6 +93,7 @@ void startCurrentWalletChangeReaction( } await wallet.connectToNode(node: node); + SyncingSyncStatus.blockHistory.clear(); if (wallet.type == WalletType.nano || wallet.type == WalletType.banano) { final powNode = settingsStore.getCurrentPowNode(wallet.type); await wallet.connectToPowNode(node: powNode); diff --git a/lib/reactions/on_wallet_sync_status_change.dart b/lib/reactions/on_wallet_sync_status_change.dart index bbf4f44fee..6d31cb2273 100644 --- a/lib/reactions/on_wallet_sync_status_change.dart +++ b/lib/reactions/on_wallet_sync_status_change.dart @@ -19,13 +19,23 @@ void startWalletSyncStatusChangeReaction( _onWalletSyncStatusChangeReaction = reaction((_) => wallet.syncStatus, (SyncStatus status) async { try { if (status is ConnectedSyncStatus) { + SyncingSyncStatus.resetSyncStartTime(); await wallet.startSync(); } + if (status is SyncingSyncStatus || status is ProcessingSyncStatus) { await WakelockPlus.enable(); } - if (status is SyncedSyncStatus || status is FailedSyncStatus) { + + if (status is SyncedSyncStatus) { + await WakelockPlus.disable(); + SyncingSyncStatus.resetSyncStartTime(); + SyncingSyncStatus.blockHistory.clear(); + } + + if (status is FailedSyncStatus) { await WakelockPlus.disable(); + SyncingSyncStatus.resetSyncStartTime(); } if (status is SyncedSyncStatus &&