diff --git a/cw_core/lib/sync_status.dart b/cw_core/lib/sync_status.dart index 235ae826a..e9b44984d 100644 --- a/cw_core/lib/sync_status.dart +++ b/cw_core/lib/sync_status.dart @@ -1,6 +1,10 @@ abstract class SyncStatus { const SyncStatus(); double progress(); + + String formattedProgress() { + return "${(progress() * 100).toStringAsFixed(2)}%"; + } } class StartingScanSyncStatus extends SyncStatus { @@ -15,10 +19,12 @@ class StartingScanSyncStatus extends SyncStatus { } class SyncingSyncStatus extends SyncStatus { - SyncingSyncStatus(this.blocksLeft, this.ptc); + SyncingSyncStatus(this.blocksLeft, this.ptc) { + updateEtaHistory(blocksLeft); + } - final double ptc; - final int blocksLeft; + double ptc; + int blocksLeft; @override double progress() => ptc; @@ -31,10 +37,161 @@ class SyncingSyncStatus extends SyncStatus { final diff = track - (chainTip - syncHeight); final ptc = diff <= 0 ? 0.0 : diff / track; final left = chainTip - syncHeight; + updateEtaHistory(left + 1); // sum 1 because if at the chain tip, will say "0 blocks left" return SyncingSyncStatus(left + 1, ptc); } + + 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; + + String? getFormattedEtaWithPlaceholder() { + _cleanOldEntries(); + + // If we have enough data, show actual ETA + if (blockHistory.length >= _minDataPoints) { + final eta = getFormattedEta(); + if (eta != null) return eta; + } + + // Show the placeholder ETA while gathering data + return '--:--'; + } + + void _cleanOldEntries() { + final cutoffTime = DateTime.now().subtract(Duration(minutes: _maxDataAgeMinutes)); + blockHistory.removeWhere((key, value) => key.isBefore(cutoffTime)); + } + + String? getFormattedEta() { + Duration? duration = getEtaDuration(); + + // Don't show ETA for very long durations or very few blocks + if (duration.inDays > 0 || blocksLeft < 100) return null; + + // Apply smoothing to prevent ETA jumping + duration = _applySmoothing(duration); + lastEtaDuration = duration; + + return _formatDuration(duration); + } + + Duration getEtaDuration() { + DateTime now = DateTime.now(); + DateTime completionTime = calculateEta(); + return completionTime.difference(now); + } + + Duration _applySmoothing(Duration newDuration) { + if (lastEtaDuration == null) { + return newDuration; + } + + final currentSeconds = lastEtaDuration!.inSeconds; + final newSeconds = newDuration.inSeconds; + final diff = (newSeconds - currentSeconds).abs(); + + // Apply different smoothing based on the magnitude of change + if (diff > 3600) { + // If it's more than 1 hour difference, it's a large change so we move by max 30 minutes + final direction = newSeconds > currentSeconds ? 1 : -1; + final maxChange = 30 * 60; + final adjustedSeconds = currentSeconds + (direction * maxChange); + return Duration(seconds: adjustedSeconds); + } else if (diff > 300) { + // If it's more than 5 minutes difference, it's a medium change so we move by max 2 minutes + final direction = newSeconds > currentSeconds ? 1 : -1; + final maxChange = 2 * 60; + final adjustedSeconds = currentSeconds + (direction * maxChange); + return Duration(seconds: adjustedSeconds); + } else if (diff > 60) { + // If it's more than 1 minute difference, it's a small change so we move by max 30 seconds + final direction = newSeconds > currentSeconds ? 1 : -1; + final maxChange = 30; + final adjustedSeconds = currentSeconds + (direction * maxChange); + return Duration(seconds: adjustedSeconds); + } + + return newDuration; + } + + String _formatDuration(Duration duration) { + String twoDigits(int n) => n.toString().padLeft(2, '0'); + + final hours = twoDigits(duration.inHours); + final minutes = twoDigits(duration.inMinutes.remainder(60)); + final seconds = twoDigits(duration.inSeconds.remainder(60)); + + if (hours == '00') { + return '${minutes}m${seconds}s'; + } + return '${hours}h${minutes}m${seconds}s'; + } + + DateTime calculateEta() { + double rate = _calculateBlockRate(); + if (rate == 0) { + return DateTime.now().add(const Duration(days: 2)); + } + int remainingBlocks = this.blocksLeft; + double timeRemainingSeconds = remainingBlocks / rate; + return DateTime.now().add(Duration(seconds: timeRemainingSeconds.round())); + } + + // Enhanced block rate calculation with weighted averages + double _calculateBlockRate() { + List timestamps = blockHistory.keys.toList(); + List blockCounts = blockHistory.values.toList(); + + if (timestamps.length < 2) return 0; + + // Sort by timestamp to ensure chronological order + final sortedData = + List.generate(timestamps.length, (i) => MapEntry(timestamps[i], blockCounts[i])) + ..sort((a, b) => a.key.compareTo(b.key)); + + double totalWeightedTime = 0; + double totalWeightedBlocks = 0; + double totalWeight = 0; + + for (int i = 0; i < sortedData.length - 1; i++) { + final current = sortedData[i]; + final next = sortedData[i + 1]; + + final blocksProcessed = current.value - next.value; + + if (blocksProcessed <= 0) continue; // Skip invalid data + + final timeDifference = next.key.difference(current.key); + + if (timeDifference.inSeconds <= 0) continue; // Skip invalid time + + // Weight recent data more heavily (exponential decay) + final weight = 1.0 / (1.0 + (sortedData.length - 1 - i) * 0.1); + + totalWeightedTime += timeDifference.inSeconds * weight; + totalWeightedBlocks += blocksProcessed * weight; + totalWeight += weight; + } + + if (totalWeight == 0 || totalWeightedTime == 0) return 0; + + final weightedRate = totalWeightedBlocks / totalWeightedTime; + + return weightedRate; + } } class ProcessingSyncStatus extends SyncStatus { diff --git a/lib/core/sync_status_title.dart b/lib/core/sync_status_title.dart index cf4cb8e88..44628ccdc 100644 --- a/lib/core/sync_status_title.dart +++ b/lib/core/sync_status_title.dart @@ -1,11 +1,26 @@ +import 'package:cake_wallet/entities/sync_status_display_mode.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cw_core/sync_status.dart'; -String syncStatusTitle(SyncStatus syncStatus) { +String syncStatusTitle(SyncStatus syncStatus, SyncStatusDisplayMode syncStatusDisplayMode) { if (syncStatus is SyncingSyncStatus) { - return syncStatus.blocksLeft == 1 - ? S.current.block_remaining - : S.current.Blocks_remaining('${syncStatus.blocksLeft}'); + if (syncStatus.blocksLeft == 1) { + return S.current.block_remaining; + } + + // Check user preference for sync status display + if (syncStatusDisplayMode == SyncStatusDisplayMode.eta) { + // Get ETA with placeholder while gathering data + String eta = syncStatus.getFormattedEtaWithPlaceholder() ?? ''; + + if (eta.isEmpty) { + return S.current.Blocks_remaining('${syncStatus.blocksLeft}'); + } else { + return "${syncStatus.formattedProgress()} - ${S.current.eta} $eta"; + } + } else { + return S.current.Blocks_remaining('${syncStatus.blocksLeft}'); + } } if (syncStatus is SyncedTipSyncStatus) { diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index f0edd95ca..36022a929 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -114,5 +114,6 @@ class PreferencesKey { static String backgroundSyncLastTrigger(String walletId) => 'background_sync_last_trigger_${walletId}'; static const backgroundSyncNotificationsEnabled = 'background_sync_notifications_enabled'; static const enableAutomaticNodeSwitching = 'enable_automatic_node_switching'; + static const syncStatusDisplayMode = 'sync_status_display_mode'; static const backgroundImage = 'background_image'; } diff --git a/lib/entities/sync_status_display_mode.dart b/lib/entities/sync_status_display_mode.dart new file mode 100644 index 000000000..6d2e8256f --- /dev/null +++ b/lib/entities/sync_status_display_mode.dart @@ -0,0 +1,34 @@ +enum SyncStatusDisplayMode { eta, blocksRemaining } + +extension SyncStatusDisplayModeExtension on SyncStatusDisplayMode { + String get title { + switch (this) { + case SyncStatusDisplayMode.eta: + return 'ETA'; + case SyncStatusDisplayMode.blocksRemaining: + return 'Blocks Remaining'; + } + } + + String get description { + switch (this) { + case SyncStatusDisplayMode.eta: + return 'Show estimated time remaining for sync completion'; + case SyncStatusDisplayMode.blocksRemaining: + return 'Show number of blocks remaining to sync'; + } + } + + static SyncStatusDisplayMode fromString(String value) { + switch (value) { + case 'eta': + return SyncStatusDisplayMode.eta; + case 'blocksRemaining': + return SyncStatusDisplayMode.blocksRemaining; + default: + return SyncStatusDisplayMode.eta; + } + } + + String toJson() => name; +} diff --git a/lib/src/screens/dashboard/widgets/sync_indicator.dart b/lib/src/screens/dashboard/widgets/sync_indicator.dart index 2543e2bc5..ed2c65981 100644 --- a/lib/src/screens/dashboard/widgets/sync_indicator.dart +++ b/lib/src/screens/dashboard/widgets/sync_indicator.dart @@ -20,7 +20,7 @@ class SyncIndicator extends StatelessWidget { return Observer(builder: (_) { final syncIndicatorWidth = 237.0; final status = dashboardViewModel.status; - final statusText = syncStatusTitle(status); + final statusText = syncStatusTitle(status, dashboardViewModel.settingsStore.syncStatusDisplayMode); final progress = status.progress(); final indicatorOffset = progress * syncIndicatorWidth; final indicatorWidth = progress < 1 diff --git a/lib/src/screens/settings/display_settings_page.dart b/lib/src/screens/settings/display_settings_page.dart index 833e3a4f5..7420575f1 100644 --- a/lib/src/screens/settings/display_settings_page.dart +++ b/lib/src/screens/settings/display_settings_page.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/language_service.dart'; +import 'package:cake_wallet/entities/sync_status_display_mode.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; @@ -46,6 +47,15 @@ class DisplaySettingsPage extends BasePage { _displaySettingsViewModel.setShowAddressBookPopup(value); }, ), + SettingsPickerCell( + title: S.current.sync_status_display_mode, + items: SyncStatusDisplayMode.values.toList(), + selectedItem: _displaySettingsViewModel.syncStatusDisplayMode, + onItemSelected: (SyncStatusDisplayMode mode) => + _displaySettingsViewModel.setSyncStatusDisplayMode(mode), + displayItem: (SyncStatusDisplayMode mode) => mode.title, + isGridView: false, + ), //if (!isHaven) it does not work correctly if (!_displaySettingsViewModel.disabledFiatApiMode) SettingsPickerCell( diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 3bdbb2a2f..4b2b11704 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -24,6 +24,7 @@ import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/entities/seed_phrase_length.dart'; import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/entities/sort_balance_types.dart'; +import 'package:cake_wallet/entities/sync_status_display_mode.dart'; import 'package:cake_wallet/entities/wallet_list_order_types.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/wownero/wownero.dart'; @@ -55,6 +56,7 @@ abstract class SettingsStoreBase with Store { required SharedPreferences sharedPreferences, required bool initialShouldShowMarketPlaceInDashboard, required bool initialShowAddressBookPopupEnabled, + required SyncStatusDisplayMode initialSyncStatusDisplayMode, required FiatCurrency initialFiatCurrency, required BalanceDisplayMode initialBalanceDisplayMode, required bool initialSaveRecipientAddress, @@ -168,6 +170,7 @@ abstract class SettingsStoreBase with Store { contactListAscending = initialContactListAscending, shouldShowMarketPlaceInDashboard = initialShouldShowMarketPlaceInDashboard, showAddressBookPopupEnabled = initialShowAddressBookPopupEnabled, + syncStatusDisplayMode = initialSyncStatusDisplayMode, exchangeStatus = initialExchangeStatus, pinCodeLength = initialPinLength, languageCode = initialLanguageCode, @@ -388,6 +391,11 @@ abstract class SettingsStoreBase with Store { (bool value) => sharedPreferences.setBool(PreferencesKey.showAddressBookPopupEnabled, value)); + reaction( + (_) => syncStatusDisplayMode, + (SyncStatusDisplayMode value) => + sharedPreferences.setString(PreferencesKey.syncStatusDisplayMode, value.toJson())); + reaction((_) => pinCodeLength, (int pinLength) => sharedPreferences.setInt(PreferencesKey.currentPinLength, pinLength)); @@ -674,6 +682,9 @@ abstract class SettingsStoreBase with Store { @observable bool showAddressBookPopupEnabled; + @observable + SyncStatusDisplayMode syncStatusDisplayMode; + @observable ObservableList actionlistDisplayMode; @@ -1012,6 +1023,8 @@ abstract class SettingsStoreBase with Store { sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ?? true; final showAddressBookPopupEnabled = sharedPreferences.getBool(PreferencesKey.showAddressBookPopupEnabled) ?? true; + final syncStatusDisplayMode = SyncStatusDisplayModeExtension.fromString( + sharedPreferences.getString(PreferencesKey.syncStatusDisplayMode) ?? SyncStatusDisplayMode.eta.name); final exchangeStatus = ExchangeApiMode.deserialize( raw: sharedPreferences.getInt(PreferencesKey.exchangeStatusKey) ?? ExchangeApiMode.enabled.raw); @@ -1307,6 +1320,7 @@ abstract class SettingsStoreBase with Store { sharedPreferences: sharedPreferences, initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard, initialShowAddressBookPopupEnabled: showAddressBookPopupEnabled, + initialSyncStatusDisplayMode: syncStatusDisplayMode, nodes: nodes, powNodes: powNodes, appVersion: packageInfo.version, @@ -1504,6 +1518,8 @@ abstract class SettingsStoreBase with Store { showAddressBookPopupEnabled = sharedPreferences.getBool(PreferencesKey.showAddressBookPopupEnabled) ?? showAddressBookPopupEnabled; + syncStatusDisplayMode = SyncStatusDisplayModeExtension.fromString( + sharedPreferences.getString(PreferencesKey.syncStatusDisplayMode) ?? SyncStatusDisplayMode.eta.name); exchangeStatus = ExchangeApiMode.deserialize( raw: sharedPreferences.getInt(PreferencesKey.exchangeStatusKey) ?? ExchangeApiMode.enabled.raw); diff --git a/lib/view_model/settings/display_settings_view_model.dart b/lib/view_model/settings/display_settings_view_model.dart index ecd732d84..c3c420902 100644 --- a/lib/view_model/settings/display_settings_view_model.dart +++ b/lib/view_model/settings/display_settings_view_model.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; +import 'package:cake_wallet/entities/sync_status_display_mode.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/themes/core/material_base_theme.dart'; import 'package:mobx/mobx.dart'; @@ -44,6 +45,9 @@ abstract class DisplaySettingsViewModelBase with Store { @computed bool get showAddressBookPopup => _settingsStore.showAddressBookPopupEnabled; + @computed + SyncStatusDisplayMode get syncStatusDisplayMode => _settingsStore.syncStatusDisplayMode; + @computed String get backgroundImage => _settingsStore.backgroundImage; @@ -91,6 +95,10 @@ abstract class DisplaySettingsViewModelBase with Store { @action void setShowAddressBookPopup(bool value) => _settingsStore.showAddressBookPopupEnabled = value; + @action + void setSyncStatusDisplayMode(SyncStatusDisplayMode value) => + _settingsStore.syncStatusDisplayMode = value; + @action void setBackgroundImage(String path) => _settingsStore.backgroundImage = path; } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index f9fcd662d..f2b832c5e 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -354,6 +354,7 @@ "estimated": "مُقدَّر", "estimated_new_fee": "رسوم جديدة مقدرة", "estimated_receive_amount": "مقدرة المبلغ الاستقبال", + "eta": "إيتا", "etherscan_history": "Etherscan تاريخ", "event": "ﺙﺪﺣ", "events": "ﺙﺍﺪﺣﻷﺍ", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "جاري محاولة المزامنة", "sync_status_connected": "متصل", "sync_status_connecting": "يتم التوصيل", + "sync_status_display_mode": "مزامنة عرض حالة", + "sync_status_display_mode_blocks": "إظهار الكتل المتبقية", + "sync_status_display_mode_eta": "تظهر ETA", "sync_status_failed_connect": "انقطع الاتصال", "sync_status_not_connected": "غير متصل", "sync_status_starting_scan": "بدء المسح الضوئي (من ${height})", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 127a63bea..195fbbc8c 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -354,6 +354,7 @@ "estimated": "Изчислено", "estimated_new_fee": "Прогнозна нова такса", "estimated_receive_amount": "Прогнозна сума за получаване", + "eta": "Ета", "etherscan_history": "История на Etherscan", "event": "Събитие", "events": "събития", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "ОПИТ ЗА СИНХРОНИЗАЦИЯ", "sync_status_connected": "СВЪРЗВАНЕ", "sync_status_connecting": "СВЪРЗВАНЕ", + "sync_status_display_mode": "Дисплей на състоянието на синхронизиране", + "sync_status_display_mode_blocks": "Покажете останали блокове", + "sync_status_display_mode_eta": "Покажи ETA", "sync_status_failed_connect": "НЕУСПЕШНО СВЪРЗВАНЕ", "sync_status_not_connected": "НЯМА ВРЪЗКА", "sync_status_starting_scan": "Стартиране на сканиране (от ${height})", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 54b736c6b..f09470b3d 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -354,6 +354,7 @@ "estimated": "Odhadováno", "estimated_new_fee": "Odhadovaný nový poplatek", "estimated_receive_amount": "Odhadovaná částka přijímání", + "eta": "ETA", "etherscan_history": "Historie Etherscanu", "event": "událost", "events": "Události", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "ZAHAJUJI SYNCHR.", "sync_status_connected": "PŘIPOJENO", "sync_status_connecting": "PŘIPOJOVÁNÍ", + "sync_status_display_mode": "Synchronizace stavu stavu", + "sync_status_display_mode_blocks": "Zobrazit bloky zbývající", + "sync_status_display_mode_eta": "Ukažte ETA", "sync_status_failed_connect": "ODPOJENO", "sync_status_not_connected": "NEPŘIPOJENO", "sync_status_starting_scan": "Počáteční skenování (z ${height})", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index c1f064adc..4bd1e7388 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -354,6 +354,7 @@ "estimated": "Geschätzt", "estimated_new_fee": "Geschätzte neue Gebühr", "estimated_receive_amount": "Geschätzter Empfangsbetrag", + "eta": "ETA", "etherscan_history": "Etherscan-Historie", "event": "Ereignis", "events": "Veranstaltungen", @@ -911,6 +912,9 @@ "sync_status_attempting_sync": "SYNC VERSUCHEN", "sync_status_connected": "VERBUNDEN", "sync_status_connecting": "VERBINDEN", + "sync_status_display_mode": "Synchronisationsstatusanzeige", + "sync_status_display_mode_blocks": "Blöcke verbleibend zeigen", + "sync_status_display_mode_eta": "Zeigen ETA", "sync_status_failed_connect": "GETRENNT", "sync_status_not_connected": "NICHT VERBUNDEN", "sync_status_starting_scan": "SCAN starten (von ${height})", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index bcff86f8e..e7c307b46 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -354,6 +354,7 @@ "estimated": "Estimated", "estimated_new_fee": "Estimated new fee", "estimated_receive_amount": "Estimated receive amount", + "eta": "ETA", "etherscan_history": "Etherscan history", "event": "Event", "events": "Events", @@ -911,6 +912,9 @@ "sync_status_attempting_sync": "ATTEMPTING SYNC", "sync_status_connected": "CONNECTED", "sync_status_connecting": "CONNECTING", + "sync_status_display_mode": "Sync Status Display", + "sync_status_display_mode_blocks": "Show Blocks Remaining", + "sync_status_display_mode_eta": "Show ETA", "sync_status_failed_connect": "DISCONNECTED", "sync_status_not_connected": "NOT CONNECTED", "sync_status_starting_scan": "STARTING SCAN (from ${height})", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 0b6549949..d11eda1e4 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -354,6 +354,7 @@ "estimated": "Estimado", "estimated_new_fee": "Nueva tarifa estimada", "estimated_receive_amount": "Cantidad de recepción estimada", + "eta": "ETA", "etherscan_history": "historia de etherscan", "event": "Evento", "events": "Eventos", @@ -911,6 +912,9 @@ "sync_status_attempting_sync": "INTENTAR SINCRONIZAR", "sync_status_connected": "CONECTADO", "sync_status_connecting": "CONECTANDO", + "sync_status_display_mode": "Pantalla de estado de sincronización", + "sync_status_display_mode_blocks": "Mostrar bloques restantes", + "sync_status_display_mode_eta": "Mostrar ETA", "sync_status_failed_connect": "DESCONECTADO", "sync_status_not_connected": "NO CONECTADO", "sync_status_starting_scan": "Iniciar escaneo (de ${height})", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 9a1db4992..7a9d108f9 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -354,6 +354,7 @@ "estimated": "Estimé", "estimated_new_fee": "De nouveaux frais estimés", "estimated_receive_amount": "Recevoir estimé le montant", + "eta": "ETA", "etherscan_history": "Historique Etherscan", "event": "Événement", "events": "Événements", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "TENTATIVE DE SYNCHRONISATION", "sync_status_connected": "CONNECTÉ", "sync_status_connecting": "CONNEXION EN COURS", + "sync_status_display_mode": "Affichage de l'état de synchronisation", + "sync_status_display_mode_blocks": "Afficher les blocs restants", + "sync_status_display_mode_eta": "Show Eta", "sync_status_failed_connect": "DÉCONNECTÉ", "sync_status_not_connected": "NON CONNECTÉ", "sync_status_starting_scan": "Démarrer la numérisation (à partir de ${height})", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index ae8a782e1..76bb857a8 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -354,6 +354,7 @@ "estimated": "Kiyasta", "estimated_new_fee": "An kiyasta sabon kudin", "estimated_receive_amount": "Kiyasta samun adadin", + "eta": "Tsire-tsire", "etherscan_history": "Etherscan tarihin kowane zamani", "event": "Lamarin", "events": "Abubuwan da suka faru", @@ -912,6 +913,9 @@ "sync_status_attempting_sync": "KWAFI", "sync_status_connected": "HANNU", "sync_status_connecting": "HADA", + "sync_status_display_mode": "Nunin halin Setc", + "sync_status_display_mode_blocks": "Nuna shinge da sauran", + "sync_status_display_mode_eta": "Nuna ETA", "sync_status_failed_connect": "BABU INTERNET", "sync_status_not_connected": "BABU INTERNET", "sync_status_starting_scan": "Farawa Scan (daga ${height})", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 21109c44e..fd6b6d719 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -354,6 +354,7 @@ "estimated": "अनुमानित", "estimated_new_fee": "अनुमानित नया शुल्क", "estimated_receive_amount": "अनुमानित राशि", + "eta": "ईटा", "etherscan_history": "इथरस्कैन इतिहास", "event": "आयोजन", "events": "आयोजन", @@ -912,6 +913,9 @@ "sync_status_attempting_sync": "सिंक करने का प्रयास", "sync_status_connected": "जुड़े हुए", "sync_status_connecting": "कनेक्ट", + "sync_status_display_mode": "सिंक स्थिति प्रदर्शन", + "sync_status_display_mode_blocks": "शेष ब्लॉक दिखाएं", + "sync_status_display_mode_eta": "शो एटा", "sync_status_failed_connect": "डिस्कनेक्ट किया गया", "sync_status_not_connected": "जुड़े नहीं हैं", "sync_status_starting_scan": "स्कैन शुरू करना (${height} से)", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index eafbe3da8..ecf2afcc3 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -354,6 +354,7 @@ "estimated": "procijenjen", "estimated_new_fee": "Procijenjena nova naknada", "estimated_receive_amount": "Procijenjeni iznos primanja", + "eta": "Eta", "etherscan_history": "Etherscan povijest", "event": "Događaj", "events": "Događaji", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "POKUŠAJ SINKRONIZACIJE", "sync_status_connected": "SPOJENO", "sync_status_connecting": "SPAJANJE", + "sync_status_display_mode": "Sinkronizirani prikaz statusa", + "sync_status_display_mode_blocks": "Prikažite preostale blokove", + "sync_status_display_mode_eta": "Pokazati eta", "sync_status_failed_connect": "ISKLJUČENO", "sync_status_not_connected": "NIJE POVEZANO", "sync_status_starting_scan": "Početno skeniranje (od ${height})", diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index 5cea9e3bf..d1cb95eb1 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -354,6 +354,7 @@ "estimated": "Գնահատված", "estimated_new_fee": "Գնահատված նոր միջնորդավճար", "estimated_receive_amount": "Գնահատված ստացված գումար", + "eta": "Eta", "etherscan_history": "Etherscan պատմություն", "event": "Իրադարձություն", "events": "Իրադարձություններ", @@ -908,6 +909,9 @@ "sync_status_attempting_sync": "ՀԱՄԱԺԱՄԵՑՄԱՆ ՓՈՐՁ", "sync_status_connected": "ՄԻԱՑՎԱԾԷ", "sync_status_connecting": "ՄԻԱՑՎՈՒՄ Է", + "sync_status_display_mode": "Sync կարգավիճակի ցուցադրում", + "sync_status_display_mode_blocks": "Show ուցադրել բլոկները", + "sync_status_display_mode_eta": "Show ուցադրել ETA", "sync_status_failed_connect": "ՉՄԻԱՑԱՎ", "sync_status_not_connected": "ՄԻԱՑՎԱԾ ՉԷ", "sync_status_starting_scan": "Սկսած սկան (${height})", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index b80ec53f8..fb4d3b0f6 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -354,6 +354,7 @@ "estimated": "Diperkirakan", "estimated_new_fee": "Perkiraan biaya baru", "estimated_receive_amount": "Diperkirakan jumlah menerima", + "eta": "Eta", "etherscan_history": "Sejarah Etherscan", "event": "Peristiwa", "events": "Acara", @@ -913,6 +914,9 @@ "sync_status_attempting_sync": "MENCOBA SINKRONISASI", "sync_status_connected": "TERHUBUNG", "sync_status_connecting": "MENGHUBUNGKAN", + "sync_status_display_mode": "Tampilan Status Sinkronisasi", + "sync_status_display_mode_blocks": "Tampilkan blok tersisa", + "sync_status_display_mode_eta": "Tunjukkan eta", "sync_status_failed_connect": "GAGAL TERHUBUNG", "sync_status_not_connected": "TIDAK TERHUBUNG", "sync_status_starting_scan": "Mulai pemindaian (dari ${height})", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 5800313d8..14bd16499 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -354,6 +354,7 @@ "estimated": "Stimato", "estimated_new_fee": "Nuova commissione stimata", "estimated_receive_amount": "Importo di ricezione stimato", + "eta": "Eta", "etherscan_history": "Cronologia Etherscan", "event": "Evento", "events": "Eventi", @@ -911,6 +912,9 @@ "sync_status_attempting_sync": "TENTATIVO DI SINCRONIZZAZIONE", "sync_status_connected": "CONNESSO", "sync_status_connecting": "CONNESSIONE", + "sync_status_display_mode": "Visualizzazione dello stato di sincronizzazione", + "sync_status_display_mode_blocks": "Mostra blocchi rimanenti", + "sync_status_display_mode_eta": "Mostra eta", "sync_status_failed_connect": "DISCONNESSO", "sync_status_not_connected": "NON CONNESSO", "sync_status_starting_scan": "AVVIO SCANSIONE (da ${height})", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index ae7a8a257..ea5feffad 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -354,6 +354,7 @@ "estimated": "推定", "estimated_new_fee": "推定新しい料金", "estimated_receive_amount": "推定受信金額", + "eta": "ETA", "etherscan_history": "イーサスキャンの歴史", "event": "イベント", "events": "イベント", @@ -911,6 +912,9 @@ "sync_status_attempting_sync": "同期を試みています", "sync_status_connected": "接続済み", "sync_status_connecting": "接続中", + "sync_status_display_mode": "ステータス表示を同期します", + "sync_status_display_mode_blocks": "残りのブロックを表示します", + "sync_status_display_mode_eta": "ETAを表示します", "sync_status_failed_connect": "切断されました", "sync_status_not_connected": "接続されていません", "sync_status_starting_scan": "スキャンを開始する(${height} から)", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 2adb9cedf..2fac02eb2 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -354,6 +354,7 @@ "estimated": "예상", "estimated_new_fee": "예상 새 수수료", "estimated_receive_amount": "예상 수령 금액", + "eta": "에타", "etherscan_history": "Etherscan 내역", "event": "이벤트", "events": "이벤트", @@ -911,6 +912,9 @@ "sync_status_attempting_sync": "동기화 시도 중", "sync_status_connected": "연결됨", "sync_status_connecting": "연결 중", + "sync_status_display_mode": "동기화 상태 표시", + "sync_status_display_mode_blocks": "남은 블록을 보여줍니다", + "sync_status_display_mode_eta": "ETA를 보여주십시오", "sync_status_failed_connect": "연결 끊김", "sync_status_not_connected": "연결되지 않음", "sync_status_starting_scan": "스캔 시작 중 (${height}부터)", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index b445ac76a..63c902dce 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -354,6 +354,7 @@ "estimated": "ခန့်မှန်း", "estimated_new_fee": "ခန့်မှန်းသစ်ခန့်မှန်း", "estimated_receive_amount": "ခန့်မှန်းရရှိသောပမာဏ", + "eta": "eta", "etherscan_history": "Etherscan သမိုင်း", "event": "ပွဲ", "events": "အဲ့ဒါနဲ့", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "ချိန်ကိုက်ခြင်းကို ကြိုးစားနေသည်။", "sync_status_connected": "ချိတ်ဆက်ထားသည်။", "sync_status_connecting": "ချိတ်ဆက်ခြင်း။", + "sync_status_display_mode": "Sync status display ကို", + "sync_status_display_mode_blocks": "ကျန်ရှိနေသေးသောလုပ်ကွက်များကိုပြပါ", + "sync_status_display_mode_eta": "Eta ကိုပြပါ", "sync_status_failed_connect": "အဆက်အသွယ်ဖြတ်ထားသည်။", "sync_status_not_connected": "မချိတ်ဆက်ပါ။", "sync_status_starting_scan": "စကင်ဖတ်စစ်ဆေးမှုစတင်ခြင်း (${height})", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 82b12380c..a97f2c0b1 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -354,6 +354,7 @@ "estimated": "Geschatte", "estimated_new_fee": "Geschatte nieuwe vergoeding", "estimated_receive_amount": "Geschat ontvangen bedrag", + "eta": "ETA", "etherscan_history": "Etherscan-geschiedenis", "event": "Evenement", "events": "Evenementen", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "SYNCHRONISATIE PROBEREN", "sync_status_connected": "VERBONDEN", "sync_status_connecting": "AANSLUITING", + "sync_status_display_mode": "Synchronisatiestatusweergave", + "sync_status_display_mode_blocks": "Toon blokken overgebleven", + "sync_status_display_mode_eta": "Toon ETA", "sync_status_failed_connect": "LOSGEKOPPELD", "sync_status_not_connected": "NIET VERBONDEN", "sync_status_starting_scan": "SCAN starten (van ${height})", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index a78c860f1..c1a51292b 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -354,6 +354,7 @@ "estimated": "Oszacowano", "estimated_new_fee": "Szacowana nowa opłata", "estimated_receive_amount": "Szacowana kwota otrzymania", + "eta": "Eta", "etherscan_history": "Historia Etherscanu", "event": "Wydarzenie", "events": "Wydarzenia", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "PRÓBA SYNCHRONIZACJI", "sync_status_connected": "POŁĄCZONY", "sync_status_connecting": "ŁĄCZENIE", + "sync_status_display_mode": "Wyświetlanie statusu synchronizacji", + "sync_status_display_mode_blocks": "Pokaż pozostałe bloki", + "sync_status_display_mode_eta": "Pokaż eta", "sync_status_failed_connect": "POŁĄCZENIE NIEUDANE", "sync_status_not_connected": "NIE POŁĄCZONY", "sync_status_starting_scan": "Rozpoczęcie skanowania (od ${height})", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 5b7358438..744cc000b 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -354,6 +354,7 @@ "estimated": "Estimado", "estimated_new_fee": "Nova taxa estimada", "estimated_receive_amount": "Valor estimado de recebimento", + "eta": "ETA", "etherscan_history": "história Etherscan", "event": "Evento", "events": "Eventos", @@ -912,6 +913,9 @@ "sync_status_attempting_sync": "TENTANDO SINCRONIZAR", "sync_status_connected": "CONECTADO", "sync_status_connecting": "CONECTANDO", + "sync_status_display_mode": "Exibição de status de sincronização", + "sync_status_display_mode_blocks": "Mostre blocos restantes", + "sync_status_display_mode_eta": "Mostre ETA", "sync_status_failed_connect": "DESCONECTADO", "sync_status_not_connected": "DESCONECTADO", "sync_status_starting_scan": "Começando scan (de ${height})", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 0fd39258c..0681e012a 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -354,6 +354,7 @@ "estimated": "Примерно", "estimated_new_fee": "Расчетная новая плата", "estimated_receive_amount": "Расчетная сумма получения", + "eta": "ETA", "etherscan_history": "История Эфириума", "event": "Событие", "events": "События", @@ -911,6 +912,9 @@ "sync_status_attempting_sync": "ПОПЫТКА СИНХРОНИЗАЦИИ", "sync_status_connected": "ПОДКЛЮЧЕНО", "sync_status_connecting": "ПОДКЛЮЧЕНИЕ", + "sync_status_display_mode": "Отображение статуса синхронизации", + "sync_status_display_mode_blocks": "Показать оставшиеся блоки", + "sync_status_display_mode_eta": "Показать ETA", "sync_status_failed_connect": "ОТКЛЮЧЕНО", "sync_status_not_connected": "НЕ ПОДКЛЮЧЁН", "sync_status_starting_scan": "Начальное сканирование (от ${height})", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 669e0128d..77e41087d 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -354,6 +354,7 @@ "estimated": "ประมาณการ", "estimated_new_fee": "ค่าธรรมเนียมใหม่โดยประมาณ", "estimated_receive_amount": "โดยประมาณว่าจำนวนเงินที่ได้รับ", + "eta": "กทพ.", "etherscan_history": "ประวัติอีเธอร์สแกน", "event": "เหตุการณ์", "events": "กิจกรรม", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "พยายามซิงโครไนซ์", "sync_status_connected": "เชื่อมต่อแล้ว", "sync_status_connecting": "กำลังเชื่อมต่อ", + "sync_status_display_mode": "การแสดงสถานะซิงค์", + "sync_status_display_mode_blocks": "โชว์บล็อกที่เหลืออยู่", + "sync_status_display_mode_eta": "แสดง ETA", "sync_status_failed_connect": "การเชื่อมต่อล้มเหลว", "sync_status_not_connected": "ไม่ได้เชื่อมต่อ", "sync_status_starting_scan": "การสแกนเริ่มต้น (จาก ${height})", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 38bc4aee4..b258cd115 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -354,6 +354,7 @@ "estimated": "Tinatayang", "estimated_new_fee": "Tinatayang bagong fee", "estimated_receive_amount": "Tinatayang natanggap na halaga", + "eta": "Eta", "etherscan_history": "Kasaysayan ng Etherscan", "event": "Kaganapan", "events": "Mga kaganapan", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "SINUSUBUKANG I-SYNC", "sync_status_connected": "KONEKTADO", "sync_status_connecting": "KUMOKENEKTA", + "sync_status_display_mode": "Pagpapakita ng katayuan ng pag -sync", + "sync_status_display_mode_blocks": "Ipakita ang natitirang mga bloke", + "sync_status_display_mode_eta": "Ipakita ang Eta", "sync_status_failed_connect": "NADISKONEKTA", "sync_status_not_connected": "HINDI KONEKTADO", "sync_status_starting_scan": "Simula sa pag -scan (mula sa ${height})", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index df359feb9..26590d450 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -354,6 +354,7 @@ "estimated": "Tahmini", "estimated_new_fee": "Tahmini yeni ücret", "estimated_receive_amount": "Tahmini alma miktarı", + "eta": "ETA", "etherscan_history": "Etherscan geçmişi", "event": "Etkinlik", "events": "Olaylar", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "SENKRONİZE EDİLMEYE ÇALIŞILIYOR", "sync_status_connected": "BAĞLANILDI", "sync_status_connecting": "BAĞLANILIYOR", + "sync_status_display_mode": "Senkronizasyon Durum Ekranı", + "sync_status_display_mode_blocks": "Kalan blokları göster", + "sync_status_display_mode_eta": "ETA'yı Göster", "sync_status_failed_connect": "BAĞLANTI KESİLDİ", "sync_status_not_connected": "BAĞLI DEĞİL", "sync_status_starting_scan": "Başlangıç ​​taraması (${height})", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 8cc022044..9fc1f96b4 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -354,6 +354,7 @@ "estimated": "Приблизно ", "estimated_new_fee": "Орієнтовна нова комісія", "estimated_receive_amount": "Орієнтовна сума отримує", + "eta": "ETA", "etherscan_history": "Історія Etherscan", "event": "Подія", "events": "Події", @@ -911,6 +912,9 @@ "sync_status_attempting_sync": "СПРОБА СИНХРОНІЗАЦІЇ", "sync_status_connected": "ПІДКЛЮЧЕНО", "sync_status_connecting": "ПІДКЛЮЧЕННЯ", + "sync_status_display_mode": "Дисплей стану синхронізації", + "sync_status_display_mode_blocks": "Показати блоки, що залишилися", + "sync_status_display_mode_eta": "Шоу ETA", "sync_status_failed_connect": "ВІДКЛЮЧЕНО", "sync_status_not_connected": "НЕ ПІДКЛЮЧЕННИЙ", "sync_status_starting_scan": "Початок сканування (від ${height})", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 14f7eeeeb..6ddeea3e9 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -354,6 +354,7 @@ "estimated": "تخمینہ لگایا", "estimated_new_fee": "تخمینہ شدہ نئی فیس", "estimated_receive_amount": "تخمینہ وصول کی رقم", + "eta": "ETA", "etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ", "event": "ﺐﯾﺮﻘﺗ", "events": "ﺕﺎﺒﯾﺮﻘﺗ", @@ -912,6 +913,9 @@ "sync_status_attempting_sync": "ہم آہنگی کی کوشش کر رہا ہے۔", "sync_status_connected": "منسلک", "sync_status_connecting": "جڑ رہا ہے۔", + "sync_status_display_mode": "مطابقت پذیری کی حیثیت ڈسپلے", + "sync_status_display_mode_blocks": "باقی بلاکس دکھائیں", + "sync_status_display_mode_eta": "ETA دکھائیں", "sync_status_failed_connect": "منقطع", "sync_status_not_connected": "منسلک نہیں", "sync_status_starting_scan": "اسکین شروع کرنا (${height})", diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb index fdb20c8c6..f9601bed6 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -353,6 +353,7 @@ "estimated": "Ước tính", "estimated_new_fee": "Phí mới ước tính", "estimated_receive_amount": "Số tiền nhận ước tính", + "eta": "Eta", "etherscan_history": "Lịch sử Etherscan", "event": "Sự kiện", "events": "Các sự kiện", @@ -907,6 +908,9 @@ "sync_status_attempting_sync": "ĐANG THỬ ĐỒNG BỘ", "sync_status_connected": "ĐÃ KẾT NỐI", "sync_status_connecting": "ĐANG KẾT NỐI", + "sync_status_display_mode": "Hiển thị trạng thái đồng bộ hóa", + "sync_status_display_mode_blocks": "Hiển thị các khối còn lại", + "sync_status_display_mode_eta": "Hiển thị ETA", "sync_status_failed_connect": "ĐÃ NGẮT KẾT NỐI", "sync_status_not_connected": "CHƯA KẾT NỐI", "sync_status_starting_scan": "ĐANG BẮT ĐẦU QUÉT (${height})", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 0a2a68c63..ff236e133 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -355,6 +355,7 @@ "estimated": "Ó tó a fojú díwọ̀n", "estimated_new_fee": "Ifoju tuntun owo tuntun", "estimated_receive_amount": "Ifoju gba iye", + "eta": "Eta", "etherscan_history": "Etherscan itan", "event": "Iṣẹlẹ", "events": "Awọn iṣẹlẹ", @@ -911,6 +912,9 @@ "sync_status_attempting_sync": "Ń GBÌYÀNJÚ MÚDỌ́GBA", "sync_status_connected": "TI DÁRAPỌ̀ MỌ́", "sync_status_connecting": "Ń DÁRAPỌ̀ MỌ́", + "sync_status_display_mode": "Sync Ifihan Ipo", + "sync_status_display_mode_blocks": "Ṣafihan awọn bulọọki ti o ku", + "sync_status_display_mode_eta": "Fihan efa", "sync_status_failed_connect": "ÌKÀNPỌ̀ TI KÚ", "sync_status_not_connected": "KÒ TI DÁRAPỌ̀ MỌ́ Ọ", "sync_status_starting_scan": "Ibẹrẹ ọlọjẹ (lati ${height})", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 994ad91f1..5cdebdea9 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -354,6 +354,7 @@ "estimated": "估计值", "estimated_new_fee": "估计新费用", "estimated_receive_amount": "估计接收金额", + "eta": "ETA", "etherscan_history": "以太扫描历史", "event": "事件", "events": "活动", @@ -910,6 +911,9 @@ "sync_status_attempting_sync": "嘗試同步", "sync_status_connected": "已连接", "sync_status_connecting": "连接中", + "sync_status_display_mode": "同步状态显示", + "sync_status_display_mode_blocks": "剩下的显示块", + "sync_status_display_mode_eta": "显示ETA", "sync_status_failed_connect": "断线", "sync_status_not_connected": "未连接", "sync_status_starting_scan": "启动扫描(来自 ${height})",