Skip to content

Commit

Permalink
fix(EWM-260): allow comma and dot as a decimal separator in the amoun…
Browse files Browse the repository at this point in the history
…t input (#469)

Co-authored-by: Egor Komarov <[email protected]>
  • Loading branch information
Odrin and Egor Komarov authored Sep 4, 2024
1 parent cf51d2e commit 794c130
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ class WalletPrepareTransferPageWidgetModel extends CustomWidgetModel<
}

final amnt = Fixed.parse(
amountController.text.trim(),
amountController.text.trim().replaceAll(',', '.'),
scale: _selectedAsset?.balance.decimalDigits,
);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/data/wallet_prepare_transfer_asset.dart';
import 'package:app/feature/wallet/wallet_prepare_transfer/wallet_prepare_transfer_page/widgets/wallet_prepare_transfer_asset_select.dart';
import 'package:app/generated/generated.dart';
import 'package:app/utils/utils.dart';
import 'package:auto_size_text_field/auto_size_text_field.dart';
import 'package:flutter/material.dart';
import 'package:string_extensions/string_extensions.dart';
import 'package:ui_components_lib/ui_components_lib.dart';
import 'package:ui_components_lib/v2/ui_components_lib_v2.dart';

class WalletPrepareTransferAmountInput extends StatelessWidget {
class WalletPrepareTransferAmountInput extends StatefulWidget {
const WalletPrepareTransferAmountInput({
required this.focusNode,
required this.controller,
Expand All @@ -33,10 +34,34 @@ class WalletPrepareTransferAmountInput extends StatelessWidget {

final ValueChanged<String> onSubmitted;

@override
State<WalletPrepareTransferAmountInput> createState() =>
_WalletPrepareTransferAmountInputState();
}

class _WalletPrepareTransferAmountInputState
extends State<WalletPrepareTransferAmountInput> {
CurrencyTextInputFormatter? formatter;
CurrencyTextInputValidator? validator;

@override
void initState() {
_updateFormatterValidator();
super.initState();
}

@override
void didUpdateWidget(covariant WalletPrepareTransferAmountInput oldWidget) {
if (oldWidget.selectedAsset != widget.selectedAsset) {
_updateFormatterValidator();
}
super.didUpdateWidget(oldWidget);
}

@override
Widget build(BuildContext context) {
final theme = context.themeStyleV2;
final balance = selectedAsset?.balance.amount.toString() ?? '';
final balance = widget.selectedAsset?.balance.amount.toString() ?? '';

return Container(
width: double.maxFinite,
Expand All @@ -61,9 +86,9 @@ class WalletPrepareTransferAmountInput extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
WalletPrepareTransferAssetSelect(
values: assets ?? [],
currentValue: selectedAsset,
onChanged: onSelectedAssetChanged,
values: widget.assets ?? [],
currentValue: widget.selectedAsset,
onChanged: widget.onSelectedAssetChanged,
),
Text(
LocaleKeys.balance.tr(args: [balance]),
Expand All @@ -81,7 +106,7 @@ class WalletPrepareTransferAmountInput extends StatelessWidget {
title: LocaleKeys.maxWord.tr(),
buttonShape: ButtonShape.rectangle,
buttonSize: ButtonSize.small,
onPressed: onMaxAmount,
onPressed: widget.onMaxAmount,
),
],
),
Expand All @@ -94,11 +119,11 @@ class WalletPrepareTransferAmountInput extends StatelessWidget {
),
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: focusNode.requestFocus,
onTap: widget.focusNode.requestFocus,
child: FormField<String>(
initialValue: controller.text,
initialValue: widget.controller.text,
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: _validator,
validator: validator?.validate,
builder: _fieldBuilder,
),
),
Expand All @@ -114,7 +139,7 @@ class WalletPrepareTransferAmountInput extends StatelessWidget {
color:
state.hasError ? theme.colors.contentNegative : theme.colors.content0,
);
final price = Fixed.parse(selectedAsset?.currency?.price ?? '0');
final price = Fixed.parse(widget.selectedAsset?.currency?.price ?? '0');
final amount = Fixed.parse(state.value.nullIf('') ?? '0');
final usd = Fixed.copyWith(amount * price, scale: 2);

Expand All @@ -125,8 +150,8 @@ class WalletPrepareTransferAmountInput extends StatelessWidget {
children: [
Flexible(
child: AutoSizeTextField(
controller: controller,
focusNode: focusNode,
controller: widget.controller,
focusNode: widget.focusNode,
style: inputStyle,
decoration: InputDecoration(
border: InputBorder.none,
Expand All @@ -138,17 +163,14 @@ class WalletPrepareTransferAmountInput extends StatelessWidget {
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
inputFormatters: [
if (selectedAsset != null)
CurrencyTextInputFormatter(selectedAsset!.balance.currency),
],
inputFormatters: formatter?.let((formatter) => [formatter]),
fullwidth: false,
onChanged: state.didChange,
onSubmitted: onSubmitted,
onSubmitted: widget.onSubmitted,
),
),
Text(
selectedAsset?.tokenSymbol ?? '',
widget.selectedAsset?.tokenSymbol ?? '',
style: theme.textStyles.headingLarge.copyWith(
color: theme.colors.content3,
),
Expand All @@ -173,13 +195,21 @@ class WalletPrepareTransferAmountInput extends StatelessWidget {
);
}

String? _validator(String? value) => selectedAsset == null
? null
: CurrencyTextInputValidator(
selectedAsset!.balance.currency,
emptyError: LocaleKeys.amountIsEmpty.tr(),
error: LocaleKeys.amountIsWrong.tr(),
max: selectedAsset!.balance.amount,
maxError: LocaleKeys.insufficientFunds.tr(),
).validate(value);
void _updateFormatterValidator() {
if (widget.selectedAsset == null) {
formatter = null;
validator = null;
} else {
final (f, v) = createCurrencyTextInputFormatterValidator(
widget.selectedAsset!.balance.currency,
emptyError: LocaleKeys.amountIsEmpty.tr(),
error: LocaleKeys.amountIsWrong.tr(),
max: widget.selectedAsset!.balance.amount,
maxError: LocaleKeys.insufficientFunds.tr(),
decimalSeparators: ['.', ','],
);
formatter = f;
validator = v;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,19 @@ class CurrencyTextInputFormatter extends TextInputFormatter {
/// [allowNegative] determines whether negative values are allowed.
/// [includeTicker] determines whether the currency ticker is included in the
/// validation.
/// [decimalSeparators] allows to pass a list of valid decimal separators
CurrencyTextInputFormatter(
this.currency, {
this.allowNegative = false,
this.includeTicker = false,
this.decimalSeparators,
}) {
_tickerString = includeTicker ? ' ${currency.isoCode}' : '';
_fullRegExp = createRegExp(
currency: currency,
allowNegative: allowNegative,
includeTicker: includeTicker,
decimalSeparators: decimalSeparators,
);
}

Expand All @@ -29,12 +32,14 @@ class CurrencyTextInputFormatter extends TextInputFormatter {
validator.currency,
allowNegative: validator.allowNegative,
includeTicker: validator.includeTicker,
decimalSeparators: validator.decimalSeparators,
);
}

final Currency currency;
final bool allowNegative;
final bool includeTicker;
final List<String>? decimalSeparators;

late final String _tickerString;
late final RegExp _fullRegExp;
Expand All @@ -43,9 +48,12 @@ class CurrencyTextInputFormatter extends TextInputFormatter {
required Currency currency,
required bool allowNegative,
required bool includeTicker,
List<String>? decimalSeparators,
}) {
final scale = currency.decimalDigits;
final decimalSeparator = scale > 0 ? currency.decimalSeparator : '';
final decimalSeparator = scale > 0
? (decimalSeparators ?? [currency.decimalSeparator]).join('|')
: '';
final tickerString = includeTicker ? ' ${currency.isoCode}' : '';

final minusSignExp = allowNegative ? '(?<minus>-?)' : '(?<minus>)';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class CurrencyTextInputValidator {
this.minError,
this.max,
this.maxError,
this.decimalSeparators,
}) : assert(
min != null && minError != null || min == null && minError == null,
'minError must be provided when min is provided',
Expand All @@ -40,6 +41,7 @@ class CurrencyTextInputValidator {
currency: currency,
allowNegative: allowNegative,
includeTicker: includeTicker,
decimalSeparators: decimalSeparators,
);
}

Expand Down Expand Up @@ -72,6 +74,7 @@ class CurrencyTextInputValidator {
minError: minError,
max: max,
maxError: maxError,
decimalSeparators: formatter.decimalSeparators,
);
}

Expand All @@ -84,6 +87,7 @@ class CurrencyTextInputValidator {
final String? minError;
final Fixed? max;
final String? maxError;
final List<String>? decimalSeparators;

late final RegExp _fullRegExp;
late int _scale;
Expand Down Expand Up @@ -149,6 +153,7 @@ class CurrencyTextInputValidator {
String? minError,
Fixed? max,
String? maxError,
List<String>? decimalSeparators,
}) {
final validator = CurrencyTextInputValidator(
currency,
Expand All @@ -160,6 +165,7 @@ class CurrencyTextInputValidator {
minError: minError,
max: max,
maxError: maxError,
decimalSeparators: decimalSeparators,
);
final formatter = CurrencyTextInputFormatter.fromValidator(validator);

Expand Down

0 comments on commit 794c130

Please sign in to comment.