From 388c60d016f518e270d7453adf3212ac79a79d7f Mon Sep 17 00:00:00 2001 From: julian <julian@cypherstack.com> Date: Tue, 30 May 2023 09:02:09 -0600 Subject: [PATCH] fix: various amount unit issues --- lib/pages/send_view/send_view.dart | 61 ++++++++++++---- .../transaction_fee_selection_sheet.dart | 72 +++++++++++-------- lib/pages/send_view/token_send_view.dart | 17 ++++- .../sub_widgets/desktop_fee_dropdown.dart | 9 +-- .../wallet_view/sub_widgets/desktop_send.dart | 20 ++++-- lib/utilities/amount/amount_formatter.dart | 2 + lib/utilities/amount/amount_unit.dart | 15 ++-- 7 files changed, 138 insertions(+), 58 deletions(-) diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index 6024ee05b..45494c0c3 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -30,6 +30,7 @@ import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/address_utils.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount_formatter.dart'; +import 'package:stackwallet/utilities/amount/amount_unit.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/barcode_scanner_interface.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart'; @@ -115,14 +116,25 @@ class _SendViewState extends ConsumerState<SendView> { void _cryptoAmountChanged() async { if (!_cryptoAmountChangeLock) { - final String cryptoAmount = cryptoAmountController.text; + String cryptoAmount = cryptoAmountController.text; if (cryptoAmount.isNotEmpty && cryptoAmount != "." && cryptoAmount != ",") { + if (cryptoAmount.startsWith("~")) { + cryptoAmount = cryptoAmount.substring(1); + } + if (cryptoAmount.contains(" ")) { + cryptoAmount = cryptoAmount.split(" ").first; + } + + final shift = ref.read(pAmountUnit(coin)).shift; + _amountToSend = cryptoAmount.contains(",") ? Decimal.parse(cryptoAmount.replaceFirst(",", ".")) + .shift(0 - shift) .toAmount(fractionDigits: coin.decimals) : Decimal.parse(cryptoAmount) + .shift(0 - shift) .toAmount(fractionDigits: coin.decimals); if (_cachedAmountToSend != null && _cachedAmountToSend == _amountToSend) { @@ -189,6 +201,15 @@ class _SendViewState extends ConsumerState<SendView> { late Amount _currentFee; void _setCurrentFee(String fee, bool shouldSetState) { + fee = fee.trim(); + + if (fee.startsWith("~")) { + fee = fee.substring(1); + } + if (fee.contains(" ")) { + fee = fee.split(" ").first; + } + final value = fee.contains(",") ? Decimal.parse(fee.replaceFirst(",", ".")) .toAmount(fractionDigits: coin.decimals) @@ -269,7 +290,6 @@ class _SendViewState extends ConsumerState<SendView> { break; } - final String locale = ref.read(localeServiceChangeNotifierProvider).locale; Amount fee; if (coin == Coin.monero) { MoneroTransactionPriority specialMoneroId; @@ -288,7 +308,8 @@ class _SendViewState extends ConsumerState<SendView> { fee = await manager.estimateFeeFor(amount, specialMoneroId.raw!); cachedFees[amount] = ref.read(pAmountFormatter(coin)).format( fee, - withUnitName: false, + withUnitName: true, + indicatePrecisionLoss: false, ); return cachedFees[amount]!; @@ -299,7 +320,8 @@ class _SendViewState extends ConsumerState<SendView> { cachedFiroPrivateFees[amount] = ref.read(pAmountFormatter(coin)).format( fee, - withUnitName: false, + withUnitName: true, + indicatePrecisionLoss: false, ); return cachedFiroPrivateFees[amount]!; @@ -309,7 +331,8 @@ class _SendViewState extends ConsumerState<SendView> { cachedFiroPublicFees[amount] = ref.read(pAmountFormatter(coin)).format( fee, - withUnitName: false, + withUnitName: true, + indicatePrecisionLoss: false, ); return cachedFiroPublicFees[amount]!; @@ -318,7 +341,8 @@ class _SendViewState extends ConsumerState<SendView> { fee = await manager.estimateFeeFor(amount, feeRate); cachedFees[amount] = ref.read(pAmountFormatter(coin)).format( fee, - withUnitName: false, + withUnitName: true, + indicatePrecisionLoss: false, ); return cachedFees[amount]!; @@ -340,7 +364,6 @@ class _SendViewState extends ConsumerState<SendView> { return ref.read(pAmountFormatter(coin)).format( balance, - withUnitName: false, ); } @@ -599,7 +622,15 @@ class _SendViewState extends ConsumerState<SendView> { if (_data != null) { if (_data!.amount != null) { - cryptoAmountController.text = _data!.amount!.toString(); + final amount = Amount.fromDecimal( + _data!.amount!, + fractionDigits: coin.decimals, + ); + + cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format( + amount, + withUnitName: false, + ); } sendToController.text = _data!.contactLabel; _address = _data!.address.trim(); @@ -1360,7 +1391,7 @@ class _SendViewState extends ConsumerState<SendView> { _privateBalanceString != null) { return Text( - "$_privateBalanceString ${coin.ticker}", + "$_privateBalanceString", style: STextStyles .itemSubtitle(context), ); @@ -1373,7 +1404,7 @@ class _SendViewState extends ConsumerState<SendView> { _publicBalanceString != null) { return Text( - "$_publicBalanceString ${coin.ticker}", + "$_publicBalanceString", style: STextStyles .itemSubtitle(context), ); @@ -1511,7 +1542,9 @@ class _SendViewState extends ConsumerState<SendView> { child: Padding( padding: const EdgeInsets.all(12), child: Text( - coin.ticker, + ref + .watch(pAmountUnit(coin)) + .unitForCoin(coin), style: STextStyles.smallMed14(context) .copyWith( color: Theme.of(context) @@ -1833,6 +1866,8 @@ class _SendViewState extends ConsumerState<SendView> { amount: (Decimal.tryParse( cryptoAmountController .text) ?? + _amountToSend + ?.decimal ?? Decimal.zero) .toAmount( fractionDigits: coin.decimals, @@ -1872,7 +1907,7 @@ class _SendViewState extends ConsumerState<SendView> { false, ); return Text( - "~${snapshot.data! as String} ${coin.ticker}", + "~${snapshot.data! as String}", style: STextStyles .itemSubtitle( context), @@ -1929,7 +1964,7 @@ class _SendViewState extends ConsumerState<SendView> { false, ); return Text( - "~${snapshot.data! as String} ${coin.ticker}", + "~${snapshot.data! as String}", style: STextStyles .itemSubtitle( context), diff --git a/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart b/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart index f2458349e..9fbda1576 100644 --- a/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart +++ b/lib/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart @@ -1,5 +1,4 @@ import 'package:cw_core/monero_transaction_priority.dart'; -import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/models/paymint/fee_object_model.dart'; @@ -10,6 +9,7 @@ import 'package:stackwallet/providers/wallet/public_private_balance_state_provid import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; +import 'package:stackwallet/utilities/amount/amount_formatter.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart'; @@ -325,20 +325,21 @@ class _TransactionFeeSelectionSheetState feeRate: feeObject!.fast, amount: amount, ), - // future: manager.estimateFeeFor( - // Format.decimalAmountToSatoshis( - // amount), - // feeObject!.fast), builder: (_, AsyncSnapshot<Amount> snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { return Text( - "(~${snapshot.data!.decimal.toStringAsFixed( - manager.coin.decimals, - )}" - " ${manager.coin.ticker})", + "(~${ref.watch( + pAmountFormatter( + manager.coin, + ), + ).format( + snapshot.data!, + indicatePrecisionLoss: + false, + )})", style: STextStyles.itemSubtitle( context), textAlign: TextAlign.left, @@ -461,18 +462,21 @@ class _TransactionFeeSelectionSheetState feeRate: feeObject!.medium, amount: amount, ), - // future: manager.estimateFeeFor( - // Format.decimalAmountToSatoshis( - // amount), - // feeObject!.fast), builder: (_, AsyncSnapshot<Amount> snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { return Text( - "(~${snapshot.data!.decimal.toStringAsFixed(manager.coin.decimals)}" - " ${manager.coin.ticker})", + "(~${ref.watch( + pAmountFormatter( + manager.coin, + ), + ).format( + snapshot.data!, + indicatePrecisionLoss: + false, + )})", style: STextStyles.itemSubtitle( context), textAlign: TextAlign.left, @@ -596,17 +600,21 @@ class _TransactionFeeSelectionSheetState feeRate: feeObject!.slow, amount: amount, ), - // future: manager.estimateFeeFor( - // Format.decimalAmountToSatoshis( - // amount), - // feeObject!.fast), builder: (_, AsyncSnapshot<Amount> snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { return Text( - "(~${snapshot.data!.decimal.toStringAsFixed(manager.coin.decimals)} ${manager.coin.ticker})", + "(~${ref.watch( + pAmountFormatter( + manager.coin, + ), + ).format( + snapshot.data!, + indicatePrecisionLoss: + false, + )})", style: STextStyles.itemSubtitle( context), textAlign: TextAlign.left, @@ -669,25 +677,31 @@ class _TransactionFeeSelectionSheetState switch (feeRateType) { case FeeRateType.fast: if (ref.read(feeSheetSessionCacheProvider).fast[amount] != null) { - return (ref.read(feeSheetSessionCacheProvider).fast[amount] - as Decimal) - .toStringAsFixed(coin.decimals); + return ref.read(pAmountFormatter(coin)).format( + ref.read(feeSheetSessionCacheProvider).fast[amount]!, + indicatePrecisionLoss: false, + withUnitName: false, + ); } return null; case FeeRateType.average: if (ref.read(feeSheetSessionCacheProvider).average[amount] != null) { - return (ref.read(feeSheetSessionCacheProvider).average[amount] - as Decimal) - .toStringAsFixed(coin.decimals); + return ref.read(pAmountFormatter(coin)).format( + ref.read(feeSheetSessionCacheProvider).average[amount]!, + indicatePrecisionLoss: false, + withUnitName: false, + ); } return null; case FeeRateType.slow: if (ref.read(feeSheetSessionCacheProvider).slow[amount] != null) { - return (ref.read(feeSheetSessionCacheProvider).slow[amount] - as Decimal) - .toStringAsFixed(coin.decimals); + return ref.read(pAmountFormatter(coin)).format( + ref.read(feeSheetSessionCacheProvider).slow[amount]!, + indicatePrecisionLoss: false, + withUnitName: false, + ); } return null; } diff --git a/lib/pages/send_view/token_send_view.dart b/lib/pages/send_view/token_send_view.dart index f6bc60095..4a744f372 100644 --- a/lib/pages/send_view/token_send_view.dart +++ b/lib/pages/send_view/token_send_view.dart @@ -170,6 +170,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> { cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format( amount, withUnitName: false, + indicatePrecisionLoss: false, ); _amountToSend = amount; } @@ -261,10 +262,17 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> { void _cryptoAmountChanged() async { if (!_cryptoAmountChangeLock) { - final String cryptoAmount = cryptoAmountController.text; + String cryptoAmount = cryptoAmountController.text; if (cryptoAmount.isNotEmpty && cryptoAmount != "." && cryptoAmount != ",") { + if (cryptoAmount.startsWith("~")) { + cryptoAmount = cryptoAmount.substring(1); + } + if (cryptoAmount.contains(" ")) { + cryptoAmount = cryptoAmount.split(" ").first; + } + _amountToSend = Amount.fromDecimal( cryptoAmount.contains(",") ? Decimal.parse(cryptoAmount.replaceFirst(",", ".")) @@ -361,7 +369,8 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> { final Amount fee = wallet.estimateFeeFor(feeRate); cachedFees = ref.read(pAmountFormatter(coin)).format( fee, - withUnitName: false, + withUnitName: true, + indicatePrecisionLoss: false, ); return cachedFees; @@ -684,7 +693,9 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> { .read(tokenServiceProvider)! .balance .spendable, + ethContract: tokenContract, withUnitName: false, + indicatePrecisionLoss: true, ); }, child: Container( @@ -1164,7 +1175,7 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> { ConnectionState.done && snapshot.hasData) { return Text( - "~${snapshot.data! as String} ${coin.ticker}", + "~${snapshot.data! as String}", style: STextStyles.itemSubtitle( context), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart index cabe94d81..5155bfd46 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart @@ -12,6 +12,7 @@ import 'package:stackwallet/providers/wallet/public_private_balance_state_provid import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; +import 'package:stackwallet/utilities/amount/amount_formatter.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -360,10 +361,10 @@ class FeeDropDownChild extends ConsumerWidget { children: [ Text( "${feeRateType.prettyName} " - "(~${snapshot.data!.decimal.toStringAsFixed( - manager.coin.decimals, - )} " - "${manager.coin.ticker})", + "(~${ref.watch(pAmountFormatter(manager.coin)).format( + snapshot.data!, + indicatePrecisionLoss: false, + )})", style: STextStyles.desktopTextExtraExtraSmall(context).copyWith( color: Theme.of(context) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart index ee5c740b4..eb6d9b3ec 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_send.dart @@ -28,6 +28,7 @@ import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/address_utils.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount_formatter.dart'; +import 'package:stackwallet/utilities/amount/amount_unit.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/barcode_scanner_interface.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart'; @@ -430,14 +431,25 @@ class _DesktopSendState extends ConsumerState<DesktopSend> { void _cryptoAmountChanged() async { if (!_cryptoAmountChangeLock) { - final String cryptoAmount = cryptoAmountController.text; + String cryptoAmount = cryptoAmountController.text; if (cryptoAmount.isNotEmpty && cryptoAmount != "." && cryptoAmount != ",") { + if (cryptoAmount.startsWith("~")) { + cryptoAmount = cryptoAmount.substring(1); + } + if (cryptoAmount.contains(" ")) { + cryptoAmount = cryptoAmount.split(" ").first; + } + + final shift = ref.read(pAmountUnit(coin)).shift; + _amountToSend = cryptoAmount.contains(",") ? Decimal.parse(cryptoAmount.replaceFirst(",", ".")) + .shift(0 - shift) .toAmount(fractionDigits: coin.decimals) : Decimal.parse(cryptoAmount) + .shift(0 - shift) .toAmount(fractionDigits: coin.decimals); if (_cachedAmountToSend != null && _cachedAmountToSend == _amountToSend) { @@ -527,12 +539,12 @@ class _DesktopSendState extends ConsumerState<DesktopSend> { } if (private && _privateBalanceString != null) { return Text( - "$_privateBalanceString ${coin.ticker}", + "$_privateBalanceString", style: STextStyles.itemSubtitle(context), ); } else if (!private && _publicBalanceString != null) { return Text( - "$_publicBalanceString ${coin.ticker}", + "$_publicBalanceString", style: STextStyles.itemSubtitle(context), ); } else { @@ -1043,7 +1055,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> { child: Padding( padding: const EdgeInsets.all(12), child: Text( - coin.ticker, + ref.watch(pAmountUnit(coin)).unitForCoin(coin), style: STextStyles.smallMed14(context).copyWith( color: Theme.of(context) .extension<StackColors>()! diff --git a/lib/utilities/amount/amount_formatter.dart b/lib/utilities/amount/amount_formatter.dart index 00e427e8c..3e556c95f 100644 --- a/lib/utilities/amount/amount_formatter.dart +++ b/lib/utilities/amount/amount_formatter.dart @@ -50,6 +50,7 @@ class AmountFormatter { String? overrideUnit, EthContract? ethContract, bool withUnitName = true, + bool indicatePrecisionLoss = true, }) { return unit.displayAmount( amount: amount, @@ -57,6 +58,7 @@ class AmountFormatter { coin: coin, maxDecimalPlaces: maxDecimals, withUnitName: withUnitName, + indicatePrecisionLoss: indicatePrecisionLoss, overrideUnit: overrideUnit, tokenContract: ethContract, ); diff --git a/lib/utilities/amount/amount_unit.dart b/lib/utilities/amount/amount_unit.dart index 275352f53..0e7f5eacf 100644 --- a/lib/utilities/amount/amount_unit.dart +++ b/lib/utilities/amount/amount_unit.dart @@ -114,6 +114,7 @@ extension AmountUnitExt on AmountUnit { required Coin coin, required int maxDecimalPlaces, bool withUnitName = true, + bool indicatePrecisionLoss = true, String? overrideUnit, EthContract? tokenContract, }) { @@ -192,7 +193,15 @@ extension AmountUnitExt on AmountUnit { returnValue += "$separator$remainder"; } - if (!withUnitName) { + if (!withUnitName && !indicatePrecisionLoss) { + return returnValue; + } + + if (didLosePrecision && indicatePrecisionLoss) { + returnValue = "~$returnValue"; + } + + if (!withUnitName && indicatePrecisionLoss) { return returnValue; } @@ -201,10 +210,6 @@ extension AmountUnitExt on AmountUnit { overrideUnit = unitForContract(tokenContract); } - if (didLosePrecision) { - returnValue = "~$returnValue"; - } - return "$returnValue ${overrideUnit ?? unitForCoin(coin)}"; } }