From de9ea917a3bd06cc7c4becbf937eade3ba3c92a4 Mon Sep 17 00:00:00 2001 From: redDwarf03 Date: Mon, 20 Jan 2025 00:08:07 +0100 Subject: [PATCH] perf: :zap: Use AEIP21 updates to display recent txn --- lib/application/account/account_notifier.dart | 435 ++++++----- .../account/account_notifier.g.dart | 2 +- lib/application/account/accounts.dart | 13 +- lib/application/account/accounts.g.dart | 52 +- .../account/accounts_notifier.dart | 10 - .../account/accounts_notifier.g.dart | 2 +- lib/application/account/selected_account.dart | 16 - .../account/selected_account.g.dart | 20 - lib/application/address_service.dart | 15 + lib/application/address_service.g.dart | 150 ++++ lib/application/formated_name.dart | 84 +++ lib/application/formated_name.g.dart | 181 +++++ lib/application/recent_transactions.dart | 44 ++ lib/application/recent_transactions.g.dart | 188 +++++ lib/domain/repositories/market/market.dart | 29 - .../tokens/verified_tokens_repository.dart | 7 - .../tokens/tokens.repository.dart | 3 +- .../recent_transactions.repository.dart | 562 +++++++++++---- lib/l10n/intl_en.arb | 51 -- lib/l10n/intl_fr.arb | 47 -- lib/model/blockchain/recent_transaction.dart | 234 +++--- .../recent_transaction.freezed.dart | 614 ++++++++++++++++ .../blockchain/recent_transaction.g.dart | 33 +- lib/model/blockchain/token_information.dart | 124 +--- .../blockchain/token_information.freezed.dart | 472 ++++++++++++ lib/model/blockchain/token_information.g.dart | 48 +- lib/model/data/account.dart | 3 +- lib/model/data/account.freezed.dart | 49 +- lib/model/data/account.g.dart | 5 +- lib/model/data/account_token.dart | 12 +- lib/model/data/hive_app_wallet_dto.dart | 1 - lib/model/transaction_infos.dart | 65 -- .../aeswap/application/session/state.dart | 7 + lib/router/router.authenticated.dart | 9 - lib/router/router.dart | 1 - lib/service/app_service.dart | 677 ------------------ lib/ui/menu/settings/security_menu_view.dart | 3 - .../views/add_custom_token/bloc/provider.dart | 8 +- .../add_custom_token/bloc/provider.g.dart | 2 +- .../layouts/add_custom_token_sheet.dart | 8 +- .../main/components/menu_widget_wallet.dart | 16 +- lib/ui/views/main/transactions_tab.dart | 85 +-- .../views/nft_search/bloc/state.freezed.dart | 19 + .../layouts/token_detail_sheet.dart | 7 +- .../template/transaction_action.dart | 23 + .../components/template/transaction_fees.dart | 61 +- .../template/transaction_information.dart | 29 +- .../template/transaction_ledger_mvts.dart | 144 ++++ .../template/transaction_template.dart | 116 ++- .../template/transaction_warning.dart | 35 - .../components/template/transfer_balance.dart | 59 -- .../token_creation_balance.dart | 40 +- .../token_creation_information.dart | 13 +- .../transaction_hosting_information.dart | 1 - .../transaction_input/transaction_input.dart | 21 +- .../transaction_input_information.dart | 34 +- .../transaction_output.dart | 26 +- .../transaction_output_icon.dart | 18 +- .../transaction_output_information.dart | 34 - .../components/transfer_transaction.dart | 61 -- .../transactions/transaction_infos_sheet.dart | 355 --------- .../views/transactions/transactions_list.dart | 87 ++- lib/ui/views/transfer/bloc/provider.dart | 10 +- .../components/transfer_confirm_sheet.dart | 7 +- .../widgets/tokens/verified_token_icon.dart | 8 +- lib/util/keychain_util.dart | 1 - test/token_parser_test.mocks.dart | 33 +- 67 files changed, 3209 insertions(+), 2420 deletions(-) create mode 100644 lib/application/formated_name.dart create mode 100644 lib/application/formated_name.g.dart create mode 100644 lib/application/recent_transactions.dart create mode 100644 lib/application/recent_transactions.g.dart delete mode 100644 lib/domain/repositories/market/market.dart delete mode 100644 lib/domain/repositories/tokens/verified_tokens_repository.dart create mode 100644 lib/model/blockchain/recent_transaction.freezed.dart create mode 100644 lib/model/blockchain/token_information.freezed.dart delete mode 100644 lib/model/transaction_infos.dart create mode 100644 lib/ui/views/transactions/components/template/transaction_action.dart create mode 100644 lib/ui/views/transactions/components/template/transaction_ledger_mvts.dart delete mode 100644 lib/ui/views/transactions/components/template/transaction_warning.dart delete mode 100644 lib/ui/views/transactions/components/template/transfer_balance.dart delete mode 100644 lib/ui/views/transactions/components/transaction_output/transaction_output_information.dart delete mode 100644 lib/ui/views/transactions/components/transfer_transaction.dart delete mode 100755 lib/ui/views/transactions/transaction_infos_sheet.dart diff --git a/lib/application/account/account_notifier.dart b/lib/application/account/account_notifier.dart index bfcafe8d2..2fba76181 100644 --- a/lib/application/account/account_notifier.dart +++ b/lib/application/account/account_notifier.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'package:aewallet/application/app_service.dart'; import 'package:aewallet/application/nft/nft.dart'; +import 'package:aewallet/application/recent_transactions.dart'; import 'package:aewallet/application/refresh_in_progress.dart'; import 'package:aewallet/application/session/session.dart'; import 'package:aewallet/infrastructure/datasources/account.hive.dart'; @@ -30,23 +31,39 @@ class AccountNotifier extends _$AccountNotifier { return await AccountLocalRepository().getAccount(accountName); } - Future _refresh( - List Function(Account account)> doRefreshes, - ) async { + /// Updates the account in Hive and handles errors consistently + Future _updateAccount() async { + if (state.value == null) return; + try { - final account = await future; - if (account == null) return; + await AccountHiveDatasource.instance().updateAccount(state.value!); + _logger.fine('Account updated successfully'); + } catch (error, stack) { + _logger.severe('Failed to update account', error, stack); + } + } + /// Executes a refresh operation with proper error handling and state management + Future _performOperation( + String operationName, + Future Function(Account account) operation, + ) async { + final account = state.value; + if (account == null) { + _logger.warning('$operationName: No account available'); + return; + } + + _logger.fine('Starting $operationName'); + try { ref.read(refreshInProgressNotifierProvider.notifier).refreshInProgress = true; - for (final doRefresh in doRefreshes) { - await doRefresh(account); + await operation(account); + await _updateAccount(); - final newAccountData = account.copyWith(); - state = AsyncData(newAccountData); - } + _logger.fine('$operationName completed successfully'); } catch (e, stack) { - _logger.severe('Refresh failed', e, stack); + _logger.severe('$operationName failed', e, stack); } finally { ref.read(refreshInProgressNotifierProvider.notifier).refreshInProgress = false; @@ -54,69 +71,47 @@ class AccountNotifier extends _$AccountNotifier { } Future refreshRecentTransactions() async { - await _refresh([ - (account) async { - await updateRecentTransactions(); - }, - ]); + await _performOperation( + 'Recent Transactions', + updateRecentTransactions, + ); } - Future refreshFungibleTokens() async { - await _refresh([ - (account) async { - await updateFungiblesTokens(); - }, - ]); - } + Future refreshFungibleTokens() => + _performOperation('Refresh Fungible Tokens', updateFungibleTokens); - Future refreshNFT() async { - await _refresh([ - (account) async { - await updateNFT(); - }, - ]); - } + Future refreshNFT() => _performOperation('Refresh NFT', updateNFT); - Future refreshBalance() async { - await _refresh([ - (account) async { - await updateBalance(); - }, - ]); - } + Future refreshBalance() => + _performOperation('Refresh Balance', updateBalance); Future refreshAll() async { - await _refresh( - [ - (account) async { - await updateBalance(); - }, - (account) async { - await updateRecentTransactions(); - }, - (account) async { - await updateFungiblesTokens(); - }, - (account) async { - await updateNFT(); - }, - ], - ); + final operations = [ + updateBalance, + updateFungibleTokens, + updateNFT, + ]; + + for (final operation in operations) { + await _performOperation('Refresh All', operation); + } } - Future updateBalance() async { - _logger.fine('RefreshAll - Start Balance refresh'); - await _update((account) async { - var totalUSD = 0.0; + Future updateBalance(Account account) async { + _logger.fine('Starting balance update'); - final balanceGetResponseMap = await ref - .read(appServiceProvider) - .getBalanceGetResponse([account.genesisAddress]); + final balanceGetResponseMap = await ref + .read(appServiceProvider) + .getBalanceGetResponse([account.genesisAddress]); - if (balanceGetResponseMap[account.genesisAddress] == null) { - return account; - } + if (balanceGetResponseMap[account.genesisAddress] == null) { + _logger.warning( + 'No balance response for address: ${account.genesisAddress}', + ); + return; + } + try { final ucidsTokens = await ref.read( aedappfm.UcidsTokensProviders.ucidsTokens( environment: ref.read(environmentProvider), @@ -124,170 +119,232 @@ class AccountNotifier extends _$AccountNotifier { ); final cryptoPrice = ref.read(aedappfm.CoinPriceProviders.coinPrices); - final balanceGetResponse = balanceGetResponseMap[account.genesisAddress]!; - final ucoAmount = fromBigInt(balanceGetResponse.uco).toDouble(); - final accountBalance = AccountBalance( - nativeTokenName: AccountBalance.cryptoCurrencyLabel, - nativeTokenValue: ucoAmount, + + final accountBalance = await _calculateAccountBalance( + balanceGetResponse, + ucidsTokens, + cryptoPrice, ); - if (balanceGetResponse.uco > 0) { - accountBalance.tokensFungiblesNb++; + state = AsyncData(account.copyWith(balance: accountBalance)); - final archethicOracleUCO = await ref.read( - aedappfm.ArchethicOracleUCOProviders.archethicOracleUCO.future, - ); - totalUSD = (Decimal.parse(totalUSD.toString()) + - Decimal.parse(ucoAmount.toString()) * - Decimal.parse(archethicOracleUCO.usd.toString())) - .toDouble(); - } + _logger.fine('Balance update completed successfully'); + return; + } catch (e, stack) { + _logger.severe('Failed to update balance', e, stack); + } + } + + Future _calculateAccountBalance( + Balance balanceGetResponse, + Map ucidsTokens, + aedappfm.CryptoPrice cryptoPrice, + ) async { + final ucoAmount = fromBigInt(balanceGetResponse.uco).toDouble(); + final accountBalance = AccountBalance( + nativeTokenName: AccountBalance.cryptoCurrencyLabel, + nativeTokenValue: ucoAmount, + ); + + var totalUSD = 0.0; + + if (balanceGetResponse.uco > 0) { + accountBalance.tokensFungiblesNb++; + + final archethicOracleUCO = await ref.read( + aedappfm.ArchethicOracleUCOProviders.archethicOracleUCO.future, + ); + totalUSD = (Decimal.parse(totalUSD.toString()) + + Decimal.parse(ucoAmount.toString()) * + Decimal.parse(archethicOracleUCO.usd.toString())) + .toDouble(); + } + + totalUSD += await _calculateTokensValue( + balanceGetResponse.token, + accountBalance, + ucidsTokens, + cryptoPrice, + ); - for (final token in balanceGetResponse.token) { - if (token.tokenId != null) { - if (token.tokenId == 0) { - accountBalance.tokensFungiblesNb++; - - final ucidsToken = ucidsTokens[token.address]; - if (ucidsToken != null && ucidsToken != 0) { - final amountTokenUSD = - (Decimal.parse(fromBigInt(token.amount).toString()) * - Decimal.parse( - aedappfm.CoinPriceRepositoryImpl() - .getPriceFromUcid(ucidsToken, cryptoPrice) - .toString(), - )) - .toDouble(); - totalUSD = totalUSD + amountTokenUSD; - } - } else { - accountBalance.nftNb++; + accountBalance.totalUSD = totalUSD; + return accountBalance; + } + + Future _calculateTokensValue( + List tokens, + AccountBalance accountBalance, + Map ucidsTokens, + aedappfm.CryptoPrice cryptoPrice, + ) async { + var totalUSD = 0.0; + + for (final token in tokens) { + if (token.tokenId != null) { + if (token.tokenId == 0) { + accountBalance.tokensFungiblesNb++; + + final ucidsToken = ucidsTokens[token.address]; + if (ucidsToken != null && ucidsToken != 0) { + final amountTokenUSD = + (Decimal.parse(fromBigInt(token.amount).toString()) * + Decimal.parse( + aedappfm.CoinPriceRepositoryImpl() + .getPriceFromUcid(ucidsToken, cryptoPrice) + .toString(), + )) + .toDouble(); + totalUSD += amountTokenUSD; } + } else { + accountBalance.nftNb++; } } - accountBalance.totalUSD = totalUSD; + } - return account.copyWith(balance: accountBalance); - }); - _logger.fine('RefreshAll - End Balance refresh'); + return totalUSD; } - Future updateFungiblesTokens() async { - _logger.fine('RefreshAll - Start Fungible Tokens refresh'); - await _update((account) async { + Future updateFungibleTokens(Account account) async { + _logger.fine('Starting fungible tokens update'); + try { final appService = ref.read(appServiceProvider); final poolsListRaw = await ref.read(DexPoolProviders.getPoolListRaw.future); - return account.copyWith( - accountTokens: await appService.getFungiblesTokensList( + final fungiblesTokensList = await appService.getFungiblesTokensList( + account.genesisAddress, + poolsListRaw, + ); + + state = AsyncData(account.copyWith(accountTokens: fungiblesTokensList)); + _logger.fine('Fungible tokens update completed successfully'); + return; + } catch (e, stack) { + _logger.severe('Failed to update fungible tokens', e, stack); + } + } + + Future updateRecentTransactions(Account account) async { + _logger.fine('Starting recent transactions update'); + try { + ref.invalidate( + recentTransactionsProvider( account.genesisAddress, - poolsListRaw, ), ); - }); - _logger.fine('RefreshAll - End Fungible Tokens refresh'); + _logger.fine('Recent transactions update completed successfully'); + return; + } catch (e, stack) { + _logger.severe('Failed to update recent transactions', e, stack); + } } - Future updateRecentTransactions() async { - _logger.fine('RefreshAll - Start recent transactions refresh'); - await _update((account) async { + Future updateNFT(Account account) async { + _logger.fine('Starting NFT update'); + try { final session = ref.read(sessionNotifierProvider).loggedIn!; - final appService = ref.read(appServiceProvider); - - return account.copyWith( - recentTransactions: await appService.getAccountRecentTransactions( + final tokenInformation = await ref.read( + NFTProviders.getNFTList( account.genesisAddress, account.name, session.wallet.keychainSecuredInfos, - account.recentTransactions ?? [], + ).future, + ); + + state = AsyncData( + account.copyWith( + accountNFT: tokenInformation.$1, + accountNFTCollections: tokenInformation.$2, ), - lastLoadingTransactionInputs: DateTime.now().millisecondsSinceEpoch ~/ - Duration.millisecondsPerSecond, ); - }); - _logger.fine('RefreshAll - End recent transactions refresh'); + _logger.fine('NFT update completed successfully'); + return; + } catch (e, stack) { + _logger.severe('Failed to update NFT', e, stack); + } } - Future addCustomTokenAddress(String tokenAddress) async { - if (Address(address: tokenAddress).isValid() == false) return; - await _update((account) async { - return account.copyWith( - customTokenAddressList: [ - ...account.customTokenAddressList ?? [], - tokenAddress.toUpperCase(), - ], + Future addCustomTokenAddress( + Account account, + String tokenAddress, + ) async { + _logger.fine('Adding custom token address'); + try { + if (Address(address: tokenAddress).isValid() == false) { + _logger.warning('Invalid token address: $tokenAddress'); + return; + } + + state = AsyncData( + account.copyWith( + customTokenAddressList: [ + ...account.customTokenAddressList ?? [], + tokenAddress.toUpperCase(), + ], + ), ); - }); + await _updateAccount(); + + _logger.fine('Custom token address added successfully'); + } catch (e, stack) { + _logger.severe('Failed to add custom token address', e, stack); + } } - Future removeCustomTokenAddress(String tokenAddress) async { - if (Address(address: tokenAddress).isValid() == false) return; - await _update((account) async { + Future removeCustomTokenAddress( + Account account, + String tokenAddress, + ) async { + _logger.fine('Removing custom token address'); + try { + if (Address(address: tokenAddress).isValid() == false) { + _logger.warning('Invalid token address: $tokenAddress'); + return; + } + final customTokenAddressList = account.customTokenAddressList; - if (customTokenAddressList == null) return account; - - return account.copyWith( - customTokenAddressList: customTokenAddressList - .where( - (element) => element != tokenAddress.toUpperCase(), - ) - .toList(), + if (customTokenAddressList == null) { + _logger.warning('No custom token addresses found'); + return; + } + + state = AsyncData( + account.copyWith( + customTokenAddressList: customTokenAddressList + .where( + (element) => element != tokenAddress.toUpperCase(), + ) + .toList(), + ), ); - }); - } + await _updateAccount(); - Future checkCustomTokenAddress(String tokenAddress) async { - final account = await future; - if (account == null) { - return false; + _logger.fine('Custom token address removed successfully'); + } catch (e, stack) { + _logger.severe('Failed to remove custom token address', e, stack); } - - if (Address(address: tokenAddress).isValid() == false) return false; - return (account.customTokenAddressList ?? []) - .contains(tokenAddress.toUpperCase()); } - Future updateNFT() async { - _logger.fine('RefreshAll - Start NFT refresh'); - await _update( - (account) async { - final session = ref.read(sessionNotifierProvider).loggedIn!; - final tokenInformation = await ref.read( - NFTProviders.getNFTList( - account.genesisAddress, - account.name, - session.wallet.keychainSecuredInfos, - ).future, - ); - - return account.copyWith( - accountNFT: tokenInformation.$1, - accountNFTCollections: tokenInformation.$2, - ); - }, - ); - _logger.fine('RefreshAll - End NFT refresh'); - } + Future checkCustomTokenAddress(String tokenAddress) async { + try { + final account = await future; + if (account == null) { + _logger.warning('Account not found for token address check'); + return false; + } - Future clearRecentTransactionsFromCache() async { - await _update( - (account) => account.copyWith(recentTransactions: []), - ); - } + if (Address(address: tokenAddress).isValid() == false) { + _logger.warning('Invalid token address: $tokenAddress'); + return false; + } - Future _update( - FutureOr Function(Account) doUpdate, - ) async { - await update( - (account) async { - if (account == null) return null; - - final newState = await doUpdate(account); - await AccountHiveDatasource.instance().updateAccount(newState); - return newState; - }, - ); + return (account.customTokenAddressList ?? []) + .contains(tokenAddress.toUpperCase()); + } catch (e, stack) { + _logger.severe('Failed to check custom token address', e, stack); + return false; + } } } diff --git a/lib/application/account/account_notifier.g.dart b/lib/application/account/account_notifier.g.dart index 44cb5529c..18f3753c5 100644 --- a/lib/application/account/account_notifier.g.dart +++ b/lib/application/account/account_notifier.g.dart @@ -6,7 +6,7 @@ part of 'account_notifier.dart'; // RiverpodGenerator // ************************************************************************** -String _$accountNotifierHash() => r'de8963b79efec47c784a345e2fda8bed1be89951'; +String _$accountNotifierHash() => r'9769742204cef6836ddfd285e8c4d8a58afa2c7d'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/application/account/accounts.dart b/lib/application/account/accounts.dart index a2bfcb7e0..d1e0c1f30 100644 --- a/lib/application/account/accounts.dart +++ b/lib/application/account/accounts.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:aewallet/application/account/accounts_notifier.dart'; +import 'package:aewallet/application/address_service.dart'; import 'package:aewallet/model/data/account.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -12,10 +13,16 @@ part 'accounts.g.dart'; @riverpod Future accountWithGenesisAddress( Ref ref, - String genesisAddress, -) async { + String address, { + bool searchGenesisAddress = false, +}) async { + var addressToSearch = address; + if (searchGenesisAddress) { + addressToSearch = await ref.watch(genesisAddressProvider(address).future); + } + final accounts = await ref.watch(accountsNotifierProvider.future); - return accounts.getAccountWithGenesisAddress(genesisAddress); + return accounts.getAccountWithGenesisAddress(addressToSearch); } @riverpod diff --git a/lib/application/account/accounts.g.dart b/lib/application/account/accounts.g.dart index d59f84d54..d21dad947 100644 --- a/lib/application/account/accounts.g.dart +++ b/lib/application/account/accounts.g.dart @@ -7,7 +7,7 @@ part of 'accounts.dart'; // ************************************************************************** String _$accountWithGenesisAddressHash() => - r'8db23b0bcb13925deb221daa6ec7637b05803f51'; + r'52ee31c2665b1e4690f91f8ae06beef075023f64'; /// Copied from Dart SDK class _SystemHash { @@ -41,10 +41,12 @@ class AccountWithGenesisAddressFamily extends Family> { /// See also [accountWithGenesisAddress]. AccountWithGenesisAddressProvider call( - String genesisAddress, - ) { + String address, { + bool searchGenesisAddress = false, + }) { return AccountWithGenesisAddressProvider( - genesisAddress, + address, + searchGenesisAddress: searchGenesisAddress, ); } @@ -53,7 +55,8 @@ class AccountWithGenesisAddressFamily extends Family> { covariant AccountWithGenesisAddressProvider provider, ) { return call( - provider.genesisAddress, + provider.address, + searchGenesisAddress: provider.searchGenesisAddress, ); } @@ -77,11 +80,13 @@ class AccountWithGenesisAddressProvider extends AutoDisposeFutureProvider { /// See also [accountWithGenesisAddress]. AccountWithGenesisAddressProvider( - String genesisAddress, - ) : this._internal( + String address, { + bool searchGenesisAddress = false, + }) : this._internal( (ref) => accountWithGenesisAddress( ref as AccountWithGenesisAddressRef, - genesisAddress, + address, + searchGenesisAddress: searchGenesisAddress, ), from: accountWithGenesisAddressProvider, name: r'accountWithGenesisAddressProvider', @@ -92,7 +97,8 @@ class AccountWithGenesisAddressProvider dependencies: AccountWithGenesisAddressFamily._dependencies, allTransitiveDependencies: AccountWithGenesisAddressFamily._allTransitiveDependencies, - genesisAddress: genesisAddress, + address: address, + searchGenesisAddress: searchGenesisAddress, ); AccountWithGenesisAddressProvider._internal( @@ -102,10 +108,12 @@ class AccountWithGenesisAddressProvider required super.allTransitiveDependencies, required super.debugGetCreateSourceHash, required super.from, - required this.genesisAddress, + required this.address, + required this.searchGenesisAddress, }) : super.internal(); - final String genesisAddress; + final String address; + final bool searchGenesisAddress; @override Override overrideWith( @@ -120,7 +128,8 @@ class AccountWithGenesisAddressProvider dependencies: null, allTransitiveDependencies: null, debugGetCreateSourceHash: null, - genesisAddress: genesisAddress, + address: address, + searchGenesisAddress: searchGenesisAddress, ), ); } @@ -133,13 +142,15 @@ class AccountWithGenesisAddressProvider @override bool operator ==(Object other) { return other is AccountWithGenesisAddressProvider && - other.genesisAddress == genesisAddress; + other.address == address && + other.searchGenesisAddress == searchGenesisAddress; } @override int get hashCode { var hash = _SystemHash.combine(0, runtimeType.hashCode); - hash = _SystemHash.combine(hash, genesisAddress.hashCode); + hash = _SystemHash.combine(hash, address.hashCode); + hash = _SystemHash.combine(hash, searchGenesisAddress.hashCode); return _SystemHash.finish(hash); } @@ -148,8 +159,11 @@ class AccountWithGenesisAddressProvider @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element mixin AccountWithGenesisAddressRef on AutoDisposeFutureProviderRef { - /// The parameter `genesisAddress` of this provider. - String get genesisAddress; + /// The parameter `address` of this provider. + String get address; + + /// The parameter `searchGenesisAddress` of this provider. + bool get searchGenesisAddress; } class _AccountWithGenesisAddressProviderElement @@ -158,8 +172,10 @@ class _AccountWithGenesisAddressProviderElement _AccountWithGenesisAddressProviderElement(super.provider); @override - String get genesisAddress => - (origin as AccountWithGenesisAddressProvider).genesisAddress; + String get address => (origin as AccountWithGenesisAddressProvider).address; + @override + bool get searchGenesisAddress => + (origin as AccountWithGenesisAddressProvider).searchGenesisAddress; } String _$accountWithNameHash() => r'4cddbfdc930ca6db516b0f16e45758305c853224'; diff --git a/lib/application/account/accounts_notifier.dart b/lib/application/account/accounts_notifier.dart index b3352ee6a..364fa8dd4 100644 --- a/lib/application/account/accounts_notifier.dart +++ b/lib/application/account/accounts_notifier.dart @@ -53,16 +53,6 @@ class AccountsNotifier extends _$AccountsNotifier { return ref.read(accountNotifierProvider(accountName).notifier); } - - // TODO(Chralu): check if this works - Future clearRecentTransactionsFromCache() async { - final accounts = await future; - for (final account in accounts) { - await ref - .read(accountNotifierProvider(account.name).notifier) - .clearRecentTransactionsFromCache(); - } - } } extension AccountsExt on Iterable { diff --git a/lib/application/account/accounts_notifier.g.dart b/lib/application/account/accounts_notifier.g.dart index e939ce1a9..84c638ba4 100644 --- a/lib/application/account/accounts_notifier.g.dart +++ b/lib/application/account/accounts_notifier.g.dart @@ -6,7 +6,7 @@ part of 'accounts_notifier.dart'; // RiverpodGenerator // ************************************************************************** -String _$accountsNotifierHash() => r'58f869b3689672b3bd919a0071e727e97feeeb67'; +String _$accountsNotifierHash() => r'4fcda60a8198211c1c2bf9e8f6f40a0ed4db5bf9'; /// See also [AccountsNotifier]. @ProviderFor(AccountsNotifier) diff --git a/lib/application/account/selected_account.dart b/lib/application/account/selected_account.dart index 3c857a095..c74abe73e 100644 --- a/lib/application/account/selected_account.dart +++ b/lib/application/account/selected_account.dart @@ -1,28 +1,12 @@ /// SPDX-License-Identifier: AGPL-3.0-or-later -import 'dart:async'; - import 'package:aewallet/application/account/accounts_notifier.dart'; -import 'package:aewallet/model/blockchain/recent_transaction.dart'; import 'package:aewallet/model/data/account_token.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; part 'selected_account.g.dart'; -@riverpod -Future?> selectedAccountRecentTransactions( - Ref ref, -) async { - return ref - .watch( - accountsNotifierProvider.select( - (accounts) => accounts.valueOrNull?.selectedAccount, - ), - ) - ?.recentTransactions; -} - @riverpod List selectedAccountNFTFiltered( Ref ref, diff --git a/lib/application/account/selected_account.g.dart b/lib/application/account/selected_account.g.dart index 4e7f04780..4950bbeb3 100644 --- a/lib/application/account/selected_account.g.dart +++ b/lib/application/account/selected_account.g.dart @@ -6,26 +6,6 @@ part of 'selected_account.dart'; // RiverpodGenerator // ************************************************************************** -String _$selectedAccountRecentTransactionsHash() => - r'f747c960536720308b38ecb91ea5089f943957c9'; - -/// See also [selectedAccountRecentTransactions]. -@ProviderFor(selectedAccountRecentTransactions) -final selectedAccountRecentTransactionsProvider = - AutoDisposeFutureProvider?>.internal( - selectedAccountRecentTransactions, - name: r'selectedAccountRecentTransactionsProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$selectedAccountRecentTransactionsHash, - dependencies: null, - allTransitiveDependencies: null, -); - -@Deprecated('Will be removed in 3.0. Use Ref instead') -// ignore: unused_element -typedef SelectedAccountRecentTransactionsRef - = AutoDisposeFutureProviderRef?>; String _$selectedAccountNFTFilteredHash() => r'5bab7f1eab6363f522fe9ee7b4f59dff7bb09acc'; diff --git a/lib/application/address_service.dart b/lib/application/address_service.dart index 831ba5686..34900d3e2 100644 --- a/lib/application/address_service.dart +++ b/lib/application/address_service.dart @@ -1,3 +1,4 @@ +import 'package:aewallet/application/api_service.dart'; import 'package:aewallet/modules/aeswap/application/session/provider.dart'; import 'package:archethic_lib_dart/archethic_lib_dart.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -10,3 +11,17 @@ AddressService addressService(Ref ref) { final environment = ref.watch(environmentProvider); return AddressService(environment.endpoint); } + +@riverpod +Future genesisAddress(Ref ref, String address) async { + final apiService = ref.watch(apiServiceProvider); + var genesisAddress = address; + try { + genesisAddress = + (await apiService.getGenesisAddress(address)).address ?? address; + } catch (e) + // ignore: empty_catches + {} + + return genesisAddress; +} diff --git a/lib/application/address_service.g.dart b/lib/application/address_service.g.dart index 13668904e..30775c988 100644 --- a/lib/application/address_service.g.dart +++ b/lib/application/address_service.g.dart @@ -23,5 +23,155 @@ final addressServiceProvider = AutoDisposeProvider.internal( @Deprecated('Will be removed in 3.0. Use Ref instead') // ignore: unused_element typedef AddressServiceRef = AutoDisposeProviderRef; +String _$genesisAddressHash() => r'a1192fe19625715ac2ff80318fadd121e9c9365d'; + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// See also [genesisAddress]. +@ProviderFor(genesisAddress) +const genesisAddressProvider = GenesisAddressFamily(); + +/// See also [genesisAddress]. +class GenesisAddressFamily extends Family> { + /// See also [genesisAddress]. + const GenesisAddressFamily(); + + /// See also [genesisAddress]. + GenesisAddressProvider call( + String address, + ) { + return GenesisAddressProvider( + address, + ); + } + + @override + GenesisAddressProvider getProviderOverride( + covariant GenesisAddressProvider provider, + ) { + return call( + provider.address, + ); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'genesisAddressProvider'; +} + +/// See also [genesisAddress]. +class GenesisAddressProvider extends AutoDisposeFutureProvider { + /// See also [genesisAddress]. + GenesisAddressProvider( + String address, + ) : this._internal( + (ref) => genesisAddress( + ref as GenesisAddressRef, + address, + ), + from: genesisAddressProvider, + name: r'genesisAddressProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$genesisAddressHash, + dependencies: GenesisAddressFamily._dependencies, + allTransitiveDependencies: + GenesisAddressFamily._allTransitiveDependencies, + address: address, + ); + + GenesisAddressProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.address, + }) : super.internal(); + + final String address; + + @override + Override overrideWith( + FutureOr Function(GenesisAddressRef provider) create, + ) { + return ProviderOverride( + origin: this, + override: GenesisAddressProvider._internal( + (ref) => create(ref as GenesisAddressRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + address: address, + ), + ); + } + + @override + AutoDisposeFutureProviderElement createElement() { + return _GenesisAddressProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is GenesisAddressProvider && other.address == address; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, address.hashCode); + + return _SystemHash.finish(hash); + } +} + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +mixin GenesisAddressRef on AutoDisposeFutureProviderRef { + /// The parameter `address` of this provider. + String get address; +} + +class _GenesisAddressProviderElement + extends AutoDisposeFutureProviderElement with GenesisAddressRef { + _GenesisAddressProviderElement(super.provider); + + @override + String get address => (origin as GenesisAddressProvider).address; +} // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/application/formated_name.dart b/lib/application/formated_name.dart new file mode 100644 index 000000000..a37d1bcbd --- /dev/null +++ b/lib/application/formated_name.dart @@ -0,0 +1,84 @@ +import 'package:aewallet/application/account/accounts.dart'; +import 'package:aewallet/application/account/accounts_notifier.dart'; +import 'package:aewallet/application/address_service.dart'; +import 'package:aewallet/application/recent_transactions.dart'; +import 'package:aewallet/modules/aeswap/application/session/provider.dart'; +import 'package:aewallet/modules/aeswap/application/session/state.dart'; +import 'package:aewallet/ui/util/address_formatters.dart'; +import 'package:aewallet/util/account_formatters.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'formated_name.g.dart'; + +@riverpod +Future formatedNameFromAddress( + Ref ref, + BuildContext context, + String address, +) async { + final localizations = AppLocalizations.of(context)!; + final selectedAccount = ref.watch( + accountsNotifierProvider.select( + (accounts) => accounts.valueOrNull?.selectedAccount, + ), + ); + + ref.watch(recentTransactionsProvider(selectedAccount!.genesisAddress)); + if (address == + '00000000000000000000000000000000000000000000000000000000000000000000') { + return localizations.burnAddressLbl; + } + + final genesisAddressAsync = ref.watch(genesisAddressProvider(address)); + var addressToSearch = address; + genesisAddressAsync.map( + data: (data) { + addressToSearch = data.value; + }, + error: (_) {}, + loading: (_) {}, + ); + + return ref + .watch( + accountWithGenesisAddressProvider( + addressToSearch, + ), + ) + .map( + data: (data) { + if (data.value != null) { + return data.value!.nameDisplayed; + } + + final environment = ref.watch(environmentProvider); + if (addressToSearch.toUpperCase() == + environment.aeETHUCOPoolAddress.toUpperCase()) { + return 'Pool aeETH/UCO'; + } + if (addressToSearch.toUpperCase() == + environment.aeETHUCOFarmLockAddress.toUpperCase()) { + return 'Farm aeETH/UCO'; + } + if (addressToSearch.toUpperCase() == + environment.aeETHUCOFarmLegacyAddress.toUpperCase()) { + return 'Farm legacy aeETH/UCO'; + } + if (addressToSearch.toUpperCase() == + environment.nodeRewardsChain.toUpperCase()) { + return 'Node rewards'; + } + + return AddressFormatters(addressToSearch).getShortString4(); + }, + error: (_) { + return AddressFormatters(addressToSearch).getShortString4(); + }, + loading: (_) { + return AddressFormatters(addressToSearch).getShortString4(); + }, + ); +} diff --git a/lib/application/formated_name.g.dart b/lib/application/formated_name.g.dart new file mode 100644 index 000000000..69a502bbb --- /dev/null +++ b/lib/application/formated_name.g.dart @@ -0,0 +1,181 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'formated_name.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$formatedNameFromAddressHash() => + r'd31d7d49e32302612dc334e5e9e06d79bc609e63'; + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// See also [formatedNameFromAddress]. +@ProviderFor(formatedNameFromAddress) +const formatedNameFromAddressProvider = FormatedNameFromAddressFamily(); + +/// See also [formatedNameFromAddress]. +class FormatedNameFromAddressFamily extends Family> { + /// See also [formatedNameFromAddress]. + const FormatedNameFromAddressFamily(); + + /// See also [formatedNameFromAddress]. + FormatedNameFromAddressProvider call( + BuildContext context, + String address, + ) { + return FormatedNameFromAddressProvider( + context, + address, + ); + } + + @override + FormatedNameFromAddressProvider getProviderOverride( + covariant FormatedNameFromAddressProvider provider, + ) { + return call( + provider.context, + provider.address, + ); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'formatedNameFromAddressProvider'; +} + +/// See also [formatedNameFromAddress]. +class FormatedNameFromAddressProvider + extends AutoDisposeFutureProvider { + /// See also [formatedNameFromAddress]. + FormatedNameFromAddressProvider( + BuildContext context, + String address, + ) : this._internal( + (ref) => formatedNameFromAddress( + ref as FormatedNameFromAddressRef, + context, + address, + ), + from: formatedNameFromAddressProvider, + name: r'formatedNameFromAddressProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$formatedNameFromAddressHash, + dependencies: FormatedNameFromAddressFamily._dependencies, + allTransitiveDependencies: + FormatedNameFromAddressFamily._allTransitiveDependencies, + context: context, + address: address, + ); + + FormatedNameFromAddressProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.context, + required this.address, + }) : super.internal(); + + final BuildContext context; + final String address; + + @override + Override overrideWith( + FutureOr Function(FormatedNameFromAddressRef provider) create, + ) { + return ProviderOverride( + origin: this, + override: FormatedNameFromAddressProvider._internal( + (ref) => create(ref as FormatedNameFromAddressRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + context: context, + address: address, + ), + ); + } + + @override + AutoDisposeFutureProviderElement createElement() { + return _FormatedNameFromAddressProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is FormatedNameFromAddressProvider && + other.context == context && + other.address == address; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, context.hashCode); + hash = _SystemHash.combine(hash, address.hashCode); + + return _SystemHash.finish(hash); + } +} + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +mixin FormatedNameFromAddressRef on AutoDisposeFutureProviderRef { + /// The parameter `context` of this provider. + BuildContext get context; + + /// The parameter `address` of this provider. + String get address; +} + +class _FormatedNameFromAddressProviderElement + extends AutoDisposeFutureProviderElement + with FormatedNameFromAddressRef { + _FormatedNameFromAddressProviderElement(super.provider); + + @override + BuildContext get context => + (origin as FormatedNameFromAddressProvider).context; + @override + String get address => (origin as FormatedNameFromAddressProvider).address; +} +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/application/recent_transactions.dart b/lib/application/recent_transactions.dart new file mode 100644 index 000000000..2494c0e07 --- /dev/null +++ b/lib/application/recent_transactions.dart @@ -0,0 +1,44 @@ +import 'package:aewallet/application/account/accounts_notifier.dart'; +import 'package:aewallet/application/api_service.dart'; +import 'package:aewallet/application/session/session.dart'; +import 'package:aewallet/application/tokens/tokens.dart'; +import 'package:aewallet/domain/repositories/transaction/recent_transactions.repository.dart'; +import 'package:aewallet/infrastructure/repositories/transaction/recent_transactions.repository.dart'; +import 'package:aewallet/model/blockchain/recent_transaction.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'recent_transactions.g.dart'; + +@riverpod +RecentTransactionsRepository recentTransactionsRepository( + Ref ref, +) { + final apiService = ref.watch(apiServiceProvider); + final tokensRepository = ref.watch(tokensRepositoryProvider); + final session = ref.read(sessionNotifierProvider).loggedIn!; + final accountSelected = ref.watch( + accountsNotifierProvider.select( + (accounts) => accounts.valueOrNull?.selectedAccount, + ), + ); + + return RecentTransactionsRepositoryImpl( + apiService: apiService, + tokensRepository: tokensRepository, + keyPair: session + .wallet.keychainSecuredInfos.services[accountSelected!.name]!.keyPair!, + ); +} + +@riverpod +Future> recentTransactions( + Ref ref, + String genesisAddress, +) async { + final recentTransactions = await ref + .watch(recentTransactionsRepositoryProvider) + .getAccountRecentTransactions(genesisAddress); + + return recentTransactions; +} diff --git a/lib/application/recent_transactions.g.dart b/lib/application/recent_transactions.g.dart new file mode 100644 index 000000000..2451d9eb3 --- /dev/null +++ b/lib/application/recent_transactions.g.dart @@ -0,0 +1,188 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'recent_transactions.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$recentTransactionsRepositoryHash() => + r'ff6fe6c3e570f2c5c5398c749f9cb25d474921dd'; + +/// See also [recentTransactionsRepository]. +@ProviderFor(recentTransactionsRepository) +final recentTransactionsRepositoryProvider = + AutoDisposeProvider.internal( + recentTransactionsRepository, + name: r'recentTransactionsRepositoryProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$recentTransactionsRepositoryHash, + dependencies: null, + allTransitiveDependencies: null, +); + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +typedef RecentTransactionsRepositoryRef + = AutoDisposeProviderRef; +String _$recentTransactionsHash() => + r'9ddd058382be0d42abf3a49ecce5ab13cb1ae2c2'; + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// See also [recentTransactions]. +@ProviderFor(recentTransactions) +const recentTransactionsProvider = RecentTransactionsFamily(); + +/// See also [recentTransactions]. +class RecentTransactionsFamily + extends Family>> { + /// See also [recentTransactions]. + const RecentTransactionsFamily(); + + /// See also [recentTransactions]. + RecentTransactionsProvider call( + String genesisAddress, + ) { + return RecentTransactionsProvider( + genesisAddress, + ); + } + + @override + RecentTransactionsProvider getProviderOverride( + covariant RecentTransactionsProvider provider, + ) { + return call( + provider.genesisAddress, + ); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'recentTransactionsProvider'; +} + +/// See also [recentTransactions]. +class RecentTransactionsProvider + extends AutoDisposeFutureProvider> { + /// See also [recentTransactions]. + RecentTransactionsProvider( + String genesisAddress, + ) : this._internal( + (ref) => recentTransactions( + ref as RecentTransactionsRef, + genesisAddress, + ), + from: recentTransactionsProvider, + name: r'recentTransactionsProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$recentTransactionsHash, + dependencies: RecentTransactionsFamily._dependencies, + allTransitiveDependencies: + RecentTransactionsFamily._allTransitiveDependencies, + genesisAddress: genesisAddress, + ); + + RecentTransactionsProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.genesisAddress, + }) : super.internal(); + + final String genesisAddress; + + @override + Override overrideWith( + FutureOr> Function(RecentTransactionsRef provider) + create, + ) { + return ProviderOverride( + origin: this, + override: RecentTransactionsProvider._internal( + (ref) => create(ref as RecentTransactionsRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + genesisAddress: genesisAddress, + ), + ); + } + + @override + AutoDisposeFutureProviderElement> createElement() { + return _RecentTransactionsProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is RecentTransactionsProvider && + other.genesisAddress == genesisAddress; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, genesisAddress.hashCode); + + return _SystemHash.finish(hash); + } +} + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +mixin RecentTransactionsRef + on AutoDisposeFutureProviderRef> { + /// The parameter `genesisAddress` of this provider. + String get genesisAddress; +} + +class _RecentTransactionsProviderElement + extends AutoDisposeFutureProviderElement> + with RecentTransactionsRef { + _RecentTransactionsProviderElement(super.provider); + + @override + String get genesisAddress => + (origin as RecentTransactionsProvider).genesisAddress; +} +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/lib/domain/repositories/market/market.dart b/lib/domain/repositories/market/market.dart deleted file mode 100644 index c9cfc616b..000000000 --- a/lib/domain/repositories/market/market.dart +++ /dev/null @@ -1,29 +0,0 @@ -/// SPDX-License-Identifier: AGPL-3.0-or-later - -import 'package:aewallet/domain/models/core/failures.dart'; -import 'package:aewallet/domain/models/core/result.dart'; -import 'package:aewallet/domain/models/market_price.dart'; -import 'package:aewallet/model/available_currency.dart'; -import 'package:archethic_lib_dart/archethic_lib_dart.dart' as archethic; - -abstract class MarketRepositoryInterface { - MarketRepositoryInterface(); - - bool canHandleCurrency(AvailableCurrencyEnum currency); - - Future> getUCOMarketPrice( - AvailableCurrencyEnum currency, - archethic.OracleService oracleService, - ); -} - -abstract class MarketLocalRepositoryInterface { - Future> getPrice({ - required AvailableCurrencyEnum currency, - }); - - Future> setPrice({ - required AvailableCurrencyEnum currency, - required MarketPrice price, - }); -} diff --git a/lib/domain/repositories/tokens/verified_tokens_repository.dart b/lib/domain/repositories/tokens/verified_tokens_repository.dart deleted file mode 100644 index ac554ae30..000000000 --- a/lib/domain/repositories/tokens/verified_tokens_repository.dart +++ /dev/null @@ -1,7 +0,0 @@ -/// SPDX-License-Identifier: AGPL-3.0-or-later - -import 'package:aewallet/domain/models/verified_tokens.dart'; - -abstract class VerifiedTokensRepositoryInterface { - Future getVerifiedTokens(); -} diff --git a/lib/infrastructure/repositories/tokens/tokens.repository.dart b/lib/infrastructure/repositories/tokens/tokens.repository.dart index 4d39638a6..d600db3c9 100644 --- a/lib/infrastructure/repositories/tokens/tokens.repository.dart +++ b/lib/infrastructure/repositories/tokens/tokens.repository.dart @@ -57,10 +57,11 @@ class TokensRepositoryImpl with TokenParser implements TokensRepository { final getTokens = await Future.wait(futures); for (final Map getToken in getTokens) { getToken.forEach((key, value) async { + value = value.copyWith(address: key); if (value.type == tokenFungibleType) { - value = value.copyWith(address: key); await tokensListDatasource.setToken(value.toHive()); } + tokenMap[key] = value; }); } diff --git a/lib/infrastructure/repositories/transaction/recent_transactions.repository.dart b/lib/infrastructure/repositories/transaction/recent_transactions.repository.dart index cb1e70399..71cdb07c3 100644 --- a/lib/infrastructure/repositories/transaction/recent_transactions.repository.dart +++ b/lib/infrastructure/repositories/transaction/recent_transactions.repository.dart @@ -1,198 +1,492 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:aewallet/domain/models/token_parser.dart'; +import 'package:aewallet/domain/repositories/tokens/tokens.repository.dart'; import 'package:aewallet/domain/repositories/transaction/recent_transactions.repository.dart'; -import 'package:aewallet/infrastructure/repositories/tokens/tokens.repository.dart'; import 'package:aewallet/model/blockchain/recent_transaction.dart'; -import 'package:archethic_dapp_framework_flutter/archethic_dapp_framework_flutter.dart' - as aedappfm; +import 'package:aewallet/model/blockchain/token_information.dart'; +import 'package:aewallet/model/keychain_service_keypair.dart'; +import 'package:aewallet/util/task.dart'; import 'package:archethic_lib_dart/archethic_lib_dart.dart' as archethic; +/// A repository implementation for fetching and processing recent transactions +/// for a given account in a wallet. This class integrates with the Archethic +/// blockchain API and processes transactions to provide enriched data, +/// including token information and categorized transaction types. +/// +/// This repository handles: +/// - Fetching unspent outputs and transaction chains for a given account. +/// - Filtering and processing transaction inputs and outputs to exclude +/// self-created UTXOs and other non-relevant data. +/// - Enriching transaction data with token details such as name, symbol, +/// and supply using the TokensRepository. +/// +/// ### Specifications +/// - Consumed inputs include outputs from the user's own chain (e.g., recalculated UTXOs) +/// that are excluded from the displayed list. +/// - Unspent outputs created by the user are filtered from the transaction chain. +/// - Inputs are derived by combining chain unspent outputs and consumed inputs, +/// minus the user's unspent outputs, to exclude inputs originating from their chain. +/// - Older inputs are filtered to only display those newer than the current transaction. +/// - Transactions are enriched with movements and token metadata. +/// +/// ### Dependencies +/// - [ApiService]: For interacting with the Archethic blockchain API. +/// - [TokensRepository]: For fetching token metadata and details. + +// TODO(reddwarf03): Skip transactions with protocol version < 7 class RecentTransactionsRepositoryImpl with TokenParser implements RecentTransactionsRepository { RecentTransactionsRepositoryImpl({ required this.apiService, - required this.defTokensRepository, + required this.tokensRepository, + required this.keyPair, }); final archethic.ApiService apiService; - final TokensRepositoryImpl defTokensRepository; + final TokensRepository tokensRepository; + final KeychainServiceKeyPair keyPair; @override Future> getAccountRecentTransactions( String genesisAddress, ) async { - var recentTransactions = []; + final results = await _fetchTransactionData(genesisAddress); + final accountInputs = await _processTransactionInputs(results); + final recentTransactions = await _processTransactions( + results, + accountInputs, + genesisAddress, + ); + final enrichedRecentTransactions = + await _enrichWithTokenInformation(recentTransactions); + return _enrichMessageFromSecret(enrichedRecentTransactions); + } + Future< + ( + Map>, + Map> + )> _fetchTransactionData( + String genesisAddress, + ) async { final results = await Future.wait([ apiService.chainUnspentOutputs([genesisAddress]), - apiService.getTransactionChain({genesisAddress: ''}), + apiService.getTransactionChain( + {genesisAddress: ''}, + orderAsc: false, + request: ''' + address, + data { content, + ownerships { authorizedPublicKeys { encryptedSecretKey, publicKey } secret } + ledger { uco { transfers { amount, to } }, + token { transfers { amount, to, tokenAddress, tokenId } } } + recipients, + actionRecipients { action address args } + } + type, + validationStamp { timestamp, + ledgerOperations { fee, + unspentOutputs { amount from timestamp tokenAddress tokenId type } + consumedInputs { amount from timestamp tokenAddress tokenId type protocolVersion } + transactionMovements { amount to tokenAddress tokenId type } + } + protocolVersion, + }, + version + ''', + ), ]); - final unspentOutputsResultMap = - results[0] as Map>; - final transactionChainResultMap = - results[1] as Map>; + return ( + results[0] as Map>, + results[1] as Map>, + ); + } - // Unspent Outputs list - final accountUnspentOutputs = transactionChainResultMap[genesisAddress]! - .first - .validationStamp! - .ledgerOperations! - .unspentOutputs; + Future> _processTransactionInputs( + ( + Map>, + Map> + ) results, + ) async { + final (unspentOutputsResultMap, _) = results; + final accountInputs = []; - // Inputs list + // Process unspent outputs final accountInputsFromChainUnspentOutputs = - unspentOutputsResultMap[genesisAddress] ?? []; + unspentOutputsResultMap.values.expand((e) => e).toList(); + + accountInputs.addAll( + accountInputsFromChainUnspentOutputs.map( + (output) => archethic.TransactionInput( + amount: output.amount, + from: output.from, + timestamp: output.timestamp, + tokenAddress: output.tokenAddress, + tokenId: output.tokenId, + type: output.type, + ), + ), + ); - final accountInputsFromConsumedInputsTxChain = - transactionChainResultMap[genesisAddress]! - .first - .validationStamp! - .ledgerOperations! - .consumedInputs; + return accountInputs; + } - final accountInputs = []; - for (final accountInputsFromChainUnspentOutput - in accountInputsFromChainUnspentOutputs) { - accountInputs.add( - archethic.TransactionInput( - amount: accountInputsFromChainUnspentOutput.amount, - from: accountInputsFromChainUnspentOutput.from, - timestamp: accountInputsFromChainUnspentOutput.timestamp, - tokenAddress: accountInputsFromChainUnspentOutput.tokenAddress, - tokenId: accountInputsFromChainUnspentOutput.tokenId, - type: accountInputsFromChainUnspentOutput.type, + Future> _processTransactions( + ( + Map>, + Map> + ) results, + List accountInputs, + String genesisAddress, + ) async { + final (_, transactionChainResultMap) = results; + final recentTransactions = []; + + final transactionChain = + transactionChainResultMap[genesisAddress] ?? []; + + // Process each transaction + for (final transaction in transactionChain) { + // Process consumed inputs + final consumedInputs = + transaction.validationStamp?.ledgerOperations?.consumedInputs ?? []; + accountInputs.addAll( + consumedInputs.map( + (input) => archethic.TransactionInput( + amount: input.amount, + from: input.from, + timestamp: input.timestamp, + tokenAddress: input.tokenAddress, + tokenId: input.tokenId, + type: input.type, + ), ), ); + + final recentTransaction = await _createRecentTransaction(transaction); + if (recentTransaction != null) { + recentTransactions.add(recentTransaction); + } } - for (final accountInputFromConsumedInputsTxChain - in accountInputsFromConsumedInputsTxChain) { - accountInputs.add( - archethic.TransactionInput( - amount: accountInputFromConsumedInputsTxChain.amount, - from: accountInputFromConsumedInputsTxChain.from, - timestamp: accountInputFromConsumedInputsTxChain.timestamp, - tokenAddress: accountInputFromConsumedInputsTxChain.tokenAddress, - tokenId: accountInputFromConsumedInputsTxChain.tokenId, - type: accountInputFromConsumedInputsTxChain.type, + // Process remaining inputs + await _processRemainingInputs( + transactionChain, + accountInputs, + recentTransactions, + ); + + // Sort and limit transactions + recentTransactions.sort((a, b) => b.timestamp!.compareTo(a.timestamp!)); + return recentTransactions.take(10).toList(); + } + + Future _createRecentTransaction( + archethic.Transaction transaction, + ) async { + final movements = + transaction.validationStamp?.ledgerOperations?.transactionMovements ?? + []; + + final ledgerOperationMvtInfo = movements + .map( + (movement) => ( + amount: archethic.fromBigInt(movement.amount).toDouble(), + to: movement.to, + type: movement.type, + tokenInformation: TokenInformation( + address: movement.type == 'UCO' ? 'UCO' : movement.tokenAddress, + type: movement.type, + symbol: movement.type == 'UCO' ? 'UCO' : movement.type, + ), + ), + ) + .toList(); + + if (transaction.type == 'token') { + final tokenInfo = + archethic.Token.fromJson(jsonDecode(transaction.data!.content!)); + ledgerOperationMvtInfo.add( + ( + amount: 0, + to: '', + type: transaction.type, + tokenInformation: TokenInformation( + address: transaction.address!.address, + supply: archethic.fromBigInt(tokenInfo.supply).toDouble(), + symbol: tokenInfo.symbol, + type: tokenInfo.type, + name: tokenInfo.name, + ), ), ); } - for (final accountInput in accountInputs) { - final recentTransaction = RecentTransaction() - ..amount = archethic.fromBigInt(accountInput.amount).toDouble() - ..typeTx = RecentTransaction.transferInput - ..tokenAddress = accountInput.tokenAddress - ..from = accountInput.from - ..address = accountInput.from - ..timestamp = accountInput.timestamp; - recentTransactions.add(recentTransaction); + final action = _extractAction(transaction); + + final recentTransaction = RecentTransaction( + address: transaction.address!.address, + timestamp: transaction.validationStamp!.timestamp, + ledgerOperationMvtInfo: ledgerOperationMvtInfo, + fee: archethic + .fromBigInt(transaction.validationStamp!.ledgerOperations!.fee) + .toDouble(), + ); + + return _applyTransactionType(recentTransaction, transaction.type, action); + } + + String _extractAction(archethic.Transaction transaction) { + final actions = transaction.data?.recipients + .where((r) => r.action != null) + .map((r) => r.action![0].toUpperCase() + r.action!.substring(1)) + .toList() ?? + []; + return actions.join(',').replaceAll('_', ' '); + } + + RecentTransaction? _applyTransactionType( + RecentTransaction transaction, + String? type, + String action, + ) { + switch (type) { + case 'token': + return transaction.copyWith( + typeTx: RecentTransaction.tokenCreation, + action: action.isEmpty ? 'Token' : action, + ); + case 'hosting': + return transaction.copyWith( + typeTx: RecentTransaction.hosting, + action: action.isEmpty ? 'Hosting' : action, + ); + case 'transfer': + return transaction.copyWith( + typeTx: RecentTransaction.transferOutput, + action: action.isEmpty ? 'Transfer' : action, + ); + default: + return null; } + } - // Movements - final transaction = transactionChainResultMap[genesisAddress]!.first; - final accountTransactionMovements = - transaction.validationStamp!.ledgerOperations!.transactionMovements; - - for (final accountTransactionMovement in accountTransactionMovements) { - final recentTransaction = RecentTransaction() - ..amount = - archethic.fromBigInt(accountTransactionMovement.amount).toDouble() - ..address = transaction.address!.address - ..timestamp = transaction.validationStamp!.timestamp - ..fee = archethic - .fromBigInt( - transaction.validationStamp!.ledgerOperations!.fee, - ) - .toDouble(); - switch (transaction.type) { - case 'token': - recentTransaction.typeTx = RecentTransaction.tokenCreation; - break; - case 'hosting': - recentTransaction.typeTx = RecentTransaction.hosting; - break; - case 'transfer': - recentTransaction.typeTx = RecentTransaction.transferOutput; - break; - default: + Future _processRemainingInputs( + List transactionChain, + List accountInputs, + List recentTransactions, + ) async { + // Remove spent outputs + for (final transaction in transactionChain) { + final unspentOutputs = + transaction.validationStamp?.ledgerOperations?.unspentOutputs ?? []; + for (final unspentOutput in unspentOutputs) { + accountInputs.removeWhere( + (input) => + input.timestamp == unspentOutput.timestamp && + input.amount == unspentOutput.amount && + input.from == unspentOutput.from && + input.tokenAddress == unspentOutput.tokenAddress && + input.tokenId == unspentOutput.tokenId && + input.type == unspentOutput.type, + ); } + } - recentTransactions.add(recentTransaction); + bool _isValidInput(archethic.TransactionInput input) { + return input.amount != null && + input.amount! > 0 && + input.from != null && + input.timestamp != null; } - recentTransactions.sort((a, b) { - final compareTimestamp = b.timestamp!.compareTo(a.timestamp!); - if (compareTimestamp != 0) { - return compareTimestamp; - } else { - return b.indexInLedger.compareTo(a.indexInLedger); + // Add remaining inputs as transactions + for (final input in accountInputs) { + if (_isValidInput(input)) { + recentTransactions.add( + RecentTransaction( + typeTx: RecentTransaction.transferInput, + from: input.from, + address: input.from, + timestamp: input.timestamp, + ledgerOperationMvtInfo: [ + ( + amount: archethic.fromBigInt(input.amount).toDouble(), + to: '', + type: input.type, + tokenInformation: TokenInformation( + address: input.type == 'UCO' ? 'UCO' : input.tokenAddress, + type: input.type, + symbol: input.type == 'UCO' ? 'UCO' : null, + ), + ), + ], + ), + ); } - }); + } + } - recentTransactions = recentTransactions.sublist( - 0, - recentTransactions.length > 10 ? 10 : recentTransactions.length, - ); + Future> _enrichMessageFromSecret( + List recentTransactions, + ) async { + final ownershipsAddresses = []; - // Get token id - final tokensAddresses = []; for (final recentTransaction in recentTransactions) { - if (recentTransaction.tokenAddress != null && - recentTransaction.tokenAddress!.isNotEmpty && - recentTransaction.tokenInformation == null) { - tokensAddresses.add(recentTransaction.tokenAddress!); + // Decrypt secrets + switch (recentTransaction.typeTx) { + case RecentTransaction.transferInput: + if (recentTransaction.from != null) { + ownershipsAddresses.add(recentTransaction.from!); + } + case RecentTransaction.transferOutput: + if (recentTransaction.address != null) { + ownershipsAddresses.add(recentTransaction.address!); + } + break; } } - // Search token Information - final tokensAddressMap = await defTokensRepository.getTokensFromAddresses( - tokensAddresses.toSet().toList(), - ); + // Get List of ownerships + final getTransactionOwnerships = await ownershipsAddresses + .toSet() + .map( + (ownershipsAddress) => Task( + name: + 'GetAccountRecentTransactions - ownershipsAddress: $ownershipsAddress', + action: () => apiService.getTransactionOwnerships( + [ownershipsAddress], + ), + ), + ) + .autoRetry() + .batch(); - for (final recentTransaction in recentTransactions) { - // Get token Information - if (recentTransaction.tokenAddress != null && - recentTransaction.tokenAddress!.isNotEmpty && - recentTransaction.tokenInformation == null && - recentTransaction.timestamp! >= mostRecentTimestamp) { - final token = tokensAddressMap[recentTransaction.tokenAddress]; - if (token != null) { - recentTransaction - ..tokenAddress = token.address - ..tokenInformation = TokenInformation( - address: token.address, - name: token.name, - supply: archethic.fromBigInt(token.supply).toDouble(), - symbol: token.symbol, - type: token.type, - ); - } - } + final ownershipsMap = >{}; + for (final getTransactionOwnership in getTransactionOwnerships) { + ownershipsMap.addAll(getTransactionOwnership); + } - // Decrypt secrets + final newRecentTransactions = []; + for (var recentTransaction in recentTransactions) { switch (recentTransaction.typeTx) { case RecentTransaction.transferInput: if (recentTransaction.from != null) { - if (recentTransaction.timestamp! > mostRecentTimestamp) { - ownershipsAddresses.add(recentTransaction.from!); - } - recentTransactionLastAddresses.add(recentTransaction.from!); + recentTransaction = _decryptedSecret( + keypair: keyPair, + ownerships: ownershipsMap[recentTransaction.from!] ?? [], + recentTransaction: recentTransaction, + ); } break; case RecentTransaction.transferOutput: - if (recentTransaction.from != null) { - if (recentTransaction.timestamp! > mostRecentTimestamp) { - ownershipsAddresses.add(recentTransaction.from!); - } - recentTransactionLastAddresses.add(recentTransaction.from!); + if (recentTransaction.address != null) { + recentTransaction = _decryptedSecret( + keypair: keyPair, + ownerships: ownershipsMap[recentTransaction.address!] ?? [], + recentTransaction: recentTransaction, + ); } break; } + newRecentTransactions.add(recentTransaction); } - return recentTransactions; + return newRecentTransactions; + } + + RecentTransaction _decryptedSecret({ + required KeychainServiceKeyPair keypair, + required List ownerships, + required RecentTransaction recentTransaction, + }) { + if (ownerships.isEmpty) { + return recentTransaction; + } + var updatedTransaction = + recentTransaction.copyWith(decryptedSecret: []); + + for (final ownership in ownerships) { + final authorizedPublicKey = ownership.authorizedPublicKeys.firstWhere( + (archethic.AuthorizedKey authKey) => + authKey.publicKey!.toUpperCase() == + archethic + .uint8ListToHex(Uint8List.fromList(keypair.publicKey)) + .toUpperCase(), + orElse: archethic.AuthorizedKey.new, + ); + if (authorizedPublicKey.encryptedSecretKey != null) { + final aesKey = archethic.ecDecrypt( + authorizedPublicKey.encryptedSecretKey, + Uint8List.fromList(keypair.privateKey), + ); + final decryptedSecret = archethic.aesDecrypt(ownership.secret, aesKey); + final decodedSecret = utf8.decode(decryptedSecret); + updatedTransaction = updatedTransaction.copyWith( + decryptedSecret: [ + ...?updatedTransaction.decryptedSecret, + decodedSecret, + ], + ); + } + } + return updatedTransaction; + } + + Future> _enrichWithTokenInformation( + List recentTransactions, + ) async { + final tokenAddressSet = {}; + for (final transaction in recentTransactions) { + for (final mvt in transaction.ledgerOperationMvtInfo ?? []) { + if (mvt.tokenInformation?.address != null && + mvt.tokenInformation!.address!.isNotEmpty && + mvt.tokenInformation?.address != 'UCO' && + (mvt.tokenInformation?.name == null || + mvt.tokenInformation?.name.isEmpty)) { + tokenAddressSet.add(mvt.tokenInformation!.address!); + } + } + } + final tokensAddresses = tokenAddressSet.toList(); + + if (tokensAddresses.isEmpty) return recentTransactions; + + final tokensAddressMap = + await tokensRepository.getTokensFromAddresses(tokensAddresses); + + if (tokensAddressMap.isEmpty) { + return recentTransactions; + } + + return recentTransactions.map((transaction) { + final updatedMvtInfo = transaction.ledgerOperationMvtInfo?.map((mvt) { + final tokenAddress = mvt.tokenInformation?.address; + if (tokenAddress != null && + tokenAddress.isNotEmpty && + tokensAddressMap[tokenAddress] != null) { + final token = tokensAddressMap[tokenAddress]!; + return ( + amount: mvt.amount, + to: mvt.to, + type: mvt.type, + tokenInformation: TokenInformation( + address: token.address, + name: token.name, + supply: archethic.fromBigInt(token.supply).toDouble(), + symbol: token.symbol, + type: token.type, + ), + ); + } + return mvt; + }).toList(); + + return transaction.copyWith(ledgerOperationMvtInfo: updatedMvtInfo); + }).toList(); } } diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index ec9bebdd9..4078d7e39 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -25,15 +25,7 @@ "qrInvalidPermissions": "Please Grant Camera Permissions to scan QR Codes", "qrUnknownError": "Could not Read QR Code", "networksHeader": "Networks", - "enterEndpoint": "Enter an endpoint", - "removeContact": "Remove Contact", - "removeContactConfirmation": "Are you sure you want to delete %1?", - "contactAddressInfoKeychainService": "The following QR Code contains the address of your account.\n\nYou can use this address to receive funds or tokens on this account.\n\nTo use it, you can :\n- either scan the QR Code above,\n- or click on it to copy the address.", - "contactAddressInfoExternalContact": "The following QR Code contains the address of your contact.\n\nYou can use this address to send funds or tokens to your contact.\n\nTo use it, you can :\n- either scan the QR Code above,\n- or click on it to copy the address.", - "addContact": "Add Contact", "contactInvalid": "Invalid Contact Name", - "contactRemoved": "%1 has been removed from address book!", - "contactNameMissing": "Choose a Name for this contact", "transfering": "Transfering", "sendError": "An error occurred. Try again later.", "enterAmount": "Enter Amount", @@ -133,7 +125,6 @@ "tokenNameMissing": "Choose a Name for the Token", "tokenNameUCO": "Unfortunately, we have already taken this name of token.", "tokenSymbolUCO": "Unfortunately, we have already taken this name of symbol.", - "notOfficialUCOWarning": "This is not the official UCO token.", "tokenInitialSupply": "Initial supply :", "tokenInitialSupplyPositive": "The initial supply should be > 0", "txListDate": "Date :", @@ -145,11 +136,6 @@ "type": "text", "placeholders": {} }, - "txListFeesIncluded": "Included in the previous transaction", - "@txListFeesIncluded": { - "type": "text", - "placeholders": {} - }, "recoveryPhrase": "Recovery Phrase", "recoveryPhraseIntroExplanation": "Here is the list of 24 words to remember. The following screen will prompt you to find them in order to help you verify that you have written them down correctly.\n\nThis list will also be available in your wallet in the \"Security\" menu.", "dipslayPhraseExplanation": "Here is the list of 24 words to remember. This list allows you to find your funds at any time if you lose your application or device.\nDo not give it to anyone! Even in the context of a support. \nArchethic will never ask you for this information.", @@ -170,27 +156,6 @@ "enterYubikeyClientAPIKey": "Enter the client API Key", "enterYubikeyClientIDEmpty": "The client ID is mandatory", "enterYubikeyAPIKeyEmpty": "The API Key is mandatory", - "transactionInfosHeader": "Transaction information", - "transactionInfosKeyAddress": "Address", - "transactionInfosKeyType": "Type", - "transactionInfosKeyVersion": "Version", - "transactionInfosKeyPreviousPublicKey": "Previous Public Key", - "transactionInfosKeyPreviousSignature": "Previous Signature", - "transactionInfosKeyOriginSignature": "Origin Signature", - "transactionInfosKeyData": "Data", - "transactionInfosKeyContent": "Content", - "transactionInfosKeyCode": "Code", - "transactionInfosKeyUCOLedger": "UCO Ledger", - "transactionInfosKeyTo": "To", - "transactionInfosKeyAmount": "Amount", - "transactionInfosKeyTokenLedger": "Token Ledger", - "transactionInfosKeyToken": "Token", - "transactionInfosKeyValidationStamp": "Validation Stamp", - "transactionInfosKeyProofOfWork": "Proof of work", - "transactionInfosKeyProofOfIntegrity": "Proof of integrity", - "transactionInfosKeyTimeStamp": "Timestamp", - "transactionInfosKeyCrossValidationStamps": "Cross Validation Stamp", - "transactionInfosKeySignature": "Signature", "transactionBuyHeader": "Where to Buy UCO ?", "receive": "Receive", "buy": "Buy UCO", @@ -239,16 +204,10 @@ "welcomeDisclaimerChoice": "I have read and agree to the privacy policy", "showBalances": "Show balances", "testnetEnabled": "Testnet enabled", - "enterEndpointBlank": "The endpoint cannot be empty", - "enterEndpointNotValid": "The endpoint is not valid", - "enterEndpointUseByNetwork": "The endpoint is already used by a network", - "enterEndpointHeader": "Please, specify your endpoint", "introNewWalletGetFirstInfosWelcome": "Welcome to Archethic Wallet.", "introNewWalletGetFirstInfosNameRequest": "Let's start by naming your first account, which will be stored on your decentralized keychain", "introNewWalletGetFirstInfosNameInfos": "It will allow you to distinguish this account from other accounts that you can, if you want, create later.\n\nWARNING : This name will be added to your decentralized keychain and cannot be modified.", "introNewWalletGetFirstInfosNameBlank": "Please, enter the name of the new account", - "contactExistsName": "You already have a contact with this name", - "contactExistsAddress": "You already have a contact with this address", "accountsHeader": "Accounts", "transactionHeader": "Transactions", "newAccount": "New account", @@ -296,10 +255,6 @@ "tokens": "tokens", "tokensHeader": "Tokens", "nft": "NFT", - "keychainCreationTransactionConfirmed1": "Your keychain has been created with %1 confirmation out of %2", - "keychainCreationTransactionConfirmed": "Your keychain has been created with %1 confirmations out of %2", - "keychainAccessCreationTransactionConfirmed1": "Accesses to your keychain have been created with %1 confirmation out of %2", - "keychainAccessCreationTransactionConfirmed": "Accesses to your keychain have been created with %1 confirmations out of %2", "nftCreated": "NFT created :", "notEnoughConfirmations": "The application could not be completed due to lack of consensus", "serviceNotFound": "The service doesn't exist in the keychain.", @@ -340,12 +295,9 @@ "passRecoveryPhraseBackupSecureNow": "Secure my wallet now", "recoveryPhraseBackupRequired": "Save your secret recovery phrase to protect your wallet and associated funds", "recoveryPhraseSave": "Save secret phrase", - "newContact": "New contact", "save": "Save", "next": "Next", "name": "Name", - "deleteContact": "Delete the contact", - "favorites": "Favorites", "explorer": "Explorer", "nftPropertiesOpenseaStructure": "Properties format modeled after Opensea's structure", "bottomMainMenuTransactions": "Transactions", @@ -364,7 +316,6 @@ "removeKeychainAtLeast1": "You must keep at least one account in your keychain.", "ucoBalance": "UCO balance", "tokenBalance": "Token balance", - "executionSC": "Smart-contract execution", "seedHex": "View in Hexadecimal", "burnAddressLbl": "Burn address", "webChannelIncompatibilityWarning": "Unsupported operating system", @@ -640,7 +591,6 @@ "earnHeaderAddLiquidityTxt": "To get LP token you have to provide liquidity here :", "liquidityAddDesc": "Add liquidity to the pool and receive LP Tokens representing your share", "keychainAddressCopied": "Keychain address copied", - "seedCopied": "Seed phrase copied", "farmLockTokensSummaryMoreInfo": "More info", "swapRefreshDone": "Your swap data has been refreshed", "swapInfoPair": "Swap Details", @@ -704,7 +654,6 @@ "dappBoardMyDAppFavoriteAdd": "The DApp has been added to your favorites. You'll find it in the \"DApps Board\" section of the general menu.", "dappBoardMyDAppFavoriteRemove": "The DApp has been removed from your favorites.", "dappBoardDisclaimer": "Diclaimer: By accessing third-party decentralized applications (DApps) via aeWallet, you agree to the terms of our Terms of Use. (See About menu)", - "dappBoardDisclaimerTitle": "Diclaimer", "networkChangeHeader": "Network change", "networkChangeKeychainNotExists": "Your wallet doesn't yet exist in the {envName} environment. You can create it automatically.\nPlease select the accounts you wish to duplicate in this environment. You can add or remove accounts from your {envName} wallet at any time.\n\nImportant:\n- There is no automatic synchronization between environments.\n- Funds, tokens, NFTs, and UCO cannot be transferred or duplicated between environments. Accounts created in Testnet will be empty.", "@networkChangeKeychainNotExists": { diff --git a/lib/l10n/intl_fr.arb b/lib/l10n/intl_fr.arb index 2fef5232c..3784e6f26 100644 --- a/lib/l10n/intl_fr.arb +++ b/lib/l10n/intl_fr.arb @@ -25,15 +25,7 @@ "qrInvalidPermissions": "Veuillez accorder des autorisations à l'appareil photo pour scanner les QR codes", "qrUnknownError": "Impossible de lire le QR code", "networksHeader": "Réseaux", - "enterEndpoint": "Entrer un point d'accès", - "removeContact": "Supprimer contact", - "removeContactConfirmation": "Êtes-vous sûr(e) de vouloir supprimer %1 ?", - "contactAddressInfoKeychainService": "Le QR Code suivant contient l'adresse de votre compte.\n\nVous pouvez utiliser cette adresse pour recevoir des fonds ou des tokens sur ce compte.\n\nPour l'utiliser, vous pouvez :\n- soit scanner le QR Code ci-dessus,\n- soit cliquer dessus pour copier l'adresse.", - "contactAddressInfoExternalContact": "Le QR Code suivant contient l'adresse de votre contact.\n\nVous pouvez utiliser cette adresse pour envoyer des fonds ou des tokens à votre contact.\n\nPour l'utiliser, vous pouvez :\n- soit scanner le QR Code ci-dessus,\n- soit cliquer dessus pour copier l'adresse.", - "addContact": "Ajouter un contact", "contactInvalid": "Nom de contact invalide", - "contactRemoved": "%1 a été supprimé du carnet d'adresses !", - "contactNameMissing": "Choisissez un nom pour ce contact", "transfering": "Transfert", "sendError": "Une erreur s'est produite. Essayez à nouveau plus tard.", "enterAmount": "Entrer le montant", @@ -134,7 +126,6 @@ "tokenNameMissing": "Choisissez un nom pour le jeton", "tokenNameUCO": "Malheureusement, nous avons déjà pris ce nom de jeton.", "tokenSymbolUCO": "Malheureusement, nous avons déjà pris ce nom de symbole.", - "notOfficialUCOWarning": "Ceci n'est pas le jeton officiel UCO.", "tokenInitialSupply": "Offre initiale :", "tokenInitialSupplyPositive": "L'offre initiale doit être > 0", "txListDate": "Date :", @@ -142,7 +133,6 @@ "txListAmount": "Montant :", "txListTo": "À :", "txListFees": "Frais :", - "txListFeesIncluded": "Inclus dans la transaction précédente", "recoveryPhrase": "Phrase secrète", "recoveryPhraseIntroExplanation": "Voici la liste des 24 mots à retenir. L'écran suivant va vous inviter à les retrouver dans l'ordre pour vous aider à vérifier que vous les avez bien noté.\n\nCette liste sera aussi disponible dans votre wallet dans le menu \"Sécurité\".", "dipslayPhraseExplanation": "Voici la liste des 24 mots que vous devez retenir. Cette liste vous permet à tout moment de retrouver vos fonds en cas de perte de votre application ou votre appareil.\nNe la communiquez à personne ! Même dans le cadre d'un support.\nArchethic ne vous demandera jamais cette information.", @@ -163,27 +153,6 @@ "enterYubikeyClientAPIKey": "Entrer la clé client de l'API", "enterYubikeyClientIDEmpty": "L'ID client est obligatoire", "enterYubikeyAPIKeyEmpty": "La clé client de l'API est obligatoire", - "transactionInfosHeader": "Infos de la transaction", - "transactionInfosKeyAddress": "Adresse", - "transactionInfosKeyType": "Type", - "transactionInfosKeyVersion": "Version", - "transactionInfosKeyPreviousPublicKey": "Clé publique de la transaction précédente", - "transactionInfosKeyPreviousSignature": "Signature à partir de la clé privée associée à la clé publique", - "transactionInfosKeyOriginSignature": "Signature à partir de la clé privée associée au dispositif", - "transactionInfosKeyData": "Données", - "transactionInfosKeyContent": "Contenu", - "transactionInfosKeyCode": "Code", - "transactionInfosKeyUCOLedger": "Opérations sur les registres - UCO", - "transactionInfosKeyTo": "À", - "transactionInfosKeyAmount": "Montant", - "transactionInfosKeyTokenLedger": "Opérations sur les registres - Jeton", - "transactionInfosKeyToken": "Jeton", - "transactionInfosKeyValidationStamp": "Estampille de Validation", - "transactionInfosKeyProofOfWork": "Preuve de Travail", - "transactionInfosKeyProofOfIntegrity": "Preuve d'intégrité", - "transactionInfosKeyTimeStamp": "Date de la génération de la transaction", - "transactionInfosKeyCrossValidationStamps": "Estampille(s) de contre-validation", - "transactionInfosKeySignature": "Signature", "transactionBuyHeader": "Où acheter des UCO ?", "receive": "Recevoir", "buy": "Acheter\nUCO", @@ -232,16 +201,10 @@ "welcomeDisclaimerChoice": "J'ai lu et j'accepte la politique de confidentialité", "showBalances": "Afficher les balances", "testnetEnabled": "Testnet activé", - "enterEndpointBlank": "Le point d'accès est obligatoire.", - "enterEndpointNotValid": "Le point d'accès n'est pas valide.", - "enterEndpointUseByNetwork": "Le point d'accès est déjà utilisé par un réseau.", - "enterEndpointHeader": "Veuillez préciser votre point d'accès.", "introNewWalletGetFirstInfosWelcome": "Bienvenue dans le Wallet Archethic.", "introNewWalletGetFirstInfosNameRequest": "Commençons par donner un nom à votre premier compte, qui sera stocké sur votre porte-clés décentralisé.", "introNewWalletGetFirstInfosNameInfos": "Il vous permettra de distinguer ce compte avec les autres comptes que vous pourrez, si vous le souhaitez, créer par la suite.\n\nATTENTION : Ce nom sera rattaché à votre porte-clés décentralisé et ne pourra plus être modifié.", "introNewWalletGetFirstInfosNameBlank": "Veuillez préciser le nom du nouveau compte", - "contactExistsName": "Vous possédez déjà un contact avec ce nom", - "contactExistsAddress": "Vous possédez déjà un contact avec cette adresse", "accountsHeader": "Comptes", "transactionHeader": "Transactions", "newAccount": "Nouveau compte", @@ -288,10 +251,6 @@ "tokens": "jetons", "tokensHeader": "Jetons", "nft": "NFT", - "keychainCreationTransactionConfirmed1": "Votre porte-clés a été créé avec %1 confirmation sur %2", - "keychainCreationTransactionConfirmed": "Votre porte-clés a été créé avec %1 confirmations sur %2", - "keychainAccessCreationTransactionConfirmed1": "Les accès à votre porte-clés ont été créés avec %1 confirmation sur %2", - "keychainAccessCreationTransactionConfirmed": "Les accès à votre porte-clés ont été créés avec %1 confirmations sur %2", "nftCreated": "NFT créé :", "notEnoughConfirmations": "La demande n'a pas pu être complétée en raison d'un manque de consensus", "serviceNotFound": "Le service n'existe pas dans le porte-clés", @@ -332,12 +291,9 @@ "passRecoveryPhraseBackupSecureNow": "Sécuriser mon portefeuille maintenant", "recoveryPhraseBackupRequired": "Sauvegardez votre phrase secrète de récupération pour protéger votre portefeuille et les fonds associés", "recoveryPhraseSave": "Sauvegarder la phrase secrète", - "newContact": "Nouveau contact", "save": "Sauvegarder", "next": "Suivant", "name": "Nom", - "deleteContact": "Supprimer le contact", - "favorites": "Favoris", "explorer": "Explorateur", "nftPropertiesOpenseaStructure": "Structure des propriétés basée sur le modèle d'Opensea", "bottomMainMenuTransactions": "Transactions", @@ -356,7 +312,6 @@ "removeKeychainAtLeast1": "Vous devez conserver au moins un service dans votre porte-clés.", "ucoBalance": "Balance UCO", "tokenBalance": "Balance Token", - "executionSC": "Exécution Smart-contract", "seedHex": "Voir en hexadécimal", "burnAddressLbl": "Adresse de destruction", "webChannelIncompatibilityWarning": "Système non compatible", @@ -629,7 +584,6 @@ "earnHeaderAddLiquidityTxt": "Pour obtenir des jetons LP, vous devez fournir des liquidités ici :", "liquidityAddDesc": "Ajoutez de la liquidité à la pool et recevez des Jetons LP représentant votre part", "keychainAddressCopied": "Adresse de la keychain copiée", - "seedCopied": "Phrase secrète copiée", "withdrawFarmLockDone": "Retrait complété avec succès", "claimFarmLockDone": "Récupération complétée avec succès", "levelUpFarmLockDone": "Montée de niveau complétée avec succès", @@ -696,7 +650,6 @@ "dappBoardMyDAppFavoriteAdd": "La DApp a été ajoutée dans vos favoris. Retrouvez là dans le menu général, section \"DApps Board\"", "dappBoardMyDAppFavoriteRemove": "La DApp a été supprimée de vos favoris.", "dappBoardDisclaimer": "Attention : En accédant à des applications décentralisées (DApps) tierces via aeWallet, vous acceptez les termes de nos Conditions d'Utilisation. (Cf menu A Propos)", - "dappBoardDisclaimerTitle": "Avis de non-responsabilité", "networkChangeHeader": "Changement d'env.", "networkChangeKeychainNotExists": "Votre wallet n'existe pas encore dans l'environnement {envName}. Nous vous proposons de le créer automatiquement.\nVeuillez sélectionner les comptes que vous souhaitez dupliquer dans cet environnement. Vous pourrez à tout moment ajouter ou retirer des comptes sur votre wallet {envName}.\n\nImportant :\n- Il n'existe aucune synchronisation automatique entre les environnements.\n- Les fonds, tokens, NFT et UCO ne peuvent pas être transférés ou dupliqués entre les environnements. Les comptes créés dans Testnet seront donc vides.", "@networkChangeKeychainNotExists": { diff --git a/lib/model/blockchain/recent_transaction.dart b/lib/model/blockchain/recent_transaction.dart index 1e01c7d9a..4730c87b0 100644 --- a/lib/model/blockchain/recent_transaction.dart +++ b/lib/model/blockchain/recent_transaction.dart @@ -2,114 +2,152 @@ import 'package:aewallet/infrastructure/datasources/appdb.hive.dart'; import 'package:aewallet/model/blockchain/token_information.dart'; import 'package:aewallet/model/data/contact.dart'; import 'package:archethic_lib_dart/archethic_lib_dart.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive/hive.dart'; +part 'recent_transaction.freezed.dart'; part 'recent_transaction.g.dart'; -/// [TransactionInput] represents the inputs from the transaction. +class RecentTransactionConverter + implements JsonConverter> { + const RecentTransactionConverter(); + + @override + RecentTransaction fromJson(Map json) { + return RecentTransaction( + address: json['address'] as String?, + typeTx: json['typeTx'] as int?, + timestamp: json['timestamp'] as int?, + fee: (json['fee'] as num?)?.toDouble(), + from: json['from'] as String?, + content: json['content'] as String?, + type: json['type'] as String?, + decryptedSecret: (json['decryptedSecret'] as List?) + ?.map((e) => e as String) + .toList(), + action: json['action'] as String?, + ledgerOperationMvtInfo: (json['ledgerOperationMvtInfo'] as List?) + ?.map((e) => _ledgerOperationMvtFromJson(e as Map)) + .toList(), + ownerships: (json['ownerships'] as List?) + ?.map((e) => Ownership.fromJson(e as Map)) + .toList(), + ); + } + + @override + Map toJson(RecentTransaction recentTransaction) { + return { + 'address': recentTransaction.address, + 'typeTx': recentTransaction.typeTx, + 'timestamp': recentTransaction.timestamp, + 'fee': recentTransaction.fee, + 'from': recentTransaction.from, + 'content': recentTransaction.content, + 'type': recentTransaction.type, + 'decryptedSecret': recentTransaction.decryptedSecret, + 'action': recentTransaction.action, + 'ledgerOperationMvtInfo': recentTransaction.ledgerOperationMvtInfo + ?.map(_ledgerOperationMvtToJson) + .toList(), + 'ownerships': recentTransaction.ownerships + ?.map((ownership) => ownership.toJson()) + .toList(), + }; + } + + // Helper to deserialize `LedgerOperationMvt` + LedgerOperationMvt _ledgerOperationMvtFromJson(Map json) { + return ( + amount: (json['amount'] as num?)?.toDouble(), + to: json['to'] as String?, + type: json['type'] as String?, + tokenInformation: json['tokenInformation'] != null + ? TokenInformation.fromJson( + json['tokenInformation'] as Map, + ) + : null, + ); + } + + // Helper to serialize `LedgerOperationMvt` + Map _ledgerOperationMvtToJson(LedgerOperationMvt mvt) { + return { + 'amount': mvt.amount, + 'to': mvt.to, + 'type': mvt.type, + 'tokenInformation': mvt.tokenInformation?.toJson(), + }; + } +} -/// Next field available : 15 +/// Next field available : 17 @HiveType(typeId: HiveTypeIds.recentTransactions) -class RecentTransaction extends HiveObject { - RecentTransaction({ - this.address, - this.typeTx, - this.amount, - this.recipient, - this.from, - this.fee, - this.content, - this.timestamp, - this.type, - this.decryptedSecret, - this.ownerships, - this.indexInLedger = 0, - }); - - factory RecentTransaction.fromJson(Map json) => - RecentTransaction( - address: json['address'], - typeTx: json['typeTx']?.toInt(), - recipient: json['recipient'], - amount: json['amount']?.toDouble(), - fee: json['fee']?.toDouble(), - from: json['from'], - content: json['content'], - timestamp: json['timestamp'], - type: json['type'], - decryptedSecret: json['decryptedSecret'], - ); +@RecentTransactionConverter() +@freezed +class RecentTransaction extends HiveObject with _$RecentTransaction { + factory RecentTransaction({ + /// Address of transaction + @HiveField(0) String? address, + + /// Type of transaction : 1=Transfer/Input, 2=Transfer/Output, 3=Token creation + @HiveField(1) int? typeTx, + + /// Amount: asset amount + //@HiveField(2) double? amount, + + /// Recipients: For non asset transfers, the list of recipients + /// of the transaction (e.g Smart contract interactions) + //@HiveField(3) String? recipient, + + /// Timestamp: Date time when the transaction was generated + @HiveField(4) int? timestamp, + + /// Fee: transaction fee (distributed over the node rewards) + @HiveField(5) double? fee, + + /// From: transaction which send the amount of assets + @HiveField(6) String? from, + + /// Content: free zone for data hosting (string or hexadecimal) + @HiveField(9) String? content, + + /// Type: UCO/tokens/Call + @HiveField(10) String? type, + + /// Token Information + //@HiveField(11) TokenInformation? tokenInformation, + + /// Contact Information + @HiveField(12) + @Deprecated('Thanks to hive, we should keep this unused property...') + Contact? contactInformation, + + /// Decrypted Secret + @HiveField(14) List? decryptedSecret, + + /// Action + @HiveField(15) String? action, + + /// Ledger operations movements + @HiveField(16) LedgerOperationMvtInfo? ledgerOperationMvtInfo, + List? ownerships, + }) = _RecentTransaction; + + RecentTransaction._(); /// Types of transaction static const int transferInput = 1; static const int transferOutput = 2; static const int tokenCreation = 3; static const int hosting = 4; - - /// Address of transaction - @HiveField(0) - String? address; - - /// Type of transaction : 1=Transfer/Input, 2=Transfer/Output, 3=Token creation - @HiveField(1) - int? typeTx; - - /// Amount: asset amount - @HiveField(2) - double? amount; - - /// Recipients: For non asset transfers, the list of recipients - /// of the transaction (e.g Smart contract interactions) - @HiveField(3) - String? recipient; - - /// Timestamp: Date time when the transaction was generated - @HiveField(4) - int? timestamp; - - /// Fee: transaction fee (distributed over the node rewards) - @HiveField(5) - double? fee; - - /// From: transaction which send the amount of assets - @HiveField(6) - String? from; - - /// Content: free zone for data hosting (string or hexadecimal) - @HiveField(9) - String? content; - - /// Type: UCO/tokens/Call - @HiveField(10) - String? type; - - /// Token Information - @HiveField(11) - TokenInformation? tokenInformation; - - /// Contact Information - @HiveField(12) - @Deprecated('Thanks to hive, we should keep this unused property...') - Contact? contactInformation; - - /// Decrypted Secret - @HiveField(14) - List? decryptedSecret; - - List? ownerships; - String? tokenAddress; - int indexInLedger; - - Map toJson() => { - 'address': address, - 'typeTx': typeTx, - 'recipient': recipient, - 'amount': amount, - 'fee': fee, - 'from': from, - 'content': content, - 'timestamp': timestamp, - 'type': type, - 'decryptedSecret': decryptedSecret, - }; } + +typedef LedgerOperationMvt = ({ + double? amount, + String? to, + String? type, + TokenInformation? tokenInformation, +}); + +typedef LedgerOperationMvtInfo = List; diff --git a/lib/model/blockchain/recent_transaction.freezed.dart b/lib/model/blockchain/recent_transaction.freezed.dart new file mode 100644 index 000000000..ff79ba1b2 --- /dev/null +++ b/lib/model/blockchain/recent_transaction.freezed.dart @@ -0,0 +1,614 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'recent_transaction.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$RecentTransaction { + /// Address of transaction + @HiveField(0) + String? get address => throw _privateConstructorUsedError; + + /// Type of transaction : 1=Transfer/Input, 2=Transfer/Output, 3=Token creation + @HiveField(1) + int? get typeTx => throw _privateConstructorUsedError; + + /// Amount: asset amount +//@HiveField(2) double? amount, + /// Recipients: For non asset transfers, the list of recipients + /// of the transaction (e.g Smart contract interactions) +//@HiveField(3) String? recipient, + /// Timestamp: Date time when the transaction was generated + @HiveField(4) + int? get timestamp => throw _privateConstructorUsedError; + + /// Fee: transaction fee (distributed over the node rewards) + @HiveField(5) + double? get fee => throw _privateConstructorUsedError; + + /// From: transaction which send the amount of assets + @HiveField(6) + String? get from => throw _privateConstructorUsedError; + + /// Content: free zone for data hosting (string or hexadecimal) + @HiveField(9) + String? get content => throw _privateConstructorUsedError; + + /// Type: UCO/tokens/Call + @HiveField(10) + String? get type => throw _privateConstructorUsedError; + + /// Token Information +//@HiveField(11) TokenInformation? tokenInformation, + /// Contact Information + @HiveField(12) + @Deprecated('Thanks to hive, we should keep this unused property...') + Contact? get contactInformation => throw _privateConstructorUsedError; + + /// Decrypted Secret + @HiveField(14) + List? get decryptedSecret => throw _privateConstructorUsedError; + + /// Action + @HiveField(15) + String? get action => throw _privateConstructorUsedError; + + /// Ledger operations movements + @HiveField(16) + List< + ({ + double? amount, + String? to, + TokenInformation? tokenInformation, + String? type + })>? get ledgerOperationMvtInfo => throw _privateConstructorUsedError; + List? get ownerships => throw _privateConstructorUsedError; + + /// Create a copy of RecentTransaction + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $RecentTransactionCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $RecentTransactionCopyWith<$Res> { + factory $RecentTransactionCopyWith( + RecentTransaction value, $Res Function(RecentTransaction) then) = + _$RecentTransactionCopyWithImpl<$Res, RecentTransaction>; + @useResult + $Res call( + {@HiveField(0) String? address, + @HiveField(1) int? typeTx, + @HiveField(4) int? timestamp, + @HiveField(5) double? fee, + @HiveField(6) String? from, + @HiveField(9) String? content, + @HiveField(10) String? type, + @HiveField(12) + @Deprecated('Thanks to hive, we should keep this unused property...') + Contact? contactInformation, + @HiveField(14) List? decryptedSecret, + @HiveField(15) String? action, + @HiveField(16) + List< + ({ + double? amount, + String? to, + TokenInformation? tokenInformation, + String? type + })>? + ledgerOperationMvtInfo, + List? ownerships}); +} + +/// @nodoc +class _$RecentTransactionCopyWithImpl<$Res, $Val extends RecentTransaction> + implements $RecentTransactionCopyWith<$Res> { + _$RecentTransactionCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of RecentTransaction + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? address = freezed, + Object? typeTx = freezed, + Object? timestamp = freezed, + Object? fee = freezed, + Object? from = freezed, + Object? content = freezed, + Object? type = freezed, + Object? contactInformation = freezed, + Object? decryptedSecret = freezed, + Object? action = freezed, + Object? ledgerOperationMvtInfo = freezed, + Object? ownerships = freezed, + }) { + return _then(_value.copyWith( + address: freezed == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as String?, + typeTx: freezed == typeTx + ? _value.typeTx + : typeTx // ignore: cast_nullable_to_non_nullable + as int?, + timestamp: freezed == timestamp + ? _value.timestamp + : timestamp // ignore: cast_nullable_to_non_nullable + as int?, + fee: freezed == fee + ? _value.fee + : fee // ignore: cast_nullable_to_non_nullable + as double?, + from: freezed == from + ? _value.from + : from // ignore: cast_nullable_to_non_nullable + as String?, + content: freezed == content + ? _value.content + : content // ignore: cast_nullable_to_non_nullable + as String?, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String?, + contactInformation: freezed == contactInformation + ? _value.contactInformation + : contactInformation // ignore: cast_nullable_to_non_nullable + as Contact?, + decryptedSecret: freezed == decryptedSecret + ? _value.decryptedSecret + : decryptedSecret // ignore: cast_nullable_to_non_nullable + as List?, + action: freezed == action + ? _value.action + : action // ignore: cast_nullable_to_non_nullable + as String?, + ledgerOperationMvtInfo: freezed == ledgerOperationMvtInfo + ? _value.ledgerOperationMvtInfo + : ledgerOperationMvtInfo // ignore: cast_nullable_to_non_nullable + as List< + ({ + double? amount, + String? to, + TokenInformation? tokenInformation, + String? type + })>?, + ownerships: freezed == ownerships + ? _value.ownerships + : ownerships // ignore: cast_nullable_to_non_nullable + as List?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$RecentTransactionImplCopyWith<$Res> + implements $RecentTransactionCopyWith<$Res> { + factory _$$RecentTransactionImplCopyWith(_$RecentTransactionImpl value, + $Res Function(_$RecentTransactionImpl) then) = + __$$RecentTransactionImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {@HiveField(0) String? address, + @HiveField(1) int? typeTx, + @HiveField(4) int? timestamp, + @HiveField(5) double? fee, + @HiveField(6) String? from, + @HiveField(9) String? content, + @HiveField(10) String? type, + @HiveField(12) + @Deprecated('Thanks to hive, we should keep this unused property...') + Contact? contactInformation, + @HiveField(14) List? decryptedSecret, + @HiveField(15) String? action, + @HiveField(16) + List< + ({ + double? amount, + String? to, + TokenInformation? tokenInformation, + String? type + })>? + ledgerOperationMvtInfo, + List? ownerships}); +} + +/// @nodoc +class __$$RecentTransactionImplCopyWithImpl<$Res> + extends _$RecentTransactionCopyWithImpl<$Res, _$RecentTransactionImpl> + implements _$$RecentTransactionImplCopyWith<$Res> { + __$$RecentTransactionImplCopyWithImpl(_$RecentTransactionImpl _value, + $Res Function(_$RecentTransactionImpl) _then) + : super(_value, _then); + + /// Create a copy of RecentTransaction + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? address = freezed, + Object? typeTx = freezed, + Object? timestamp = freezed, + Object? fee = freezed, + Object? from = freezed, + Object? content = freezed, + Object? type = freezed, + Object? contactInformation = freezed, + Object? decryptedSecret = freezed, + Object? action = freezed, + Object? ledgerOperationMvtInfo = freezed, + Object? ownerships = freezed, + }) { + return _then(_$RecentTransactionImpl( + address: freezed == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as String?, + typeTx: freezed == typeTx + ? _value.typeTx + : typeTx // ignore: cast_nullable_to_non_nullable + as int?, + timestamp: freezed == timestamp + ? _value.timestamp + : timestamp // ignore: cast_nullable_to_non_nullable + as int?, + fee: freezed == fee + ? _value.fee + : fee // ignore: cast_nullable_to_non_nullable + as double?, + from: freezed == from + ? _value.from + : from // ignore: cast_nullable_to_non_nullable + as String?, + content: freezed == content + ? _value.content + : content // ignore: cast_nullable_to_non_nullable + as String?, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String?, + contactInformation: freezed == contactInformation + ? _value.contactInformation + : contactInformation // ignore: cast_nullable_to_non_nullable + as Contact?, + decryptedSecret: freezed == decryptedSecret + ? _value._decryptedSecret + : decryptedSecret // ignore: cast_nullable_to_non_nullable + as List?, + action: freezed == action + ? _value.action + : action // ignore: cast_nullable_to_non_nullable + as String?, + ledgerOperationMvtInfo: freezed == ledgerOperationMvtInfo + ? _value._ledgerOperationMvtInfo + : ledgerOperationMvtInfo // ignore: cast_nullable_to_non_nullable + as List< + ({ + double? amount, + String? to, + TokenInformation? tokenInformation, + String? type + })>?, + ownerships: freezed == ownerships + ? _value._ownerships + : ownerships // ignore: cast_nullable_to_non_nullable + as List?, + )); + } +} + +/// @nodoc + +class _$RecentTransactionImpl extends _RecentTransaction { + _$RecentTransactionImpl( + {@HiveField(0) this.address, + @HiveField(1) this.typeTx, + @HiveField(4) this.timestamp, + @HiveField(5) this.fee, + @HiveField(6) this.from, + @HiveField(9) this.content, + @HiveField(10) this.type, + @HiveField(12) + @Deprecated('Thanks to hive, we should keep this unused property...') + this.contactInformation, + @HiveField(14) final List? decryptedSecret, + @HiveField(15) this.action, + @HiveField(16) + final List< + ({ + double? amount, + String? to, + TokenInformation? tokenInformation, + String? type + })>? + ledgerOperationMvtInfo, + final List? ownerships}) + : _decryptedSecret = decryptedSecret, + _ledgerOperationMvtInfo = ledgerOperationMvtInfo, + _ownerships = ownerships, + super._(); + + /// Address of transaction + @override + @HiveField(0) + final String? address; + + /// Type of transaction : 1=Transfer/Input, 2=Transfer/Output, 3=Token creation + @override + @HiveField(1) + final int? typeTx; + + /// Amount: asset amount +//@HiveField(2) double? amount, + /// Recipients: For non asset transfers, the list of recipients + /// of the transaction (e.g Smart contract interactions) +//@HiveField(3) String? recipient, + /// Timestamp: Date time when the transaction was generated + @override + @HiveField(4) + final int? timestamp; + + /// Fee: transaction fee (distributed over the node rewards) + @override + @HiveField(5) + final double? fee; + + /// From: transaction which send the amount of assets + @override + @HiveField(6) + final String? from; + + /// Content: free zone for data hosting (string or hexadecimal) + @override + @HiveField(9) + final String? content; + + /// Type: UCO/tokens/Call + @override + @HiveField(10) + final String? type; + + /// Token Information +//@HiveField(11) TokenInformation? tokenInformation, + /// Contact Information + @override + @HiveField(12) + @Deprecated('Thanks to hive, we should keep this unused property...') + final Contact? contactInformation; + + /// Decrypted Secret + final List? _decryptedSecret; + + /// Decrypted Secret + @override + @HiveField(14) + List? get decryptedSecret { + final value = _decryptedSecret; + if (value == null) return null; + if (_decryptedSecret is EqualUnmodifiableListView) return _decryptedSecret; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + /// Action + @override + @HiveField(15) + final String? action; + + /// Ledger operations movements + final List< + ({ + double? amount, + String? to, + TokenInformation? tokenInformation, + String? type + })>? _ledgerOperationMvtInfo; + + /// Ledger operations movements + @override + @HiveField(16) + List< + ({ + double? amount, + String? to, + TokenInformation? tokenInformation, + String? type + })>? get ledgerOperationMvtInfo { + final value = _ledgerOperationMvtInfo; + if (value == null) return null; + if (_ledgerOperationMvtInfo is EqualUnmodifiableListView) + return _ledgerOperationMvtInfo; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + final List? _ownerships; + @override + List? get ownerships { + final value = _ownerships; + if (value == null) return null; + if (_ownerships is EqualUnmodifiableListView) return _ownerships; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + String toString() { + return 'RecentTransaction(address: $address, typeTx: $typeTx, timestamp: $timestamp, fee: $fee, from: $from, content: $content, type: $type, contactInformation: $contactInformation, decryptedSecret: $decryptedSecret, action: $action, ledgerOperationMvtInfo: $ledgerOperationMvtInfo, ownerships: $ownerships)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$RecentTransactionImpl && + (identical(other.address, address) || other.address == address) && + (identical(other.typeTx, typeTx) || other.typeTx == typeTx) && + (identical(other.timestamp, timestamp) || + other.timestamp == timestamp) && + (identical(other.fee, fee) || other.fee == fee) && + (identical(other.from, from) || other.from == from) && + (identical(other.content, content) || other.content == content) && + (identical(other.type, type) || other.type == type) && + (identical(other.contactInformation, contactInformation) || + other.contactInformation == contactInformation) && + const DeepCollectionEquality() + .equals(other._decryptedSecret, _decryptedSecret) && + (identical(other.action, action) || other.action == action) && + const DeepCollectionEquality().equals( + other._ledgerOperationMvtInfo, _ledgerOperationMvtInfo) && + const DeepCollectionEquality() + .equals(other._ownerships, _ownerships)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + address, + typeTx, + timestamp, + fee, + from, + content, + type, + contactInformation, + const DeepCollectionEquality().hash(_decryptedSecret), + action, + const DeepCollectionEquality().hash(_ledgerOperationMvtInfo), + const DeepCollectionEquality().hash(_ownerships)); + + /// Create a copy of RecentTransaction + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$RecentTransactionImplCopyWith<_$RecentTransactionImpl> get copyWith => + __$$RecentTransactionImplCopyWithImpl<_$RecentTransactionImpl>( + this, _$identity); +} + +abstract class _RecentTransaction extends RecentTransaction { + factory _RecentTransaction( + {@HiveField(0) final String? address, + @HiveField(1) final int? typeTx, + @HiveField(4) final int? timestamp, + @HiveField(5) final double? fee, + @HiveField(6) final String? from, + @HiveField(9) final String? content, + @HiveField(10) final String? type, + @HiveField(12) + @Deprecated('Thanks to hive, we should keep this unused property...') + final Contact? contactInformation, + @HiveField(14) final List? decryptedSecret, + @HiveField(15) final String? action, + @HiveField(16) + final List< + ({ + double? amount, + String? to, + TokenInformation? tokenInformation, + String? type + })>? + ledgerOperationMvtInfo, + final List? ownerships}) = _$RecentTransactionImpl; + _RecentTransaction._() : super._(); + + /// Address of transaction + @override + @HiveField(0) + String? get address; + + /// Type of transaction : 1=Transfer/Input, 2=Transfer/Output, 3=Token creation + @override + @HiveField(1) + int? get typeTx; + + /// Amount: asset amount +//@HiveField(2) double? amount, + /// Recipients: For non asset transfers, the list of recipients + /// of the transaction (e.g Smart contract interactions) +//@HiveField(3) String? recipient, + /// Timestamp: Date time when the transaction was generated + @override + @HiveField(4) + int? get timestamp; + + /// Fee: transaction fee (distributed over the node rewards) + @override + @HiveField(5) + double? get fee; + + /// From: transaction which send the amount of assets + @override + @HiveField(6) + String? get from; + + /// Content: free zone for data hosting (string or hexadecimal) + @override + @HiveField(9) + String? get content; + + /// Type: UCO/tokens/Call + @override + @HiveField(10) + String? get type; + + /// Token Information +//@HiveField(11) TokenInformation? tokenInformation, + /// Contact Information + @override + @HiveField(12) + @Deprecated('Thanks to hive, we should keep this unused property...') + Contact? get contactInformation; + + /// Decrypted Secret + @override + @HiveField(14) + List? get decryptedSecret; + + /// Action + @override + @HiveField(15) + String? get action; + + /// Ledger operations movements + @override + @HiveField(16) + List< + ({ + double? amount, + String? to, + TokenInformation? tokenInformation, + String? type + })>? get ledgerOperationMvtInfo; + @override + List? get ownerships; + + /// Create a copy of RecentTransaction + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$RecentTransactionImplCopyWith<_$RecentTransactionImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/model/blockchain/recent_transaction.g.dart b/lib/model/blockchain/recent_transaction.g.dart index 711083310..8caf399dc 100644 --- a/lib/model/blockchain/recent_transaction.g.dart +++ b/lib/model/blockchain/recent_transaction.g.dart @@ -19,31 +19,32 @@ class RecentTransactionAdapter extends TypeAdapter { return RecentTransaction( address: fields[0] as String?, typeTx: fields[1] as int?, - amount: fields[2] as double?, - recipient: fields[3] as String?, - from: fields[6] as String?, + timestamp: fields[4] as int?, fee: fields[5] as double?, + from: fields[6] as String?, content: fields[9] as String?, - timestamp: fields[4] as int?, type: fields[10] as String?, + contactInformation: fields[12] as Contact?, decryptedSecret: (fields[14] as List?)?.cast(), - ) - ..tokenInformation = fields[11] as TokenInformation? - ..contactInformation = fields[12] as Contact?; + action: fields[15] as String?, + ledgerOperationMvtInfo: (fields[16] as List?)?.cast< + ({ + double amount, + String to, + TokenInformation tokenInformation, + String type + })>(), + ); } @override void write(BinaryWriter writer, RecentTransaction obj) { writer - ..writeByte(12) + ..writeByte(11) ..writeByte(0) ..write(obj.address) ..writeByte(1) ..write(obj.typeTx) - ..writeByte(2) - ..write(obj.amount) - ..writeByte(3) - ..write(obj.recipient) ..writeByte(4) ..write(obj.timestamp) ..writeByte(5) @@ -54,12 +55,14 @@ class RecentTransactionAdapter extends TypeAdapter { ..write(obj.content) ..writeByte(10) ..write(obj.type) - ..writeByte(11) - ..write(obj.tokenInformation) ..writeByte(12) ..write(obj.contactInformation) ..writeByte(14) - ..write(obj.decryptedSecret); + ..write(obj.decryptedSecret) + ..writeByte(15) + ..write(obj.action) + ..writeByte(16) + ..write(obj.ledgerOperationMvtInfo); } @override diff --git a/lib/model/blockchain/token_information.dart b/lib/model/blockchain/token_information.dart index 0013885ed..7e896d470 100644 --- a/lib/model/blockchain/token_information.dart +++ b/lib/model/blockchain/token_information.dart @@ -5,112 +5,28 @@ import 'package:aewallet/infrastructure/datasources/appdb.hive.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive/hive.dart'; +part 'token_information.freezed.dart'; part 'token_information.g.dart'; -class TokenInformationConverter - implements JsonConverter> { - const TokenInformationConverter(); - - @override - TokenInformation fromJson(Map json) { - return TokenInformation( - address: json['address'] as String?, - name: json['name'] as String?, - id: json['id'] as String?, - supply: json['supply'] as double?, - type: json['type'] as String?, - symbol: json['symbol'] as String?, - tokenProperties: json['tokenProperties'] as Map?, - tokenCollection: json['tokenCollection'] as List>?, - aeip: json['aeip'] as List?, - decimals: json['decimals'] as int?, - isLPToken: json['isLPToken'] as bool?, - isVerified: json['isVerified'] as bool?, - ); - } - - @override - Map toJson(TokenInformation tokenInformation) { - return { - 'address': tokenInformation.address, - 'name': tokenInformation.name, - 'id': tokenInformation.id, - 'supply': tokenInformation.supply, - 'type': tokenInformation.type, - 'symbol': tokenInformation.symbol, - 'tokenProperties': tokenInformation.tokenProperties, - 'tokenCollection': tokenInformation.tokenCollection, - 'aeip': tokenInformation.aeip, - 'decimals': tokenInformation.decimals, - 'isLPToken': tokenInformation.isLPToken, - 'isVerified': tokenInformation.isVerified, - }; - } -} - /// Next field available : 18 @HiveType(typeId: HiveTypeIds.tokenInformation) -class TokenInformation extends HiveObject { - TokenInformation({ - this.address, - this.name, - this.id, - this.supply, - this.type, - this.symbol, - this.tokenProperties, - this.tokenCollection, - this.aeip, - this.decimals, - this.isLPToken, - this.isVerified, - }); - - /// Address of token - @HiveField(0) - String? address; - - /// Name of token - @HiveField(1) - String? name; - - /// Type - @HiveField(3) - String? type; - - /// Symbol - @HiveField(4) - String? symbol; - - /// Supply - @HiveField(9) - double? supply; - - /// Token's Id - @HiveField(10) - String? id; - - /// Token Properties - @HiveField(12) - Map? tokenProperties; - - /// AEIP - @HiveField(13) - List? aeip; - - /// Collection - @HiveField(14) - List>? tokenCollection; - - /// Decimals - @HiveField(15) - int? decimals; - - /// LP Token ? - @HiveField(16) - bool? isLPToken; - - /// Verified token ? - @HiveField(17) - bool? isVerified; +@freezed +class TokenInformation with _$TokenInformation { + const factory TokenInformation({ + @HiveField(0) String? address, + @HiveField(1) String? name, + @HiveField(3) String? type, + @HiveField(4) String? symbol, + @HiveField(9) double? supply, + @HiveField(10) String? id, + @HiveField(12) Map? tokenProperties, + @HiveField(13) List? aeip, + @HiveField(14) List>? tokenCollection, + @HiveField(15) int? decimals, + @HiveField(16) bool? isLPToken, + @HiveField(17) bool? isVerified, + }) = _TokenInformation; + + factory TokenInformation.fromJson(Map json) => + _$TokenInformationFromJson(json); } diff --git a/lib/model/blockchain/token_information.freezed.dart b/lib/model/blockchain/token_information.freezed.dart new file mode 100644 index 000000000..c665d0b20 --- /dev/null +++ b/lib/model/blockchain/token_information.freezed.dart @@ -0,0 +1,472 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'token_information.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +TokenInformation _$TokenInformationFromJson(Map json) { + return _TokenInformation.fromJson(json); +} + +/// @nodoc +mixin _$TokenInformation { + @HiveField(0) + String? get address => throw _privateConstructorUsedError; + @HiveField(1) + String? get name => throw _privateConstructorUsedError; + @HiveField(3) + String? get type => throw _privateConstructorUsedError; + @HiveField(4) + String? get symbol => throw _privateConstructorUsedError; + @HiveField(9) + double? get supply => throw _privateConstructorUsedError; + @HiveField(10) + String? get id => throw _privateConstructorUsedError; + @HiveField(12) + Map? get tokenProperties => + throw _privateConstructorUsedError; + @HiveField(13) + List? get aeip => throw _privateConstructorUsedError; + @HiveField(14) + List>? get tokenCollection => + throw _privateConstructorUsedError; + @HiveField(15) + int? get decimals => throw _privateConstructorUsedError; + @HiveField(16) + bool? get isLPToken => throw _privateConstructorUsedError; + @HiveField(17) + bool? get isVerified => throw _privateConstructorUsedError; + + /// Serializes this TokenInformation to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of TokenInformation + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $TokenInformationCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TokenInformationCopyWith<$Res> { + factory $TokenInformationCopyWith( + TokenInformation value, $Res Function(TokenInformation) then) = + _$TokenInformationCopyWithImpl<$Res, TokenInformation>; + @useResult + $Res call( + {@HiveField(0) String? address, + @HiveField(1) String? name, + @HiveField(3) String? type, + @HiveField(4) String? symbol, + @HiveField(9) double? supply, + @HiveField(10) String? id, + @HiveField(12) Map? tokenProperties, + @HiveField(13) List? aeip, + @HiveField(14) List>? tokenCollection, + @HiveField(15) int? decimals, + @HiveField(16) bool? isLPToken, + @HiveField(17) bool? isVerified}); +} + +/// @nodoc +class _$TokenInformationCopyWithImpl<$Res, $Val extends TokenInformation> + implements $TokenInformationCopyWith<$Res> { + _$TokenInformationCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of TokenInformation + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? address = freezed, + Object? name = freezed, + Object? type = freezed, + Object? symbol = freezed, + Object? supply = freezed, + Object? id = freezed, + Object? tokenProperties = freezed, + Object? aeip = freezed, + Object? tokenCollection = freezed, + Object? decimals = freezed, + Object? isLPToken = freezed, + Object? isVerified = freezed, + }) { + return _then(_value.copyWith( + address: freezed == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as String?, + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String?, + symbol: freezed == symbol + ? _value.symbol + : symbol // ignore: cast_nullable_to_non_nullable + as String?, + supply: freezed == supply + ? _value.supply + : supply // ignore: cast_nullable_to_non_nullable + as double?, + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + tokenProperties: freezed == tokenProperties + ? _value.tokenProperties + : tokenProperties // ignore: cast_nullable_to_non_nullable + as Map?, + aeip: freezed == aeip + ? _value.aeip + : aeip // ignore: cast_nullable_to_non_nullable + as List?, + tokenCollection: freezed == tokenCollection + ? _value.tokenCollection + : tokenCollection // ignore: cast_nullable_to_non_nullable + as List>?, + decimals: freezed == decimals + ? _value.decimals + : decimals // ignore: cast_nullable_to_non_nullable + as int?, + isLPToken: freezed == isLPToken + ? _value.isLPToken + : isLPToken // ignore: cast_nullable_to_non_nullable + as bool?, + isVerified: freezed == isVerified + ? _value.isVerified + : isVerified // ignore: cast_nullable_to_non_nullable + as bool?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$TokenInformationImplCopyWith<$Res> + implements $TokenInformationCopyWith<$Res> { + factory _$$TokenInformationImplCopyWith(_$TokenInformationImpl value, + $Res Function(_$TokenInformationImpl) then) = + __$$TokenInformationImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {@HiveField(0) String? address, + @HiveField(1) String? name, + @HiveField(3) String? type, + @HiveField(4) String? symbol, + @HiveField(9) double? supply, + @HiveField(10) String? id, + @HiveField(12) Map? tokenProperties, + @HiveField(13) List? aeip, + @HiveField(14) List>? tokenCollection, + @HiveField(15) int? decimals, + @HiveField(16) bool? isLPToken, + @HiveField(17) bool? isVerified}); +} + +/// @nodoc +class __$$TokenInformationImplCopyWithImpl<$Res> + extends _$TokenInformationCopyWithImpl<$Res, _$TokenInformationImpl> + implements _$$TokenInformationImplCopyWith<$Res> { + __$$TokenInformationImplCopyWithImpl(_$TokenInformationImpl _value, + $Res Function(_$TokenInformationImpl) _then) + : super(_value, _then); + + /// Create a copy of TokenInformation + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? address = freezed, + Object? name = freezed, + Object? type = freezed, + Object? symbol = freezed, + Object? supply = freezed, + Object? id = freezed, + Object? tokenProperties = freezed, + Object? aeip = freezed, + Object? tokenCollection = freezed, + Object? decimals = freezed, + Object? isLPToken = freezed, + Object? isVerified = freezed, + }) { + return _then(_$TokenInformationImpl( + address: freezed == address + ? _value.address + : address // ignore: cast_nullable_to_non_nullable + as String?, + name: freezed == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String?, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String?, + symbol: freezed == symbol + ? _value.symbol + : symbol // ignore: cast_nullable_to_non_nullable + as String?, + supply: freezed == supply + ? _value.supply + : supply // ignore: cast_nullable_to_non_nullable + as double?, + id: freezed == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String?, + tokenProperties: freezed == tokenProperties + ? _value._tokenProperties + : tokenProperties // ignore: cast_nullable_to_non_nullable + as Map?, + aeip: freezed == aeip + ? _value._aeip + : aeip // ignore: cast_nullable_to_non_nullable + as List?, + tokenCollection: freezed == tokenCollection + ? _value._tokenCollection + : tokenCollection // ignore: cast_nullable_to_non_nullable + as List>?, + decimals: freezed == decimals + ? _value.decimals + : decimals // ignore: cast_nullable_to_non_nullable + as int?, + isLPToken: freezed == isLPToken + ? _value.isLPToken + : isLPToken // ignore: cast_nullable_to_non_nullable + as bool?, + isVerified: freezed == isVerified + ? _value.isVerified + : isVerified // ignore: cast_nullable_to_non_nullable + as bool?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$TokenInformationImpl implements _TokenInformation { + const _$TokenInformationImpl( + {@HiveField(0) this.address, + @HiveField(1) this.name, + @HiveField(3) this.type, + @HiveField(4) this.symbol, + @HiveField(9) this.supply, + @HiveField(10) this.id, + @HiveField(12) final Map? tokenProperties, + @HiveField(13) final List? aeip, + @HiveField(14) final List>? tokenCollection, + @HiveField(15) this.decimals, + @HiveField(16) this.isLPToken, + @HiveField(17) this.isVerified}) + : _tokenProperties = tokenProperties, + _aeip = aeip, + _tokenCollection = tokenCollection; + + factory _$TokenInformationImpl.fromJson(Map json) => + _$$TokenInformationImplFromJson(json); + + @override + @HiveField(0) + final String? address; + @override + @HiveField(1) + final String? name; + @override + @HiveField(3) + final String? type; + @override + @HiveField(4) + final String? symbol; + @override + @HiveField(9) + final double? supply; + @override + @HiveField(10) + final String? id; + final Map? _tokenProperties; + @override + @HiveField(12) + Map? get tokenProperties { + final value = _tokenProperties; + if (value == null) return null; + if (_tokenProperties is EqualUnmodifiableMapView) return _tokenProperties; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + final List? _aeip; + @override + @HiveField(13) + List? get aeip { + final value = _aeip; + if (value == null) return null; + if (_aeip is EqualUnmodifiableListView) return _aeip; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + final List>? _tokenCollection; + @override + @HiveField(14) + List>? get tokenCollection { + final value = _tokenCollection; + if (value == null) return null; + if (_tokenCollection is EqualUnmodifiableListView) return _tokenCollection; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(value); + } + + @override + @HiveField(15) + final int? decimals; + @override + @HiveField(16) + final bool? isLPToken; + @override + @HiveField(17) + final bool? isVerified; + + @override + String toString() { + return 'TokenInformation(address: $address, name: $name, type: $type, symbol: $symbol, supply: $supply, id: $id, tokenProperties: $tokenProperties, aeip: $aeip, tokenCollection: $tokenCollection, decimals: $decimals, isLPToken: $isLPToken, isVerified: $isVerified)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TokenInformationImpl && + (identical(other.address, address) || other.address == address) && + (identical(other.name, name) || other.name == name) && + (identical(other.type, type) || other.type == type) && + (identical(other.symbol, symbol) || other.symbol == symbol) && + (identical(other.supply, supply) || other.supply == supply) && + (identical(other.id, id) || other.id == id) && + const DeepCollectionEquality() + .equals(other._tokenProperties, _tokenProperties) && + const DeepCollectionEquality().equals(other._aeip, _aeip) && + const DeepCollectionEquality() + .equals(other._tokenCollection, _tokenCollection) && + (identical(other.decimals, decimals) || + other.decimals == decimals) && + (identical(other.isLPToken, isLPToken) || + other.isLPToken == isLPToken) && + (identical(other.isVerified, isVerified) || + other.isVerified == isVerified)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash( + runtimeType, + address, + name, + type, + symbol, + supply, + id, + const DeepCollectionEquality().hash(_tokenProperties), + const DeepCollectionEquality().hash(_aeip), + const DeepCollectionEquality().hash(_tokenCollection), + decimals, + isLPToken, + isVerified); + + /// Create a copy of TokenInformation + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$TokenInformationImplCopyWith<_$TokenInformationImpl> get copyWith => + __$$TokenInformationImplCopyWithImpl<_$TokenInformationImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$TokenInformationImplToJson( + this, + ); + } +} + +abstract class _TokenInformation implements TokenInformation { + const factory _TokenInformation( + {@HiveField(0) final String? address, + @HiveField(1) final String? name, + @HiveField(3) final String? type, + @HiveField(4) final String? symbol, + @HiveField(9) final double? supply, + @HiveField(10) final String? id, + @HiveField(12) final Map? tokenProperties, + @HiveField(13) final List? aeip, + @HiveField(14) final List>? tokenCollection, + @HiveField(15) final int? decimals, + @HiveField(16) final bool? isLPToken, + @HiveField(17) final bool? isVerified}) = _$TokenInformationImpl; + + factory _TokenInformation.fromJson(Map json) = + _$TokenInformationImpl.fromJson; + + @override + @HiveField(0) + String? get address; + @override + @HiveField(1) + String? get name; + @override + @HiveField(3) + String? get type; + @override + @HiveField(4) + String? get symbol; + @override + @HiveField(9) + double? get supply; + @override + @HiveField(10) + String? get id; + @override + @HiveField(12) + Map? get tokenProperties; + @override + @HiveField(13) + List? get aeip; + @override + @HiveField(14) + List>? get tokenCollection; + @override + @HiveField(15) + int? get decimals; + @override + @HiveField(16) + bool? get isLPToken; + @override + @HiveField(17) + bool? get isVerified; + + /// Create a copy of TokenInformation + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$TokenInformationImplCopyWith<_$TokenInformationImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/model/blockchain/token_information.g.dart b/lib/model/blockchain/token_information.g.dart index 7042fa6df..aa1bc0ac5 100644 --- a/lib/model/blockchain/token_information.g.dart +++ b/lib/model/blockchain/token_information.g.dart @@ -19,15 +19,15 @@ class TokenInformationAdapter extends TypeAdapter { return TokenInformation( address: fields[0] as String?, name: fields[1] as String?, - id: fields[10] as String?, - supply: fields[9] as double?, type: fields[3] as String?, symbol: fields[4] as String?, + supply: fields[9] as double?, + id: fields[10] as String?, tokenProperties: (fields[12] as Map?)?.cast(), + aeip: (fields[13] as List?)?.cast(), tokenCollection: (fields[14] as List?) ?.map((dynamic e) => (e as Map).cast()) ?.toList(), - aeip: (fields[13] as List?)?.cast(), decimals: fields[15] as int?, isLPToken: fields[16] as bool?, isVerified: fields[17] as bool?, @@ -74,3 +74,45 @@ class TokenInformationAdapter extends TypeAdapter { runtimeType == other.runtimeType && typeId == other.typeId; } + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$TokenInformationImpl _$$TokenInformationImplFromJson( + Map json) => + _$TokenInformationImpl( + address: json['address'] as String?, + name: json['name'] as String?, + type: json['type'] as String?, + symbol: json['symbol'] as String?, + supply: (json['supply'] as num?)?.toDouble(), + id: json['id'] as String?, + tokenProperties: json['tokenProperties'] as Map?, + aeip: (json['aeip'] as List?) + ?.map((e) => (e as num).toInt()) + .toList(), + tokenCollection: (json['tokenCollection'] as List?) + ?.map((e) => e as Map) + .toList(), + decimals: (json['decimals'] as num?)?.toInt(), + isLPToken: json['isLPToken'] as bool?, + isVerified: json['isVerified'] as bool?, + ); + +Map _$$TokenInformationImplToJson( + _$TokenInformationImpl instance) => + { + 'address': instance.address, + 'name': instance.name, + 'type': instance.type, + 'symbol': instance.symbol, + 'supply': instance.supply, + 'id': instance.id, + 'tokenProperties': instance.tokenProperties, + 'aeip': instance.aeip, + 'tokenCollection': instance.tokenCollection, + 'decimals': instance.decimals, + 'isLPToken': instance.isLPToken, + 'isVerified': instance.isVerified, + }; diff --git a/lib/model/data/account.dart b/lib/model/data/account.dart index 1036a5780..dd610bc4a 100644 --- a/lib/model/data/account.dart +++ b/lib/model/data/account.dart @@ -1,5 +1,4 @@ import 'package:aewallet/infrastructure/datasources/appdb.hive.dart'; -import 'package:aewallet/model/blockchain/recent_transaction.dart'; import 'package:aewallet/model/data/account_balance.dart'; import 'package:aewallet/model/data/account_token.dart'; import 'package:aewallet/model/data/nft_infos_off_chain.dart'; @@ -58,7 +57,7 @@ class Account extends HiveObject with _$Account { @HiveField(5) AccountBalance? balance, /// Recent transactions - @HiveField(6) List? recentTransactions, + //@HiveField(6) List? recentTransactions, /// Tokens @HiveField(7) List? accountTokens, diff --git a/lib/model/data/account.freezed.dart b/lib/model/data/account.freezed.dart index c492aa3aa..b6731b1e5 100644 --- a/lib/model/data/account.freezed.dart +++ b/lib/model/data/account.freezed.dart @@ -43,10 +43,7 @@ mixin _$Account { AccountBalance? get balance => throw _privateConstructorUsedError; /// Recent transactions - @HiveField(6) - List? get recentTransactions => - throw _privateConstructorUsedError; - +//@HiveField(6) List? recentTransactions, /// Tokens @HiveField(7) List? get accountTokens => throw _privateConstructorUsedError; @@ -96,7 +93,6 @@ abstract class $AccountCopyWith<$Res> { 'Genesis address should be preferred instead of last address after AEIP21') String? lastAddress, @HiveField(5) AccountBalance? balance, - @HiveField(6) List? recentTransactions, @HiveField(7) List? accountTokens, @HiveField(8) List? accountNFT, @Deprecated('Thanks to hive, we should keep this unused property...') @@ -128,7 +124,6 @@ class _$AccountCopyWithImpl<$Res, $Val extends Account> Object? selected = freezed, Object? lastAddress = freezed, Object? balance = freezed, - Object? recentTransactions = freezed, Object? accountTokens = freezed, Object? accountNFT = freezed, Object? nftInfosOffChainList = freezed, @@ -161,10 +156,6 @@ class _$AccountCopyWithImpl<$Res, $Val extends Account> ? _value.balance : balance // ignore: cast_nullable_to_non_nullable as AccountBalance?, - recentTransactions: freezed == recentTransactions - ? _value.recentTransactions - : recentTransactions // ignore: cast_nullable_to_non_nullable - as List?, accountTokens: freezed == accountTokens ? _value.accountTokens : accountTokens // ignore: cast_nullable_to_non_nullable @@ -210,7 +201,6 @@ abstract class _$$AccountImplCopyWith<$Res> implements $AccountCopyWith<$Res> { 'Genesis address should be preferred instead of last address after AEIP21') String? lastAddress, @HiveField(5) AccountBalance? balance, - @HiveField(6) List? recentTransactions, @HiveField(7) List? accountTokens, @HiveField(8) List? accountNFT, @Deprecated('Thanks to hive, we should keep this unused property...') @@ -240,7 +230,6 @@ class __$$AccountImplCopyWithImpl<$Res> Object? selected = freezed, Object? lastAddress = freezed, Object? balance = freezed, - Object? recentTransactions = freezed, Object? accountTokens = freezed, Object? accountNFT = freezed, Object? nftInfosOffChainList = freezed, @@ -273,10 +262,6 @@ class __$$AccountImplCopyWithImpl<$Res> ? _value.balance : balance // ignore: cast_nullable_to_non_nullable as AccountBalance?, - recentTransactions: freezed == recentTransactions - ? _value._recentTransactions - : recentTransactions // ignore: cast_nullable_to_non_nullable - as List?, accountTokens: freezed == accountTokens ? _value._accountTokens : accountTokens // ignore: cast_nullable_to_non_nullable @@ -319,7 +304,6 @@ class _$AccountImpl extends _Account { 'Genesis address should be preferred instead of last address after AEIP21') this.lastAddress, @HiveField(5) this.balance, - @HiveField(6) final List? recentTransactions, @HiveField(7) final List? accountTokens, @HiveField(8) final List? accountNFT, @Deprecated('Thanks to hive, we should keep this unused property...') @@ -328,8 +312,7 @@ class _$AccountImpl extends _Account { @HiveField(13) this.serviceType, @HiveField(14) final List? accountNFTCollections, @HiveField(15) final List? customTokenAddressList}) - : _recentTransactions = recentTransactions, - _accountTokens = accountTokens, + : _accountTokens = accountTokens, _accountNFT = accountNFT, _nftInfosOffChainList = nftInfosOffChainList, _accountNFTCollections = accountNFTCollections, @@ -369,23 +352,12 @@ class _$AccountImpl extends _Account { final AccountBalance? balance; /// Recent transactions - final List? _recentTransactions; - - /// Recent transactions - @override - @HiveField(6) - List? get recentTransactions { - final value = _recentTransactions; - if (value == null) return null; - if (_recentTransactions is EqualUnmodifiableListView) - return _recentTransactions; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(value); - } - +//@HiveField(6) List? recentTransactions, /// Tokens final List? _accountTokens; + /// Recent transactions +//@HiveField(6) List? recentTransactions, /// Tokens @override @HiveField(7) @@ -464,7 +436,7 @@ class _$AccountImpl extends _Account { @override String toString() { - return 'Account(name: $name, genesisAddress: $genesisAddress, lastLoadingTransactionInputs: $lastLoadingTransactionInputs, selected: $selected, lastAddress: $lastAddress, balance: $balance, recentTransactions: $recentTransactions, accountTokens: $accountTokens, accountNFT: $accountNFT, nftInfosOffChainList: $nftInfosOffChainList, serviceType: $serviceType, accountNFTCollections: $accountNFTCollections, customTokenAddressList: $customTokenAddressList)'; + return 'Account(name: $name, genesisAddress: $genesisAddress, lastLoadingTransactionInputs: $lastLoadingTransactionInputs, selected: $selected, lastAddress: $lastAddress, balance: $balance, accountTokens: $accountTokens, accountNFT: $accountNFT, nftInfosOffChainList: $nftInfosOffChainList, serviceType: $serviceType, accountNFTCollections: $accountNFTCollections, customTokenAddressList: $customTokenAddressList)'; } @override @@ -484,8 +456,6 @@ class _$AccountImpl extends _Account { (identical(other.lastAddress, lastAddress) || other.lastAddress == lastAddress) && (identical(other.balance, balance) || other.balance == balance) && - const DeepCollectionEquality() - .equals(other._recentTransactions, _recentTransactions) && const DeepCollectionEquality() .equals(other._accountTokens, _accountTokens) && const DeepCollectionEquality() @@ -509,7 +479,6 @@ class _$AccountImpl extends _Account { selected, lastAddress, balance, - const DeepCollectionEquality().hash(_recentTransactions), const DeepCollectionEquality().hash(_accountTokens), const DeepCollectionEquality().hash(_accountNFT), const DeepCollectionEquality().hash(_nftInfosOffChainList), @@ -537,7 +506,6 @@ abstract class _Account extends Account { 'Genesis address should be preferred instead of last address after AEIP21') final String? lastAddress, @HiveField(5) final AccountBalance? balance, - @HiveField(6) final List? recentTransactions, @HiveField(7) final List? accountTokens, @HiveField(8) final List? accountNFT, @Deprecated('Thanks to hive, we should keep this unused property...') @@ -582,10 +550,7 @@ abstract class _Account extends Account { AccountBalance? get balance; /// Recent transactions - @override - @HiveField(6) - List? get recentTransactions; - +//@HiveField(6) List? recentTransactions, /// Tokens @override @HiveField(7) diff --git a/lib/model/data/account.g.dart b/lib/model/data/account.g.dart index 25fc00b26..c4081fa71 100644 --- a/lib/model/data/account.g.dart +++ b/lib/model/data/account.g.dart @@ -23,7 +23,6 @@ class AccountImplAdapter extends TypeAdapter<_$AccountImpl> { selected: fields[3] as bool?, lastAddress: fields[4] as String?, balance: fields[5] as AccountBalance?, - recentTransactions: (fields[6] as List?)?.cast(), accountTokens: (fields[7] as List?)?.cast(), accountNFT: (fields[8] as List?)?.cast(), nftInfosOffChainList: (fields[10] as List?)?.cast(), @@ -36,7 +35,7 @@ class AccountImplAdapter extends TypeAdapter<_$AccountImpl> { @override void write(BinaryWriter writer, _$AccountImpl obj) { writer - ..writeByte(13) + ..writeByte(12) ..writeByte(0) ..write(obj.name) ..writeByte(1) @@ -51,8 +50,6 @@ class AccountImplAdapter extends TypeAdapter<_$AccountImpl> { ..write(obj.balance) ..writeByte(13) ..write(obj.serviceType) - ..writeByte(6) - ..write(obj.recentTransactions) ..writeByte(7) ..write(obj.accountTokens) ..writeByte(8) diff --git a/lib/model/data/account_token.dart b/lib/model/data/account_token.dart index 53c9a1cb0..5dad8c384 100644 --- a/lib/model/data/account_token.dart +++ b/lib/model/data/account_token.dart @@ -2,7 +2,6 @@ import 'package:aewallet/infrastructure/datasources/appdb.hive.dart'; import 'package:aewallet/model/blockchain/token_information.dart'; -// Project imports: import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hive/hive.dart'; @@ -15,8 +14,11 @@ class AccountTokenConverter @override AccountToken fromJson(Map json) { return AccountToken( - tokenInformation: - const TokenInformationConverter().fromJson(json['tokenInformation']), + tokenInformation: json['tokenInformation'] != null + ? TokenInformation.fromJson( + json['tokenInformation'] as Map, + ) + : null, amount: json['amount'] as double, ); } @@ -24,8 +26,7 @@ class AccountTokenConverter @override Map toJson(AccountToken accountToken) { return { - 'tokenInformation': const TokenInformationConverter() - .toJson(accountToken.tokenInformation!), + 'tokenInformation': accountToken.tokenInformation?.toJson(), 'amount': accountToken.amount, }; } @@ -33,7 +34,6 @@ class AccountTokenConverter /// Next field available : 9 @HiveType(typeId: HiveTypeIds.accountToken) -@TokenInformationConverter() class AccountToken extends HiveObject { AccountToken({ this.tokenInformation, diff --git a/lib/model/data/hive_app_wallet_dto.dart b/lib/model/data/hive_app_wallet_dto.dart index b70fbe24d..3bb221072 100644 --- a/lib/model/data/hive_app_wallet_dto.dart +++ b/lib/model/data/hive_app_wallet_dto.dart @@ -58,7 +58,6 @@ class HiveAppWalletDTO extends HiveObject { ), selected: true, serviceType: 'archethicWallet', - recentTransactions: [], ); return AccountHiveDatasource.instance().addAccount(selectedAcct); } diff --git a/lib/model/transaction_infos.dart b/lib/model/transaction_infos.dart deleted file mode 100644 index c5cc7fcd5..000000000 --- a/lib/model/transaction_infos.dart +++ /dev/null @@ -1,65 +0,0 @@ -/// SPDX-License-Identifier: AGPL-3.0-or-later - -import 'package:flutter/material.dart'; -// Project imports: -import 'package:flutter_gen/gen_l10n/localizations.dart'; - -class TransactionInfos { - TransactionInfos({ - required this.domain, - required this.titleInfo, - required this.valueInfo, - }); - - String domain = ''; - String titleInfo = ''; - String valueInfo = ''; - - static String getDisplayName(BuildContext context, String label) { - final localizations = AppLocalizations.of(context)!; - switch (label) { - case 'Address': - return localizations.transactionInfosKeyAddress; - case 'Amount': - return localizations.transactionInfosKeyAmount; - case 'Code': - return localizations.transactionInfosKeyCode; - case 'Content': - return localizations.transactionInfosKeyContent; - case 'CrossValidationStamps': - return localizations.transactionInfosKeyCrossValidationStamps; - case 'Data': - return localizations.transactionInfosKeyData; - case 'TokenLedger': - return localizations.transactionInfosKeyTokenLedger; - case 'Token': - return localizations.transactionInfosKeyToken; - case 'OriginSignature': - return localizations.transactionInfosKeyOriginSignature; - case 'PreviousPublicKey': - return localizations.transactionInfosKeyPreviousPublicKey; - case 'PreviousSignature': - return localizations.transactionInfosKeyPreviousSignature; - case 'ProofOfIntegrity': - return localizations.transactionInfosKeyProofOfIntegrity; - case 'ProofOfWork': - return localizations.transactionInfosKeyProofOfWork; - case 'Signature': - return localizations.transactionInfosKeySignature; - case 'TimeStamp': - return localizations.transactionInfosKeyTimeStamp; - case 'To': - return localizations.transactionInfosKeyTo; - case 'Type': - return localizations.transactionInfosKeyType; - case 'UCOLedger': - return localizations.transactionInfosKeyUCOLedger; - case 'ValidationStamp': - return localizations.transactionInfosKeyValidationStamp; - case 'Version': - return localizations.transactionInfosKeyVersion; - default: - return ''; - } - } -} diff --git a/lib/modules/aeswap/application/session/state.dart b/lib/modules/aeswap/application/session/state.dart index c59f1e025..af588dc65 100644 --- a/lib/modules/aeswap/application/session/state.dart +++ b/lib/modules/aeswap/application/session/state.dart @@ -26,4 +26,11 @@ extension EnvironmentAddressesExt on Environment { Environment.devnet => '00007338a899446b8d211bb82b653dfd134cc351dd4060bb926d7d9c7028cf0273bf', }; + + String get nodeRewardsChain => switch (this) { + Environment.mainnet => + '000088CCDFB1DAC2B12C0BBD41A7AC0308693AF9DA0DE045FD2A11E3CE0BA9A49CA2', + Environment.testnet => 'none', + Environment.devnet => 'none', + }; } diff --git a/lib/router/router.authenticated.dart b/lib/router/router.authenticated.dart index 29ab60aa6..30aeab373 100644 --- a/lib/router/router.authenticated.dart +++ b/lib/router/router.authenticated.dart @@ -53,15 +53,6 @@ final _authenticatedRoutes = [ ); }, ), - GoRoute( - path: TransactionInfosSheet.routerPage, - pageBuilder: (context, state) => NoTransitionPage( - key: state.pageKey, - child: TransactionInfosSheet( - state.extra! as String, - ), - ), - ), GoRoute( path: TransferSheet.routerPage, pageBuilder: (context, state) => NoTransitionPage( diff --git a/lib/router/router.dart b/lib/router/router.dart index f25f50552..6073f6695 100644 --- a/lib/router/router.dart +++ b/lib/router/router.dart @@ -49,7 +49,6 @@ import 'package:aewallet/ui/views/sheets/buy_sheet.dart'; import 'package:aewallet/ui/views/sheets/connectivity_warning.dart'; import 'package:aewallet/ui/views/tokens_detail/layouts/token_detail_sheet.dart'; import 'package:aewallet/ui/views/tokens_fungibles/layouts/add_token_sheet.dart'; -import 'package:aewallet/ui/views/transactions/transaction_infos_sheet.dart'; import 'package:aewallet/ui/views/transfer/bloc/state.dart'; import 'package:aewallet/ui/views/transfer/layouts/transfer_sheet.dart'; import 'package:aewallet/ui/widgets/components/dialog.dart'; diff --git a/lib/service/app_service.dart b/lib/service/app_service.dart index 32c47f889..04ccc8b1d 100644 --- a/lib/service/app_service.dart +++ b/lib/service/app_service.dart @@ -3,27 +3,19 @@ // ignore_for_file: avoid_redundant_argument_values import 'dart:async'; -import 'dart:convert'; import 'dart:math'; import 'dart:typed_data'; import 'package:aewallet/infrastructure/datasources/tokens_list.hive.dart'; import 'package:aewallet/infrastructure/datasources/wallet_token_dto.hive.dart'; -import 'package:aewallet/model/blockchain/keychain_secured_infos.dart'; -import 'package:aewallet/model/blockchain/recent_transaction.dart'; import 'package:aewallet/model/blockchain/token_information.dart'; import 'package:aewallet/model/data/account_token.dart'; import 'package:aewallet/model/keychain_service_keypair.dart'; -import 'package:aewallet/model/transaction_infos.dart'; import 'package:aewallet/modules/aeswap/domain/models/dex_token.dart'; import 'package:aewallet/modules/aeswap/domain/models/util/get_pool_list_response.dart'; -import 'package:aewallet/util/keychain_util.dart'; -import 'package:aewallet/util/number_util.dart'; import 'package:aewallet/util/task.dart'; import 'package:archethic_lib_dart/archethic_lib_dart.dart'; import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; import 'package:logging/logging.dart'; const blockchainTxVersion = 3; @@ -37,17 +29,6 @@ class AppService { final ApiService apiService; - Future>> getTransactionChain( - Map addresses, - String? request, - ) async { - final transactionChainMap = await apiService.getTransactionChain( - addresses, - request: request!, - ); - return transactionChainMap; - } - // TODO(reddwarf03): doublons with TokenRepositoryImpl Future> getToken( List addresses, @@ -123,468 +104,6 @@ class AppService { return transactionInputs; } - List _removeRecentTransactionsDuplicates( - List recentTransactions, - ) => - recentTransactions.fold>( - [], - (keptRecentTransactions, element) { - final matchingIndex = keptRecentTransactions.indexWhere( - (keptRecentTransaction) => - keptRecentTransaction.typeTx == element.typeTx && - keptRecentTransaction.from == element.from && - keptRecentTransaction.type == element.type && - keptRecentTransaction.tokenAddress == element.tokenAddress && - keptRecentTransaction.indexInLedger == element.indexInLedger, - ); - - if (matchingIndex == -1) { - return [ - ...keptRecentTransactions, - element, - ]; - } - - final matchingElement = keptRecentTransactions[matchingIndex]; - if (matchingElement.timestamp! > element.timestamp!) { - return keptRecentTransactions; - } - - return [ - for (var i = 0; i < keptRecentTransactions.length; i++) - i == matchingIndex ? element : keptRecentTransactions[i], - ]; - }, - ); - - List _populateRecentTransactionsFromTransactionInputs( - List transactionInputs, - String notificationRecipientAddress, - int mostRecentTimestamp, - int transactionTimestamp, - ) { - final recentTransactions = []; - for (final transactionInput in transactionInputs) { - if (transactionInput.from!.toUpperCase() != - notificationRecipientAddress.toUpperCase() && - transactionInput.timestamp! >= mostRecentTimestamp && - transactionInput.timestamp! >= transactionTimestamp) { - final recentTransaction = RecentTransaction() - ..address = transactionInput.from - ..amount = fromBigInt(transactionInput.amount).toDouble() - ..typeTx = RecentTransaction.transferInput - ..from = transactionInput.from - ..recipient = notificationRecipientAddress - ..timestamp = transactionInput.timestamp - ..fee = 0 - ..tokenAddress = transactionInput.tokenAddress; - recentTransactions.add(recentTransaction); - } - } - return recentTransactions; - } - - List _populateRecentTransactionsFromTransactionChain( - List transactionChain, - ) { - final recentTransactions = []; - for (final transaction in transactionChain) { - _logger.info('type ${transaction.type!} ${transaction.toJson()}'); - if (transaction.type! == 'token') { - final recentTransaction = RecentTransaction() - ..address = transaction.address!.address - ..timestamp = transaction.validationStamp!.timestamp - ..typeTx = RecentTransaction.tokenCreation - ..fee = fromBigInt(transaction.validationStamp!.ledgerOperations!.fee) - .toDouble() - ..tokenAddress = transaction.address!.address; - recentTransactions.add(recentTransaction); - } - - if (transaction.type! == 'hosting') { - final recentTransaction = RecentTransaction() - ..address = transaction.address!.address - ..timestamp = transaction.validationStamp!.timestamp - ..typeTx = RecentTransaction.hosting - ..fee = fromBigInt(transaction.validationStamp!.ledgerOperations!.fee) - .toDouble() - ..tokenAddress = transaction.address!.address; - recentTransactions.add(recentTransaction); - } - - if (transaction.type! == 'transfer') { - var nbTrf = 0; - var indexInLedger = 0; - for (final transfer in transaction.data!.ledger!.uco!.transfers) { - final recentTransaction = RecentTransaction() - ..address = transaction.address!.address - ..typeTx = RecentTransaction.transferOutput - ..amount = fromBigInt( - transfer.amount, - ).toDouble() - ..recipient = transfer.to - ..fee = - fromBigInt(transaction.validationStamp!.ledgerOperations!.fee) - .toDouble() - ..timestamp = transaction.validationStamp!.timestamp - ..from = transaction.address!.address - ..ownerships = transaction.data!.ownerships - ..indexInLedger = indexInLedger; - recentTransactions.add(recentTransaction); - indexInLedger++; - nbTrf++; - } - indexInLedger = 0; - for (final transfer in transaction.data!.ledger!.token!.transfers) { - final recentTransaction = RecentTransaction() - ..address = transaction.address!.address - ..typeTx = RecentTransaction.transferOutput - ..amount = fromBigInt( - transfer.amount, - ).toDouble() - ..recipient = transfer.to - ..fee = - fromBigInt(transaction.validationStamp!.ledgerOperations!.fee) - .toDouble() - ..timestamp = transaction.validationStamp!.timestamp - ..from = transaction.address!.address - ..ownerships = transaction.data!.ownerships - ..tokenAddress = transfer.tokenAddress - ..indexInLedger = indexInLedger; - recentTransactions.add(recentTransaction); - indexInLedger++; - nbTrf++; - } - if (nbTrf == 0) { - for (final contractRecipient in transaction.data!.recipients) { - final recentTransaction = RecentTransaction() - ..address = transaction.address!.address - ..typeTx = RecentTransaction.transferOutput - ..fee = - fromBigInt(transaction.validationStamp!.ledgerOperations!.fee) - .toDouble() - ..timestamp = transaction.validationStamp!.timestamp - ..from = contractRecipient.address - ..ownerships = transaction.data!.ownerships - ..indexInLedger = 0; - recentTransactions.add(recentTransaction); - } - } - } - } - return recentTransactions; - } - - Future> _buildRecentTransactionFromTransaction( - List recentTransactionList, - String address, - int mostRecentTimestamp, - ) async { - final _logger = Logger('AppService - recentTx'); - - var newRecentTransactionList = recentTransactionList; - _logger.info('>> START getTransaction : ${DateTime.now()}'); - final transaction = await apiService.getTransaction( - [address], - request: - 'address, type, chainLength, validationStamp { timestamp, ledgerOperations { fee } }, data { actionRecipients { action, address, args } ledger { uco { transfers { amount, to } } token {transfers {amount, to, tokenAddress, tokenId } } } }', - ); - _logger - ..info('$transaction') - ..info('>> END getTransaction : ${DateTime.now()}') - ..info('>> START getTransactionInputs : ${DateTime.now()}'); - final transactionInputs = await apiService.getTransactionInputs( - [address], - request: 'from, spent, tokenAddress, tokenId, amount, timestamp', - limit: 10, - ); - _logger.info('>> END getTransactionInputs : ${DateTime.now()}'); - if (transaction[address] != null) { - final transactionTimeStamp = - transaction[address]!.validationStamp!.timestamp!; - - if (transactionInputs[address] != null) { - newRecentTransactionList - ..addAll( - _populateRecentTransactionsFromTransactionInputs( - transactionInputs[address]!, - address, - mostRecentTimestamp, - transaction[address]!.validationStamp!.timestamp!, - ), - ) - ..sort((tx1, tx2) => tx1.timestamp!.compareTo(tx2.timestamp!)); - } - - _logger - ..info('1) $transactionInputs') - ..info( - 'transactionTimeStamp $transactionTimeStamp > mostRecentTimestamp $mostRecentTimestamp)', - ); - if (transactionTimeStamp > mostRecentTimestamp) { - newRecentTransactionList - ..addAll( - _populateRecentTransactionsFromTransactionChain( - [transaction[address]!], - ), - ) - ..sort((tx1, tx2) => tx1.timestamp!.compareTo(tx2.timestamp!)); - } - - // Remove doublons (on type / token address / from / timestamp) - if (newRecentTransactionList.isNotEmpty) { - newRecentTransactionList = - _removeRecentTransactionsDuplicates(newRecentTransactionList); - } - } - - return newRecentTransactionList; - } - - Future> getAccountRecentTransactions( - String genesisAddress, - String name, - KeychainSecuredInfos keychainSecuredInfos, - List localRecentTransactionList, - ) async { - _logger.info( - '>> START getRecentTransactions : ${DateTime.now()}', - ); - - final _localRecentTransactionList = - List.from(localRecentTransactionList); - - // get the most recent movement in cache - var mostRecentTimestamp = 0; - if (_localRecentTransactionList.isNotEmpty) { - _localRecentTransactionList.sort( - (a, b) => b.timestamp!.compareTo(a.timestamp!), - ); - mostRecentTimestamp = _localRecentTransactionList.first.timestamp ?? 0; - } - var recentTransactions = []; - - final keychain = keychainSecuredInfos.toKeychain(); - - final lastIndex = await apiService.getTransactionIndex( - [genesisAddress], - ); - _logger.info('genesisAddress : $genesisAddress -> lastIndex: $lastIndex'); - var index = lastIndex[genesisAddress] ?? 0; - String addressToSearch; - var iterMax = 10; - recentTransactions.addAll(_localRecentTransactionList); - - while (index > 0 && iterMax > 0) { - addressToSearch = uint8ListToHex( - keychain.deriveAddress( - name, - index: index, - ), - ); - _logger.info('addressToSearch : $addressToSearch'); - if (_localRecentTransactionList.any( - (element) => - element.address!.toUpperCase() == addressToSearch.toUpperCase(), - )) { - _logger.info('addressToSearch exists in local -> break'); - break; - } - - recentTransactions = await _buildRecentTransactionFromTransaction( - recentTransactions, - addressToSearch, - mostRecentTimestamp, - ); - index--; - iterMax--; - } - - if (recentTransactions.length < 10) { - // Get transaction inputs from genesis address if filtered list is < 10 - final genesisTransactionInputsMap = await getTransactionInputs( - [genesisAddress], - 'from, type, spent, tokenAddress, amount, timestamp', - limit: 10 - recentTransactions.length, - ); - - if (genesisTransactionInputsMap[genesisAddress] != null) { - recentTransactions.addAll( - _populateRecentTransactionsFromTransactionInputs( - genesisTransactionInputsMap[genesisAddress]!, - genesisAddress, - mostRecentTimestamp, - 0, - ), - ); - } - } - - // Remove doublons (on type / token address / from / timestamp) - if (recentTransactions.isNotEmpty) { - recentTransactions = - _removeRecentTransactionsDuplicates(recentTransactions); - } - - // Sort by timestamp desc and index ledger desc - recentTransactions.sort((a, b) { - final compareTimestamp = b.timestamp!.compareTo(a.timestamp!); - if (compareTimestamp != 0) { - return compareTimestamp; - } else { - return b.indexInLedger.compareTo(a.indexInLedger); - } - }); - - // Get 10 first transactions - recentTransactions = recentTransactions.sublist( - 0, - recentTransactions.length > 10 ? 10 : recentTransactions.length, - ); - - // Get token id - final tokensAddresses = []; - for (final recentTransaction in recentTransactions) { - if (recentTransaction.tokenAddress != null && - recentTransaction.tokenAddress!.isNotEmpty && - recentTransaction.tokenInformation == null && - recentTransaction.timestamp! >= mostRecentTimestamp) { - tokensAddresses.add(recentTransaction.tokenAddress!); - } - } - - final recentTransactionLastAddresses = []; - final ownershipsAddresses = []; - - // Search token Information - final tokensAddressMap = await getToken( - tokensAddresses.toSet().toList(), - ); - - for (final recentTransaction in recentTransactions) { - // Get token Information - if (recentTransaction.tokenAddress != null && - recentTransaction.tokenAddress!.isNotEmpty && - recentTransaction.tokenInformation == null && - recentTransaction.timestamp! >= mostRecentTimestamp) { - final token = tokensAddressMap[recentTransaction.tokenAddress]; - if (token != null) { - recentTransaction - ..tokenAddress = token.address - ..tokenInformation = TokenInformation( - address: token.address, - name: token.name, - supply: fromBigInt(token.supply).toDouble(), - symbol: token.symbol, - type: token.type, - ); - } - } - - // Decrypt secrets - switch (recentTransaction.typeTx) { - case RecentTransaction.transferInput: - if (recentTransaction.from != null) { - if (recentTransaction.timestamp! > mostRecentTimestamp) { - ownershipsAddresses.add(recentTransaction.from!); - } - recentTransactionLastAddresses.add(recentTransaction.from!); - } - break; - case RecentTransaction.transferOutput: - if (recentTransaction.from != null) { - if (recentTransaction.timestamp! > mostRecentTimestamp) { - ownershipsAddresses.add(recentTransaction.from!); - } - recentTransactionLastAddresses.add(recentTransaction.from!); - } - break; - } - } - - // Get List of ownerships - final ownershipsMap = >{}; - - final getTransactionOwnerships = await ownershipsAddresses - .toSet() - .map( - (ownershipsAddress) => Task( - name: - 'GetAccountRecentTransactions - ownershipsAddress: $ownershipsAddress', - logger: _logger, - action: () => apiService.getTransactionOwnerships( - [ownershipsAddress], - ), - ), - ) - .autoRetry() - .batch(); - - for (final getTransactionOwnership in getTransactionOwnerships) { - ownershipsMap.addAll(getTransactionOwnership); - } - - final keychainServiceKeyPair = keychainSecuredInfos.services[name]!.keyPair; - for (var recentTransaction in recentTransactions) { - switch (recentTransaction.typeTx) { - case RecentTransaction.transferInput: - if (recentTransaction.from != null && - recentTransaction.timestamp! > mostRecentTimestamp) { - recentTransaction = _decryptedSecret( - keypair: keychainServiceKeyPair!, - ownerships: ownershipsMap[recentTransaction.from!] ?? [], - recentTransaction: recentTransaction, - ); - } - break; - case RecentTransaction.transferOutput: - if (recentTransaction.address != null && - recentTransaction.timestamp! > mostRecentTimestamp) { - recentTransaction = _decryptedSecret( - keypair: keychainServiceKeyPair!, - ownerships: ownershipsMap[recentTransaction.address!] ?? [], - recentTransaction: recentTransaction, - ); - } - break; - } - } - - _logger.info( - '>> END getRecentTransactions : ${DateTime.now()}', - ); - - return recentTransactions; - } - - RecentTransaction _decryptedSecret({ - required KeychainServiceKeyPair keypair, - required List ownerships, - required RecentTransaction recentTransaction, - }) { - if (ownerships.isEmpty) { - return recentTransaction; - } - recentTransaction.decryptedSecret = []; - for (final ownership in ownerships) { - final authorizedPublicKey = ownership.authorizedPublicKeys.firstWhere( - (AuthorizedKey authKey) => - authKey.publicKey!.toUpperCase() == - uint8ListToHex(Uint8List.fromList(keypair.publicKey)).toUpperCase(), - orElse: AuthorizedKey.new, - ); - if (authorizedPublicKey.encryptedSecretKey != null) { - final aesKey = ecDecrypt( - authorizedPublicKey.encryptedSecretKey, - Uint8List.fromList(keypair.privateKey), - ); - final decryptedSecret = aesDecrypt(ownership.secret, aesKey); - recentTransaction.decryptedSecret!.add(utf8.decode(decryptedSecret)); - } - } - return recentTransaction; - } - // TODO(reddwarf03): USE PROVIDER Future> getFungiblesTokensList( String address, @@ -711,202 +230,6 @@ class AppService { return balancesToReturn; } - Future> getTransaction( - List addresses, { - String request = Transaction.kTransactionQueryAllFields, - }) async { - final transactionMap = await apiService.getTransaction( - addresses.toSet().toList(), - request: request, - ); - return transactionMap; - } - - Future> getTransactionAllInfos( - String address, - DateFormat dateFormat, - String cryptoCurrency, - BuildContext context, - KeychainServiceKeyPair keychainServiceKeyPair, - ) async { - final transactionsInfos = List.empty(growable: true); - - final transactionMap = await apiService.getTransaction( - [address], - request: - ' address, data { content, ownerships { authorizedPublicKeys { encryptedSecretKey, publicKey } secret } ledger { uco { transfers { amount, to } }, token { transfers { amount, to, tokenAddress, tokenId } } } actionRecipients { action, address, args } }, type ', - ); - final transaction = transactionMap[address]; - if (transaction == null) { - return []; - } - if (transaction.address != null) { - transactionsInfos.add( - TransactionInfos( - domain: '', - titleInfo: 'Address', - valueInfo: transaction.address!.address!, - ), - ); - } - if (transaction.type != null) { - transactionsInfos.add( - TransactionInfos( - domain: '', - titleInfo: 'Type', - valueInfo: transaction.type!, - ), - ); - } - if (transaction.data != null) { - transactionsInfos - .add(TransactionInfos(domain: 'Data', titleInfo: '', valueInfo: '')); - - if (transaction.data!.content != null) { - transactionsInfos.add( - TransactionInfos( - domain: 'Data', - titleInfo: 'Content', - valueInfo: - transaction.type == 'token' || transaction.type == 'hosting' - ? 'See explorer...' - : transaction.data!.content == '' - ? 'N/A' - : transaction.data!.content!, - ), - ); - } - - if (transaction.data!.code != null) { - transactionsInfos.add( - TransactionInfos( - domain: 'Data', - titleInfo: 'Code', - valueInfo: transaction.data!.code!, - ), - ); - } - if (transaction.data!.ownerships.isNotEmpty) { - for (final ownership in transaction.data!.ownerships) { - final authorizedPublicKey = ownership.authorizedPublicKeys.firstWhere( - (AuthorizedKey authKey) => - authKey.publicKey!.toUpperCase() == - uint8ListToHex( - Uint8List.fromList(keychainServiceKeyPair.publicKey), - ).toUpperCase(), - orElse: AuthorizedKey.new, - ); - if (authorizedPublicKey.encryptedSecretKey != null) { - final aesKey = ecDecrypt( - authorizedPublicKey.encryptedSecretKey, - Uint8List.fromList(keychainServiceKeyPair.privateKey), - ); - final decryptedSecret = aesDecrypt(ownership.secret, aesKey); - transactionsInfos.add( - TransactionInfos( - domain: 'Data', - titleInfo: 'Secret', - valueInfo: utf8.decode(decryptedSecret), - ), - ); - } - } - } - if (transaction.data!.ledger != null && - transaction.data!.ledger!.uco != null && - transaction.data!.ledger!.uco!.transfers.isNotEmpty) { - transactionsInfos.add( - TransactionInfos( - domain: 'UCOLedger', - titleInfo: '', - valueInfo: '', - ), - ); - for (var i = 0; - i < transaction.data!.ledger!.uco!.transfers.length; - i++) { - if (transaction.data!.ledger!.uco!.transfers[i].to != null) { - transactionsInfos.add( - TransactionInfos( - domain: 'UCOLedger', - titleInfo: 'To', - valueInfo: transaction.data!.ledger!.uco!.transfers[i].to!, - ), - ); - } - if (transaction.data!.ledger!.uco!.transfers[i].amount != null) { - transactionsInfos.add( - TransactionInfos( - domain: 'UCOLedger', - titleInfo: 'Amount', - valueInfo: - '${NumberUtil.formatThousands(fromBigInt(transaction.data!.ledger!.uco!.transfers[i].amount))} $cryptoCurrency', - ), - ); - } - } - } - if (transaction.data!.ledger != null && - transaction.data!.ledger!.token != null && - transaction.data!.ledger!.token!.transfers.isNotEmpty) { - transactionsInfos.add( - TransactionInfos( - domain: 'TokenLedger', - titleInfo: '', - valueInfo: '', - ), - ); - for (var i = 0; - i < transaction.data!.ledger!.token!.transfers.length; - i++) { - if (transaction.data!.ledger!.token!.transfers[i].tokenAddress != - null) { - transactionsInfos.add( - TransactionInfos( - domain: 'TokenLedger', - titleInfo: 'Token', - valueInfo: - transaction.data!.ledger!.token!.transfers[i].tokenAddress!, - ), - ); - } - if (transaction.data!.ledger!.token!.transfers[i].to != null) { - transactionsInfos.add( - TransactionInfos( - domain: 'TokenLedger', - titleInfo: 'To', - valueInfo: transaction.data!.ledger!.token!.transfers[i].to!, - ), - ); - } - if (transaction.data!.ledger!.token!.transfers[i].amount != null) { - final tokenMap = await getToken( - [transaction.data!.ledger!.token!.transfers[i].tokenAddress!], - ); - var tokenSymbol = ''; - if (tokenMap[transaction - .data!.ledger!.token!.transfers[i].tokenAddress!] != - null) { - tokenSymbol = tokenMap[transaction - .data!.ledger!.token!.transfers[i].tokenAddress!]! - .symbol ?? - ''; - } - transactionsInfos.add( - TransactionInfos( - domain: 'TokenLedger', - titleInfo: 'Amount', - valueInfo: - '${NumberUtil.formatThousands(fromBigInt(transaction.data!.ledger!.token!.transfers[i].amount))} $tokenSymbol', - ), - ); - } - } - } - } - return transactionsInfos; - } - Future getFeesEstimation( String originPrivateKey, String seed, diff --git a/lib/ui/menu/settings/security_menu_view.dart b/lib/ui/menu/settings/security_menu_view.dart index 196316b88..76e6a3294 100644 --- a/lib/ui/menu/settings/security_menu_view.dart +++ b/lib/ui/menu/settings/security_menu_view.dart @@ -293,9 +293,6 @@ class _SyncBlockchainSettingsListItem extends ConsumerWidget { localizations.resyncWalletAreYouSure, localizations.yes, () async { - await ref - .read(accountsNotifierProvider.notifier) - .clearRecentTransactionsFromCache(); final cache = await Hive.openBox( CacheManagerHive.cacheManagerHiveTable, ); diff --git a/lib/ui/views/add_custom_token/bloc/provider.dart b/lib/ui/views/add_custom_token/bloc/provider.dart index 2b4357097..fb943f460 100644 --- a/lib/ui/views/add_custom_token/bloc/provider.dart +++ b/lib/ui/views/add_custom_token/bloc/provider.dart @@ -127,11 +127,15 @@ class AddCustomTokenFormNotifier extends _$AddCustomTokenFormNotifier if (await control(ref, appLocalizations) == false) { return false; } - + final accountSelected = ref.read( + accountsNotifierProvider.select( + (accounts) => accounts.valueOrNull?.selectedAccount, + ), + ); await (await ref .read(accountsNotifierProvider.notifier) .selectedAccountNotifier) - ?.addCustomTokenAddress(state.tokenAddress); + ?.addCustomTokenAddress(accountSelected!, state.tokenAddress); return true; } diff --git a/lib/ui/views/add_custom_token/bloc/provider.g.dart b/lib/ui/views/add_custom_token/bloc/provider.g.dart index f5f9d100a..acab02908 100644 --- a/lib/ui/views/add_custom_token/bloc/provider.g.dart +++ b/lib/ui/views/add_custom_token/bloc/provider.g.dart @@ -7,7 +7,7 @@ part of 'provider.dart'; // ************************************************************************** String _$addCustomTokenFormNotifierHash() => - r'da6d902ba2ae059d9503a9948fbca949435ba741'; + r'93f8ea59199a4d212d88e71d087f0b3e14c86a5c'; /// See also [AddCustomTokenFormNotifier]. @ProviderFor(AddCustomTokenFormNotifier) diff --git a/lib/ui/views/add_custom_token/layouts/add_custom_token_sheet.dart b/lib/ui/views/add_custom_token/layouts/add_custom_token_sheet.dart index 0627ff0e9..382098bd0 100644 --- a/lib/ui/views/add_custom_token/layouts/add_custom_token_sheet.dart +++ b/lib/ui/views/add_custom_token/layouts/add_custom_token_sheet.dart @@ -130,11 +130,17 @@ class AddCustomTokenSheet extends ConsumerWidget { if (!result) return; + final accountSelected = ref.read( + accountsNotifierProvider.select( + (accounts) => accounts.valueOrNull?.selectedAccount, + ), + ); + unawaited( (await ref .read(accountsNotifierProvider.notifier) .selectedAccountNotifier) - ?.updateBalance(), + ?.updateBalance(accountSelected!), ); unawaited( (await ref diff --git a/lib/ui/views/main/components/menu_widget_wallet.dart b/lib/ui/views/main/components/menu_widget_wallet.dart index b4167d871..8a15ffae7 100644 --- a/lib/ui/views/main/components/menu_widget_wallet.dart +++ b/lib/ui/views/main/components/menu_widget_wallet.dart @@ -20,9 +20,7 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import 'package:url_launcher/url_launcher.dart'; class MenuWidgetWallet extends ConsumerWidget { - const MenuWidgetWallet({super.key, this.refreshFunction}); - - final Function()? refreshFunction; + const MenuWidgetWallet({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -131,14 +129,10 @@ class MenuWidgetWallet extends ConsumerWidget { return; } - if (refreshFunction == null) { - await (await ref - .read(accountsNotifierProvider.notifier) - .selectedAccountNotifier) - ?.refreshAll(); - } else { - refreshFunction!(); - } + await (await ref + .read(accountsNotifierProvider.notifier) + .selectedAccountNotifier) + ?.refreshAll(); }, ) .animate() diff --git a/lib/ui/views/main/transactions_tab.dart b/lib/ui/views/main/transactions_tab.dart index 3450c445a..c024bf176 100644 --- a/lib/ui/views/main/transactions_tab.dart +++ b/lib/ui/views/main/transactions_tab.dart @@ -3,12 +3,8 @@ import 'dart:ui'; import 'package:aewallet/application/account/accounts_notifier.dart'; -import 'package:aewallet/application/account/selected_account.dart'; import 'package:aewallet/application/connectivity_status.dart'; -import 'package:aewallet/model/blockchain/recent_transaction.dart'; import 'package:aewallet/modules/aeswap/application/session/provider.dart'; -import 'package:aewallet/ui/themes/archethic_theme.dart'; -import 'package:aewallet/ui/themes/styles.dart'; import 'package:aewallet/ui/util/dimens.dart'; import 'package:aewallet/ui/views/main/components/menu_widget_wallet.dart'; import 'package:aewallet/ui/views/transactions/transactions_list.dart'; @@ -20,7 +16,6 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:material_symbols_icons/symbols.dart'; import 'package:url_launcher/url_launcher.dart'; class TransactionsTab extends ConsumerWidget { @@ -34,20 +29,9 @@ class TransactionsTab extends ConsumerWidget { ), ); - final recentTransactions = - ref.watch(selectedAccountRecentTransactionsProvider).valueOrNull; - return Stack( children: [ - SingleChildScrollView( - child: Column( - key: const Key('recentTransactions'), - children: [ - if (recentTransactions != null) - _TransactionsList(recentTransactions: recentTransactions), - ], - ), - ), + const _TransactionsList(), if (accountSelected != null) Align( alignment: Alignment.bottomCenter, @@ -80,11 +64,7 @@ class TransactionsTab extends ConsumerWidget { } class _TransactionsList extends ConsumerWidget { - const _TransactionsList({ - required this.recentTransactions, - }); - - final List recentTransactions; + const _TransactionsList(); @override Widget build(BuildContext context, WidgetRef ref) { @@ -99,7 +79,7 @@ class _TransactionsList extends ConsumerWidget { await (await ref .read(accountsNotifierProvider.notifier) .selectedAccountNotifier) - ?.refreshRecentTransactions(); + ?.refreshAll(); }), child: ScrollConfiguration( behavior: ScrollConfiguration.of(context).copyWith( @@ -120,63 +100,14 @@ class _TransactionsList extends ConsumerWidget { top: MediaQuery.of(context).padding.top + 10, bottom: 160, ), - child: Column( + child: const Column( children: [ - const BalanceInfos(), - const SizedBox( + BalanceInfos(), + SizedBox( height: 10, ), - MenuWidgetWallet( - refreshFunction: () async { - await (await ref - .read(accountsNotifierProvider.notifier) - .selectedAccountNotifier) - ?.refreshRecentTransactions(); - }, - ), - if (recentTransactions.isEmpty) - Padding( - padding: const EdgeInsets.only(top: 20), - child: Card( - shape: RoundedRectangleBorder( - side: BorderSide( - color: ArchethicTheme - .backgroundFungiblesTokensListCard, - ), - borderRadius: BorderRadius.circular(10), - ), - elevation: 0, - color: ArchethicTheme - .backgroundFungiblesTokensListCard, - child: Container( - padding: const EdgeInsets.all(9.5), - width: MediaQuery.of(context).size.width, - alignment: Alignment.center, - child: Row( - children: [ - const Icon( - Symbols.info, - size: 18, - weight: IconSize.weightM, - opticalSize: IconSize.opticalSizeM, - grade: IconSize.gradeM, - ), - const SizedBox(width: 8), - Text( - AppLocalizations.of(context)! - .recentTransactionsNoTransactionYet, - style: ArchethicThemeStyles - .textStyleSize12W100Primary, - ), - ], - ), - ), - ), - ) - else - TransactionsList( - transactionsList: recentTransactions, - ), + MenuWidgetWallet(), + TransactionsList(), ], ), ), diff --git a/lib/ui/views/nft_search/bloc/state.freezed.dart b/lib/ui/views/nft_search/bloc/state.freezed.dart index cac3b69b1..c01760897 100644 --- a/lib/ui/views/nft_search/bloc/state.freezed.dart +++ b/lib/ui/views/nft_search/bloc/state.freezed.dart @@ -39,6 +39,8 @@ abstract class $NftSearchBarFormStateCopyWith<$Res> { bool loading, String error, TokenInformation? tokenInformation}); + + $TokenInformationCopyWith<$Res>? get tokenInformation; } /// @nodoc @@ -81,6 +83,20 @@ class _$NftSearchBarFormStateCopyWithImpl<$Res, as TokenInformation?, ) as $Val); } + + /// Create a copy of NftSearchBarFormState + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $TokenInformationCopyWith<$Res>? get tokenInformation { + if (_value.tokenInformation == null) { + return null; + } + + return $TokenInformationCopyWith<$Res>(_value.tokenInformation!, (value) { + return _then(_value.copyWith(tokenInformation: value) as $Val); + }); + } } /// @nodoc @@ -97,6 +113,9 @@ abstract class _$$NftSearchBarFormStateImplCopyWith<$Res> bool loading, String error, TokenInformation? tokenInformation}); + + @override + $TokenInformationCopyWith<$Res>? get tokenInformation; } /// @nodoc diff --git a/lib/ui/views/tokens_detail/layouts/token_detail_sheet.dart b/lib/ui/views/tokens_detail/layouts/token_detail_sheet.dart index 7d86169f8..f58edd9d1 100644 --- a/lib/ui/views/tokens_detail/layouts/token_detail_sheet.dart +++ b/lib/ui/views/tokens_detail/layouts/token_detail_sheet.dart @@ -159,13 +159,16 @@ class TokenDetailSheet extends ConsumerWidget await (await ref .read(accountsNotifierProvider.notifier) .selectedAccountNotifier) - ?.removeCustomTokenAddress(aeToken.address!); + ?.removeCustomTokenAddress( + accountSelected, + aeToken.address!, + ); unawaited( (await ref .read(accountsNotifierProvider.notifier) .selectedAccountNotifier) - ?.updateBalance(), + ?.updateBalance(accountSelected), ); unawaited( (await ref diff --git a/lib/ui/views/transactions/components/template/transaction_action.dart b/lib/ui/views/transactions/components/template/transaction_action.dart new file mode 100644 index 000000000..9e649323a --- /dev/null +++ b/lib/ui/views/transactions/components/template/transaction_action.dart @@ -0,0 +1,23 @@ +import 'package:aewallet/ui/themes/styles.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class TransactionAction extends ConsumerWidget { + const TransactionAction({super.key, required this.action}); + + final String? action; + + @override + Widget build(BuildContext context, WidgetRef ref) { + if (action == null) return const SizedBox(); + + return Row( + children: [ + Text( + action!, + style: ArchethicThemeStyles.textStyleSize12W100Primary, + ), + ], + ); + } +} diff --git a/lib/ui/views/transactions/components/template/transaction_fees.dart b/lib/ui/views/transactions/components/template/transaction_fees.dart index 9316d0bf7..8bd4190a1 100644 --- a/lib/ui/views/transactions/components/template/transaction_fees.dart +++ b/lib/ui/views/transactions/components/template/transaction_fees.dart @@ -41,52 +41,39 @@ class TransactionFees extends ConsumerWidget { return Row( children: [ - if (transaction.indexInLedger > 0) + if (settings.showBalances == true) Row( children: [ Text( '${localizations.txListFees} ', style: ArchethicThemeStyles.textStyleSize12W100Primary60, ), - Text( - localizations.txListFeesIncluded, - style: ArchethicThemeStyles.textStyleSize12W100Primary, - ), + if (primaryCurrency.primaryCurrency == + AvailablePrimaryCurrencyEnum.native) + Text( + '${transaction.fee!.toStringAsFixed(3)} ${AccountBalance.cryptoCurrencyLabel} ($amountConverted)', + style: ArchethicThemeStyles.textStyleSize12W100Primary, + ) + else + Text( + '$amountConverted (${transaction.fee!.toStringAsFixed(3)} ${AccountBalance.cryptoCurrencyLabel})', + style: ArchethicThemeStyles.textStyleSize12W100Primary, + ), ], ) else - settings.showBalances == true - ? Row( - children: [ - Text( - '${localizations.txListFees} ', - style: ArchethicThemeStyles.textStyleSize12W100Primary60, - ), - if (primaryCurrency.primaryCurrency == - AvailablePrimaryCurrencyEnum.native) - Text( - '${transaction.fee!.toStringAsFixed(3)} ${AccountBalance.cryptoCurrencyLabel} ($amountConverted)', - style: ArchethicThemeStyles.textStyleSize12W100Primary, - ) - else - Text( - '$amountConverted (${transaction.fee!.toStringAsFixed(3)} ${AccountBalance.cryptoCurrencyLabel})', - style: ArchethicThemeStyles.textStyleSize12W100Primary, - ), - ], - ) - : Row( - children: [ - Text( - '${localizations.txListFees} ', - style: ArchethicThemeStyles.textStyleSize12W100Primary60, - ), - Text( - '···········', - style: ArchethicThemeStyles.textStyleSize12W100Primary60, - ), - ], - ), + Row( + children: [ + Text( + '${localizations.txListFees} ', + style: ArchethicThemeStyles.textStyleSize12W100Primary60, + ), + Text( + '···········', + style: ArchethicThemeStyles.textStyleSize12W100Primary60, + ), + ], + ), ], ); } diff --git a/lib/ui/views/transactions/components/template/transaction_information.dart b/lib/ui/views/transactions/components/template/transaction_information.dart index 3bbd9a599..13cf0f29f 100644 --- a/lib/ui/views/transactions/components/template/transaction_information.dart +++ b/lib/ui/views/transactions/components/template/transaction_information.dart @@ -1,43 +1,30 @@ import 'package:aewallet/ui/themes/styles.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class TransactionInformation extends ConsumerWidget { const TransactionInformation({ super.key, - required this.isEmpty, required this.message, this.prefixMessage = '', }); - final bool isEmpty; final String message; final String prefixMessage; @override Widget build(BuildContext context, WidgetRef ref) { return Row( - children: [ - if (isEmpty) + children: [ + if (prefixMessage.isNotEmpty) Text( - AppLocalizations.of(context)!.executionSC, - style: ArchethicThemeStyles.textStyleSize12W100Primary, - ) - else - Row( - children: [ - if (prefixMessage.isNotEmpty) - Text( - '$prefixMessage ', - style: ArchethicThemeStyles.textStyleSize12W100Primary60, - ), - Text( - message, - style: ArchethicThemeStyles.textStyleSize12W100Primary, - ), - ], + '$prefixMessage ', + style: ArchethicThemeStyles.textStyleSize12W100Primary60, ), + Text( + message, + style: ArchethicThemeStyles.textStyleSize12W100Primary, + ), ], ); } diff --git a/lib/ui/views/transactions/components/template/transaction_ledger_mvts.dart b/lib/ui/views/transactions/components/template/transaction_ledger_mvts.dart new file mode 100644 index 000000000..63f4c5c49 --- /dev/null +++ b/lib/ui/views/transactions/components/template/transaction_ledger_mvts.dart @@ -0,0 +1,144 @@ +import 'package:aewallet/application/formated_name.dart'; +import 'package:aewallet/application/settings/primary_currency.dart'; +import 'package:aewallet/application/settings/settings.dart'; +import 'package:aewallet/model/blockchain/recent_transaction.dart'; +import 'package:aewallet/model/data/account_balance.dart'; +import 'package:aewallet/model/primary_currency.dart'; +import 'package:aewallet/ui/themes/styles.dart'; +import 'package:aewallet/ui/util/address_formatters.dart'; +import 'package:aewallet/ui/views/transactions/components/template/transaction_hidden_value.dart'; +import 'package:aewallet/ui/widgets/tokens/verified_token_icon.dart'; +import 'package:aewallet/util/number_util.dart'; +import 'package:archethic_dapp_framework_flutter/archethic_dapp_framework_flutter.dart' + as aedappfm; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/localizations.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class TransactionLedgerMvts extends ConsumerWidget { + const TransactionLedgerMvts({super.key, required this.transaction}); + + final RecentTransaction transaction; + + @override + Widget build(BuildContext context, WidgetRef ref) { + if (transaction.ledgerOperationMvtInfo == null || + transaction.ledgerOperationMvtInfo!.isEmpty) { + return const SizedBox.shrink(); + } + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: transaction.ledgerOperationMvtInfo!.map((mvtInfo) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _recipient(context, ref, mvtInfo), + _amount( + context, + ref, + transaction.typeTx == RecentTransaction.transferInput, + mvtInfo, + ), + ], + ); + }).toList(), + ); + } +} + +Widget _amount( + BuildContext context, + WidgetRef ref, + bool isInput, + LedgerOperationMvt mvtInfo, +) { + final amount = mvtInfo.amount; + final amountPrefix = isInput ? '' : '-'; + final hasTransactionInfo = mvtInfo.tokenInformation != null; + final primaryCurrency = ref.watch(selectedPrimaryCurrencyProvider); + final localizations = AppLocalizations.of(context)!; + final settings = ref.watch(SettingsProviders.settings); + + if (amount != null) { + final amountFormatted = + NumberUtil.formatThousandsStr(amount.formatNumber()); + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${localizations.txListAmount} ', + style: ArchethicThemeStyles.textStyleSize12W100Primary60, + ), + if (settings.showBalances == true) + Row( + children: [ + Text( + hasTransactionInfo + ? '$amountPrefix$amountFormatted ${primaryCurrency.primaryCurrency == AvailablePrimaryCurrencyEnum.native ? (mvtInfo.tokenInformation!.symbol != null && mvtInfo.tokenInformation!.symbol! == '' ? 'NFT' : mvtInfo.tokenInformation!.symbol!) : mvtInfo.tokenInformation!.symbol!}' + : '$amountPrefix$amountFormatted ${AccountBalance.cryptoCurrencyLabel}', + style: ArchethicThemeStyles.textStyleSize12W100Primary, + ), + ], + ) + else + const TransactionHiddenValue(), + if (mvtInfo.tokenInformation != null && + (mvtInfo.tokenInformation!.type == 'fungible' || + mvtInfo.tokenInformation!.type == 'UCO') && + mvtInfo.tokenInformation!.address != null) + Row( + children: [ + const SizedBox( + width: 5, + ), + VerifiedTokenIcon( + address: mvtInfo.tokenInformation!.address!, + iconSize: 12, + paddingBottom: 0, + paddingTop: 3, + ), + ], + ), + const SizedBox( + width: 2, + ), + ], + ); + } else { + return const SizedBox.shrink(); + } +} + +Widget _recipient( + BuildContext context, + WidgetRef ref, + LedgerOperationMvt mvtInfo, +) { + if (mvtInfo.to == null || mvtInfo.to!.isEmpty) { + return const SizedBox.shrink(); + } + final localizations = AppLocalizations.of(context)!; + + final formatedName = ref + .watch( + formatedNameFromAddressProvider(context, mvtInfo.to!), + ) + .value ?? + AddressFormatters(mvtInfo.to!).getShortString4(); + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${localizations.txListTo} ', + style: ArchethicThemeStyles.textStyleSize12W100Primary60, + ), + Text( + formatedName, + style: ArchethicThemeStyles.textStyleSize12W100Primary, + ), + ], + ); +} diff --git a/lib/ui/views/transactions/components/template/transaction_template.dart b/lib/ui/views/transactions/components/template/transaction_template.dart index 15db8788b..ecbf283a0 100644 --- a/lib/ui/views/transactions/components/template/transaction_template.dart +++ b/lib/ui/views/transactions/components/template/transaction_template.dart @@ -2,26 +2,23 @@ import 'dart:ui'; -import 'package:aewallet/domain/models/token.dart'; import 'package:aewallet/model/blockchain/recent_transaction.dart'; +import 'package:aewallet/modules/aeswap/application/session/provider.dart'; +import 'package:aewallet/ui/views/transactions/components/template/transaction_action.dart'; import 'package:aewallet/ui/views/transactions/components/template/transaction_comment.dart'; import 'package:aewallet/ui/views/transactions/components/template/transaction_date.dart'; -import 'package:aewallet/ui/views/transactions/components/template/transaction_warning.dart'; import 'package:aewallet/ui/views/transactions/components/transaction_input/transaction_input_icon.dart'; import 'package:aewallet/ui/views/transactions/components/transaction_output/transaction_output_icon.dart'; -import 'package:aewallet/ui/views/transactions/transaction_infos_sheet.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/localizations.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:url_launcher/url_launcher.dart'; class TransactionTemplate extends ConsumerWidget { const TransactionTemplate({ super.key, required this.transaction, - this.onLongPress, required this.borderColor, required this.backgroundColor, required this.content, @@ -30,7 +27,6 @@ class TransactionTemplate extends ConsumerWidget { }); final RecentTransaction transaction; - final Function()? onLongPress; final Color borderColor; final Color backgroundColor; final Widget content; @@ -39,74 +35,58 @@ class TransactionTemplate extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final localizations = AppLocalizations.of(context)!; - - final hasWarning = transaction.tokenInformation != null && - (kTokenFordiddenName.contains( - transaction.tokenInformation!.name!.toUpperCase(), - ) || - kTokenFordiddenName.contains( - transaction.tokenInformation!.symbol!.toUpperCase(), - )); - - return GestureDetector( - onTap: () { - context.go( - TransactionInfosSheet.routerPage, - extra: transaction.address, - ); - }, - onLongPress: onLongPress, - child: Padding( - padding: const EdgeInsets.only(top: 15), - child: ClipRRect( - borderRadius: BorderRadius.circular(16), - child: BackdropFilter( - filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), - child: DecoratedBox( - decoration: BoxDecoration( - color: backgroundColor, - borderRadius: BorderRadius.circular(16), - border: Border.all( - color: borderColor, - ), + return Padding( + padding: const EdgeInsets.only(top: 15), + child: ClipRRect( + borderRadius: BorderRadius.circular(16), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10), + child: DecoratedBox( + decoration: BoxDecoration( + color: backgroundColor, + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: borderColor, ), - child: Container( - padding: const EdgeInsets.all(9.5), - width: MediaQuery.of(context).size.width, - child: Stack( - alignment: Alignment.topRight, - children: [ - Column( + ), + child: Container( + padding: const EdgeInsets.all(9.5), + width: MediaQuery.of(context).size.width, + child: Stack( + alignment: Alignment.topRight, + children: [ + GestureDetector( + onTap: () async { + await launchUrl( + Uri.parse( + '${ref.read(environmentProvider).endpoint}/explorer/transaction/${transaction.address}', + ), + mode: LaunchMode.externalApplication, + ); + }, + child: Column( children: [ + TransactionAction(action: transaction.action), TransactionDate(timestamp: transaction.timestamp), information, content, if (fees != null) fees!, - Row( - children: [ - if (hasWarning) - TransactionWarning( - message: localizations.notOfficialUCOWarning, - ), - ], - ), ], ), - if (transaction.typeTx == 1) - const TransactionInputIcon() - else - transaction.typeTx == 2 - ? TransactionOutputIcon(transaction.recipient) - : const SizedBox.shrink(), - if (transaction.decryptedSecret != null && - transaction.decryptedSecret!.isNotEmpty) - Positioned( - top: 13, - child: TransactionComment(transaction: transaction), - ), - ], - ), + ), + if (transaction.typeTx == 1) + const TransactionInputIcon() + else + transaction.typeTx == 2 + ? TransactionOutputIcon(transaction) + : const SizedBox.shrink(), + if (transaction.decryptedSecret != null && + transaction.decryptedSecret!.isNotEmpty) + Positioned( + top: 13, + child: TransactionComment(transaction: transaction), + ), + ], ), ), ), diff --git a/lib/ui/views/transactions/components/template/transaction_warning.dart b/lib/ui/views/transactions/components/template/transaction_warning.dart deleted file mode 100644 index 4234e7c2c..000000000 --- a/lib/ui/views/transactions/components/template/transaction_warning.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:aewallet/ui/themes/styles.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:material_symbols_icons/symbols.dart'; - -class TransactionWarning extends ConsumerWidget { - const TransactionWarning({ - super.key, - required this.message, - }); - - final String message; - - @override - Widget build(BuildContext context, WidgetRef ref) { - return Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - const Icon( - Symbols.warning, - size: 10, - weight: IconSize.weightM, - opticalSize: IconSize.opticalSizeM, - grade: IconSize.gradeM, - ), - const SizedBox(width: 5), - Text( - message, - style: ArchethicThemeStyles.textStyleSize12W100Primary, - textAlign: TextAlign.end, - ), - ], - ); - } -} diff --git a/lib/ui/views/transactions/components/template/transfer_balance.dart b/lib/ui/views/transactions/components/template/transfer_balance.dart deleted file mode 100644 index a81f853c1..000000000 --- a/lib/ui/views/transactions/components/template/transfer_balance.dart +++ /dev/null @@ -1,59 +0,0 @@ -/// SPDX-License-Identifier: AGPL-3.0-or-later - -import 'package:aewallet/application/settings/settings.dart'; -import 'package:aewallet/model/blockchain/recent_transaction.dart'; -import 'package:aewallet/ui/themes/styles.dart'; -import 'package:aewallet/ui/views/transactions/components/template/transaction_hidden_value.dart'; -import 'package:aewallet/util/currency_util.dart'; -import 'package:archethic_dapp_framework_flutter/archethic_dapp_framework_flutter.dart' - as aedappfm; -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/localizations.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class TransfertBalance extends ConsumerWidget { - const TransfertBalance({ - super.key, - required this.transaction, - required this.isCurrencyNative, - required this.child, - }); - - final RecentTransaction transaction; - final bool isCurrencyNative; - final Widget child; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final settings = ref.watch(SettingsProviders.settings); - final archethicOracleUCO = ref - .watch(aedappfm.ArchethicOracleUCOProviders.archethicOracleUCO) - .valueOrNull; - - return Row( - verticalDirection: - isCurrencyNative ? VerticalDirection.down : VerticalDirection.up, - children: [ - AutoSizeText( - '${AppLocalizations.of(context)!.txListAmount} ', - style: ArchethicThemeStyles.textStyleSize12W100Primary60, - ), - if (transaction.amount != null) - if (settings.showBalances == true) - child - else - const TransactionHiddenValue(), - if (transaction.tokenInformation == null && transaction.amount != null) - if (settings.showBalances == true) - Text( - ' (${CurrencyUtil.convertAmountFormated( - archethicOracleUCO?.usd ?? 0, - transaction.amount!, - )})', - style: ArchethicThemeStyles.textStyleSize12W100Primary, - ), - ], - ); - } -} diff --git a/lib/ui/views/transactions/components/token_creation/token_creation_balance.dart b/lib/ui/views/transactions/components/token_creation/token_creation_balance.dart index 2d1875907..c7d6d669f 100644 --- a/lib/ui/views/transactions/components/token_creation/token_creation_balance.dart +++ b/lib/ui/views/transactions/components/token_creation/token_creation_balance.dart @@ -17,33 +17,41 @@ class TokenCreationBalance extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + if (transaction.ledgerOperationMvtInfo == null || + transaction.ledgerOperationMvtInfo!.isEmpty || + transaction.ledgerOperationMvtInfo!.first.tokenInformation == null) { + return const SizedBox.shrink(); + } + final settings = ref.watch(SettingsProviders.settings); + final tokenInformation = + transaction.ledgerOperationMvtInfo!.first.tokenInformation; var currency = ''; - if (transaction.tokenInformation != null && - transaction.tokenInformation!.type == 'fungible') { - currency = NumberUtil.formatThousandsStr( - transaction.tokenInformation!.supply!.formatNumber(), - ); - } else { - currency = NumberUtil.formatThousandsStr( - transaction.tokenInformation!.supply!.toString(), - ); + if (tokenInformation != null && tokenInformation.supply != null) { + if (tokenInformation.type == 'fungible') { + currency = NumberUtil.formatThousandsStr( + tokenInformation.supply!.formatNumber(), + ); + } else { + currency = NumberUtil.formatThousandsStr( + tokenInformation.supply!.toString(), + ); + } } - final symbol = transaction.tokenInformation!.symbol! == '' - ? 'NFT' - : transaction.tokenInformation!.symbol!; + final symbol = + tokenInformation?.symbol! == '' ? 'NFT' : tokenInformation?.symbol!; return Row( children: [ + AutoSizeText( + '${AppLocalizations.of(context)!.tokenInitialSupply} ', + style: ArchethicThemeStyles.textStyleSize12W100Primary60, + ), if (settings.showBalances) Row( children: [ - AutoSizeText( - '${AppLocalizations.of(context)!.tokenInitialSupply} ', - style: ArchethicThemeStyles.textStyleSize12W100Primary60, - ), AutoSizeText( ' $currency $symbol', style: ArchethicThemeStyles.textStyleSize12W100Primary, diff --git a/lib/ui/views/transactions/components/token_creation/token_creation_information.dart b/lib/ui/views/transactions/components/token_creation/token_creation_information.dart index 0b23a8960..e1b84b906 100644 --- a/lib/ui/views/transactions/components/token_creation/token_creation_information.dart +++ b/lib/ui/views/transactions/components/token_creation/token_creation_information.dart @@ -15,10 +15,17 @@ class TokenCreationInformation extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final localizations = AppLocalizations.of(context)!; + if (transaction.ledgerOperationMvtInfo == null || + transaction.ledgerOperationMvtInfo!.isEmpty || + transaction.ledgerOperationMvtInfo!.first.tokenInformation == null) { + return const SizedBox.shrink(); + } + final tokenInformation = + transaction.ledgerOperationMvtInfo!.first.tokenInformation; return Row( children: [ - if (transaction.tokenInformation!.type == 'fungible') + if (tokenInformation!.type == 'fungible') Expanded( child: Row( children: [ @@ -27,7 +34,7 @@ class TokenCreationInformation extends ConsumerWidget { style: ArchethicThemeStyles.textStyleSize12W100Primary60, ), AutoSizeText( - transaction.tokenInformation!.name!, + tokenInformation.name!, style: ArchethicThemeStyles.textStyleSize12W100Primary, ), ], @@ -42,7 +49,7 @@ class TokenCreationInformation extends ConsumerWidget { style: ArchethicThemeStyles.textStyleSize12W100Primary60, ), AutoSizeText( - transaction.tokenInformation!.name!, + tokenInformation.name!, style: ArchethicThemeStyles.textStyleSize12W100Primary, ), ], diff --git a/lib/ui/views/transactions/components/transaction_hosting/transaction_hosting_information.dart b/lib/ui/views/transactions/components/transaction_hosting/transaction_hosting_information.dart index 838f78662..8c2cf84e1 100644 --- a/lib/ui/views/transactions/components/transaction_hosting/transaction_hosting_information.dart +++ b/lib/ui/views/transactions/components/transaction_hosting/transaction_hosting_information.dart @@ -16,7 +16,6 @@ class TransactionHostingInformation extends ConsumerWidget { final localizations = AppLocalizations.of(context)!; return TransactionInformation( - isEmpty: false, message: localizations.aewebHosting, ); } diff --git a/lib/ui/views/transactions/components/transaction_input/transaction_input.dart b/lib/ui/views/transactions/components/transaction_input/transaction_input.dart index 3478c0f2d..f4785e86b 100644 --- a/lib/ui/views/transactions/components/transaction_input/transaction_input.dart +++ b/lib/ui/views/transactions/components/transaction_input/transaction_input.dart @@ -1,13 +1,8 @@ -/// SPDX-License-Identifier: AGPL-3.0-or-later - -import 'package:aewallet/application/settings/primary_currency.dart'; import 'package:aewallet/model/blockchain/recent_transaction.dart'; -import 'package:aewallet/model/primary_currency.dart'; import 'package:aewallet/ui/themes/archethic_theme.dart'; +import 'package:aewallet/ui/views/transactions/components/template/transaction_ledger_mvts.dart'; import 'package:aewallet/ui/views/transactions/components/template/transaction_template.dart'; -import 'package:aewallet/ui/views/transactions/components/template/transfer_balance.dart'; import 'package:aewallet/ui/views/transactions/components/transaction_input/transaction_input_information.dart'; -import 'package:aewallet/ui/views/transactions/components/transfer_transaction.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -21,8 +16,6 @@ class TransactionInput extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final primaryCurrency = ref.watch(selectedPrimaryCurrencyProvider); - return TransactionTemplate( transaction: transaction, borderColor: ArchethicTheme.backgroundRecentTxListCardTransferInput, @@ -30,17 +23,7 @@ class TransactionInput extends ConsumerWidget { content: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ - TransfertBalance( - transaction: transaction, - isCurrencyNative: primaryCurrency.primaryCurrency == - AvailablePrimaryCurrencyEnum.native, - child: TransferTransaction( - transaction: transaction, - isCurrencyNative: primaryCurrency.primaryCurrency == - AvailablePrimaryCurrencyEnum.native, - isInput: true, - ), - ), + TransactionLedgerMvts(transaction: transaction), ], ), information: TransactionInputInformation( diff --git a/lib/ui/views/transactions/components/transaction_input/transaction_input_information.dart b/lib/ui/views/transactions/components/transaction_input/transaction_input_information.dart index b3bc5bc0d..88ed58b41 100644 --- a/lib/ui/views/transactions/components/transaction_input/transaction_input_information.dart +++ b/lib/ui/views/transactions/components/transaction_input/transaction_input_information.dart @@ -1,8 +1,7 @@ -/// SPDX-License-Identifier: AGPL-3.0-or-later - +import 'package:aewallet/application/formated_name.dart'; import 'package:aewallet/model/blockchain/recent_transaction.dart'; +import 'package:aewallet/ui/themes/styles.dart'; import 'package:aewallet/ui/util/address_formatters.dart'; -import 'package:aewallet/ui/views/transactions/components/template/transaction_information.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -16,12 +15,29 @@ class TransactionInputInformation extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final localizations = AppLocalizations.of(context)!; - return TransactionInformation( - isEmpty: transaction.from == null, - prefixMessage: localizations.txListFrom, - message: - AddressFormatters(transaction.from == null ? '' : transaction.from!) - .getShortString4(), + if (transaction.from == null || transaction.from!.isEmpty) { + return const SizedBox.shrink(); + } + + final formatedName = ref + .watch( + formatedNameFromAddressProvider(context, transaction.from!), + ) + .value ?? + AddressFormatters(transaction.from!).getShortString4(); + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${localizations.txListFrom} ', + style: ArchethicThemeStyles.textStyleSize12W100Primary60, + ), + Text( + formatedName, + style: ArchethicThemeStyles.textStyleSize12W100Primary, + ), + ], ); } } diff --git a/lib/ui/views/transactions/components/transaction_output/transaction_output.dart b/lib/ui/views/transactions/components/transaction_output/transaction_output.dart index 350854c55..d67369829 100644 --- a/lib/ui/views/transactions/components/transaction_output/transaction_output.dart +++ b/lib/ui/views/transactions/components/transaction_output/transaction_output.dart @@ -1,14 +1,8 @@ -/// SPDX-License-Identifier: AGPL-3.0-or-later - -import 'package:aewallet/application/settings/primary_currency.dart'; import 'package:aewallet/model/blockchain/recent_transaction.dart'; -import 'package:aewallet/model/primary_currency.dart'; import 'package:aewallet/ui/themes/archethic_theme.dart'; import 'package:aewallet/ui/views/transactions/components/template/transaction_fees.dart'; +import 'package:aewallet/ui/views/transactions/components/template/transaction_ledger_mvts.dart'; import 'package:aewallet/ui/views/transactions/components/template/transaction_template.dart'; -import 'package:aewallet/ui/views/transactions/components/template/transfer_balance.dart'; -import 'package:aewallet/ui/views/transactions/components/transaction_output/transaction_output_information.dart'; -import 'package:aewallet/ui/views/transactions/components/transfer_transaction.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -22,8 +16,6 @@ class TransactionOuput extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final primaryCurrency = ref.watch(selectedPrimaryCurrencyProvider); - return TransactionTemplate( transaction: transaction, borderColor: ArchethicTheme.backgroundRecentTxListCardTransferOutput, @@ -31,22 +23,10 @@ class TransactionOuput extends ConsumerWidget { content: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ - TransfertBalance( - isCurrencyNative: primaryCurrency.primaryCurrency == - AvailablePrimaryCurrencyEnum.native, - transaction: transaction, - child: TransferTransaction( - isCurrencyNative: primaryCurrency.primaryCurrency == - AvailablePrimaryCurrencyEnum.native, - transaction: transaction, - isInput: false, - ), - ), + TransactionLedgerMvts(transaction: transaction), ], ), - information: TransactionOutputInformation( - transaction: transaction, - ), + information: const SizedBox.shrink(), fees: TransactionFees( transaction: transaction, ), diff --git a/lib/ui/views/transactions/components/transaction_output/transaction_output_icon.dart b/lib/ui/views/transactions/components/transaction_output/transaction_output_icon.dart index e38ad4f6f..12955ba3f 100644 --- a/lib/ui/views/transactions/components/transaction_output/transaction_output_icon.dart +++ b/lib/ui/views/transactions/components/transaction_output/transaction_output_icon.dart @@ -1,24 +1,30 @@ /// SPDX-License-Identifier: AGPL-3.0-or-later - +import 'package:aewallet/model/blockchain/recent_transaction.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:material_symbols_icons/symbols.dart'; class TransactionOutputIcon extends ConsumerWidget { - const TransactionOutputIcon(this.transactionRecipient, {super.key}); + const TransactionOutputIcon(this.recentTransaction, {super.key}); - final String? transactionRecipient; + final RecentTransaction? recentTransaction; @override Widget build(BuildContext context, WidgetRef ref) { const burnAddress = '00000000000000000000000000000000000000000000000000000000000000000000'; - + String? recipient; + if (recentTransaction != null && + recentTransaction!.ledgerOperationMvtInfo != null && + recentTransaction!.ledgerOperationMvtInfo!.isNotEmpty && + recentTransaction!.ledgerOperationMvtInfo!.first.to != null) { + recipient = recentTransaction!.ledgerOperationMvtInfo!.first.to; + } return Padding( padding: const EdgeInsets.only(top: 1), child: Icon( - transactionRecipient == null + recipient == null ? Symbols.call_made - : transactionRecipient == burnAddress + : recipient == burnAddress ? Symbols.mode_heat : Symbols.call_made, size: 12, diff --git a/lib/ui/views/transactions/components/transaction_output/transaction_output_information.dart b/lib/ui/views/transactions/components/transaction_output/transaction_output_information.dart deleted file mode 100644 index c926136b7..000000000 --- a/lib/ui/views/transactions/components/transaction_output/transaction_output_information.dart +++ /dev/null @@ -1,34 +0,0 @@ -/// SPDX-License-Identifier: AGPL-3.0-or-later - -import 'package:aewallet/model/blockchain/recent_transaction.dart'; -import 'package:aewallet/ui/util/address_formatters.dart'; -import 'package:aewallet/ui/views/transactions/components/template/transaction_information.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/localizations.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class TransactionOutputInformation extends ConsumerWidget { - const TransactionOutputInformation({super.key, required this.transaction}); - - final RecentTransaction transaction; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final localizations = AppLocalizations.of(context)!; - - return transaction.recipient == - '00000000000000000000000000000000000000000000000000000000000000000000' - ? TransactionInformation( - isEmpty: transaction.recipient == null, - prefixMessage: localizations.txListTo, - message: localizations.burnAddressLbl, - ) - : TransactionInformation( - isEmpty: transaction.recipient == null, - prefixMessage: localizations.txListTo, - message: AddressFormatters( - transaction.recipient == null ? '' : transaction.recipient!, - ).getShortString4(), - ); - } -} diff --git a/lib/ui/views/transactions/components/transfer_transaction.dart b/lib/ui/views/transactions/components/transfer_transaction.dart deleted file mode 100644 index 022f66026..000000000 --- a/lib/ui/views/transactions/components/transfer_transaction.dart +++ /dev/null @@ -1,61 +0,0 @@ -/// SPDX-License-Identifier: AGPL-3.0-or-later -import 'package:aewallet/model/blockchain/recent_transaction.dart'; -import 'package:aewallet/model/data/account_balance.dart'; -import 'package:aewallet/ui/themes/styles.dart'; -import 'package:aewallet/ui/widgets/tokens/verified_token_icon.dart'; -import 'package:aewallet/util/number_util.dart'; -import 'package:archethic_dapp_framework_flutter/archethic_dapp_framework_flutter.dart' - as aedappfm; -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -class TransferTransaction extends ConsumerWidget { - const TransferTransaction({ - super.key, - required this.transaction, - required this.isCurrencyNative, - required this.isInput, - }); - - final RecentTransaction transaction; - final bool isCurrencyNative; - final bool isInput; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final hasTransactionInfo = transaction.tokenInformation != null; - final amountFormatted = NumberUtil.formatThousandsStr( - transaction.amount!.formatNumber(), - ); - - final amountPrefix = isInput ? '' : '-'; - - return Row( - children: [ - AutoSizeText( - hasTransactionInfo - ? '$amountPrefix$amountFormatted ${isCurrencyNative ? (transaction.tokenInformation!.symbol! == '' ? 'NFT' : transaction.tokenInformation!.symbol!) : transaction.tokenInformation!.symbol!}' - : '$amountPrefix$amountFormatted ${AccountBalance.cryptoCurrencyLabel}', - style: ArchethicThemeStyles.textStyleSize12W100Primary, - ), - if (transaction.tokenInformation != null && - transaction.tokenInformation!.type == 'fungible' && - transaction.tokenAddress != null) - Row( - children: [ - const SizedBox( - width: 5, - ), - VerifiedTokenIcon( - address: transaction.tokenAddress!, - ), - ], - ), - const SizedBox( - width: 2, - ), - ], - ); - } -} diff --git a/lib/ui/views/transactions/transaction_infos_sheet.dart b/lib/ui/views/transactions/transaction_infos_sheet.dart deleted file mode 100755 index 1203cc98f..000000000 --- a/lib/ui/views/transactions/transaction_infos_sheet.dart +++ /dev/null @@ -1,355 +0,0 @@ -/// SPDX-License-Identifier: AGPL-3.0-or-later - -import 'package:aewallet/application/account/accounts.dart'; -import 'package:aewallet/application/account/accounts_notifier.dart'; -import 'package:aewallet/application/app_service.dart'; -import 'package:aewallet/application/session/session.dart'; -import 'package:aewallet/application/settings/settings.dart'; -import 'package:aewallet/model/data/account_balance.dart'; -import 'package:aewallet/model/transaction_infos.dart'; -import 'package:aewallet/modules/aeswap/application/session/provider.dart'; -import 'package:aewallet/ui/themes/archethic_theme.dart'; -import 'package:aewallet/ui/themes/styles.dart'; -import 'package:aewallet/ui/util/dimens.dart'; -import 'package:aewallet/ui/views/main/components/sheet_appbar.dart'; -import 'package:aewallet/ui/views/main/home_page.dart'; -import 'package:aewallet/ui/widgets/components/app_button_tiny.dart'; -import 'package:aewallet/ui/widgets/components/icon_widget.dart'; -import 'package:aewallet/ui/widgets/components/sheet_skeleton.dart'; -import 'package:aewallet/ui/widgets/components/sheet_skeleton_interface.dart'; -import 'package:aewallet/util/account_formatters.dart'; -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/localizations.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:go_router/go_router.dart'; -import 'package:intl/intl.dart'; -import 'package:logging/logging.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class TransactionInfosSheet extends ConsumerStatefulWidget { - const TransactionInfosSheet(this.notificationRecipientAddress, {super.key}); - - final String notificationRecipientAddress; - - static const routerPage = '/transaction_info'; - - @override - ConsumerState createState() => - _TransactionInfosSheetState(); -} - -class _TransactionInfosSheetState extends ConsumerState - implements SheetSkeletonInterface { - @override - Widget build(BuildContext context) { - final selectedAccount = ref.watch( - accountsNotifierProvider.select( - (accounts) => accounts.valueOrNull?.selectedAccount, - ), - ); - - if (selectedAccount == null) return const SizedBox(); - - return SheetSkeleton( - appBar: getAppBar(context, ref), - floatingActionButton: getFloatingActionButton(context, ref), - sheetContent: getSheetContent(context, ref), - thumbVisibility: false, - ); - } - - @override - Widget getFloatingActionButton(BuildContext context, WidgetRef ref) { - final localizations = AppLocalizations.of(context)!; - return Row( - children: [ - AppButtonTinyConnectivity( - localizations.viewExplorer, - Dimens.buttonBottomDimens, - key: const Key('viewExplorer'), - onPressed: () async { - await launchUrl( - Uri.parse( - '${ref.read(environmentProvider).endpoint}/explorer/transaction/${widget.notificationRecipientAddress}', - ), - mode: LaunchMode.externalApplication, - ); - }, - ), - ], - ); - } - - @override - PreferredSizeWidget getAppBar(BuildContext context, WidgetRef ref) { - final localizations = AppLocalizations.of(context)!; - return SheetAppBar( - title: localizations.transactionInfosHeader, - widgetLeft: BackButton( - key: const Key('back'), - color: ArchethicTheme.text, - onPressed: () { - context.go(HomePage.routerPage); - }, - ), - ); - } - - @override - Widget getSheetContent(BuildContext context, WidgetRef ref) { - final session = ref.watch(sessionNotifierProvider).loggedIn!; - final selectedAccount = ref.watch( - accountsNotifierProvider.select( - (accounts) => accounts.valueOrNull?.selectedAccount, - ), - ); - final appService = ref.watch(appServiceProvider); - return FutureBuilder>( - future: appService.getTransactionAllInfos( - widget.notificationRecipientAddress, - DateFormat.yMEd(Localizations.localeOf(context).languageCode), - AccountBalance.cryptoCurrencyLabel, - context, - session.wallet.keychainSecuredInfos.services[selectedAccount!.name]! - .keyPair!, - ), - builder: ( - BuildContext context, - AsyncSnapshot> list, - ) { - return list.hasData - ? _TransactionInfos( - list: list, - notificationRecipientAddress: - widget.notificationRecipientAddress, - ) - : _TransactionLoading(); - }, - ); - } -} - -class _TransactionLoading extends ConsumerWidget { - @override - Widget build(BuildContext context, WidgetRef ref) { - return Center( - child: CircularProgressIndicator( - color: ArchethicTheme.text, - strokeWidth: 1, - ), - ); - } -} - -class _TransactionInfos extends ConsumerWidget { - const _TransactionInfos({ - required this.list, - required this.notificationRecipientAddress, - }); - - final AsyncSnapshot> list; - final String notificationRecipientAddress; - - @override - Widget build(BuildContext context, WidgetRef ref) { - return Container( - height: MediaQuery.of(context).size.height, - padding: const EdgeInsets.only(bottom: 80), - child: ListView.builder( - padding: const EdgeInsets.only( - top: 15, - bottom: 200, - ), - itemCount: list.data == null ? 0 : list.data!.length, - itemBuilder: ( - BuildContext context, - int index, - ) { - return Padding( - padding: const EdgeInsets.only( - right: 10, - ), - child: _TransactionBuildInfos( - transactionInfo: list.data![index], - ), - ); - }, - ), - ); - } -} - -class _TransactionBuildInfos extends ConsumerWidget { - const _TransactionBuildInfos({required this.transactionInfo}); - - final TransactionInfos transactionInfo; - - static final _logger = Logger('TransactionBuildInfos'); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final preferences = ref.watch(SettingsProviders.settings); - return (preferences.showBalances == true || - (preferences.showBalances == false && - transactionInfo.titleInfo != 'Amount')) - ? Row( - children: [ - Expanded( - child: Stack( - children: [ - if (transactionInfo.titleInfo == '') - const SizedBox() - else - Container( - padding: const EdgeInsets.only(left: 10, top: 20), - width: 50, - height: 50, - child: IconWidget( - icon: - 'assets/icons/txInfos/${transactionInfo.titleInfo}.png', - width: 50, - height: 50, - ), - ), - if (transactionInfo.titleInfo == '') - ColoredBox( - color: ArchethicTheme.text05, - child: Container( - padding: const EdgeInsets.only(left: 15, top: 15), - child: Column( - children: [ - // Main Container - Container( - padding: const EdgeInsets.only( - bottom: 15, - ), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - AutoSizeText( - TransactionInfos.getDisplayName( - context, - transactionInfo.domain, - ), - style: ArchethicThemeStyles - .textStyleSize16W600Primary, - ), - ], - ), - ), - ], - ), - ), - ], - ), - ), - ) - else - Container( - padding: const EdgeInsets.only(left: 15, top: 15), - child: Column( - children: [ - // Main Container - Container( - padding: const EdgeInsets.only( - left: 45, - right: 5, - bottom: 15, - ), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - AutoSizeText( - TransactionInfos.getDisplayName( - context, - transactionInfo.titleInfo, - ), - style: ArchethicThemeStyles - .textStyleSize14W600Primary, - ), - _transactionInfoValue( - ref, - transactionInfo, - ), - ], - ), - ), - ], - ), - ), - ], - ), - ), - Divider( - height: 2, - color: ArchethicTheme.text15, - ), - ], - ), - ), - ], - ) - : const SizedBox(); - } - - Widget _transactionInfoValue( - WidgetRef ref, - TransactionInfos transactionInfo, - ) { - if (transactionInfo.domain == 'UCOLedger' && - transactionInfo.titleInfo == 'To') { - _logger.info('transactionInfo.valueInfo: ${transactionInfo.valueInfo}'); - return ref - .watch( - accountWithGenesisAddressProvider(transactionInfo.valueInfo), - ) - .when( - data: (data) { - if (data == null) { - _logger.info( - 'transactionInfo.valueInfo: ${transactionInfo.valueInfo} : data null', - ); - return SelectableText( - transactionInfo.valueInfo, - style: ArchethicThemeStyles.textStyleSize14W200Primary, - ); - } else { - _logger.info( - 'transactionInfo.valueInfo: ${transactionInfo.valueInfo} : data not null', - ); - return SelectableText( - data.format, - style: ArchethicThemeStyles.textStyleSize14W200Primary, - ); - } - }, - error: (error, stacktrace) => SelectableText( - transactionInfo.valueInfo, - style: ArchethicThemeStyles.textStyleSize14W200Primary, - ), - loading: () => SelectableText( - transactionInfo.valueInfo, - style: ArchethicThemeStyles.textStyleSize14W200Primary, - ), - ); - } else { - return SelectableText( - transactionInfo.valueInfo, - style: ArchethicThemeStyles.textStyleSize14W200Primary, - ); - } - } -} diff --git a/lib/ui/views/transactions/transactions_list.dart b/lib/ui/views/transactions/transactions_list.dart index 3584e8cc3..56d3123b2 100644 --- a/lib/ui/views/transactions/transactions_list.dart +++ b/lib/ui/views/transactions/transactions_list.dart @@ -1,13 +1,17 @@ -import 'package:aewallet/model/blockchain/recent_transaction.dart'; - +import 'package:aewallet/application/account/accounts_notifier.dart'; +import 'package:aewallet/application/recent_transactions.dart'; +import 'package:aewallet/ui/themes/archethic_theme.dart'; +import 'package:aewallet/ui/themes/styles.dart'; import 'package:aewallet/ui/views/transactions/components/transaction_detail.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/localizations.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:material_symbols_icons/symbols.dart'; class TransactionsList extends ConsumerStatefulWidget { - const TransactionsList({super.key, required this.transactionsList}); - - final List transactionsList; + const TransactionsList({ + super.key, + }); @override ConsumerState createState() => _TransactionsListState(); @@ -24,12 +28,75 @@ class _TransactionsListState extends ConsumerState ) { super.build(context); + final selectedAccount = ref.watch( + accountsNotifierProvider.select( + (accounts) => accounts.valueOrNull?.selectedAccount, + ), + ); + if (selectedAccount == null) { + return const SizedBox.shrink(); + } + final recentTransactionsAsync = ref.watch( + recentTransactionsProvider(selectedAccount.genesisAddress), + ); + return Column( - children: widget.transactionsList.map((transaction) { - return TransactionDetail( - transaction: transaction, - ); - }).toList(), + children: [ + recentTransactionsAsync.map( + data: (data) { + if (data.value.isEmpty) { + return _recentTransactionsEmpty(context); + } + + return Column( + children: data.value.map((transaction) { + return TransactionDetail( + transaction: transaction, + ); + }).toList(), + ); + }, + error: (_) => const SizedBox.shrink(), + loading: (_) => const SizedBox.shrink(), + ), + ], ); } } + +Widget _recentTransactionsEmpty(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 20), + child: Card( + shape: RoundedRectangleBorder( + side: BorderSide( + color: ArchethicTheme.backgroundFungiblesTokensListCard, + ), + borderRadius: BorderRadius.circular(10), + ), + elevation: 0, + color: ArchethicTheme.backgroundFungiblesTokensListCard, + child: Container( + padding: const EdgeInsets.all(9.5), + width: MediaQuery.of(context).size.width, + alignment: Alignment.center, + child: Row( + children: [ + const Icon( + Symbols.info, + size: 18, + weight: IconSize.weightM, + opticalSize: IconSize.opticalSizeM, + grade: IconSize.gradeM, + ), + const SizedBox(width: 8), + Text( + AppLocalizations.of(context)!.recentTransactionsNoTransactionYet, + style: ArchethicThemeStyles.textStyleSize12W100Primary, + ), + ], + ), + ), + ), + ); +} diff --git a/lib/ui/views/transfer/bloc/provider.dart b/lib/ui/views/transfer/bloc/provider.dart index 6025b7a2c..cb114eff2 100644 --- a/lib/ui/views/transfer/bloc/provider.dart +++ b/lib/ui/views/transfer/bloc/provider.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:aewallet/application/account/accounts.dart'; import 'package:aewallet/application/account/accounts_notifier.dart'; -import 'package:aewallet/application/app_service.dart'; +import 'package:aewallet/application/api_service.dart'; import 'package:aewallet/application/session/session.dart'; import 'package:aewallet/application/settings/primary_currency.dart'; import 'package:aewallet/application/transaction_repository.dart'; @@ -179,9 +179,11 @@ class TransferFormNotifier extends AutoDisposeNotifier { return true; } - final appService = ref.read(appServiceProvider); - final transactionTypeMap = await appService - .getTransaction([state.recipient.address!.address!], request: 'type'); + final apiService = ref.read(apiServiceProvider); + final transactionTypeMap = await apiService.getTransaction( + {state.recipient.address!.address!}.toList(), + request: 'type', + ); if (transactionTypeMap[state.recipient.address!.address] == null) { return true; } diff --git a/lib/ui/views/transfer/layouts/components/transfer_confirm_sheet.dart b/lib/ui/views/transfer/layouts/components/transfer_confirm_sheet.dart index e4add0755..e3d6e34f7 100755 --- a/lib/ui/views/transfer/layouts/components/transfer_confirm_sheet.dart +++ b/lib/ui/views/transfer/layouts/components/transfer_confirm_sheet.dart @@ -102,12 +102,17 @@ class _TransferConfirmSheetState extends ConsumerState try { final transfer = ref.read(TransferFormProvider.transferForm); + final accountSelected = ref.read( + accountsNotifierProvider.select( + (accounts) => accounts.valueOrNull?.selectedAccount, + ), + ); if (transfer.transferType == TransferType.nft) { unawaited( (await ref .read(accountsNotifierProvider.notifier) .selectedAccountNotifier) - ?.updateNFT(), + ?.updateNFT(accountSelected!), ); } unawaited( diff --git a/lib/ui/widgets/tokens/verified_token_icon.dart b/lib/ui/widgets/tokens/verified_token_icon.dart index 586f9ed7b..09fd5f435 100644 --- a/lib/ui/widgets/tokens/verified_token_icon.dart +++ b/lib/ui/widgets/tokens/verified_token_icon.dart @@ -10,17 +10,21 @@ class VerifiedTokenIcon extends ConsumerWidget { const VerifiedTokenIcon({ required this.address, this.iconSize = 14, + this.paddingBottom = 3, + this.paddingTop = 0, super.key, }); final String address; final double iconSize; + final double paddingBottom; + final double paddingTop; @override Widget build(BuildContext context, WidgetRef ref) { if (address.isUCO) { return Padding( - padding: const EdgeInsets.only(bottom: 3), + padding: EdgeInsets.only(bottom: paddingBottom, top: paddingTop), child: Tooltip( message: AppLocalizations.of(context)!.verifiedTokenIconTooltip, child: Icon( @@ -44,7 +48,7 @@ class VerifiedTokenIcon extends ConsumerWidget { if (isVerifiedToken == false) return const SizedBox(); return Padding( - padding: const EdgeInsets.only(bottom: 3), + padding: EdgeInsets.only(bottom: paddingBottom, top: paddingTop), child: Tooltip( message: AppLocalizations.of(context)!.verifiedTokenIconTooltip, child: Icon( diff --git a/lib/util/keychain_util.dart b/lib/util/keychain_util.dart index 5d97a8efd..55149a2ba 100644 --- a/lib/util/keychain_util.dart +++ b/lib/util/keychain_util.dart @@ -178,7 +178,6 @@ mixin KeychainServiceMixin { nativeTokenName: 'UCO', nativeTokenValue: 0, ), - recentTransactions: const [], serviceType: serviceType, selected: isSelected, ); diff --git a/test/token_parser_test.mocks.dart b/test/token_parser_test.mocks.dart index 22c54377b..eb0785422 100644 --- a/test/token_parser_test.mocks.dart +++ b/test/token_parser_test.mocks.dart @@ -7,9 +7,9 @@ import 'dart:async' as _i9; import 'dart:typed_data' as _i16; import 'package:aewallet/domain/repositories/tokens/tokens.repository.dart' - as _i21; -import 'package:aewallet/modules/aeswap/domain/models/util/get_pool_list_response.dart' as _i22; +import 'package:aewallet/modules/aeswap/domain/models/util/get_pool_list_response.dart' + as _i23; import 'package:archethic_dapp_framework_flutter/archethic_dapp_framework_flutter.dart' as _i8; import 'package:archethic_lib_dart/archethic_lib_dart.dart' as _i10; @@ -27,6 +27,7 @@ import 'package:archethic_lib_dart/src/model/transaction.dart' as _i4; import 'package:archethic_lib_dart/src/model/transaction_fee.dart' as _i3; import 'package:archethic_lib_dart/src/model/transaction_input.dart' as _i14; import 'package:archethic_lib_dart/src/model/transaction_status.dart' as _i2; +import 'package:archethic_lib_dart/src/model/unspent_outputs.dart' as _i21; import 'package:graphql/client.dart' as _i20; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i11; @@ -720,6 +721,30 @@ class MockApiService extends _i1.Mock implements _i10.ApiService { returnValueForMissingStub: null, ); + @override + _i9.Future>> chainUnspentOutputs( + List? genesisAddresses, { + String? request = r' amount, from, timestamp, tokenAddress, tokenId, type', + int? limit = 0, + String? pagingOffset = r'', + }) => + (super.noSuchMethod( + Invocation.method( + #chainUnspentOutputs, + [genesisAddresses], + { + #request: request, + #limit: limit, + #pagingOffset: pagingOffset, + }, + ), + returnValue: _i9.Future>>.value( + >{}), + returnValueForMissingStub: + _i9.Future>>.value( + >{}), + ) as _i9.Future>>); + @override String setJsonRPCRequest( String? method, @@ -794,7 +819,7 @@ class MockApiService extends _i1.Mock implements _i10.ApiService { /// A class which mocks [TokensRepository]. /// /// See the documentation for Mockito's code generation for more information. -class MockTokensRepository extends _i1.Mock implements _i21.TokensRepository { +class MockTokensRepository extends _i1.Mock implements _i22.TokensRepository { @override _i9.Future> getTokensFromAddresses( List? addresses) => @@ -813,7 +838,7 @@ class MockTokensRepository extends _i1.Mock implements _i21.TokensRepository { _i9.Future> getTokensFromUserBalance( String? userGenesisAddress, List? userTokenLocalAddresses, - List<_i22.GetPoolListResponse>? poolsListRaw, + List<_i23.GetPoolListResponse>? poolsListRaw, _i8.Environment? environment, { bool? withUCO = true, bool? withVerified = true,