diff --git a/lib/pages/buy_view/buy_form.dart b/lib/pages/buy_view/buy_form.dart index 87f2c175d..029bc9aa4 100644 --- a/lib/pages/buy_view/buy_form.dart +++ b/lib/pages/buy_view/buy_form.dart @@ -98,9 +98,11 @@ class _BuyFormState extends ConsumerState { bool _hovering1 = false; bool _hovering2 = false; + // TODO actually check USD min and max, these could get updated by Simplex static Decimal minFiat = Decimal.fromInt(50); static Decimal maxFiat = Decimal.fromInt(20000); + // We can't get crypto min and max without asking for a quote static Decimal minCrypto = Decimal.parse((0.00000001) .toString()); // lol how to go from double->Decimal more easily? static Decimal maxCrypto = Decimal.parse((10000.00000000).toString()); @@ -476,8 +478,7 @@ class _BuyFormState extends ConsumerState { } else { return StackDialog( title: "Simplex API error", - message: - "${quoteResponse.exception?.errorMessage.substring(19, quoteResponse.exception?.errorMessage?.length ?? 109 - (14 + 19))}", + message: "${quoteResponse.exception?.errorMessage}", rightButton: TextButton( style: Theme.of(context) .extension()! @@ -501,10 +502,10 @@ class _BuyFormState extends ConsumerState { } else { // Error; probably amount out of bounds String errorMessage = "${quoteResponse.exception?.errorMessage}"; - errorMessage = errorMessage.substring( - (errorMessage.indexOf('getQuote exception: ') ?? 19) + 20, - errorMessage.indexOf(", value: null")); if (errorMessage.contains('must be between')) { + errorMessage = errorMessage.substring( + (errorMessage.indexOf('getQuote exception: ') ?? 19) + 20, + errorMessage.indexOf(", value: null")); _BuyFormState.boundedCryptoTicker = errorMessage.substring( errorMessage.indexOf('The ') + 4, errorMessage.indexOf(' amount must be between')); @@ -565,7 +566,8 @@ class _BuyFormState extends ConsumerState { } else { return StackDialog( title: "Simplex API error", - message: errorMessage, + message: "${quoteResponse.exception?.errorMessage}", + // "${quoteResponse.exception?.errorMessage.substring(8, (quoteResponse.exception?.errorMessage?.length ?? 109) - (8 + 6))}", rightButton: TextButton( style: Theme.of(context) .extension()! @@ -936,7 +938,10 @@ class _BuyFormState extends ConsumerState { color: Theme.of(context).extension()!.textDark, ), key: const Key("buyAmountInputFieldTextFieldKey"), - controller: _buyAmountController..text = '50.00', + controller: _buyAmountController + ..text = _BuyFormState.buyWithFiat + ? _BuyFormState.minFiat.toStringAsFixed(2) ?? '50.00' + : _BuyFormState.minCrypto.toStringAsFixed(8), focusNode: _buyAmountFocusNode, keyboardType: Util.isDesktop ? null @@ -1016,11 +1021,20 @@ class _BuyFormState extends ConsumerState { _buyAmountController.text.isNotEmpty ? TextFieldIconButton( key: const Key( - "buyViewClearAddressFieldButtonKey"), + "buyViewClearAmountFieldButtonKey"), onTap: () { - _buyAmountController.text = ""; - // _receiveAddress = ""; - setState(() {}); + if (_BuyFormState.buyWithFiat) { + _buyAmountController.text = _BuyFormState + .minFiat + .toStringAsFixed(2); + } else { + if (selectedCrypto?.ticker == + _BuyFormState.boundedCryptoTicker) { + _buyAmountController.text = _BuyFormState + .minCrypto + .toStringAsFixed(8); + } + } }, child: const XIcon(), ) @@ -1366,8 +1380,10 @@ class NumericalRangeFormatter extends TextInputFormatter { TextEditingValue oldValue, TextEditingValue newValue, ) { - final TextSelection newSelection = newValue.selection; - String newVal = newValue.text; + TextSelection newSelection = newValue.selection; + String newVal = _BuyFormState.buyWithFiat + ? Decimal.parse(newValue.text).toStringAsFixed(2) + : Decimal.parse(newValue.text).toStringAsFixed(8); if (newValue.text == '') { return newValue; } else { @@ -1398,7 +1414,7 @@ class NumericalRangeFormatter extends TextInputFormatter { : r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$'; // return RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$') - return RegExp(regexString).hasMatch(newValue.text) + return RegExp(regexString).hasMatch(newVal) ? TextEditingValue(text: newVal, selection: newSelection) : oldValue; } diff --git a/lib/pages/buy_view/sub_widgets/buy_warning_popup.dart b/lib/pages/buy_view/sub_widgets/buy_warning_popup.dart index 082a19306..c98d53580 100644 --- a/lib/pages/buy_view/sub_widgets/buy_warning_popup.dart +++ b/lib/pages/buy_view/sub_widgets/buy_warning_popup.dart @@ -29,18 +29,9 @@ class BuyWarningPopup extends StatelessWidget { SimplexOrder? order; Future> newOrder(SimplexQuote quote) async { - final response = await SimplexAPI.instance.newOrder(quote); + final orderResponse = await SimplexAPI.instance.newOrder(quote); - // if (response.value != null) { - // ref.read(simplexProvider).updateOrder(response.value!); - // } else { - // Logging.instance.log( - // "_loadQuote: $response", - // level: LogLevel.Warning, - // ); - // } - - return response; + return orderResponse; } Future> redirect(SimplexOrder order) async { @@ -122,13 +113,90 @@ class BuyWarningPopup extends StatelessWidget { rightButton: PrimaryButton( label: "Continue", onPressed: () async { - BuyResponse order = await newOrder(quote); - await redirect(order.value as SimplexOrder).then((_response) async { - this.order = order.value as SimplexOrder; - Navigator.of(context, rootNavigator: isDesktop).pop(); - Navigator.of(context, rootNavigator: isDesktop).pop(); - await _buyInvoice(); - }); + BuyResponse orderResponse = await newOrder(quote); + if (orderResponse.exception == null) { + await redirect(orderResponse.value as SimplexOrder) + .then((_response) async { + this.order = orderResponse.value as SimplexOrder; + Navigator.of(context, rootNavigator: isDesktop).pop(); + Navigator.of(context, rootNavigator: isDesktop).pop(); + await _buyInvoice(); + }); + } else { + await showDialog( + context: context, + barrierDismissible: true, + builder: (context) { + if (isDesktop) { + return DesktopDialog( + maxWidth: 450, + child: Padding( + padding: const EdgeInsets.all(32), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Simplex API error", + style: STextStyles.desktopH3(context), + ), + const SizedBox( + height: 24, + ), + Text( + "${orderResponse.exception?.errorMessage}", + style: STextStyles.smallMed14(context), + ), + const SizedBox( + height: 56, + ), + Row( + children: [ + const Spacer(), + Expanded( + child: PrimaryButton( + buttonHeight: ButtonHeight.l, + label: "Ok", + onPressed: () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + Navigator.of(context).pop(); // weee + }, + ), + ), + ], + ) + ], + ), + ), + ); + } else { + return StackDialog( + title: "Simplex API error", + message: "${orderResponse.exception?.errorMessage}", + // "${quoteResponse.exception?.errorMessage.substring(8, (quoteResponse.exception?.errorMessage?.length ?? 109) - (8 + 6))}", + rightButton: TextButton( + style: Theme.of(context) + .extension()! + .getSecondaryEnabledButtonStyle(context), + child: Text( + "Ok", + style: STextStyles.button(context).copyWith( + color: Theme.of(context) + .extension()! + .accentColorDark), + ), + onPressed: () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + Navigator.of(context).pop(); // weee + }, + ), + ); + } + }, + ); + } }, ), icon: SizedBox( diff --git a/lib/services/buy/simplex/simplex_api.dart b/lib/services/buy/simplex/simplex_api.dart index 1726d55aa..4060567f0 100644 --- a/lib/services/buy/simplex/simplex_api.dart +++ b/lib/services/buy/simplex/simplex_api.dart @@ -16,7 +16,7 @@ import 'package:url_launcher/url_launcher.dart'; class SimplexAPI { static const String authority = "simplex-sandbox.stackwallet.com"; - // static const String authority = "localhost"; + // static const String authority = "localhost"; // For development purposes static const String scheme = authority == "localhost" ? "http" : "https"; final _prefs = Prefs.instance; @@ -188,7 +188,10 @@ class SimplexAPI { } final jsonArray = jsonDecode(res.body); if (jsonArray.containsKey('error') as bool) { - throw Exception('getQuote exception: ${jsonArray['error']}'); + if (jsonArray['error'] == true || jsonArray['error'] == 'true') { + // jsonArray['error'] as bool == true? + throw Exception('getQuote exception: ${jsonArray['error']}'); + } } jsonArray['quote'] = quote; // Add and pass this on @@ -273,6 +276,11 @@ class SimplexAPI { throw Exception('newOrder exception: statusCode= ${res.statusCode}'); } final jsonArray = jsonDecode(res.body); // TODO check if valid json + if (jsonArray.containsKey('error') as bool) { + if (jsonArray['error'] == true || jsonArray['error'] == 'true') { + throw Exception(jsonArray['message']); + } + } SimplexOrder _order = SimplexOrder( quote: quote,