exchange form state implementation and various small bug fixes

This commit is contained in:
julian 2022-10-03 18:01:04 -06:00
parent d87c9981c6
commit bb12e149f6
13 changed files with 1288 additions and 1161 deletions

View file

@ -18,6 +18,7 @@ import 'package:path_provider/path_provider.dart';
import 'package:stackwallet/hive/db.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.dart';
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.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/exchange/response_objects/trade.dart';
import 'package:stackwallet/models/isar/models/log.dart'; import 'package:stackwallet/models/isar/models/log.dart';
import 'package:stackwallet/models/models.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_currencies_state_provider.dart';
import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_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/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/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/providers/global/auto_swb_service_provider.dart'; import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/global/base_currencies_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/route_generator.dart';
import 'package:stackwallet/services/debug_service.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_api.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
import 'package:stackwallet/services/locale_service.dart'; import 'package:stackwallet/services/locale_service.dart';
import 'package:stackwallet/services/node_service.dart'; import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/notifications_api.dart'; import 'package:stackwallet/services/notifications_api.dart';
@ -271,15 +271,15 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
response2.value!; response2.value!;
if (response.value!.length > 1) { 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) { 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); 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) { 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); response.value!.firstWhere((e) => e.ticker == "doge"), false);
} }
} }
@ -319,12 +319,12 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
ref.read(fixedRateMarketPairsStateProvider.state).state = ref.read(fixedRateMarketPairsStateProvider.state).state =
response3.value!; response3.value!;
if (ref.read(fixedRateExchangeFormProvider).market == null) { if (ref.read(exchangeFormStateProvider).market == null) {
final matchingMarkets = final matchingMarkets =
response3.value!.where((e) => e.to == "doge" && e.from == "btc"); response3.value!.where((e) => e.to == "doge" && e.from == "btc");
if (matchingMarkets.isNotEmpty) { if (matchingMarkets.isNotEmpty) {
await ref await ref
.read(fixedRateExchangeFormProvider) .read(exchangeFormStateProvider)
.updateMarket(matchingMarkets.first, true); .updateMarket(matchingMarkets.first, true);
} }
} }
@ -355,6 +355,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
@override @override
void initState() { void initState() {
ref.read(exchangeFormStateProvider).exchange = ChangeNowExchange();
final colorScheme = DB.instance final colorScheme = DB.instance
.get<dynamic>(boxName: DB.boxNameTheme, key: "colorScheme") as String?; .get<dynamic>(boxName: DB.boxNameTheme, key: "colorScheme") as String?;

View file

@ -35,8 +35,10 @@ class EstimatedExchangeAmount {
factory EstimatedExchangeAmount.fromJson(Map<String, dynamic> json) { factory EstimatedExchangeAmount.fromJson(Map<String, dynamic> json) {
try { try {
return EstimatedExchangeAmount( return EstimatedExchangeAmount(
estimatedAmount: Decimal.parse(json["estimatedAmount"].toString()), estimatedAmount: Decimal.parse(json["estimatedAmount"]?.toString() ??
transactionSpeedForecast: json["transactionSpeedForecast"] as String, json["estimatedDeposit"].toString()),
transactionSpeedForecast:
json["transactionSpeedForecast"] as String? ?? "",
warningMessage: json["warningMessage"] as String?, warningMessage: json["warningMessage"] as String?,
rateId: json["rateId"] as String?, rateId: json["rateId"] as String?,
networkFee: Decimal.tryParse(json["networkFee"].toString()), networkFee: Decimal.tryParse(json["networkFee"].toString()),

View file

@ -1,282 +1,282 @@
import 'package:decimal/decimal.dart'; // import 'package:decimal/decimal.dart';
import 'package:flutter/cupertino.dart'; // import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'package:stackwallet/models/exchange/response_objects/currency.dart'; // import 'package:stackwallet/models/exchange/response_objects/currency.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; // import 'package:stackwallet/services/exchange/change_now/change_now_api.dart';
import 'package:stackwallet/utilities/logger.dart'; // import 'package:stackwallet/utilities/logger.dart';
//
class EstimatedRateExchangeFormState extends ChangeNotifier { // class EstimatedRateExchangeFormState extends ChangeNotifier {
/// used in testing to inject mock // /// used in testing to inject mock
ChangeNowAPI? cnTesting; // ChangeNowAPI? cnTesting;
//
Decimal? _fromAmount; // Decimal? _fromAmount;
Decimal? _toAmount; // Decimal? _toAmount;
//
Decimal? _minFromAmount; // Decimal? _minFromAmount;
Decimal? _minToAmount; // Decimal? _minToAmount;
//
Decimal? rate; // Decimal? rate;
//
Currency? _from; // Currency? _from;
Currency? _to; // Currency? _to;
//
void Function(String)? _onError; // void Function(String)? _onError;
//
Currency? get from => _from; // Currency? get from => _from;
Currency? get to => _to; // Currency? get to => _to;
//
String get fromAmountString => // String get fromAmountString =>
_fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8); // _fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8);
String get toAmountString => // String get toAmountString =>
_toAmount == null ? "" : _toAmount!.toStringAsFixed(8); // _toAmount == null ? "" : _toAmount!.toStringAsFixed(8);
//
String get rateDisplayString { // String get rateDisplayString {
if (rate == null || from == null || to == null) { // if (rate == null || from == null || to == null) {
return "N/A"; // return "N/A";
} else { // } else {
return "1 ${from!.ticker.toUpperCase()} ~${rate!.toStringAsFixed(8)} ${to!.ticker.toUpperCase()}"; // return "1 ${from!.ticker.toUpperCase()} ~${rate!.toStringAsFixed(8)} ${to!.ticker.toUpperCase()}";
} // }
} // }
//
bool get canExchange { // bool get canExchange {
return _fromAmount != null && // return _fromAmount != null &&
_fromAmount != Decimal.zero && // _fromAmount != Decimal.zero &&
_toAmount != null && // _toAmount != null &&
rate != null && // rate != null &&
minimumSendWarning.isEmpty; // minimumSendWarning.isEmpty;
} // }
//
String get minimumSendWarning { // String get minimumSendWarning {
if (_from != null && // if (_from != null &&
_fromAmount != null && // _fromAmount != null &&
_minFromAmount != null && // _minFromAmount != null &&
_fromAmount! < _minFromAmount!) { // _fromAmount! < _minFromAmount!) {
return "Minimum amount ${_minFromAmount!.toString()} ${from!.ticker.toUpperCase()}"; // return "Minimum amount ${_minFromAmount!.toString()} ${from!.ticker.toUpperCase()}";
} // }
//
return ""; // return "";
} // }
//
Future<void> init(Currency? from, Currency? to) async { // Future<void> init(Currency? from, Currency? to) async {
_from = from; // _from = from;
_to = to; // _to = to;
} // }
//
void clearAmounts(bool shouldNotifyListeners) { // void clearAmounts(bool shouldNotifyListeners) {
_fromAmount = null; // _fromAmount = null;
_toAmount = null; // _toAmount = null;
_minFromAmount = null; // _minFromAmount = null;
_minToAmount = null; // _minToAmount = null;
rate = null; // rate = null;
//
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
notifyListeners(); // notifyListeners();
} // }
} // }
//
Future<void> swap() async { // Future<void> swap() async {
final Decimal? newToAmount = _fromAmount; // final Decimal? newToAmount = _fromAmount;
final Decimal? newFromAmount = _toAmount; // final Decimal? newFromAmount = _toAmount;
//
final Decimal? newMinFromAmount = _minToAmount; // final Decimal? newMinFromAmount = _minToAmount;
final Decimal? newMinToAmount = _minFromAmount; // final Decimal? newMinToAmount = _minFromAmount;
//
final Currency? newTo = from; // final Currency? newTo = from;
final Currency? newFrom = to; // final Currency? newFrom = to;
//
_fromAmount = newFromAmount; // _fromAmount = newFromAmount;
_toAmount = newToAmount; // _toAmount = newToAmount;
//
_minToAmount = newMinToAmount; // _minToAmount = newMinToAmount;
_minFromAmount = newMinFromAmount; // _minFromAmount = newMinFromAmount;
//
// rate = newRate; // // rate = newRate;
//
_to = newTo; // _to = newTo;
_from = newFrom; // _from = newFrom;
//
await _updateMinFromAmount(shouldNotifyListeners: false); // await _updateMinFromAmount(shouldNotifyListeners: false);
//
await updateRate(); // await updateRate();
//
notifyListeners(); // notifyListeners();
} // }
//
Future<void> updateTo(Currency to, bool shouldNotifyListeners) async { // Future<void> updateTo(Currency to, bool shouldNotifyListeners) async {
try { // try {
_to = to; // _to = to;
if (_from == null) { // if (_from == null) {
rate = null; // rate = null;
notifyListeners(); // notifyListeners();
return; // return;
} // }
//
await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); // await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners);
//
await updateRate(shouldNotifyListeners: shouldNotifyListeners); // await updateRate(shouldNotifyListeners: shouldNotifyListeners);
//
debugPrint( // debugPrint(
"_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); // "_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate");
//
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
notifyListeners(); // notifyListeners();
} // }
} catch (e, s) { // } catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Error); // Logging.instance.log("$e\n$s", level: LogLevel.Error);
} // }
} // }
//
Future<void> updateFrom(Currency from, bool shouldNotifyListeners) async { // Future<void> updateFrom(Currency from, bool shouldNotifyListeners) async {
try { // try {
_from = from; // _from = from;
//
if (_to == null) { // if (_to == null) {
rate = null; // rate = null;
notifyListeners(); // notifyListeners();
return; // return;
} // }
//
await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners); // await _updateMinFromAmount(shouldNotifyListeners: shouldNotifyListeners);
//
await updateRate(shouldNotifyListeners: shouldNotifyListeners); // await updateRate(shouldNotifyListeners: shouldNotifyListeners);
//
debugPrint( // debugPrint(
"_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate"); // "_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$_fromAmount _toAmount=$_toAmount rate:$rate");
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
notifyListeners(); // notifyListeners();
} // }
} catch (e, s) { // } catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Error); // Logging.instance.log("$e\n$s", level: LogLevel.Error);
} // }
} // }
//
Future<void> _updateMinFromAmount( // Future<void> _updateMinFromAmount(
{required bool shouldNotifyListeners}) async { // {required bool shouldNotifyListeners}) async {
_minFromAmount = await getStandardMinExchangeAmount(from: from!, to: to!); // _minFromAmount = await getStandardMinExchangeAmount(from: from!, to: to!);
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
notifyListeners(); // notifyListeners();
} // }
} // }
//
// Future<void> setToAmountAndCalculateFromAmount( // // Future<void> setToAmountAndCalculateFromAmount(
// Decimal newToAmount, // // Decimal newToAmount,
// bool shouldNotifyListeners, // // bool shouldNotifyListeners,
// ) async { // // ) async {
// if (newToAmount == Decimal.zero) { // // if (newToAmount == Decimal.zero) {
// _fromAmount = Decimal.zero; // // _fromAmount = Decimal.zero;
// } // // }
// // //
// _toAmount = newToAmount; // // _toAmount = newToAmount;
// await updateRate(); // // await updateRate();
// if (shouldNotifyListeners) { // // if (shouldNotifyListeners) {
// notifyListeners(); // // notifyListeners();
// } // // }
// } // // }
//
Future<void> setFromAmountAndCalculateToAmount( // Future<void> setFromAmountAndCalculateToAmount(
Decimal newFromAmount, // Decimal newFromAmount,
bool shouldNotifyListeners, // bool shouldNotifyListeners,
) async { // ) async {
if (newFromAmount == Decimal.zero) { // if (newFromAmount == Decimal.zero) {
_toAmount = Decimal.zero; // _toAmount = Decimal.zero;
} // }
//
_fromAmount = newFromAmount; // _fromAmount = newFromAmount;
await updateRate(shouldNotifyListeners: shouldNotifyListeners); // await updateRate(shouldNotifyListeners: shouldNotifyListeners);
//
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
notifyListeners(); // notifyListeners();
} // }
} // }
//
Future<Decimal?> getStandardEstimatedToAmount({ // Future<Decimal?> getStandardEstimatedToAmount({
required Decimal fromAmount, // required Decimal fromAmount,
required Currency from, // required Currency from,
required Currency to, // required Currency to,
}) async { // }) async {
final response = // final response =
await (cnTesting ?? ChangeNowAPI.instance).getEstimatedExchangeAmount( // await (cnTesting ?? ChangeNowAPI.instance).getEstimatedExchangeAmount(
fromTicker: from.ticker, // fromTicker: from.ticker,
toTicker: to.ticker, // toTicker: to.ticker,
fromAmount: fromAmount, // fromAmount: fromAmount,
); // );
//
if (response.value != null) { // if (response.value != null) {
return response.value!.estimatedAmount; // return response.value!.estimatedAmount;
} else { // } else {
_onError?.call( // _onError?.call(
"Failed to fetch estimated amount: ${response.exception?.toString()}"); // "Failed to fetch estimated amount: ${response.exception?.toString()}");
return null; // return null;
} // }
} // }
//
// Future<Decimal?> getStandardEstimatedFromAmount({ // // Future<Decimal?> getStandardEstimatedFromAmount({
// required Decimal toAmount, // // required Decimal toAmount,
// required Currency from, // // required Currency from,
// required Currency to, // // required Currency to,
// }) async { // // }) async {
// final response = await (cnTesting ?? ChangeNow.instance) // // final response = await (cnTesting ?? ChangeNow.instance)
// .getEstimatedExchangeAmount( // // .getEstimatedExchangeAmount(
// fromTicker: from.ticker, // // fromTicker: from.ticker,
// toTicker: to.ticker, // // toTicker: to.ticker,
// fromAmount: toAmount, ); // // fromAmount: toAmount, );
// // //
// if (response.value != null) { // // if (response.value != null) {
// return response.value!.fromAmount; // // return response.value!.fromAmount;
// } else { // // } else {
// _onError?.call( // // _onError?.call(
// "Failed to fetch estimated amount: ${response.exception?.toString()}"); // // "Failed to fetch estimated amount: ${response.exception?.toString()}");
// return null; // // return null;
// } // // }
// } // // }
//
Future<Decimal?> getStandardMinExchangeAmount({ // Future<Decimal?> getStandardMinExchangeAmount({
required Currency from, // required Currency from,
required Currency to, // required Currency to,
}) async { // }) async {
final response = await (cnTesting ?? ChangeNowAPI.instance) // final response = await (cnTesting ?? ChangeNowAPI.instance)
.getMinimalExchangeAmount(fromTicker: from.ticker, toTicker: to.ticker); // .getMinimalExchangeAmount(fromTicker: from.ticker, toTicker: to.ticker);
//
if (response.value != null) { // if (response.value != null) {
return response.value!; // return response.value!;
} else { // } else {
_onError?.call( // _onError?.call(
"Could not update minimal exchange amounts: ${response.exception?.toString()}"); // "Could not update minimal exchange amounts: ${response.exception?.toString()}");
return null; // return null;
} // }
} // }
//
void setOnError({ // void setOnError({
required void Function(String)? onError, // required void Function(String)? onError,
bool shouldNotifyListeners = false, // bool shouldNotifyListeners = false,
}) { // }) {
_onError = onError; // _onError = onError;
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
notifyListeners(); // notifyListeners();
} // }
} // }
//
Future<void> updateRate({bool shouldNotifyListeners = false}) async { // Future<void> updateRate({bool shouldNotifyListeners = false}) async {
rate = null; // rate = null;
final amount = _fromAmount; // final amount = _fromAmount;
final minAmount = _minFromAmount; // final minAmount = _minFromAmount;
if (amount != null && amount > Decimal.zero) { // if (amount != null && amount > Decimal.zero) {
Decimal? amt; // Decimal? amt;
if (minAmount != null) { // if (minAmount != null) {
if (minAmount <= amount) { // if (minAmount <= amount) {
amt = await getStandardEstimatedToAmount( // amt = await getStandardEstimatedToAmount(
fromAmount: amount, from: _from!, to: _to!); // fromAmount: amount, from: _from!, to: _to!);
if (amt != null) { // if (amt != null) {
rate = (amt / amount).toDecimal(scaleOnInfinitePrecision: 12); // rate = (amt / amount).toDecimal(scaleOnInfinitePrecision: 12);
} // }
} // }
} // }
if (rate != null && amt != null) { // if (rate != null && amt != null) {
_toAmount = amt; // _toAmount = amt;
} // }
} // }
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
notifyListeners(); // notifyListeners();
} // }
} // }
} // }

