Cw 262 better handle user exchange amount below minimum or maximum trade size (#868)
* CW-262-Better-handle-user-exchange-amount-below-minimum-or-maximum-trade-size * fix: App should compute conversion even if it's not within the limits
Adegoke David committed
Apr 20, 2023 at 02:13 UTC
7b91b0e938c6f5c3707ecf86b33dd3ecd3b1e4d5
26 files changed
+185
-12
lib/core/amount_validator.dart
+98
-2
@@ -3,17 +3,45 @@ import 'package:cake_wallet/generated/i18n.dart';
3
import 'package:cw_core/crypto_currency.dart';
4
5
class AmountValidator extends TextValidator {
6
- AmountValidator({required CryptoCurrency currency, bool isAutovalidate = false}) {
6
+ AmountValidator({
7
+ required CryptoCurrency currency,
8
+ bool isAutovalidate = false,
9
+ String? minValue,
10
+ String? maxValue,
11
+ }) {
12
symbolsAmountValidator =
13
SymbolsAmountValidator(isAutovalidate: isAutovalidate);
14
decimalAmountValidator = DecimalAmountValidator(currency: currency,isAutovalidate: isAutovalidate);
15
+
16
+ amountMinValidator = AmountMinValidator(
17
+ minValue: minValue,
18
+ isAutovalidate: isAutovalidate,
19
+ );
20
+
21
+ amountMaxValidator = AmountMaxValidator(
22
+ maxValue: maxValue,
23
+ isAutovalidate: isAutovalidate,
24
+ );
25
}
26
27
+ late final AmountMinValidator amountMinValidator;
28
+
29
+ late final AmountMaxValidator amountMaxValidator;
30
+
31
late final SymbolsAmountValidator symbolsAmountValidator;
32
33
late final DecimalAmountValidator decimalAmountValidator;
34
16
- String? call(String? value) => symbolsAmountValidator(value) ?? decimalAmountValidator(value);
35
+ String? call(String? value) {
36
+ //* Validate for Text(length, symbols, decimals etc)
37
+
38
+ final textValidation = symbolsAmountValidator(value) ?? decimalAmountValidator(value);
39
+
40
+ //* Validate for Comparison(Value greater than min and less than )
41
+ final comparisonValidation = amountMinValidator(value) ?? amountMaxValidator(value);
42
+
43
+ return textValidation ?? comparisonValidation;
44
+ }
45
}
46
47
class SymbolsAmountValidator extends TextValidator {
@@ -57,3 +85,71 @@ class AllAmountValidator extends TextValidator {
85
minLength: 0,
86
maxLength: 0);
87
}
88
+
89
+class AmountMinValidator extends Validator<String> {
90
+ final String? minValue;
91
+ final bool isAutovalidate;
92
+
93
+ AmountMinValidator({
94
+ this.minValue,
95
+ required this.isAutovalidate,
96
+ }) : super(errorMessage: S.current.error_text_input_below_minimum_limit);
97
+
98
+ @override
99
+ bool isValid(String? value) {
100
+ if (value == null || value.isEmpty) {
101
+ return isAutovalidate ? true : false;
102
+ }
103
+
104
+ if (minValue == null || minValue == "null") {
105
+ return true;
106
+ }
107
+
108
+ final valueInDouble = parseToDouble(value);
109
+ final minInDouble = parseToDouble(minValue ?? '');
110
+
111
+ if (valueInDouble == null || minInDouble == null) {
112
+ return false;
113
+ }
114
+
115
+ return valueInDouble > minInDouble;
116
+ }
117
+
118
+ double? parseToDouble(String value) {
119
+ final data = double.tryParse(value.replaceAll(',', '.'));
120
+ return data;
121
+ }
122
+}
123
+
124
+class AmountMaxValidator extends Validator<String> {
125
+ final String? maxValue;
126
+ final bool isAutovalidate;
127
+
128
+ AmountMaxValidator({
129
+ this.maxValue,
130
+ required this.isAutovalidate,
131
+ }) : super(errorMessage: S.current.error_text_input_above_maximum_limit);
132
+
133
+ @override
134
+ bool isValid(String? value) {
135
+ if (value == null || value.isEmpty) {
136
+ return isAutovalidate ? true : false;
137
+ }
138
+
139
+ if (maxValue == null || maxValue == "null") {
140
+ return true;
141
+ }
142
+
143
+ final valueInDouble = parseToDouble(value);
144
+ final maxInDouble = parseToDouble(maxValue ?? '');
145
+
146
+ if (valueInDouble == null || maxInDouble == null) {
147
+ return false;
148
+ }
149
+ return valueInDouble < maxInDouble;
150
+ }
151
+
152
+ double? parseToDouble(String value) {
153
+ return double.tryParse(value.replaceAll(',', '.'));
154
+ }
155
+}
lib/src/screens/exchange/exchange_page.dart
+22
-8
@@ -456,8 +456,7 @@ class ExchangePage extends BasePage {
456
depositAmountController.addListener(() {
457
if (depositAmountController.text != exchangeViewModel.depositAmount) {
458
_depositAmountDebounce.run(() {
459
- exchangeViewModel.changeDepositAmount(
460
- amount: depositAmountController.text);
459
+ exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
460
exchangeViewModel.isReceiveAmountEntered = false;
461
});
462
}
@@ -469,8 +468,7 @@ class ExchangePage extends BasePage {
468
receiveAmountController.addListener(() {
469
if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
470
_receiveAmountDebounce.run(() {
472
- exchangeViewModel.changeReceiveAmount(
473
- amount: receiveAmountController.text);
471
+ exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text);
472
exchangeViewModel.isReceiveAmountEntered = true;
473
});
474
}
@@ -626,8 +624,16 @@ class ExchangePage extends BasePage {
624
currencyButtonColor: Colors.transparent,
625
addressButtonsColor: Theme.of(context).focusColor!,
626
borderColor: Theme.of(context).primaryTextTheme!.bodyText1!.color!,
629
- currencyValueValidator:
630
- AmountValidator(currency: exchangeViewModel.depositCurrency),
627
+ currencyValueValidator: (value) {
628
+ return !exchangeViewModel.isFixedRateMode
629
+ ? AmountValidator(
630
+ isAutovalidate: true,
631
+ currency: exchangeViewModel.depositCurrency,
632
+ minValue: exchangeViewModel.limits.min.toString(),
633
+ maxValue: exchangeViewModel.limits.max.toString(),
634
+ ).call(value)
635
+ : null;
636
+ },
637
addressTextFieldValidator:
638
AddressValidator(type: exchangeViewModel.depositCurrency),
639
onPushPasteButton: (context) async {
@@ -668,8 +674,16 @@ class ExchangePage extends BasePage {
674
addressButtonsColor: Theme.of(context).focusColor!,
675
borderColor:
676
Theme.of(context).primaryTextTheme!.bodyText1!.decorationColor!,
671
- currencyValueValidator:
672
- AmountValidator(currency: exchangeViewModel.receiveCurrency),
677
+ currencyValueValidator: (value) {
678
+ return exchangeViewModel.isFixedRateMode
679
+ ? AmountValidator(
680
+ isAutovalidate: true,
681
+ currency: exchangeViewModel.receiveCurrency,
682
+ minValue: exchangeViewModel.limits.min.toString(),
683
+ maxValue: exchangeViewModel.limits.max.toString(),
684
+ ).call(value)
685
+ : null;
686
+ },
687
addressTextFieldValidator:
688
AddressValidator(type: exchangeViewModel.receiveCurrency),
689
onPushPasteButton: (context) async {
lib/view_model/exchange/exchange_view_model.dart
+19
-2
@@ -198,6 +198,9 @@ abstract class ExchangeViewModelBase with Store {
198
@observable
199
bool isFixedRateMode;
200
201
+ @observable
202
+ Limits limits;
203
+
204
@computed
205
SyncStatus get status => wallet.syncStatus;
206
@@ -241,8 +244,6 @@ abstract class ExchangeViewModelBase with Store {
244
245
List<CryptoCurrency> depositCurrencies;
246
244
- Limits limits;
245
-
247
NumberFormat _cryptoNumberFormat;
248
249
final SettingsStore _settingsStore;
@@ -320,6 +321,22 @@ abstract class ExchangeViewModelBase with Store {
321
.replaceAll(RegExp('\\,'), '');
322
}
323
324
+ bool checkIfInputMeetsMinOrMaxCondition(String input) {
325
+ final _enteredAmount = double.tryParse(input.replaceAll(',', '.')) ?? 0;
326
+ double minLimit = limits.min ?? 0;
327
+ double? maxLimit = limits.max;
328
+
329
+ if (_enteredAmount < minLimit) {
330
+ return false;
331
+ }
332
+
333
+ if (maxLimit != null && _enteredAmount > maxLimit) {
334
+ return false;
335
+ }
336
+
337
+ return true;
338
+ }
339
+
340
Future<void> _calculateBestRate() async {
341
final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1;
342
res/values/strings_ar.arb
+2
@@ -697,5 +697,7 @@
697
"onion_link": "رابط البصل",
698
"settings": "إعدادات",
699
"sell_monero_com_alert_content": "بيع Monero غير مدعوم حتى الآن",
700
+ "error_text_input_below_minimum_limit":" المبلغ أقل من الحد الأدنى",
701
+ "error_text_input_above_maximum_limit":"المبلغ أكبر من الحد الأقصى",
702
"show_market_place": "إظهار السوق"
703
}
res/values/strings_bg.arb
+2
@@ -698,5 +698,7 @@
698
"clearnet_link": "Clearnet връзка",
699
"onion_link": "Лукова връзка",
700
"sell_monero_com_alert_content": "Продажбата на Monero все още не се поддържа",
701
+ "error_text_input_below_minimum_limit" : "Сумата е по-малко от минималната",
702
+ "error_text_input_above_maximum_limit" : "Сумата надвишава максималната",
703
"show_market_place":"Покажи пазар"
704
}
res/values/strings_cs.arb
+2
@@ -698,5 +698,7 @@
698
"clearnet_link": "Odkaz na Clearnet",
699
"onion_link": "Cibulový odkaz",
700
"sell_monero_com_alert_content": "Prodej Monero zatím není podporován",
701
+ "error_text_input_below_minimum_limit" : "Částka je menší než minimální hodnota",
702
+ "error_text_input_above_maximum_limit" : "Částka je větší než maximální hodnota",
703
"show_market_place": "Zobrazit trh"
704
}
res/values/strings_de.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "Zwiebel-Link",
700
"settings": "Einstellungen",
701
"sell_monero_com_alert_content": "Der Verkauf von Monero wird noch nicht unterstützt",
702
+ "error_text_input_below_minimum_limit" : "Menge ist unter dem Minimum",
703
+ "error_text_input_above_maximum_limit" : "Menge ist über dem Maximum",
704
"show_market_place": "Marktplatz anzeigen"
705
}
res/values/strings_en.arb
+2
@@ -699,5 +699,7 @@
699
"edit_node": "Edit Node",
700
"settings": "Settings",
701
"sell_monero_com_alert_content": "Selling Monero is not supported yet",
702
+ "error_text_input_below_minimum_limit" : "Amount is less than the minimum",
703
+ "error_text_input_above_maximum_limit" : "Amount is more than the maximum",
704
"show_market_place" :"Show Marketplace"
705
}
res/values/strings_es.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "Enlace de cebolla",
700
"settings": "Configuraciones",
701
"sell_monero_com_alert_content": "Aún no se admite la venta de Monero",
702
+ "error_text_input_below_minimum_limit" : "La cantidad es menos que mínima",
703
+ "error_text_input_above_maximum_limit" : "La cantidad es más que el máximo",
704
"show_market_place": "Mostrar mercado"
705
}
res/values/strings_fr.arb
+2
@@ -699,5 +699,7 @@
699
"settings": "Paramètres",
700
"onion_link": "Lien .onion",
701
"sell_monero_com_alert_content": "La vente de Monero n'est pas encore prise en charge",
702
+ "error_text_input_below_minimum_limit" : "Le montant est inférieur au minimum",
703
+ "error_text_input_above_maximum_limit" : "Le montant est supérieur au maximum",
704
"show_market_place" :"Afficher la place de marché"
705
}
res/values/strings_hi.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "प्याज का लिंक",
700
"settings": "समायोजन",
701
"sell_monero_com_alert_content": "मोनेरो बेचना अभी तक समर्थित नहीं है",
702
+ "error_text_input_below_minimum_limit" : "राशि न्यूनतम से कम है",
703
+ "error_text_input_above_maximum_limit" : "राशि अधिकतम से अधिक है",
704
"show_market_place":"बाज़ार दिखाएँ"
705
}
res/values/strings_hr.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "Poveznica luka",
700
"settings": "Postavke",
701
"sell_monero_com_alert_content": "Prodaja Monera još nije podržana",
702
+ "error_text_input_below_minimum_limit" : "Iznos je manji od minimalnog",
703
+ "error_text_input_above_maximum_limit" : "Iznos je veći od maskimalnog",
704
"show_market_place" : "Prikaži tržište"
705
}
res/values/strings_id.arb
+2
@@ -680,5 +680,7 @@
680
"clearnet_link": "Tautan clearnet",
681
"onion_link": "Tautan bawang",
682
"sell_monero_com_alert_content": "Menjual Monero belum didukung",
683
+ "error_text_input_below_minimum_limit" : "Jumlah kurang dari minimal",
684
+ "error_text_input_above_maximum_limit" : "Jumlah lebih dari maksimal",
685
"show_market_place": "Tampilkan Pasar"
686
}
res/values/strings_it.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "Collegamento a cipolla",
700
"settings": "Impostazioni",
701
"sell_monero_com_alert_content": "La vendita di Monero non è ancora supportata",
702
+ "error_text_input_below_minimum_limit" : "L'ammontare è inferiore al minimo",
703
+ "error_text_input_above_maximum_limit" : "L'ammontare è superiore al massimo",
704
"show_market_place":"Mostra mercato"
705
}
res/values/strings_ja.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "オニオンリンク",
700
"settings": "設定",
701
"sell_monero_com_alert_content": "モネロの販売はまだサポートされていません",
702
+ "error_text_input_below_minimum_limit" : "金額は最小額より少ない",
703
+ "error_text_input_above_maximum_limit" : "金額は最大値を超えています",
704
"show_market_place":"マーケットプレイスを表示"
705
}
res/values/strings_ko.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "양파 링크",
700
"settings": "설정",
701
"sell_monero_com_alert_content": "지원되지 않습니다.",
702
+ "error_text_input_below_minimum_limit" : "금액이 최소보다 적습니다.",
703
+ "error_text_input_above_maximum_limit" : "금액이 최대 값보다 많습니다.",
704
"show_market_place":"마켓플레이스 표시"
705
}
res/values/strings_my.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "ကြက်သွန်လင့်",
700
"settings": "ဆက်တင်များ",
701
"sell_monero_com_alert_content": "Monero ရောင်းချခြင်းကို မပံ့ပိုးရသေးပါ။",
702
+ "error_text_input_below_minimum_limit" : "ပမာဏသည် အနိမ့်ဆုံးထက်နည်းသည်။",
703
+ "error_text_input_above_maximum_limit" : "ပမာဏသည် အများဆုံးထက် ပိုများသည်။",
704
"show_market_place":"စျေးကွက်ကိုပြသပါ။"
705
}
res/values/strings_nl.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "Ui koppeling",
700
"settings": "Instellingen",
701
"sell_monero_com_alert_content": "Het verkopen van Monero wordt nog niet ondersteund",
702
+ "error_text_input_below_minimum_limit" : "Bedrag is minder dan minimaal",
703
+ "error_text_input_above_maximum_limit" : "Bedrag is meer dan maximaal",
704
"show_market_place":"Toon Marktplaats"
705
}
res/values/strings_pl.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "Łącznik cebulowy",
700
"settings": "Ustawienia",
701
"sell_monero_com_alert_content": "Sprzedaż Monero nie jest jeszcze obsługiwana",
702
+ "error_text_input_below_minimum_limit" : "Kwota jest mniejsza niż minimalna",
703
+ "error_text_input_above_maximum_limit" : "Kwota jest większa niż maksymalna",
704
"show_market_place" : "Pokaż rynek"
705
}
res/values/strings_pt.arb
+2
@@ -698,5 +698,7 @@
698
"onion_link": "ligação de cebola",
699
"settings": "Configurações",
700
"sell_monero_com_alert_content": "A venda de Monero ainda não é suportada",
701
+ "error_text_input_below_minimum_limit" : "O valor é menor que o mínimo",
702
+ "error_text_input_above_maximum_limit" : "O valor é superior ao máximo",
703
"show_market_place":"Mostrar mercado"
704
}
res/values/strings_ru.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "Луковая ссылка",
700
"settings": "Настройки",
701
"sell_monero_com_alert_content": "Продажа Monero пока не поддерживается",
702
+ "error_text_input_below_minimum_limit" : "Сумма меньше минимальной",
703
+ "error_text_input_above_maximum_limit" : "Сумма больше максимальной",
704
"show_market_place":"Показать торговую площадку"
705
}
res/values/strings_th.arb
+2
@@ -697,5 +697,7 @@
697
"onion_link": "ลิงค์หัวหอม",
698
"settings": "การตั้งค่า",
699
"sell_monero_com_alert_content": "ยังไม่รองรับการขาย Monero",
700
+ "error_text_input_below_minimum_limit" : "จำนวนเงินน้อยกว่าขั้นต่ำ",
701
+ "error_text_input_above_maximum_limit" : "จำนวนเงินสูงกว่าค่าสูงสุด",
702
"show_market_place":"แสดงตลาดกลาง"
703
}
res/values/strings_tr.arb
+2
@@ -699,5 +699,7 @@
699
"onion_link": "soğan bağlantısı",
700
"settings": "ayarlar",
701
"sell_monero_com_alert_content": "Monero satışı henüz desteklenmiyor",
702
+ "error_text_input_below_minimum_limit" : "Miktar minimumdan daha azdır",
703
+ "error_text_input_above_maximum_limit" : "Miktar maksimumdan daha fazla",
704
"show_market_place":"Pazar Yerini Göster"
705
}
res/values/strings_uk.arb
+2
@@ -698,5 +698,7 @@
698
"onion_link": "Посилання на цибулю",
699
"settings": "Налаштування",
700
"sell_monero_com_alert_content": "Продаж Monero ще не підтримується",
701
+ "error_text_input_below_minimum_limit" : "Сума менша мінімальної",
702
+ "error_text_input_above_maximum_limit" : "Сума більше максимальної",
703
"show_market_place":"Шоу Ринок"
704
}
res/values/strings_ur.arb
+2
@@ -699,5 +699,7 @@
699
"clearnet_link": "کلیرنیٹ لنک",
700
"onion_link": "پیاز کا لنک",
701
"sell_monero_com_alert_content": "Monero فروخت کرنا ابھی تک تعاون یافتہ نہیں ہے۔",
702
+ "error_text_input_below_minimum_limit" : "رقم کم از کم سے کم ہے۔",
703
+ "error_text_input_above_maximum_limit" : "رقم زیادہ سے زیادہ سے زیادہ ہے۔",
704
"show_market_place":"بازار دکھائیں۔"
705
}
res/values/strings_zh.arb
+2
@@ -698,5 +698,7 @@
698
"onion_link": "洋葱链接",
699
"settings": "设置",
700
"sell_monero_com_alert_content": "尚不支持出售门罗币",
701
+ "error_text_input_below_minimum_limit" : "金额小于最小值",
702
+ "error_text_input_above_maximum_limit" : "金额大于最大值",
703
"show_market_place" :"显示市场"
704
}