diff --git a/lib/common/components/recent_transactions/domain/transactions_repository.dart b/lib/common/components/recent_transactions/domain/transactions_repository.dart new file mode 100644 index 00000000..008ffade --- /dev/null +++ b/lib/common/components/recent_transactions/domain/transactions_repository.dart @@ -0,0 +1,58 @@ +import 'package:datadashwallet/common/components/recent_transactions/entity/transaction_history_model.dart'; +import 'package:mxc_logic/mxc_logic.dart'; +import 'package:datadashwallet/core/core.dart'; + +class TransactionsHistoryRepository extends ControlledCacheRepository { + @override + final String zone = 'transaction-history'; + + late final Field> transactionsHistory = + fieldWithDefault>('items', [], + serializer: (b) => b + .map((e) => { + 'chainId': e.chainId, + 'txList': e.txList.map((e) => e.toMap()).toList() + }) + .toList(), + deserializer: (b) => (b as List) + .map((e) => TransactionHistoryModel( + chainId: e['chainId'], + txList: (e['txList'] as List) + .map((e) => TransactionModel.fromMap(e)) + .toList(), + )) + .toList()); + + List get items => transactionsHistory.value; + + void addItem(TransactionHistoryModel item) => + transactionsHistory.value = [...transactionsHistory.value, item]; + + void addItemTx(TransactionModel item, int index) { + final newList = transactionsHistory.value; + newList[index].txList.insert(0, item); + transactionsHistory.value = newList; + } + + void updateItem(TransactionHistoryModel item, int index) { + final newList = transactionsHistory.value; + newList.removeAt(index); + newList.insert(index, item); + transactionsHistory.value = newList; + } + + void updateItemTx(TransactionModel item, int index, int txIndex) { + final newList = transactionsHistory.value; + + newList[index].txList[txIndex] = item; + + transactionsHistory.value = newList; + } + + void removeItem(TransactionHistoryModel item) => + transactionsHistory.value = transactionsHistory.value + .where((e) => e.chainId != item.chainId) + .toList(); + + void removeAll() => transactionsHistory.value = []; +} diff --git a/lib/common/components/recent_transactions/domain/transactions_use_case.dart b/lib/common/components/recent_transactions/domain/transactions_use_case.dart new file mode 100644 index 00000000..f400492d --- /dev/null +++ b/lib/common/components/recent_transactions/domain/transactions_use_case.dart @@ -0,0 +1,86 @@ +import 'package:datadashwallet/core/core.dart'; +import 'package:mxc_logic/mxc_logic.dart'; +import '../entity/transaction_history_model.dart'; +import 'transactions_repository.dart'; + +class TransactionsHistoryUseCase extends ReactiveUseCase { + TransactionsHistoryUseCase(this._repository, this._web3Repository); + + final Web3Repository _web3Repository; + + final TransactionsHistoryRepository _repository; + + late final ValueStream> transactionsHistory = + reactiveField(_repository.transactionsHistory); + + List getTransactionsHistory() => _repository.items; + + List updatingTxList = []; + + void addItem(TransactionHistoryModel item) { + _repository.addItem(item); + update(transactionsHistory, _repository.items); + } + + void updateItem(TransactionHistoryModel item, int index) { + _repository.updateItem(item, index); + update(transactionsHistory, _repository.items); + } + + void updateItemTx(TransactionModel item, int selectedNetworkChainId) { + final txHistory = transactionsHistory.value + .firstWhere((element) => element.chainId == selectedNetworkChainId); + + final index = transactionsHistory.value.indexOf(txHistory); + final txIndex = txHistory.txList.indexWhere( + (element) => element.hash == item.hash, + ); + + if (txIndex == -1) { + _repository.addItemTx(item, index); + } else { + _repository.updateItemTx(item, index, txIndex); + } + + update(transactionsHistory, _repository.items); + } + + void removeAll() { + _repository.removeAll(); + update(transactionsHistory, _repository.items); + } + + void removeItem(TransactionHistoryModel item) { + _repository.removeItem(item); + update(transactionsHistory, _repository.items); + } + + void spyOnTransaction(TransactionModel item, int chainId) { + if (!updatingTxList.contains(item.hash)) { + updatingTxList.add(item.hash); + final stream = _web3Repository.tokenContract.spyTransaction(item.hash); + stream.onData((succeeded) { + if (succeeded) { + final updatedItem = item.copyWith(status: TransactionStatus.done); + updateItemTx(updatedItem, chainId); + updatingTxList.remove(item.hash); + stream.cancel(); + } + }); + } + } + + void checkForPendingTransactions(int chainId) { + final index = transactionsHistory.value + .indexWhere((element) => element.chainId == chainId); + + if (index != -1) { + final chainTx = transactionsHistory.value[index]; + final pendingTxList = chainTx.txList + .where((element) => element.status == TransactionStatus.failed); + for (TransactionModel pendingTx in pendingTxList) { + spyOnTransaction(pendingTx, chainId); + } + } + } +} diff --git a/lib/common/components/recent_transactions/entity/transaction_history_model.dart b/lib/common/components/recent_transactions/entity/transaction_history_model.dart new file mode 100644 index 00000000..b7a8ecd2 --- /dev/null +++ b/lib/common/components/recent_transactions/entity/transaction_history_model.dart @@ -0,0 +1,11 @@ +import 'package:mxc_logic/mxc_logic.dart'; + +class TransactionHistoryModel { + TransactionHistoryModel({ + required this.chainId, + required this.txList, + }); + + int chainId; + List txList; +} diff --git a/lib/common/components/recent_transactions/recent_transactions.dart b/lib/common/components/recent_transactions/recent_transactions.dart index 3d0286a0..078d0167 100644 --- a/lib/common/components/recent_transactions/recent_transactions.dart +++ b/lib/common/components/recent_transactions/recent_transactions.dart @@ -8,9 +8,9 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:mxc_ui/mxc_ui.dart'; -enum TransactionType { sent, received, all } - -enum TransactionStatus { done, pending, failed } +export 'domain/transactions_use_case.dart'; +export 'domain/transactions_repository.dart'; +export 'entity/transaction_history_model.dart'; class RecentTransactions extends HookConsumerWidget { const RecentTransactions({ @@ -22,7 +22,7 @@ class RecentTransactions extends HookConsumerWidget { }); final String? walletAddress; - final List? transactions; + final List? transactions; final List tokens; final NetworkType? networkType; diff --git a/lib/common/components/recent_transactions/utils.dart b/lib/common/components/recent_transactions/utils.dart index 2be06817..23c3df77 100644 --- a/lib/common/components/recent_transactions/utils.dart +++ b/lib/common/components/recent_transactions/utils.dart @@ -75,96 +75,29 @@ class RecentTransactionsUtils { } static List generateTx(String walletAddressHash, - List items, List tokensList) { - List widgets = []; - - for (int i = 0; i < items.length; i++) { - final currentTx = items[i]; - String amount = '0'; - String symbol = 'Unknown'; - String timeStamp = 'Unknown'; - String hash = currentTx.hash ?? 'Unknown'; - TransactionType transactionType = TransactionType.sent; - TransactionStatus transactionStatus = TransactionStatus.done; - String logoUrl = 'assets/svg/networks/unknown.svg'; - - // two type of tx : coin_transfer from filtered tx list & token transfer from token transfer list - // If not 'contract_call' or 'coin_transfer' then empty and that means failed in other words - // another tx that we have are : pending coin transfer (which is received on both sides) & - // pending token transfer (which is only received on the sender side) - if (currentTx.result == 'pending') { - // could be contract_call || coin_transfer - transactionStatus = TransactionStatus.pending; - final time = DateTime.now(); - timeStamp = Formatter.localTime(time); - - transactionType = RecentTransactionsUtils.checkForTransactionType( - walletAddressHash, currentTx.from!.hash!.toLowerCase()); - amount = Formatter.convertWeiToEth( - currentTx.value ?? '0', Config.ethDecimals); - logoUrl = Config.mxcLogoUri; - symbol = Config.mxcName; - - if (currentTx.decodedInput != null) { - if (currentTx.to?.hash != null) { - final tokenIndex = tokensList - .indexWhere((element) => element.address == currentTx.to!.hash); - if (tokenIndex != -1) { - logoUrl = tokensList[tokenIndex].logoUri!; - } - symbol = currentTx.to!.name!; - amount = Formatter.convertWeiToEth( - currentTx.decodedInput?.parameters?[1].value ?? '0', - Config.ethDecimals); - } - } - } else if (currentTx.txTypes != null && - currentTx.txTypes!.contains('coin_transfer')) { - logoUrl = Config.mxcLogoUri; - symbol = Config.mxcSymbol; - timeStamp = Formatter.localTime(currentTx.timestamp!); - - transactionType = RecentTransactionsUtils.checkForTransactionType( - walletAddressHash, currentTx.from!.hash!.toLowerCase()); - amount = Formatter.convertWeiToEth( - currentTx.value ?? '0', Config.ethDecimals); - } else if (currentTx.txTypes == null && - currentTx.tokenTransfers != null && - currentTx.tokenTransfers![0].type == 'token_transfer') { - symbol = currentTx.tokenTransfers![0].token!.name!; - - if (currentTx.tokenTransfers![0].token!.name != null) { - final tokenIndex = tokensList.indexWhere((element) => - element.address == currentTx.tokenTransfers![0].token!.address!); - if (tokenIndex != -1) { - logoUrl = tokensList[tokenIndex].logoUri!; - } - } - - timeStamp = - Formatter.localTime(currentTx.tokenTransfers![0].timestamp!); - - amount = Formatter.convertWeiToEth( - currentTx.tokenTransfers![0].total!.value ?? '0', - Config.ethDecimals); - hash = currentTx.tokenTransfers![0].txHash ?? "Unknown"; - transactionType = RecentTransactionsUtils.checkForTransactionType( - walletAddressHash, - currentTx.tokenTransfers![0].from!.hash!.toLowerCase(), - ); - } - - widgets.add(RecentTrxListItem( + List items, List tokensList) { + return items.map((e) { + final foundToken = tokensList.firstWhere( + (element) => element.address == e.token.address, + orElse: () => Token()); + final logoUrl = foundToken.logoUri ?? + e.token.logoUri ?? + 'assets/svg/networks/unknown.svg'; + final decimal = + foundToken.decimals ?? e.token.decimals ?? Config.ethDecimals; + final symbol = foundToken.symbol ?? e.token.symbol ?? 'Unknown'; + + return RecentTrxListItem( logoUrl: logoUrl, - amount: amount, + amount: Formatter.convertWeiToEth(e.value, decimal), symbol: symbol, - timestamp: timeStamp, - txHash: hash, - transactionType: transactionType, - transactionStatus: transactionStatus, - )); - } - return widgets; + timestamp: + e.timeStamp == null ? "Unknown" : Formatter.localTime(e.timeStamp!), + txHash: e.hash, + transactionType: e.type, + transactionStatus: e.status, + ); + }).toList(); } static String getViewOtherTransactionsLink( diff --git a/lib/common/components/recent_transactions/widgets/recent_transaction_item.dart b/lib/common/components/recent_transactions/widgets/recent_transaction_item.dart index d5fdae2c..4850c84d 100644 --- a/lib/common/components/recent_transactions/widgets/recent_transaction_item.dart +++ b/lib/common/components/recent_transactions/widgets/recent_transaction_item.dart @@ -1,5 +1,5 @@ import 'package:datadashwallet/features/wallet/wallet.dart'; - +import 'package:mxc_logic/mxc_logic.dart'; import './transaction_status_chip.dart'; import './transaction_type_widget.dart'; import 'package:flutter/material.dart'; diff --git a/lib/core/src/cache/datadash_cache.dart b/lib/core/src/cache/datadash_cache.dart index 2215b5a7..fd4dc3cf 100644 --- a/lib/core/src/cache/datadash_cache.dart +++ b/lib/core/src/cache/datadash_cache.dart @@ -30,6 +30,8 @@ class DatadashCache extends CacheContainer { final BalanceRepository balanceHistory = BalanceRepository(); final RecipientsRepository recipients = RecipientsRepository(); final NftsRepository nfts = NftsRepository(); + final TransactionsHistoryRepository transactionsHistoryRepository = + TransactionsHistoryRepository(); @override List get repositories => [ @@ -38,5 +40,6 @@ class DatadashCache extends CacheContainer { balanceHistory, recipients, nfts, + transactionsHistoryRepository ]; } diff --git a/lib/core/src/providers/providers_use_cases.dart b/lib/core/src/providers/providers_use_cases.dart index 189253a3..f76449e8 100644 --- a/lib/core/src/providers/providers_use_cases.dart +++ b/lib/core/src/providers/providers_use_cases.dart @@ -113,6 +113,14 @@ final Provider chainConfigurationUseCaseProvider = ), ); +final Provider transactionHistoryUseCaseProvider = + Provider( + (ref) => TransactionsHistoryUseCase( + ref.watch(datadashCacheProvider).transactionsHistoryRepository, + ref.watch(web3RepositoryProvider), + ), +); + final Provider networkUnavailableUseCaseProvider = Provider( (ref) => NetworkUnavailableUseCase(), diff --git a/lib/features/common/contract/token_contract_use_case.dart b/lib/features/common/contract/token_contract_use_case.dart index f12f8420..4f8ccf07 100644 --- a/lib/features/common/contract/token_contract_use_case.dart +++ b/lib/features/common/contract/token_contract_use_case.dart @@ -98,20 +98,25 @@ class TokenContractUseCase extends ReactiveUseCase { update(online, result); } - Future getTokensBalance(String walletAddress) async { - try { - final result = await _repository.tokenContract - .getTokensBalance(tokensList.value, walletAddress); - update(tokensList, result); + Future getTokensBalance( + String walletAddress, bool shouldGetPrice) async { + final result = await _repository.tokenContract + .getTokensBalance(tokensList.value, walletAddress); + update(tokensList, result); + if (shouldGetPrice) { getTokensPrice(); - } catch (e) { - final newList = []; - for (Token token in tokensList.value) { - newList.add(token.copyWith(balance: 0.0)); - } - update(tokensList, newList); - getTokensBalance(walletAddress); + } else { + resetTokenPrice(); + } + } + + void resetTokenPrice() { + final List newList = []; + for (Token token in tokensList.value) { + newList.add(token.copyWith(balancePrice: 0.0)); } + update(tokensList, newList); + calculateTotalBalanceInXsd(); } Future getTokensPrice() async { @@ -175,4 +180,8 @@ class TokenContractUseCase extends ReactiveUseCase { } update(totalBalanceInXsd, totalPrice); } + + StreamSubscription spyOnTransaction(String hash) { + return _repository.tokenContract.spyTransaction(hash); + } } diff --git a/lib/features/portfolio/subfeatures/token/send_token/choose_crypto/choose_crypto_presenter.dart b/lib/features/portfolio/subfeatures/token/send_token/choose_crypto/choose_crypto_presenter.dart index a1abda56..af4b501e 100644 --- a/lib/features/portfolio/subfeatures/token/send_token/choose_crypto/choose_crypto_presenter.dart +++ b/lib/features/portfolio/subfeatures/token/send_token/choose_crypto/choose_crypto_presenter.dart @@ -1,3 +1,4 @@ +import 'package:datadashwallet/common/config.dart'; import 'package:datadashwallet/core/core.dart'; import 'package:flutter/material.dart'; @@ -10,6 +11,8 @@ final chooseCryptoPageContainer = class ChooseCryptoPresenter extends CompletePresenter { ChooseCryptoPresenter() : super(ChooseCryptoState()); + late final _chainConfigurationUseCase = + ref.read(chainConfigurationUseCaseProvider); late final _tokenContractUseCase = ref.read(tokenContractUseCaseProvider); late final _accountUserCase = ref.read(accountUseCaseProvider); late final TextEditingController searchController = TextEditingController(); @@ -25,6 +28,15 @@ class ChooseCryptoPresenter extends CompletePresenter { } }); + listen(_chainConfigurationUseCase.selectedNetwork, (value) { + if (value != null) { + state.network = value; + if (state.account != null) { + loadPage(); + } + } + }); + listen(_tokenContractUseCase.tokensList, (newTokens) { if (newTokens.isNotEmpty) { notify(() { @@ -36,7 +48,11 @@ class ChooseCryptoPresenter extends CompletePresenter { } Future loadPage() async { - await _tokenContractUseCase.getTokensBalance(state.account!.address); + final chainId = state.network!.chainId; + final shouldGetPrice = + Config.isMxcChains(chainId) || Config.isEthereumMainnet(chainId); + await _tokenContractUseCase.getTokensBalance( + state.account!.address, shouldGetPrice); } void fliterTokenByName(String value) { diff --git a/lib/features/portfolio/subfeatures/token/send_token/choose_crypto/choose_crypto_state.dart b/lib/features/portfolio/subfeatures/token/send_token/choose_crypto/choose_crypto_state.dart index d0e0db65..8da35414 100644 --- a/lib/features/portfolio/subfeatures/token/send_token/choose_crypto/choose_crypto_state.dart +++ b/lib/features/portfolio/subfeatures/token/send_token/choose_crypto/choose_crypto_state.dart @@ -5,6 +5,7 @@ class ChooseCryptoState with EquatableMixin { List? tokens; List? filterTokens; Account? account; + Network? network; @override List get props => [ diff --git a/lib/features/portfolio/subfeatures/token/send_token/send_crypto/send_crypto_presenter.dart b/lib/features/portfolio/subfeatures/token/send_token/send_crypto/send_crypto_presenter.dart index c5844e3f..23704d61 100644 --- a/lib/features/portfolio/subfeatures/token/send_token/send_crypto/send_crypto_presenter.dart +++ b/lib/features/portfolio/subfeatures/token/send_token/send_crypto/send_crypto_presenter.dart @@ -1,4 +1,5 @@ import 'package:datadashwallet/common/config.dart'; +import 'package:datadashwallet/common/utils/utils.dart'; import 'package:datadashwallet/core/core.dart'; import 'package:datadashwallet/features/common/common.dart'; import 'package:datadashwallet/features/common/app_nav_bar/app_nav_bar_presenter.dart'; @@ -37,6 +38,8 @@ class SendCryptoPresenter extends CompletePresenter { final Token token; + late final _transactionHistoryUseCase = + ref.read(transactionHistoryUseCaseProvider); late final TokenContractUseCase _tokenContractUseCase = ref.read(tokenContractUseCaseProvider); late final _accountUseCase = ref.read(accountUseCaseProvider); @@ -114,6 +117,11 @@ class SendCryptoPresenter extends CompletePresenter { double sumBalance = token.balance! - double.parse(amount); estimatedGasFee = await _estimatedFee(recipientAddress); sumBalance -= estimatedGasFee?.gasFee ?? 0.0; + final estimatedFee = estimatedGasFee == null + ? '--' + : Validation.isExpoNumber(estimatedGasFee.gasFee.toString()) + ? '0.000' + : estimatedGasFee.gasFee.toString(); final result = await showTransactionDialog(context!, amount: amount, @@ -122,7 +130,7 @@ class SendCryptoPresenter extends CompletePresenter { newtork: state.network?.label ?? '--', from: state.account!.address, to: recipient, - estimatedFee: estimatedGasFee?.gasFee.toString(), + estimatedFee: estimatedFee, onTap: (transactionType) => _nextTransactionStep(transactionType), networkSymbol: state.network?.symbol ?? '--'); } @@ -173,6 +181,18 @@ class SendCryptoPresenter extends CompletePresenter { amount: amount, tokenAddress: token.address); + final tx = TransactionModel( + hash: res, + status: TransactionStatus.pending, + type: TransactionType.sent, + value: amount.getValueInUnit(EtherUnit.wei).toString(), + token: token, + timeStamp: DateTime.now()); + + _transactionHistoryUseCase.updateItemTx(tx, state.network!.chainId); + + _transactionHistoryUseCase.spyOnTransaction(tx, state.network!.chainId); + return res; } catch (e, s) { addError(e, s); diff --git a/lib/features/portfolio/subfeatures/transaction_history/transaction_history_page.dart b/lib/features/portfolio/subfeatures/transaction_history/transaction_history_page.dart index d830780f..89a46932 100644 --- a/lib/features/portfolio/subfeatures/transaction_history/transaction_history_page.dart +++ b/lib/features/portfolio/subfeatures/transaction_history/transaction_history_page.dart @@ -58,7 +58,7 @@ class TransactionHistoryPage extends HookConsumerWidget { const SizedBox(height: 12), RecentTransactions( walletAddress: ref.watch(state).account!.address, - transactions: ref.watch(state).filterTransactions?.items, + transactions: ref.watch(state).filterTransactions, tokens: ref.watch(state).tokens, networkType: ref.watch(state).network?.networkType, ), diff --git a/lib/features/portfolio/subfeatures/transaction_history/transaction_history_presenter.dart b/lib/features/portfolio/subfeatures/transaction_history/transaction_history_presenter.dart index c69efa00..543c0a20 100644 --- a/lib/features/portfolio/subfeatures/transaction_history/transaction_history_presenter.dart +++ b/lib/features/portfolio/subfeatures/transaction_history/transaction_history_presenter.dart @@ -1,4 +1,3 @@ -import 'package:datadashwallet/common/common.dart'; import 'package:datadashwallet/common/components/recent_transactions/utils.dart'; import 'package:datadashwallet/core/core.dart'; import 'package:mxc_logic/mxc_logic.dart'; @@ -99,10 +98,25 @@ class TransactionHistoryPresenter return element.tokenTransfers![0].timestamp!.isAfter(sevenDays); }).toList()); + newTransactionsList = newTransactionsList.copyWith( + items: newTransactionsList.items!.where((element) { + if (element.timestamp != null) { + return element.timestamp!.isAfter(sevenDays); + } + return element.tokenTransfers![0].timestamp!.isAfter(sevenDays); + }).toList()); + + final newTxList = newTransactionsList.items! + .map((e) => TransactionModel.fromMXCTransaction( + e, state.account!.address)) + .toList(); + newTxList.removeWhere( + (element) => element.hash == "Unknown", + ); + notify(() { - state.transactions = newTransactionsList; - state.filterTransactions = - WannseeTransactionsModel(items: newTransactionsList!.items); + state.transactions = newTxList; + state.filterTransactions = newTxList; }); } } @@ -127,37 +141,31 @@ class TransactionHistoryPresenter state.dateSort = dateSort; state.amountSort = amountSort; - if (state.transactions == null || state.transactions?.items == null) { + if (state.transactions == null) { return; } - var result = state.transactions!.items!.where((item) { + var result = state.transactions!.where((item) { if (transactionType == TransactionType.all) return true; - if (item.from == null || item.from?.hash == null) { - return false; - } - - final type = RecentTransactionsUtils.checkForTransactionType( - state.account!.address, item.from!.hash!.toLowerCase()); - return transactionType == type; + return transactionType == item.type; }).toList(); result.sort((a, b) { if (SortOption.date == sortOption) { - final item1 = a.timestamp ?? a.tokenTransfers![0].timestamp; - final item2 = b.timestamp ?? b.tokenTransfers![0].timestamp; + final item1 = a.timeStamp; + final item2 = b.timeStamp; + + if (item1 == null || item2 == null) return 0; if (SortType.increase == dateSort) { - return item1!.compareTo(item2!); + return item1.compareTo(item2); } else { - return item2!.compareTo(item1!); + return item2.compareTo(item1); } } else { - final item1 = double.parse( - a.value ?? a.tokenTransfers?[0].total?.value ?? '0'); - final item2 = double.parse( - b.value ?? b.tokenTransfers?[0].total?.value ?? '0'); + final item1 = double.parse(a.value); + final item2 = double.parse(b.value); if (SortType.increase == amountSort) { return item1.compareTo(item2); @@ -167,8 +175,7 @@ class TransactionHistoryPresenter } }); - notify(() => state.filterTransactions = - state.filterTransactions?.copyWith(items: result)); + notify(() => state.filterTransactions = result); }, ); } diff --git a/lib/features/portfolio/subfeatures/transaction_history/transaction_history_state.dart b/lib/features/portfolio/subfeatures/transaction_history/transaction_history_state.dart index 4706440f..a0001216 100644 --- a/lib/features/portfolio/subfeatures/transaction_history/transaction_history_state.dart +++ b/lib/features/portfolio/subfeatures/transaction_history/transaction_history_state.dart @@ -6,10 +6,10 @@ import 'widgets/filter_and_sort_items.dart'; class TransactionHistoryState with EquatableMixin { Account? account; - WannseeTransactionsModel? transactions; + List? transactions; List tokens = []; - WannseeTransactionsModel? filterTransactions; + List? filterTransactions; TransactionType transactionType = TransactionType.all; SortOption sortOption = SortOption.date; diff --git a/lib/features/portfolio/subfeatures/transaction_history/widgets/filter_and_sort_dialog.dart b/lib/features/portfolio/subfeatures/transaction_history/widgets/filter_and_sort_dialog.dart index 0bcf1fcd..a89b320e 100644 --- a/lib/features/portfolio/subfeatures/transaction_history/widgets/filter_and_sort_dialog.dart +++ b/lib/features/portfolio/subfeatures/transaction_history/widgets/filter_and_sort_dialog.dart @@ -2,6 +2,7 @@ import 'package:datadashwallet/common/common.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:mxc_ui/mxc_ui.dart'; +import 'package:mxc_logic/mxc_logic.dart'; import 'filter_and_sort_items.dart'; diff --git a/lib/features/portfolio/subfeatures/transaction_history/widgets/filter_and_sort_items.dart b/lib/features/portfolio/subfeatures/transaction_history/widgets/filter_and_sort_items.dart index 30198db5..db2ed3c4 100644 --- a/lib/features/portfolio/subfeatures/transaction_history/widgets/filter_and_sort_items.dart +++ b/lib/features/portfolio/subfeatures/transaction_history/widgets/filter_and_sort_items.dart @@ -2,6 +2,7 @@ import 'package:datadashwallet/common/common.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:mxc_ui/mxc_ui.dart'; +import 'package:mxc_logic/mxc_logic.dart'; enum SortType { decrease, increase } diff --git a/lib/features/wallet/presentation/wallet_page.dart b/lib/features/wallet/presentation/wallet_page.dart index a0d95030..ea692ec7 100644 --- a/lib/features/wallet/presentation/wallet_page.dart +++ b/lib/features/wallet/presentation/wallet_page.dart @@ -20,13 +20,12 @@ class WalletPage extends HookConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final presenter = ref.read(walletContainer.actions); final state = ref.watch(walletContainer.state); - final List? txList = state.txList == null + final List? txList = state.txList == null ? null - : state.txList!.items == null - ? [] - : state.txList!.items!.length > 6 - ? state.txList?.items!.sublist(0, 6) - : state.txList!.items!; + : state.txList!.length > 6 + ? state.txList!.sublist(0, 6) + : state.txList!; + return MxcPage( useAppBar: true, presenter: presenter, diff --git a/lib/features/wallet/presentation/wallet_page_presenter.dart b/lib/features/wallet/presentation/wallet_page_presenter.dart index 8db0f426..aed3e34f 100644 --- a/lib/features/wallet/presentation/wallet_page_presenter.dart +++ b/lib/features/wallet/presentation/wallet_page_presenter.dart @@ -1,3 +1,4 @@ +import 'package:datadashwallet/common/components/components.dart'; import 'package:datadashwallet/common/config.dart'; import 'package:datadashwallet/core/core.dart'; import 'package:datadashwallet/features/wallet/wallet.dart'; @@ -20,12 +21,16 @@ class WalletPresenter extends CompletePresenter { late final _tweetsUseCase = ref.read(tweetsUseCaseProvider); late final _customTokenUseCase = ref.read(customTokensUseCaseProvider); late final _balanceUseCase = ref.read(balanceHistoryUseCaseProvider); + late final _transactionHistoryUseCase = + ref.read(transactionHistoryUseCaseProvider); @override void initState() { super.initState(); getMXCTweets(); + _transactionHistoryUseCase.checkForPendingTransactions( + _chainConfigurationUseCase.getCurrentNetworkWithoutRefresh().chainId); listen(_chainConfigurationUseCase.selectedNetwork, (value) { if (value != null) { @@ -43,6 +48,14 @@ class WalletPresenter extends CompletePresenter { } }); + listen(_transactionHistoryUseCase.transactionsHistory, (value) { + if (value.isNotEmpty && state.network != null) { + if (!Config.isMxcChains(state.network!.chainId)) { + getCustomChainsTransactions(value); + } + } + }); + listen(_accountUserCase.xsdConversionRate, (value) { notify(() => state.xsdConversionRate = value); }); @@ -86,12 +99,7 @@ class WalletPresenter extends CompletePresenter { Future initializeWalletPage() async { initializeBalancePanelAndTokens(); createSubscriptions(); - if (Config.isMxcChains(state.network!.chainId)) { - getTransactions(); - } else { - notify(() => state.txList = WannseeTransactionsModel(items: const [])); - } - + getTransactions(); } void createSubscriptions() async { @@ -119,7 +127,10 @@ class WalletPresenter extends CompletePresenter { final newTx = WannseeTransactionModel.fromJson( json.encode(event.payload['transactions'][0])); if (newTx.value != null) { - notify(() => state.txList!.items!.insert(0, newTx)); + notify(() => state.txList!.insert( + 0, + TransactionModel.fromMXCTransaction( + newTx, state.walletAddress!))); } break; // coin transfer done @@ -130,15 +141,21 @@ class WalletPresenter extends CompletePresenter { // We will filter token_transfer tx because It is also received from token_transfer event if (newTx.txTypes != null && !(newTx.txTypes!.contains('token_transfer'))) { - final itemIndex = state.txList!.items! + final itemIndex = state.txList! .indexWhere((txItem) => txItem.hash == newTx.hash); // checking for if the transaction is found. if (itemIndex != -1) { - notify(() => state.txList!.items! - .replaceRange(itemIndex, itemIndex + 1, [newTx])); + notify(() => state.txList!.replaceRange( + itemIndex, itemIndex + 1, [ + TransactionModel.fromMXCTransaction( + newTx, state.walletAddress!) + ])); } else { // we must have missed the pending tx - notify(() => state.txList!.items!.insert(0, newTx)); + notify(() => state.txList!.insert( + 0, + TransactionModel.fromMXCTransaction( + newTx, state.walletAddress!))); } } } @@ -150,19 +167,23 @@ class WalletPresenter extends CompletePresenter { if (newTx.txHash != null) { // Sender will get pending tx // Receiver won't get pending tx - final itemIndex = state.txList!.items! + final itemIndex = state.txList! .indexWhere((txItem) => txItem.hash == newTx.txHash); // checking for if the transaction is found. if (itemIndex != -1) { - notify(() => state.txList!.items! - .replaceRange(itemIndex, itemIndex + 1, [ - WannseeTransactionModel(tokenTransfers: [newTx]) + notify(() => + state.txList!.replaceRange(itemIndex, itemIndex + 1, [ + TransactionModel.fromMXCTransaction( + WannseeTransactionModel(tokenTransfers: [newTx]), + state.walletAddress!) ])); } else { // we must have missed the token transfer pending tx - notify(() => state.txList!.items!.insert( + notify(() => state.txList!.insert( 0, - WannseeTransactionModel(tokenTransfers: [newTx]), + TransactionModel.fromMXCTransaction( + WannseeTransactionModel(tokenTransfers: [newTx]), + state.walletAddress!), )); } } @@ -171,7 +192,7 @@ class WalletPresenter extends CompletePresenter { case 'balance': final wannseeBalanceEvent = WannseeBalanceModel.fromJson(event.payload); - getWalletTokensBalance(); + getWalletTokensBalance(true); break; default: } @@ -181,6 +202,34 @@ class WalletPresenter extends CompletePresenter { } void getTransactions() async { + if (Config.isMxcChains(state.network!.chainId)) { + getMXCTransactions(); + } else { + getCustomChainsTransactions(null); + } + } + + void getCustomChainsTransactions(List? txHistory) { + txHistory = + txHistory ?? _transactionHistoryUseCase.getTransactionsHistory(); + + if (state.network != null) { + final index = txHistory + .indexWhere((element) => element.chainId == state.network!.chainId); + + if (index == -1) { + _transactionHistoryUseCase.addItem(TransactionHistoryModel( + chainId: state.network!.chainId, txList: [])); + return; + } + + final chainTxHistory = txHistory[index]; + + notify(() => state.txList = chainTxHistory.txList); + } + } + + void getMXCTransactions() async { // final walletAddress = await _walletUserCase.getPublicAddress(); // transactions list contains all the kind of transactions // It's going to be filtered to only have native coin transfer @@ -231,12 +280,22 @@ class WalletPresenter extends CompletePresenter { } }); } + if (newTransactionsList.items!.length > 6) { newTransactionsList = newTransactionsList.copyWith( items: newTransactionsList.items!.sublist(0, 6)); } - notify(() => state.txList = newTransactionsList); + final finalTxList = newTransactionsList.items! + .map((e) => + TransactionModel.fromMXCTransaction(e, state.walletAddress!)) + .toList(); + + finalTxList.removeWhere( + (element) => element.hash == "Unknown", + ); + + notify(() => state.txList = finalTxList); } } else { // looks like error @@ -245,25 +304,10 @@ class WalletPresenter extends CompletePresenter { }); } - void getTransaction( - String hash, - ) async { - final newTx = await _tokenContractUseCase.getTransactionByHash(hash); - - if (newTx != null) { - final oldTx = state.txList!.items! - .firstWhere((element) => element.hash == newTx.hash); - oldTx.tokenTransfers = [TokenTransfer()]; - oldTx.tokenTransfers![0].from = newTx.tokenTransfers![0].from; - oldTx.tokenTransfers![0].to = newTx.tokenTransfers![0].to; - notify( - () => oldTx.value = newTx.tokenTransfers![0].total!.value.toString()); - } - } - initializeBalancePanelAndTokens() { - getDefaultTokens().then((value) => - value != null ? getWalletTokensBalance() : getDefaultTokens()); + getDefaultTokens().then((value) => getWalletTokensBalance( + Config.isMxcChains(state.network!.chainId) || + Config.isEthereumMainnet(state.network!.chainId))); } Future getDefaultTokens() async { @@ -383,7 +427,8 @@ class WalletPresenter extends CompletePresenter { } } - void getWalletTokensBalance() async { - _tokenContractUseCase.getTokensBalance(state.walletAddress!); + void getWalletTokensBalance(bool shouldGetPrice) async { + _tokenContractUseCase.getTokensBalance( + state.walletAddress!, shouldGetPrice); } } diff --git a/lib/features/wallet/presentation/wallet_page_state.dart b/lib/features/wallet/presentation/wallet_page_state.dart index 5b765f40..d6d421aa 100644 --- a/lib/features/wallet/presentation/wallet_page_state.dart +++ b/lib/features/wallet/presentation/wallet_page_state.dart @@ -8,7 +8,7 @@ class WalletState with EquatableMixin { String walletBalance = "0.0"; - WannseeTransactionsModel? txList; + List? txList; bool isTxListLoading = true; diff --git a/packages/shared b/packages/shared index 48c16017..b3dfc273 160000 --- a/packages/shared +++ b/packages/shared @@ -1 +1 @@ -Subproject commit 48c16017adf3404442c0385184880342578d9031 +Subproject commit b3dfc273f494a53f6570d1f59cf94ca0618b6da0