View file

@ -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<ExchangeFormState>((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<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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<void> 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();
}
}

View file

@ -1,179 +1,179 @@
import 'package:decimal/decimal.dart'; // import 'package:decimal/decimal.dart';
import 'package:flutter/cupertino.dart'; // import 'package:flutter/cupertino.dart';
import 'package:stackwallet/models/exchange/change_now/cn_exchange_estimate.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/models/exchange/response_objects/fixed_rate_market.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; // import 'package:stackwallet/services/exchange/change_now/change_now_api.dart';
import 'package:stackwallet/utilities/logger.dart'; // import 'package:stackwallet/utilities/logger.dart';
//
class FixedRateExchangeFormState extends ChangeNotifier { // class FixedRateExchangeFormState extends ChangeNotifier {
Decimal? _fromAmount; // Decimal? _fromAmount;
Decimal? _toAmount; // Decimal? _toAmount;
//
FixedRateMarket? _market; // FixedRateMarket? _market;
FixedRateMarket? get market => _market; // FixedRateMarket? get market => _market;
//
CNExchangeEstimate? _estimate; // CNExchangeEstimate? _estimate;
CNExchangeEstimate? get estimate => _estimate; // CNExchangeEstimate? get estimate => _estimate;
//
Decimal? get rate { // Decimal? get rate {
if (_estimate == null) { // if (_estimate == null) {
return null; // return null;
} else { // } else {
return (_estimate!.toAmount / _estimate!.fromAmount) // return (_estimate!.toAmount / _estimate!.fromAmount)
.toDecimal(scaleOnInfinitePrecision: 12); // .toDecimal(scaleOnInfinitePrecision: 12);
} // }
} // }
//
Future<void> swap(FixedRateMarket reverseFixedRateMarket) async { // Future<void> swap(FixedRateMarket reverseFixedRateMarket) async {
final Decimal? tmp = _fromAmount; // final Decimal? tmp = _fromAmount;
_fromAmount = _toAmount; // _fromAmount = _toAmount;
_toAmount = tmp; // _toAmount = tmp;
//
await updateMarket(reverseFixedRateMarket, false); // await updateMarket(reverseFixedRateMarket, false);
await updateRateEstimate(CNEstimateType.direct); // await updateRateEstimate(CNEstimateType.direct);
_toAmount = _estimate?.toAmount ?? Decimal.zero; // _toAmount = _estimate?.toAmount ?? Decimal.zero;
notifyListeners(); // notifyListeners();
} // }
//
String get fromAmountString => // String get fromAmountString =>
_fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8); // _fromAmount == null ? "" : _fromAmount!.toStringAsFixed(8);
String get toAmountString => // String get toAmountString =>
_toAmount == null ? "" : _toAmount!.toStringAsFixed(8); // _toAmount == null ? "" : _toAmount!.toStringAsFixed(8);
//
Future<void> updateMarket( // Future<void> updateMarket(
FixedRateMarket? market, // FixedRateMarket? market,
bool shouldNotifyListeners, // bool shouldNotifyListeners,
) async { // ) async {
_market = market; // _market = market;
//
if (_market == null) { // if (_market == null) {
_fromAmount = null; // _fromAmount = null;
_toAmount = null; // _toAmount = null;
} else { // } else {
if (_fromAmount != null) { // if (_fromAmount != null) {
if (_fromAmount! <= Decimal.zero) { // if (_fromAmount! <= Decimal.zero) {
_toAmount = Decimal.zero; // _toAmount = Decimal.zero;
} else { // } else {
await updateRateEstimate(CNEstimateType.direct); // await updateRateEstimate(CNEstimateType.direct);
} // }
} // }
} // }
//
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
notifyListeners(); // notifyListeners();
} // }
} // }
//
String get rateDisplayString { // String get rateDisplayString {
if (_market == null || _estimate == null) { // if (_market == null || _estimate == null) {
return "N/A"; // return "N/A";
} else { // } else {
return "1 ${_estimate!.fromCurrency.toUpperCase()} ~${rate!.toStringAsFixed(8)} ${_estimate!.toCurrency.toUpperCase()}"; // return "1 ${_estimate!.fromCurrency.toUpperCase()} ~${rate!.toStringAsFixed(8)} ${_estimate!.toCurrency.toUpperCase()}";
} // }
} // }
//
bool get canExchange { // bool get canExchange {
return _market != null && // return _market != null &&
_fromAmount != null && // _fromAmount != null &&
_toAmount != null && // _toAmount != null &&
sendAmountWarning.isEmpty; // sendAmountWarning.isEmpty;
} // }
//
String get sendAmountWarning { // String get sendAmountWarning {
if (_market != null && _fromAmount != null) { // if (_market != null && _fromAmount != null) {
if (_fromAmount! < _market!.min) { // if (_fromAmount! < _market!.min) {
return "Minimum amount ${_market!.min.toString()} ${_market!.from.toUpperCase()}"; // return "Minimum amount ${_market!.min.toString()} ${_market!.from.toUpperCase()}";
} else if (_fromAmount! > _market!.max) { // } else if (_fromAmount! > _market!.max) {
return "Maximum amount ${_market!.max.toString()} ${_market!.from.toUpperCase()}"; // return "Maximum amount ${_market!.max.toString()} ${_market!.from.toUpperCase()}";
} // }
} // }
//
return ""; // return "";
} // }
//
Future<void> setToAmountAndCalculateFromAmount( // Future<void> setToAmountAndCalculateFromAmount(
Decimal newToAmount, // Decimal newToAmount,
bool shouldNotifyListeners, // bool shouldNotifyListeners,
) async { // ) async {
_toAmount = newToAmount; // _toAmount = newToAmount;
//
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
await updateRateEstimate(CNEstimateType.reverse); // await updateRateEstimate(CNEstimateType.reverse);
notifyListeners(); // notifyListeners();
} // }
} // }
//
Future<void> setFromAmountAndCalculateToAmount( // Future<void> setFromAmountAndCalculateToAmount(
Decimal newFromAmount, // Decimal newFromAmount,
bool shouldNotifyListeners, // bool shouldNotifyListeners,
) async { // ) async {
_fromAmount = newFromAmount; // _fromAmount = newFromAmount;
//
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
await updateRateEstimate(CNEstimateType.direct); // await updateRateEstimate(CNEstimateType.direct);
notifyListeners(); // notifyListeners();
} // }
} // }
//
void Function(String)? _onError; // void Function(String)? _onError;
//
void setOnError({ // void setOnError({
required void Function(String)? onError, // required void Function(String)? onError,
bool shouldNotifyListeners = false, // bool shouldNotifyListeners = false,
}) { // }) {
_onError = onError; // _onError = onError;
if (shouldNotifyListeners) { // if (shouldNotifyListeners) {
notifyListeners(); // notifyListeners();
} // }
} // }
//
Future<void> updateRateEstimate(CNEstimateType direction) async { // Future<void> updateRateEstimate(CNEstimateType direction) async {
if (market != null) { // if (market != null) {
Decimal? amount; // Decimal? amount;
// set amount based on trade estimate direction // // set amount based on trade estimate direction
switch (direction) { // switch (direction) {
case CNEstimateType.direct: // case CNEstimateType.direct:
if (_fromAmount != null // if (_fromAmount != null
// && // // &&
// market!.min >= _fromAmount! && // // market!.min >= _fromAmount! &&
// _fromAmount! <= market!.max // // _fromAmount! <= market!.max
) { // ) {
amount = _fromAmount!; // amount = _fromAmount!;
} // }
break; // break;
case CNEstimateType.reverse: // case CNEstimateType.reverse:
if (_toAmount != null // if (_toAmount != null
// && // // &&
// market!.min >= _toAmount! && // // market!.min >= _toAmount! &&
// _toAmount! <= market!.max // // _toAmount! <= market!.max
) { // ) {
amount = _toAmount!; // amount = _toAmount!;
} // }
break; // break;
} // }
//
if (amount != null && market != null && amount > Decimal.zero) { // if (amount != null && market != null && amount > Decimal.zero) {
final response = // final response =
await ChangeNowAPI.instance.getEstimatedExchangeAmountV2( // await ChangeNowAPI.instance.getEstimatedExchangeAmountV2(
fromTicker: market!.from, // fromTicker: market!.from,
toTicker: market!.to, // toTicker: market!.to,
fromOrTo: direction, // fromOrTo: direction,
flow: CNFlowType.fixedRate, // flow: CNFlowType.fixedRate,
amount: amount, // amount: amount,
); // );
//
if (response.value != null) { // if (response.value != null) {
// update estimate if response succeeded // // update estimate if response succeeded
_estimate = response.value; // _estimate = response.value;
//
_toAmount = _estimate?.toAmount; // _toAmount = _estimate?.toAmount;
_fromAmount = _estimate?.fromAmount; // _fromAmount = _estimate?.fromAmount;
notifyListeners(); // notifyListeners();
} else if (response.exception != null) { // } else if (response.exception != null) {
Logging.instance.log("updateRateEstimate(): ${response.exception}", // Logging.instance.log("updateRateEstimate(): ${response.exception}",
level: LogLevel.Warning); // level: LogLevel.Warning);
} // }
} // }
} // }
} // }
} // }

