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
This commit is contained in:
Adegoke David 2023-04-20 02:13:37 +01:00 committed by GitHub
parent efef30f8eb
commit 7b91b0e938
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 185 additions and 12 deletions

View file

@ -3,17 +3,45 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
class AmountValidator extends TextValidator { class AmountValidator extends TextValidator {
AmountValidator({required CryptoCurrency currency, bool isAutovalidate = false}) { AmountValidator({
required CryptoCurrency currency,
bool isAutovalidate = false,
String? minValue,
String? maxValue,
}) {
symbolsAmountValidator = symbolsAmountValidator =
SymbolsAmountValidator(isAutovalidate: isAutovalidate); SymbolsAmountValidator(isAutovalidate: isAutovalidate);
decimalAmountValidator = DecimalAmountValidator(currency: currency,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 SymbolsAmountValidator symbolsAmountValidator;
late final DecimalAmountValidator decimalAmountValidator; 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 { class SymbolsAmountValidator extends TextValidator {
@ -57,3 +85,71 @@ class AllAmountValidator extends TextValidator {
minLength: 0, minLength: 0,
maxLength: 0); maxLength: 0);
} }
class AmountMinValidator extends Validator<String> {
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<String> {
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(',', '.'));
}
}

View file

@ -456,8 +456,7 @@ class ExchangePage extends BasePage {
depositAmountController.addListener(() { depositAmountController.addListener(() {
if (depositAmountController.text != exchangeViewModel.depositAmount) { if (depositAmountController.text != exchangeViewModel.depositAmount) {
_depositAmountDebounce.run(() { _depositAmountDebounce.run(() {
exchangeViewModel.changeDepositAmount( exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
amount: depositAmountController.text);
exchangeViewModel.isReceiveAmountEntered = false; exchangeViewModel.isReceiveAmountEntered = false;
}); });
} }
@ -469,8 +468,7 @@ class ExchangePage extends BasePage {
receiveAmountController.addListener(() { receiveAmountController.addListener(() {
if (receiveAmountController.text != exchangeViewModel.receiveAmount) { if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
_receiveAmountDebounce.run(() { _receiveAmountDebounce.run(() {
exchangeViewModel.changeReceiveAmount( exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text);
amount: receiveAmountController.text);
exchangeViewModel.isReceiveAmountEntered = true; exchangeViewModel.isReceiveAmountEntered = true;
}); });
} }
@ -626,8 +624,16 @@ class ExchangePage extends BasePage {
currencyButtonColor: Colors.transparent, currencyButtonColor: Colors.transparent,
addressButtonsColor: Theme.of(context).focusColor!, addressButtonsColor: Theme.of(context).focusColor!,
borderColor: Theme.of(context).primaryTextTheme!.bodyText1!.color!, borderColor: Theme.of(context).primaryTextTheme!.bodyText1!.color!,
currencyValueValidator: currencyValueValidator: (value) {
AmountValidator(currency: exchangeViewModel.depositCurrency), return !exchangeViewModel.isFixedRateMode
? AmountValidator(
isAutovalidate: true,
currency: exchangeViewModel.depositCurrency,
minValue: exchangeViewModel.limits.min.toString(),
maxValue: exchangeViewModel.limits.max.toString(),
).call(value)
: null;
},
addressTextFieldValidator: addressTextFieldValidator:
AddressValidator(type: exchangeViewModel.depositCurrency), AddressValidator(type: exchangeViewModel.depositCurrency),
onPushPasteButton: (context) async { onPushPasteButton: (context) async {
@ -668,8 +674,16 @@ class ExchangePage extends BasePage {
addressButtonsColor: Theme.of(context).focusColor!, addressButtonsColor: Theme.of(context).focusColor!,
borderColor: borderColor:
Theme.of(context).primaryTextTheme!.bodyText1!.decorationColor!, Theme.of(context).primaryTextTheme!.bodyText1!.decorationColor!,
currencyValueValidator: currencyValueValidator: (value) {
AmountValidator(currency: exchangeViewModel.receiveCurrency), return exchangeViewModel.isFixedRateMode
? AmountValidator(
isAutovalidate: true,
currency: exchangeViewModel.receiveCurrency,
minValue: exchangeViewModel.limits.min.toString(),
maxValue: exchangeViewModel.limits.max.toString(),
).call(value)
: null;
},
addressTextFieldValidator: addressTextFieldValidator:
AddressValidator(type: exchangeViewModel.receiveCurrency), AddressValidator(type: exchangeViewModel.receiveCurrency),
onPushPasteButton: (context) async { onPushPasteButton: (context) async {

View file

@ -198,6 +198,9 @@ abstract class ExchangeViewModelBase with Store {
@observable @observable
bool isFixedRateMode; bool isFixedRateMode;
@observable
Limits limits;
@computed @computed
SyncStatus get status => wallet.syncStatus; SyncStatus get status => wallet.syncStatus;
@ -241,8 +244,6 @@ abstract class ExchangeViewModelBase with Store {
List<CryptoCurrency> depositCurrencies; List<CryptoCurrency> depositCurrencies;
Limits limits;
NumberFormat _cryptoNumberFormat; NumberFormat _cryptoNumberFormat;
final SettingsStore _settingsStore; final SettingsStore _settingsStore;
@ -320,6 +321,22 @@ abstract class ExchangeViewModelBase with Store {
.replaceAll(RegExp('\\,'), ''); .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<void> _calculateBestRate() async { Future<void> _calculateBestRate() async {
final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1; final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1;

View file

@ -697,5 +697,7 @@
"onion_link": "رابط البصل", "onion_link": "رابط البصل",
"settings": "إعدادات", "settings": "إعدادات",
"sell_monero_com_alert_content": "بيع Monero غير مدعوم حتى الآن", "sell_monero_com_alert_content": "بيع Monero غير مدعوم حتى الآن",
"error_text_input_below_minimum_limit":" المبلغ أقل من الحد الأدنى",
"error_text_input_above_maximum_limit":"المبلغ أكبر من الحد الأقصى",
"show_market_place": "إظهار السوق" "show_market_place": "إظهار السوق"
} }

View file

@ -698,5 +698,7 @@
"clearnet_link": "Clearnet връзка", "clearnet_link": "Clearnet връзка",
"onion_link": "Лукова връзка", "onion_link": "Лукова връзка",
"sell_monero_com_alert_content": "Продажбата на Monero все още не се поддържа", "sell_monero_com_alert_content": "Продажбата на Monero все още не се поддържа",
"error_text_input_below_minimum_limit" : "Сумата е по-малко от минималната",
"error_text_input_above_maximum_limit" : "Сумата надвишава максималната",
"show_market_place":"Покажи пазар" "show_market_place":"Покажи пазар"
} }

View file

@ -698,5 +698,7 @@
"clearnet_link": "Odkaz na Clearnet", "clearnet_link": "Odkaz na Clearnet",
"onion_link": "Cibulový odkaz", "onion_link": "Cibulový odkaz",
"sell_monero_com_alert_content": "Prodej Monero zatím není podporován", "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" "show_market_place": "Zobrazit trh"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "Zwiebel-Link", "onion_link": "Zwiebel-Link",
"settings": "Einstellungen", "settings": "Einstellungen",
"sell_monero_com_alert_content": "Der Verkauf von Monero wird noch nicht unterstützt", "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" "show_market_place": "Marktplatz anzeigen"
} }

View file

@ -699,5 +699,7 @@
"edit_node": "Edit Node", "edit_node": "Edit Node",
"settings": "Settings", "settings": "Settings",
"sell_monero_com_alert_content": "Selling Monero is not supported yet", "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" "show_market_place" :"Show Marketplace"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "Enlace de cebolla", "onion_link": "Enlace de cebolla",
"settings": "Configuraciones", "settings": "Configuraciones",
"sell_monero_com_alert_content": "Aún no se admite la venta de Monero", "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" "show_market_place": "Mostrar mercado"
} }

View file

@ -699,5 +699,7 @@
"settings": "Paramètres", "settings": "Paramètres",
"onion_link": "Lien .onion", "onion_link": "Lien .onion",
"sell_monero_com_alert_content": "La vente de Monero n'est pas encore prise en charge", "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é" "show_market_place" :"Afficher la place de marché"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "प्याज का लिंक", "onion_link": "प्याज का लिंक",
"settings": "समायोजन", "settings": "समायोजन",
"sell_monero_com_alert_content": "मोनेरो बेचना अभी तक समर्थित नहीं है", "sell_monero_com_alert_content": "मोनेरो बेचना अभी तक समर्थित नहीं है",
"error_text_input_below_minimum_limit" : "राशि न्यूनतम से कम है",
"error_text_input_above_maximum_limit" : "राशि अधिकतम से अधिक है",
"show_market_place":"बाज़ार दिखाएँ" "show_market_place":"बाज़ार दिखाएँ"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "Poveznica luka", "onion_link": "Poveznica luka",
"settings": "Postavke", "settings": "Postavke",
"sell_monero_com_alert_content": "Prodaja Monera još nije podržana", "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" "show_market_place" : "Prikaži tržište"
} }

View file

@ -680,5 +680,7 @@
"clearnet_link": "Tautan clearnet", "clearnet_link": "Tautan clearnet",
"onion_link": "Tautan bawang", "onion_link": "Tautan bawang",
"sell_monero_com_alert_content": "Menjual Monero belum didukung", "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" "show_market_place": "Tampilkan Pasar"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "Collegamento a cipolla", "onion_link": "Collegamento a cipolla",
"settings": "Impostazioni", "settings": "Impostazioni",
"sell_monero_com_alert_content": "La vendita di Monero non è ancora supportata", "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" "show_market_place":"Mostra mercato"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "オニオンリンク", "onion_link": "オニオンリンク",
"settings": "設定", "settings": "設定",
"sell_monero_com_alert_content": "モネロの販売はまだサポートされていません", "sell_monero_com_alert_content": "モネロの販売はまだサポートされていません",
"error_text_input_below_minimum_limit" : "金額は最小額より少ない",
"error_text_input_above_maximum_limit" : "金額は最大値を超えています",
"show_market_place":"マーケットプレイスを表示" "show_market_place":"マーケットプレイスを表示"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "양파 링크", "onion_link": "양파 링크",
"settings": "설정", "settings": "설정",
"sell_monero_com_alert_content": "지원되지 않습니다.", "sell_monero_com_alert_content": "지원되지 않습니다.",
"error_text_input_below_minimum_limit" : "금액이 최소보다 적습니다.",
"error_text_input_above_maximum_limit" : "금액이 최대 값보다 많습니다.",
"show_market_place":"마켓플레이스 표시" "show_market_place":"마켓플레이스 표시"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "ကြက်သွန်လင့်", "onion_link": "ကြက်သွန်လင့်",
"settings": "ဆက်တင်များ", "settings": "ဆက်တင်များ",
"sell_monero_com_alert_content": "Monero ရောင်းချခြင်းကို မပံ့ပိုးရသေးပါ။", "sell_monero_com_alert_content": "Monero ရောင်းချခြင်းကို မပံ့ပိုးရသေးပါ။",
"error_text_input_below_minimum_limit" : "ပမာဏသည် အနိမ့်ဆုံးထက်နည်းသည်။",
"error_text_input_above_maximum_limit" : "ပမာဏသည် အများဆုံးထက် ပိုများသည်။",
"show_market_place":"စျေးကွက်ကိုပြသပါ။" "show_market_place":"စျေးကွက်ကိုပြသပါ။"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "Ui koppeling", "onion_link": "Ui koppeling",
"settings": "Instellingen", "settings": "Instellingen",
"sell_monero_com_alert_content": "Het verkopen van Monero wordt nog niet ondersteund", "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" "show_market_place":"Toon Marktplaats"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "Łącznik cebulowy", "onion_link": "Łącznik cebulowy",
"settings": "Ustawienia", "settings": "Ustawienia",
"sell_monero_com_alert_content": "Sprzedaż Monero nie jest jeszcze obsługiwana", "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" "show_market_place" : "Pokaż rynek"
} }

View file

@ -698,5 +698,7 @@
"onion_link": "ligação de cebola", "onion_link": "ligação de cebola",
"settings": "Configurações", "settings": "Configurações",
"sell_monero_com_alert_content": "A venda de Monero ainda não é suportada", "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" "show_market_place":"Mostrar mercado"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "Луковая ссылка", "onion_link": "Луковая ссылка",
"settings": "Настройки", "settings": "Настройки",
"sell_monero_com_alert_content": "Продажа Monero пока не поддерживается", "sell_monero_com_alert_content": "Продажа Monero пока не поддерживается",
"error_text_input_below_minimum_limit" : "Сумма меньше минимальной",
"error_text_input_above_maximum_limit" : "Сумма больше максимальной",
"show_market_place":"Показать торговую площадку" "show_market_place":"Показать торговую площадку"
} }

View file

@ -697,5 +697,7 @@
"onion_link": "ลิงค์หัวหอม", "onion_link": "ลิงค์หัวหอม",
"settings": "การตั้งค่า", "settings": "การตั้งค่า",
"sell_monero_com_alert_content": "ยังไม่รองรับการขาย Monero", "sell_monero_com_alert_content": "ยังไม่รองรับการขาย Monero",
"error_text_input_below_minimum_limit" : "จำนวนเงินน้อยกว่าขั้นต่ำ",
"error_text_input_above_maximum_limit" : "จำนวนเงินสูงกว่าค่าสูงสุด",
"show_market_place":"แสดงตลาดกลาง" "show_market_place":"แสดงตลาดกลาง"
} }

View file

@ -699,5 +699,7 @@
"onion_link": "soğan bağlantısı", "onion_link": "soğan bağlantısı",
"settings": "ayarlar", "settings": "ayarlar",
"sell_monero_com_alert_content": "Monero satışı henüz desteklenmiyor", "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" "show_market_place":"Pazar Yerini Göster"
} }

View file

@ -698,5 +698,7 @@
"onion_link": "Посилання на цибулю", "onion_link": "Посилання на цибулю",
"settings": "Налаштування", "settings": "Налаштування",
"sell_monero_com_alert_content": "Продаж Monero ще не підтримується", "sell_monero_com_alert_content": "Продаж Monero ще не підтримується",
"error_text_input_below_minimum_limit" : "Сума менша мінімальної",
"error_text_input_above_maximum_limit" : "Сума більше максимальної",
"show_market_place":"Шоу Ринок" "show_market_place":"Шоу Ринок"
} }

View file

@ -699,5 +699,7 @@
"clearnet_link": "کلیرنیٹ لنک", "clearnet_link": "کلیرنیٹ لنک",
"onion_link": "پیاز کا لنک", "onion_link": "پیاز کا لنک",
"sell_monero_com_alert_content": "Monero فروخت کرنا ابھی تک تعاون یافتہ نہیں ہے۔", "sell_monero_com_alert_content": "Monero فروخت کرنا ابھی تک تعاون یافتہ نہیں ہے۔",
"error_text_input_below_minimum_limit" : "رقم کم از کم سے کم ہے۔",
"error_text_input_above_maximum_limit" : "رقم زیادہ سے زیادہ سے زیادہ ہے۔",
"show_market_place":"بازار دکھائیں۔" "show_market_place":"بازار دکھائیں۔"
} }

View file

@ -698,5 +698,7 @@
"onion_link": "洋葱链接", "onion_link": "洋葱链接",
"settings": "设置", "settings": "设置",
"sell_monero_com_alert_content": "尚不支持出售门罗币", "sell_monero_com_alert_content": "尚不支持出售门罗币",
"error_text_input_below_minimum_limit" : "金额小于最小值",
"error_text_input_above_maximum_limit" : "金额大于最大值",
"show_market_place" :"显示市场" "show_market_place" :"显示市场"
} }