-
Notifications
You must be signed in to change notification settings - Fork 3
Polling Bug #502
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Polling Bug #502
Changes from all commits
5e92394
322d04e
2fffead
0b4ee4a
a9ba276
62e6719
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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<void> 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); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Silent refresh triggered twice for active accountLow Severity
Additional Locations (1)Reviewed by Cursor Bugbot for commit 62e6719. Configure here. |
||
| 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); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reconciliation
|
||
| ); | ||
| } | ||
|
|
||
| // 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: () => <TransactionEvent>[], | ||
| error: (_, _) => <TransactionEvent>[], | ||
| ); | ||
| final confirmedTransactions = _loadConfirmedTransactions(txService, accountIds); | ||
|
|
||
| if (confirmedTransactions.isEmpty) { | ||
| print('PendingReconciliation: No confirmed transactions to match against'); | ||
|
|
@@ -85,6 +87,33 @@ class PendingTransactionReconciliationService { | |
| } | ||
| } | ||
|
|
||
| List<TransactionEvent> _loadConfirmedTransactions(TransactionService txService, Set<String> accountIds) { | ||
| final pendingCancellationIds = _ref.read(pendingCancellationsProvider); | ||
| final confirmedById = <String, TransactionEvent>{}; | ||
|
|
||
| for (final accountId in accountIds) { | ||
| final params = FilteredTransactionsParams( | ||
| accountIds: AccountIdListCache.get([accountId]), | ||
| filter: TransactionFilter.all, | ||
| ); | ||
| 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; | ||
| } | ||
| } | ||
|
|
||
| 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 +186,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}'); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<String, Timer> _timers = {}; | ||
| final Map<String, Timer> _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,14 +46,10 @@ class ReversibleTransferMonitoringService { | |
| } | ||
|
|
||
| void _listenToTransactions() { | ||
| _ref.listen(allTransactionsProvider, (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); | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -144,14 +143,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 = <String>{transfer.from, transfer.to}; | ||
| for (final accountId in affectedAccounts) { | ||
|
|
@@ -160,15 +152,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 +180,7 @@ class ReversibleTransferMonitoringService { | |
| /// Manually trigger a check for all monitored transfers (useful for testing) | ||
| Future<void> forceCheckAllMonitoredTransfers() async { | ||
| if (_executionPollers.isNotEmpty) { | ||
| await _ref.read(paginationControllerProvider.notifier).silentRefresh(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Subscription not closed in dispose methodMedium Severity The new Reviewed by Cursor Bugbot for commit 62e6719. Configure here. |
||
| final active = _ref.read(activeAccountProvider).value; | ||
| if (active != null) { | ||
| updatePaginationFiltersFor(_ref.read, [active.account.accountId], (notifier, _) { | ||
| notifier.silentRefresh(); | ||
| }); | ||
| } | ||
| await silentRefreshActiveAccount(_ref); | ||
| } | ||
| } | ||
|
|
||
|
|
||


Uh oh!
There was an error while loading. Please reload this page.