Skip to content

Commit 9ee72f1

Browse files
committed
refactor: replace manual amount validation with useValidateAmount hook in TokenCard component
1 parent da4d9b4 commit 9ee72f1

2 files changed

Lines changed: 104 additions & 71 deletions

File tree

lib/app/features/wallets/views/pages/coins_flow/swap_coins/components/token_card.dart

Lines changed: 11 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import 'package:ion/app/extensions/extensions.dart';
99
import 'package:ion/app/features/wallets/model/coin_in_wallet_data.f.dart';
1010
import 'package:ion/app/features/wallets/model/coins_group.f.dart';
1111
import 'package:ion/app/features/wallets/model/network_data.f.dart';
12-
import 'package:ion/app/features/wallets/utils/crypto_amount_converter.dart';
1312
import 'package:ion/app/features/wallets/views/components/coin_icon_with_network.dart';
1413
import 'package:ion/app/features/wallets/views/pages/coins_flow/swap_coins/components/sum_percentage_action.dart';
1514
import 'package:ion/app/features/wallets/views/pages/coins_flow/swap_coins/enums/coin_swap_type.dart';
15+
import 'package:ion/app/features/wallets/views/pages/coins_flow/swap_coins/hooks/use_validate_amount.dart';
1616
import 'package:ion/app/features/wallets/views/pages/coins_flow/swap_coins/providers/swap_coins_controller_provider.r.dart';
1717
import 'package:ion/app/features/wallets/views/pages/coins_flow/swap_coins/utils/swap_constants.dart';
1818
import 'package:ion/app/features/wallets/views/utils/amount_parser.dart';
@@ -68,39 +68,6 @@ class TokenCard extends HookConsumerWidget {
6868
ref.read(swapCoinsControllerProvider.notifier).setAmount(newAmount);
6969
}
7070