View file

@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_svg/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/incomplete_exchange.dart';
import 'package:stackwallet/models/exchange/response_objects/currency.dart'; import 'package:stackwallet/models/exchange/response_objects/currency.dart';
import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.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_currencies_state_provider.dart';
import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_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/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/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/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
@ -62,42 +61,27 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
final FocusNode _receiveFocusNode = FocusNode(); final FocusNode _receiveFocusNode = FocusNode();
bool _swapLock = false; bool _swapLock = false;
bool _reversed = false;
void sendFieldOnChanged(String value) async { void sendFieldOnChanged(String value) async {
final newFromAmount = Decimal.tryParse(value); final newFromAmount = Decimal.tryParse(value);
final isEstimated =
ref.read(prefsChangeNotifierProvider).exchangeRateType == ref.read(exchangeFormStateProvider).fromAmount =
ExchangeRateType.estimated; newFromAmount ?? Decimal.zero;
if (newFromAmount != null) {
if (isEstimated) { if (newFromAmount == null) {
await ref _receiveController.text =
.read(estimatedRateExchangeFormProvider) ref.read(prefsChangeNotifierProvider).exchangeRateType ==
.setFromAmountAndCalculateToAmount(newFromAmount, false); ExchangeRateType.estimated
} 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 ? "-" : "";
} }
} }
void selectSendCurrency() async { void selectSendCurrency() async {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType == if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
final fromTicker = final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? "-";
ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "-"; // ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "-";
if (walletInitiated && if (walletInitiated &&
fromTicker.toLowerCase() == coin!.ticker.toLowerCase()) { fromTicker.toLowerCase() == coin!.ticker.toLowerCase()) {
@ -108,16 +92,13 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
await _showFloatingRateSelectionSheet( await _showFloatingRateSelectionSheet(
currencies: currencies:
ref.read(availableChangeNowCurrenciesStateProvider.state).state, ref.read(availableChangeNowCurrenciesStateProvider.state).state,
excludedTicker: excludedTicker: ref.read(exchangeFormStateProvider).toTicker ?? "-",
ref.read(estimatedRateExchangeFormProvider).to?.ticker ?? "-",
fromTicker: fromTicker, fromTicker: fromTicker,
onSelected: (from) => ref onSelected: (from) =>
.read(estimatedRateExchangeFormProvider) ref.read(exchangeFormStateProvider).updateFrom(from, true));
.updateFrom(from, true));
} else { } else {
final toTicker = ref.read(fixedRateExchangeFormProvider).market?.to ?? ""; final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? "";
final fromTicker = final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? "";
ref.read(fixedRateExchangeFormProvider).market?.from ?? "";
if (walletInitiated && if (walletInitiated &&
fromTicker.toLowerCase() == coin!.ticker.toLowerCase()) { fromTicker.toLowerCase() == coin!.ticker.toLowerCase()) {
@ -138,7 +119,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
); );
await ref await ref
.read(fixedRateExchangeFormProvider) .read(exchangeFormStateProvider)
.updateMarket(market, true); .updateMarket(market, true);
} catch (e) { } catch (e) {
unawaited(showDialog<dynamic>( unawaited(showDialog<dynamic>(
@ -158,8 +139,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
void selectReceiveCurrency() async { void selectReceiveCurrency() async {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType == if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
final toTicker = final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? "";
ref.read(estimatedRateExchangeFormProvider).to?.ticker ?? "";
if (walletInitiated && if (walletInitiated &&
toTicker.toLowerCase() == coin!.ticker.toLowerCase()) { toTicker.toLowerCase() == coin!.ticker.toLowerCase()) {
@ -170,16 +150,13 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
await _showFloatingRateSelectionSheet( await _showFloatingRateSelectionSheet(
currencies: currencies:
ref.read(availableChangeNowCurrenciesStateProvider.state).state, ref.read(availableChangeNowCurrenciesStateProvider.state).state,
excludedTicker: excludedTicker: ref.read(exchangeFormStateProvider).fromTicker ?? "",
ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "", fromTicker: ref.read(exchangeFormStateProvider).fromTicker ?? "",
fromTicker:
ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "",
onSelected: (to) => onSelected: (to) =>
ref.read(estimatedRateExchangeFormProvider).updateTo(to, true)); ref.read(exchangeFormStateProvider).updateTo(to, true));
} else { } else {
final fromTicker = final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? "";
ref.read(fixedRateExchangeFormProvider).market?.from ?? ""; final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? "";
final toTicker = ref.read(fixedRateExchangeFormProvider).market?.to ?? "";
if (walletInitiated && if (walletInitiated &&
toTicker.toLowerCase() == coin!.ticker.toLowerCase()) { toTicker.toLowerCase() == coin!.ticker.toLowerCase()) {
@ -200,7 +177,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
); );
await ref await ref
.read(fixedRateExchangeFormProvider) .read(exchangeFormStateProvider)
.updateMarket(market, true); .updateMarket(market, true);
} catch (e) { } catch (e) {
unawaited(showDialog<dynamic>( unawaited(showDialog<dynamic>(
@ -222,28 +199,11 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
final isEstimated = final isEstimated =
ref.read(prefsChangeNotifierProvider).exchangeRateType == ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated; ExchangeRateType.estimated;
if (newToAmount != null) { if (!isEstimated) {
if (isEstimated) { ref.read(exchangeFormStateProvider).toAmount =
// await ref newToAmount ?? Decimal.zero;
// .read(estimatedRateExchangeFormProvider) }
// .setToAmountAndCalculateFromAmount( if (newToAmount == null) {
// 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);
}
_sendController.text = ""; _sendController.text = "";
} }
} }
@ -276,10 +236,10 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
if (ref.watch(prefsChangeNotifierProvider if (ref.watch(prefsChangeNotifierProvider
.select((pref) => pref.exchangeRateType)) == .select((pref) => pref.exchangeRateType)) ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
await ref.read(estimatedRateExchangeFormProvider).swap(); await ref.read(exchangeFormStateProvider).swap();
} else { } else {
final from = ref.read(fixedRateExchangeFormProvider).market?.from; final from = ref.read(exchangeFormStateProvider).fromTicker;
final to = ref.read(fixedRateExchangeFormProvider).market?.to; final to = ref.read(exchangeFormStateProvider).toTicker;
if (to != null && from != null) { if (to != null && from != null) {
final markets = ref final markets = ref
@ -288,7 +248,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
.where((e) => e.from == to && e.to == from); .where((e) => e.from == to && e.to == from);
if (markets.isNotEmpty) { 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<ExchangeForm> {
} }
} }
String? _fetchIconUrlFromTickerForFixedRateFlow(String? ticker) { String? _fetchIconUrlFromTicker(String? ticker) {
if (ticker == null) return null; if (ticker == null) return null;
final possibleCurrencies = ref final possibleCurrencies = ref
@ -417,15 +377,35 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
void onRateTypeChanged(ExchangeRateType rateType) async { void onRateTypeChanged(ExchangeRateType rateType) async {
_receiveFocusNode.unfocus(); _receiveFocusNode.unfocus();
_sendFocusNode.unfocus(); _sendFocusNode.unfocus();
unawaited(
showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => WillPopScope(
onWillPop: () async => false,
child: Container(
color: Theme.of(context)
.extension<StackColors>()!
.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) { switch (rateType) {
case ExchangeRateType.estimated: case ExchangeRateType.estimated:
final market = ref.read(fixedRateExchangeFormProvider).market; if (!(toTicker == "-" || fromTicker == "-")) {
final fromTicker = market?.from ?? "";
final toTicker = market?.to ?? "";
if (!(fromTicker.isEmpty ||
toTicker.isEmpty ||
toTicker == "-" ||
fromTicker == "-")) {
final available = ref final available = ref
.read(availableFloatingRatePairsStateProvider.state) .read(availableFloatingRatePairsStateProvider.state)
.state .state
@ -442,28 +422,29 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
availableCurrencies.firstWhere((e) => e.ticker == toTicker); availableCurrencies.firstWhere((e) => e.ticker == toTicker);
final newFromAmount = Decimal.tryParse(_sendController.text); final newFromAmount = Decimal.tryParse(_sendController.text);
if (newFromAmount != null) { ref.read(exchangeFormStateProvider).fromAmount =
await ref newFromAmount ?? Decimal.zero;
.read(estimatedRateExchangeFormProvider) if (newFromAmount == null) {
.setFromAmountAndCalculateToAmount(newFromAmount, false);
} else {
await ref
.read(estimatedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(Decimal.zero, false);
_receiveController.text = ""; _receiveController.text = "";
} }
await ref await ref.read(exchangeFormStateProvider).updateTo(to, false);
.read(estimatedRateExchangeFormProvider) await ref.read(exchangeFormStateProvider).updateFrom(from, true);
.updateTo(to, false);
await ref _receiveController.text =
.read(estimatedRateExchangeFormProvider) ref.read(exchangeFormStateProvider).toAmountString.isEmpty
.updateFrom(from, true); ? "-"
: ref.read(exchangeFormStateProvider).toAmountString;
if (mounted) {
Navigator.of(context).pop();
}
return; return;
} }
} }
} }
if (mounted) {
Navigator.of(context).pop();
}
unawaited(showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.warning, type: FlushBarType.warning,
message: message:
@ -472,14 +453,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
)); ));
break; break;
case ExchangeRateType.fixed: case ExchangeRateType.fixed:
final fromTicker = if (!(toTicker == "-" || fromTicker == "-")) {
ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? "";
final toTicker =
ref.read(estimatedRateExchangeFormProvider).to?.ticker ?? "";
if (!(fromTicker.isEmpty ||
toTicker.isEmpty ||
toTicker == "-" ||
fromTicker == "-")) {
FixedRateMarket? market; FixedRateMarket? market;
try { try {
market = ref market = ref
@ -491,29 +465,28 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
} }
final newFromAmount = Decimal.tryParse(_sendController.text); final newFromAmount = Decimal.tryParse(_sendController.text);
if (newFromAmount != null) { ref.read(exchangeFormStateProvider).fromAmount =
await ref newFromAmount ?? Decimal.zero;
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(newFromAmount, false);
} else {
await ref
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(Decimal.zero, false);
if (newFromAmount == null) {
_receiveController.text = ""; _receiveController.text = "";
} }
await ref.read(exchangeFormStateProvider).updateMarket(market, false);
await ref await ref
.read(fixedRateExchangeFormProvider) .read(exchangeFormStateProvider)
.updateMarket(market, false);
await ref
.read(fixedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount( .setFromAmountAndCalculateToAmount(
Decimal.tryParse(_sendController.text) ?? Decimal.zero, Decimal.tryParse(_sendController.text) ?? Decimal.zero,
true, true,
); );
if (mounted) {
Navigator.of(context).pop();
}
return; return;
} }
if (mounted) {
Navigator.of(context).pop();
}
unawaited(showFloatingFlushBar( unawaited(showFloatingFlushBar(
type: FlushBarType.warning, type: FlushBarType.warning,
message: message:
@ -527,10 +500,8 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
void onExchangePressed() async { void onExchangePressed() async {
if (ref.read(prefsChangeNotifierProvider).exchangeRateType == if (ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated) { ExchangeRateType.estimated) {
final fromTicker = final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? "";
ref.read(estimatedRateExchangeFormProvider).from?.ticker ?? ""; final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? "";
final toTicker =
ref.read(estimatedRateExchangeFormProvider).to?.ticker ?? "";
bool isAvailable = false; bool isAvailable = false;
final availableFloatingPairs = final availableFloatingPairs =
@ -555,8 +526,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
return; return;
} }
final sendAmount = Decimal.parse( final sendAmount = ref.read(exchangeFormStateProvider).fromAmount!;
ref.read(estimatedRateExchangeFormProvider).fromAmountString);
final rateType = ref.read(prefsChangeNotifierProvider).exchangeRateType; final rateType = ref.read(prefsChangeNotifierProvider).exchangeRateType;
@ -610,12 +580,10 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
} }
} }
} else { } else {
final fromTicker = final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? "";
ref.read(fixedRateExchangeFormProvider).market?.from ?? ""; final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? "";
final toTicker = ref.read(fixedRateExchangeFormProvider).market?.to ?? "";
final sendAmount = Decimal.parse( final sendAmount = ref.read(exchangeFormStateProvider).fromAmount!;
ref.read(fixedRateExchangeFormProvider).fromAmountString);
final rateType = ref.read(prefsChangeNotifierProvider).exchangeRateType; final rateType = ref.read(prefsChangeNotifierProvider).exchangeRateType;
@ -683,7 +651,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
} }
String rate = 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( final model = IncompleteExchangeModel(
sendTicker: fromTicker, sendTicker: fromTicker,
@ -721,21 +689,10 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
String? ticker; String? ticker;
if (ref.read(prefsChangeNotifierProvider).exchangeRateType == if (isSend) {
ExchangeRateType.estimated) { ticker = ref.read(exchangeFormStateProvider).fromTicker;
if (isSend) {
ticker = ref.watch(estimatedRateExchangeFormProvider
.select((value) => value.from?.ticker));
} else {
ticker = ref.watch(estimatedRateExchangeFormProvider
.select((value) => value.to?.ticker));
}
} else { } else {
if (isSend) { ticker = ref.read(exchangeFormStateProvider).toTicker;
ticker = ref.read(fixedRateExchangeFormProvider).market?.from;
} else {
ticker = ref.read(fixedRateExchangeFormProvider).market?.to;
}
} }
if (ticker == null) { if (ticker == null) {
@ -756,46 +713,29 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
if (walletInitiated) { if (walletInitiated) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
ref.read(estimatedRateExchangeFormProvider).clearAmounts(true); ref.read(exchangeFormStateProvider).clearAmounts(true);
// ref.read(fixedRateExchangeFormProvider); // ref.read(fixedRateExchangeFormProvider);
}); });
} else { } else {
final isEstimated = final isEstimated =
ref.read(prefsChangeNotifierProvider).exchangeRateType == ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated; ExchangeRateType.estimated;
_sendController.text = isEstimated _sendController.text =
? ref.read(estimatedRateExchangeFormProvider).fromAmountString ref.read(exchangeFormStateProvider).fromAmountString;
: ref.read(fixedRateExchangeFormProvider).fromAmountString;
_receiveController.text = isEstimated _receiveController.text = isEstimated
? "-" //ref.read(estimatedRateExchangeFormProvider).toAmountString ? "-" //ref.read(estimatedRateExchangeFormProvider).toAmountString
: ref.read(fixedRateExchangeFormProvider).toAmountString; : ref.read(exchangeFormStateProvider).toAmountString;
} }
_sendFocusNode.addListener(() async { _sendFocusNode.addListener(() async {
if (!_sendFocusNode.hasFocus) { if (!_sendFocusNode.hasFocus) {
final newFromAmount = Decimal.tryParse(_sendController.text); final newFromAmount = Decimal.tryParse(_sendController.text);
if (newFromAmount != null) { await ref
if (ref.read(prefsChangeNotifierProvider).exchangeRateType == .read(exchangeFormStateProvider)
ExchangeRateType.estimated) { .setFromAmountAndCalculateToAmount(
await ref newFromAmount ?? Decimal.zero, true);
.read(estimatedRateExchangeFormProvider)
.setFromAmountAndCalculateToAmount(newFromAmount, true); if (newFromAmount == null) {
} 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);
}
_receiveController.text = _receiveController.text =
ref.read(prefsChangeNotifierProvider).exchangeRateType == ref.read(prefsChangeNotifierProvider).exchangeRateType ==
ExchangeRateType.estimated ExchangeRateType.estimated
@ -807,28 +747,14 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
_receiveFocusNode.addListener(() async { _receiveFocusNode.addListener(() async {
if (!_receiveFocusNode.hasFocus) { if (!_receiveFocusNode.hasFocus) {
final newToAmount = Decimal.tryParse(_receiveController.text); final newToAmount = Decimal.tryParse(_receiveController.text);
if (newToAmount != null) { if (ref.read(prefsChangeNotifierProvider).exchangeRateType !=
if (ref.read(prefsChangeNotifierProvider).exchangeRateType == ExchangeRateType.estimated) {
ExchangeRateType.estimated) { await ref
// await ref .read(exchangeFormStateProvider)
// .read(estimatedRateExchangeFormProvider) .setToAmountAndCalculateFromAmount(
// .setToAmountAndCalculateFromAmount(newToAmount, true); newToAmount ?? Decimal.zero, true);
} else { }
await ref if (newToAmount == null) {
.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);
}
_sendController.text = ""; _sendController.text = "";
} }
} }
@ -853,39 +779,29 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
ExchangeRateType.estimated; ExchangeRateType.estimated;
ref.listen( ref.listen(
isEstimated exchangeFormStateProvider.select((value) => value.toAmountString),
? estimatedRateExchangeFormProvider (previous, String next) {
.select((value) => value.toAmountString)
: fixedRateExchangeFormProvider.select(
(value) => value.toAmountString), (previous, String next) {
if (!_receiveFocusNode.hasFocus) { if (!_receiveFocusNode.hasFocus) {
_receiveController.text = isEstimated && next.isEmpty ? "-" : next; _receiveController.text = isEstimated && next.isEmpty ? "-" : next;
debugPrint("RECEIVE AMOUNT LISTENER ACTIVATED"); debugPrint("RECEIVE AMOUNT LISTENER ACTIVATED");
if (_swapLock) { if (_swapLock) {
_sendController.text = isEstimated _sendController.text =
? ref.read(estimatedRateExchangeFormProvider).fromAmountString ref.read(exchangeFormStateProvider).fromAmountString;
: ref.read(fixedRateExchangeFormProvider).fromAmountString;
} }
} }
}); });
ref.listen( ref.listen(
isEstimated exchangeFormStateProvider.select((value) => value.fromAmountString),
? estimatedRateExchangeFormProvider (previous, String next) {
.select((value) => value.fromAmountString)
: fixedRateExchangeFormProvider.select(
(value) => value.fromAmountString), (previous, String next) {
if (!_sendFocusNode.hasFocus) { if (!_sendFocusNode.hasFocus) {
_sendController.text = next; _sendController.text = next;
debugPrint("SEND AMOUNT LISTENER ACTIVATED"); debugPrint("SEND AMOUNT LISTENER ACTIVATED");
if (_swapLock) { if (_swapLock) {
_receiveController.text = isEstimated _receiveController.text = isEstimated
? ref ? ref.read(exchangeFormStateProvider).toAmountString.isEmpty
.read(estimatedRateExchangeFormProvider)
.toAmountString
.isEmpty
? "-" ? "-"
: ref.read(estimatedRateExchangeFormProvider).toAmountString : ref.read(exchangeFormStateProvider).toAmountString
: ref.read(fixedRateExchangeFormProvider).toAmountString; : ref.read(exchangeFormStateProvider).toAmountString;
} }
} }
}); });
@ -956,20 +872,10 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
), ),
child: Builder( child: Builder(
builder: (context) { builder: (context) {
String? image; final image = _fetchIconUrlFromTicker(ref.watch(
if (ref.watch(prefsChangeNotifierProvider.select( exchangeFormStateProvider
(value) => value.exchangeRateType)) == .select((value) => value.fromTicker)));
ExchangeRateType.estimated) {
image = ref
.watch(estimatedRateExchangeFormProvider
.select((value) => value.from))
?.image;
} else {
image = _fetchIconUrlFromTickerForFixedRateFlow(
ref.watch(
fixedRateExchangeFormProvider.select(
(value) => value.market?.from)));
}
if (image != null && image.isNotEmpty) { if (image != null && image.isNotEmpty) {
return Center( return Center(
child: SvgPicture.network( child: SvgPicture.network(
@ -1020,17 +926,9 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
width: 6, width: 6,
), ),
Text( Text(
ref.watch(prefsChangeNotifierProvider.select( ref.watch(exchangeFormStateProvider.select((value) =>
(value) => value.exchangeRateType)) == value.fromTicker?.toUpperCase())) ??
ExchangeRateType.estimated "-",
? ref.watch(estimatedRateExchangeFormProvider
.select((value) =>
value.from?.ticker.toUpperCase())) ??
"-"
: ref.watch(fixedRateExchangeFormProvider.select(
(value) =>
value.market?.from.toUpperCase())) ??
"-",
style: STextStyles.smallMed14(context).copyWith( style: STextStyles.smallMed14(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
@ -1105,14 +1003,13 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
), ),
Positioned.fill( Positioned.fill(
child: Align( child: Align(
alignment: Alignment.topRight, alignment: ref.watch(exchangeFormStateProvider
.select((value) => value.reversed))
? Alignment.bottomRight
: Alignment.topRight,
child: Text( child: Text(
ref.read(prefsChangeNotifierProvider).exchangeRateType == ref.watch(exchangeFormStateProvider
ExchangeRateType.estimated .select((value) => value.warning)),
? ref.watch(estimatedRateExchangeFormProvider
.select((value) => value.minimumSendWarning))
: ref.watch(fixedRateExchangeFormProvider
.select((value) => value.sendAmountWarning)),
style: STextStyles.errorSmall(context), style: STextStyles.errorSmall(context),
), ),
), ),
@ -1179,19 +1076,10 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
), ),
child: Builder( child: Builder(
builder: (context) { builder: (context) {
String? image; final image = _fetchIconUrlFromTicker(ref.watch(
if (ref.watch(prefsChangeNotifierProvider.select( exchangeFormStateProvider
(value) => value.exchangeRateType)) == .select((value) => value.toTicker)));
ExchangeRateType.estimated) {
image = ref
.watch(estimatedRateExchangeFormProvider
.select((value) => value.to))
?.image;
} else {
image = _fetchIconUrlFromTickerForFixedRateFlow(
ref.watch(fixedRateExchangeFormProvider
.select((value) => value.market?.to)));
}
if (image != null && image.isNotEmpty) { if (image != null && image.isNotEmpty) {
return Center( return Center(
child: SvgPicture.network( child: SvgPicture.network(
@ -1240,17 +1128,9 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
width: 6, width: 6,
), ),
Text( Text(
ref.watch(prefsChangeNotifierProvider.select( ref.watch(exchangeFormStateProvider.select(
(value) => value.exchangeRateType)) == (value) => value.toTicker?.toUpperCase())) ??
ExchangeRateType.estimated "-",
? ref.watch(estimatedRateExchangeFormProvider
.select((value) =>
value.to?.ticker.toUpperCase())) ??
"-"
: ref.watch(fixedRateExchangeFormProvider.select(
(value) =>
value.market?.to.toUpperCase())) ??
"-",
style: STextStyles.smallMed14(context).copyWith( style: STextStyles.smallMed14(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!
@ -1293,24 +1173,17 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
fixedRate: ref.watch(prefsChangeNotifierProvider fixedRate: ref.watch(prefsChangeNotifierProvider
.select((value) => value.exchangeRateType)) == .select((value) => value.exchangeRateType)) ==
ExchangeRateType.fixed, ExchangeRateType.fixed,
reversed: _reversed, reversed: ref.watch(
exchangeFormStateProvider.select((value) => value.reversed)),
), ),
const SizedBox( const SizedBox(
height: 12, height: 12,
), ),
PrimaryButton( PrimaryButton(
enabled: ((ref.read(prefsChangeNotifierProvider).exchangeRateType == enabled: ref.watch(
ExchangeRateType.estimated) exchangeFormStateProvider.select((value) => value.canExchange)),
? ref.watch(estimatedRateExchangeFormProvider onPressed: ref.watch(exchangeFormStateProvider
.select((value) => value.canExchange)) .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 ? onExchangePressed
: null, : null,
label: "Exchange", label: "Exchange",

View file

@ -2,22 +2,14 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.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/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/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/exchange/trade_sent_from_stack_lookup_provider.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart'; import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.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:stackwallet/widgets/trade_card.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
@ -191,137 +183,3 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
); );
} }
} }
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<StackColors>()!.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<void>(
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<dynamic>(
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<EdgeInsetsGeometry>(
const EdgeInsets.all(2),
),
backgroundColor: MaterialStateProperty.all<Color>(
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<StackColors>()!
.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),
),
),
),
],
),
),
);
}
}

View file

@ -2,12 +2,11 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.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/pages/exchange_view/exchange_view.dart';
import 'package:stackwallet/providers/exchange/available_currencies_state_provider.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/available_floating_rate_pairs_state_provider.dart';
import 'package:stackwallet/providers/exchange/change_now_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/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; import 'package:stackwallet/services/exchange/change_now/change_now_api.dart';
@ -54,15 +53,15 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
response2.value!; response2.value!;
if (response.value!.length > 1) { 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) { 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); 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) { 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); response.value!.firstWhere((e) => e.ticker == "doge"), true);
} }
} }
@ -97,13 +96,13 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
ref.read(fixedRateMarketPairsStateProvider.state).state = ref.read(fixedRateMarketPairsStateProvider.state).state =
response3.value!; response3.value!;
if (ref.read(fixedRateExchangeFormProvider).market == null) { if (ref.read(exchangeFormStateProvider).market == null) {
final matchingMarkets = final matchingMarkets =
response3.value!.where((e) => e.to == "doge" && e.from == "btc"); response3.value!.where((e) => e.to == "doge" && e.from == "btc");
if (matchingMarkets.isNotEmpty) { if (matchingMarkets.isNotEmpty) {
await ref await ref
.read(fixedRateExchangeFormProvider) .read(exchangeFormStateProvider)
.updateMarket(matchingMarkets.first, true); .updateMarket(matchingMarkets.first, true);
} }
} }
@ -126,7 +125,7 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
@override @override
void initState() { void initState() {
ref.read(estimatedRateExchangeFormProvider).setOnError( ref.read(exchangeFormStateProvider).setOnError(
onError: (String message) => showDialog<dynamic>( onError: (String message) => showDialog<dynamic>(
context: context, context: context,
barrierDismissible: true, barrierDismissible: true,

View file

@ -5,6 +5,7 @@ import 'package:event_bus/event_bus.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.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/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.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/sub_widgets/wallet_summary.dart';
import 'package:stackwallet/pages/wallet_view/transaction_views/all_transactions_view.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/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/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart'; import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
@ -250,6 +250,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
final walletId = ref.read(managerProvider).walletId; final walletId = ref.read(managerProvider).walletId;
ref.read(prefsChangeNotifierProvider).exchangeRateType = ref.read(prefsChangeNotifierProvider).exchangeRateType =
ExchangeRateType.estimated; ExchangeRateType.estimated;
ref.read(exchangeFormStateProvider).exchangeType =
ExchangeRateType.estimated;
final currencies = ref final currencies = ref
.read(availableChangeNowCurrenciesStateProvider.state) .read(availableChangeNowCurrenciesStateProvider.state)
@ -258,28 +260,31 @@ class _WalletViewState extends ConsumerState<WalletView> {
element.ticker.toLowerCase() == coin.ticker.toLowerCase()); element.ticker.toLowerCase() == coin.ticker.toLowerCase());
if (currencies.isNotEmpty) { if (currencies.isNotEmpty) {
unawaited(ref ref.read(exchangeFormStateProvider).setCurrencies(
.read(estimatedRateExchangeFormProvider) currencies.first,
.updateFrom(currencies.first, false)); ref
unawaited(ref.read(estimatedRateExchangeFormProvider).updateTo( .read(availableChangeNowCurrenciesStateProvider.state)
ref .state
.read(availableChangeNowCurrenciesStateProvider.state) .firstWhere(
.state (element) =>
.firstWhere( element.ticker.toLowerCase() !=
(element) => coin.ticker.toLowerCase(),
element.ticker.toLowerCase() != coin.ticker.toLowerCase(), ),
), );
false));
} }
unawaited(Navigator.of(context).pushNamed( if (mounted) {
WalletInitiatedExchangeView.routeName, unawaited(
arguments: Tuple3( Navigator.of(context).pushNamed(
walletId, WalletInitiatedExchangeView.routeName,
coin, arguments: Tuple3(
_loadCNData, walletId,
), coin,
)); _loadCNData,
),
),
);
}
} }
} }

View file

@ -1,5 +1,5 @@
import 'package:flutter_riverpod/flutter_riverpod.dart'; // import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/estimated_rate_exchange_form_state.dart'; // import 'package:stackwallet/models/exchange/estimated_rate_exchange_form_state.dart';
//
final estimatedRateExchangeFormProvider = // final estimatedRateExchangeFormProvider =
ChangeNotifierProvider((ref) => EstimatedRateExchangeFormState()); // ChangeNotifierProvider((ref) => EstimatedRateExchangeFormState());

View file

@ -1,6 +1,6 @@
import 'package:flutter_riverpod/flutter_riverpod.dart'; // import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/fixed_rate_exchange_form_state.dart'; // import 'package:stackwallet/models/exchange/fixed_rate_exchange_form_state.dart';
//
final fixedRateExchangeFormProvider = // final fixedRateExchangeFormProvider =
ChangeNotifierProvider<FixedRateExchangeFormState>( // ChangeNotifierProvider<FixedRateExchangeFormState>(
(ref) => FixedRateExchangeFormState()); // (ref) => FixedRateExchangeFormState());

View file

@ -1,9 +1,8 @@
import 'package:flutter_riverpod/flutter_riverpod.dart'; 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_currencies_state_provider.dart';
import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_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/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/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'; import 'package:stackwallet/services/exchange/change_now/change_now_api.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -38,7 +37,7 @@ class ChangeNowLoadingService {
ref.read(fixedRateMarketPairsStateProvider.state).state = ref.read(fixedRateMarketPairsStateProvider.state).state =
response3.value!; response3.value!;
if (ref.read(fixedRateExchangeFormProvider).market == null) { if (ref.read(exchangeFormStateProvider).market == null) {
String fromTicker = "btc"; String fromTicker = "btc";
String toTicker = "xmr"; String toTicker = "xmr";
@ -50,7 +49,7 @@ class ChangeNowLoadingService {
.where((e) => e.to == toTicker && e.from == fromTicker); .where((e) => e.to == toTicker && e.from == fromTicker);
if (matchingMarkets.isNotEmpty) { if (matchingMarkets.isNotEmpty) {
await ref await ref
.read(fixedRateExchangeFormProvider) .read(exchangeFormStateProvider)
.updateMarket(matchingMarkets.first, true); .updateMarket(matchingMarkets.first, true);
} }
} }
@ -99,18 +98,18 @@ class ChangeNowLoadingService {
} }
if (response.value!.length > 1) { if (response.value!.length > 1) {
if (ref.read(estimatedRateExchangeFormProvider).from == null) { if (ref.read(exchangeFormStateProvider).from == null) {
if (response.value! if (response.value!
.where((e) => e.ticker == fromTicker) .where((e) => e.ticker == fromTicker)
.isNotEmpty) { .isNotEmpty) {
await ref.read(estimatedRateExchangeFormProvider).updateFrom( await ref.read(exchangeFormStateProvider).updateFrom(
response.value!.firstWhere((e) => e.ticker == fromTicker), response.value!.firstWhere((e) => e.ticker == fromTicker),
false); false);
} }
} }
if (ref.read(estimatedRateExchangeFormProvider).to == null) { if (ref.read(exchangeFormStateProvider).to == null) {
if (response.value!.where((e) => e.ticker == toTicker).isNotEmpty) { 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), response.value!.firstWhere((e) => e.ticker == toTicker),
false); false);
} }

View file

@ -1,219 +1,219 @@
import 'package:decimal/decimal.dart'; // import 'package:decimal/decimal.dart';
import 'package:flutter_test/flutter_test.dart'; // import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart'; // import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart'; // import 'package:mockito/mockito.dart';
import 'package:stackwallet/models/exchange/estimated_rate_exchange_form_state.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/currency.dart';
import 'package:stackwallet/models/exchange/response_objects/estimate.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/change_now/change_now_api.dart';
import 'package:stackwallet/services/exchange/exchange_response.dart'; // import 'package:stackwallet/services/exchange/exchange_response.dart';
//
import 'estimated_rate_exchange_form_state_test.mocks.dart'; // import 'estimated_rate_exchange_form_state_test.mocks.dart';
//
@GenerateMocks([ChangeNowAPI]) // @GenerateMocks([ChangeNowAPI])
void main() { // void main() {
final currencyA = Currency( // final currencyA = Currency(
ticker: "btc", // ticker: "btc",
name: "Bitcoin", // name: "Bitcoin",
image: "image.url", // image: "image.url",
hasExternalId: false, // hasExternalId: false,
isFiat: false, // isFiat: false,
featured: false, // featured: false,
isStable: true, // isStable: true,
supportsFixedRate: true, // supportsFixedRate: true,
network: '', // network: '',
); // );
final currencyB = Currency( // final currencyB = Currency(
ticker: "xmr", // ticker: "xmr",
name: "Monero", // name: "Monero",
image: "image.url", // image: "image.url",
hasExternalId: false, // hasExternalId: false,
isFiat: false, // isFiat: false,
featured: false, // featured: false,
isStable: true, // isStable: true,
supportsFixedRate: true, // supportsFixedRate: true,
network: '', // network: '',
); // );
final currencyC = Currency( // final currencyC = Currency(
ticker: "firo", // ticker: "firo",
name: "Firo", // name: "Firo",
image: "image.url", // image: "image.url",
hasExternalId: false, // hasExternalId: false,
isFiat: false, // isFiat: false,
featured: false, // featured: false,
isStable: true, // isStable: true,
supportsFixedRate: true, // supportsFixedRate: true,
network: '', // network: '',
); // );
//
test("EstimatedRateExchangeFormState constructor", () async { // test("EstimatedRateExchangeFormState constructor", () async {
final state = EstimatedRateExchangeFormState(); // final state = EstimatedRateExchangeFormState();
//
expect(state.from, null); // expect(state.from, null);
expect(state.to, null); // expect(state.to, null);
expect(state.canExchange, false); // expect(state.canExchange, false);
expect(state.rate, null); // expect(state.rate, null);
expect(state.rateDisplayString, "N/A"); // expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, ""); // expect(state.fromAmountString, "");
expect(state.toAmountString, ""); // expect(state.toAmountString, "");
expect(state.minimumSendWarning, ""); // expect(state.minimumSendWarning, "");
}); // });
//
test("init EstimatedRateExchangeFormState", () async { // test("init EstimatedRateExchangeFormState", () async {
final state = EstimatedRateExchangeFormState(); // final state = EstimatedRateExchangeFormState();
//
await state.init(currencyA, currencyB); // await state.init(currencyA, currencyB);
//
expect(state.from, currencyA); // expect(state.from, currencyA);
expect(state.to, currencyB); // expect(state.to, currencyB);
expect(state.canExchange, false); // expect(state.canExchange, false);
expect(state.rate, null); // expect(state.rate, null);
expect(state.rateDisplayString, "N/A"); // expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, ""); // expect(state.fromAmountString, "");
expect(state.toAmountString, ""); // expect(state.toAmountString, "");
expect(state.minimumSendWarning, ""); // expect(state.minimumSendWarning, "");
}); // });
//
test("updateTo on fresh state", () async { // test("updateTo on fresh state", () async {
final state = EstimatedRateExchangeFormState(); // final state = EstimatedRateExchangeFormState();
//
await state.updateTo(currencyA, false); // await state.updateTo(currencyA, false);
//
expect(state.from, null); // expect(state.from, null);
expect(state.to, currencyA); // expect(state.to, currencyA);
expect(state.canExchange, false); // expect(state.canExchange, false);
expect(state.rate, null); // expect(state.rate, null);
expect(state.rateDisplayString, "N/A"); // expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, ""); // expect(state.fromAmountString, "");
expect(state.toAmountString, ""); // expect(state.toAmountString, "");
expect(state.minimumSendWarning, ""); // expect(state.minimumSendWarning, "");
}); // });
//
test( // test(
"updateTo after updateFrom where amounts are null and getMinimalExchangeAmount succeeds", // "updateTo after updateFrom where amounts are null and getMinimalExchangeAmount succeeds",
() async { // () async {
final cn = MockChangeNowAPI(); // final cn = MockChangeNowAPI();
//
final state = EstimatedRateExchangeFormState(); // final state = EstimatedRateExchangeFormState();
state.cnTesting = cn; // state.cnTesting = cn;
//
when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) // when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42))); // .thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42)));
//
await state.updateFrom(currencyA, true); // await state.updateFrom(currencyA, true);
await state.updateTo(currencyB, true); // await state.updateTo(currencyB, true);
//
expect(state.from, currencyA); // expect(state.from, currencyA);
expect(state.to, currencyB); // expect(state.to, currencyB);
expect(state.canExchange, false); // expect(state.canExchange, false);
expect(state.rate, null); // expect(state.rate, null);
expect(state.rateDisplayString, "N/A"); // expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, ""); // expect(state.fromAmountString, "");
expect(state.toAmountString, ""); // expect(state.toAmountString, "");
expect(state.minimumSendWarning, ""); // expect(state.minimumSendWarning, "");
//
verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) // verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.called(1); // .called(1);
}); // });
//
test( // test(
"updateTo after updateFrom where amounts are null and getMinimalExchangeAmount fails", // "updateTo after updateFrom where amounts are null and getMinimalExchangeAmount fails",
() async { // () async {
final cn = MockChangeNowAPI(); // final cn = MockChangeNowAPI();
//
final state = EstimatedRateExchangeFormState(); // final state = EstimatedRateExchangeFormState();
state.cnTesting = cn; // state.cnTesting = cn;
//
when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) // when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.thenAnswer((_) async => ExchangeResponse()); // .thenAnswer((_) async => ExchangeResponse());
//
await state.updateFrom(currencyA, true); // await state.updateFrom(currencyA, true);
await state.updateTo(currencyB, true); // await state.updateTo(currencyB, true);
//
expect(state.from, currencyA); // expect(state.from, currencyA);
expect(state.to, currencyB); // expect(state.to, currencyB);
expect(state.canExchange, false); // expect(state.canExchange, false);
expect(state.rate, null); // expect(state.rate, null);
expect(state.rateDisplayString, "N/A"); // expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, ""); // expect(state.fromAmountString, "");
expect(state.toAmountString, ""); // expect(state.toAmountString, "");
expect(state.minimumSendWarning, ""); // expect(state.minimumSendWarning, "");
//
verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) // verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.called(1); // .called(1);
}); // });
//
test( // test(
"updateTo after updateFrom and setFromAmountAndCalculateToAmount where fromAmount is less than the minimum required exchange amount", // "updateTo after updateFrom and setFromAmountAndCalculateToAmount where fromAmount is less than the minimum required exchange amount",
() async { // () async {
final cn = MockChangeNowAPI(); // final cn = MockChangeNowAPI();
//
final state = EstimatedRateExchangeFormState(); // final state = EstimatedRateExchangeFormState();
state.cnTesting = cn; // state.cnTesting = cn;
//
when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) // when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42))); // .thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42)));
//
await state.updateFrom(currencyA, true); // await state.updateFrom(currencyA, true);
await state.setFromAmountAndCalculateToAmount(Decimal.parse("10.10"), true); // await state.setFromAmountAndCalculateToAmount(Decimal.parse("10.10"), true);
await state.updateTo(currencyB, true); // await state.updateTo(currencyB, true);
//
expect(state.from, currencyA); // expect(state.from, currencyA);
expect(state.to, currencyB); // expect(state.to, currencyB);
expect(state.canExchange, false); // expect(state.canExchange, false);
expect(state.rate, null); // expect(state.rate, null);
expect(state.rateDisplayString, "N/A"); // expect(state.rateDisplayString, "N/A");
expect(state.fromAmountString, "10.10000000"); // expect(state.fromAmountString, "10.10000000");
expect(state.toAmountString, ""); // expect(state.toAmountString, "");
expect(state.minimumSendWarning, "Minimum amount 42 BTC"); // expect(state.minimumSendWarning, "Minimum amount 42 BTC");
//
verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) // verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.called(1); // .called(1);
}); // });
//
test( // test(
"updateTo after updateFrom and setFromAmountAndCalculateToAmount where fromAmount is greater than the minimum required exchange amount", // "updateTo after updateFrom and setFromAmountAndCalculateToAmount where fromAmount is greater than the minimum required exchange amount",
() async { // () async {
final cn = MockChangeNowAPI(); // final cn = MockChangeNowAPI();
//
final state = EstimatedRateExchangeFormState(); // final state = EstimatedRateExchangeFormState();
state.cnTesting = cn; // state.cnTesting = cn;
//
when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) // when(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42))); // .thenAnswer((_) async => ExchangeResponse(value: Decimal.fromInt(42)));
when(cn.getEstimatedExchangeAmount( // when(cn.getEstimatedExchangeAmount(
fromTicker: "btc", // fromTicker: "btc",
toTicker: "xmr", // toTicker: "xmr",
fromAmount: Decimal.parse("110.10"))) // fromAmount: Decimal.parse("110.10")))
.thenAnswer((_) async => ExchangeResponse( // .thenAnswer((_) async => ExchangeResponse(
value: Estimate( // value: Estimate(
reversed: false, // reversed: false,
fixedRate: false, // fixedRate: false,
rateId: 'some rate id', // rateId: 'some rate id',
warningMessage: '', // warningMessage: '',
estimatedAmount: Decimal.parse("302.002348"), // estimatedAmount: Decimal.parse("302.002348"),
))); // )));
//
await state.updateFrom(currencyA, true); // await state.updateFrom(currencyA, true);
await state.setFromAmountAndCalculateToAmount( // await state.setFromAmountAndCalculateToAmount(
Decimal.parse("110.10"), true); // Decimal.parse("110.10"), true);
await state.updateTo(currencyB, true); // await state.updateTo(currencyB, true);
//
expect(state.from, currencyA); // expect(state.from, currencyA);
expect(state.to, currencyB); // expect(state.to, currencyB);
expect(state.canExchange, true); // expect(state.canExchange, true);
expect(state.rate, Decimal.parse("2.742982270663")); // expect(state.rate, Decimal.parse("2.742982270663"));
expect(state.rateDisplayString, "1 BTC ~2.74298227 XMR"); // expect(state.rateDisplayString, "1 BTC ~2.74298227 XMR");
expect(state.fromAmountString, "110.10000000"); // expect(state.fromAmountString, "110.10000000");
expect(state.toAmountString, "302.00234800"); // expect(state.toAmountString, "302.00234800");
expect(state.minimumSendWarning, ""); // expect(state.minimumSendWarning, "");
//
verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr")) // verify(cn.getMinimalExchangeAmount(fromTicker: "btc", toTicker: "xmr"))
.called(1); // .called(1);
verify(cn.getEstimatedExchangeAmount( // verify(cn.getEstimatedExchangeAmount(
fromTicker: "btc", // fromTicker: "btc",
toTicker: "xmr", // toTicker: "xmr",
fromAmount: Decimal.parse("110.10"))) // fromAmount: Decimal.parse("110.10")))
.called(1); // .called(1);
}); // });
} // }