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/models/exchange/change_now/exchange_transaction.dart';
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart';
import 'package:stackwallet/models/exchange/exchange_form_state.dart';
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
import 'package:stackwallet/models/isar/models/log.dart';
import 'package:stackwallet/models/models.dart';
@ -35,8 +36,6 @@ import 'package:stackwallet/pages_desktop_specific/home/desktop_home_view.dart';
import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart';
import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_state_provider.dart';
import 'package:stackwallet/providers/exchange/changenow_initial_load_status.dart';
import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/global/base_currencies_provider.dart';
@ -47,6 +46,7 @@ import 'package:stackwallet/providers/ui/color_theme_provider.dart';
import 'package:stackwallet/route_generator.dart';
import 'package:stackwallet/services/debug_service.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_api.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
import 'package:stackwallet/services/locale_service.dart';
import 'package:stackwallet/services/node_service.dart';
import 'package:stackwallet/services/notifications_api.dart';
@ -271,15 +271,15 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
response2.value!;
if (response.value!.length > 1) {
if (ref.read(estimatedRateExchangeFormProvider).from == null) {
if (ref.read(exchangeFormStateProvider).from == null) {
if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) {
await ref.read(estimatedRateExchangeFormProvider).updateFrom(
await ref.read(exchangeFormStateProvider).updateFrom(
response.value!.firstWhere((e) => e.ticker == "btc"), false);
}
}
if (ref.read(estimatedRateExchangeFormProvider).to == null) {
if (ref.read(exchangeFormStateProvider).to == null) {
if (response.value!.where((e) => e.ticker == "doge").isNotEmpty) {
await ref.read(estimatedRateExchangeFormProvider).updateTo(
await ref.read(exchangeFormStateProvider).updateTo(
response.value!.firstWhere((e) => e.ticker == "doge"), false);
}
}
@ -319,12 +319,12 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
ref.read(fixedRateMarketPairsStateProvider.state).state =
response3.value!;
if (ref.read(fixedRateExchangeFormProvider).market == null) {
if (ref.read(exchangeFormStateProvider).market == null) {
final matchingMarkets =
response3.value!.where((e) => e.to == "doge" && e.from == "btc");
if (matchingMarkets.isNotEmpty) {
await ref
.read(fixedRateExchangeFormProvider)
.read(exchangeFormStateProvider)
.updateMarket(matchingMarkets.first, true);
}
}
@ -355,6 +355,7 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
@override
void initState() {
ref.read(exchangeFormStateProvider).exchange = ChangeNowExchange();
final colorScheme = DB.instance
.get<dynamic>(boxName: DB.boxNameTheme, key: "colorScheme") as String?;

View file

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

View file

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

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

View file

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

View file

@ -2,22 +2,14 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/pages/exchange_view/exchange_form.dart';
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
import 'package:stackwallet/pages/exchange_view/trade_details_view.dart';
import 'package:stackwallet/providers/exchange/changenow_initial_load_status.dart';
import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/trade_sent_from_stack_lookup_provider.dart';
import 'package:stackwallet/providers/global/trades_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/utilities/theme/stack_colors.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:stackwallet/widgets/trade_card.dart';
import 'package:tuple/tuple.dart';
@ -191,137 +183,3 @@ class _ExchangeViewState extends ConsumerState<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_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/exchange/exchange_form_state.dart';
import 'package:stackwallet/pages/exchange_view/exchange_view.dart';
import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart';
import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_state_provider.dart';
import 'package:stackwallet/providers/exchange/change_now_provider.dart';
import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_api.dart';
@ -54,15 +53,15 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
response2.value!;
if (response.value!.length > 1) {
if (ref.read(estimatedRateExchangeFormProvider).from == null) {
if (ref.read(exchangeFormStateProvider).from == null) {
if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) {
await ref.read(estimatedRateExchangeFormProvider).updateFrom(
await ref.read(exchangeFormStateProvider).updateFrom(
response.value!.firstWhere((e) => e.ticker == "btc"), true);
}
}
if (ref.read(estimatedRateExchangeFormProvider).to == null) {
if (ref.read(exchangeFormStateProvider).to == null) {
if (response.value!.where((e) => e.ticker == "doge").isNotEmpty) {
await ref.read(estimatedRateExchangeFormProvider).updateTo(
await ref.read(exchangeFormStateProvider).updateTo(
response.value!.firstWhere((e) => e.ticker == "doge"), true);
}
}
@ -97,13 +96,13 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
ref.read(fixedRateMarketPairsStateProvider.state).state =
response3.value!;
if (ref.read(fixedRateExchangeFormProvider).market == null) {
if (ref.read(exchangeFormStateProvider).market == null) {
final matchingMarkets =
response3.value!.where((e) => e.to == "doge" && e.from == "btc");
if (matchingMarkets.isNotEmpty) {
await ref
.read(fixedRateExchangeFormProvider)
.read(exchangeFormStateProvider)
.updateMarket(matchingMarkets.first, true);
}
}
@ -126,7 +125,7 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
@override
void initState() {
ref.read(estimatedRateExchangeFormProvider).setOnError(
ref.read(exchangeFormStateProvider).setOnError(
onError: (String message) => showDialog<dynamic>(
context: context,
barrierDismissible: true,

View file

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

View file

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

View file

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

View file

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

View file

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