71-
String? _validateAmount(
72-
String? value,
73-
BuildContext context,
74-
CoinInWalletData? coinForNetwork,
75-
) {
76-
if (skipValidation) return null;
77-
78-
final trimmedValue = value?.trim() ?? '';
79-
if (trimmedValue.isEmpty) return null;
80-
81-
final parsed = parseAmount(trimmedValue);
82-
if (parsed == null) return '';
83-
84-
final maxValue = coinForNetwork?.amount;
85-
if (maxValue != null && (parsed > maxValue || parsed < 0)) {
86-
final abbreviation = coinsGroup?.abbreviation ?? '';
87-
return '${context.i18n.wallet_coin_amount_insufficient} $abbreviation';
88-
} else if (parsed < 0) {
89-
return context.i18n.wallet_coin_amount_must_be_positive;
90-
}
91-
92-
// If we know decimals for the selected network, enforce min amount check
93-
final decimals = coinForNetwork?.coin.decimals;
94-
if (decimals != null) {
95-
final amount = toBlockchainUnits(parsed, decimals);
96-
if (amount == BigInt.zero && parsed > 0) {
97-
return context.i18n.wallet_coin_amount_too_low_for_sending;
98-
}
99-
}
100-
101-
return null;
102-
}
103-
10471
@override
10572
Widget build(BuildContext context, WidgetRef ref) {
10673
final colors = context.theme.appColors;
@@ -123,6 +90,16 @@ class TokenCard extends HookConsumerWidget {
12390
[controller?.text, coinForNetwork?.coin.priceUSD],
12491
);
12592

93+
useValidateAmount(
94+
controller: controller,
95+
focusNode: focusNode,
96+
coinForNetwork: coinForNetwork,
97+
coinsGroup: coinsGroup,
98+
onValidationError: onValidationError,
99+
context: context,
100+
skipValidation: skipValidation,
101+
);
102+
126103
useEffect(
127104
() {
128105
void formatAmount() {
@@ -155,43 +132,6 @@ class TokenCard extends HookConsumerWidget {
155132
[focusNode, controller, isReadOnly, skipAmountFormatting, coinForNetwork],
156133
);
157134

158-
useEffect(
159-
() {
160-
void validateAndNotify() {
161-
if (onValidationError == null) return;
162-
163-
final error = _validateAmount(
164-
controller?.text,
165-
context,
166-
coinForNetwork,
167-
);
168-
onValidationError!(error);
169-
}
170-
171-
void onTextChanged() {
172-
validateAndNotify();
173-
}
174-
175-
void onFocusChanged() {
176-
validateAndNotify();
177-
}
178-
179-
controller?.addListener(onTextChanged);
180-
focusNode.addListener(onFocusChanged);
181-
182-
// Validate initially
183-
WidgetsBinding.instance.addPostFrameCallback((_) {
184-
validateAndNotify();
185-
});
186-
187-
return () {
188-
controller?.removeListener(onTextChanged);
189-
focusNode.removeListener(onFocusChanged);
190-
};
191-
},
192-
[controller, focusNode, coinForNetwork, onValidationError, context, skipValidation],
193-
);
194-
195135
return Container(
196136
margin: EdgeInsets.symmetric(
197137
horizontal: 16.0.s,
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_hooks/flutter_hooks.dart';
3+
import 'package:ion/app/extensions/extensions.dart';
4+
import 'package:ion/app/features/wallets/model/coin_in_wallet_data.f.dart';
5+
import 'package:ion/app/features/wallets/model/coins_group.f.dart';
6+
import 'package:ion/app/features/wallets/utils/crypto_amount_converter.dart';
7+
import 'package:ion/app/features/wallets/views/utils/amount_parser.dart';
8+
9+
void useValidateAmount({
10+
required TextEditingController? controller,
11+
required FocusNode focusNode,
12+
required CoinInWalletData? coinForNetwork,
13+
required BuildContext context,
14+
required ValueChanged<String?>? onValidationError,
15+
required CoinsGroup? coinsGroup,
16+
required bool skipValidation,
17+
}) {
18+
useEffect(
19+
() {
20+
void validateAndNotify() {
21+
if (onValidationError == null || skipValidation) return;
22+
23+
final error = _validateAmount(
24+
controller?.text,
25+
context,
26+
coinForNetwork,
27+
coinsGroup,
28+
);
29+
onValidationError(error);
30+
}
31+
32+
void onTextChanged() {
33+
validateAndNotify();
34+
}
35+
36+
void onFocusChanged() {
37+
validateAndNotify();
38+
}
39+
40+
controller?.addListener(onTextChanged);
41+
focusNode.addListener(onFocusChanged);
42+
43+
// Validate initially
44+
WidgetsBinding.instance.addPostFrameCallback((_) {
45+
validateAndNotify();
46+
});
47+
48+
return () {
49+
controller?.removeListener(onTextChanged);
50+
focusNode.removeListener(onFocusChanged);
51+
};
52+
},
53+
[
54+
controller,
55+
focusNode,
56+
coinForNetwork,
57+
onValidationError,
58+
coinsGroup,
59+
],
60+
);
61+
}
62+
63+
String? _validateAmount(
64+
String? value,
65+
BuildContext context,
66+
CoinInWalletData? coinForNetwork,
67+
CoinsGroup? coinsGroup,
68+
) {
69+
final trimmedValue = value?.trim() ?? '';
70+
if (trimmedValue.isEmpty) return null;
71+
72+
final parsed = parseAmount(trimmedValue);
73+
if (parsed == null) return '';
74+
75+
final maxValue = coinForNetwork?.amount;
76+
if (maxValue != null && (parsed > maxValue || parsed < 0)) {
77+
final abbreviation = coinsGroup?.abbreviation ?? '';
78+
return '${context.i18n.wallet_coin_amount_insufficient} $abbreviation';
79+
} else if (parsed < 0) {
80+
return context.i18n.wallet_coin_amount_must_be_positive;
81+
}
82+
83+
// If we know decimals for the selected network, enforce min amount check
84+
final decimals = coinForNetwork?.coin.decimals;
85+
if (decimals != null) {
86+
final amount = toBlockchainUnits(parsed, decimals);
87+
if (amount == BigInt.zero && parsed > 0) {
88+
return context.i18n.wallet_coin_amount_too_low_for_sending;
89+
}
90+
}
91+
92+
return null;
93+
}

0 commit comments

Comments
 (0)