mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 11:59:30 +00:00
changed amount validation and (hopefully) fixed preview quote button
This commit is contained in:
parent
1e5f624c8b
commit
47fb446b2e
1 changed files with 143 additions and 102 deletions
|
@ -108,6 +108,37 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
static Decimal maxCrypto = Decimal.parse((10000.00000000).toString());
|
static Decimal maxCrypto = Decimal.parse((10000.00000000).toString());
|
||||||
static String boundedCryptoTicker = '';
|
static String boundedCryptoTicker = '';
|
||||||
|
|
||||||
|
String _amountOutOfRangeErrorString = "";
|
||||||
|
void validateAmount() {
|
||||||
|
if (_buyAmountController.text.isEmpty) {
|
||||||
|
setState(() {
|
||||||
|
_amountOutOfRangeErrorString = "";
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final value = Decimal.tryParse(_buyAmountController.text);
|
||||||
|
if (value == null) {
|
||||||
|
setState(() {
|
||||||
|
_amountOutOfRangeErrorString = "Invalid amount";
|
||||||
|
});
|
||||||
|
} else if (value > maxFiat) {
|
||||||
|
setState(() {
|
||||||
|
_amountOutOfRangeErrorString =
|
||||||
|
"Maximum amount: ${maxFiat.toStringAsFixed(2)}";
|
||||||
|
});
|
||||||
|
} else if (value < minFiat) {
|
||||||
|
setState(() {
|
||||||
|
_amountOutOfRangeErrorString =
|
||||||
|
"Minimum amount: ${minFiat.toStringAsFixed(2)}";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
_amountOutOfRangeErrorString = "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void selectCrypto() async {
|
void selectCrypto() async {
|
||||||
if (ref.read(simplexProvider).supportedCryptos.isEmpty) {
|
if (ref.read(simplexProvider).supportedCryptos.isEmpty) {
|
||||||
bool shouldPop = false;
|
bool shouldPop = false;
|
||||||
|
@ -355,11 +386,11 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String? _fetchIconUrlFromTicker(String? ticker) {
|
// String? _fetchIconUrlFromTicker(String? ticker) {
|
||||||
if (ticker == null) return null;
|
// if (ticker == null) return null;
|
||||||
|
//
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
bool isStackCoin(String? ticker) {
|
bool isStackCoin(String? ticker) {
|
||||||
if (ticker == null) return false;
|
if (ticker == null) return false;
|
||||||
|
@ -373,13 +404,15 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget? getIconForTicker(String ticker) {
|
Widget? getIconForTicker(String ticker) {
|
||||||
String? iconAsset = /*isStackCoin(ticker)
|
String iconAsset = /*isStackCoin(ticker)
|
||||||
?*/
|
?*/
|
||||||
Assets.svg.iconFor(coin: coinFromTickerCaseInsensitive(ticker));
|
Assets.svg.iconFor(coin: coinFromTickerCaseInsensitive(ticker));
|
||||||
// : Assets.svg.buyIconFor(ticker);
|
// : Assets.svg.buyIconFor(ticker);
|
||||||
return (iconAsset != null)
|
// return (iconAsset != null)
|
||||||
? SvgPicture.asset(iconAsset, height: 20, width: 20)
|
// ? SvgPicture.asset(iconAsset, height: 20, width: 20)
|
||||||
: null;
|
// : null;
|
||||||
|
|
||||||
|
return SvgPicture.asset(iconAsset, height: 20, width: 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> previewQuote(SimplexQuote quote) async {
|
Future<void> previewQuote(SimplexQuote quote) async {
|
||||||
|
@ -500,7 +533,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
String errorMessage = "${quoteResponse.exception?.errorMessage}";
|
String errorMessage = "${quoteResponse.exception?.errorMessage}";
|
||||||
if (errorMessage.contains('must be between')) {
|
if (errorMessage.contains('must be between')) {
|
||||||
errorMessage = errorMessage.substring(
|
errorMessage = errorMessage.substring(
|
||||||
(errorMessage.indexOf('getQuote exception: ') ?? 19) + 20,
|
errorMessage.indexOf('getQuote exception: ') + 20,
|
||||||
errorMessage.indexOf(", value: null"));
|
errorMessage.indexOf(", value: null"));
|
||||||
_BuyFormState.boundedCryptoTicker = errorMessage.substring(
|
_BuyFormState.boundedCryptoTicker = errorMessage.substring(
|
||||||
errorMessage.indexOf('The ') + 4,
|
errorMessage.indexOf('The ') + 4,
|
||||||
|
@ -848,7 +881,11 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
.textFieldDefaultBG,
|
.textFieldDefaultBG,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 12.0, top: 12.0, right: 12.0, bottom: 12.0),
|
left: 12.0,
|
||||||
|
top: 12.0,
|
||||||
|
right: 12.0,
|
||||||
|
bottom: 12.0,
|
||||||
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Container(
|
Container(
|
||||||
|
@ -874,7 +911,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
width: 8,
|
width: 8,
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
"${selectedFiat?.ticker ?? 'ERR'}",
|
selectedFiat?.ticker ?? 'ERR',
|
||||||
style: STextStyles.largeMedium14(context),
|
style: STextStyles.largeMedium14(context),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
|
@ -882,7 +919,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Text(
|
child: Text(
|
||||||
"${selectedFiat?.name ?? 'Error'}",
|
selectedFiat?.name ?? 'Error',
|
||||||
style: STextStyles.largeMedium14(context),
|
style: STextStyles.largeMedium14(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -934,10 +971,11 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
color: Theme.of(context).extension<StackColors>()!.textDark,
|
color: Theme.of(context).extension<StackColors>()!.textDark,
|
||||||
),
|
),
|
||||||
key: const Key("buyAmountInputFieldTextFieldKey"),
|
key: const Key("buyAmountInputFieldTextFieldKey"),
|
||||||
controller: _buyAmountController
|
controller: _buyAmountController,
|
||||||
..text = _BuyFormState.buyWithFiat
|
// note: setting the text value here will set it every time this widget rebuilds
|
||||||
? _BuyFormState.minFiat.toStringAsFixed(2) ?? '50.00'
|
// ..text = _BuyFormState.buyWithFiat
|
||||||
: _BuyFormState.minCrypto.toStringAsFixed(8),
|
// ? _BuyFormState.minFiat.toStringAsFixed(2) ?? '50.00'
|
||||||
|
// : _BuyFormState.minCrypto.toStringAsFixed(8),
|
||||||
focusNode: _buyAmountFocusNode,
|
focusNode: _buyAmountFocusNode,
|
||||||
keyboardType: Util.isDesktop
|
keyboardType: Util.isDesktop
|
||||||
? null
|
? null
|
||||||
|
@ -946,7 +984,10 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
decimal: true,
|
decimal: true,
|
||||||
),
|
),
|
||||||
textAlign: TextAlign.left,
|
textAlign: TextAlign.left,
|
||||||
inputFormatters: [NumericalRangeFormatter()],
|
// inputFormatters: [NumericalRangeFormatter()],
|
||||||
|
onChanged: (_) {
|
||||||
|
validateAmount();
|
||||||
|
},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
contentPadding: const EdgeInsets.only(
|
contentPadding: const EdgeInsets.only(
|
||||||
// top: 22,
|
// top: 22,
|
||||||
|
@ -981,8 +1022,8 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
),
|
),
|
||||||
child: Text(
|
child: Text(
|
||||||
format.simpleCurrencySymbol(
|
format.simpleCurrencySymbol(
|
||||||
selectedFiat?.ticker ??
|
selectedFiat?.ticker.toUpperCase() ??
|
||||||
"ERR".toUpperCase()),
|
"ERR"),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: STextStyles.smallMed12(context).copyWith(
|
style: STextStyles.smallMed12(context).copyWith(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
|
@ -1019,18 +1060,20 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
key: const Key(
|
key: const Key(
|
||||||
"buyViewClearAmountFieldButtonKey"),
|
"buyViewClearAmountFieldButtonKey"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (_BuyFormState.buyWithFiat) {
|
// if (_BuyFormState.buyWithFiat) {
|
||||||
_buyAmountController.text = _BuyFormState
|
// _buyAmountController.text = _BuyFormState
|
||||||
.minFiat
|
// .minFiat
|
||||||
.toStringAsFixed(2);
|
// .toStringAsFixed(2);
|
||||||
} else {
|
// } else {
|
||||||
if (selectedCrypto?.ticker ==
|
// if (selectedCrypto?.ticker ==
|
||||||
_BuyFormState.boundedCryptoTicker) {
|
// _BuyFormState.boundedCryptoTicker) {
|
||||||
_buyAmountController.text = _BuyFormState
|
// _buyAmountController.text = _BuyFormState
|
||||||
.minCrypto
|
// .minCrypto
|
||||||
.toStringAsFixed(8);
|
// .toStringAsFixed(8);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
_buyAmountController.text = "";
|
||||||
|
validateAmount();
|
||||||
},
|
},
|
||||||
child: const XIcon(),
|
child: const XIcon(),
|
||||||
)
|
)
|
||||||
|
@ -1047,7 +1090,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
_buyAmountController.text =
|
_buyAmountController.text =
|
||||||
amountString.toString();
|
amountString.toString();
|
||||||
|
|
||||||
setState(() {});
|
validateAmount();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: _buyAmountController.text.isEmpty
|
child: _buyAmountController.text.isEmpty
|
||||||
|
@ -1060,6 +1103,14 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: isDesktop ? 10 : 4,
|
||||||
|
),
|
||||||
|
if (_amountOutOfRangeErrorString.isNotEmpty)
|
||||||
|
Text(
|
||||||
|
_amountOutOfRangeErrorString,
|
||||||
|
style: STextStyles.errorSmall(context),
|
||||||
|
),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: isDesktop ? 20 : 12,
|
height: isDesktop ? 20 : 12,
|
||||||
),
|
),
|
||||||
|
@ -1167,7 +1218,7 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
_receiveAddressController.text = "";
|
_receiveAddressController.text = "";
|
||||||
_address = "";
|
_address = "";
|
||||||
setState(() {
|
setState(() {
|
||||||
_addressToggleFlag = true;
|
_addressToggleFlag = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: const XIcon(),
|
child: const XIcon(),
|
||||||
|
@ -1338,27 +1389,15 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
SizedBox(
|
SizedBox(
|
||||||
height: isDesktop ? 20 : 12,
|
height: isDesktop ? 20 : 12,
|
||||||
),
|
),
|
||||||
MouseRegion(
|
PrimaryButton(
|
||||||
cursor: SystemMouseCursors.click,
|
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
||||||
child: GestureDetector(
|
enabled: _addressToggleFlag &&
|
||||||
onTap: () {
|
_amountOutOfRangeErrorString.isEmpty &&
|
||||||
if (_receiveAddressController.text.isNotEmpty &&
|
_buyAmountController.text.isNotEmpty,
|
||||||
_buyAmountController.text.isNotEmpty) {
|
onPressed: () {
|
||||||
previewQuote(quote);
|
previewQuote(quote);
|
||||||
}
|
},
|
||||||
},
|
label: "Preview quote",
|
||||||
child: PrimaryButton(
|
|
||||||
buttonHeight: isDesktop ? ButtonHeight.l : null,
|
|
||||||
enabled: _receiveAddressController.text.isNotEmpty &&
|
|
||||||
_buyAmountController.text.isNotEmpty,
|
|
||||||
onPressed: () {
|
|
||||||
if (_receiveAddressController.text.isNotEmpty &&
|
|
||||||
_buyAmountController.text.isNotEmpty) {
|
|
||||||
previewQuote(quote);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
label: "Preview quote",
|
|
||||||
)),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1367,51 +1406,53 @@ class _BuyFormState extends ConsumerState<BuyForm> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// See https://stackoverflow.com/a/68072967
|
// might need this again in the future
|
||||||
class NumericalRangeFormatter extends TextInputFormatter {
|
|
||||||
NumericalRangeFormatter();
|
|
||||||
|
|
||||||
@override
|
// // See https://stackoverflow.com/a/68072967
|
||||||
TextEditingValue formatEditUpdate(
|
// class NumericalRangeFormatter extends TextInputFormatter {
|
||||||
TextEditingValue oldValue,
|
// NumericalRangeFormatter();
|
||||||
TextEditingValue newValue,
|
//
|
||||||
) {
|
// @override
|
||||||
TextSelection newSelection = newValue.selection;
|
// TextEditingValue formatEditUpdate(
|
||||||
String newVal = _BuyFormState.buyWithFiat
|
// TextEditingValue oldValue,
|
||||||
? Decimal.parse(newValue.text).toStringAsFixed(2)
|
// TextEditingValue newValue,
|
||||||
: Decimal.parse(newValue.text).toStringAsFixed(8);
|
// ) {
|
||||||
if (newValue.text == '') {
|
// TextSelection newSelection = newValue.selection;
|
||||||
return newValue;
|
// String newVal = _BuyFormState.buyWithFiat
|
||||||
} else {
|
// ? Decimal.parse(newValue.text).toStringAsFixed(2)
|
||||||
if (_BuyFormState.buyWithFiat) {
|
// : Decimal.parse(newValue.text).toStringAsFixed(8);
|
||||||
if (Decimal.parse(newValue.text) < _BuyFormState.minFiat) {
|
// if (newValue.text == '') {
|
||||||
newVal = _BuyFormState.minFiat.toStringAsFixed(2);
|
// return newValue;
|
||||||
// _BuyFormState._buyAmountController.selection =
|
// } else {
|
||||||
// TextSelection.collapsed(
|
// if (_BuyFormState.buyWithFiat) {
|
||||||
// offset: _BuyFormState.buyWithFiat
|
// if (Decimal.parse(newValue.text) < _BuyFormState.minFiat) {
|
||||||
// ? _BuyFormState._buyAmountController.text.length - 2
|
// newVal = _BuyFormState.minFiat.toStringAsFixed(2);
|
||||||
// : _BuyFormState._buyAmountController.text.length - 8);
|
// // _BuyFormState._buyAmountController.selection =
|
||||||
} else if (Decimal.parse(newValue.text) > _BuyFormState.maxFiat) {
|
// // TextSelection.collapsed(
|
||||||
newVal = _BuyFormState.maxFiat.toStringAsFixed(2);
|
// // offset: _BuyFormState.buyWithFiat
|
||||||
}
|
// // ? _BuyFormState._buyAmountController.text.length - 2
|
||||||
} else if (!_BuyFormState.buyWithFiat &&
|
// // : _BuyFormState._buyAmountController.text.length - 8);
|
||||||
_BuyFormState.selectedCrypto?.ticker ==
|
// } else if (Decimal.parse(newValue.text) > _BuyFormState.maxFiat) {
|
||||||
_BuyFormState.boundedCryptoTicker) {
|
// newVal = _BuyFormState.maxFiat.toStringAsFixed(2);
|
||||||
if (Decimal.parse(newValue.text) < _BuyFormState.minCrypto) {
|
// }
|
||||||
newVal = _BuyFormState.minCrypto.toStringAsFixed(8);
|
// } else if (!_BuyFormState.buyWithFiat &&
|
||||||
} else if (Decimal.parse(newValue.text) > _BuyFormState.maxCrypto) {
|
// _BuyFormState.selectedCrypto?.ticker ==
|
||||||
newVal = _BuyFormState.maxCrypto.toStringAsFixed(8);
|
// _BuyFormState.boundedCryptoTicker) {
|
||||||
}
|
// if (Decimal.parse(newValue.text) < _BuyFormState.minCrypto) {
|
||||||
}
|
// newVal = _BuyFormState.minCrypto.toStringAsFixed(8);
|
||||||
}
|
// } else if (Decimal.parse(newValue.text) > _BuyFormState.maxCrypto) {
|
||||||
|
// newVal = _BuyFormState.maxCrypto.toStringAsFixed(8);
|
||||||
final regexString = _BuyFormState.buyWithFiat
|
// }
|
||||||
? r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$'
|
// }
|
||||||
: r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$';
|
// }
|
||||||
|
//
|
||||||
// return RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
// final regexString = _BuyFormState.buyWithFiat
|
||||||
return RegExp(regexString).hasMatch(newVal)
|
// ? r'^([0-9]*[,.]?[0-9]{0,2}|[,.][0-9]{0,2})$'
|
||||||
? TextEditingValue(text: newVal, selection: newSelection)
|
// : r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$';
|
||||||
: oldValue;
|
//
|
||||||
}
|
// // return RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||||
}
|
// return RegExp(regexString).hasMatch(newVal)
|
||||||
|
// ? TextEditingValue(text: newVal, selection: newSelection)
|
||||||
|
// : oldValue;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
Loading…
Reference in a new issue