From ccfe424b28a39abb4dd5f8e84789da5a3b5d98b8 Mon Sep 17 00:00:00 2001 From: Serhii Date: Sat, 22 Nov 2025 00:05:15 +0200 Subject: [PATCH] CW-1186-Show-the-fiat-amount-for-UTXOs-in-coin-control (#2595) * Add fiat amount display to unspent coins list * refactor fiat amount handling for unspent coins * Update lib/view_model/unspent_coins/unspent_coins_list_view_model.dart [skip ci] --------- Co-authored-by: Omar Hatem --- lib/di.dart | 2 + .../unspent_coins_list_page.dart | 48 ++++++++++--------- .../widgets/unspent_coins_list_item.dart | 18 +++++++ .../unspent_coins_list_view_model.dart | 37 +++++++++++++- 4 files changed, 82 insertions(+), 23 deletions(-) diff --git a/lib/di.dart b/lib/di.dart index fde64bd72..90b8a5ba4 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1428,6 +1428,8 @@ Future setup({ return UnspentCoinsListViewModel( wallet: wallet!, unspentCoinsInfo: _unspentCoinsInfoSource, + fiatConversationStore: getIt.get(), + settingsStore: getIt.get(), coinTypeToSpendFrom: coinTypeToSpendFrom ?? UnspentCoinType.any, ); }); diff --git a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart index 3c47ea203..70812dcd9 100644 --- a/lib/src/screens/unspent_coins/unspent_coins_list_page.dart +++ b/lib/src/screens/unspent_coins/unspent_coins_list_page.dart @@ -178,28 +178,32 @@ class UnspentCoinsListFormState extends State { itemBuilder: (_, int index) { final item = unspentCoinsListViewModel.items[index]; return Observer( - builder: (_) => GestureDetector( - onTap: () => Navigator.of(context).pushNamed( - Routes.unspentCoinsDetails, - arguments: [item, unspentCoinsListViewModel], - ), - child: UnspentCoinsListItem( - note: item.note, - amount: item.amount, - address: item.address, - isSending: item.isSending, - isFrozen: item.isFrozen, - isChange: item.isChange, - isSilentPayment: item.isSilentPayment, - onCheckBoxTap: item.isFrozen - ? null - : () async { - item.isSending = !item.isSending; - await unspentCoinsListViewModel - .saveUnspentCoinInfo(item); - }, - ), - ), + builder: (_) { + final fiatAmount = unspentCoinsListViewModel.fiatAmounts[item.hash] ?? ''; + return GestureDetector( + onTap: () => Navigator.of(context).pushNamed( + Routes.unspentCoinsDetails, + arguments: [item, unspentCoinsListViewModel], + ), + child: UnspentCoinsListItem( + note: item.note, + amount: item.amount, + fiatAmount: fiatAmount, + address: item.address, + isSending: item.isSending, + isFrozen: item.isFrozen, + isChange: item.isChange, + isSilentPayment: item.isSilentPayment, + onCheckBoxTap: item.isFrozen + ? null + : () async { + item.isSending = !item.isSending; + await unspentCoinsListViewModel + .saveUnspentCoinInfo(item); + }, + ), + ); + } ); }, ), diff --git a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart index fd9dc0c6d..d95fd1347 100644 --- a/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart +++ b/lib/src/screens/unspent_coins/widgets/unspent_coins_list_item.dart @@ -7,6 +7,7 @@ class UnspentCoinsListItem extends StatelessWidget { UnspentCoinsListItem({ required this.note, required this.amount, + required this.fiatAmount, required this.address, required this.isSending, required this.isFrozen, @@ -17,6 +18,7 @@ class UnspentCoinsListItem extends StatelessWidget { final String note; final String amount; + final String fiatAmount; final String address; final bool isSending; final bool isFrozen; @@ -108,6 +110,22 @@ class UnspentCoinsListItem extends StatelessWidget { ), ], ), + if (fiatAmount.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AutoSizeText( + fiatAmount, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: amountColor, + fontSize: 1, + fontWeight: FontWeight.w600, + ), + maxLines: 1, + ), + ], + ), Expanded( child: Row( crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart index a5103fa33..deca2bc33 100644 --- a/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart +++ b/lib/view_model/unspent_coins/unspent_coins_list_view_model.dart @@ -1,5 +1,10 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/entities/calculate_fiat_amount_raw.dart'; +import 'package:cake_wallet/entities/fiat_api_mode.dart'; +import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/exception_handler.dart'; import 'package:cake_wallet/decred/decred.dart'; import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart'; @@ -27,13 +32,19 @@ abstract class UnspentCoinsListViewModelBase with Store { required this.wallet, required Box unspentCoinsInfo, this.coinTypeToSpendFrom = UnspentCoinType.any, + required FiatConversionStore fiatConversationStore, + required SettingsStore settingsStore, }) : _unspentCoinsInfo = unspentCoinsInfo, + _fiatConversationStore = fiatConversationStore, + _settingsStore = settingsStore, items = ObservableList(), _originalState = {}; @observable WalletBase, TransactionInfo> wallet; final Box _unspentCoinsInfo; + final FiatConversionStore _fiatConversationStore; + final SettingsStore _settingsStore; final UnspentCoinType coinTypeToSpendFrom; @observable @@ -47,6 +58,30 @@ abstract class UnspentCoinsListViewModelBase with Store { @computed bool get isAllSelected => items.every((element) => element.isFrozen || element.isSending); + @computed + FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency; + + @computed + bool get isFiatDisabled => _settingsStore.fiatApiMode == FiatApiMode.disabled; + + @computed + Map get fiatAmounts { + + final currency = wallet.currency; + final price = _fiatConversationStore.prices[currency]; + if (price == null || price == 0.0 || isFiatDisabled) return {}; + + final result = {}; + for (final item in items) { + final formatted = formatAmountToString(item.value); + final cryptoAmount = double.tryParse(formatted.replaceAll(',', '')) ?? 0.0; + final fiatValue = price * cryptoAmount; + result[item.hash] = fiatCurrency.title + ' ' + fiatValue.toStringAsFixed(2); + } + + return result; + } + Future initialSetup() async { await _updateUnspents(); _storeOriginalState(); @@ -176,7 +211,7 @@ abstract class UnspentCoinsListViewModelBase with Store { .map((elem) { try { final existingItem = _unspentCoinsInfo.values - .firstWhereOrNull((item) => item.walletId == wallet.id && item == elem); + .firstWhereOrNull((item) => item.walletId == wallet.id && item == elem);; if (existingItem == null) return null;