diff --git a/lib/main.dart b/lib/main.dart index 40f5321fc..0c74aa4a4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -18,6 +18,7 @@ import 'package:path_provider/path_provider.dart'; import 'package:stackwallet/hive/db.dart'; import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart'; import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart'; +import 'package:stackwallet/models/exchange/exchange_form_state.dart'; import 'package:stackwallet/models/exchange/response_objects/trade.dart'; import 'package:stackwallet/models/isar/models/log.dart'; import 'package:stackwallet/models/models.dart'; @@ -35,8 +36,6 @@ import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart'; import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart'; import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_state_provider.dart'; import 'package:stackwallet/providers/exchange/changenow_initial_load_status.dart'; -import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; -import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart'; import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart'; import 'package:stackwallet/providers/global/auto_swb_service_provider.dart'; import 'package:stackwallet/providers/global/base_currencies_provider.dart'; @@ -47,6 +46,7 @@ import 'package:stackwallet/providers/ui/color_theme_provider.dart'; import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/services/debug_service.dart'; import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; +import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart'; import 'package:stackwallet/services/locale_service.dart'; import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/notifications_api.dart'; @@ -271,15 +271,15 @@ class _MaterialAppWithThemeState extends ConsumerState response2.value!; if (response.value!.length > 1) { - if (ref.read(estimatedRateExchangeFormProvider).from == null) { + if (ref.read(exchangeFormStateProvider).from == null) { if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) { - await ref.read(estimatedRateExchangeFormProvider).updateFrom( + await ref.read(exchangeFormStateProvider).updateFrom( response.value!.firstWhere((e) => e.ticker == "btc"), false); } } - if (ref.read(estimatedRateExchangeFormProvider).to == null) { + if (ref.read(exchangeFormStateProvider).to == null) { if (response.value!.where((e) => e.ticker == "doge").isNotEmpty) { - await ref.read(estimatedRateExchangeFormProvider).updateTo( + await ref.read(exchangeFormStateProvider).updateTo( response.value!.firstWhere((e) => e.ticker == "doge"), false); } } @@ -319,12 +319,12 @@ class _MaterialAppWithThemeState extends ConsumerState ref.read(fixedRateMarketPairsStateProvider.state).state = response3.value!; - if (ref.read(fixedRateExchangeFormProvider).market == null) { + if (ref.read(exchangeFormStateProvider).market == null) { final matchingMarkets = response3.value!.where((e) => e.to == "doge" && e.from == "btc"); if (matchingMarkets.isNotEmpty) { await ref - .read(fixedRateExchangeFormProvider) + .read(exchangeFormStateProvider) .updateMarket(matchingMarkets.first, true); } } @@ -355,6 +355,7 @@ class _MaterialAppWithThemeState extends ConsumerState @override void initState() { + ref.read(exchangeFormStateProvider).exchange = ChangeNowExchange(); final colorScheme = DB.instance .get(boxName: DB.boxNameTheme, key: "colorScheme") as String?; diff --git a/lib/models/exchange/change_now/estimated_exchange_amount.dart b/lib/models/exchange/change_now/estimated_exchange_amount.dart index cf70f812f..dccf6b3b3 100644 --- a/lib/models/exchange/change_now/estimated_exchange_amount.dart +++ b/lib/models/exchange/change_now/estimated_exchange_amount.dart @@ -35,8 +35,10 @@ class EstimatedExchangeAmount { factory EstimatedExchangeAmount.fromJson(Map json) { try { return EstimatedExchangeAmount( - estimatedAmount: Decimal.parse(json["estimatedAmount"].toString()), - transactionSpeedForecast: json["transactionSpeedForecast"] as String, + estimatedAmount: Decimal.parse(json["estimatedAmount"]?.toString() ?? + json["estimatedDeposit"].toString()), + transactionSpeedForecast: + json["transactionSpeedForecast"] as String? ?? "", warningMessage: json["warningMessage"] as String?, rateId: json["rateId"] as String?, networkFee: Decimal.tryParse(json["networkFee"].toString()), diff --git a/lib/models/exchange/estimated_rate_exchange_form_state.dart b/lib/models/exchange/estimated_rate_exchange_form_state.dart index aed815de8..2a465c3ef 100644 --- a/lib/models/exchange/estimated_rate_exchange_form_state.dart +++ b/lib/models/exchange/estimated_rate_exchange_form_state.dart @@ -1,282 +1,282 @@ -import 'package:decimal/decimal.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:stackwallet/models/exchange/response_objects/currency.dart'; -import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; -import 'package:stackwallet/utilities/logger.dart'; - -class EstimatedRateExchangeFormState extends ChangeNotifier { - /// used in testing to inject mock - ChangeNowAPI? cnTesting; - - Decimal? _fromAmount; - Decimal? _toAmount; - - Decimal? _minFromAmount; - Decimal? _minToAmount; - - Decimal? rate; - - Currency? _from; - Currency? _to; - - void Function(String)? _onError; - - Currency? get from => _from; - Currency? get to => _to; - - String get fromAmountString => - _fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8); - String get toAmountString => - _toAmount == null ? "" : _toAmount!.toStringAsFixed(8); - - String get rateDisplayString { - if (rate == null || from == null || to == null) { - return "N/A"; - } else { - return "1 ${from!.ticker.toUpperCase()} ~${rate!.toStringAsFixed(8)} ${to!.ticker.toUpperCase()}"; - } - } - - bool get canExchange { - return _fromAmount != null && - _fromAmount != Decimal.zero && - _toAmount != null && - rate != null && - minimumSendWarning.isEmpty; - } - - String get minimumSendWarning { - if (_from != null && - _fromAmount != null && - _minFromAmount != null && - _fromAmount! < _minFromAmount!) { - return "Minimum amount ${_minFromAmount!.toString()} ${from!.ticker.toUpperCase()}"; - } - - return ""; - } - - Future init(Currency? from, Currency? to) async { - _from = from; - _to = to; - } - - void clearAmounts(bool shouldNotifyListeners) { - _fromAmount = null; - _toAmount = null; - _minFromAmount = null; - _minToAmount = null; - rate = null; - - if (shouldNotifyListeners) { - notifyListeners(); - } - } - - Future swap() async { - final Decimal? newToAmount = _fromAmount; - final Decimal? newFromAmount = _toAmount; - - final Decimal? newMinFromAmount = _minToAmount; - final Decimal? newMinToAmount = _minFromAmount; - - final Currency? newTo = from; - final Currency? newFrom = to; - - _fromAmount = newFromAmount; - _toAmount = newToAmount; - - _minToAmount = newMinToAmount; - _minFromAmount = newMinFromAmount; - - // rate = newRate; - - _to = newTo; - _from = newFrom; - - await _updateMinFromAmount(shouldNotifyListeners: false); - - await updateRate(); - - notifyListeners(); - } - - Future updateTo(Currency to, bool shouldNotifyListeners) async { - try { - _to = to; - if (_from == null) { - rate = null; - notifyListeners(); - return; - } - - await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); - - await updateRate(shouldNotifyListeners: shouldNotifyListeners); - - debugPrint( - "_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); - - if (shouldNotifyListeners) { - notifyListeners(); - } - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Error); - } - } - - Future updateFrom(Currency from, bool shouldNotifyListeners) async { - try { - _from = from; - - if (_to == null) { - rate = null; - notifyListeners(); - return; - } - - await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); - - await updateRate(shouldNotifyListeners: shouldNotifyListeners); - - debugPrint( - "_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); - if (shouldNotifyListeners) { - notifyListeners(); - } - } catch (e, s) { - Logging.instance.log("$e\n$s", level: LogLevel.Error); - } - } - - Future _updateMinFromAmount( - {required bool shouldNotifyListeners}) async { - _minFromAmount = await getStandardMinExchangeAmount(from: from!, to: to!); - if (shouldNotifyListeners) { - notifyListeners(); - } - } - - // Future setToAmountAndCalculateFromAmount( - // Decimal newToAmount, - // bool shouldNotifyListeners, - // ) async { - // if (newToAmount == Decimal.zero) { - // _fromAmount = Decimal.zero; - // } - // - // _toAmount = newToAmount; - // await updateRate(); - // if (shouldNotifyListeners) { - // notifyListeners(); - // } - // } - - Future setFromAmountAndCalculateToAmount( - Decimal newFromAmount, - bool shouldNotifyListeners, - ) async { - if (newFromAmount == Decimal.zero) { - _toAmount = Decimal.zero; - } - - _fromAmount = newFromAmount; - await updateRate(shouldNotifyListeners: shouldNotifyListeners); - - if (shouldNotifyListeners) { - notifyListeners(); - } - } - - Future getStandardEstimatedToAmount({ - required Decimal fromAmount, - required Currency from, - required Currency to, - }) async { - final response = - await (cnTesting ?? ChangeNowAPI.instance).getEstimatedExchangeAmount( - fromTicker: from.ticker, - toTicker: to.ticker, - fromAmount: fromAmount, - ); - - if (response.value != null) { - return response.value!.estimatedAmount; - } else { - _onError?.call( - "Failed to fetch estimated amount: ${response.exception?.toString()}"); - return null; - } - } - - // Future getStandardEstimatedFromAmount({ - // required Decimal toAmount, - // required Currency from, - // required Currency to, - // }) async { - // final response = await (cnTesting ?? ChangeNow.instance) - // .getEstimatedExchangeAmount( - // fromTicker: from.ticker, - // toTicker: to.ticker, - // fromAmount: toAmount, ); - // - // if (response.value != null) { - // return response.value!.fromAmount; - // } else { - // _onError?.call( - // "Failed to fetch estimated amount: ${response.exception?.toString()}"); - // return null; - // } - // } - - Future getStandardMinExchangeAmount({ - required Currency from, - required Currency to, - }) async { - final response = await (cnTesting ?? ChangeNowAPI.instance) - .getMinimalExchangeAmount(fromTicker: from.ticker, toTicker: to.ticker); - - if (response.value != null) { - return response.value!; - } else { - _onError?.call( - "Could not update minimal exchange amounts: ${response.exception?.toString()}"); - return null; - } - } - - void setOnError({ - required void Function(String)? onError, - bool shouldNotifyListeners = false, - }) { - _onError = onError; - if (shouldNotifyListeners) { - notifyListeners(); - } - } - - Future updateRate({bool shouldNotifyListeners = false}) async { - rate = null; - final amount = _fromAmount; - final minAmount = _minFromAmount; - if (amount != null && amount > Decimal.zero) { - Decimal? amt; - if (minAmount != null) { - if (minAmount <= amount) { - amt = await getStandardEstimatedToAmount( - fromAmount: amount, from: _from!, to: _to!); - if (amt != null) { - rate = (amt / amount).toDecimal(scaleOnInfinitePrecision: 12); - } - } - } - if (rate != null && amt != null) { - _toAmount = amt; - } - } - if (shouldNotifyListeners) { - notifyListeners(); - } - } -} +// import 'package:decimal/decimal.dart'; +// import 'package:flutter/cupertino.dart'; +// import 'package:flutter/material.dart'; +// import 'package:stackwallet/models/exchange/response_objects/currency.dart'; +// import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; +// import 'package:stackwallet/utilities/logger.dart'; +// +// class EstimatedRateExchangeFormState extends ChangeNotifier { +// /// used in testing to inject mock +// ChangeNowAPI? cnTesting; +// +// Decimal? _fromAmount; +// Decimal? _toAmount; +// +// Decimal? _minFromAmount; +// Decimal? _minToAmount; +// +// Decimal? rate; +// +// Currency? _from; +// Currency? _to; +// +// void Function(String)? _onError; +// +// Currency? get from => _from; +// Currency? get to => _to; +// +// String get fromAmountString => +// _fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8); +// String get toAmountString => +// _toAmount == null ? "" : _toAmount!.toStringAsFixed(8); +// +// String get rateDisplayString { +// if (rate == null || from == null || to == null) { +// return "N/A"; +// } else { +// return "1 ${from!.ticker.toUpperCase()} ~${rate!.toStringAsFixed(8)} ${to!.ticker.toUpperCase()}"; +// } +// } +// +// bool get canExchange { +// return _fromAmount != null && +// _fromAmount != Decimal.zero && +// _toAmount != null && +// rate != null && +// minimumSendWarning.isEmpty; +// } +// +// String get minimumSendWarning { +// if (_from != null && +// _fromAmount != null && +// _minFromAmount != null && +// _fromAmount! < _minFromAmount!) { +// return "Minimum amount ${_minFromAmount!.toString()} ${from!.ticker.toUpperCase()}"; +// } +// +// return ""; +// } +// +// Future init(Currency? from, Currency? to) async { +// _from = from; +// _to = to; +// } +// +// void clearAmounts(bool shouldNotifyListeners) { +// _fromAmount = null; +// _toAmount = null; +// _minFromAmount = null; +// _minToAmount = null; +// rate = null; +// +// if (shouldNotifyListeners) { +// notifyListeners(); +// } +// } +// +// Future swap() async { +// final Decimal? newToAmount = _fromAmount; +// final Decimal? newFromAmount = _toAmount; +// +// final Decimal? newMinFromAmount = _minToAmount; +// final Decimal? newMinToAmount = _minFromAmount; +// +// final Currency? newTo = from; +// final Currency? newFrom = to; +// +// _fromAmount = newFromAmount; +// _toAmount = newToAmount; +// +// _minToAmount = newMinToAmount; +// _minFromAmount = newMinFromAmount; +// +// // rate = newRate; +// +// _to = newTo; +// _from = newFrom; +// +// await _updateMinFromAmount(shouldNotifyListeners: false); +// +// await updateRate(); +// +// notifyListeners(); +// } +// +// Future updateTo(Currency to, bool shouldNotifyListeners) async { +// try { +// _to = to; +// if (_from == null) { +// rate = null; +// notifyListeners(); +// return; +// } +// +// await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); +// +// await updateRate(shouldNotifyListeners: shouldNotifyListeners); +// +// debugPrint( +// "_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); +// +// if (shouldNotifyListeners) { +// notifyListeners(); +// } +// } catch (e, s) { +// Logging.instance.log("$e\n$s", level: LogLevel.Error); +// } +// } +// +// Future updateFrom(Currency from, bool shouldNotifyListeners) async { +// try { +// _from = from; +// +// if (_to == null) { +// rate = null; +// notifyListeners(); +// return; +// } +// +// await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); +// +// await updateRate(shouldNotifyListeners: shouldNotifyListeners); +// +// debugPrint( +// "_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); +// if (shouldNotifyListeners) { +// notifyListeners(); +// } +// } catch (e, s) { +// Logging.instance.log("$e\n$s", level: LogLevel.Error); +// } +// } +// +// Future _updateMinFromAmount( +// {required bool shouldNotifyListeners}) async { +// _minFromAmount = await getStandardMinExchangeAmount(from: from!, to: to!); +// if (shouldNotifyListeners) { +// notifyListeners(); +// } +// } +// +// // Future setToAmountAndCalculateFromAmount( +// // Decimal newToAmount, +// // bool shouldNotifyListeners, +// // ) async { +// // if (newToAmount == Decimal.zero) { +// // _fromAmount = Decimal.zero; +// // } +// // +// // _toAmount = newToAmount; +// // await updateRate(); +// // if (shouldNotifyListeners) { +// // notifyListeners(); +// // } +// // } +// +// Future setFromAmountAndCalculateToAmount( +// Decimal newFromAmount, +// bool shouldNotifyListeners, +// ) async { +// if (newFromAmount == Decimal.zero) { +// _toAmount = Decimal.zero; +// } +// +// _fromAmount = newFromAmount; +// await updateRate(shouldNotifyListeners: shouldNotifyListeners); +// +// if (shouldNotifyListeners) { +// notifyListeners(); +// } +// } +// +// Future getStandardEstimatedToAmount({ +// required Decimal fromAmount, +// required Currency from, +// required Currency to, +// }) async { +// final response = +// await (cnTesting ?? ChangeNowAPI.instance).getEstimatedExchangeAmount( +// fromTicker: from.ticker, +// toTicker: to.ticker, +// fromAmount: fromAmount, +// ); +// +// if (response.value != null) { +// return response.value!.estimatedAmount; +// } else { +// _onError?.call( +// "Failed to fetch estimated amount: ${response.exception?.toString()}"); +// return null; +// } +// } +// +// // Future getStandardEstimatedFromAmount({ +// // required Decimal toAmount, +// // required Currency from, +// // required Currency to, +// // }) async { +// // final response = await (cnTesting ?? ChangeNow.instance) +// // .getEstimatedExchangeAmount( +// // fromTicker: from.ticker, +// // toTicker: to.ticker, +// // fromAmount: toAmount, ); +// // +// // if (response.value != null) { +// // return response.value!.fromAmount; +// // } else { +// // _onError?.call( +// // "Failed to fetch estimated amount: ${response.exception?.toString()}"); +// // return null; +// // } +// // } +// +// Future getStandardMinExchangeAmount({ +// required Currency from, +// required Currency to, +// }) async { +// final response = await (cnTesting ?? ChangeNowAPI.instance) +// .getMinimalExchangeAmount(fromTicker: from.ticker, toTicker: to.ticker); +// +// if (response.value != null) { +// return response.value!; +// } else { +// _onError?.call( +// "Could not update minimal exchange amounts: ${response.exception?.toString()}"); +// return null; +// } +// } +// +// void setOnError({ +// required void Function(String)? onError, +// bool shouldNotifyListeners = false, +// }) { +// _onError = onError; +// if (shouldNotifyListeners) { +// notifyListeners(); +// } +// } +// +// Future updateRate({bool shouldNotifyListeners = false}) async { +// rate = null; +// final amount = _fromAmount; +// final minAmount = _minFromAmount; +// if (amount != null && amount > Decimal.zero) { +// Decimal? amt; +// if (minAmount != null) { +// if (minAmount <= amount) { +// amt = await getStandardEstimatedToAmount( +// fromAmount: amount, from: _from!, to: _to!); +// if (amt != null) { +// rate = (amt / amount).toDecimal(scaleOnInfinitePrecision: 12); +// } +// } +// } +// if (rate != null && amt != null) { +// _toAmount = amt; +// } +// } +// if (shouldNotifyListeners) { +// notifyListeners(); +// } +// } +// } diff --git a/lib/models/exchange/exchange_form_state.dart b/lib/models/exchange/exchange_form_state.dart new file mode 100644 index 000000000..ad30e0c1f --- /dev/null +++ b/lib/models/exchange/exchange_form_state.dart @@ -0,0 +1,390 @@ +import 'package:decimal/decimal.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/models/exchange/response_objects/currency.dart'; +import 'package:stackwallet/models/exchange/response_objects/estimate.dart'; +import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart'; +import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; +import 'package:stackwallet/services/exchange/exchange.dart'; +import 'package:stackwallet/utilities/logger.dart'; + +final exchangeFormStateProvider = + ChangeNotifierProvider((ref) => ExchangeFormState()); + +class ExchangeFormState extends ChangeNotifier { + Exchange? _exchange; + Exchange? get exchange => _exchange; + set exchange(Exchange? value) { + _exchange = value; + _onExchangeTypeChanged(); + } + + ExchangeRateType _exchangeType = ExchangeRateType.estimated; + ExchangeRateType get exchangeType => _exchangeType; + set exchangeType(ExchangeRateType value) { + _exchangeType = value; + _onExchangeRateTypeChanged(); + } + + bool reversed = false; + + Decimal? fromAmount; + Decimal? toAmount; + + Decimal? minAmount; + Decimal? maxAmount; + + Decimal? rate; + Estimate? estimate; + + FixedRateMarket? _market; + FixedRateMarket? get market => _market; + + Currency? _from; + Currency? _to; + + String? get fromTicker { + switch (exchangeType) { + case ExchangeRateType.estimated: + return _from?.ticker; + case ExchangeRateType.fixed: + return _market?.from; + } + } + + String? get toTicker { + switch (exchangeType) { + case ExchangeRateType.estimated: + return _to?.ticker; + case ExchangeRateType.fixed: + return _market?.to; + } + } + + void Function(String)? _onError; + + Currency? get from => _from; + Currency? get to => _to; + + void setCurrencies(Currency from, Currency to) { + _from = from; + _to = to; + } + + String get warning { + if (reversed) { + if (toTicker != null && toAmount != null) { + if (minAmount != null && toAmount! < minAmount!) { + return "Minimum amount ${minAmount!.toString()} ${toTicker!.toUpperCase()}"; + } else if (maxAmount != null && toAmount! > maxAmount!) { + return "Maximum amount ${maxAmount!.toString()} ${toTicker!.toUpperCase()}"; + } + } + } else { + if (fromTicker != null && fromAmount != null) { + if (minAmount != null && fromAmount! < minAmount!) { + return "Minimum amount ${minAmount!.toString()} ${fromTicker!.toUpperCase()}"; + } else if (maxAmount != null && fromAmount! > maxAmount!) { + return "Maximum amount ${maxAmount!.toString()} ${fromTicker!.toUpperCase()}"; + } + } + } + + return ""; + } + + String get fromAmountString => fromAmount?.toStringAsFixed(8) ?? ""; + String get toAmountString => toAmount?.toStringAsFixed(8) ?? ""; + + bool get canExchange { + switch (exchangeType) { + case ExchangeRateType.estimated: + return fromAmount != null && + fromAmount != Decimal.zero && + toAmount != null && + rate != null && + warning.isEmpty; + case ExchangeRateType.fixed: + return _market != null && + fromAmount != null && + toAmount != null && + warning.isEmpty; + } + } + + void clearAmounts(bool shouldNotifyListeners) { + fromAmount = null; + toAmount = null; + minAmount = null; + maxAmount = null; + rate = null; + + if (shouldNotifyListeners) { + notifyListeners(); + } + } + + Future setFromAmountAndCalculateToAmount( + Decimal newFromAmount, + bool shouldNotifyListeners, + ) async { + if (newFromAmount == Decimal.zero) { + toAmount = Decimal.zero; + } + + fromAmount = newFromAmount; + reversed = false; + + await updateRanges(shouldNotifyListeners: false); + + await updateEstimate( + shouldNotifyListeners: false, + reversed: reversed, + ); + + if (shouldNotifyListeners) { + notifyListeners(); + } + } + + Future setToAmountAndCalculateFromAmount( + Decimal newToAmount, + bool shouldNotifyListeners, + ) async { + if (newToAmount == Decimal.zero) { + fromAmount = Decimal.zero; + } + + toAmount = newToAmount; + reversed = true; + + await updateRanges(shouldNotifyListeners: false); + + await updateEstimate( + shouldNotifyListeners: false, + reversed: reversed, + ); + + if (shouldNotifyListeners) { + notifyListeners(); + } + } + + Future updateTo(Currency to, bool shouldNotifyListeners) async { + try { + _to = to; + if (_from == null) { + rate = null; + notifyListeners(); + return; + } + + await updateRanges(shouldNotifyListeners: false); + + await updateEstimate( + shouldNotifyListeners: false, + reversed: reversed, + ); + + debugPrint( + "_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$fromAmount _toAmount=$toAmount rate:$rate"); + + if (shouldNotifyListeners) { + notifyListeners(); + } + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Error); + } + } + + Future updateFrom(Currency from, bool shouldNotifyListeners) async { + try { + _from = from; + + if (_to == null) { + rate = null; + notifyListeners(); + return; + } + + await updateRanges(shouldNotifyListeners: false); + + await updateEstimate( + shouldNotifyListeners: false, + reversed: reversed, + ); + + debugPrint( + "_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$fromAmount _toAmount=$toAmount rate:$rate"); + if (shouldNotifyListeners) { + notifyListeners(); + } + } catch (e, s) { + Logging.instance.log("$e\n$s", level: LogLevel.Error); + } + } + + Future updateMarket( + FixedRateMarket? market, + bool shouldNotifyListeners, + ) async { + _market = market; + + if (_market == null) { + fromAmount = null; + toAmount = null; + } else { + if (fromAmount != null) { + if (fromAmount! <= Decimal.zero) { + toAmount = Decimal.zero; + } else { + await updateRanges(shouldNotifyListeners: false); + await updateEstimate( + shouldNotifyListeners: false, + reversed: reversed, + ); + } + } + } + + if (shouldNotifyListeners) { + notifyListeners(); + } + } + + void _onExchangeRateTypeChanged() { + print("_onExchangeRateTypeChanged"); + } + + void _onExchangeTypeChanged() { + print("_onExchangeTypeChanged"); + } + + Future updateRanges({required bool shouldNotifyListeners}) async { + final _fromTicker = reversed ? toTicker : fromTicker; + final _toTicker = reversed ? fromTicker : toTicker; + if (_fromTicker == null || _toTicker == null) { + Logging.instance.log( + "Tried to $runtimeType.updateRanges where (from: $_fromTicker || to: $_toTicker)", + level: LogLevel.Info, + ); + return; + } + final response = await exchange?.getRange( + _fromTicker, + _toTicker, + exchangeType == ExchangeRateType.fixed, + ); + + if (response?.value == null) { + Logging.instance.log( + "Tried to $runtimeType.updateRanges where response: $response", + level: LogLevel.Info, + ); + return; + } + + final range = response!.value!; + + minAmount = range.min; + maxAmount = range.max; + + debugPrint("updated range for $_fromTicker-$_toTicker: $range"); + + if (shouldNotifyListeners) { + notifyListeners(); + } + } + + Future updateEstimate({ + required bool shouldNotifyListeners, + required bool reversed, + }) async { + final amount = reversed ? toAmount : fromAmount; + if (fromTicker == null || + toTicker == null || + amount == null || + amount <= Decimal.zero) { + Logging.instance.log( + "Tried to $runtimeType.updateEstimate where (from: $fromTicker || to: $toTicker || amount: $amount)", + level: LogLevel.Info, + ); + return; + } + final response = await exchange?.getEstimate( + fromTicker!, + toTicker!, + amount, + exchangeType == ExchangeRateType.fixed, + reversed, + ); + + if (response?.value == null) { + Logging.instance.log( + "Tried to $runtimeType.updateEstimate where response: $response", + level: LogLevel.Info, + ); + return; + } + + estimate = response!.value!; + + if (reversed) { + fromAmount = estimate!.estimatedAmount; + } else { + toAmount = estimate!.estimatedAmount; + } + + rate = (toAmount! / fromAmount!).toDecimal(scaleOnInfinitePrecision: 12); + + debugPrint("updated estimate for $fromTicker-$toTicker: $estimate"); + + if (shouldNotifyListeners) { + notifyListeners(); + } + } + + void setOnError({ + required void Function(String)? onError, + bool shouldNotifyListeners = false, + }) { + _onError = onError; + if (shouldNotifyListeners) { + notifyListeners(); + } + } + + Future swap({FixedRateMarket? market}) async { + final Decimal? newToAmount = fromAmount; + final Decimal? newFromAmount = toAmount; + + fromAmount = newFromAmount; + toAmount = newToAmount; + + minAmount = null; + maxAmount = null; + + switch (exchangeType) { + case ExchangeRateType.estimated: + final Currency? newTo = from; + final Currency? newFrom = to; + + _to = newTo; + _from = newFrom; + + await updateRanges(shouldNotifyListeners: false); + + await updateEstimate( + shouldNotifyListeners: false, + reversed: reversed, + ); + break; + case ExchangeRateType.fixed: + await updateMarket(market, false); + break; + } + + notifyListeners(); + } +} diff --git a/lib/models/exchange/fixed_rate_exchange_form_state.dart b/lib/models/exchange/fixed_rate_exchange_form_state.dart index 65104ef8b..fcb686a73 100644 --- a/lib/models/exchange/fixed_rate_exchange_form_state.dart +++ b/lib/models/exchange/fixed_rate_exchange_form_state.dart @@ -1,179 +1,179 @@ -import 'package:decimal/decimal.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:stackwallet/models/exchange/change_now/cn_exchange_estimate.dart'; -import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart'; -import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; -import 'package:stackwallet/utilities/logger.dart'; - -class FixedRateExchangeFormState extends ChangeNotifier { - Decimal? _fromAmount; - Decimal? _toAmount; - - FixedRateMarket? _market; - FixedRateMarket? get market => _market; - - CNExchangeEstimate? _estimate; - CNExchangeEstimate? get estimate => _estimate; - - Decimal? get rate { - if (_estimate == null) { - return null; - } else { - return (_estimate!.toAmount / _estimate!.fromAmount) - .toDecimal(scaleOnInfinitePrecision: 12); - } - } - - Future swap(FixedRateMarket reverseFixedRateMarket) async { - final Decimal? tmp = _fromAmount; - _fromAmount = _toAmount; - _toAmount = tmp; - - await updateMarket(reverseFixedRateMarket, false); - await updateRateEstimate(CNEstimateType.direct); - _toAmount = _estimate?.toAmount ?? Decimal.zero; - notifyListeners(); - } - - String get fromAmountString => - _fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8); - String get toAmountString => - _toAmount == null ? "" : _toAmount!.toStringAsFixed(8); - - Future updateMarket( - FixedRateMarket? market, - bool shouldNotifyListeners, - ) async { - _market = market; - - if (_market == null) { - _fromAmount = null; - _toAmount = null; - } else { - if (_fromAmount != null) { - if (_fromAmount! <= Decimal.zero) { - _toAmount = Decimal.zero; - } else { - await updateRateEstimate(CNEstimateType.direct); - } - } - } - - if (shouldNotifyListeners) { - notifyListeners(); - } - } - - String get rateDisplayString { - if (_market == null || _estimate == null) { - return "N/A"; - } else { - return "1 ${_estimate!.fromCurrency.toUpperCase()} ~${rate!.toStringAsFixed(8)} ${_estimate!.toCurrency.toUpperCase()}"; - } - } - - bool get canExchange { - return _market != null && - _fromAmount != null && - _toAmount != null && - sendAmountWarning.isEmpty; - } - - String get sendAmountWarning { - if (_market != null && _fromAmount != null) { - if (_fromAmount! < _market!.min) { - return "Minimum amount ${_market!.min.toString()} ${_market!.from.toUpperCase()}"; - } else if (_fromAmount! > _market!.max) { - return "Maximum amount ${_market!.max.toString()} ${_market!.from.toUpperCase()}"; - } - } - - return ""; - } - - Future setToAmountAndCalculateFromAmount( - Decimal newToAmount, - bool shouldNotifyListeners, - ) async { - _toAmount = newToAmount; - - if (shouldNotifyListeners) { - await updateRateEstimate(CNEstimateType.reverse); - notifyListeners(); - } - } - - Future setFromAmountAndCalculateToAmount( - Decimal newFromAmount, - bool shouldNotifyListeners, - ) async { - _fromAmount = newFromAmount; - - if (shouldNotifyListeners) { - await updateRateEstimate(CNEstimateType.direct); - notifyListeners(); - } - } - - void Function(String)? _onError; - - void setOnError({ - required void Function(String)? onError, - bool shouldNotifyListeners = false, - }) { - _onError = onError; - if (shouldNotifyListeners) { - notifyListeners(); - } - } - - Future updateRateEstimate(CNEstimateType direction) async { - if (market != null) { - Decimal? amount; - // set amount based on trade estimate direction - switch (direction) { - case CNEstimateType.direct: - if (_fromAmount != null - // && - // market!.min >= _fromAmount! && - // _fromAmount! <= market!.max - ) { - amount = _fromAmount!; - } - break; - case CNEstimateType.reverse: - if (_toAmount != null - // && - // market!.min >= _toAmount! && - // _toAmount! <= market!.max - ) { - amount = _toAmount!; - } - break; - } - - if (amount != null && market != null && amount > Decimal.zero) { - final response = - await ChangeNowAPI.instance.getEstimatedExchangeAmountV2( - fromTicker: market!.from, - toTicker: market!.to, - fromOrTo: direction, - flow: CNFlowType.fixedRate, - amount: amount, - ); - - if (response.value != null) { - // update estimate if response succeeded - _estimate = response.value; - - _toAmount = _estimate?.toAmount; - _fromAmount = _estimate?.fromAmount; - notifyListeners(); - } else if (response.exception != null) { - Logging.instance.log("updateRateEstimate(): ${response.exception}", - level: LogLevel.Warning); - } - } - } - } -} +// import 'package:decimal/decimal.dart'; +// import 'package:flutter/cupertino.dart'; +// import 'package:stackwallet/models/exchange/change_now/cn_exchange_estimate.dart'; +// import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart'; +// import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; +// import 'package:stackwallet/utilities/logger.dart'; +// +// class FixedRateExchangeFormState extends ChangeNotifier { +// Decimal? _fromAmount; +// Decimal? _toAmount; +// +// FixedRateMarket? _market; +// FixedRateMarket? get market => _market; +// +// CNExchangeEstimate? _estimate; +// CNExchangeEstimate? get estimate => _estimate; +// +// Decimal? get rate { +// if (_estimate == null) { +// return null; +// } else { +// return (_estimate!.toAmount / _estimate!.fromAmount) +// .toDecimal(scaleOnInfinitePrecision: 12); +// } +// } +// +// Future swap(FixedRateMarket reverseFixedRateMarket) async { +// final Decimal? tmp = _fromAmount; +// _fromAmount = _toAmount; +// _toAmount = tmp; +// +// await updateMarket(reverseFixedRateMarket, false); +// await updateRateEstimate(CNEstimateType.direct); +// _toAmount = _estimate?.toAmount ?? Decimal.zero; +// notifyListeners(); +// } +// +// String get fromAmountString => +// _fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8); +// String get toAmountString => +// _toAmount == null ? "" : _toAmount!.toStringAsFixed(8); +// +// Future updateMarket( +// FixedRateMarket? market, +// bool shouldNotifyListeners, +// ) async { +// _market = market; +// +// if (_market == null) { +// _fromAmount = null; +// _toAmount = null; +// } else { +// if (_fromAmount != null) { +// if (_fromAmount! <= Decimal.zero) { +// _toAmount = Decimal.zero; +// } else { +// await updateRateEstimate(CNEstimateType.direct); +// } +// } +// } +// +// if (shouldNotifyListeners) { +// notifyListeners(); +// } +// } +// +// String get rateDisplayString { +// if (_market == null || _estimate == null) { +// return "N/A"; +// } else { +// return "1 ${_estimate!.fromCurrency.toUpperCase()} ~${rate!.toStringAsFixed(8)} ${_estimate!.toCurrency.toUpperCase()}"; +// } +// } +// +// bool get canExchange { +// return _market != null && +// _fromAmount != null && +// _toAmount != null && +// sendAmountWarning.isEmpty; +// } +// +// String get sendAmountWarning { +// if (_market != null && _fromAmount != null) { +// if (_fromAmount! < _market!.min) { +// return "Minimum amount ${_market!.min.toString()} ${_market!.from.toUpperCase()}"; +// } else if (_fromAmount! > _market!.max) { +// return "Maximum amount ${_market!.max.toString()} ${_market!.from.toUpperCase()}"; +// } +// } +// +// return ""; +// } +// +// Future setToAmountAndCalculateFromAmount( +// Decimal newToAmount, +// bool shouldNotifyListeners, +// ) async { +// _toAmount = newToAmount; +// +// if (shouldNotifyListeners) { +// await updateRateEstimate(CNEstimateType.reverse); +// notifyListeners(); +// } +// } +// +// Future setFromAmountAndCalculateToAmount( +// Decimal newFromAmount, +// bool shouldNotifyListeners, +// ) async { +// _fromAmount = newFromAmount; +// +// if (shouldNotifyListeners) { +// await updateRateEstimate(CNEstimateType.direct); +// notifyListeners(); +// } +// } +// +// void Function(String)? _onError; +// +// void setOnError({ +// required void Function(String)? onError, +// bool shouldNotifyListeners = false, +// }) { +// _onError = onError; +// if (shouldNotifyListeners) { +// notifyListeners(); +// } +// } +// +// Future updateRateEstimate(CNEstimateType direction) async { +// if (market != null) { +// Decimal? amount; +// // set amount based on trade estimate direction +// switch (direction) { +// case CNEstimateType.direct: +// if (_fromAmount != null +// // && +// // market!.min >= _fromAmount! && +// // _fromAmount! <= market!.max +// ) { +// amount = _fromAmount!; +// } +// break; +// case CNEstimateType.reverse: +// if (_toAmount != null +// // && +// // market!.min >= _toAmount! && +// // _toAmount! <= market!.max +// ) { +// amount = _toAmount!; +// } +// break; +// } +// +// if (amount != null && market != null && amount > Decimal.zero) { +// final response = +// await ChangeNowAPI.instance.getEstimatedExchangeAmountV2( +// fromTicker: market!.from, +// toTicker: market!.to, +// fromOrTo: direction, +// flow: CNFlowType.fixedRate, +// amount: amount, +// ); +// +// if (response.value != null) { +// // update estimate if response succeeded +// _estimate = response.value; +// +// _toAmount = _estimate?.toAmount; +// _fromAmount = _estimate?.fromAmount; +// notifyListeners(); +// } else if (response.exception != null) { +// Logging.instance.log("updateRateEstimate(): ${response.exception}", +// level: LogLevel.Warning); +// } +// } +// } +// } +// } diff --git a/lib/pages/exchange_view/exchange_form.dart b/lib/pages/exchange_view/exchange_form.dart index b1bb739b6..62ea9d34b 100644 --- a/lib/pages/exchange_view/exchange_form.dart +++ b/lib/pages/exchange_view/exchange_form.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/models/exchange/exchange_form_state.dart'; import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; import 'package:stackwallet/models/exchange/response_objects/currency.dart'; import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart'; @@ -21,9 +22,7 @@ import 'package:stackwallet/pages/exchange_view/sub_widgets/rate_type_toggle.dar import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart'; import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_state_provider.dart'; import 'package:stackwallet/providers/exchange/change_now_provider.dart'; -import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_provider.dart'; -import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart'; import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/utilities/assets.dart'; @@ -62,42 +61,27 @@ class _ExchangeFormState extends ConsumerState { final FocusNode _receiveFocusNode = FocusNode(); bool _swapLock = false; - bool _reversed = false; void sendFieldOnChanged(String value) async { final newFromAmount = Decimal.tryParse(value); - final isEstimated = - ref.read(prefsChangeNotifierProvider).exchangeRateType == - ExchangeRateType.estimated; - if (newFromAmount != null) { - if (isEstimated) { - await ref - .read(estimatedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(newFromAmount, false); - } else { - await ref - .read(fixedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(newFromAmount, false); - } - } else { - if (isEstimated) { - await ref - .read(estimatedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(Decimal.zero, false); - } else { - await ref - .read(fixedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(Decimal.zero, false); - } - _receiveController.text = isEstimated ? "-" : ""; + + ref.read(exchangeFormStateProvider).fromAmount = + newFromAmount ?? Decimal.zero; + + if (newFromAmount == null) { + _receiveController.text = + ref.read(prefsChangeNotifierProvider).exchangeRateType == + ExchangeRateType.estimated + ? "-" + : ""; } } void selectSendCurrency() async { if (ref.read(prefsChangeNotifierProvider).exchangeRateType == ExchangeRateType.estimated) { - final fromTicker = - ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "-"; + final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? "-"; + // ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "-"; if (walletInitiated && fromTicker.toLowerCase() == coin!.ticker.toLowerCase()) { @@ -108,16 +92,13 @@ class _ExchangeFormState extends ConsumerState { await _showFloatingRateSelectionSheet( currencies: ref.read(availableChangeNowCurrenciesStateProvider.state).state, - excludedTicker: - ref.read(estimatedRateExchangeFormProvider).to?.ticker ?? "-", + excludedTicker: ref.read(exchangeFormStateProvider).toTicker ?? "-", fromTicker: fromTicker, - onSelected: (from) => ref - .read(estimatedRateExchangeFormProvider) - .updateFrom(from, true)); + onSelected: (from) => + ref.read(exchangeFormStateProvider).updateFrom(from, true)); } else { - final toTicker = ref.read(fixedRateExchangeFormProvider).market?.to ?? ""; - final fromTicker = - ref.read(fixedRateExchangeFormProvider).market?.from ?? ""; + final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? ""; + final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? ""; if (walletInitiated && fromTicker.toLowerCase() == coin!.ticker.toLowerCase()) { @@ -138,7 +119,7 @@ class _ExchangeFormState extends ConsumerState { ); await ref - .read(fixedRateExchangeFormProvider) + .read(exchangeFormStateProvider) .updateMarket(market, true); } catch (e) { unawaited(showDialog( @@ -158,8 +139,7 @@ class _ExchangeFormState extends ConsumerState { void selectReceiveCurrency() async { if (ref.read(prefsChangeNotifierProvider).exchangeRateType == ExchangeRateType.estimated) { - final toTicker = - ref.read(estimatedRateExchangeFormProvider).to?.ticker ?? ""; + final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? ""; if (walletInitiated && toTicker.toLowerCase() == coin!.ticker.toLowerCase()) { @@ -170,16 +150,13 @@ class _ExchangeFormState extends ConsumerState { await _showFloatingRateSelectionSheet( currencies: ref.read(availableChangeNowCurrenciesStateProvider.state).state, - excludedTicker: - ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "", - fromTicker: - ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "", + excludedTicker: ref.read(exchangeFormStateProvider).fromTicker ?? "", + fromTicker: ref.read(exchangeFormStateProvider).fromTicker ?? "", onSelected: (to) => - ref.read(estimatedRateExchangeFormProvider).updateTo(to, true)); + ref.read(exchangeFormStateProvider).updateTo(to, true)); } else { - final fromTicker = - ref.read(fixedRateExchangeFormProvider).market?.from ?? ""; - final toTicker = ref.read(fixedRateExchangeFormProvider).market?.to ?? ""; + final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? ""; + final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? ""; if (walletInitiated && toTicker.toLowerCase() == coin!.ticker.toLowerCase()) { @@ -200,7 +177,7 @@ class _ExchangeFormState extends ConsumerState { ); await ref - .read(fixedRateExchangeFormProvider) + .read(exchangeFormStateProvider) .updateMarket(market, true); } catch (e) { unawaited(showDialog( @@ -222,28 +199,11 @@ class _ExchangeFormState extends ConsumerState { final isEstimated = ref.read(prefsChangeNotifierProvider).exchangeRateType == ExchangeRateType.estimated; - if (newToAmount != null) { - if (isEstimated) { - // await ref - // .read(estimatedRateExchangeFormProvider) - // .setToAmountAndCalculateFromAmount( - // newToAmount, false); - } else { - await ref - .read(fixedRateExchangeFormProvider) - .setToAmountAndCalculateFromAmount(newToAmount, false); - } - } else { - if (isEstimated) { - // await ref - // .read(estimatedRateExchangeFormProvider) - // .setToAmountAndCalculateFromAmount( - // Decimal.zero, false); - } else { - await ref - .read(fixedRateExchangeFormProvider) - .setToAmountAndCalculateFromAmount(Decimal.zero, false); - } + if (!isEstimated) { + ref.read(exchangeFormStateProvider).toAmount = + newToAmount ?? Decimal.zero; + } + if (newToAmount == null) { _sendController.text = ""; } } @@ -276,10 +236,10 @@ class _ExchangeFormState extends ConsumerState { if (ref.watch(prefsChangeNotifierProvider .select((pref) => pref.exchangeRateType)) == ExchangeRateType.estimated) { - await ref.read(estimatedRateExchangeFormProvider).swap(); + await ref.read(exchangeFormStateProvider).swap(); } else { - final from = ref.read(fixedRateExchangeFormProvider).market?.from; - final to = ref.read(fixedRateExchangeFormProvider).market?.to; + final from = ref.read(exchangeFormStateProvider).fromTicker; + final to = ref.read(exchangeFormStateProvider).toTicker; if (to != null && from != null) { final markets = ref @@ -288,7 +248,7 @@ class _ExchangeFormState extends ConsumerState { .where((e) => e.from == to && e.to == from); if (markets.isNotEmpty) { - await ref.read(fixedRateExchangeFormProvider).swap(markets.first); + await ref.read(exchangeFormStateProvider).swap(market: markets.first); } } } @@ -351,7 +311,7 @@ class _ExchangeFormState extends ConsumerState { } } - String? _fetchIconUrlFromTickerForFixedRateFlow(String? ticker) { + String? _fetchIconUrlFromTicker(String? ticker) { if (ticker == null) return null; final possibleCurrencies = ref @@ -417,15 +377,35 @@ class _ExchangeFormState extends ConsumerState { void onRateTypeChanged(ExchangeRateType rateType) async { _receiveFocusNode.unfocus(); _sendFocusNode.unfocus(); + + unawaited( + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => WillPopScope( + onWillPop: () async => false, + child: Container( + color: Theme.of(context) + .extension()! + .overlay + .withOpacity(0.6), + child: const CustomLoadingOverlay( + message: "Updating exchange rate", + eventBus: null, + ), + ), + ), + ), + ); + + final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? "-"; + final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? "-"; + + ref.read(exchangeFormStateProvider).exchangeType = rateType; + ref.read(exchangeFormStateProvider).reversed = false; switch (rateType) { case ExchangeRateType.estimated: - final market = ref.read(fixedRateExchangeFormProvider).market; - final fromTicker = market?.from ?? ""; - final toTicker = market?.to ?? ""; - if (!(fromTicker.isEmpty || - toTicker.isEmpty || - toTicker == "-" || - fromTicker == "-")) { + if (!(toTicker == "-" || fromTicker == "-")) { final available = ref .read(availableFloatingRatePairsStateProvider.state) .state @@ -442,28 +422,29 @@ class _ExchangeFormState extends ConsumerState { availableCurrencies.firstWhere((e) => e.ticker == toTicker); final newFromAmount = Decimal.tryParse(_sendController.text); - if (newFromAmount != null) { - await ref - .read(estimatedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(newFromAmount, false); - } else { - await ref - .read(estimatedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(Decimal.zero, false); - + ref.read(exchangeFormStateProvider).fromAmount = + newFromAmount ?? Decimal.zero; + if (newFromAmount == null) { _receiveController.text = ""; } - await ref - .read(estimatedRateExchangeFormProvider) - .updateTo(to, false); - await ref - .read(estimatedRateExchangeFormProvider) - .updateFrom(from, true); + await ref.read(exchangeFormStateProvider).updateTo(to, false); + await ref.read(exchangeFormStateProvider).updateFrom(from, true); + + _receiveController.text = + ref.read(exchangeFormStateProvider).toAmountString.isEmpty + ? "-" + : ref.read(exchangeFormStateProvider).toAmountString; + if (mounted) { + Navigator.of(context).pop(); + } return; } } } + if (mounted) { + Navigator.of(context).pop(); + } unawaited(showFloatingFlushBar( type: FlushBarType.warning, message: @@ -472,14 +453,7 @@ class _ExchangeFormState extends ConsumerState { )); break; case ExchangeRateType.fixed: - final fromTicker = - ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? ""; - final toTicker = - ref.read(estimatedRateExchangeFormProvider).to?.ticker ?? ""; - if (!(fromTicker.isEmpty || - toTicker.isEmpty || - toTicker == "-" || - fromTicker == "-")) { + if (!(toTicker == "-" || fromTicker == "-")) { FixedRateMarket? market; try { market = ref @@ -491,29 +465,28 @@ class _ExchangeFormState extends ConsumerState { } final newFromAmount = Decimal.tryParse(_sendController.text); - if (newFromAmount != null) { - await ref - .read(fixedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(newFromAmount, false); - } else { - await ref - .read(fixedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(Decimal.zero, false); + ref.read(exchangeFormStateProvider).fromAmount = + newFromAmount ?? Decimal.zero; + if (newFromAmount == null) { _receiveController.text = ""; } + await ref.read(exchangeFormStateProvider).updateMarket(market, false); await ref - .read(fixedRateExchangeFormProvider) - .updateMarket(market, false); - await ref - .read(fixedRateExchangeFormProvider) + .read(exchangeFormStateProvider) .setFromAmountAndCalculateToAmount( Decimal.tryParse(_sendController.text) ?? Decimal.zero, true, ); + if (mounted) { + Navigator.of(context).pop(); + } return; } + if (mounted) { + Navigator.of(context).pop(); + } unawaited(showFloatingFlushBar( type: FlushBarType.warning, message: @@ -527,10 +500,8 @@ class _ExchangeFormState extends ConsumerState { void onExchangePressed() async { if (ref.read(prefsChangeNotifierProvider).exchangeRateType == ExchangeRateType.estimated) { - final fromTicker = - ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? ""; - final toTicker = - ref.read(estimatedRateExchangeFormProvider).to?.ticker ?? ""; + final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? ""; + final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? ""; bool isAvailable = false; final availableFloatingPairs = @@ -555,8 +526,7 @@ class _ExchangeFormState extends ConsumerState { return; } - final sendAmount = Decimal.parse( - ref.read(estimatedRateExchangeFormProvider).fromAmountString); + final sendAmount = ref.read(exchangeFormStateProvider).fromAmount!; final rateType = ref.read(prefsChangeNotifierProvider).exchangeRateType; @@ -610,12 +580,10 @@ class _ExchangeFormState extends ConsumerState { } } } else { - final fromTicker = - ref.read(fixedRateExchangeFormProvider).market?.from ?? ""; - final toTicker = ref.read(fixedRateExchangeFormProvider).market?.to ?? ""; + final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? ""; + final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? ""; - final sendAmount = Decimal.parse( - ref.read(fixedRateExchangeFormProvider).fromAmountString); + final sendAmount = ref.read(exchangeFormStateProvider).fromAmount!; final rateType = ref.read(prefsChangeNotifierProvider).exchangeRateType; @@ -683,7 +651,7 @@ class _ExchangeFormState extends ConsumerState { } String rate = - "1 ${fromTicker.toUpperCase()} ~${ref.read(fixedRateExchangeFormProvider).rate!.toStringAsFixed(8)} ${toTicker.toUpperCase()}"; + "1 ${fromTicker.toUpperCase()} ~${ref.read(exchangeFormStateProvider).rate!.toStringAsFixed(8)} ${toTicker.toUpperCase()}"; final model = IncompleteExchangeModel( sendTicker: fromTicker, @@ -721,21 +689,10 @@ class _ExchangeFormState extends ConsumerState { String? ticker; - if (ref.read(prefsChangeNotifierProvider).exchangeRateType == - ExchangeRateType.estimated) { - if (isSend) { - ticker = ref.watch(estimatedRateExchangeFormProvider - .select((value) => value.from?.ticker)); - } else { - ticker = ref.watch(estimatedRateExchangeFormProvider - .select((value) => value.to?.ticker)); - } + if (isSend) { + ticker = ref.read(exchangeFormStateProvider).fromTicker; } else { - if (isSend) { - ticker = ref.read(fixedRateExchangeFormProvider).market?.from; - } else { - ticker = ref.read(fixedRateExchangeFormProvider).market?.to; - } + ticker = ref.read(exchangeFormStateProvider).toTicker; } if (ticker == null) { @@ -756,46 +713,29 @@ class _ExchangeFormState extends ConsumerState { if (walletInitiated) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - ref.read(estimatedRateExchangeFormProvider).clearAmounts(true); + ref.read(exchangeFormStateProvider).clearAmounts(true); // ref.read(fixedRateExchangeFormProvider); }); } else { final isEstimated = ref.read(prefsChangeNotifierProvider).exchangeRateType == ExchangeRateType.estimated; - _sendController.text = isEstimated - ? ref.read(estimatedRateExchangeFormProvider).fromAmountString - : ref.read(fixedRateExchangeFormProvider).fromAmountString; + _sendController.text = + ref.read(exchangeFormStateProvider).fromAmountString; _receiveController.text = isEstimated ? "-" //ref.read(estimatedRateExchangeFormProvider).toAmountString - : ref.read(fixedRateExchangeFormProvider).toAmountString; + : ref.read(exchangeFormStateProvider).toAmountString; } _sendFocusNode.addListener(() async { if (!_sendFocusNode.hasFocus) { final newFromAmount = Decimal.tryParse(_sendController.text); - if (newFromAmount != null) { - if (ref.read(prefsChangeNotifierProvider).exchangeRateType == - ExchangeRateType.estimated) { - await ref - .read(estimatedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(newFromAmount, true); - } else { - await ref - .read(fixedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(newFromAmount, true); - } - } else { - if (ref.read(prefsChangeNotifierProvider).exchangeRateType == - ExchangeRateType.estimated) { - await ref - .read(estimatedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(Decimal.zero, true); - } else { - await ref - .read(fixedRateExchangeFormProvider) - .setFromAmountAndCalculateToAmount(Decimal.zero, true); - } + await ref + .read(exchangeFormStateProvider) + .setFromAmountAndCalculateToAmount( + newFromAmount ?? Decimal.zero, true); + + if (newFromAmount == null) { _receiveController.text = ref.read(prefsChangeNotifierProvider).exchangeRateType == ExchangeRateType.estimated @@ -807,28 +747,14 @@ class _ExchangeFormState extends ConsumerState { _receiveFocusNode.addListener(() async { if (!_receiveFocusNode.hasFocus) { final newToAmount = Decimal.tryParse(_receiveController.text); - if (newToAmount != null) { - if (ref.read(prefsChangeNotifierProvider).exchangeRateType == - ExchangeRateType.estimated) { - // await ref - // .read(estimatedRateExchangeFormProvider) - // .setToAmountAndCalculateFromAmount(newToAmount, true); - } else { - await ref - .read(fixedRateExchangeFormProvider) - .setToAmountAndCalculateFromAmount(newToAmount, true); - } - } else { - if (ref.read(prefsChangeNotifierProvider).exchangeRateType == - ExchangeRateType.estimated) { - // await ref - // .read(estimatedRateExchangeFormProvider) - // .setToAmountAndCalculateFromAmount(Decimal.zero, true); - } else { - await ref - .read(fixedRateExchangeFormProvider) - .setToAmountAndCalculateFromAmount(Decimal.zero, true); - } + if (ref.read(prefsChangeNotifierProvider).exchangeRateType != + ExchangeRateType.estimated) { + await ref + .read(exchangeFormStateProvider) + .setToAmountAndCalculateFromAmount( + newToAmount ?? Decimal.zero, true); + } + if (newToAmount == null) { _sendController.text = ""; } } @@ -853,39 +779,29 @@ class _ExchangeFormState extends ConsumerState { ExchangeRateType.estimated; ref.listen( - isEstimated - ? estimatedRateExchangeFormProvider - .select((value) => value.toAmountString) - : fixedRateExchangeFormProvider.select( - (value) => value.toAmountString), (previous, String next) { + exchangeFormStateProvider.select((value) => value.toAmountString), + (previous, String next) { if (!_receiveFocusNode.hasFocus) { _receiveController.text = isEstimated && next.isEmpty ? "-" : next; debugPrint("RECEIVE AMOUNT LISTENER ACTIVATED"); if (_swapLock) { - _sendController.text = isEstimated - ? ref.read(estimatedRateExchangeFormProvider).fromAmountString - : ref.read(fixedRateExchangeFormProvider).fromAmountString; + _sendController.text = + ref.read(exchangeFormStateProvider).fromAmountString; } } }); ref.listen( - isEstimated - ? estimatedRateExchangeFormProvider - .select((value) => value.fromAmountString) - : fixedRateExchangeFormProvider.select( - (value) => value.fromAmountString), (previous, String next) { + exchangeFormStateProvider.select((value) => value.fromAmountString), + (previous, String next) { if (!_sendFocusNode.hasFocus) { _sendController.text = next; debugPrint("SEND AMOUNT LISTENER ACTIVATED"); if (_swapLock) { _receiveController.text = isEstimated - ? ref - .read(estimatedRateExchangeFormProvider) - .toAmountString - .isEmpty + ? ref.read(exchangeFormStateProvider).toAmountString.isEmpty ? "-" - : ref.read(estimatedRateExchangeFormProvider).toAmountString - : ref.read(fixedRateExchangeFormProvider).toAmountString; + : ref.read(exchangeFormStateProvider).toAmountString + : ref.read(exchangeFormStateProvider).toAmountString; } } }); @@ -956,20 +872,10 @@ class _ExchangeFormState extends ConsumerState { ), child: Builder( builder: (context) { - String? image; - if (ref.watch(prefsChangeNotifierProvider.select( - (value) => value.exchangeRateType)) == - ExchangeRateType.estimated) { - image = ref - .watch(estimatedRateExchangeFormProvider - .select((value) => value.from)) - ?.image; - } else { - image = _fetchIconUrlFromTickerForFixedRateFlow( - ref.watch( - fixedRateExchangeFormProvider.select( - (value) => value.market?.from))); - } + final image = _fetchIconUrlFromTicker(ref.watch( + exchangeFormStateProvider + .select((value) => value.fromTicker))); + if (image != null && image.isNotEmpty) { return Center( child: SvgPicture.network( @@ -1020,17 +926,9 @@ class _ExchangeFormState extends ConsumerState { width: 6, ), Text( - ref.watch(prefsChangeNotifierProvider.select( - (value) => value.exchangeRateType)) == - ExchangeRateType.estimated - ? ref.watch(estimatedRateExchangeFormProvider - .select((value) => - value.from?.ticker.toUpperCase())) ?? - "-" - : ref.watch(fixedRateExchangeFormProvider.select( - (value) => - value.market?.from.toUpperCase())) ?? - "-", + ref.watch(exchangeFormStateProvider.select((value) => + value.fromTicker?.toUpperCase())) ?? + "-", style: STextStyles.smallMed14(context).copyWith( color: Theme.of(context) .extension()! @@ -1105,14 +1003,13 @@ class _ExchangeFormState extends ConsumerState { ), Positioned.fill( child: Align( - alignment: Alignment.topRight, + alignment: ref.watch(exchangeFormStateProvider + .select((value) => value.reversed)) + ? Alignment.bottomRight + : Alignment.topRight, child: Text( - ref.read(prefsChangeNotifierProvider).exchangeRateType == - ExchangeRateType.estimated - ? ref.watch(estimatedRateExchangeFormProvider - .select((value) => value.minimumSendWarning)) - : ref.watch(fixedRateExchangeFormProvider - .select((value) => value.sendAmountWarning)), + ref.watch(exchangeFormStateProvider + .select((value) => value.warning)), style: STextStyles.errorSmall(context), ), ), @@ -1179,19 +1076,10 @@ class _ExchangeFormState extends ConsumerState { ), child: Builder( builder: (context) { - String? image; - if (ref.watch(prefsChangeNotifierProvider.select( - (value) => value.exchangeRateType)) == - ExchangeRateType.estimated) { - image = ref - .watch(estimatedRateExchangeFormProvider - .select((value) => value.to)) - ?.image; - } else { - image = _fetchIconUrlFromTickerForFixedRateFlow( - ref.watch(fixedRateExchangeFormProvider - .select((value) => value.market?.to))); - } + final image = _fetchIconUrlFromTicker(ref.watch( + exchangeFormStateProvider + .select((value) => value.toTicker))); + if (image != null && image.isNotEmpty) { return Center( child: SvgPicture.network( @@ -1240,17 +1128,9 @@ class _ExchangeFormState extends ConsumerState { width: 6, ), Text( - ref.watch(prefsChangeNotifierProvider.select( - (value) => value.exchangeRateType)) == - ExchangeRateType.estimated - ? ref.watch(estimatedRateExchangeFormProvider - .select((value) => - value.to?.ticker.toUpperCase())) ?? - "-" - : ref.watch(fixedRateExchangeFormProvider.select( - (value) => - value.market?.to.toUpperCase())) ?? - "-", + ref.watch(exchangeFormStateProvider.select( + (value) => value.toTicker?.toUpperCase())) ?? + "-", style: STextStyles.smallMed14(context).copyWith( color: Theme.of(context) .extension()! @@ -1293,24 +1173,17 @@ class _ExchangeFormState extends ConsumerState { fixedRate: ref.watch(prefsChangeNotifierProvider .select((value) => value.exchangeRateType)) == ExchangeRateType.fixed, - reversed: _reversed, + reversed: ref.watch( + exchangeFormStateProvider.select((value) => value.reversed)), ), const SizedBox( height: 12, ), PrimaryButton( - enabled: ((ref.read(prefsChangeNotifierProvider).exchangeRateType == - ExchangeRateType.estimated) - ? ref.watch(estimatedRateExchangeFormProvider + enabled: ref.watch( + exchangeFormStateProvider.select((value) => value.canExchange)), + onPressed: ref.watch(exchangeFormStateProvider .select((value) => value.canExchange)) - : ref.watch(fixedRateExchangeFormProvider - .select((value) => value.canExchange))), - onPressed: ((ref.read(prefsChangeNotifierProvider).exchangeRateType == - ExchangeRateType.estimated) - ? ref.watch(estimatedRateExchangeFormProvider - .select((value) => value.canExchange)) - : ref.watch(fixedRateExchangeFormProvider - .select((value) => value.canExchange))) ? onExchangePressed : null, label: "Exchange", diff --git a/lib/pages/exchange_view/exchange_view.dart b/lib/pages/exchange_view/exchange_view.dart index 0b0ff9eda..1b66994f4 100644 --- a/lib/pages/exchange_view/exchange_view.dart +++ b/lib/pages/exchange_view/exchange_view.dart @@ -2,22 +2,14 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:flutter_svg/flutter_svg.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/pages/exchange_view/exchange_form.dart'; -import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; import 'package:stackwallet/pages/exchange_view/trade_details_view.dart'; -import 'package:stackwallet/providers/exchange/changenow_initial_load_status.dart'; -import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; -import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart'; import 'package:stackwallet/providers/exchange/trade_sent_from_stack_lookup_provider.dart'; import 'package:stackwallet/providers/global/trades_service_provider.dart'; import 'package:stackwallet/providers/providers.dart'; -import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; -import 'package:stackwallet/widgets/stack_dialog.dart'; import 'package:stackwallet/widgets/trade_card.dart'; import 'package:tuple/tuple.dart'; @@ -191,137 +183,3 @@ class _ExchangeViewState extends ConsumerState { ); } } - -class RateInfo extends ConsumerWidget { - const RateInfo({ - Key? key, - required this.onChanged, - }) : super(key: key); - - final void Function(ExchangeRateType) onChanged; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final type = ref.watch( - prefsChangeNotifierProvider.select((pref) => pref.exchangeRateType)); - final isEstimated = type == ExchangeRateType.estimated; - - return Container( - decoration: BoxDecoration( - color: Theme.of(context).extension()!.popupBG, - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - TextButton( - onPressed: kFixedRateEnabled - ? () async { - if (isEstimated) { - if (ref - .read( - changeNowFixedInitialLoadStatusStateProvider - .state) - .state == - ChangeNowLoadStatus.loading) { - bool userPoppedDialog = false; - await showDialog( - context: context, - builder: (context) => Consumer( - builder: (context, ref, __) { - return StackOkDialog( - title: "Loading rate data...", - message: - "Performing initial fetch of ChangeNOW fixed rate market data", - onOkPressed: (value) { - userPoppedDialog = value == "OK"; - }, - ); - }, - ), - ); - if (ref - .read( - changeNowFixedInitialLoadStatusStateProvider - .state) - .state == - ChangeNowLoadStatus.loading) { - return; - } - } - } - - unawaited(showModalBottomSheet( - backgroundColor: Colors.transparent, - context: context, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical( - top: Radius.circular(20), - ), - ), - builder: (_) => const ExchangeRateSheet(), - ).then((value) { - if (value is ExchangeRateType && value != type) { - onChanged(value); - } - })); - } - : null, - style: Theme.of(context).textButtonTheme.style?.copyWith( - minimumSize: MaterialStateProperty.all( - const Size(0, 0), - ), - padding: MaterialStateProperty.all( - const EdgeInsets.all(2), - ), - backgroundColor: MaterialStateProperty.all( - Colors.transparent, - ), - ), - child: Row( - children: [ - Text( - isEstimated ? "Estimated rate" : "Fixed rate", - style: STextStyles.itemSubtitle(context), - ), - const SizedBox( - width: 6, - ), - if (kFixedRateEnabled) - SvgPicture.asset( - Assets.svg.chevronDown, - width: 5, - height: 2.5, - color: Theme.of(context) - .extension()! - .infoItemLabel, - ), - ], - ), - ), - FittedBox( - fit: BoxFit.scaleDown, - child: ConstrainedBox( - constraints: const BoxConstraints( - minWidth: 1, - ), - child: Text( - isEstimated - ? ref.watch(estimatedRateExchangeFormProvider - .select((value) => value.rateDisplayString)) - : ref.watch(fixedRateExchangeFormProvider - .select((value) => value.rateDisplayString)), - style: STextStyles.itemSubtitle12(context), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/pages/home_view/sub_widgets/home_view_button_bar.dart b/lib/pages/home_view/sub_widgets/home_view_button_bar.dart index 3563df550..aebaf0da9 100644 --- a/lib/pages/home_view/sub_widgets/home_view_button_bar.dart +++ b/lib/pages/home_view/sub_widgets/home_view_button_bar.dart @@ -2,12 +2,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/models/exchange/exchange_form_state.dart'; import 'package:stackwallet/pages/exchange_view/exchange_view.dart'; import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart'; import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_state_provider.dart'; import 'package:stackwallet/providers/exchange/change_now_provider.dart'; -import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; -import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart'; import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; @@ -54,15 +53,15 @@ class _HomeViewButtonBarState extends ConsumerState { response2.value!; if (response.value!.length > 1) { - if (ref.read(estimatedRateExchangeFormProvider).from == null) { + if (ref.read(exchangeFormStateProvider).from == null) { if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) { - await ref.read(estimatedRateExchangeFormProvider).updateFrom( + await ref.read(exchangeFormStateProvider).updateFrom( response.value!.firstWhere((e) => e.ticker == "btc"), true); } } - if (ref.read(estimatedRateExchangeFormProvider).to == null) { + if (ref.read(exchangeFormStateProvider).to == null) { if (response.value!.where((e) => e.ticker == "doge").isNotEmpty) { - await ref.read(estimatedRateExchangeFormProvider).updateTo( + await ref.read(exchangeFormStateProvider).updateTo( response.value!.firstWhere((e) => e.ticker == "doge"), true); } } @@ -97,13 +96,13 @@ class _HomeViewButtonBarState extends ConsumerState { ref.read(fixedRateMarketPairsStateProvider.state).state = response3.value!; - if (ref.read(fixedRateExchangeFormProvider).market == null) { + if (ref.read(exchangeFormStateProvider).market == null) { final matchingMarkets = response3.value!.where((e) => e.to == "doge" && e.from == "btc"); if (matchingMarkets.isNotEmpty) { await ref - .read(fixedRateExchangeFormProvider) + .read(exchangeFormStateProvider) .updateMarket(matchingMarkets.first, true); } } @@ -126,7 +125,7 @@ class _HomeViewButtonBarState extends ConsumerState { @override void initState() { - ref.read(estimatedRateExchangeFormProvider).setOnError( + ref.read(exchangeFormStateProvider).setOnError( onError: (String message) => showDialog( context: context, barrierDismissible: true, diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 1a1918557..94e8c5fa6 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -5,6 +5,7 @@ import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/models/exchange/exchange_form_state.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart'; @@ -19,7 +20,6 @@ import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_navigation_bar. import 'package:stackwallet/pages/wallet_view/sub_widgets/wallet_summary.dart'; import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.dart'; import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart'; -import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; import 'package:stackwallet/providers/global/auto_swb_service_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; @@ -250,6 +250,8 @@ class _WalletViewState extends ConsumerState { final walletId = ref.read(managerProvider).walletId; ref.read(prefsChangeNotifierProvider).exchangeRateType = ExchangeRateType.estimated; + ref.read(exchangeFormStateProvider).exchangeType = + ExchangeRateType.estimated; final currencies = ref .read(availableChangeNowCurrenciesStateProvider.state) @@ -258,28 +260,31 @@ class _WalletViewState extends ConsumerState { element.ticker.toLowerCase() == coin.ticker.toLowerCase()); if (currencies.isNotEmpty) { - unawaited(ref - .read(estimatedRateExchangeFormProvider) - .updateFrom(currencies.first, false)); - unawaited(ref.read(estimatedRateExchangeFormProvider).updateTo( - ref - .read(availableChangeNowCurrenciesStateProvider.state) - .state - .firstWhere( - (element) => - element.ticker.toLowerCase() != coin.ticker.toLowerCase(), - ), - false)); + ref.read(exchangeFormStateProvider).setCurrencies( + currencies.first, + ref + .read(availableChangeNowCurrenciesStateProvider.state) + .state + .firstWhere( + (element) => + element.ticker.toLowerCase() != + coin.ticker.toLowerCase(), + ), + ); } - unawaited(Navigator.of(context).pushNamed( - WalletInitiatedExchangeView.routeName, - arguments: Tuple3( - walletId, - coin, - _loadCNData, - ), - )); + if (mounted) { + unawaited( + Navigator.of(context).pushNamed( + WalletInitiatedExchangeView.routeName, + arguments: Tuple3( + walletId, + coin, + _loadCNData, + ), + ), + ); + } } } diff --git a/lib/providers/exchange/estimate_rate_exchange_form_provider.dart b/lib/providers/exchange/estimate_rate_exchange_form_provider.dart index 9a59cb734..59705b54e 100644 --- a/lib/providers/exchange/estimate_rate_exchange_form_provider.dart +++ b/lib/providers/exchange/estimate_rate_exchange_form_provider.dart @@ -1,5 +1,5 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/models/exchange/estimated_rate_exchange_form_state.dart'; - -final estimatedRateExchangeFormProvider = - ChangeNotifierProvider((ref) => EstimatedRateExchangeFormState()); +// import 'package:flutter_riverpod/flutter_riverpod.dart'; +// import 'package:stackwallet/models/exchange/estimated_rate_exchange_form_state.dart'; +// +// final estimatedRateExchangeFormProvider = +// ChangeNotifierProvider((ref) => EstimatedRateExchangeFormState()); diff --git a/lib/providers/exchange/fixed_rate_exchange_form_provider.dart b/lib/providers/exchange/fixed_rate_exchange_form_provider.dart index 81b063d33..7f0af1cf1 100644 --- a/lib/providers/exchange/fixed_rate_exchange_form_provider.dart +++ b/lib/providers/exchange/fixed_rate_exchange_form_provider.dart @@ -1,6 +1,6 @@ -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/models/exchange/fixed_rate_exchange_form_state.dart'; - -final fixedRateExchangeFormProvider = - ChangeNotifierProvider( - (ref) => FixedRateExchangeFormState()); +// import 'package:flutter_riverpod/flutter_riverpod.dart'; +// import 'package:stackwallet/models/exchange/fixed_rate_exchange_form_state.dart'; +// +// final fixedRateExchangeFormProvider = +// ChangeNotifierProvider( +// (ref) => FixedRateExchangeFormState()); diff --git a/lib/services/exchange/change_now/change_now_loading_service.dart b/lib/services/exchange/change_now/change_now_loading_service.dart index d4412d864..0d25bccee 100644 --- a/lib/services/exchange/change_now/change_now_loading_service.dart +++ b/lib/services/exchange/change_now/change_now_loading_service.dart @@ -1,9 +1,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/models/exchange/exchange_form_state.dart'; import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart'; import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_state_provider.dart'; import 'package:stackwallet/providers/exchange/changenow_initial_load_status.dart'; -import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart'; -import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart'; import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart'; import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; @@ -38,7 +37,7 @@ class ChangeNowLoadingService { ref.read(fixedRateMarketPairsStateProvider.state).state = response3.value!; - if (ref.read(fixedRateExchangeFormProvider).market == null) { + if (ref.read(exchangeFormStateProvider).market == null) { String fromTicker = "btc"; String toTicker = "xmr"; @@ -50,7 +49,7 @@ class ChangeNowLoadingService { .where((e) => e.to == toTicker && e.from == fromTicker); if (matchingMarkets.isNotEmpty) { await ref - .read(fixedRateExchangeFormProvider) + .read(exchangeFormStateProvider) .updateMarket(matchingMarkets.first, true); } } @@ -99,18 +98,18 @@ class ChangeNowLoadingService { } if (response.value!.length > 1) { - if (ref.read(estimatedRateExchangeFormProvider).from == null) { + if (ref.read(exchangeFormStateProvider).from == null) { if (response.value! .where((e) => e.ticker == fromTicker) .isNotEmpty) { - await ref.read(estimatedRateExchangeFormProvider).updateFrom( + await ref.read(exchangeFormStateProvider).updateFrom( response.value!.firstWhere((e) => e.ticker == fromTicker), false); } } - if (ref.read(estimatedRateExchangeFormProvider).to == null) { + if (ref.read(exchangeFormStateProvider).to == null) { if (response.value!.where((e) => e.ticker == toTicker).isNotEmpty) { - await ref.read(estimatedRateExchangeFormProvider).updateTo( + await ref.read(exchangeFormStateProvider).updateTo( response.value!.firstWhere((e) => e.ticker == toTicker), false); } diff --git a/test/models/exchange/estimated_rate_exchange_form_state_test.dart b/test/models/exchange/estimated_rate_exchange_form_state_test.dart index 21af573eb..88b73ab47 100644 --- a/test/models/exchange/estimated_rate_exchange_form_state_test.dart +++ b/test/models/exchange/estimated_rate_exchange_form_state_test.dart @@ -1,219 +1,219 @@ -import 'package:decimal/decimal.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/annotations.dart'; -import 'package:mockito/mockito.dart'; -import 'package:stackwallet/models/exchange/estimated_rate_exchange_form_state.dart'; -import 'package:stackwallet/models/exchange/response_objects/currency.dart'; -import 'package:stackwallet/models/exchange/response_objects/estimate.dart'; -import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; -import 'package:stackwallet/services/exchange/exchange_response.dart'; - -import 'estimated_rate_exchange_form_state_test.mocks.dart'; - -@GenerateMocks([ChangeNowAPI]) -void main() { - final currencyA = Currency( - ticker: "btc", - name: "Bitcoin", - image: "image.url", - hasExternalId: false, - isFiat: false, - featured: false, - isStable: true, - supportsFixedRate: true, - network: '', - ); - final currencyB = Currency( - ticker: "xmr", - name: "Monero", - image: "image.url", - hasExternalId: false, - isFiat: false, - featured: false, - isStable: true, - supportsFixedRate: true, - network: '', - ); - final currencyC = Currency( - ticker: "firo", - name: "Firo", - image: "image.url", - hasExternalId: false, - isFiat: false, - featured: false, - isStable: true, - supportsFixedRate: true, - network: '', - ); - - test("EstimatedRateExchangeFormState constructor", () async { - final state = EstimatedRateExchangeFormState(); - - expect(state.from, null); - expect(state.to, null); - expect(state.canExchange, false); - expect(state.rate, null); - expect(state.rateDisplayString, "N/A"); - expect(state.fromAmountString, ""); - expect(state.toAmountString, ""); - expect(state.minimumSendWarning, ""); - }); - - test("init EstimatedRateExchangeFormState", () async { - final state = EstimatedRateExchangeFormState(); - - await state.init(currencyA, currencyB); - - expect(state.from, currencyA); - expect(state.to, currencyB); - expect(state.canExchange, false); - expect(state.rate, null); - expect(state.rateDisplayString, "N/A"); - expect(state.fromAmountString, ""); - expect(state.toAmountString, ""); - expect(state.minimumSendWarning, ""); - }); - - test("updateTo on fresh state", () async { - final state = EstimatedRateExchangeFormState(); - - await state.updateTo(currencyA, false); - - expect(state.from, null); - expect(state.to, currencyA); - expect(state.canExchange, false); - expect(state.rate, null); - expect(state.rateDisplayString, "N/A"); - expect(state.fromAmountString, ""); - expect(state.toAmountString, ""); - expect(state.minimumSendWarning, ""); - }); - - test( - "updateTo after updateFrom where amounts are null and getMinimalExchangeAmount succeeds", - () async { - final cn = MockChangeNowAPI(); - - final state = EstimatedRateExchangeFormState(); - state.cnTesting = cn; - - when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) - .thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42))); - - await state.updateFrom(currencyA, true); - await state.updateTo(currencyB, true); - - expect(state.from, currencyA); - expect(state.to, currencyB); - expect(state.canExchange, false); - expect(state.rate, null); - expect(state.rateDisplayString, "N/A"); - expect(state.fromAmountString, ""); - expect(state.toAmountString, ""); - expect(state.minimumSendWarning, ""); - - verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) - .called(1); - }); - - test( - "updateTo after updateFrom where amounts are null and getMinimalExchangeAmount fails", - () async { - final cn = MockChangeNowAPI(); - - final state = EstimatedRateExchangeFormState(); - state.cnTesting = cn; - - when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) - .thenAnswer((_) async => ExchangeResponse()); - - await state.updateFrom(currencyA, true); - await state.updateTo(currencyB, true); - - expect(state.from, currencyA); - expect(state.to, currencyB); - expect(state.canExchange, false); - expect(state.rate, null); - expect(state.rateDisplayString, "N/A"); - expect(state.fromAmountString, ""); - expect(state.toAmountString, ""); - expect(state.minimumSendWarning, ""); - - verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) - .called(1); - }); - - test( - "updateTo after updateFrom and setFromAmountAndCalculateToAmount where fromAmount is less than the minimum required exchange amount", - () async { - final cn = MockChangeNowAPI(); - - final state = EstimatedRateExchangeFormState(); - state.cnTesting = cn; - - when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) - .thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42))); - - await state.updateFrom(currencyA, true); - await state.setFromAmountAndCalculateToAmount(Decimal.parse("10.10"), true); - await state.updateTo(currencyB, true); - - expect(state.from, currencyA); - expect(state.to, currencyB); - expect(state.canExchange, false); - expect(state.rate, null); - expect(state.rateDisplayString, "N/A"); - expect(state.fromAmountString, "10.10000000"); - expect(state.toAmountString, ""); - expect(state.minimumSendWarning, "Minimum amount 42 BTC"); - - verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) - .called(1); - }); - - test( - "updateTo after updateFrom and setFromAmountAndCalculateToAmount where fromAmount is greater than the minimum required exchange amount", - () async { - final cn = MockChangeNowAPI(); - - final state = EstimatedRateExchangeFormState(); - state.cnTesting = cn; - - when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) - .thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42))); - when(cn.getEstimatedExchangeAmount( - fromTicker: "btc", - toTicker: "xmr", - fromAmount: Decimal.parse("110.10"))) - .thenAnswer((_) async => ExchangeResponse( - value: Estimate( - reversed: false, - fixedRate: false, - rateId: 'some rate id', - warningMessage: '', - estimatedAmount: Decimal.parse("302.002348"), - ))); - - await state.updateFrom(currencyA, true); - await state.setFromAmountAndCalculateToAmount( - Decimal.parse("110.10"), true); - await state.updateTo(currencyB, true); - - expect(state.from, currencyA); - expect(state.to, currencyB); - expect(state.canExchange, true); - expect(state.rate, Decimal.parse("2.742982270663")); - expect(state.rateDisplayString, "1 BTC ~2.74298227 XMR"); - expect(state.fromAmountString, "110.10000000"); - expect(state.toAmountString, "302.00234800"); - expect(state.minimumSendWarning, ""); - - verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) - .called(1); - verify(cn.getEstimatedExchangeAmount( - fromTicker: "btc", - toTicker: "xmr", - fromAmount: Decimal.parse("110.10"))) - .called(1); - }); -} +// import 'package:decimal/decimal.dart'; +// import 'package:flutter_test/flutter_test.dart'; +// import 'package:mockito/annotations.dart'; +// import 'package:mockito/mockito.dart'; +// import 'package:stackwallet/models/exchange/estimated_rate_exchange_form_state.dart'; +// import 'package:stackwallet/models/exchange/response_objects/currency.dart'; +// import 'package:stackwallet/models/exchange/response_objects/estimate.dart'; +// import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; +// import 'package:stackwallet/services/exchange/exchange_response.dart'; +// +// import 'estimated_rate_exchange_form_state_test.mocks.dart'; +// +// @GenerateMocks([ChangeNowAPI]) +// void main() { +// final currencyA = Currency( +// ticker: "btc", +// name: "Bitcoin", +// image: "image.url", +// hasExternalId: false, +// isFiat: false, +// featured: false, +// isStable: true, +// supportsFixedRate: true, +// network: '', +// ); +// final currencyB = Currency( +// ticker: "xmr", +// name: "Monero", +// image: "image.url", +// hasExternalId: false, +// isFiat: false, +// featured: false, +// isStable: true, +// supportsFixedRate: true, +// network: '', +// ); +// final currencyC = Currency( +// ticker: "firo", +// name: "Firo", +// image: "image.url", +// hasExternalId: false, +// isFiat: false, +// featured: false, +// isStable: true, +// supportsFixedRate: true, +// network: '', +// ); +// +// test("EstimatedRateExchangeFormState constructor", () async { +// final state = EstimatedRateExchangeFormState(); +// +// expect(state.from, null); +// expect(state.to, null); +// expect(state.canExchange, false); +// expect(state.rate, null); +// expect(state.rateDisplayString, "N/A"); +// expect(state.fromAmountString, ""); +// expect(state.toAmountString, ""); +// expect(state.minimumSendWarning, ""); +// }); +// +// test("init EstimatedRateExchangeFormState", () async { +// final state = EstimatedRateExchangeFormState(); +// +// await state.init(currencyA, currencyB); +// +// expect(state.from, currencyA); +// expect(state.to, currencyB); +// expect(state.canExchange, false); +// expect(state.rate, null); +// expect(state.rateDisplayString, "N/A"); +// expect(state.fromAmountString, ""); +// expect(state.toAmountString, ""); +// expect(state.minimumSendWarning, ""); +// }); +// +// test("updateTo on fresh state", () async { +// final state = EstimatedRateExchangeFormState(); +// +// await state.updateTo(currencyA, false); +// +// expect(state.from, null); +// expect(state.to, currencyA); +// expect(state.canExchange, false); +// expect(state.rate, null); +// expect(state.rateDisplayString, "N/A"); +// expect(state.fromAmountString, ""); +// expect(state.toAmountString, ""); +// expect(state.minimumSendWarning, ""); +// }); +// +// test( +// "updateTo after updateFrom where amounts are null and getMinimalExchangeAmount succeeds", +// () async { +// final cn = MockChangeNowAPI(); +// +// final state = EstimatedRateExchangeFormState(); +// state.cnTesting = cn; +// +// when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) +// .thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42))); +// +// await state.updateFrom(currencyA, true); +// await state.updateTo(currencyB, true); +// +// expect(state.from, currencyA); +// expect(state.to, currencyB); +// expect(state.canExchange, false); +// expect(state.rate, null); +// expect(state.rateDisplayString, "N/A"); +// expect(state.fromAmountString, ""); +// expect(state.toAmountString, ""); +// expect(state.minimumSendWarning, ""); +// +// verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) +// .called(1); +// }); +// +// test( +// "updateTo after updateFrom where amounts are null and getMinimalExchangeAmount fails", +// () async { +// final cn = MockChangeNowAPI(); +// +// final state = EstimatedRateExchangeFormState(); +// state.cnTesting = cn; +// +// when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) +// .thenAnswer((_) async => ExchangeResponse()); +// +// await state.updateFrom(currencyA, true); +// await state.updateTo(currencyB, true); +// +// expect(state.from, currencyA); +// expect(state.to, currencyB); +// expect(state.canExchange, false); +// expect(state.rate, null); +// expect(state.rateDisplayString, "N/A"); +// expect(state.fromAmountString, ""); +// expect(state.toAmountString, ""); +// expect(state.minimumSendWarning, ""); +// +// verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) +// .called(1); +// }); +// +// test( +// "updateTo after updateFrom and setFromAmountAndCalculateToAmount where fromAmount is less than the minimum required exchange amount", +// () async { +// final cn = MockChangeNowAPI(); +// +// final state = EstimatedRateExchangeFormState(); +// state.cnTesting = cn; +// +// when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) +// .thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42))); +// +// await state.updateFrom(currencyA, true); +// await state.setFromAmountAndCalculateToAmount(Decimal.parse("10.10"), true); +// await state.updateTo(currencyB, true); +// +// expect(state.from, currencyA); +// expect(state.to, currencyB); +// expect(state.canExchange, false); +// expect(state.rate, null); +// expect(state.rateDisplayString, "N/A"); +// expect(state.fromAmountString, "10.10000000"); +// expect(state.toAmountString, ""); +// expect(state.minimumSendWarning, "Minimum amount 42 BTC"); +// +// verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) +// .called(1); +// }); +// +// test( +// "updateTo after updateFrom and setFromAmountAndCalculateToAmount where fromAmount is greater than the minimum required exchange amount", +// () async { +// final cn = MockChangeNowAPI(); +// +// final state = EstimatedRateExchangeFormState(); +// state.cnTesting = cn; +// +// when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) +// .thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42))); +// when(cn.getEstimatedExchangeAmount( +// fromTicker: "btc", +// toTicker: "xmr", +// fromAmount: Decimal.parse("110.10"))) +// .thenAnswer((_) async => ExchangeResponse( +// value: Estimate( +// reversed: false, +// fixedRate: false, +// rateId: 'some rate id', +// warningMessage: '', +// estimatedAmount: Decimal.parse("302.002348"), +// ))); +// +// await state.updateFrom(currencyA, true); +// await state.setFromAmountAndCalculateToAmount( +// Decimal.parse("110.10"), true); +// await state.updateTo(currencyB, true); +// +// expect(state.from, currencyA); +// expect(state.to, currencyB); +// expect(state.canExchange, true); +// expect(state.rate, Decimal.parse("2.742982270663")); +// expect(state.rateDisplayString, "1 BTC ~2.74298227 XMR"); +// expect(state.fromAmountString, "110.10000000"); +// expect(state.toAmountString, "302.00234800"); +// expect(state.minimumSendWarning, ""); +// +// verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) +// .called(1); +// verify(cn.getEstimatedExchangeAmount( +// fromTicker: "btc", +// toTicker: "xmr", +// fromAmount: Decimal.parse("110.10"))) +// .called(1); +// }); +// }