Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 61 additions & 11 deletions lib/pages/buy_view/buy_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import '../../services/buy/buy_response.dart';
import '../../services/buy/simplex/simplex_api.dart';
import '../../themes/stack_colors.dart';
import '../../utilities/address_utils.dart';
import '../../utilities/amount/amount.dart';
import '../../utilities/amount/amount_formatter.dart';
import '../../utilities/amount/amount_input_formatter.dart';
import '../../utilities/assets.dart';
import '../../utilities/barcode_scanner_interface.dart';
import '../../utilities/clipboard_interface.dart';
Expand Down Expand Up @@ -124,6 +127,39 @@ class _BuyFormState extends ConsumerState<BuyForm> {
// static Decimal maxCrypto = Decimal.parse((10000.00000000).toString());
// static String boundedCryptoTicker = '';

/// Parse a buy amount string using the active locale's decimal and group
/// separators. Fiat amounts are parsed via [Amount.tryParseFiatString];
/// crypto amounts reuse the locale-aware [pAmountFormatter] parser so that
/// full crypto precision is preserved.
Decimal? _tryParseBuyAmount(String value) {
if (buyWithFiat) {
return Amount.tryParseFiatString(
value,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
)?.decimal;
}

final cc = coin;
if (cc != null) {
return ref.read(pAmountFormatter(cc)).tryParse(value)?.decimal;
}

// Fall back to fiat-style locale parsing when no coin is available.
return Amount.tryParseFiatString(
value,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
)?.decimal;
}

AmountInputFormatter _buyAmountInputFormatter() {
final cc = coin;
return AmountInputFormatter(
decimals: buyWithFiat ? 2 : cc?.fractionDigits ?? 8,
locale: ref.read(localeServiceChangeNotifierProvider).locale,
unit: buyWithFiat || cc == null ? null : ref.read(pAmountUnit(cc)),
);
}

String _amountOutOfRangeErrorString = "";
void validateAmount() {
if (_buyAmountController.text.isEmpty) {
Expand All @@ -133,7 +169,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
return;
}

final value = Decimal.tryParse(_buyAmountController.text);
final value = _tryParseBuyAmount(_buyAmountController.text);
if (value == null) {
setState(() {
_amountOutOfRangeErrorString = "Invalid amount";
Expand Down Expand Up @@ -396,6 +432,12 @@ class _BuyFormState extends ConsumerState<BuyForm> {
// }

Future<void> previewQuote(SimplexQuote quote) async {
final amount = _tryParseBuyAmount(_buyAmountController.text);
if (amount == null) {
validateAmount();
return;
}

bool shouldPop = false;
unawaited(
showDialog(
Expand All @@ -414,11 +456,11 @@ class _BuyFormState extends ConsumerState<BuyForm> {
crypto: selectedCrypto!,
fiat: selectedFiat!,
youPayFiatPrice: buyWithFiat
? Decimal.parse(_buyAmountController.text)
? amount
: Decimal.parse("100"), // dummy value
youReceiveCryptoAmount: buyWithFiat
? Decimal.parse("0.000420282") // dummy value
: Decimal.parse(_buyAmountController.text), // Ternary for this
: amount,
id: "id", // anything; we get an ID back
receivingAddress: _receiveAddressController.text,
buyWithFiat: buyWithFiat,
Expand Down Expand Up @@ -1013,7 +1055,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
decimal: true,
),
textAlign: TextAlign.left,
// inputFormatters: [NumericalRangeFormatter()],
inputFormatters: [_buyAmountInputFormatter()],
onChanged: (_) {
validateAmount();
},
Expand Down Expand Up @@ -1123,13 +1165,21 @@ class _BuyFormState extends ConsumerState<BuyForm> {
final ClipboardData? data = await clipboard
.getData(Clipboard.kTextPlain);

final amountString = Decimal.tryParse(
data?.text ?? "",
);
if (amountString != null) {
_buyAmountController.text = amountString
.toString();

final pasted = data?.text ?? "";
final formatted = _buyAmountInputFormatter()
.formatEditUpdate(
TextEditingValue.empty,
TextEditingValue(
text: pasted,
selection: TextSelection.collapsed(
offset: pasted.length,
),
),
);
if (formatted.text.isNotEmpty &&
_tryParseBuyAmount(formatted.text) !=
null) {
_buyAmountController.value = formatted;
validateAmount();
}
},
Expand Down
9 changes: 5 additions & 4 deletions lib/pages/send_view/send_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1277,10 +1277,11 @@ class _SendViewState extends ConsumerState<SendView> {
builder: (_) => TransactionFeeSelectionSheet(
walletId: walletId,
amount:
(Decimal.tryParse(cryptoAmountController.text) ??
ref.watch(pSendAmount)?.decimal ??
Decimal.zero)
.toAmount(fractionDigits: coin.fractionDigits),
ref
.read(pAmountFormatter(coin))
.tryParse(cryptoAmountController.text) ??
ref.watch(pSendAmount) ??
Amount.zeroWith(fractionDigits: coin.fractionDigits),
updateChosen: (String fee) {
if (fee == "custom") {
if (!isCustomFee.value) {
Expand Down
33 changes: 24 additions & 9 deletions lib/pages/send_view/sol_token_send_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,11 @@ class _SolTokenSendViewState extends ConsumerState<SolTokenSendView> {
final tokenWallet = ref.read(pCurrentSolanaTokenWallet);
if (tokenWallet == null) return;

final cryptoAmount = Decimal.tryParse(
cryptoAmountController.text,
)?.toAmount(fractionDigits: tokenWallet.tokenDecimals);
final cryptoAmount = ref
.read(pAmountFormatter(Solana(CryptoCurrencyNetwork.main)))
.tryParse(cryptoAmountController.text)
?.decimal
.toAmount(fractionDigits: tokenWallet.tokenDecimals);
if (cryptoAmount != null) {
_amountToSend = cryptoAmount;
if (_cachedAmountToSend != null &&
Expand Down Expand Up @@ -1258,16 +1260,29 @@ class _SolTokenSendViewState extends ConsumerState<SolTokenSendView> {
walletId: walletId,
isToken: true,
amount:
(Decimal.tryParse(
cryptoAmountController
.text,
) ??
Decimal.zero)
ref
.read(
pAmountFormatter(
Solana(
CryptoCurrencyNetwork
.main,
),
),
)
.tryParse(
cryptoAmountController
.text,
)
?.decimal
.toAmount(
fractionDigits:
tokenWallet
.tokenDecimals,
),
) ??
Amount.zeroWith(
fractionDigits: tokenWallet
.tokenDecimals,
),
updateChosen: (String fee) {
setState(() {
_calculateFeesFuture = Future(
Expand Down
24 changes: 14 additions & 10 deletions lib/pages/send_view/token_send_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1200,16 +1200,20 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
walletId: walletId,
isToken: true,
amount:
(Decimal.tryParse(
cryptoAmountController
.text,
) ??
Decimal.zero)
.toAmount(
fractionDigits:
tokenContract
.decimals,
),
ref
.read(
pAmountFormatter(coin),
)
.tryParse(
cryptoAmountController
.text,
tokenContract:
tokenContract,
) ??
Amount.zeroWith(
fractionDigits:
tokenContract.decimals,
),
updateChosen: (String fee) {
if (fee == "custom") {
if (!isCustomFee.value) {
Expand Down
Loading
Loading