Merge branch 'dev' into cw-1497-transaction-history-improved-layout

# Conflicts:
#	lib/new-ui/widgets/coins_page/assets_history/history_section.dart
#	lib/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart
This commit is contained in:
Robert Malikowski
2026-07-01 16:53:59 +02:00
263 changed files with 6371 additions and 4762 deletions
@@ -9,6 +9,7 @@ import 'package:cw_core/root_dir.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:path/path.dart' as path;
class DevMoneroWalletCacheDebugPage extends StatelessWidget {
@@ -263,6 +264,7 @@ class _JsonExplorerState extends State<JsonExplorer> {
),
)
: ListView.builder(
controller: ModalScrollController.of(context),
itemCount: _filteredItems.length,
itemBuilder: (context, index) {
final item = _filteredItems[index];
+2 -2
View File
@@ -733,7 +733,7 @@ class ExchangePage extends BasePage {
exchangeViewModel.depositAddress =
await fetchParsedAddress(context, domain, exchangeViewModel.depositCurrency);
},
useSatoshis: exchangeViewModel.useSatoshiDeposit,
useSatoshis: exchangeViewModel.useDepositBaseUnit,
),
);
@@ -782,7 +782,7 @@ class ExchangePage extends BasePage {
exchangeViewModel.receiveAddress =
await fetchParsedAddress(context, domain, exchangeViewModel.receiveCurrency);
},
useSatoshis: exchangeViewModel.useSatoshisReceive,
useSatoshis: exchangeViewModel.useReceiveBaseUnit,
),
);
@@ -1,4 +1,3 @@
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/reactions/wallet_connect.dart';
import 'package:cake_wallet/routes.dart';
@@ -11,7 +10,6 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_shee
import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart';
import 'package:cake_wallet/utils/request_review_handler.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:mobx/mobx.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter/material.dart';
@@ -330,10 +328,8 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
fee: isEVMCompatibleChain(sendVM.walletType)
? S.of(bottomSheetContext).send_estimated_fee
: S.of(bottomSheetContext).send_fee,
feeValue: "${sendVM.amountParsingProxy.getDisplayCryptoAmount(
sendVM.pendingTransaction!.feeFormattedValue,
sendVM.selectedCryptoCurrency)} ${sendVM.amountParsingProxy
.getCryptoSymbol(sendVM.wallet.currency)}",
feeValue: sendVM.amountParsingProxy.asDisplayStringWithSymbol(
sendVM.pendingTransaction!.fee),
feeFiatAmount: sendVM.pendingTransactionFeeFiatAmountFormatted,
outputs: sendVM.outputs,
onSlideActionComplete: () async {
@@ -58,7 +58,8 @@ class DEuroSavingsPage extends BasePage {
Observer(
builder: (_) => SavingsCard(
interestRate: "${_dEuroViewModel.interestRateFormated}%",
savingsBalance: _dEuroViewModel.savingsBalanceFormated,
savingsBalance:
_dEuroViewModel.savingsBalance.toStringWithPrecision(fractionalDigits: 6),
fiatSavingsBalance: _dEuroViewModel.fiatSavingsBalanceFormated,
currency: CryptoCurrency.deuro,
fiatCurrency: _dEuroViewModel.isFiatDisabled ? null : _dEuroViewModel.fiat,
@@ -68,6 +69,10 @@ class DEuroSavingsPage extends BasePage {
onTooltipPressed: () => _onSavingsTooltipPressed(context),
isEnabled: _dEuroViewModel.isEnabled,
isLoading: _dEuroViewModel.isLoading,
onWithdrawV1Pressed: () => _onWithdrawV1(context),
savingsBalanceV1: _dEuroViewModel.savingsBalanceV1
?.toStringWithPrecision(fractionalDigits: 6),
fiatSavingsBalanceV1: _dEuroViewModel.fiatSavingsBalanceV1Formated,
),
),
Observer(
@@ -126,6 +131,16 @@ class DEuroSavingsPage extends BasePage {
_editSheetIsOpen = false;
}
Future<void> _onWithdrawV1(BuildContext context) async {
if (_editSheetIsOpen) return;
_editSheetIsOpen = true;
try {
await _requireHardwareWallet(context);
await _dEuroViewModel.prepareSavingsV1Withdraw();
} catch (_) {}
_editSheetIsOpen = false;
}
Future<void> _onSavingsRemove(BuildContext context) async {
if (_editSheetIsOpen) return;
_editSheetIsOpen = true;
@@ -215,7 +230,7 @@ class DEuroSavingsPage extends BasePage {
amount: S.of(bottomSheetContext).send_amount,
amountValue: _dEuroViewModel.actionType == DEuroActionType.reinvest
? _dEuroViewModel.accruedInterestFormated
: tx.amountFormatted,
: tx.amount.toStringWithSymbol(),
fiatAmountValue: _dEuroViewModel.actionType == DEuroActionType.reinvest
? _dEuroViewModel.fiatAccruedInterestFormated
: _dEuroViewModel.pendingTransactionFiatAmountFormatted,
@@ -251,7 +266,7 @@ class DEuroSavingsPage extends BasePage {
amountValue: tx.amountFormatted,
fiatAmountValue: "",
fee: S.of(bottomSheetContext).send_estimated_fee,
feeValue: tx.feeFormatted,
feeValue: tx.fee.toStringWithSymbol(),
feeFiatAmount: "",
outputs: [],
onSlideActionComplete: () {
@@ -411,8 +426,8 @@ class DEuroSavingsPage extends BasePage {
? S.of(context).deuro_savings_available_to_add
: S.of(context).deuro_savings_available_to_remove,
balance: isAdding
? _dEuroViewModel.accountBalanceFormated
: _dEuroViewModel.savingsBalanceFormated,
? _dEuroViewModel.accountBalance
: _dEuroViewModel.savingsBalance,
footerType: FooterType.none,
maxHeight: MediaQuery.of(context).size.height * 0.8,
),
@@ -23,6 +23,9 @@ class SavingsCard extends StatelessWidget {
this.isLoading = false,
this.fiatSavingsBalance,
this.fiatCurrency,
this.savingsBalanceV1,
this.fiatSavingsBalanceV1,
this.onWithdrawV1Pressed,
});
final bool isEnabled;
@@ -30,8 +33,11 @@ class SavingsCard extends StatelessWidget {
final String interestRate;
final String savingsBalance;
final String? fiatSavingsBalance;
final String? savingsBalanceV1;
final String? fiatSavingsBalanceV1;
final FiatCurrency? fiatCurrency;
final CryptoCurrency currency;
final VoidCallback? onWithdrawV1Pressed;
final VoidCallback onAddSavingsPressed;
final VoidCallback onRemoveSavingsPressed;
final VoidCallback onApproveSavingsPressed;
@@ -98,46 +104,72 @@ class SavingsCard extends StatelessWidget {
onTooltipPressed: onTooltipPressed,
),
),
if (savingsBalanceV1 != null)
Padding(
padding: const EdgeInsets.only(bottom: 20),
child: getAssetBalanceRow(
context,
title: "${S.of(context).deuro_savings_balance} V1",
amount: savingsBalanceV1!,
fiatAmount: fiatSavingsBalanceV1,
currency: currency,
fiatCurrency: fiatCurrency,
hideSymbol: true,
),
),
isLoading
? CupertinoActivityIndicator(color: Theme.of(context).colorScheme.onSurface)
: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: isEnabled
children: savingsBalanceV1 != null
? [
Expanded(
child: getButton(
context,
label: S.of(context).deuro_savings_add,
imagePath: 'assets/images/received.png',
onPressed: onAddSavingsPressed,
label: "${S.of(context).deuro_savings_remove} V1",
onPressed: onWithdrawV1Pressed ?? () {},
backgroundColor: Theme.of(context).colorScheme.primary,
color: Theme.of(context).colorScheme.onPrimary,
),
),
SizedBox(width: 12),
Expanded(
child: getButton(
context,
label: S.of(context).deuro_savings_remove,
imagePath: 'assets/images/upload.png',
onPressed: onRemoveSavingsPressed,
backgroundColor: Theme.of(context).colorScheme.surface,
color: Theme.of(context).colorScheme.onSecondaryContainer,
),
),
]
: [
Expanded(
child: getButton(
context,
label: S.of(context).deuro_savings_approve_app,
onPressed: onApproveSavingsPressed,
backgroundColor: Theme.of(context).colorScheme.primary,
color: Theme.of(context).colorScheme.onPrimary,
icon: Icons.check,
icon: Icons.arrow_downward,
),
)
],
]
: isEnabled
? [
Expanded(
child: getButton(
context,
label: S.of(context).deuro_savings_add,
imagePath: 'assets/images/received.png',
onPressed: onAddSavingsPressed,
backgroundColor: Theme.of(context).colorScheme.primary,
color: Theme.of(context).colorScheme.onPrimary,
),
),
SizedBox(width: 12),
Expanded(
child: getButton(
context,
label: S.of(context).deuro_savings_remove,
imagePath: 'assets/images/upload.png',
onPressed: onRemoveSavingsPressed,
backgroundColor: Theme.of(context).colorScheme.surface,
color: Theme.of(context).colorScheme.onSecondaryContainer,
),
),
]
: [
Expanded(
child: getButton(
context,
label: S.of(context).deuro_savings_approve_app,
onPressed: onApproveSavingsPressed,
backgroundColor: Theme.of(context).colorScheme.primary,
color: Theme.of(context).colorScheme.onPrimary,
icon: Icons.check,
),
)
],
),
],
),
@@ -264,33 +296,33 @@ class SavingsCard extends StatelessWidget {
),
],
),
SizedBox(
child: Center(
child: Column(
children: [
CakeImageWidget(
imageUrl: currency.iconPath,
height: 40,
width: 40,
errorWidget: Container(
height: 30.0,
width: 30.0,
child: Center(
child: Text(
currency.title.substring(0, min(currency.title.length, 2)),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontSize: 11,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
if (!hideSymbol)
SizedBox(
child: Center(
child: Column(
children: [
CakeImageWidget(
imageUrl: currency.iconPath,
height: 40,
width: 40,
errorWidget: Container(
height: 30.0,
width: 30.0,
child: Center(
child: Text(
currency.title.substring(0, min(currency.title.length, 2)),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontSize: 11,
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surfaceContainer,
),
),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surfaceContainer,
),
),
),
if (!hideSymbol) ...[
const SizedBox(height: 3),
Text(
currency.title,
@@ -301,11 +333,10 @@ class SavingsCard extends StatelessWidget {
height: 1,
),
),
]
],
],
),
),
),
),
],
);
}
@@ -3,11 +3,11 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/integrations/deuro/widgets/numpad.dart';
import 'package:cake_wallet/src/widgets/bottom_sheet/base_bottom_sheet_widget.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cw_core/parse_fixed.dart';
import 'package:cw_core/amount/money.dart';
import 'package:flutter/material.dart';
class SavingsEditSheet extends BaseBottomSheet {
final String? balance;
final Money? balance;
final String? balanceTitle;
const SavingsEditSheet({
@@ -32,7 +32,7 @@ class SavingsEditSheet extends BaseBottomSheet {
}
class _SavingsEditBody extends StatefulWidget {
final String? balance;
final Money? balance;
final String? balanceTitle;
const _SavingsEditBody({this.balance, this.balanceTitle});
@@ -59,7 +59,7 @@ class _SavingsEditBodyState extends State<_SavingsEditBody> {
final FocusNode _numpadFocusNode = FocusNode();
void _onPressedAll() => setState(() {
amount = widget.balance!;
amount = widget.balance!.toString();
isValid = _validate();
});
@@ -79,9 +79,12 @@ class _SavingsEditBodyState extends State<_SavingsEditBody> {
});
bool _validate() {
final amountBigInt = parseFixed(amount, 18);
final balanceBigInt = parseFixed(widget.balance!, 18);
return balanceBigInt >= amountBigInt && amountBigInt > BigInt.zero;
try {
final amountBigInt = Money.parse(amount, widget.balance!.currency);
return widget.balance! >= amountBigInt && amountBigInt.sign > 0;
} catch (_) {
return false;
}
}
@override
@@ -108,7 +111,7 @@ class _SavingsEditBodyState extends State<_SavingsEditBody> {
Divider(),
AssetBalanceRow(
title: widget.balanceTitle!,
amount: widget.balance!,
amount: widget.balance!.toStringWithSymbol(fractionalDigits: 6),
onAllPressed: _onPressedAll,
),
],
@@ -1,5 +1,6 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cw_core/amount/amount_sanitizer.dart';
import 'package:cw_core/crypto_amount_format.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -198,8 +199,7 @@ class CurrencyAmountTextField extends StatelessWidget {
),
validator: isAmountEditable ? currencyValueValidator : null,
onChanged: (value) {
var sanitized =
value.replaceAll(',', '.').withMaxDecimals(selectedCurrencyDecimals);
var sanitized = value.sanitized().withMaxDecimals(selectedCurrencyDecimals);
if (selectedCurrencyDecimals == 0) {
sanitized = sanitized.replaceAll('.', '');
@@ -1,6 +1,8 @@
import 'package:cake_wallet/core/backup_service_v3.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -18,6 +20,7 @@ class RestoreFromBackupPage extends BasePage {
final RestoreFromBackupViewModel restoreFromBackupViewModel;
final TextEditingController textEditingController;
bool _isStateReactionSet = false;
@override
String get title => S.current.restore_title_from_backup;
@@ -32,22 +35,26 @@ class RestoreFromBackupPage extends BasePage {
@override
Widget body(BuildContext context) {
reaction((_) => restoreFromBackupViewModel.state, (ExecutionState state) {
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
});
});
}
});
if (!_isStateReactionSet) {
_isStateReactionSet = true;
reaction((_) => restoreFromBackupViewModel.state, (ExecutionState state) {
if (state is FailureState) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).error,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
});
});
}
});
}
return Center(
child: ConstrainedBox(
@@ -175,6 +182,29 @@ class RestoreFromBackupPage extends BasePage {
}
try {
await restoreFromBackupViewModel.import(textEditingController.text);
} on IncompatibleBackupAppException catch (e) {
final isConfirmed = await showPopUp<bool>(
context: context,
builder: (_) {
return AlertWithTwoActions(
alertTitle: S.current.warning,
alertContent:
'This backup was created in ${e.sourceAppName}, but you are restoring it in ${e.currentAppName}. '
'Please make sure this is the correct backup file before continuing.',
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).ok,
actionLeftButton: () => Navigator.of(context).pop(false),
actionRightButton: () => Navigator.of(context).pop(true),
);
},
);
if (isConfirmed ?? false) {
await restoreFromBackupViewModel.import(
textEditingController.text,
checkBackupApp: false,
);
}
} catch (e) {
await showPopUp<void>(
context: context,
+3 -2
View File
@@ -35,6 +35,7 @@ import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cake_wallet/view_model/send/send_view_model.dart';
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
import 'package:cake_wallet/view_model/wallet_switcher_view_model.dart';
import 'package:cw_core/amount/money.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/utils/print_verbose.dart';
import 'package:cw_core/wallet_type.dart';
@@ -445,9 +446,9 @@ class SendPage extends BasePage {
}
if (sendViewModel.wallet.type == WalletType.monero) {
int amount = 0;
var amount = Money.zero(sendViewModel.wallet.currency);
for (var item in sendViewModel.outputs) {
amount += item.formattedCryptoAmount;
amount += item.cryptoAmountMoney;
}
if (monero!.needExportOutputs(sendViewModel.wallet, amount)) {
await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage,
@@ -3,7 +3,6 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/rounded_icon_button.dart';
import 'package:cake_wallet/utils/image_utill.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -11,7 +10,6 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
const CakePayTransactionSentBottomSheet({
super.key,
required this.titleText,
required this.currency,
required this.amount,
required this.amountValue,
required this.fiatAmountValue,
@@ -28,7 +26,6 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
final String titleText;
final Widget? titleIconWidget;
final CryptoCurrency currency;
final String amount;
final String amountValue;
final String fiatAmountValue;
@@ -41,32 +38,15 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
final String paymentId;
final String paymentIdValue;
TextStyle _titleStyle(BuildContext ctx) =>
Theme
.of(ctx)
.textTheme
.bodyLarge!;
TextStyle _titleStyle(BuildContext ctx) => Theme.of(ctx).textTheme.bodyLarge!;
TextStyle _valueStyle(BuildContext ctx) =>
Theme
.of(ctx)
.textTheme
.titleMedium!
.copyWith(
TextStyle _valueStyle(BuildContext ctx) => Theme.of(ctx).textTheme.titleMedium!.copyWith(
fontSize: 18,
fontWeight: FontWeight.w600,
);
TextStyle _subStyle(BuildContext ctx) =>
Theme
.of(ctx)
.textTheme
.labelSmall!
.copyWith(
color: Theme
.of(ctx)
.colorScheme
.onSurfaceVariant,
TextStyle _subStyle(BuildContext ctx) => Theme.of(ctx).textTheme.labelSmall!.copyWith(
color: Theme.of(ctx).colorScheme.onSurfaceVariant,
);
Widget _buildHeader(BuildContext ctx) =>
@@ -78,10 +58,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
height: 5,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(4),
color: Theme
.of(ctx)
.colorScheme
.onSurface,
color: Theme.of(ctx).colorScheme.onSurface,
),
),
const SizedBox(height: 20),
@@ -92,15 +69,11 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
const SizedBox(width: 6),
Text(
titleText,
style: Theme
.of(ctx)
.textTheme
.titleLarge!
.copyWith(
fontSize: 20,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
style: Theme.of(ctx).textTheme.titleLarge!.copyWith(
fontSize: 20,
fontWeight: FontWeight.w600,
decoration: TextDecoration.none,
),
),
],
),
@@ -116,7 +89,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
_StandardTile(
itemTitle: amount,
titleStyle: _titleStyle(context),
itemValue: '$amountValue ${currency.title}',
itemValue: amountValue,
itemValueStyle: _valueStyle(context),
itemSubTitle: fiatAmountValue,
itemSubTitleStyle: _subStyle(context),
@@ -143,9 +116,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
const SizedBox(height: 8),
_StandardTile(
itemTitle: paymentId + ': \n' + paymentIdValue,
titleStyle: _titleStyle(context).copyWith(
fontSize: 14
),
titleStyle: _titleStyle(context).copyWith(fontSize: 14),
itemValue: '',
itemValueStyle: _valueStyle(context),
copyButton: true,
@@ -161,28 +132,15 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
Image.asset('assets/images/envelope.png'),
const SizedBox(height: 18),
Text(
S
.of(context)
.cake_pay_card_email_delivered_message,
style: Theme
.of(context)
.textTheme
.bodyLarge,
S.of(context).cake_pay_card_email_delivered_message,
style: Theme.of(context).textTheme.bodyLarge,
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
PrimaryButton(
text: S
.of(context)
.close,
color: Theme
.of(context)
.colorScheme
.primary,
textColor: Theme
.of(context)
.colorScheme
.onPrimaryContainer,
text: S.of(context).close,
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimaryContainer,
onPressed: onClose,
),
],
@@ -194,10 +152,7 @@ class CakePayTransactionSentBottomSheet extends StatelessWidget {
@override
Widget build(BuildContext context) {
final maxHeight = MediaQuery
.of(context)
.size
.height * 0.9;
final maxHeight = MediaQuery.of(context).size.height * 0.9;
return ClipRRect(
borderRadius: const BorderRadius.vertical(top: Radius.circular(30)),
@@ -7,6 +7,7 @@ import 'package:cake_wallet/utils/address_formatter.dart';
import 'package:cake_wallet/utils/image_utill.dart';
import 'package:cake_wallet/view_model/cake_pay/cake_pay_buy_card_view_model.dart';
import 'package:cake_wallet/view_model/send/output.dart';
import 'package:cw_core/amount/amount_sanitizer.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/wallet_type.dart';
@@ -134,7 +135,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
),
StandardTile(
itemTitle: amount,
itemValue: '$amountValue ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}',
itemValue: amountValue,
itemTitleTextStyle: itemTitleTextStyle,
itemSubTitle: fiatAmountValue,
itemSubTitleTextStyle: itemSubTitleTextStyle,
@@ -167,7 +168,7 @@ class ConfirmSendingBottomSheet extends BaseBottomSheet {
final batchContactTitle =
'${index + 1}/${outputs.length} - ${contactName.isEmpty ? 'Address' : contactName}';
final _address = item.isParsedAddress ? item.extractedAddress : item.address;
final _amount = '${item.cryptoAmount.replaceAll(',', '.')} ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}';
final _amount = '${item.cryptoAmount.sanitized()} ${amountParsingProxy?.getCryptoSymbol(currency) ?? currency.title}';
return isBatchSending || (contactName.isNotEmpty && !isCakePayName)
? ExpansionAddressTile(
contactType: isOpenCryptoPay ? 'Open CryptoPay' : S.of(context).contact,
@@ -10,6 +10,7 @@ import 'package:cake_wallet/src/widgets/bottom_sheet/swap_details_bottom_sheet.d
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
import 'package:cake_wallet/utils/address_formatter.dart';
import 'package:cake_wallet/utils/debounce.dart';
import 'package:cw_core/amount/amount_sanitizer.dart';
import 'package:cw_core/currency_for_wallet_type.dart';
import 'package:cw_core/crypto_amount_format.dart';
@@ -197,7 +198,7 @@ class SwapConfirmationContentState extends State<SwapConfirmationContent> {
],
onChanged: (value) {
final sanitized = value
.replaceAll(',', '.')
.sanitized()
.withMaxDecimals(widget.exchangeViewModel.receiveCurrency.decimals);
if (sanitized != _amountController.text) {
// Update text while preserving a sane cursor position to avoid auto-selection