improve evm ui (#3129)

* improve evm ui

* fix model generation

* add db schema

* bugfixes

* scrollable fav token modal

* make combined balance the default

* add crypto_full_icons to pubspec

* optimize transaction duplication check

* fix format issue

* add chain icons to swap tiles

* decrease asset tile font weight

* show chain icon in swap currency selector

* move chain icon for tokens

* new trade history row

* ui fix

* fix padding on modal

* remove vec
This commit is contained in:
malik1004x
2026-04-01 11:04:58 +02:00
committed by GitHub
parent ed34c4b3df
commit 915e76d4f9
51 changed files with 710 additions and 195 deletions
@@ -1,4 +1,5 @@
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/utilities.dart';
import 'package:cake_wallet/entities/balance_display_mode.dart';
import 'package:cake_wallet/entities/calculate_fiat_amount.dart';
import 'package:cake_wallet/entities/fiat_api_mode.dart';
@@ -6,8 +7,12 @@ import 'package:cake_wallet/entities/sort_balance_types.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/evm/evm.dart';
import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/zano/zano.dart';
import 'package:cw_core/crypto_amount_format.dart';
import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
@@ -57,11 +62,16 @@ class BalanceRecord {
.toString().withMaxDecimals(8);
String get combinedFiatAvailableBalance =>
fiatAvailableBalance.split(" ").first +
fiatCurrencyTicker +
" " +
((double.tryParse(fiatAvailableBalance.split(" ").last) ?? 0) +
(double.tryParse(fiatSecondAvailableBalance.split(" ").last) ?? 0))
.toString().withMaxDecimals(8);
((double.tryParse(fiatAvailableBalanceRaw) ?? 0) +
(double.tryParse(fiatSecondAvailableBalanceRaw) ?? 0))
.toString()
.withMaxDecimals(8);
String get fiatAvailableBalanceRaw => fiatAvailableBalance.split(" ").last;
String get fiatSecondAvailableBalanceRaw => fiatSecondAvailableBalance.split(" ").last;
String get fiatCurrencyTicker => fiatAvailableBalance.split(" ").first;
}
class BalanceViewModel = BalanceViewModelBase with _$BalanceViewModel;
@@ -410,6 +420,58 @@ abstract class BalanceViewModelBase with Store {
return balance;
}
BalanceRecord? getMainBalanceRecord(bool lightningMode) {
if (lightningMode) {
return formattedBalances.elementAtOrNull(1);
}
if (wallet.walletInfo.favoriteTokenAddress != null) {
return formattedBalances.firstWhereOrNull((item) => (getTokenAddressBasedOnWallet(item.asset) ==
wallet.walletInfo.favoriteTokenAddress)) ??
formattedBalances.elementAt(0);
}
return formattedBalances.elementAt(0);
}
String? getTokenAddressBasedOnWallet(CryptoCurrency asset) {
if (wallet.type == WalletType.tron) {
return tron!.getTokenAddress(asset);
}
if (wallet.type == WalletType.solana) {
return solana!.getTokenAddress(asset);
}
if (isEVMCompatibleChain(wallet.type) && asset is Erc20Token) {
return evm!.getTokenAddress(asset);
}
if (wallet.type == WalletType.zano) {
return zano!.getZanoAssetAddress(asset);
}
return null;
}
@computed
bool get showCombinedBalance {
if(wallet.type == WalletType.bitcoin) return false;
if(balances.values.length == 1) return false;
return wallet.walletInfo.showCombinedBalance;
}
@computed
String get combinedFiatBalance {
double ret = 0.0;
for(final record in balances.values) {
ret += double.tryParse(record.fiatAvailableBalanceRaw) ?? 0;
}
return ret.toStringAsFixed(2);
}
Balance _currencyBalance(CryptoCurrency cryptoCurrency) {
final balance = wallet.balance[cryptoCurrency];
@@ -23,6 +23,7 @@ import 'package:cake_wallet/store/dashboard/order_filter_store.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/zcash/zcash.dart';
import 'package:cw_core/transaction_direction.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cake_wallet/utils/tor.dart';
import 'package:cake_wallet/wownero/wownero.dart' as wow;
@@ -481,18 +482,29 @@ abstract class DashboardViewModelBase with Store {
}
// printV("Transaction disposer callback (relevantTxs: ${relevantTxs.length} current: ${transactions.length})");
String _txIdentityString(String txHash, TransactionDirection direction) => '${txHash}_$direction';
final existingKeys = transactions
.map((item) => _txIdentityString(item.transaction.txHash, item.transaction.direction))
.toSet();
final newTransactions = relevantTxs
.where((tx) => !existingKeys.contains(_txIdentityString(tx.txHash, tx.direction)))
.map((tx) => TransactionListItem(
transaction: tx,
balanceViewModel: balanceViewModel,
appStore: appStore,
key: ValueKey('${wallet.type.name}_transaction_history_item_${tx.id}_key'),
))
.where((item) => !transactions.contains(item));
.toList();
transactions.removeWhere((item) => newTransactions.any((tx) =>
tx.transaction.txHash == item.transaction.txHash &&
tx.transaction.direction == item.transaction.direction));
final newKeys = newTransactions
.map((item) => _txIdentityString(item.transaction.txHash, item.transaction.direction))
.toSet();
transactions.removeWhere(
(item) => newKeys.contains(_txIdentityString(item.transaction.txHash, item.transaction.direction)),
);
transactions.addAll(newTransactions);
// transactions.clear();
@@ -9,6 +9,7 @@ import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/solana/solana.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/tron/tron.dart';
import 'package:cake_wallet/utils/token_utilities.dart';
import 'package:cw_core/utils/proxy_wrapper.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cake_wallet/zano/zano.dart';
@@ -29,10 +30,14 @@ abstract class HomeSettingsViewModelBase with Store {
: tokens = ObservableSet<CryptoCurrency>(),
isAddingToken = false,
isDeletingToken = false,
isValidatingContractAddress = false {
isValidatingContractAddress = false,
showCombinedBalance = _balanceViewModel.wallet.walletInfo.showCombinedBalance,
favoriteToken = _balanceViewModel.wallet.currency {
_updateTokensList();
_updateLocalFavoriteToken();
// React to wallet changes
// React to wallet changes
reaction((_) => _balanceViewModel.wallet, (_) {
_updateTokensList();
});
@@ -55,6 +60,9 @@ abstract class HomeSettingsViewModelBase with Store {
final ObservableSet<CryptoCurrency> tokens;
@computed
List<CryptoCurrency> get enabledTokens => [_balanceViewModel.wallet.currency, ...tokens.where((item)=>item.enabled).toList()];
WalletType get walletType => _balanceViewModel.wallet.type;
@observable
@@ -491,6 +499,45 @@ abstract class HomeSettingsViewModelBase with Store {
}
}
// FIXME these two observables cause duplicated state and are needed because mobx. remove them as part of the refactor and replace with proper getters
@observable
bool showCombinedBalance;
@observable
CryptoCurrency favoriteToken;
@action
void setShowCombinedBalance(bool value) {
_balanceViewModel.wallet.walletInfo.showCombinedBalance = value;
showCombinedBalance = value;
_balanceViewModel.wallet.updateBalance();
_balanceViewModel.wallet.walletInfo.save();
}
@action
Future<void> setFavoriteToken(CryptoCurrency token) async {
final String? address;
if(token == _balanceViewModel.wallet.currency) {
address = null;
} else {
address = getTokenAddressBasedOnWallet(token);
}
_balanceViewModel.wallet.walletInfo.favoriteTokenAddress = address;
_balanceViewModel.wallet.walletInfo.save();
_updateLocalFavoriteToken();
}
@action
Future<void> _updateLocalFavoriteToken() async {
favoriteToken = await TokenUtilities.findTokenByAddress(
walletType: _balanceViewModel.wallet.type, address: _balanceViewModel.wallet.walletInfo.favoriteTokenAddress ?? "") ??
_balanceViewModel.wallet.currency;
}
@action
void _refreshTokensList() {
final _tokens = Set.of(tokens);