From 7b91b0e938c6f5c3707ecf86b33dd3ecd3b1e4d5 Mon Sep 17 00:00:00 2001 From: Adegoke David <64401859+Blazebrain@users.noreply.github.com> Date: Thu, 20 Apr 2023 02:13:37 +0100 Subject: [PATCH] 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 --- lib/core/amount_validator.dart | 100 +++++++++++++++++- lib/src/screens/exchange/exchange_page.dart | 30 ++++-- .../exchange/exchange_view_model.dart | 21 +++- res/values/strings_ar.arb | 2 + res/values/strings_bg.arb | 2 + res/values/strings_cs.arb | 2 + res/values/strings_de.arb | 2 + res/values/strings_en.arb | 2 + res/values/strings_es.arb | 2 + res/values/strings_fr.arb | 2 + res/values/strings_hi.arb | 2 + res/values/strings_hr.arb | 2 + res/values/strings_id.arb | 2 + res/values/strings_it.arb | 2 + res/values/strings_ja.arb | 2 + res/values/strings_ko.arb | 2 + res/values/strings_my.arb | 2 + res/values/strings_nl.arb | 2 + res/values/strings_pl.arb | 2 + res/values/strings_pt.arb | 2 + res/values/strings_ru.arb | 2 + res/values/strings_th.arb | 2 + res/values/strings_tr.arb | 2 + res/values/strings_uk.arb | 2 + res/values/strings_ur.arb | 2 + res/values/strings_zh.arb | 2 + 26 files changed, 185 insertions(+), 12 deletions(-) diff --git a/lib/core/amount_validator.dart b/lib/core/amount_validator.dart index 42eb3c32d..acd0ab135 100644 --- a/lib/core/amount_validator.dart +++ b/lib/core/amount_validator.dart @@ -3,17 +3,45 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cw_core/crypto_currency.dart'; class AmountValidator extends TextValidator { - AmountValidator({required CryptoCurrency currency, bool isAutovalidate = false}) { + AmountValidator({ + required CryptoCurrency currency, + bool isAutovalidate = false, + String? minValue, + String? maxValue, + }) { symbolsAmountValidator = SymbolsAmountValidator(isAutovalidate: isAutovalidate); decimalAmountValidator = DecimalAmountValidator(currency: currency,isAutovalidate: isAutovalidate); + + amountMinValidator = AmountMinValidator( + minValue: minValue, + isAutovalidate: isAutovalidate, + ); + + amountMaxValidator = AmountMaxValidator( + maxValue: maxValue, + isAutovalidate: isAutovalidate, + ); } + late final AmountMinValidator amountMinValidator; + + late final AmountMaxValidator amountMaxValidator; + late final SymbolsAmountValidator symbolsAmountValidator; late final DecimalAmountValidator decimalAmountValidator; - String? call(String? value) => symbolsAmountValidator(value) ?? decimalAmountValidator(value); + String? call(String? value) { + //* Validate for Text(length, symbols, decimals etc) + + final textValidation = symbolsAmountValidator(value) ?? decimalAmountValidator(value); + + //* Validate for Comparison(Value greater than min and less than ) + final comparisonValidation = amountMinValidator(value) ?? amountMaxValidator(value); + + return textValidation ?? comparisonValidation; + } } class SymbolsAmountValidator extends TextValidator { @@ -57,3 +85,71 @@ class AllAmountValidator extends TextValidator { minLength: 0, maxLength: 0); } + +class AmountMinValidator extends Validator { + final String? minValue; + final bool isAutovalidate; + + AmountMinValidator({ + this.minValue, + required this.isAutovalidate, + }) : super(errorMessage: S.current.error_text_input_below_minimum_limit); + + @override + bool isValid(String? value) { + if (value == null || value.isEmpty) { + return isAutovalidate ? true : false; + } + + if (minValue == null || minValue == "null") { + return true; + } + + final valueInDouble = parseToDouble(value); + final minInDouble = parseToDouble(minValue ?? ''); + + if (valueInDouble == null || minInDouble == null) { + return false; + } + + return valueInDouble > minInDouble; + } + + double? parseToDouble(String value) { + final data = double.tryParse(value.replaceAll(',', '.')); + return data; + } +} + +class AmountMaxValidator extends Validator { + final String? maxValue; + final bool isAutovalidate; + + AmountMaxValidator({ + this.maxValue, + required this.isAutovalidate, + }) : super(errorMessage: S.current.error_text_input_above_maximum_limit); + + @override + bool isValid(String? value) { + if (value == null || value.isEmpty) { + return isAutovalidate ? true : false; + } + + if (maxValue == null || maxValue == "null") { + return true; + } + + final valueInDouble = parseToDouble(value); + final maxInDouble = parseToDouble(maxValue ?? ''); + + if (valueInDouble == null || maxInDouble == null) { + return false; + } + return valueInDouble < maxInDouble; + } + + double? parseToDouble(String value) { + return double.tryParse(value.replaceAll(',', '.')); + } +} diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 99eeae7dc..310e40cd8 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -456,8 +456,7 @@ class ExchangePage extends BasePage { depositAmountController.addListener(() { if (depositAmountController.text != exchangeViewModel.depositAmount) { _depositAmountDebounce.run(() { - exchangeViewModel.changeDepositAmount( - amount: depositAmountController.text); + exchangeViewModel.changeDepositAmount(amount: depositAmountController.text); exchangeViewModel.isReceiveAmountEntered = false; }); } @@ -469,8 +468,7 @@ class ExchangePage extends BasePage { receiveAmountController.addListener(() { if (receiveAmountController.text != exchangeViewModel.receiveAmount) { _receiveAmountDebounce.run(() { - exchangeViewModel.changeReceiveAmount( - amount: receiveAmountController.text); + exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text); exchangeViewModel.isReceiveAmountEntered = true; }); } @@ -626,8 +624,16 @@ class ExchangePage extends BasePage { currencyButtonColor: Colors.transparent, addressButtonsColor: Theme.of(context).focusColor!, borderColor: Theme.of(context).primaryTextTheme!.bodyText1!.color!, - currencyValueValidator: - AmountValidator(currency: exchangeViewModel.depositCurrency), + currencyValueValidator: (value) { + return !exchangeViewModel.isFixedRateMode + ? AmountValidator( + isAutovalidate: true, + currency: exchangeViewModel.depositCurrency, + minValue: exchangeViewModel.limits.min.toString(), + maxValue: exchangeViewModel.limits.max.toString(), + ).call(value) + : null; + }, addressTextFieldValidator: AddressValidator(type: exchangeViewModel.depositCurrency), onPushPasteButton: (context) async { @@ -668,8 +674,16 @@ class ExchangePage extends BasePage { addressButtonsColor: Theme.of(context).focusColor!, borderColor: Theme.of(context).primaryTextTheme!.bodyText1!.decorationColor!, - currencyValueValidator: - AmountValidator(currency: exchangeViewModel.receiveCurrency), + currencyValueValidator: (value) { + return exchangeViewModel.isFixedRateMode + ? AmountValidator( + isAutovalidate: true, + currency: exchangeViewModel.receiveCurrency, + minValue: exchangeViewModel.limits.min.toString(), + maxValue: exchangeViewModel.limits.max.toString(), + ).call(value) + : null; + }, addressTextFieldValidator: AddressValidator(type: exchangeViewModel.receiveCurrency), onPushPasteButton: (context) async { diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index bf0d88cd0..1e25f1353 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -198,6 +198,9 @@ abstract class ExchangeViewModelBase with Store { @observable bool isFixedRateMode; + @observable + Limits limits; + @computed SyncStatus get status => wallet.syncStatus; @@ -241,8 +244,6 @@ abstract class ExchangeViewModelBase with Store { List depositCurrencies; - Limits limits; - NumberFormat _cryptoNumberFormat; final SettingsStore _settingsStore; @@ -320,6 +321,22 @@ abstract class ExchangeViewModelBase with Store { .replaceAll(RegExp('\\,'), ''); } + bool checkIfInputMeetsMinOrMaxCondition(String input) { + final _enteredAmount = double.tryParse(input.replaceAll(',', '.')) ?? 0; + double minLimit = limits.min ?? 0; + double? maxLimit = limits.max; + + if (_enteredAmount < minLimit) { + return false; + } + + if (maxLimit != null && _enteredAmount > maxLimit) { + return false; + } + + return true; + } + Future _calculateBestRate() async { final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1; diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 1bf0a27b1..8dbf1b900 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -697,5 +697,7 @@ "onion_link": "رابط البصل", "settings": "إعدادات", "sell_monero_com_alert_content": "بيع Monero غير مدعوم حتى الآن", + "error_text_input_below_minimum_limit":" المبلغ أقل من الحد الأدنى", + "error_text_input_above_maximum_limit":"المبلغ أكبر من الحد الأقصى", "show_market_place": "إظهار السوق" } diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 0929ad181..2a95efeee 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -698,5 +698,7 @@ "clearnet_link": "Clearnet връзка", "onion_link": "Лукова връзка", "sell_monero_com_alert_content": "Продажбата на Monero все още не се поддържа", + "error_text_input_below_minimum_limit" : "Сумата е по-малко от минималната", + "error_text_input_above_maximum_limit" : "Сумата надвишава максималната", "show_market_place":"Покажи пазар" } diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index dbfa086ce..ddd22eb61 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -698,5 +698,7 @@ "clearnet_link": "Odkaz na Clearnet", "onion_link": "Cibulový odkaz", "sell_monero_com_alert_content": "Prodej Monero zatím není podporován", + "error_text_input_below_minimum_limit" : "Částka je menší než minimální hodnota", + "error_text_input_above_maximum_limit" : "Částka je větší než maximální hodnota", "show_market_place": "Zobrazit trh" } diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 1d016a716..d29a641e2 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -699,5 +699,7 @@ "onion_link": "Zwiebel-Link", "settings": "Einstellungen", "sell_monero_com_alert_content": "Der Verkauf von Monero wird noch nicht unterstützt", + "error_text_input_below_minimum_limit" : "Menge ist unter dem Minimum", + "error_text_input_above_maximum_limit" : "Menge ist über dem Maximum", "show_market_place": "Marktplatz anzeigen" } diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 316f49f1c..f4d693e8f 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -699,5 +699,7 @@ "edit_node": "Edit Node", "settings": "Settings", "sell_monero_com_alert_content": "Selling Monero is not supported yet", + "error_text_input_below_minimum_limit" : "Amount is less than the minimum", + "error_text_input_above_maximum_limit" : "Amount is more than the maximum", "show_market_place" :"Show Marketplace" } diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index c376b869d..cee502fd3 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -699,5 +699,7 @@ "onion_link": "Enlace de cebolla", "settings": "Configuraciones", "sell_monero_com_alert_content": "Aún no se admite la venta de Monero", + "error_text_input_below_minimum_limit" : "La cantidad es menos que mínima", + "error_text_input_above_maximum_limit" : "La cantidad es más que el máximo", "show_market_place": "Mostrar mercado" } diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 4dd2b74a9..c4a44895a 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -699,5 +699,7 @@ "settings": "Paramètres", "onion_link": "Lien .onion", "sell_monero_com_alert_content": "La vente de Monero n'est pas encore prise en charge", + "error_text_input_below_minimum_limit" : "Le montant est inférieur au minimum", + "error_text_input_above_maximum_limit" : "Le montant est supérieur au maximum", "show_market_place" :"Afficher la place de marché" } diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 164b8a4a6..2669af5dc 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -699,5 +699,7 @@ "onion_link": "प्याज का लिंक", "settings": "समायोजन", "sell_monero_com_alert_content": "मोनेरो बेचना अभी तक समर्थित नहीं है", + "error_text_input_below_minimum_limit" : "राशि न्यूनतम से कम है", + "error_text_input_above_maximum_limit" : "राशि अधिकतम से अधिक है", "show_market_place":"बाज़ार दिखाएँ" } diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index f0d04ed36..865494c8d 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -699,5 +699,7 @@ "onion_link": "Poveznica luka", "settings": "Postavke", "sell_monero_com_alert_content": "Prodaja Monera još nije podržana", + "error_text_input_below_minimum_limit" : "Iznos je manji od minimalnog", + "error_text_input_above_maximum_limit" : "Iznos je veći od maskimalnog", "show_market_place" : "Prikaži tržište" } diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 501280351..12e1f2cfa 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -680,5 +680,7 @@ "clearnet_link": "Tautan clearnet", "onion_link": "Tautan bawang", "sell_monero_com_alert_content": "Menjual Monero belum didukung", + "error_text_input_below_minimum_limit" : "Jumlah kurang dari minimal", + "error_text_input_above_maximum_limit" : "Jumlah lebih dari maksimal", "show_market_place": "Tampilkan Pasar" } diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index bbc3a4f3f..dce7df8e3 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -699,5 +699,7 @@ "onion_link": "Collegamento a cipolla", "settings": "Impostazioni", "sell_monero_com_alert_content": "La vendita di Monero non è ancora supportata", + "error_text_input_below_minimum_limit" : "L'ammontare è inferiore al minimo", + "error_text_input_above_maximum_limit" : "L'ammontare è superiore al massimo", "show_market_place":"Mostra mercato" } diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 2c3b1c841..674457116 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -699,5 +699,7 @@ "onion_link": "オニオンリンク", "settings": "設定", "sell_monero_com_alert_content": "モネロの販売はまだサポートされていません", + "error_text_input_below_minimum_limit" : "金額は最小額より少ない", + "error_text_input_above_maximum_limit" : "金額は最大値を超えています", "show_market_place":"マーケットプレイスを表示" } diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 41d5ba014..4b35b313b 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -699,5 +699,7 @@ "onion_link": "양파 링크", "settings": "설정", "sell_monero_com_alert_content": "지원되지 않습니다.", + "error_text_input_below_minimum_limit" : "금액이 최소보다 적습니다.", + "error_text_input_above_maximum_limit" : "금액이 최대 값보다 많습니다.", "show_market_place":"마켓플레이스 표시" } diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 1ff29dd8f..f08634f48 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -699,5 +699,7 @@ "onion_link": "ကြက်သွန်လင့်", "settings": "ဆက်တင်များ", "sell_monero_com_alert_content": "Monero ရောင်းချခြင်းကို မပံ့ပိုးရသေးပါ။", + "error_text_input_below_minimum_limit" : "ပမာဏသည် အနိမ့်ဆုံးထက်နည်းသည်။", + "error_text_input_above_maximum_limit" : "ပမာဏသည် အများဆုံးထက် ပိုများသည်။", "show_market_place":"စျေးကွက်ကိုပြသပါ။" } diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index e01bf15d7..6e416b004 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -699,5 +699,7 @@ "onion_link": "Ui koppeling", "settings": "Instellingen", "sell_monero_com_alert_content": "Het verkopen van Monero wordt nog niet ondersteund", + "error_text_input_below_minimum_limit" : "Bedrag is minder dan minimaal", + "error_text_input_above_maximum_limit" : "Bedrag is meer dan maximaal", "show_market_place":"Toon Marktplaats" } diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index aba2bf1fb..357edc2e3 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -699,5 +699,7 @@ "onion_link": "Łącznik cebulowy", "settings": "Ustawienia", "sell_monero_com_alert_content": "Sprzedaż Monero nie jest jeszcze obsługiwana", + "error_text_input_below_minimum_limit" : "Kwota jest mniejsza niż minimalna", + "error_text_input_above_maximum_limit" : "Kwota jest większa niż maksymalna", "show_market_place" : "Pokaż rynek" } diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index a3308f1ce..d02147050 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -698,5 +698,7 @@ "onion_link": "ligação de cebola", "settings": "Configurações", "sell_monero_com_alert_content": "A venda de Monero ainda não é suportada", + "error_text_input_below_minimum_limit" : "O valor é menor que o mínimo", + "error_text_input_above_maximum_limit" : "O valor é superior ao máximo", "show_market_place":"Mostrar mercado" } diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index f7202b472..ad14f4cb0 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -699,5 +699,7 @@ "onion_link": "Луковая ссылка", "settings": "Настройки", "sell_monero_com_alert_content": "Продажа Monero пока не поддерживается", + "error_text_input_below_minimum_limit" : "Сумма меньше минимальной", + "error_text_input_above_maximum_limit" : "Сумма больше максимальной", "show_market_place":"Показать торговую площадку" } diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 88d12e091..b8f229a09 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -697,5 +697,7 @@ "onion_link": "ลิงค์หัวหอม", "settings": "การตั้งค่า", "sell_monero_com_alert_content": "ยังไม่รองรับการขาย Monero", + "error_text_input_below_minimum_limit" : "จำนวนเงินน้อยกว่าขั้นต่ำ", + "error_text_input_above_maximum_limit" : "จำนวนเงินสูงกว่าค่าสูงสุด", "show_market_place":"แสดงตลาดกลาง" } diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index fcd09dda2..2ef0b9302 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -699,5 +699,7 @@ "onion_link": "soğan bağlantısı", "settings": "ayarlar", "sell_monero_com_alert_content": "Monero satışı henüz desteklenmiyor", + "error_text_input_below_minimum_limit" : "Miktar minimumdan daha azdır", + "error_text_input_above_maximum_limit" : "Miktar maksimumdan daha fazla", "show_market_place":"Pazar Yerini Göster" } diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 4698ac46c..72cccf43b 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -698,5 +698,7 @@ "onion_link": "Посилання на цибулю", "settings": "Налаштування", "sell_monero_com_alert_content": "Продаж Monero ще не підтримується", + "error_text_input_below_minimum_limit" : "Сума менша мінімальної", + "error_text_input_above_maximum_limit" : "Сума більше максимальної", "show_market_place":"Шоу Ринок" } diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 3f3b5efc4..000ce76b4 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -699,5 +699,7 @@ "clearnet_link": "کلیرنیٹ لنک", "onion_link": "پیاز کا لنک", "sell_monero_com_alert_content": "Monero فروخت کرنا ابھی تک تعاون یافتہ نہیں ہے۔", + "error_text_input_below_minimum_limit" : "رقم کم از کم سے کم ہے۔", + "error_text_input_above_maximum_limit" : "رقم زیادہ سے زیادہ سے زیادہ ہے۔", "show_market_place":"بازار دکھائیں۔" } diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index c0fa68d72..e95e854d9 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -698,5 +698,7 @@ "onion_link": "洋葱链接", "settings": "设置", "sell_monero_com_alert_content": "尚不支持出售门罗币", + "error_text_input_below_minimum_limit" : "金额小于最小值", + "error_text_input_above_maximum_limit" : "金额大于最大值", "show_market_place" :"显示市场" }