From 5e92394fca15e6ff8421e522e27c8c8991277c39 Mon Sep 17 00:00:00 2001 From: Beast Date: Wed, 27 May 2026 21:13:34 +0800 Subject: [PATCH 01/10] fix: polling inactive accounts - Only poll for active account --- .../global_history_polling_service.dart | 50 ++++---- .../lib/services/history_polling_manager.dart | 21 +--- .../pending_transaction_polling_service.dart | 21 +--- ...ng_transaction_reconciliation_service.dart | 63 +++++++--- ...eversible_transfer_monitoring_service.dart | 36 ++---- .../shared/utils/polling_refresh_scope.dart | 116 ++++++++++++++++++ .../utils/tx_filter_family_provider.dart | 7 +- .../utils/polling_refresh_scope_test.dart | 69 +++++++++++ 8 files changed, 277 insertions(+), 106 deletions(-) create mode 100644 mobile-app/lib/shared/utils/polling_refresh_scope.dart create mode 100644 mobile-app/test/shared/utils/polling_refresh_scope_test.dart diff --git a/mobile-app/lib/services/global_history_polling_service.dart b/mobile-app/lib/services/global_history_polling_service.dart index a9d12511..1785653e 100644 --- a/mobile-app/lib/services/global_history_polling_service.dart +++ b/mobile-app/lib/services/global_history_polling_service.dart @@ -2,12 +2,10 @@ import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:resonance_network_wallet/providers/account_providers.dart'; -import 'package:resonance_network_wallet/providers/all_transactions_provider.dart'; -import 'package:resonance_network_wallet/providers/wallet_providers.dart'; import 'package:resonance_network_wallet/providers/connectivity_provider.dart'; import 'package:resonance_network_wallet/services/pending_transaction_reconciliation_service.dart'; import 'package:resonance_network_wallet/services/telemetry_service.dart'; -import 'package:resonance_network_wallet/shared/utils/tx_filter_family_provider.dart'; +import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; /// Service that handles global history polling - refreshes transaction history /// every minute to keep the UI up to date with the latest blockchain state. @@ -73,35 +71,19 @@ class GlobalHistoryPollingService { } try { - // Check if we have accounts available - final accountsState = _ref.read(accountsProvider); - if (accountsState.value?.isEmpty ?? true) { + final activeId = activeAccountId(_ref); + if (activeId == null) { _scheduleNextPoll(); return; } - print('Performing global history poll...'); + print('Performing global history poll for active account...'); - // Refresh balance silently (transactions might have changed balance) - _ref.invalidate(balanceProviderFamily); - - // Silently refresh without showing loading indicators for global - // and active filtered - _ref.read(paginationControllerProvider.notifier).silentRefresh(); - final accountIds = _ref.read(accountsProvider).value?.map((a) => a.accountId).toList() ?? []; - final targetIds = [ - ...accountIds.map((id) => [id]), - accountIds, - ]; - - for (final ids in targetIds) { - updatePaginationFiltersFor(_ref.read, ids, (notifier, _) { - notifier.silentRefresh(); - }); - } + invalidateActiveAccountBalance(_ref); + await silentRefreshActiveAccount(_ref); // Reconcile pending transactions with confirmed transactions - _ref.read(pendingTransactionReconciliationServiceProvider).reconcilePendingTransactions(); + await _ref.read(pendingTransactionReconciliationServiceProvider).reconcilePendingTransactions(); print('Global history poll completed'); } catch (e) { @@ -125,12 +107,14 @@ class GlobalHistoryPollingService { return; } - await _ref.read(paginationControllerProvider.notifier).loadingRefresh(); final active = _ref.read(activeAccountProvider).value; if (active != null) { - updatePaginationFiltersFor(_ref.read, [active.account.accountId], (notifier, _) { - notifier.loadingRefresh(); - }); + await refreshAccountsPagination( + _ref, + accountIds: [active.account.accountId], + action: (notifier) => notifier.loadingRefresh(), + ); + invalidateActiveAccountBalance(_ref); } // Also reconcile pending transactions during manual refresh @@ -169,6 +153,14 @@ final globalHistoryPollingServiceProvider = Provider service.dispose()); diff --git a/mobile-app/lib/services/history_polling_manager.dart b/mobile-app/lib/services/history_polling_manager.dart index e0059885..4758fede 100644 --- a/mobile-app/lib/services/history_polling_manager.dart +++ b/mobile-app/lib/services/history_polling_manager.dart @@ -1,9 +1,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:resonance_network_wallet/providers/account_providers.dart'; -import 'package:resonance_network_wallet/providers/all_transactions_provider.dart'; import 'package:resonance_network_wallet/providers/wallet_providers.dart'; import 'package:resonance_network_wallet/services/global_history_polling_service.dart'; import 'package:resonance_network_wallet/services/reversible_transfer_monitoring_service.dart'; +import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; /// Manager that coordinates all polling services: global history, transaction /// tracking, @@ -59,28 +58,18 @@ class HistoryPollingManager { Future triggerSilentRefresh() async { print('History polling manager: Silent Refresh!'); - // Refresh balance silently (no loading indicators) _refreshBalance(showLoading: false); - - // Use silent refresh for background updates - await _ref.read(paginationControllerProvider.notifier).silentRefresh(); + await silentRefreshActiveAccount(_ref); await _reversibleMonitor.forceCheckAllMonitoredTransfers(); } /// Helper method to refresh balance with or without loading indicators void _refreshBalance({required bool showLoading}) { if (showLoading) { - // For manual refresh - invalidate balance providers to show loading - final activeDisplayAccount = _ref.read(activeAccountProvider).value; - if (activeDisplayAccount != null) { - _ref.invalidate(balanceProviderFamily); - } - _ref.invalidate(balanceProviderRaw); // Invalidate raw balance for loading state - // displayBalanceProvider (effective) will auto-update when raw balance changes + invalidateActiveAccountBalance(_ref); + _ref.invalidate(balanceProviderRaw); } else { - // For silent refresh - just invalidate family to refresh data silently - _ref.invalidate(balanceProviderFamily); - // displayBalanceProvider (effective) will auto-update when raw balance changes + invalidateActiveAccountBalance(_ref); } } diff --git a/mobile-app/lib/services/pending_transaction_polling_service.dart b/mobile-app/lib/services/pending_transaction_polling_service.dart index bdc6c68d..ce5ffe93 100644 --- a/mobile-app/lib/services/pending_transaction_polling_service.dart +++ b/mobile-app/lib/services/pending_transaction_polling_service.dart @@ -2,10 +2,9 @@ import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:resonance_network_wallet/providers/account_providers.dart'; -import 'package:resonance_network_wallet/providers/all_transactions_provider.dart'; import 'package:resonance_network_wallet/providers/pending_transactions_provider.dart'; import 'package:resonance_network_wallet/providers/wallet_providers.dart'; +import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; import 'package:resonance_network_wallet/shared/utils/tx_filter_family_provider.dart'; class PendingTransactionPollingService { @@ -92,7 +91,7 @@ class PendingTransactionPollingService { onFound?.call(result); _ref.read(pendingTransactionsProvider.notifier).remove(pendingTx.id); - _ref.invalidate(balanceProviderFamily); + invalidateAccountBalances(_ref, {pendingTx.from, pendingTx.to}); } else { print('[PendingTxPoller] no match yet for ${pendingTx.id}, will retry'); } @@ -117,18 +116,10 @@ final pendingTransactionPollingServiceProvider = Provider affectedAccountIds, TransactionEvent? newTransaction}) { try { - final mainController = ref.read(paginationControllerProvider.notifier); - if (newTransaction != null) mainController.addTransactionToHistory(newTransaction); - mainController.silentRefresh(); - - final targets = affectedAccountIds.map((id) => [id]).toList(); - final active = ref.read(activeAccountProvider).value; - if (active != null) targets.add([active.account.accountId]); - - final accountIds = ref.read(accountsProvider).value?.map((a) => a.accountId).toList() ?? []; - if (accountIds.isNotEmpty) { - targets.add(accountIds); - } + final targets = accountRefreshTargets( + affectedAccountIds: affectedAccountIds, + activeId: activeAccountId(ref), + ); for (final targetIds in targets) { if (newTransaction != null) { diff --git a/mobile-app/lib/services/pending_transaction_reconciliation_service.dart b/mobile-app/lib/services/pending_transaction_reconciliation_service.dart index 7fed1e4f..bb8b7866 100644 --- a/mobile-app/lib/services/pending_transaction_reconciliation_service.dart +++ b/mobile-app/lib/services/pending_transaction_reconciliation_service.dart @@ -4,10 +4,13 @@ import 'dart:async'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; -import 'package:resonance_network_wallet/providers/all_transactions_provider.dart'; +import 'package:resonance_network_wallet/models/filtered_transactions_params.dart'; +import 'package:resonance_network_wallet/providers/account_id_list_cache.dart'; +import 'package:resonance_network_wallet/providers/filtered_all_transactions_provider.dart'; +import 'package:resonance_network_wallet/providers/pending_cancellations_provider.dart'; import 'package:resonance_network_wallet/providers/pending_transactions_provider.dart'; -import 'package:resonance_network_wallet/providers/wallet_providers.dart'; import 'package:resonance_network_wallet/services/transaction_service.dart'; +import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; /// Service that reconciles pending transactions with confirmed transactions /// from blockchain history. This handles cases where the inBlock status @@ -42,20 +45,19 @@ class PendingTransactionReconciliationService { 'PendingReconciliation: Checking ${pendingTxs.length} ' 'pending transactions', ); + final activeId = activeAccountId(_ref); + final accountIds = reconciliationAccountIds(activeId: activeId, pendingTxs: pendingTxs); + + for (final accountId in accountIds) { + await refreshAccountsPagination( + _ref, + accountIds: [accountId], + action: (notifier) => notifier.silentRefresh(), + onlyIfAlive: accountId == activeId, + ); + } - // Get recent history to match against - final allTransactionsAsync = _ref.read(allTransactionsProvider); - - final confirmedTransactions = allTransactionsAsync.when( - data: (transactions) => txService.combineAndDeduplicateTransactions( - pendingCancellationIds: transactions.pendingCancellationIds, - pendingTransactions: [], // Don't include pending here as we're comparing against them - scheduledReversibleTransfers: transactions.scheduledReversibleTransfers, - otherTransfers: transactions.otherTransfers, - ), - loading: () => [], - error: (_, _) => [], - ); + final confirmedTransactions = _loadConfirmedTransactions(txService, accountIds); if (confirmedTransactions.isEmpty) { print('PendingReconciliation: No confirmed transactions to match against'); @@ -85,6 +87,34 @@ class PendingTransactionReconciliationService { } } + List _loadConfirmedTransactions(TransactionService txService, Set accountIds) { + final pendingCancellationIds = _ref.read(pendingCancellationsProvider); + final confirmedById = {}; + + for (final accountId in accountIds) { + final params = FilteredTransactionsParams( + accountIds: AccountIdListCache.get([accountId]), + filter: TransactionFilter.all, + ); + final transactionsAsync = _ref.read(filteredTransactionsProviderFamily(params)); + + transactionsAsync.whenData((transactions) { + final combined = txService.combineAndDeduplicateTransactions( + pendingCancellationIds: pendingCancellationIds, + pendingTransactions: [], + scheduledReversibleTransfers: transactions.scheduledReversibleTransfers, + otherTransfers: transactions.otherTransfers, + ); + + for (final tx in combined) { + confirmedById[tx.id] = tx; + } + }); + } + + return confirmedById.values.toList(); + } + /// Determines if a pending transaction is stale and should be checked for /// reconciliation bool _isStalePendingTransaction(PendingTransactionEvent pendingTx, DateTime now) { @@ -157,8 +187,7 @@ class PendingTransactionReconciliationService { await _removePendingTransaction(pendingTx, 'Found matching confirmed transaction in history'); - // Refresh balance since transaction was actually completed - _ref.invalidate(balanceProviderFamily); + invalidateAccountBalances(_ref, {pendingTx.from, pendingTx.to}); } else { print('PendingReconciliation: No matching confirmed transaction found for ${pendingTx.id}'); diff --git a/mobile-app/lib/services/reversible_transfer_monitoring_service.dart b/mobile-app/lib/services/reversible_transfer_monitoring_service.dart index 0c5193df..a2ec0f67 100644 --- a/mobile-app/lib/services/reversible_transfer_monitoring_service.dart +++ b/mobile-app/lib/services/reversible_transfer_monitoring_service.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/app_lifecycle_manager.dart'; -import 'package:resonance_network_wallet/providers/account_providers.dart'; -import 'package:resonance_network_wallet/providers/all_transactions_provider.dart'; +import 'package:resonance_network_wallet/providers/active_account_transactions_provider.dart'; import 'package:resonance_network_wallet/providers/pending_cancellations_provider.dart'; import 'package:resonance_network_wallet/providers/wallet_providers.dart'; import 'package:resonance_network_wallet/providers/connectivity_provider.dart'; +import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; import 'package:resonance_network_wallet/shared/utils/tx_filter_family_provider.dart'; /// Service that monitors reversible transfers approaching execution time @@ -18,6 +18,7 @@ class ReversibleTransferMonitoringService { final Ref _ref; final Map _timers = {}; final Map _executionPollers = {}; + ProviderSubscription? _txSubscription; static const Duration _pollInterval = Duration(seconds: 5); // Aggressive polling @@ -26,6 +27,8 @@ class ReversibleTransferMonitoringService { if (next == AppLifecycleState.resumed) { _listenToTransactions(); } else { + _txSubscription?.close(); + _txSubscription = null; dispose(); } }); @@ -43,7 +46,7 @@ class ReversibleTransferMonitoringService { } void _listenToTransactions() { - _ref.listen(allTransactionsProvider, (previous, current) { + _txSubscription = _ref.listen(activeAccountTransactionsProvider(TransactionFilter.all), (previous, current) { current.when( data: (combinedData) { _handleTransactionsUpdate(combinedData.scheduledReversibleTransfers); @@ -144,14 +147,7 @@ class ReversibleTransferMonitoringService { // Stop polling for this transfer _stopExecutionPolling(transfer.id); - // Update the transfer status inline - move from reversible - // to executed list for both global and filtered controllers - _ref - .read(paginationControllerProvider.notifier) - .updateReversibleTransferToExecuted(transfer.txId, transaction.status); - _ref.read(pendingCancellationsProvider.notifier).removePendingCancellation(transfer.id); - - // Also update filtered controllers for affected accounts so + // Update filtered controllers for affected accounts so // active-account views reflect the change immediately final affectedAccounts = {transfer.from, transfer.to}; for (final accountId in affectedAccounts) { @@ -160,15 +156,9 @@ class ReversibleTransferMonitoringService { }); } - // Also update filtered controllers for all accounts so - // tx screen views for all accounts reflect the change immediately - final accountIds = _ref.read(accountsProvider).value?.map((a) => a.accountId).toList() ?? []; - updatePaginationFiltersFor(_ref.read, accountIds, (notifier, _) { - notifier.updateReversibleTransferToExecuted(transfer.txId, transaction.status); - }); + invalidateAccountBalances(_ref, affectedAccounts); - // Refresh balance since transfer execution changes balance - _ref.invalidate(balanceProviderFamily); + _ref.read(pendingCancellationsProvider.notifier).removePendingCancellation(transfer.id); print('Updated transfer status inline - moved to done list'); } @@ -194,13 +184,7 @@ class ReversibleTransferMonitoringService { /// Manually trigger a check for all monitored transfers (useful for testing) Future forceCheckAllMonitoredTransfers() async { if (_executionPollers.isNotEmpty) { - await _ref.read(paginationControllerProvider.notifier).silentRefresh(); - final active = _ref.read(activeAccountProvider).value; - if (active != null) { - updatePaginationFiltersFor(_ref.read, [active.account.accountId], (notifier, _) { - notifier.silentRefresh(); - }); - } + await silentRefreshActiveAccount(_ref); } } diff --git a/mobile-app/lib/shared/utils/polling_refresh_scope.dart b/mobile-app/lib/shared/utils/polling_refresh_scope.dart new file mode 100644 index 00000000..df8c9eaa --- /dev/null +++ b/mobile-app/lib/shared/utils/polling_refresh_scope.dart @@ -0,0 +1,116 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:resonance_network_wallet/models/filtered_transactions_params.dart'; +import 'package:resonance_network_wallet/providers/account_id_list_cache.dart'; +import 'package:resonance_network_wallet/providers/account_providers.dart'; +import 'package:resonance_network_wallet/providers/controllers/unified_pagination_controller.dart'; +import 'package:resonance_network_wallet/providers/filtered_all_transactions_provider.dart'; +import 'package:resonance_network_wallet/providers/wallet_providers.dart'; + +const _backgroundPollFilters = [TransactionFilter.all]; + +/// Returns the currently selected wallet account ID, if any. +String? activeAccountId(Ref ref) => ref.read(activeAccountProvider).value?.account.accountId; + +/// Builds deduplicated single-account refresh targets for event-driven updates. +List> accountRefreshTargets({ + required Set affectedAccountIds, + String? activeId, +}) { + final targets = {...affectedAccountIds}; + if (activeId != null) { + targets.add(activeId); + } + + return targets.map((id) => [id]).toList(); +} + +/// Account IDs whose cached history should be consulted during pending-tx reconciliation. +Set reconciliationAccountIds({ + required String? activeId, + required Iterable pendingTxs, +}) { + final ids = {}; + if (activeId != null) { + ids.add(activeId); + } + + for (final tx in pendingTxs) { + ids.add(tx.from); + ids.add(tx.to); + } + + return ids; +} + +/// Invalidates balance for the active account only. +void invalidateActiveAccountBalance(Ref ref) { + final accountId = activeAccountId(ref); + if (accountId == null) return; + + ref.invalidate(balanceProviderFamily(accountId)); +} + +/// Invalidates balance for the given account IDs. +void invalidateAccountBalances(Ref ref, Iterable accountIds) { + for (final accountId in accountIds) { + ref.invalidate(balanceProviderFamily(accountId)); + } +} + +Future refreshAccountsPagination( + Ref ref, { + required List accountIds, + required Future Function(UnifiedPaginationController notifier) action, + Iterable filters = _backgroundPollFilters, + bool onlyIfAlive = false, +}) async { + if (accountIds.isEmpty) return; + + final cachedIds = AccountIdListCache.get(accountIds); + + for (final filter in filters) { + final params = FilteredTransactionsParams(accountIds: cachedIds, filter: filter); + if (onlyIfAlive && !ref.exists(filteredPaginationControllerProviderFamily(params))) { + continue; + } + + final notifier = ref.read(filteredPaginationControllerProviderFamily(params).notifier); + await action(notifier); + } +} + +/// Silently refreshes pagination for the active account (background poll scope). +Future silentRefreshActiveAccount(Ref ref) async { + final accountId = activeAccountId(ref); + if (accountId == null) return; + + await refreshAccountsPagination( + ref, + accountIds: [accountId], + action: (notifier) => notifier.silentRefresh(), + onlyIfAlive: true, + ); +} + +/// Refreshes the active account when the user switches accounts. +Future refreshActiveAccountOnSwitch(Ref ref) async { + final accountId = activeAccountId(ref); + if (accountId == null) return; + + final params = FilteredTransactionsParams( + accountIds: AccountIdListCache.get([accountId]), + filter: TransactionFilter.all, + ); + + final paginationState = ref.read(filteredPaginationControllerProviderFamily(params)); + final notifier = ref.read(filteredPaginationControllerProviderFamily(params).notifier); + + if (paginationState.hasLoadedChainData) { + await notifier.silentRefresh(); + } else { + await notifier.loadingRefresh(); + } + + invalidateActiveAccountBalance(ref); +} diff --git a/mobile-app/lib/shared/utils/tx_filter_family_provider.dart b/mobile-app/lib/shared/utils/tx_filter_family_provider.dart index f3bb276a..429cd20b 100644 --- a/mobile-app/lib/shared/utils/tx_filter_family_provider.dart +++ b/mobile-app/lib/shared/utils/tx_filter_family_provider.dart @@ -10,11 +10,12 @@ typedef Reader = T Function(ProviderListenable provider); void updatePaginationFiltersFor( Reader read, List targetIds, - void Function(UnifiedPaginationController notifier, TransactionFilter filter) action, -) { + void Function(UnifiedPaginationController notifier, TransactionFilter filter) action, { + Iterable filters = TransactionFilter.values, +}) { final cachedIds = AccountIdListCache.get(targetIds); - for (final filter in TransactionFilter.values) { + for (final filter in filters) { final notifier = read( filteredPaginationControllerProviderFamily( FilteredTransactionsParams(accountIds: cachedIds, filter: filter), diff --git a/mobile-app/test/shared/utils/polling_refresh_scope_test.dart b/mobile-app/test/shared/utils/polling_refresh_scope_test.dart new file mode 100644 index 00000000..60e8aa71 --- /dev/null +++ b/mobile-app/test/shared/utils/polling_refresh_scope_test.dart @@ -0,0 +1,69 @@ +import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; +import 'package:test/test.dart'; + +void main() { + group('accountRefreshTargets', () { + test('includes affected and active accounts without wallet-wide fan-out', () { + final targets = accountRefreshTargets( + affectedAccountIds: {'account-a', 'account-b'}, + activeId: 'account-c', + ); + + expect(targets, hasLength(3)); + expect(targets.map((ids) => ids.single).toSet(), {'account-a', 'account-b', 'account-c'}); + }); + + test('deduplicates active account when already affected', () { + final targets = accountRefreshTargets( + affectedAccountIds: {'account-a'}, + activeId: 'account-a', + ); + + expect(targets, hasLength(1)); + expect(targets.single, ['account-a']); + }); + }); + + group('reconciliationAccountIds', () { + test('scopes to active account and pending transaction parties', () { + final pendingTx = PendingTransactionEvent( + tempId: 'tx-1', + from: 'sender', + to: 'receiver', + amount: BigInt.one, + timestamp: DateTime(2024), + transactionState: TransactionState.pending, + isReversible: false, + fee: BigInt.zero, + ); + + final ids = reconciliationAccountIds( + activeId: 'active', + pendingTxs: [pendingTx], + ); + + expect(ids, {'active', 'sender', 'receiver'}); + }); + + test('does not include unrelated wallet accounts', () { + final pendingTx = PendingTransactionEvent( + tempId: 'tx-2', + from: 'sender', + to: 'receiver', + amount: BigInt.one, + timestamp: DateTime(2024), + transactionState: TransactionState.pending, + isReversible: false, + fee: BigInt.zero, + ); + + final ids = reconciliationAccountIds( + activeId: 'active', + pendingTxs: [pendingTx], + ); + + expect(ids.contains('unrelated-account'), isFalse); + }); + }); +} From 2fffeadc778eb46fa936a4d47caf4ac55e3e08ab Mon Sep 17 00:00:00 2001 From: Beast Date: Wed, 27 May 2026 21:19:01 +0800 Subject: [PATCH 02/10] chore: formatting --- .../pending_transaction_polling_service.dart | 5 +---- .../shared/utils/polling_refresh_scope.dart | 5 +---- .../utils/polling_refresh_scope_test.dart | 20 ++++--------------- 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/mobile-app/lib/services/pending_transaction_polling_service.dart b/mobile-app/lib/services/pending_transaction_polling_service.dart index ce5ffe93..354a0f0c 100644 --- a/mobile-app/lib/services/pending_transaction_polling_service.dart +++ b/mobile-app/lib/services/pending_transaction_polling_service.dart @@ -116,10 +116,7 @@ final pendingTransactionPollingServiceProvider = Provider affectedAccountIds, TransactionEvent? newTransaction}) { try { - final targets = accountRefreshTargets( - affectedAccountIds: affectedAccountIds, - activeId: activeAccountId(ref), - ); + final targets = accountRefreshTargets(affectedAccountIds: affectedAccountIds, activeId: activeAccountId(ref)); for (final targetIds in targets) { if (newTransaction != null) { diff --git a/mobile-app/lib/shared/utils/polling_refresh_scope.dart b/mobile-app/lib/shared/utils/polling_refresh_scope.dart index df8c9eaa..2970aca8 100644 --- a/mobile-app/lib/shared/utils/polling_refresh_scope.dart +++ b/mobile-app/lib/shared/utils/polling_refresh_scope.dart @@ -13,10 +13,7 @@ const _backgroundPollFilters = [TransactionFilter.all]; String? activeAccountId(Ref ref) => ref.read(activeAccountProvider).value?.account.accountId; /// Builds deduplicated single-account refresh targets for event-driven updates. -List> accountRefreshTargets({ - required Set affectedAccountIds, - String? activeId, -}) { +List> accountRefreshTargets({required Set affectedAccountIds, String? activeId}) { final targets = {...affectedAccountIds}; if (activeId != null) { targets.add(activeId); diff --git a/mobile-app/test/shared/utils/polling_refresh_scope_test.dart b/mobile-app/test/shared/utils/polling_refresh_scope_test.dart index 60e8aa71..304ce0d5 100644 --- a/mobile-app/test/shared/utils/polling_refresh_scope_test.dart +++ b/mobile-app/test/shared/utils/polling_refresh_scope_test.dart @@ -5,20 +5,14 @@ import 'package:test/test.dart'; void main() { group('accountRefreshTargets', () { test('includes affected and active accounts without wallet-wide fan-out', () { - final targets = accountRefreshTargets( - affectedAccountIds: {'account-a', 'account-b'}, - activeId: 'account-c', - ); + final targets = accountRefreshTargets(affectedAccountIds: {'account-a', 'account-b'}, activeId: 'account-c'); expect(targets, hasLength(3)); expect(targets.map((ids) => ids.single).toSet(), {'account-a', 'account-b', 'account-c'}); }); test('deduplicates active account when already affected', () { - final targets = accountRefreshTargets( - affectedAccountIds: {'account-a'}, - activeId: 'account-a', - ); + final targets = accountRefreshTargets(affectedAccountIds: {'account-a'}, activeId: 'account-a'); expect(targets, hasLength(1)); expect(targets.single, ['account-a']); @@ -38,10 +32,7 @@ void main() { fee: BigInt.zero, ); - final ids = reconciliationAccountIds( - activeId: 'active', - pendingTxs: [pendingTx], - ); + final ids = reconciliationAccountIds(activeId: 'active', pendingTxs: [pendingTx]); expect(ids, {'active', 'sender', 'receiver'}); }); @@ -58,10 +49,7 @@ void main() { fee: BigInt.zero, ); - final ids = reconciliationAccountIds( - activeId: 'active', - pendingTxs: [pendingTx], - ); + final ids = reconciliationAccountIds(activeId: 'active', pendingTxs: [pendingTx]); expect(ids.contains('unrelated-account'), isFalse); }); From 0b4ee4af427700bdfd9b04f73027a0881e3afdca Mon Sep 17 00:00:00 2001 From: Beast Date: Wed, 27 May 2026 21:30:19 +0800 Subject: [PATCH 03/10] fix: waiting when on no async --- ...ng_transaction_reconciliation_service.dart | 25 +++++++++---------- ...eversible_transfer_monitoring_service.dart | 12 +++------ 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/mobile-app/lib/services/pending_transaction_reconciliation_service.dart b/mobile-app/lib/services/pending_transaction_reconciliation_service.dart index bb8b7866..84348060 100644 --- a/mobile-app/lib/services/pending_transaction_reconciliation_service.dart +++ b/mobile-app/lib/services/pending_transaction_reconciliation_service.dart @@ -96,20 +96,19 @@ class PendingTransactionReconciliationService { accountIds: AccountIdListCache.get([accountId]), filter: TransactionFilter.all, ); - final transactionsAsync = _ref.read(filteredTransactionsProviderFamily(params)); - - transactionsAsync.whenData((transactions) { - final combined = txService.combineAndDeduplicateTransactions( - pendingCancellationIds: pendingCancellationIds, - pendingTransactions: [], - scheduledReversibleTransfers: transactions.scheduledReversibleTransfers, - otherTransfers: transactions.otherTransfers, - ); + final pagination = _ref.read(filteredPaginationControllerProviderFamily(params)); + if (!pagination.hasLoadedChainData) continue; + + final combined = txService.combineAndDeduplicateTransactions( + pendingCancellationIds: pendingCancellationIds, + pendingTransactions: [], + scheduledReversibleTransfers: pagination.scheduledReversibleTransfers, + otherTransfers: pagination.otherTransfers, + ); - for (final tx in combined) { - confirmedById[tx.id] = tx; - } - }); + for (final tx in combined) { + confirmedById[tx.id] = tx; + } } return confirmedById.values.toList(); diff --git a/mobile-app/lib/services/reversible_transfer_monitoring_service.dart b/mobile-app/lib/services/reversible_transfer_monitoring_service.dart index a2ec0f67..64317d2d 100644 --- a/mobile-app/lib/services/reversible_transfer_monitoring_service.dart +++ b/mobile-app/lib/services/reversible_transfer_monitoring_service.dart @@ -46,14 +46,10 @@ class ReversibleTransferMonitoringService { } void _listenToTransactions() { - _txSubscription = _ref.listen(activeAccountTransactionsProvider(TransactionFilter.all), (previous, current) { - current.when( - data: (combinedData) { - _handleTransactionsUpdate(combinedData.scheduledReversibleTransfers); - }, - loading: () {}, - error: (_, _) {}, - ); + _txSubscription?.close(); + _txSubscription = _ref.listen(activeAccountPaginationProvider(TransactionFilter.all), (previous, current) { + if (current == null) return; + _handleTransactionsUpdate(current.scheduledReversibleTransfers); }); } From a9ba276dd4d2ee855b8d07ca143719e4f5c68828 Mon Sep 17 00:00:00 2001 From: Beast Date: Thu, 28 May 2026 18:53:03 +0800 Subject: [PATCH 04/10] fix: double schedule --- .../lib/services/global_history_polling_service.dart | 8 ++------ .../test/shared/utils/polling_refresh_scope_test.dart | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/mobile-app/lib/services/global_history_polling_service.dart b/mobile-app/lib/services/global_history_polling_service.dart index 1785653e..8eee0de4 100644 --- a/mobile-app/lib/services/global_history_polling_service.dart +++ b/mobile-app/lib/services/global_history_polling_service.dart @@ -54,6 +54,8 @@ class GlobalHistoryPollingService { } void _scheduleNextPoll() { + _pollingTimer?.cancel(); + _pollingTimer = Timer(const Duration(minutes: 1), () { _performPoll(); }); @@ -71,12 +73,6 @@ class GlobalHistoryPollingService { } try { - final activeId = activeAccountId(_ref); - if (activeId == null) { - _scheduleNextPoll(); - return; - } - print('Performing global history poll for active account...'); invalidateActiveAccountBalance(_ref); diff --git a/mobile-app/test/shared/utils/polling_refresh_scope_test.dart b/mobile-app/test/shared/utils/polling_refresh_scope_test.dart index 304ce0d5..9ec1e2d7 100644 --- a/mobile-app/test/shared/utils/polling_refresh_scope_test.dart +++ b/mobile-app/test/shared/utils/polling_refresh_scope_test.dart @@ -1,6 +1,6 @@ import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; -import 'package:test/test.dart'; +import 'package:flutter_test/flutter_test.dart'; void main() { group('accountRefreshTargets', () { From 62e671971b460da8bddff923a4eaad892f40da7b Mon Sep 17 00:00:00 2001 From: Beast Date: Thu, 28 May 2026 19:05:28 +0800 Subject: [PATCH 05/10] chore: change debug print --- mobile-app/lib/services/global_history_polling_service.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile-app/lib/services/global_history_polling_service.dart b/mobile-app/lib/services/global_history_polling_service.dart index 8eee0de4..b907f322 100644 --- a/mobile-app/lib/services/global_history_polling_service.dart +++ b/mobile-app/lib/services/global_history_polling_service.dart @@ -6,6 +6,7 @@ import 'package:resonance_network_wallet/providers/connectivity_provider.dart'; import 'package:resonance_network_wallet/services/pending_transaction_reconciliation_service.dart'; import 'package:resonance_network_wallet/services/telemetry_service.dart'; import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; /// Service that handles global history polling - refreshes transaction history /// every minute to keep the UI up to date with the latest blockchain state. @@ -73,7 +74,7 @@ class GlobalHistoryPollingService { } try { - print('Performing global history poll for active account...'); + quantusDebugPrint('Performing global history poll for active account...'); invalidateActiveAccountBalance(_ref); await silentRefreshActiveAccount(_ref); From 86b1053961a9a9f6f3742dd01a55c4d113ec9371 Mon Sep 17 00:00:00 2001 From: Beast Date: Fri, 29 May 2026 13:24:46 +0800 Subject: [PATCH 06/10] fix: not closing subscription on dispose --- .../services/reversible_transfer_monitoring_service.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile-app/lib/services/reversible_transfer_monitoring_service.dart b/mobile-app/lib/services/reversible_transfer_monitoring_service.dart index 64317d2d..ff25bff3 100644 --- a/mobile-app/lib/services/reversible_transfer_monitoring_service.dart +++ b/mobile-app/lib/services/reversible_transfer_monitoring_service.dart @@ -27,8 +27,6 @@ class ReversibleTransferMonitoringService { if (next == AppLifecycleState.resumed) { _listenToTransactions(); } else { - _txSubscription?.close(); - _txSubscription = null; dispose(); } }); @@ -192,6 +190,10 @@ class ReversibleTransferMonitoringService { for (final poller in _executionPollers.values) { poller.cancel(); } + + _txSubscription?.close(); + _txSubscription = null; + _timers.clear(); _executionPollers.clear(); } From 3ace9eb77e24b18191425ec29b33521d5af0492c Mon Sep 17 00:00:00 2001 From: Beast Date: Fri, 29 May 2026 13:56:58 +0800 Subject: [PATCH 07/10] fix: recon acting inverted and bad var name --- .../services/pending_transaction_reconciliation_service.dart | 2 +- mobile-app/lib/shared/utils/polling_refresh_scope.dart | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/mobile-app/lib/services/pending_transaction_reconciliation_service.dart b/mobile-app/lib/services/pending_transaction_reconciliation_service.dart index 84348060..d7f53c20 100644 --- a/mobile-app/lib/services/pending_transaction_reconciliation_service.dart +++ b/mobile-app/lib/services/pending_transaction_reconciliation_service.dart @@ -53,7 +53,7 @@ class PendingTransactionReconciliationService { _ref, accountIds: [accountId], action: (notifier) => notifier.silentRefresh(), - onlyIfAlive: accountId == activeId, + isAccountInactive: accountId != activeId, ); } diff --git a/mobile-app/lib/shared/utils/polling_refresh_scope.dart b/mobile-app/lib/shared/utils/polling_refresh_scope.dart index 2970aca8..9b180ba7 100644 --- a/mobile-app/lib/shared/utils/polling_refresh_scope.dart +++ b/mobile-app/lib/shared/utils/polling_refresh_scope.dart @@ -60,7 +60,7 @@ Future refreshAccountsPagination( required List accountIds, required Future Function(UnifiedPaginationController notifier) action, Iterable filters = _backgroundPollFilters, - bool onlyIfAlive = false, + bool isAccountInactive = false, }) async { if (accountIds.isEmpty) return; @@ -68,7 +68,7 @@ Future refreshAccountsPagination( for (final filter in filters) { final params = FilteredTransactionsParams(accountIds: cachedIds, filter: filter); - if (onlyIfAlive && !ref.exists(filteredPaginationControllerProviderFamily(params))) { + if (isAccountInactive && !ref.exists(filteredPaginationControllerProviderFamily(params))) { continue; } @@ -86,7 +86,6 @@ Future silentRefreshActiveAccount(Ref ref) async { ref, accountIds: [accountId], action: (notifier) => notifier.silentRefresh(), - onlyIfAlive: true, ); } From 487404eaf58047cc5b1b65f189389ff2d7a1f9c4 Mon Sep 17 00:00:00 2001 From: Beast Date: Fri, 29 May 2026 14:05:03 +0800 Subject: [PATCH 08/10] feat: remove duplicate refresh --- mobile-app/lib/services/global_history_polling_service.dart | 2 +- mobile-app/lib/services/history_polling_manager.dart | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/mobile-app/lib/services/global_history_polling_service.dart b/mobile-app/lib/services/global_history_polling_service.dart index b907f322..f6e5f7ba 100644 --- a/mobile-app/lib/services/global_history_polling_service.dart +++ b/mobile-app/lib/services/global_history_polling_service.dart @@ -109,7 +109,7 @@ class GlobalHistoryPollingService { await refreshAccountsPagination( _ref, accountIds: [active.account.accountId], - action: (notifier) => notifier.loadingRefresh(), + action: (notifier) => notifier.silentRefresh(), ); invalidateActiveAccountBalance(_ref); } diff --git a/mobile-app/lib/services/history_polling_manager.dart b/mobile-app/lib/services/history_polling_manager.dart index 4758fede..53c4d270 100644 --- a/mobile-app/lib/services/history_polling_manager.dart +++ b/mobile-app/lib/services/history_polling_manager.dart @@ -51,7 +51,6 @@ class HistoryPollingManager { _refreshBalance(showLoading: true); await _globalPoller.triggerManualRefresh(); - await _reversibleMonitor.forceCheckAllMonitoredTransfers(); } /// Trigger a silent refresh of all data (no loading indicators) @@ -60,7 +59,6 @@ class HistoryPollingManager { _refreshBalance(showLoading: false); await silentRefreshActiveAccount(_ref); - await _reversibleMonitor.forceCheckAllMonitoredTransfers(); } /// Helper method to refresh balance with or without loading indicators From f9ade0605b5c216a7971ca51b766871cd73b61d6 Mon Sep 17 00:00:00 2001 From: Beast Date: Fri, 29 May 2026 14:57:01 +0800 Subject: [PATCH 09/10] feat: use quantus debug print instead of print and revert silentRefresh --- mobile-app/lib/app_lifecycle_manager.dart | 19 ++++---- .../account_associations_providers.dart | 5 +- .../lib/providers/account_providers.dart | 11 +++-- .../providers/entrusted_account_provider.dart | 3 +- .../notification_config_provider.dart | 5 +- .../lib/providers/notification_provider.dart | 15 +++--- .../pending_cancellations_provider.dart | 7 +-- .../lib/providers/raider_quest_providers.dart | 5 +- .../lib/providers/remote_config_provider.dart | 3 +- .../lib/providers/wallet_providers.dart | 3 +- .../lib/services/deep_link_service.dart | 9 ++-- .../global_history_polling_service.dart | 22 ++++----- .../lib/services/history_polling_manager.dart | 9 ++-- .../lib/services/mining_rewards_service.dart | 11 +++-- .../pending_transaction_polling_service.dart | 19 ++++---- ...ng_transaction_reconciliation_service.dart | 47 ++++++++++--------- mobile-app/lib/services/referral_service.dart | 11 +++-- .../lib/services/remote_config_service.dart | 5 +- ...eversible_transfer_monitoring_service.dart | 21 +++++---- .../transaction_submission_service.dart | 15 +++--- mobile-app/lib/services/tx_watch_service.dart | 5 +- .../lib/shared/utils/open_external_url.dart | 5 +- .../screens/import/import_wallet_screen.dart | 3 +- mobile-app/lib/wallet_initializer.dart | 17 +++---- 24 files changed, 149 insertions(+), 126 deletions(-) diff --git a/mobile-app/lib/app_lifecycle_manager.dart b/mobile-app/lib/app_lifecycle_manager.dart index 9180fb6b..7946519a 100644 --- a/mobile-app/lib/app_lifecycle_manager.dart +++ b/mobile-app/lib/app_lifecycle_manager.dart @@ -8,6 +8,7 @@ import 'package:resonance_network_wallet/providers/connectivity_provider.dart'; import 'package:resonance_network_wallet/providers/remote_config_provider.dart'; import 'package:resonance_network_wallet/providers/local_auth_provider.dart'; import 'package:resonance_network_wallet/services/history_polling_manager.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; /// Provider that holds the current app lifecycle state final appLifecycleStateProvider = StateProvider((ref) => AppLifecycleState.resumed); @@ -55,17 +56,17 @@ class _AppLifecycleManagerState extends ConsumerState with final appState = ref.read(appLifecycleStateProvider); if (status == NetworkStatus.online && appState == AppLifecycleState.resumed) { - print('Back online - resuming polling'); + quantusDebugPrint('Back online - resuming polling'); pollingManager.resumePolling(); pollingManager.triggerSilentRefresh(); } else if (status == NetworkStatus.offline) { - print('Gone offline - pausing polling'); + quantusDebugPrint('Gone offline - pausing polling'); pollingManager.pausePolling(); } }, loading: () {}, error: (e, _) { - print('Error listening to network status: $e'); + quantusDebugPrint('Error listening to network status: $e'); }, ); }); @@ -87,7 +88,7 @@ class _AppLifecycleManagerState extends ConsumerState with if (state == AppLifecycleState.resumed) { // Only resume if we were previously backgrounded if (_isBackgrounded) { - print('AppLifecycleState.resumed - resuming from background'); + quantusDebugPrint('AppLifecycleState.resumed - resuming from background'); _isBackgrounded = false; // Only resume polling if online @@ -95,7 +96,7 @@ class _AppLifecycleManagerState extends ConsumerState with pollingManager.resumePolling(); pollingManager.triggerSilentRefresh(); } else { - print('App resumed but offline - polling paused'); + quantusDebugPrint('App resumed but offline - polling paused'); } // Check authentication ONLY on resume from background. @@ -114,13 +115,13 @@ class _AppLifecycleManagerState extends ConsumerState with // Skip if the biometric dialog caused this lifecycle change — on some // Android devices the prompt triggers inactive→resumed oscillation. if (!_isBackgrounded && !ref.read(localAuthProvider).isAuthenticating) { - print('AppLifecycleState.$state - pausing (update pause time only)'); + quantusDebugPrint('AppLifecycleState.$state - pausing (update pause time only)'); _isBackgrounded = true; pollingManager.pausePolling(); localAuthNotifier.recordBackgroundTime(); } else { - print('AppLifecycleState.$state - already backgrounded, skipping actions'); + quantusDebugPrint('AppLifecycleState.$state - already backgrounded, skipping actions'); } } } @@ -134,10 +135,10 @@ class _AppLifecycleManagerState extends ConsumerState with if (hasWallet) { final taskmasterService = TaskmasterService(); await taskmasterService.ensureIsLoggedIn(); - print('Taskmaster login initialized'); + quantusDebugPrint('Taskmaster login initialized'); } } catch (e) { - print('Failed to initialize taskmaster login: $e'); + quantusDebugPrint('Failed to initialize taskmaster login: $e'); } } diff --git a/mobile-app/lib/providers/account_associations_providers.dart b/mobile-app/lib/providers/account_associations_providers.dart index 34fb3a57..c1b51ecd 100644 --- a/mobile-app/lib/providers/account_associations_providers.dart +++ b/mobile-app/lib/providers/account_associations_providers.dart @@ -2,6 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/legacy.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/providers/account_providers.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; class AccountAssociationsNotifier extends StateNotifier> { final TaskmasterService _taskmasterService = TaskmasterService(); @@ -22,8 +23,8 @@ class AccountAssociationsNotifier extends StateNotifier>> { final AccountsService _accountsService; @@ -25,7 +26,7 @@ class AccountsNotifier extends StateNotifier>> { await _accountsService.addAccount(account); state = AsyncValue.data([...accounts, account]); } catch (e, st) { - print('error adding account $e $st'); + quantusDebugPrint('error adding account $e $st'); // Handle error, maybe revert state or show a message } }); @@ -38,7 +39,7 @@ class AccountsNotifier extends StateNotifier>> { final newAccounts = accounts.where((a) => a.accountId != account.accountId).toList(); state = AsyncValue.data(newAccounts); } catch (e, st) { - print('remove account error $e $st'); + quantusDebugPrint('remove account error $e $st'); } }); } @@ -67,10 +68,10 @@ class ActiveAccountNotifier extends StateNotifier> { Future _loadActiveAccount() async { try { final account = await _settingsService.getActiveAccount(); - print('loaded active account: ${account?.account.name}'); + quantusDebugPrint('loaded active account: ${account?.account.name}'); state = AsyncValue.data(account); } catch (e, st) { - print('error loading active account: $e $st'); + quantusDebugPrint('error loading active account: $e $st'); state = AsyncValue.error(e, st); } } @@ -80,7 +81,7 @@ class ActiveAccountNotifier extends StateNotifier> { await _settingsService.setActiveAccount(account); state = AsyncValue.data(account); } catch (e, st) { - print('setActiveAccount error $e $st'); + quantusDebugPrint('setActiveAccount error $e $st'); } } diff --git a/mobile-app/lib/providers/entrusted_account_provider.dart b/mobile-app/lib/providers/entrusted_account_provider.dart index 24fbdfc5..92308413 100644 --- a/mobile-app/lib/providers/entrusted_account_provider.dart +++ b/mobile-app/lib/providers/entrusted_account_provider.dart @@ -1,9 +1,10 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; final entrustedAccountsProvider = FutureProvider.family, Account>((ref, account) async { final highSecurityService = HighSecurityService(); final interceptedAccounts = await highSecurityService.getEntrustedAccounts(account); - print('intercepted accounts: ${interceptedAccounts.map((account) => account.accountId).join(', ')}'); + quantusDebugPrint('intercepted accounts: ${interceptedAccounts.map((account) => account.accountId).join(', ')}'); return interceptedAccounts; }); diff --git a/mobile-app/lib/providers/notification_config_provider.dart b/mobile-app/lib/providers/notification_config_provider.dart index f3f63bc5..31dedf64 100644 --- a/mobile-app/lib/providers/notification_config_provider.dart +++ b/mobile-app/lib/providers/notification_config_provider.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter_riverpod/legacy.dart'; import 'package:resonance_network_wallet/models/notification_models.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; import 'package:shared_preferences/shared_preferences.dart'; const String notificationConfigKey = 'notification_config'; @@ -37,7 +38,7 @@ class NotificationConfigNotifier extends StateNotifier { state = NotificationConfig.fromJson(Map.from(jsonDecode(configJson))); } } catch (e) { - print('Error loading notification config: $e'); + quantusDebugPrint('Error loading notification config: $e'); } } @@ -46,7 +47,7 @@ class NotificationConfigNotifier extends StateNotifier { final prefs = await SharedPreferences.getInstance(); await prefs.setString(notificationConfigKey, jsonEncode(state.toJson())); } catch (e) { - print('Error saving notification config: $e'); + quantusDebugPrint('Error saving notification config: $e'); } } diff --git a/mobile-app/lib/providers/notification_provider.dart b/mobile-app/lib/providers/notification_provider.dart index f59fc774..65e90294 100644 --- a/mobile-app/lib/providers/notification_provider.dart +++ b/mobile-app/lib/providers/notification_provider.dart @@ -7,6 +7,7 @@ import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/models/notification_models.dart'; import 'package:resonance_network_wallet/providers/notification_config_provider.dart'; import 'package:resonance_network_wallet/services/local_notifications_service.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// Maximum number of notifications to keep (FIFO) @@ -64,13 +65,13 @@ class NotificationNotifier extends StateNotifier> { Future canShowNotification(NotificationIntent type) async { // Check app-level master setting if (!_config.enabled) { - print('Notifications disabled at app level'); + quantusDebugPrint('Notifications disabled at app level'); return false; } // Check specific notification type setting if (!_config.isIntentEnabled(type)) { - print('Notification type ${type.name} is disabled'); + quantusDebugPrint('Notification type ${type.name} is disabled'); return false; } @@ -80,7 +81,7 @@ class NotificationNotifier extends StateNotifier> { /// Check if any notifications can be shown (master check) Future canShowNotifications() async { if (!_config.enabled) { - print('Notifications disabled at app level'); + quantusDebugPrint('Notifications disabled at app level'); return false; } @@ -132,7 +133,7 @@ class NotificationNotifier extends StateNotifier> { final scheduledDate = notification.scheduledTime!; final duration = scheduledDate.difference(DateTime.now()); - print('DURATION ${scheduledDate.toString()}'); + quantusDebugPrint('DURATION ${scheduledDate.toString()}'); // Schedule timer to show notification at the right time final timer = Timer(duration, () { @@ -162,7 +163,7 @@ class NotificationNotifier extends StateNotifier> { // Check if this specific notification intent can be shown if (!await canShowNotification(notification.intent)) { - print('Cannot show ${notification.intent.name} notification: disabled or permission not granted'); + quantusDebugPrint('Cannot show ${notification.intent.name} notification: disabled or permission not granted'); return; } @@ -186,7 +187,7 @@ class NotificationNotifier extends StateNotifier> { Future cancelNotification(String notificationId) async { final timer = _scheduledTimers.remove(notificationId); timer?.cancel(); - print('Cancelled scheduled notification: $notificationId'); + quantusDebugPrint('Cancelled scheduled notification: $notificationId'); } Future cancelAllNotifications() async { @@ -194,7 +195,7 @@ class NotificationNotifier extends StateNotifier> { timer.cancel(); } _scheduledTimers.clear(); - print('Cancelled all scheduled notifications'); + quantusDebugPrint('Cancelled all scheduled notifications'); } List getScheduledNotificationIds() { diff --git a/mobile-app/lib/providers/pending_cancellations_provider.dart b/mobile-app/lib/providers/pending_cancellations_provider.dart index 0847d366..b7247008 100644 --- a/mobile-app/lib/providers/pending_cancellations_provider.dart +++ b/mobile-app/lib/providers/pending_cancellations_provider.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:flutter_riverpod/legacy.dart'; import 'package:resonance_network_wallet/models/pending_cancellation.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; import 'package:shared_preferences/shared_preferences.dart'; final pendingCancellationsProvider = StateNotifierProvider>((ref) { @@ -37,7 +38,7 @@ class PendingCancellationsNotifier extends StateNotifier> { if (isNotExpired) { validCancellations.add(cancellation); } else { - print('Removing expired pending cancellation: ${cancellation.transactionId}'); + quantusDebugPrint('Removing expired pending cancellation: ${cancellation.transactionId}'); } } catch (e) { throw FormatException('Error parsing pending cancellation: $e'); @@ -76,7 +77,7 @@ class PendingCancellationsNotifier extends StateNotifier> { existingCancellations.add(cancellation); } } catch (e) { - print('Error parsing existing cancellation: $e'); + quantusDebugPrint('Error parsing existing cancellation: $e'); } } @@ -107,7 +108,7 @@ class PendingCancellationsNotifier extends StateNotifier> { remainingCancellations.add(cancellation); } } catch (e) { - print('Error parsing cancellation during removal: $e'); + quantusDebugPrint('Error parsing cancellation during removal: $e'); } } diff --git a/mobile-app/lib/providers/raider_quest_providers.dart b/mobile-app/lib/providers/raider_quest_providers.dart index 95b6ce47..bfaf9747 100644 --- a/mobile-app/lib/providers/raider_quest_providers.dart +++ b/mobile-app/lib/providers/raider_quest_providers.dart @@ -2,6 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/legacy.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/providers/account_providers.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; class RaiderSubmissionsNotifier extends StateNotifier> { final TaskmasterService _taskmasterService = TaskmasterService(); @@ -22,8 +23,8 @@ class RaiderSubmissionsNotifier extends StateNotifier((ref) { return RemoteConfigService(); @@ -39,7 +40,7 @@ class RemoteConfigNotifier extends StateNotifier { state = remote; } } catch (e) { - print('Remote config remote refresh failed: $e'); + quantusDebugPrint('Remote config remote refresh failed: $e'); } finally { _isRefreshingRemote = false; } diff --git a/mobile-app/lib/providers/wallet_providers.dart b/mobile-app/lib/providers/wallet_providers.dart index 943f0c42..ca9f4b80 100644 --- a/mobile-app/lib/providers/wallet_providers.dart +++ b/mobile-app/lib/providers/wallet_providers.dart @@ -4,6 +4,7 @@ import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/providers/account_providers.dart'; import 'package:resonance_network_wallet/providers/l10n_provider.dart'; import 'package:resonance_network_wallet/providers/pending_transactions_provider.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; final settingsServiceProvider = Provider((ref) { return SettingsService(); @@ -67,7 +68,7 @@ final isHighSecurityProvider = FutureProvider.family((ref, accoun final balanceProviderFamily = FutureProvider.family((ref, accountId) async { final substrateService = ref.watch(substrateServiceProvider); - print('query balance for $accountId'); + quantusDebugPrint('query balance for $accountId'); return await substrateService.queryBalance(accountId); }); diff --git a/mobile-app/lib/services/deep_link_service.dart b/mobile-app/lib/services/deep_link_service.dart index 8df00fed..96cc6808 100644 --- a/mobile-app/lib/services/deep_link_service.dart +++ b/mobile-app/lib/services/deep_link_service.dart @@ -3,6 +3,7 @@ import 'package:app_links/app_links.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:resonance_network_wallet/providers/account_associations_providers.dart'; import 'package:resonance_network_wallet/providers/route_intent_providers.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; final deepLinkServiceProvider = Provider((ref) { final service = DeepLinkService(ref); @@ -21,14 +22,14 @@ class DeepLinkService { Future init() async { // Handle links when the app is already open (warm state) _linkSubscription = _appLinks.uriLinkStream.listen((uri) { - print('Received link while app is open: $uri'); + quantusDebugPrint('Received link while app is open: $uri'); _handleLink(uri); }); // Handle the link that opened the app (cold state) final initialUri = await _appLinks.getInitialLink(); if (initialUri != null) { - print('Received initial link: $initialUri'); + quantusDebugPrint('Received initial link: $initialUri'); _handleLink(initialUri); } } @@ -49,7 +50,7 @@ class DeepLinkService { if (accountId != null && accountId.isNotEmpty) { _ref.read(sharedAccountIntentProvider.notifier).state = accountId; } else { - print('Missing or empty account id'); + quantusDebugPrint('Missing or empty account id'); } } @@ -58,7 +59,7 @@ class DeepLinkService { if (payment != null) { _ref.read(paymentIntentProvider.notifier).state = payment; } else { - print('Missing payment parameters'); + quantusDebugPrint('Missing payment parameters'); } } diff --git a/mobile-app/lib/services/global_history_polling_service.dart b/mobile-app/lib/services/global_history_polling_service.dart index f6e5f7ba..ae0c736e 100644 --- a/mobile-app/lib/services/global_history_polling_service.dart +++ b/mobile-app/lib/services/global_history_polling_service.dart @@ -24,7 +24,7 @@ class GlobalHistoryPollingService { _isPolling = true; _scheduleNextPoll(); - print('Global history polling started'); + quantusDebugPrint('Global history polling started'); } /// Stops the global history polling. @@ -36,21 +36,21 @@ class GlobalHistoryPollingService { _pollingTimer?.cancel(); _pollingTimer = null; _isPolling = false; - print('Global history polling stopped'); + quantusDebugPrint('Global history polling stopped'); } /// Pauses polling temporarily (e.g., when app goes to background) void pausePolling() { _pollingTimer?.cancel(); _pollingTimer = null; - print('Global history polling paused'); + quantusDebugPrint('Global history polling paused'); } /// Resumes polling if it was previously started void resumePolling() { if (_isPolling && _pollingTimer == null) { _scheduleNextPoll(); - print('Global history polling resumed'); + quantusDebugPrint('Global history polling resumed'); } } @@ -68,7 +68,7 @@ class GlobalHistoryPollingService { // Check connectivity before polling final isOnline = _ref.read(isOnlineProvider); if (!isOnline) { - print('Skipping poll - offline'); + quantusDebugPrint('Skipping poll - offline'); _scheduleNextPoll(); return; } @@ -82,9 +82,9 @@ class GlobalHistoryPollingService { // Reconcile pending transactions with confirmed transactions await _ref.read(pendingTransactionReconciliationServiceProvider).reconcilePendingTransactions(); - print('Global history poll completed'); + quantusDebugPrint('Global history poll completed'); } catch (e) { - print('Error during global history poll: $e'); + quantusDebugPrint('Error during global history poll: $e'); } finally { // Schedule the next poll regardless of success/failure if (_isPolling) { @@ -95,12 +95,12 @@ class GlobalHistoryPollingService { /// Manually trigger a history refresh (useful for pull-to-refresh) Future triggerManualRefresh() async { - print('Global polling manager: Manual Refresh!'); + quantusDebugPrint('Global polling manager: Manual Refresh!'); // Check connectivity before refreshing final isOnline = _ref.read(isOnlineProvider); if (!isOnline) { - print('Skipping manual refresh - offline'); + quantusDebugPrint('Skipping manual refresh - offline'); return; } @@ -109,7 +109,7 @@ class GlobalHistoryPollingService { await refreshAccountsPagination( _ref, accountIds: [active.account.accountId], - action: (notifier) => notifier.silentRefresh(), + action: (notifier) => notifier.loadingRefresh(), ); invalidateActiveAccountBalance(_ref); } @@ -139,7 +139,7 @@ final globalHistoryPollingServiceProvider = Provider triggerManualRefresh() async { - print('History polling manager: Manual Refresh!'); + quantusDebugPrint('History polling manager: Manual Refresh!'); // Refresh balance (with loading indicators) _refreshBalance(showLoading: true); @@ -55,7 +56,7 @@ class HistoryPollingManager { /// Trigger a silent refresh of all data (no loading indicators) Future triggerSilentRefresh() async { - print('History polling manager: Silent Refresh!'); + quantusDebugPrint('History polling manager: Silent Refresh!'); _refreshBalance(showLoading: false); await silentRefreshActiveAccount(_ref); diff --git a/mobile-app/lib/services/mining_rewards_service.dart b/mobile-app/lib/services/mining_rewards_service.dart index 6abef9a1..355f75ed 100644 --- a/mobile-app/lib/services/mining_rewards_service.dart +++ b/mobile-app/lib/services/mining_rewards_service.dart @@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/shared/utils/env_utils.dart'; import 'package:resonance_network_wallet/providers/wallet_providers.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; class MiningRewardsData { final int resonanceBlocks; @@ -42,14 +43,14 @@ class MiningRewardsService { } Future getMiningRewards(Ref ref, WormholeKeyPair keyPair, List currentAccountIds) async { - print('[MiningRewards] Current account IDs: $currentAccountIds'); + quantusDebugPrint('[MiningRewards] Current account IDs: $currentAccountIds'); final wormholeUtxoService = ref.read(wormholeUtxoServiceProvider); final miners = >{}; for (final entry in _assets.entries) { final jsonStr = await rootBundle.loadString(entry.value); miners[entry.key] = _parseMiners(jsonStr); - print('[MiningRewards] ${entry.key}: loaded ${miners[entry.key]!.length} miners'); + quantusDebugPrint('[MiningRewards] ${entry.key}: loaded ${miners[entry.key]!.length} miners'); } _cachedAccountIds ??= await _resolveAllAccountIds(currentAccountIds); @@ -98,19 +99,19 @@ class MiningRewardsService { for (final id in toCheck) { final oldId = newToOld[id]; if (oldId != null && allIds.add(oldId)) { - print('[MiningRewards] Chain depth $depth: $id -> $oldId'); + quantusDebugPrint('[MiningRewards] Chain depth $depth: $id -> $oldId'); next.add(oldId); } } toCheck = next; } - print('[MiningRewards] Final account ID set (${allIds.length}): $allIds'); + quantusDebugPrint('[MiningRewards] Final account ID set (${allIds.length}): $allIds'); return allIds; } Future>> _fetchAccountMappings() async { - print('[MiningRewards] Fetching account_id_mappings from Supabase...'); + quantusDebugPrint('[MiningRewards] Fetching account_id_mappings from Supabase...'); final data = await EnvUtils.supabaseClient.from('account_id_mappings').select(); return List>.from(data); } diff --git a/mobile-app/lib/services/pending_transaction_polling_service.dart b/mobile-app/lib/services/pending_transaction_polling_service.dart index 354a0f0c..8251c1d1 100644 --- a/mobile-app/lib/services/pending_transaction_polling_service.dart +++ b/mobile-app/lib/services/pending_transaction_polling_service.dart @@ -5,6 +5,7 @@ import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/providers/pending_transactions_provider.dart'; import 'package:resonance_network_wallet/providers/wallet_providers.dart'; import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; import 'package:resonance_network_wallet/shared/utils/tx_filter_family_provider.dart'; class PendingTransactionPollingService { @@ -21,14 +22,14 @@ class PendingTransactionPollingService { /// blockNumber) matching. Fails early if neither is usable. void startPolling(PendingTransactionEvent pendingTx, {void Function(TransactionEvent result)? onFound}) { if (pendingTx.extrinsicHash == null && pendingTx.blockNumber == 0) { - print( + quantusDebugPrint( '[PendingTxPoller] ERROR: cannot poll ${pendingTx.id} — no extrinsicHash and blockNumber is 0. ' 'This would search all historical blocks and risk false positives.', ); return; } - print( + quantusDebugPrint( '[PendingTxPoller] startPolling id=${pendingTx.id} ' 'hash=${pendingTx.extrinsicHash} block=${pendingTx.blockNumber} ' 'reversible=${pendingTx.isReversible} from=${pendingTx.from} ' @@ -40,7 +41,7 @@ class PendingTransactionPollingService { final timer = Timer.periodic(_searchInterval, (_) { if (DateTime.now().difference(startTime) > _timeout) { - print('[PendingTxPoller] Timeout for ${pendingTx.id}, deferring to reconciliation'); + quantusDebugPrint('[PendingTxPoller] Timeout for ${pendingTx.id}, deferring to reconciliation'); stopPolling(pendingTx.id); _ref.read(pendingTransactionsProvider.notifier).remove(pendingTx.id); return; @@ -62,10 +63,10 @@ class PendingTransactionPollingService { final hash = pendingTx.extrinsicHash; final TransactionEvent? result; if (hash != null) { - print('[PendingTxPoller] searching by extrinsic hash $hash for ${pendingTx.id}'); + quantusDebugPrint('[PendingTxPoller] searching by extrinsic hash $hash for ${pendingTx.id}'); result = await historyService.searchByExtrinsicHash(extrinsicHash: hash, isReversible: pendingTx.isReversible); } else { - print( + quantusDebugPrint( '[PendingTxPoller] searching fallback (from, to, amount, block>${pendingTx.blockNumber}) ' 'for ${pendingTx.id}', ); @@ -79,7 +80,7 @@ class PendingTransactionPollingService { } if (result != null) { - print('[PendingTxPoller] Found matching tx for ${pendingTx.id} at block ${result.blockNumber}'); + quantusDebugPrint('[PendingTxPoller] Found matching tx for ${pendingTx.id} at block ${result.blockNumber}'); stopPolling(pendingTx.id); triggerSilentHistoryRefresh(_ref, affectedAccountIds: {pendingTx.from, pendingTx.to}, newTransaction: result); @@ -93,10 +94,10 @@ class PendingTransactionPollingService { _ref.read(pendingTransactionsProvider.notifier).remove(pendingTx.id); invalidateAccountBalances(_ref, {pendingTx.from, pendingTx.to}); } else { - print('[PendingTxPoller] no match yet for ${pendingTx.id}, will retry'); + quantusDebugPrint('[PendingTxPoller] no match yet for ${pendingTx.id}, will retry'); } } catch (e) { - print('[PendingTxPoller] Search error for ${pendingTx.id}: $e'); + quantusDebugPrint('[PendingTxPoller] Search error for ${pendingTx.id}: $e'); } } @@ -132,6 +133,6 @@ void triggerSilentHistoryRefresh(Ref ref, {required Set affectedAccountI }); } } catch (e) { - print('[SilentHistoryRefresh] Error: $e'); + quantusDebugPrint('[SilentHistoryRefresh] Error: $e'); } } diff --git a/mobile-app/lib/services/pending_transaction_reconciliation_service.dart b/mobile-app/lib/services/pending_transaction_reconciliation_service.dart index d7f53c20..368bb2b9 100644 --- a/mobile-app/lib/services/pending_transaction_reconciliation_service.dart +++ b/mobile-app/lib/services/pending_transaction_reconciliation_service.dart @@ -11,6 +11,7 @@ import 'package:resonance_network_wallet/providers/pending_cancellations_provide import 'package:resonance_network_wallet/providers/pending_transactions_provider.dart'; import 'package:resonance_network_wallet/services/transaction_service.dart'; import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; /// Service that reconciles pending transactions with confirmed transactions /// from blockchain history. This handles cases where the inBlock status @@ -26,7 +27,7 @@ class PendingTransactionReconciliationService { /// Immediately triggers reconciliation (useful for testing or manual cleanup) Future forceReconciliation() async { - print('PendingReconciliation: Force reconciliation triggered'); + quantusDebugPrint('PendingReconciliation: Force reconciliation triggered'); await reconcilePendingTransactions(); } @@ -41,7 +42,7 @@ class PendingTransactionReconciliationService { if (pendingTxs.isEmpty) return; - print( + quantusDebugPrint( 'PendingReconciliation: Checking ${pendingTxs.length} ' 'pending transactions', ); @@ -60,7 +61,7 @@ class PendingTransactionReconciliationService { final confirmedTransactions = _loadConfirmedTransactions(txService, accountIds); if (confirmedTransactions.isEmpty) { - print('PendingReconciliation: No confirmed transactions to match against'); + quantusDebugPrint('PendingReconciliation: No confirmed transactions to match against'); return; } @@ -68,11 +69,11 @@ class PendingTransactionReconciliationService { final stalePendingTxs = pendingTxs.where((tx) => _isStalePendingTransaction(tx, now)).toList(); if (stalePendingTxs.isEmpty) { - print('PendingReconciliation: No stale pending transactions found'); + quantusDebugPrint('PendingReconciliation: No stale pending transactions found'); return; } - print( + quantusDebugPrint( 'PendingReconciliation: Found ${stalePendingTxs.length} stale ' 'pending transactions', ); @@ -82,8 +83,8 @@ class PendingTransactionReconciliationService { await _reconcilePendingTransaction(pendingTx, confirmedTransactions, now); } } catch (e, stackTrace) { - print('PendingReconciliation: Error during reconciliation: $e'); - print('Stack trace: $stackTrace'); + quantusDebugPrint('PendingReconciliation: Error during reconciliation: $e'); + quantusDebugPrint('Stack trace: $stackTrace'); } } @@ -119,14 +120,14 @@ class PendingTransactionReconciliationService { bool _isStalePendingTransaction(PendingTransactionEvent pendingTx, DateTime now) { final age = now.difference(pendingTx.timestamp); - print( + quantusDebugPrint( 'PendingReconciliation: Checking tx ${pendingTx.id}: ' 'age=${age.inMinutes}min, state=${pendingTx.transactionState}', ); // Check if transaction has been pending for too long if (age > _maxPendingAge) { - print( + quantusDebugPrint( 'PendingReconciliation: Transaction ${pendingTx.id} is too ' 'old (${age.inMinutes} minutes), will be removed', ); @@ -137,7 +138,7 @@ class PendingTransactionReconciliationService { if (age > _stalePendingThreshold && (pendingTx.transactionState == TransactionState.pending || pendingTx.transactionState == TransactionState.inBlock)) { - print( + quantusDebugPrint( 'PendingReconciliation: Transaction ${pendingTx.id} is' ' stale (${age.inMinutes} minutes in ${pendingTx.transactionState} ' 'state)', @@ -145,7 +146,7 @@ class PendingTransactionReconciliationService { return true; } - print( + quantusDebugPrint( 'PendingReconciliation: Transaction ${pendingTx.id} not ' 'considered stale yet', ); @@ -163,7 +164,7 @@ class PendingTransactionReconciliationService { // If transaction is extremely old, just remove it if (age > _maxPendingAge) { - print( + quantusDebugPrint( 'PendingReconciliation: Removing expired transaction' ' ${pendingTx.id} (age: ${age.inMinutes} minutes)', ); @@ -175,12 +176,12 @@ class PendingTransactionReconciliationService { final matchingTransaction = _findMatchingConfirmedTransaction(pendingTx, confirmedTransactions); if (matchingTransaction != null) { - print( + quantusDebugPrint( 'PendingReconciliation: Found matching confirmed transaction for' ' ${pendingTx.id}', ); - print(' Pending: ${pendingTx.from} → ${pendingTx.to}, amount: ${pendingTx.amount}'); - print( + quantusDebugPrint(' Pending: ${pendingTx.from} → ${pendingTx.to}, amount: ${pendingTx.amount}'); + quantusDebugPrint( ' Confirmed: ${matchingTransaction.from} → ${matchingTransaction.to}, amount: ${matchingTransaction.amount}', ); @@ -188,17 +189,17 @@ class PendingTransactionReconciliationService { invalidateAccountBalances(_ref, {pendingTx.from, pendingTx.to}); } else { - print('PendingReconciliation: No matching confirmed transaction found for ${pendingTx.id}'); + quantusDebugPrint('PendingReconciliation: No matching confirmed transaction found for ${pendingTx.id}'); // If it's been stale for a very long time, consider it failed if (age > const Duration(minutes: 30)) { - print('PendingReconciliation: Marking long-stale transaction ${pendingTx.id} as failed'); + quantusDebugPrint('PendingReconciliation: Marking long-stale transaction ${pendingTx.id} as failed'); await _markFailedAndRemove(pendingTx, 'Transaction not found in blockchain after ${age.inMinutes} minutes'); } } } catch (e, stackTrace) { - print('PendingReconciliation: Error reconciling transaction ${pendingTx.id}: $e'); - print('Stack trace: $stackTrace'); + quantusDebugPrint('PendingReconciliation: Error reconciling transaction ${pendingTx.id}: $e'); + quantusDebugPrint('Stack trace: $stackTrace'); } } @@ -252,7 +253,7 @@ class PendingTransactionReconciliationService { /// Removes a pending transaction with logging Future _removePendingTransaction(PendingTransactionEvent pendingTx, String reason) async { - print('PendingReconciliation: Removing pending transaction ${pendingTx.id} - $reason'); + quantusDebugPrint('PendingReconciliation: Removing pending transaction ${pendingTx.id} - $reason'); // Update to inHistory state first to show completion _ref.read(pendingTransactionsProvider.notifier).updateState(pendingTx.id, TransactionState.inHistory); @@ -260,20 +261,20 @@ class PendingTransactionReconciliationService { // Remove after a short delay to let UI show the completion Timer(const Duration(seconds: 1), () { _ref.read(pendingTransactionsProvider.notifier).remove(pendingTx.id); - print('PendingReconciliation: Removed pending transaction ${pendingTx.id}'); + quantusDebugPrint('PendingReconciliation: Removed pending transaction ${pendingTx.id}'); }); } /// Marks a pending transaction as failed Future _markFailedAndRemove(PendingTransactionEvent pendingTx, String reason) async { - print('PendingReconciliation: Marking transaction ${pendingTx.id} as failed - $reason'); + quantusDebugPrint('PendingReconciliation: Marking transaction ${pendingTx.id} as failed - $reason'); _ref.read(pendingTransactionsProvider.notifier).updateState(pendingTx.id, TransactionState.failed, error: reason); // Remove failed transaction after a delay to let user see the failure Timer(const Duration(seconds: 5), () { _ref.read(pendingTransactionsProvider.notifier).remove(pendingTx.id); - print('PendingReconciliation: Removed failed transaction ${pendingTx.id}'); + quantusDebugPrint('PendingReconciliation: Removed failed transaction ${pendingTx.id}'); }); } } diff --git a/mobile-app/lib/services/referral_service.dart b/mobile-app/lib/services/referral_service.dart index 46fa64b4..19e2930e 100644 --- a/mobile-app/lib/services/referral_service.dart +++ b/mobile-app/lib/services/referral_service.dart @@ -5,6 +5,7 @@ import 'package:http/http.dart' as http; import 'package:play_install_referrer/play_install_referrer.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/models/referral_data.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; import 'package:share_plus/share_plus.dart'; class ReferralService { @@ -31,7 +32,7 @@ class ReferralService { ReferrerDetails referrerDetails = await PlayInstallReferrer.installReferrer; String? referrerString = referrerDetails.installReferrer; - print('Raw Install Referrer: $referrerString'); + quantusDebugPrint('Raw Install Referrer: $referrerString'); if (referrerString != null && referrerString.isNotEmpty) { Map params = _parseReferrer(referrerString); @@ -41,13 +42,13 @@ class ReferralService { if (referralCode != null && referralCode.isNotEmpty) { SettingsService().setReferralCode(referralCode); SettingsService().setReferralCheckCompleted(); - print('Referral Code Found: $referralCode'); + quantusDebugPrint('Referral Code Found: $referralCode'); } } - print('No referral code found'); + quantusDebugPrint('No referral code found'); } catch (e) { - print('Error checking install referrer: $e'); + quantusDebugPrint('Error checking install referrer: $e'); } } @@ -79,7 +80,7 @@ class ReferralService { headers: {'Content-Type': 'application/json'}, ); - print('getReferralData response: ${response.body}'); + quantusDebugPrint('getReferralData response: ${response.body}'); // If account doesn't have referrer, it will return 404 code. // Therefore we can confidently say it has been checked successfully. diff --git a/mobile-app/lib/services/remote_config_service.dart b/mobile-app/lib/services/remote_config_service.dart index 16396eb8..d2695149 100644 --- a/mobile-app/lib/services/remote_config_service.dart +++ b/mobile-app/lib/services/remote_config_service.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; const String remoteConfigCacheKey = 'remote_config_cache_v1'; @@ -13,7 +14,7 @@ class RemoteConfigService { final remoteData = await _taskmasterService.getRemoteConfig(); return remoteData; } catch (error) { - print('Remote config remote read failed: $error'); + quantusDebugPrint('Remote config remote read failed: $error'); return null; } } @@ -34,7 +35,7 @@ class RemoteConfigService { try { await _settingsService.setString(remoteConfigCacheKey, jsonEncode(json)); } catch (error) { - print('Remote config local save failed: $error'); + quantusDebugPrint('Remote config local save failed: $error'); } } } diff --git a/mobile-app/lib/services/reversible_transfer_monitoring_service.dart b/mobile-app/lib/services/reversible_transfer_monitoring_service.dart index ff25bff3..5147b815 100644 --- a/mobile-app/lib/services/reversible_transfer_monitoring_service.dart +++ b/mobile-app/lib/services/reversible_transfer_monitoring_service.dart @@ -9,6 +9,7 @@ import 'package:resonance_network_wallet/providers/pending_cancellations_provide import 'package:resonance_network_wallet/providers/wallet_providers.dart'; import 'package:resonance_network_wallet/providers/connectivity_provider.dart'; import 'package:resonance_network_wallet/shared/utils/polling_refresh_scope.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; import 'package:resonance_network_wallet/shared/utils/tx_filter_family_provider.dart'; /// Service that monitors reversible transfers approaching execution time @@ -58,7 +59,7 @@ class ReversibleTransferMonitoringService { .toList(); if (scheduledReversibleTransfers.isNotEmpty) { - print( + quantusDebugPrint( // ignore: lines_longer_than_80_chars 'monitoring setvice: watching ${scheduledReversibleTransfers.length} reversible transfers!', ); @@ -84,7 +85,7 @@ class ReversibleTransferMonitoringService { void _scheduleExecutionPolling(ReversibleTransferEvent transfer) { final remainingTime = transfer.remainingTime; - print( + quantusDebugPrint( 'Scheduling execution poll for ${transfer.id} ' 'in $remainingTime', ); @@ -106,7 +107,7 @@ class ReversibleTransferMonitoringService { if (_executionPollers.containsKey(transfer.id)) { return; // Already polling } - print('Starting execution polling for: ${transfer.id}'); + quantusDebugPrint('Starting execution polling for: ${transfer.id}'); // Create aggressive polling timer final poller = Timer.periodic(_pollInterval, (_) { @@ -123,12 +124,12 @@ class ReversibleTransferMonitoringService { // Check connectivity before polling final isOnline = _ref.read(isOnlineProvider); if (!isOnline) { - print('Skipping execution check - offline'); + quantusDebugPrint('Skipping execution check - offline'); return; } try { - print('polling execution on ${transfer.txId}'); + quantusDebugPrint('polling execution on ${transfer.txId}'); final historyService = _ref.read(chainHistoryServiceProvider); // Check if this specific transaction was executed using its txId @@ -136,7 +137,7 @@ class ReversibleTransferMonitoringService { final transaction = await historyService.fetchExecutedTransactionByTxId(txId: transfer.txId); if (transaction != null) { - print('Reversible transfer finished: ${transfer.id} ${transaction.status}'); + quantusDebugPrint('Reversible transfer finished: ${transfer.id} ${transaction.status}'); // Stop polling for this transfer _stopExecutionPolling(transfer.id); @@ -154,10 +155,10 @@ class ReversibleTransferMonitoringService { _ref.read(pendingCancellationsProvider.notifier).removePendingCancellation(transfer.id); - print('Updated transfer status inline - moved to done list'); + quantusDebugPrint('Updated transfer status inline - moved to done list'); } } catch (e) { - print('Error checking for transfer execution: $e'); + quantusDebugPrint('Error checking for transfer execution: $e'); // Continue polling despite errors } } @@ -165,14 +166,14 @@ class ReversibleTransferMonitoringService { void _stopExecutionPolling(String transferId) { final poller = _executionPollers.remove(transferId); poller?.cancel(); - print('Stopped execution polling for: $transferId'); + quantusDebugPrint('Stopped execution polling for: $transferId'); } void _stopMonitoringTransfer(String transferId) { final timer = _timers.remove(transferId); timer?.cancel(); _stopExecutionPolling(transferId); - print('Stopped monitoring transfer: $transferId'); + quantusDebugPrint('Stopped monitoring transfer: $transferId'); } /// Manually trigger a check for all monitored transfers (useful for testing) diff --git a/mobile-app/lib/services/transaction_submission_service.dart b/mobile-app/lib/services/transaction_submission_service.dart index 291fee68..6b5be0a4 100644 --- a/mobile-app/lib/services/transaction_submission_service.dart +++ b/mobile-app/lib/services/transaction_submission_service.dart @@ -11,6 +11,7 @@ import 'package:resonance_network_wallet/providers/notification_provider.dart'; import 'package:resonance_network_wallet/providers/pending_transactions_provider.dart'; import 'package:resonance_network_wallet/services/pending_transaction_polling_service.dart'; import 'package:resonance_network_wallet/services/telemetry_service.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; class TransactionSubmissionService { final Ref _ref; @@ -174,30 +175,30 @@ class TransactionSubmissionService { int attempt = 1, }) async { try { - print('Submitting transaction attempt $attempt/$maxRetries: ${pendingTx.id}'); + quantusDebugPrint('Submitting transaction attempt $attempt/$maxRetries: ${pendingTx.id}'); final extrinsicHashBytes = await submissionBuilder(); final extrinsicHash = '0x${hex.encode(extrinsicHashBytes)}'; - print('submission hash: $extrinsicHash'); + quantusDebugPrint('submission hash: $extrinsicHash'); final newState = TransactionState.pending; - print('updating tx ${pendingTx.amount} to $newState'); + quantusDebugPrint('updating tx ${pendingTx.amount} to $newState'); _ref .read(pendingTransactionsProvider.notifier) .updateState(pendingTx.id, newState, error: pendingTx.error, extrinsicHash: extrinsicHash); _startPollingForTransaction(pendingTx.copyWith(extrinsicHash: extrinsicHash)); } catch (e, stackTrace) { - print('Failed submitting transaction attempt $attempt: $e'); + quantusDebugPrint('Failed submitting transaction attempt $attempt: $e'); if (attempt < maxRetries) { - print('Retrying due to submission error, attempt ${attempt + 1}/$maxRetries'); + quantusDebugPrint('Retrying due to submission error, attempt ${attempt + 1}/$maxRetries'); // Brief delay before retry await Future.delayed(const Duration(seconds: 2)); await _submitAndTrackBackground(submissionBuilder, pendingTx, maxRetries: maxRetries, attempt: attempt + 1); } else { - print('Failed to submit transaction after $maxRetries attempts: $e'); - print('Stack trace: $stackTrace'); + quantusDebugPrint('Failed to submit transaction after $maxRetries attempts: $e'); + quantusDebugPrint('Stack trace: $stackTrace'); // Mark as permanently failed _ref diff --git a/mobile-app/lib/services/tx_watch_service.dart b/mobile-app/lib/services/tx_watch_service.dart index 0a880d96..41fe4ca8 100644 --- a/mobile-app/lib/services/tx_watch_service.dart +++ b/mobile-app/lib/services/tx_watch_service.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:quantus_sdk/quantus_sdk.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; class TxWatchTransfer { final String txHash; @@ -39,7 +40,7 @@ class TxWatchService { try { _ws = await WebSocket.connect(wsUrl); } catch (e) { - print('[TxWatch] WebSocket connect failed: $e'); + quantusDebugPrint('[TxWatch] WebSocket connect failed: $e'); onError(e); return; } @@ -64,7 +65,7 @@ class TxWatchService { } if (data.containsKey('error')) { final msg = (data['error'] as Map)['message']; - print('[TxWatch] RPC error: $msg'); + quantusDebugPrint('[TxWatch] RPC error: $msg'); onError(Exception(msg)); } }, onError: (e) => onError(e as Object)); diff --git a/mobile-app/lib/shared/utils/open_external_url.dart b/mobile-app/lib/shared/utils/open_external_url.dart index 6140c55a..8616ef49 100644 --- a/mobile-app/lib/shared/utils/open_external_url.dart +++ b/mobile-app/lib/shared/utils/open_external_url.dart @@ -1,3 +1,4 @@ +import 'package:resonance_network_wallet/shared/utils/print.dart'; import 'package:url_launcher/url_launcher.dart'; Future openUrl(String urlString, {LaunchMode mode = LaunchMode.platformDefault}) async { @@ -5,9 +6,9 @@ Future openUrl(String urlString, {LaunchMode mode = LaunchMode.platformDef try { final launched = await launchUrl(uri, mode: mode); if (!launched) { - print('launchUrl returned false: $urlString'); + quantusDebugPrint('launchUrl returned false: $urlString'); } } catch (e, st) { - print('launchUrl failed: $urlString error=$e\n$st'); + quantusDebugPrint('launchUrl failed: $urlString error=$e\n$st'); } } diff --git a/mobile-app/lib/v2/screens/import/import_wallet_screen.dart b/mobile-app/lib/v2/screens/import/import_wallet_screen.dart index 55ae66f6..344dff62 100644 --- a/mobile-app/lib/v2/screens/import/import_wallet_screen.dart +++ b/mobile-app/lib/v2/screens/import/import_wallet_screen.dart @@ -6,6 +6,7 @@ import 'package:resonance_network_wallet/providers/l10n_provider.dart'; import 'package:resonance_network_wallet/providers/remote_config_provider.dart'; import 'package:resonance_network_wallet/services/firebase_messaging_service.dart'; import 'package:resonance_network_wallet/services/telemetry_service.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; import 'package:resonance_network_wallet/v2/components/quantus_button.dart'; import 'package:resonance_network_wallet/v2/components/scaffold_base.dart'; import 'package:resonance_network_wallet/v2/components/scaffold_base_bottom_content.dart'; @@ -116,7 +117,7 @@ class _ImportWalletScreenV2State extends ConsumerState { ref.invalidate(accountsProvider); ref.invalidate(activeAccountProvider); } catch (e, st) { - print('error discovering accounts: $e'); + quantusDebugPrint('error discovering accounts: $e'); TelemetryService().sendError('Error discovering accounts', error: e, stackTrace: st); } } diff --git a/mobile-app/lib/wallet_initializer.dart b/mobile-app/lib/wallet_initializer.dart index 349fdda4..6d77b918 100644 --- a/mobile-app/lib/wallet_initializer.dart +++ b/mobile-app/lib/wallet_initializer.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:quantus_sdk/quantus_sdk.dart'; import 'package:resonance_network_wallet/features/components/migration_dialog.dart'; import 'package:resonance_network_wallet/providers/l10n_provider.dart'; +import 'package:resonance_network_wallet/shared/utils/print.dart'; import 'package:resonance_network_wallet/v2/components/bottom_sheet_container.dart'; import 'package:resonance_network_wallet/v2/components/quantus_button.dart'; import 'package:resonance_network_wallet/v2/components/scaffold_base.dart'; @@ -55,7 +56,7 @@ class WalletInitializerState extends ConsumerState { final migrationData = await _migrationService.getMigrationData(); for (final data in migrationData) { - print( + quantusDebugPrint( 'MIGRATION: \nold index: ${data.oldAccount.index} \nold name: ${data.oldAccount.name} \nold accountId: ${data.oldAccount.accountId} \nnew accountId: ${data.newAccountId}', ); } @@ -136,7 +137,7 @@ class WalletInitializerState extends ConsumerState { _loading = false; }); } catch (e) { - print('migration error: $e'); + quantusDebugPrint('migration error: $e'); rethrow; } } @@ -151,8 +152,8 @@ class WalletInitializerState extends ConsumerState { try { await _migrationService.performMigration(_migrationData!); } catch (e, stackTrace) { - print('error in tryLater: $e'); - print('stack trace: $stackTrace'); + quantusDebugPrint('error in tryLater: $e'); + quantusDebugPrint('stack trace: $stackTrace'); TelemetryService().sendError('Error-Migration-TryLater', error: e, stackTrace: stackTrace); rethrow; } @@ -169,7 +170,7 @@ class WalletInitializerState extends ConsumerState { } Future _uploadMigrationDataToSupabase(List migrationData) async { - print('_uploadMigrationDataToSupabase'); + quantusDebugPrint('_uploadMigrationDataToSupabase'); final supabase = EnvUtils.supabaseClient; try { @@ -184,14 +185,14 @@ class WalletInitializerState extends ConsumerState { ) .toList(); - print('uploading data to supabase: $dataToInsert'); + quantusDebugPrint('uploading data to supabase: $dataToInsert'); // Insert all records at once await supabase.from('account_id_mappings').insert(dataToInsert); - print('Successfully uploaded ${migrationData.length} migration records to Supabase'); + quantusDebugPrint('Successfully uploaded ${migrationData.length} migration records to Supabase'); } catch (e) { - print('Failed to upload migration data to Supabase: $e'); + quantusDebugPrint('Failed to upload migration data to Supabase: $e'); // Re-throw the error so it gets caught by the caller rethrow; } From 50d05ae880b496574dfb68299c6341c6547f3c4d Mon Sep 17 00:00:00 2001 From: Beast Date: Fri, 29 May 2026 14:59:06 +0800 Subject: [PATCH 10/10] chore: formatting --- mobile-app/lib/services/global_history_polling_service.dart | 2 +- mobile-app/lib/shared/utils/polling_refresh_scope.dart | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/mobile-app/lib/services/global_history_polling_service.dart b/mobile-app/lib/services/global_history_polling_service.dart index ae0c736e..2cc13a05 100644 --- a/mobile-app/lib/services/global_history_polling_service.dart +++ b/mobile-app/lib/services/global_history_polling_service.dart @@ -56,7 +56,7 @@ class GlobalHistoryPollingService { void _scheduleNextPoll() { _pollingTimer?.cancel(); - + _pollingTimer = Timer(const Duration(minutes: 1), () { _performPoll(); }); diff --git a/mobile-app/lib/shared/utils/polling_refresh_scope.dart b/mobile-app/lib/shared/utils/polling_refresh_scope.dart index 9b180ba7..9f0ab31e 100644 --- a/mobile-app/lib/shared/utils/polling_refresh_scope.dart +++ b/mobile-app/lib/shared/utils/polling_refresh_scope.dart @@ -82,11 +82,7 @@ Future silentRefreshActiveAccount(Ref ref) async { final accountId = activeAccountId(ref); if (accountId == null) return; - await refreshAccountsPagination( - ref, - accountIds: [accountId], - action: (notifier) => notifier.silentRefresh(), - ); + await refreshAccountsPagination(ref, accountIds: [accountId], action: (notifier) => notifier.silentRefresh()); } /// Refreshes the active account when the user switches accounts.