From c61215683c0cef52ee42e54a196fcfd6b4c851ca Mon Sep 17 00:00:00 2001 From: David Adegoke <64401859+Blazebrain@users.noreply.github.com> Date: Tue, 9 Sep 2025 01:36:39 +0100 Subject: [PATCH] Pay Anything RC Fixes (#2511) * feat: Swap flow for Pay Anything. [WIP] * feat: Pay Anything Swap Flow [WIP] * feat: Pay anything swap flow [WIP] - Add fiat values for send and receive amounts to swap details confirmation bottomsheet * feat: Pay anything swap flow [WIP] - Switch exchangeviewmodel back to factory - Expand address box to fill width * Handle second swipe to swap cta * Add fiat for receive amount * Scope Observers for bottomsheets and add validator * fix: Repeated instances issue for exchangeVM * fix: Estimated fee and balance not correctly updating after wallet switching occurs * fix: Available balance not updating properly after wallet switching * fix: Available balance and fee rate not displaying after wallet is switched in pay anything flow * fixes: Pay anything related RC fixes - Recieve fiat amount showing as 0.0 - ID overlapping when there's enough space - receive amount changing even in fixed rate mode - Invalid double error for setReceiveFiatAmount --- .../swap_confirmation_bottom_sheet.dart | 6 ++++++ .../bottom_sheet/swap_details_bottom_sheet.dart | 13 ++++++------- .../exchange/exchange_trade_view_model.dart | 8 ++++---- lib/view_model/exchange/exchange_view_model.dart | 10 +++++++--- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart index 6ac00c5e3..c11edf0ad 100644 --- a/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart +++ b/lib/src/widgets/bottom_sheet/swap_confirmation_bottom_sheet.dart @@ -180,6 +180,7 @@ class SwapConfirmationContentState extends State { hintText: 'Amount (${detectedCurrencyName})', focusNode: _amountFocus, controller: _amountController, + keyboardType: TextInputType.numberWithOptions(decimal: true), validator: (value) { return AmountValidator( isAutovalidate: true, @@ -252,6 +253,7 @@ class SwapConfirmationContentState extends State { hintText: 'Amount (${widget.exchangeViewModel.fiat.title})', focusNode: _amountFiatFocus, controller: _amountFiatController, + keyboardType: TextInputType.numberWithOptions(decimal: true), ), const SizedBox(height: 8), SwapConfirmationTextfield( @@ -418,6 +420,7 @@ class SwapConfirmationTextfield extends StatelessWidget { this.isAddress = false, this.maxLines = 1, this.validator, + this.keyboardType, }); final FocusNode focusNode; @@ -427,6 +430,8 @@ class SwapConfirmationTextfield extends StatelessWidget { final bool isAddress; final int maxLines; final String? Function(String?)? validator; + final TextInputType? keyboardType; + @override Widget build(BuildContext context) { return Container( @@ -470,6 +475,7 @@ class SwapConfirmationTextfield extends StatelessWidget { controller: controller, maxLines: maxLines, validator: validator, + keyboardType: keyboardType, ), ], ), diff --git a/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart b/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart index 7894162a1..9830d381a 100644 --- a/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart +++ b/lib/src/widgets/bottom_sheet/swap_details_bottom_sheet.dart @@ -10,7 +10,6 @@ import 'package:cake_wallet/src/widgets/standard_slide_button_widget.dart'; import 'package:cake_wallet/themes/core/material_base_theme.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; -import 'package:cake_wallet/src/widgets/bottom_sheet/confirm_sending_bottom_sheet_widget.dart'; import 'package:cake_wallet/src/widgets/bottom_sheet/info_bottom_sheet_widget.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; @@ -19,7 +18,6 @@ import 'package:cw_core/utils/print_verbose.dart'; import 'package:flutter/services.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:cake_wallet/reactions/wallet_connect.dart'; class SwapDetailsBottomSheet extends StatefulWidget { SwapDetailsBottomSheet({ @@ -289,7 +287,8 @@ class _SwapDetailsContent extends StatelessWidget { _SwapDetailsTile( label: 'You Get', value: '${trade.receiveAmount ?? '0'} ${trade.to?.title ?? ''}', - valueFiatFormatted: exchangeTradeViewModel.receiveAmountFiatFormatted, + valueFiatFormatted: exchangeTradeViewModel + .getReceiveAmountFiatFormatted(trade.receiveAmount ?? '0.0'), ), const SizedBox(height: 8), Container( @@ -330,6 +329,7 @@ class _SwapDetailsContent extends StatelessWidget { borderRadius: BorderRadius.circular(12), ), child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ @@ -346,28 +346,27 @@ class _SwapDetailsContent extends StatelessWidget { fontSize: 16, ), ), + const SizedBox(width: 8), ], ), - Spacer(), Expanded( - // flex: 4, child: GestureDetector( onTap: () { Clipboard.setData(ClipboardData(text: trade.id)); showBar(context, S.of(context).copied_to_clipboard); }, child: Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ Text( 'ID: ', style: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: 18), ), - Expanded( + Flexible( child: Text( trade.id, style: Theme.of(context).textTheme.bodyMedium!.copyWith(fontSize: 18), - softWrap: true, ), ), ], diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index 41bb67805..bc8be9228 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -127,16 +127,16 @@ abstract class ExchangeTradeViewModelBase with Store { @computed bool get isFiatDisabled => feesViewModel.isFiatDisabled; - @computed - String get receiveAmountFiatFormatted { + @action + String getReceiveAmountFiatFormatted(String receiveAmount) { var amount = '0.00'; try { - if (trade.receiveAmount?.isNotEmpty ?? false) { + if (receiveAmount.isNotEmpty) { if (fiatConversionStore.prices[trade.to] == null) return ''; amount = calculateFiatAmount( price: fiatConversionStore.prices[trade.to]!, - cryptoAmount: trade.receiveAmount, + cryptoAmount: receiveAmount, ); } } catch (_) { diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index bb820e184..047ea8397 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -24,7 +24,6 @@ import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart'; -import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart'; import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart'; @@ -449,8 +448,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with @action void setReceiveAmountFromFiat({required String fiatAmount}) { - final crypto = double.parse(fiatAmount.replaceAll(',', '.')) / - fiatConversionStore.prices[receiveCurrency]!; + final _enteredAmount = double.tryParse(fiatAmount.replaceAll(',', '.')) ?? 0.0; + final crypto = _enteredAmount / fiatConversionStore.prices[receiveCurrency]!; final receiveAmountTmp = _cryptoNumberFormat.format(crypto); if (receiveAmount != receiveAmountTmp) { changeReceiveAmount(amount: receiveAmountTmp); @@ -467,6 +466,10 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with return; } + /// For fixed-rate transactions, we don't want to recalculate receive amount + /// as it should remain exactly what the user set + if (isFixedRateMode) return; + final _enteredAmount = double.tryParse(amount.replaceAll(',', '.')) ?? 0; /// in case the best rate was not calculated yet @@ -475,6 +478,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with await calculateBestRate(); } + _cryptoNumberFormat.maximumFractionDigits = receiveMaxDigits; receiveAmount = _cryptoNumberFormat