WIP exchange screen state refactor

This commit is contained in:
julian 2023-05-03 14:03:31 -06:00
parent 3564426248
commit 3a97623e5b
19 changed files with 1169 additions and 942 deletions

View file

@ -299,7 +299,8 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
await ref.read(prefsChangeNotifierProvider).isExternalCallsSet()) { await ref.read(prefsChangeNotifierProvider).isExternalCallsSet()) {
if (Constants.enableExchange) { if (Constants.enableExchange) {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty( await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider), ref.read(efCurrencyPairProvider),
ref.read(efRateTypeProvider),
); );
unawaited(ExchangeDataLoadingService.instance.loadAll()); unawaited(ExchangeDataLoadingService.instance.loadAll());
} }

View file

@ -0,0 +1,35 @@
import 'package:flutter/foundation.dart';
import 'package:stackwallet/models/exchange/aggregate_currency.dart';
class ActivePair extends ChangeNotifier {
AggregateCurrency? _send;
AggregateCurrency? _receive;
AggregateCurrency? get send => _send;
AggregateCurrency? get receive => _receive;
void setSend(
AggregateCurrency? newSend, {
bool notifyListeners = false,
}) {
_send = newSend;
if (notifyListeners) {
this.notifyListeners();
}
}
void setReceive(
AggregateCurrency? newReceive, {
bool notifyListeners = false,
}) {
_receive = newReceive;
if (notifyListeners) {
this.notifyListeners();
}
}
@override
String toString() {
return "ActivePair{ send: $send, receive: $receive }";
}
}

File diff suppressed because it is too large Load diff

View file

@ -9,6 +9,7 @@ import 'package:intl/intl.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:stackwallet/models/exchange/aggregate_currency.dart'; import 'package:stackwallet/models/exchange/aggregate_currency.dart';
import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; import 'package:stackwallet/models/exchange/incomplete_exchange.dart';
import 'package:stackwallet/models/exchange/response_objects/estimate.dart';
import 'package:stackwallet/models/isar/exchange_cache/currency.dart'; import 'package:stackwallet/models/isar/exchange_cache/currency.dart';
import 'package:stackwallet/models/isar/exchange_cache/pair.dart'; import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart'; import 'package:stackwallet/models/isar/models/ethereum/eth_contract.dart';
@ -21,10 +22,13 @@ import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_ste
import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart'; import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart'; import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
import 'package:stackwallet/services/exchange/trocador/trocador_exchange.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/exchange_rate_type_enum.dart'; import 'package:stackwallet/utilities/enums/exchange_rate_type_enum.dart';
import 'package:stackwallet/utilities/logger.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/utilities/util.dart'; import 'package:stackwallet/utilities/util.dart';
@ -61,6 +65,12 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
late final Coin? coin; late final Coin? coin;
late final bool walletInitiated; late final bool walletInitiated;
final exchanges = [
MajesticBankExchange.instance,
ChangeNowExchange.instance,
TrocadorExchange.instance,
];
late final TextEditingController _sendController; late final TextEditingController _sendController;
late final TextEditingController _receiveController; late final TextEditingController _receiveController;
final isDesktop = Util.isDesktop; final isDesktop = Util.isDesktop;
@ -105,16 +115,17 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
} }
Timer? _sendFieldOnChangedTimer; Timer? _sendFieldOnChangedTimer;
void sendFieldOnChanged(String value) async { void sendFieldOnChanged(String value) {
if (_sendFocusNode.hasFocus) { if (_sendFocusNode.hasFocus) {
_sendFieldOnChangedTimer?.cancel(); _sendFieldOnChangedTimer?.cancel();
_sendFieldOnChangedTimer = Timer(_valueCheckInterval, () async { _sendFieldOnChangedTimer = Timer(_valueCheckInterval, () async {
final newFromAmount = _localizedStringToNum(value); final newFromAmount = _localizedStringToNum(value);
await ref ref.read(efSendAmountProvider.notifier).state = newFromAmount;
.read(exchangeFormStateProvider) if (!_swapLock && !ref.read(efReversedProvider)) {
.setSendAmountAndCalculateReceiveAmount(newFromAmount, true); unawaited(update());
}
}); });
} }
} }
@ -126,9 +137,10 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
_receiveFieldOnChangedTimer = Timer(_valueCheckInterval, () async { _receiveFieldOnChangedTimer = Timer(_valueCheckInterval, () async {
final newToAmount = _localizedStringToNum(value); final newToAmount = _localizedStringToNum(value);
await ref ref.read(efReceiveAmountProvider.notifier).state = newToAmount;
.read(exchangeFormStateProvider) if (!_swapLock && ref.read(efReversedProvider)) {
.setReceivingAmountAndCalculateSendAmount(newToAmount, true); unawaited(update());
}
}); });
} }
@ -147,7 +159,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
} }
Future<AggregateCurrency> _getAggregateCurrency(Currency currency) async { Future<AggregateCurrency> _getAggregateCurrency(Currency currency) async {
final rateType = ref.read(exchangeFormStateProvider).exchangeRateType; final rateType = ref.read(efRateTypeProvider);
final currencies = await ExchangeDataLoadingService.instance.isar.currencies final currencies = await ExchangeDataLoadingService.instance.isar.currencies
.filter() .filter()
.group((q) => rateType == ExchangeRateType.fixed .group((q) => rateType == ExchangeRateType.fixed
@ -178,8 +190,8 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
} }
void selectSendCurrency() async { void selectSendCurrency() async {
final type = (ref.read(exchangeFormStateProvider).exchangeRateType); final type = ref.read(efRateTypeProvider);
final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? ""; final fromTicker = ref.read(efCurrencyPairProvider).send?.ticker ?? "";
if (walletInitiated) { if (walletInitiated) {
if (widget.contract != null && if (widget.contract != null &&
@ -194,24 +206,26 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
} }
final selectedCurrency = await _showCurrencySelectionSheet( final selectedCurrency = await _showCurrencySelectionSheet(
willChange: ref.read(exchangeFormStateProvider).sendCurrency?.ticker, willChange: ref.read(efCurrencyPairProvider).send?.ticker,
willChangeIsSend: true, willChangeIsSend: true,
paired: ref.read(exchangeFormStateProvider).receiveCurrency?.ticker, paired: ref.read(efCurrencyPairProvider).receive?.ticker,
isFixedRate: type == ExchangeRateType.fixed, isFixedRate: type == ExchangeRateType.fixed,
); );
if (selectedCurrency != null) { if (selectedCurrency != null) {
await showUpdatingExchangeRate( await showUpdatingExchangeRate(
whileFuture: _getAggregateCurrency(selectedCurrency).then( whileFuture: _getAggregateCurrency(selectedCurrency).then(
(aggregateSelected) => ref (aggregateSelected) => ref.read(efCurrencyPairProvider).setSend(
.read(exchangeFormStateProvider) aggregateSelected,
.updateSendCurrency(aggregateSelected, true)), notifyListeners: true,
),
),
); );
} }
} }
void selectReceiveCurrency() async { void selectReceiveCurrency() async {
final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? ""; final toTicker = ref.read(efCurrencyPairProvider).receive?.ticker ?? "";
if (walletInitiated && if (walletInitiated &&
toTicker.toLowerCase() == coin!.ticker.toLowerCase()) { toTicker.toLowerCase() == coin!.ticker.toLowerCase()) {
// do not allow changing away from wallet coin // do not allow changing away from wallet coin
@ -219,19 +233,20 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
} }
final selectedCurrency = await _showCurrencySelectionSheet( final selectedCurrency = await _showCurrencySelectionSheet(
willChange: ref.read(exchangeFormStateProvider).receiveCurrency?.ticker, willChange: ref.read(efCurrencyPairProvider).receive?.ticker,
willChangeIsSend: false, willChangeIsSend: false,
paired: ref.read(exchangeFormStateProvider).sendCurrency?.ticker, paired: ref.read(efCurrencyPairProvider).send?.ticker,
isFixedRate: ref.read(exchangeFormStateProvider).exchangeRateType == isFixedRate: ref.read(efRateTypeProvider) == ExchangeRateType.fixed,
ExchangeRateType.fixed,
); );
if (selectedCurrency != null) { if (selectedCurrency != null) {
await showUpdatingExchangeRate( await showUpdatingExchangeRate(
whileFuture: _getAggregateCurrency(selectedCurrency).then( whileFuture: _getAggregateCurrency(selectedCurrency).then(
(aggregateSelected) => ref (aggregateSelected) => ref.read(efCurrencyPairProvider).setReceive(
.read(exchangeFormStateProvider) aggregateSelected,
.updateReceivingCurrency(aggregateSelected, true)), notifyListeners: true,
),
),
); );
} }
} }
@ -241,10 +256,25 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
_sendFocusNode.unfocus(); _sendFocusNode.unfocus();
_receiveFocusNode.unfocus(); _receiveFocusNode.unfocus();
await showUpdatingExchangeRate( final temp = ref.read(efCurrencyPairProvider).send;
whileFuture: ref.read(efCurrencyPairProvider).setSend(
ref.read(exchangeFormStateProvider).swap(shouldNotifyListeners: true), ref.read(efCurrencyPairProvider).receive,
); notifyListeners: true,
);
ref.read(efCurrencyPairProvider).setReceive(
temp,
notifyListeners: true,
);
// final reversed = ref.read(efReversedProvider);
final amount = ref.read(efSendAmountProvider);
ref.read(efSendAmountProvider.notifier).state =
ref.read(efReceiveAmountProvider);
ref.read(efReceiveAmountProvider.notifier).state = amount;
unawaited(update());
_swapLock = false; _swapLock = false;
} }
@ -331,89 +361,20 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
} }
} }
void onRateTypeChanged(ExchangeRateType newType) async { void onRateTypeChanged(ExchangeRateType newType) {
_receiveFocusNode.unfocus(); _receiveFocusNode.unfocus();
_sendFocusNode.unfocus(); _sendFocusNode.unfocus();
await showUpdatingExchangeRate( ref.read(efRateTypeProvider.notifier).state = newType;
whileFuture: _onRateTypeChangedFuture(newType), update();
);
}
Future<void> _onRateTypeChangedFuture(ExchangeRateType newType) async {
ref.read(exchangeFormStateProvider).exchangeRateType = newType;
final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? "-";
final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? "-";
ref.read(exchangeFormStateProvider).reversed = false;
if (!(toTicker == "-" || fromTicker == "-")) {
// final available = await ExchangeDataLoadingService.instance.isar.pairs
// .where()
// .exchangeNameEqualTo(
// ref.read(currentExchangeNameStateProvider.state).state)
// .filter()
// .fromEqualTo(fromTicker)
// .and()
// .toEqualTo(toTicker)
// .findAll();
await ref.read(exchangeFormStateProvider).refresh();
// if (available.isNotEmpty) {
// final availableCurrencies = await ExchangeDataLoadingService
// .instance.isar.currencies
// .where()
// .exchangeNameEqualTo(
// ref.read(currentExchangeNameStateProvider.state).state)
// .filter()
// .tickerEqualTo(fromTicker)
// .or()
// .tickerEqualTo(toTicker)
// .findAll();
//
// if (availableCurrencies.length > 1) {
// final from =
// availableCurrencies.firstWhere((e) => e.ticker == fromTicker);
// final to =
// availableCurrencies.firstWhere((e) => e.ticker == toTicker);
//
// final newFromAmount = Decimal.tryParse(_sendController.text);
// ref.read(exchangeFormStateProvider).receiveAmount = newFromAmount;
// if (newFromAmount == null) {
// _receiveController.text = "";
// }
//
// await ref
// .read(exchangeFormStateProvider)
// .updateReceivingCurrency(to, false);
// await ref
// .read(exchangeFormStateProvider)
// .updateSendCurrency(from, true);
//
// _receiveController.text =
// ref.read(exchangeFormStateProvider).toAmountString.isEmpty
// ? "-"
// : ref.read(exchangeFormStateProvider).toAmountString;
// if (mounted) {
// Navigator.of(context, rootNavigator: isDesktop).pop();
// }
// return;
// }
// }
}
} }
void onExchangePressed() async { void onExchangePressed() async {
final rateType = ref.read(exchangeFormStateProvider).exchangeRateType; final rateType = ref.read(efRateTypeProvider);
final fromTicker = ref.read(exchangeFormStateProvider).fromTicker ?? ""; final fromTicker = ref.read(efCurrencyPairProvider).send?.ticker ?? "";
final toTicker = ref.read(exchangeFormStateProvider).toTicker ?? ""; final toTicker = ref.read(efCurrencyPairProvider).receive?.ticker ?? "";
final sendAmount = ref.read(exchangeFormStateProvider).sendAmount!; final estimate = ref.read(efEstimateProvider)!;
final estimate = ref.read(exchangeFormStateProvider).estimates.firstWhere( final sendAmount = ref.read(efSendAmountProvider)!;
(e) =>
e.exchangeProvider ==
ref.read(exchangeFormStateProvider).providerName,
);
if (rateType == ExchangeRateType.fixed && toTicker.toUpperCase() == "WOW") { if (rateType == ExchangeRateType.fixed && toTicker.toUpperCase() == "WOW") {
await showDialog<void>( await showDialog<void>(
@ -430,10 +391,16 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
String rate; String rate;
final amountToSend =
estimate.reversed ? estimate.estimatedAmount : sendAmount;
final amountToReceive = estimate.reversed
? ref.read(efReceiveAmountProvider)!
: estimate.estimatedAmount;
switch (rateType) { switch (rateType) {
case ExchangeRateType.estimated: case ExchangeRateType.estimated:
rate = rate =
"1 ${fromTicker.toUpperCase()} ~${(estimate.estimatedAmount / sendAmount).toDecimal(scaleOnInfinitePrecision: 8).toStringAsFixed(8)} ${toTicker.toUpperCase()}"; "1 ${fromTicker.toUpperCase()} ~${(amountToReceive / sendAmount).toDecimal(scaleOnInfinitePrecision: 8).toStringAsFixed(8)} ${toTicker.toUpperCase()}";
break; break;
case ExchangeRateType.fixed: case ExchangeRateType.fixed:
bool? shouldCancel; bool? shouldCancel;
@ -545,7 +512,9 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
return; return;
} }
rate = rate =
"1 ${fromTicker.toUpperCase()} ~${ref.read(exchangeFormStateProvider).rate!.toStringAsFixed(8)} ${toTicker.toUpperCase()}"; "1 ${fromTicker.toUpperCase()} ~${(amountToReceive / amountToSend).toDecimal(
scaleOnInfinitePrecision: 12,
).toStringAsFixed(8)} ${toTicker.toUpperCase()}";
break; break;
} }
@ -553,10 +522,8 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
sendTicker: fromTicker.toUpperCase(), sendTicker: fromTicker.toUpperCase(),
receiveTicker: toTicker.toUpperCase(), receiveTicker: toTicker.toUpperCase(),
rateInfo: rate, rateInfo: rate,
sendAmount: estimate.reversed ? estimate.estimatedAmount : sendAmount, sendAmount: amountToSend,
receiveAmount: estimate.reversed receiveAmount: amountToReceive,
? ref.read(exchangeFormStateProvider).receiveAmount!
: estimate.estimatedAmount,
rateType: rateType, rateType: rateType,
estimate: estimate, estimate: estimate,
reversed: estimate.reversed, reversed: estimate.reversed,
@ -626,8 +593,8 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
} }
String? ticker = isSend String? ticker = isSend
? ref.read(exchangeFormStateProvider).fromTicker ? ref.read(efCurrencyPairProvider).send?.ticker
: ref.read(exchangeFormStateProvider).toTicker; : ref.read(efCurrencyPairProvider).receive?.ticker;
if (ticker == null) { if (ticker == null) {
return false; return false;
@ -636,6 +603,85 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
return coin.ticker.toUpperCase() == ticker.toUpperCase(); return coin.ticker.toUpperCase() == ticker.toUpperCase();
} }
Future<void> update() async {
ref.read(efRefreshingProvider.notifier).state = true;
for (final exchange in exchanges) {
ref.read(efEstimatesListProvider(exchange.name).notifier).state = null;
}
final reversed = ref.read(efReversedProvider);
final amount = reversed
? ref.read(efReceiveAmountProvider)
: ref.read(efSendAmountProvider);
if (amount == null || amount <= Decimal.zero) {
ref.read(efRefreshingProvider.notifier).state = false;
return;
}
final rateType = ref.read(efRateTypeProvider);
final pair = ref.read(efCurrencyPairProvider);
for (final exchange in exchanges) {
final sendCurrency = pair.send?.forExchange(exchange.name);
final receiveCurrency = pair.receive?.forExchange(exchange.name);
if (sendCurrency != null && receiveCurrency != null) {
final rangeResponse = await exchange.getRange(
reversed ? receiveCurrency.ticker : sendCurrency.ticker,
reversed ? sendCurrency.ticker : receiveCurrency.ticker,
rateType == ExchangeRateType.fixed,
);
if (rangeResponse.value == null) {
Logging.instance.log(
"Tried to $runtimeType.update Range for:"
" $exchange where response: $rangeResponse",
level: LogLevel.Info,
);
}
final estimateResponse = await exchange.getEstimates(
sendCurrency.ticker,
receiveCurrency.ticker,
amount,
rateType == ExchangeRateType.fixed,
reversed,
);
if (estimateResponse.value == null) {
Logging.instance.log(
"Tried to $runtimeType._fetchEstimateAndRange Estimate for:"
" $exchange where response: $estimateResponse",
level: LogLevel.Info,
);
}
if (estimateResponse.value != null && rangeResponse.value != null) {
ref.read(efEstimatesListProvider(exchange.name).notifier).state =
Tuple2(estimateResponse.value!, rangeResponse.value!);
}
}
}
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(efRefreshingProvider.notifier).state = false;
});
}
void updateSend(Estimate? estimate) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(efSendAmountProvider.notifier).state = estimate?.estimatedAmount;
});
}
void updateReceive(Estimate? estimate) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(efReceiveAmountProvider.notifier).state =
estimate?.estimatedAmount;
});
}
@override @override
void initState() { void initState() {
_sendController = TextEditingController(); _sendController = TextEditingController();
@ -645,9 +691,32 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
coin = widget.coin; coin = widget.coin;
walletInitiated = walletId != null && coin != null; walletInitiated = walletId != null && coin != null;
_sendFocusNode.addListener(() {
if (_sendFocusNode.hasFocus) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(efReversedProvider.notifier).state = false;
});
}
});
_receiveFocusNode.addListener(() {
if (_receiveFocusNode.hasFocus) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ref.read(efReversedProvider.notifier).state = true;
});
}
});
if (walletInitiated) { if (walletInitiated) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
ref.read(exchangeFormStateProvider).reset(shouldNotifyListeners: true); ref.read(efSendAmountProvider.notifier).state = null;
ref.read(efReceiveAmountProvider.notifier).state = null;
ref.read(efReversedProvider.notifier).state = false;
ref.read(efWarningProvider.notifier).state = "";
ref.read(efRefreshingProvider.notifier).state = false;
ref.read(efCurrencyPairProvider).setSend(null, notifyListeners: true);
ref
.read(efCurrencyPairProvider)
.setReceive(null, notifyListeners: true);
ExchangeDataLoadingService.instance ExchangeDataLoadingService.instance
.getAggregateCurrency( .getAggregateCurrency(
widget.contract == null ? coin!.ticker : widget.contract!.symbol, widget.contract == null ? coin!.ticker : widget.contract!.symbol,
@ -656,17 +725,17 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
) )
.then((value) { .then((value) {
if (value != null) { if (value != null) {
ref.read(exchangeFormStateProvider).updateSendCurrency(value, true); ref.read(efCurrencyPairProvider).setSend(
value,
notifyListeners: true,
);
} }
}); });
}); });
} else { } else {
_sendController.text =
ref.read(exchangeFormStateProvider).fromAmountString;
_receiveController.text =
ref.read(exchangeFormStateProvider).toAmountString;
WidgetsBinding.instance.addPostFrameCallback((timeStamp) { WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
ref.read(exchangeFormStateProvider).refresh(); _sendController.text = ref.read(efSendAmountStringProvider);
_receiveController.text = ref.read(efReceiveAmountStringProvider);
}); });
} }
@ -684,37 +753,42 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType"); debugPrint("BUILD: $runtimeType");
final rateType = ref.watch( final rateType = ref.watch(efRateTypeProvider);
exchangeFormStateProvider.select((value) => value.exchangeRateType));
final isEstimated = rateType == ExchangeRateType.estimated; final isEstimated = rateType == ExchangeRateType.estimated;
ref.listen( ref.listen(efReceiveAmountStringProvider, (previous, String next) {
exchangeFormStateProvider.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;
if (_swapLock) { // if (_swapLock) {
_sendController.text = _sendController.text = ref.read(efSendAmountStringProvider);
ref.read(exchangeFormStateProvider).fromAmountString; // }
}
} }
}); });
ref.listen( ref.listen(efSendAmountStringProvider, (previous, String next) {
exchangeFormStateProvider.select((value) => value.fromAmountString),
(previous, String next) {
if (!_sendFocusNode.hasFocus) { if (!_sendFocusNode.hasFocus) {
_sendController.text = next; _sendController.text = next;
if (_swapLock) { // if (_swapLock) {
_receiveController.text = isEstimated _receiveController.text =
? ref.read(exchangeFormStateProvider).toAmountString.isEmpty isEstimated && ref.read(efReceiveAmountStringProvider).isEmpty
? "-" ? "-"
: ref.read(exchangeFormStateProvider).toAmountString : ref.read(efReceiveAmountStringProvider);
: ref.read(exchangeFormStateProvider).toAmountString; // }
}
} }
}); });
ref.listen(efEstimateProvider.notifier, (previous, next) {
if (ref.read(efReversedProvider)) {
updateSend((next as StateController<Estimate?>).state);
} else {
updateReceive((next as StateController<Estimate?>).state);
}
});
ref.listen(efCurrencyPairProvider, (previous, next) {
update();
});
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
@ -729,8 +803,9 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
height: isDesktop ? 10 : 4, height: isDesktop ? 10 : 4,
), ),
ExchangeTextField( ExchangeTextField(
key: Key( key: Key("exchangeTextFieldKeyFor_"
"exchangeTextFieldKeyFor_${Theme.of(context).extension<StackColors>()!.themeType.name}"), "${Theme.of(context).extension<StackColors>()!.themeType.name}"
"${ref.watch(efCurrencyPairProvider.select((value) => value.send?.ticker))}"),
controller: _sendController, controller: _sendController,
focusNode: _sendFocusNode, focusNode: _sendFocusNode,
textStyle: STextStyles.smallMed14(context).copyWith( textStyle: STextStyles.smallMed14(context).copyWith(
@ -749,8 +824,8 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
onChanged: sendFieldOnChanged, onChanged: sendFieldOnChanged,
onButtonTap: selectSendCurrency, onButtonTap: selectSendCurrency,
isWalletCoin: isWalletCoin(coin, true), isWalletCoin: isWalletCoin(coin, true),
currency: ref.watch( currency:
exchangeFormStateProvider.select((value) => value.sendCurrency)), ref.watch(efCurrencyPairProvider.select((value) => value.send)),
), ),
SizedBox( SizedBox(
height: isDesktop ? 10 : 4, height: isDesktop ? 10 : 4,
@ -758,15 +833,10 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
SizedBox( SizedBox(
height: isDesktop ? 10 : 4, height: isDesktop ? 10 : 4,
), ),
if (ref if (ref.watch(efWarningProvider).isNotEmpty &&
.watch( !ref.watch(efReversedProvider))
exchangeFormStateProvider.select((value) => value.warning))
.isNotEmpty &&
!ref.watch(
exchangeFormStateProvider.select((value) => value.reversed)))
Text( Text(
ref.watch( ref.watch(efWarningProvider),
exchangeFormStateProvider.select((value) => value.warning)),
style: STextStyles.errorSmall(context), style: STextStyles.errorSmall(context),
), ),
Row( Row(
@ -830,8 +900,7 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
background: background:
Theme.of(context).extension<StackColors>()!.textFieldDefaultBG, Theme.of(context).extension<StackColors>()!.textFieldDefaultBG,
onTap: () { onTap: () {
if (!(ref.read(exchangeFormStateProvider).exchangeRateType == if (!(ref.read(efRateTypeProvider) == ExchangeRateType.estimated) &&
ExchangeRateType.estimated) &&
_receiveController.text == "-") { _receiveController.text == "-") {
_receiveController.text = ""; _receiveController.text = "";
} }
@ -839,22 +908,16 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
onChanged: receiveFieldOnChanged, onChanged: receiveFieldOnChanged,
onButtonTap: selectReceiveCurrency, onButtonTap: selectReceiveCurrency,
isWalletCoin: isWalletCoin(coin, true), isWalletCoin: isWalletCoin(coin, true),
currency: ref.watch(exchangeFormStateProvider currency: ref
.select((value) => value.receiveCurrency)), .watch(efCurrencyPairProvider.select((value) => value.receive)),
readOnly: (rateType) == ExchangeRateType.estimated && readOnly: (rateType) == ExchangeRateType.estimated &&
ref.watch(exchangeFormStateProvider ref.watch(efExchangeProvider).name ==
.select((value) => value.exchange.name)) ==
ChangeNowExchange.exchangeName, ChangeNowExchange.exchangeName,
), ),
if (ref if (ref.watch(efWarningProvider).isNotEmpty &&
.watch( ref.watch(efReversedProvider))
exchangeFormStateProvider.select((value) => value.warning))
.isNotEmpty &&
ref.watch(
exchangeFormStateProvider.select((value) => value.reversed)))
Text( Text(
ref.watch( ref.watch(efWarningProvider),
exchangeFormStateProvider.select((value) => value.warning)),
style: STextStyles.errorSmall(context), style: STextStyles.errorSmall(context),
), ),
SizedBox( SizedBox(
@ -867,27 +930,28 @@ class _ExchangeFormState extends ConsumerState<ExchangeForm> {
onChanged: onRateTypeChanged, onChanged: onRateTypeChanged,
), ),
), ),
// these reads should be watch // AnimatedSize(
if (ref.watch(exchangeFormStateProvider).sendAmount != null && // duration: const Duration(milliseconds: 250),
ref.watch(exchangeFormStateProvider).sendAmount != Decimal.zero) // curve: Curves.easeInOutCirc,
SizedBox( // child: ref.watch(efSendAmountProvider).sendAmount != null &&
height: isDesktop ? 20 : 12, // ref.watch(exchangeFormStateProvider).sendAmount != Decimal.zero
), // ?
// these reads should be watch Padding(
if (ref.watch(exchangeFormStateProvider).sendAmount != null && padding: EdgeInsets.only(top: isDesktop ? 20 : 12),
ref.watch(exchangeFormStateProvider).sendAmount != Decimal.zero) child: ExchangeProviderOptions(
ExchangeProviderOptions(
fixedRate: rateType == ExchangeRateType.fixed, fixedRate: rateType == ExchangeRateType.fixed,
reversed: ref.watch( reversed: ref.watch(efReversedProvider),
exchangeFormStateProvider.select((value) => value.reversed)),
), ),
// : const SizedBox(
// height: 0,
// ),
),
SizedBox( SizedBox(
height: isDesktop ? 20 : 12, height: isDesktop ? 20 : 12,
), ),
PrimaryButton( PrimaryButton(
buttonHeight: isDesktop ? ButtonHeight.l : null, buttonHeight: isDesktop ? ButtonHeight.l : null,
enabled: ref.watch( enabled: ref.watch(efCanExchangeProvider),
exchangeFormStateProvider.select((value) => value.canExchange)),
onPressed: onExchangePressed, onPressed: onExchangePressed,
label: "Swap", label: "Swap",
) )

View file

@ -124,9 +124,8 @@ class _Step2ViewState extends ConsumerState<Step2View> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final supportsRefund = ref.watch( final supportsRefund =
exchangeFormStateProvider.select((value) => value.exchange.name)) != ref.watch(efExchangeProvider).name != MajesticBankExchange.exchangeName;
MajesticBankExchange.exchangeName;
return Background( return Background(
child: Scaffold( child: Scaffold(

View file

@ -52,9 +52,8 @@ class _Step3ViewState extends ConsumerState<Step3View> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final supportsRefund = ref.watch( final supportsRefund =
exchangeFormStateProvider.select((value) => value.exchange.name)) != ref.watch(efExchangeProvider).name != MajesticBankExchange.exchangeName;
MajesticBankExchange.exchangeName;
return Background( return Background(
child: Scaffold( child: Scaffold(
@ -254,8 +253,7 @@ class _Step3ViewState extends ConsumerState<Step3View> {
final ExchangeResponse<Trade> response = final ExchangeResponse<Trade> response =
await ref await ref
.read(exchangeFormStateProvider) .read(efExchangeProvider)
.exchange
.createTrade( .createTrade(
from: model.sendTicker, from: model.sendTicker,
to: model.receiveTicker, to: model.receiveTicker,

View file

@ -70,10 +70,8 @@ class _Step4ViewState extends ConsumerState<Step4View> {
} }
Future<void> _updateStatus() async { Future<void> _updateStatus() async {
final statusResponse = await ref final statusResponse =
.read(exchangeFormStateProvider) await ref.read(efExchangeProvider).updateTrade(model.trade!);
.exchange
.updateTrade(model.trade!);
String status = "Waiting"; String status = "Waiting";
if (statusResponse.value != null) { if (statusResponse.value != null) {
status = statusResponse.value!.status; status = statusResponse.value!.status;

View file

@ -37,7 +37,8 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty( await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider), ref.read(efCurrencyPairProvider),
ref.read(efRateTypeProvider),
); );
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;
@ -53,7 +54,8 @@ class _ExchangeViewState extends ConsumerState<ExchangeView> {
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty( await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider), ref.read(efCurrencyPairProvider),
ref.read(efRateTypeProvider),
); );
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;

View file

@ -2,12 +2,10 @@ import 'package:decimal/decimal.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/exceptions/exchange/pair_unavailable_exception.dart';
import 'package:stackwallet/models/exchange/response_objects/estimate.dart'; import 'package:stackwallet/models/exchange/response_objects/estimate.dart';
import 'package:stackwallet/providers/exchange/exchange_form_state_provider.dart'; import 'package:stackwallet/providers/exchange/exchange_form_state_provider.dart';
import 'package:stackwallet/providers/global/locale_provider.dart'; import 'package:stackwallet/providers/global/locale_provider.dart';
import 'package:stackwallet/services/exchange/exchange.dart'; import 'package:stackwallet/services/exchange/exchange.dart';
import 'package:stackwallet/services/exchange/exchange_response.dart';
import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/utilities/amount/amount.dart';
import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart';
@ -42,138 +40,121 @@ class _ExchangeMultiProviderOptionState extends ConsumerState<ExchangeOption> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final sendCurrency = ref final sendCurrency =
.watch(exchangeFormStateProvider.select((value) => value.sendCurrency)); ref.watch(efCurrencyPairProvider.select((value) => value.send));
final receivingCurrency = ref.watch( final receivingCurrency =
exchangeFormStateProvider.select((value) => value.receiveCurrency)); ref.watch(efCurrencyPairProvider.select((value) => value.receive));
final fromAmount = ref final reversed = ref.watch(efReversedProvider);
.watch(exchangeFormStateProvider.select((value) => value.sendAmount)); final amount = reversed
final toAmount = ref.watch( ? ref.watch(efReceiveAmountProvider)
exchangeFormStateProvider.select((value) => value.receiveAmount)); : ref.watch(efSendAmountProvider);
final estimates = ref.watch(efEstimatesListProvider(widget.exchange.name));
return AnimatedSize( return AnimatedSize(
duration: const Duration(milliseconds: 500), duration: const Duration(milliseconds: 500),
curve: Curves.easeInOutCubicEmphasized, curve: Curves.easeInOutCubicEmphasized,
child: Column( child: Builder(
mainAxisSize: MainAxisSize.min, builder: (_) {
children: [ if (ref.watch(efRefreshingProvider)) {
if (sendCurrency != null && // show loading
return _ProviderOption(
exchange: widget.exchange,
estimate: null,
rateString: "",
loadingString: true,
);
} else if (sendCurrency != null &&
receivingCurrency != null && receivingCurrency != null &&
toAmount != null && amount != null &&
toAmount > Decimal.zero && amount > Decimal.zero) {
fromAmount != null && if (estimates != null && estimates.item1.isNotEmpty) {
fromAmount > Decimal.zero) return Column(
FutureBuilder( mainAxisSize: MainAxisSize.min,
future: widget.exchange.getEstimates( children: [
sendCurrency.ticker, for (int i = 0; i < estimates.item1.length; i++)
receivingCurrency.ticker, Builder(
widget.reversed ? toAmount : fromAmount, builder: (context) {
widget.fixedRate, final e = estimates.item1[i];
widget.reversed,
),
builder: (context,
AsyncSnapshot<ExchangeResponse<List<Estimate>>> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
final estimates = snapshot.data?.value;
if (estimates != null && estimates.isNotEmpty) { int decimals;
return Column( try {
mainAxisSize: MainAxisSize.min, decimals = coinFromTickerCaseInsensitive(
children: [ receivingCurrency.ticker)
for (int i = 0; i < estimates.length; i++) .decimals;
Builder( } catch (_) {
builder: (context) { decimals = 8; // some reasonable alternative
final e = estimates[i]; }
int decimals; Amount rate;
try { if (e.reversed) {
decimals = coinFromTickerCaseInsensitive( rate = (amount / e.estimatedAmount)
receivingCurrency.ticker) .toDecimal(scaleOnInfinitePrecision: 18)
.decimals; .toAmount(fractionDigits: decimals);
} catch (_) { } else {
decimals = 8; // some reasonable alternative rate = (e.estimatedAmount / amount)
} .toDecimal(scaleOnInfinitePrecision: 18)
Amount rate; .toAmount(fractionDigits: decimals);
if (e.reversed) { }
rate = (toAmount / e.estimatedAmount)
.toDecimal(scaleOnInfinitePrecision: 18)
.toAmount(fractionDigits: decimals);
} else {
rate = (e.estimatedAmount / fromAmount)
.toDecimal(scaleOnInfinitePrecision: 18)
.toAmount(fractionDigits: decimals);
}
final rateString = final rateString =
"1 ${sendCurrency.ticker.toUpperCase()} ~ ${rate.localizedStringAsFixed( "1 ${sendCurrency.ticker.toUpperCase()} ~ ${rate.localizedStringAsFixed(
locale: ref.watch( locale: ref.watch(
localeServiceChangeNotifierProvider localeServiceChangeNotifierProvider
.select((value) => value.locale), .select((value) => value.locale),
),
)} ${receivingCurrency.ticker.toUpperCase()}";
return ConditionalParent(
condition: i > 0,
builder: (child) => Column(
mainAxisSize: MainAxisSize.min,
children: [
isDesktop
? Container(
height: 1,
color: Theme.of(context)
.extension<StackColors>()!
.background,
)
: const SizedBox(
height: 16,
),
child,
],
),
child: _ProviderOption(
key: Key(widget.exchange.name +
e.exchangeProvider),
exchange: widget.exchange,
providerName: e.exchangeProvider,
rateString: rateString,
kycRating: e.kycRating,
),
);
},
), ),
], )} ${receivingCurrency.ticker.toUpperCase()}";
);
} else if (snapshot.data?.exception
is PairUnavailableException) {
return _ProviderOption(
exchange: widget.exchange,
providerName: widget.exchange.name,
rateString: "Unsupported pair",
);
} else {
Logging.instance.log(
"$runtimeType failed to fetch rate for ${widget.exchange.name}: ${snapshot.data}",
level: LogLevel.Warning,
);
return _ProviderOption( return ConditionalParent(
exchange: widget.exchange, condition: i > 0,
providerName: widget.exchange.name, builder: (child) => Column(
rateString: "Failed to fetch rate", mainAxisSize: MainAxisSize.min,
); children: [
} isDesktop
} else { ? Container(
// show loading height: 1,
return _ProviderOption( color: Theme.of(context)
exchange: widget.exchange, .extension<StackColors>()!
providerName: widget.exchange.name, .background,
rateString: "", )
loadingString: true, : const SizedBox(
); height: 16,
} ),
}, child,
), ],
], ),
child: _ProviderOption(
key: Key(widget.exchange.name + e.exchangeProvider),
exchange: widget.exchange,
estimate: e,
rateString: rateString,
kycRating: e.kycRating,
),
);
},
),
],
);
} else {
Logging.instance.log(
"$runtimeType failed to fetch rate for ${widget.exchange.name}: $estimates",
level: LogLevel.Warning,
);
return _ProviderOption(
exchange: widget.exchange,
estimate: null,
rateString: "Failed to fetch rate",
);
}
} else {
// show n/a
return _ProviderOption(
exchange: widget.exchange,
estimate: null,
rateString: "n/a",
);
}
},
), ),
); );
} }
@ -183,14 +164,14 @@ class _ProviderOption extends ConsumerStatefulWidget {
const _ProviderOption({ const _ProviderOption({
Key? key, Key? key,
required this.exchange, required this.exchange,
required this.providerName, required this.estimate,
required this.rateString, required this.rateString,
this.kycRating, this.kycRating,
this.loadingString = false, this.loadingString = false,
}) : super(key: key); }) : super(key: key);
final Exchange exchange; final Exchange exchange;
final String providerName; final Estimate? estimate;
final String rateString; final String rateString;
final String? kycRating; final String? kycRating;
final bool loadingString; final bool loadingString;
@ -206,25 +187,27 @@ class _ProviderOptionState extends ConsumerState<_ProviderOption> {
@override @override
void initState() { void initState() {
_id = "${widget.exchange.name} (${widget.providerName})"; _id =
"${widget.exchange.name} (${widget.estimate?.exchangeProvider ?? widget.exchange.name})";
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool selected = ref.watch(exchangeFormStateProvider String groupValue = ref.watch(currentCombinedExchangeIdProvider);
.select((value) => value.combinedExchangeId)) ==
_id;
String groupValue = ref.watch(
exchangeFormStateProvider.select((value) => value.combinedExchangeId));
if (ref.watch( if (ref.watch(efExchangeProvider).name ==
exchangeFormStateProvider.select((value) => value.exchange.name)) == (widget.estimate?.exchangeProvider ?? widget.exchange.name)) {
widget.providerName) {
selected = true;
groupValue = _id; groupValue = _id;
} }
bool selected = groupValue == _id;
print("========================================================");
print("gourpValue: $groupValue");
print("_id: $_id");
print("========================================================");
return ConditionalParent( return ConditionalParent(
condition: isDesktop, condition: isDesktop,
builder: (child) => MouseRegion( builder: (child) => MouseRegion(
@ -233,15 +216,20 @@ class _ProviderOptionState extends ConsumerState<_ProviderOption> {
), ),
child: GestureDetector( child: GestureDetector(
onTap: () { onTap: () {
if (!selected) { ref.read(efExchangeProvider.notifier).state = widget.exchange;
ref.read(exchangeFormStateProvider).updateExchange( ref.read(efExchangeProviderNameProvider.notifier).state =
exchange: widget.exchange, widget.estimate?.exchangeProvider ?? widget.exchange.name;
shouldUpdateData: true,
shouldNotifyListeners: true, // if (!selected) {
providerName: widget.providerName, // ref.read(exchangeFormStateProvider).updateExchange(
shouldAwait: false, // exchange: widget.exchange,
); // shouldUpdateData: false,
} // shouldNotifyListeners: false,
// providerName:
// widget.estimate?.exchangeProvider ?? widget.exchange.name,
// shouldAwait: false,
// );
// }
}, },
child: Container( child: Container(
color: Colors.transparent, color: Colors.transparent,
@ -263,15 +251,26 @@ class _ProviderOptionState extends ConsumerState<_ProviderOption> {
value: _id, value: _id,
groupValue: groupValue, groupValue: groupValue,
onChanged: (_) { onChanged: (_) {
if (!selected) { ref.read(efExchangeProvider.notifier).state =
ref.read(exchangeFormStateProvider).updateExchange( widget.exchange;
exchange: widget.exchange, ref
shouldUpdateData: false, .read(efExchangeProviderNameProvider.notifier)
shouldNotifyListeners: true, .state =
providerName: widget.providerName, widget.estimate?.exchangeProvider ??
shouldAwait: false, widget.exchange.name;
); // if (!selected) {
} //
//
// ref.read(exchangeFormStateProvider).updateExchange(
// exchange: widget.exchange,
// shouldUpdateData: false,
// shouldNotifyListeners: false,
// providerName:
// widget.estimate?.exchangeProvider ??
// widget.exchange.name,
// shouldAwait: false,
// );
// }
}, },
), ),
), ),
@ -303,7 +302,8 @@ class _ProviderOptionState extends ConsumerState<_ProviderOption> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
widget.providerName, widget.estimate?.exchangeProvider ??
widget.exchange.name,
style: STextStyles.titleBold12(context).copyWith( style: STextStyles.titleBold12(context).copyWith(
color: Theme.of(context) color: Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!

View file

@ -49,16 +49,10 @@ class _ExchangeProviderOptionsState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final sendCurrency = ref.watch( final sendCurrency =
exchangeFormStateProvider.select( ref.watch(efCurrencyPairProvider.select((value) => value.send));
(value) => value.sendCurrency, final receivingCurrency =
), ref.watch(efCurrencyPairProvider.select((value) => value.receive));
);
final receivingCurrency = ref.watch(
exchangeFormStateProvider.select(
(value) => value.receiveCurrency,
),
);
final showChangeNow = exchangeSupported( final showChangeNow = exchangeSupported(
exchangeName: ChangeNowExchange.exchangeName, exchangeName: ChangeNowExchange.exchangeName,

View file

@ -29,9 +29,7 @@ class RateTypeToggle extends ConsumerWidget {
onChanged?.call(ExchangeRateType.estimated); onChanged?.call(ExchangeRateType.estimated);
} }
}, },
isOn: ref.watch(exchangeFormStateProvider isOn: ref.watch(efRateTypeProvider) == ExchangeRateType.fixed,
.select((value) => value.exchangeRateType)) ==
ExchangeRateType.fixed,
onColor: isDesktop onColor: isDesktop
? Theme.of(context) ? Theme.of(context)
.extension<StackColors>()! .extension<StackColors>()!

View file

@ -54,7 +54,8 @@ class _WalletInitiatedExchangeViewState
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty( await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider), ref.read(efCurrencyPairProvider),
ref.read(efRateTypeProvider),
); );
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;
@ -70,7 +71,8 @@ class _WalletInitiatedExchangeViewState
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty( await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider), ref.read(efCurrencyPairProvider),
ref.read(efRateTypeProvider),
); );
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;

View file

@ -1,6 +1,7 @@
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/pages/exchange_view/exchange_form.dart'; import 'package:stackwallet/pages/exchange_view/exchange_form.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_all_trades_view.dart';
import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_trade_history.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_trade_history.dart';
import 'package:stackwallet/providers/exchange/exchange_form_state_provider.dart'; import 'package:stackwallet/providers/exchange/exchange_form_state_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart';
@ -14,8 +15,6 @@ import 'package:stackwallet/widgets/desktop/desktop_app_bar.dart';
import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart'; import 'package:stackwallet/widgets/desktop/desktop_scaffold.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'desktop_all_trades_view.dart';
class DesktopExchangeView extends ConsumerStatefulWidget { class DesktopExchangeView extends ConsumerStatefulWidget {
const DesktopExchangeView({Key? key}) : super(key: key); const DesktopExchangeView({Key? key}) : super(key: key);
@ -38,7 +37,8 @@ class _DesktopExchangeViewState extends ConsumerState<DesktopExchangeView> {
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty( await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider), ref.read(efCurrencyPairProvider),
ref.read(efRateTypeProvider),
); );
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;
@ -54,7 +54,8 @@ class _DesktopExchangeViewState extends ConsumerState<DesktopExchangeView> {
ExchangeDataLoadingService.instance.onLoadingComplete = () { ExchangeDataLoadingService.instance.onLoadingComplete = () {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty( await ExchangeDataLoadingService.instance.setCurrenciesIfEmpty(
ref.read(exchangeFormStateProvider), ref.read(efCurrencyPairProvider),
ref.read(efRateTypeProvider),
); );
setState(() { setState(() {
_initialCachePopulationUnderway = false; _initialCachePopulationUnderway = false;

View file

@ -84,8 +84,7 @@ class _StepScaffoldState extends ConsumerState<StepScaffold> {
); );
final ExchangeResponse<Trade> response = await ref final ExchangeResponse<Trade> response = await ref
.read(exchangeFormStateProvider) .read(efExchangeProvider)
.exchange
.createTrade( .createTrade(
from: ref.read(desktopExchangeModelProvider)!.sendTicker, from: ref.read(desktopExchangeModelProvider)!.sendTicker,
to: ref.read(desktopExchangeModelProvider)!.receiveTicker, to: ref.read(desktopExchangeModelProvider)!.receiveTicker,

View file

@ -38,8 +38,7 @@ class DesktopStep1 extends ConsumerWidget {
children: [ children: [
DesktopStepItem( DesktopStepItem(
label: "Swap", label: "Swap",
value: ref.watch(exchangeFormStateProvider value: ref.watch(efExchangeProviderNameProvider),
.select((value) => value.exchange.name)),
), ),
Container( Container(
height: 1, height: 1,

View file

@ -36,8 +36,7 @@ class _DesktopStep3State extends ConsumerState<DesktopStep3> {
children: [ children: [
DesktopStepItem( DesktopStepItem(
label: "Swap", label: "Swap",
value: ref.watch(exchangeFormStateProvider value: ref.watch(efExchangeProviderNameProvider),
.select((value) => value.exchange.name)),
), ),
Container( Container(
height: 1, height: 1,

View file

@ -46,7 +46,7 @@ class _DesktopStep4State extends ConsumerState<DesktopStep4> {
} }
final statusResponse = final statusResponse =
await ref.read(exchangeFormStateProvider).exchange.updateTrade(trade); await ref.read(efExchangeProvider).updateTrade(trade);
String status = "Waiting"; String status = "Waiting";
if (statusResponse.value != null) { if (statusResponse.value != null) {
status = statusResponse.value!.status; status = statusResponse.value!.status;

View file

@ -1,5 +1,116 @@
import 'package:decimal/decimal.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/models/exchange/active_pair.dart';
import 'package:stackwallet/models/exchange/response_objects/estimate.dart';
import 'package:stackwallet/models/exchange/response_objects/range.dart';
import 'package:stackwallet/services/exchange/exchange.dart';
import 'package:stackwallet/utilities/enums/exchange_rate_type_enum.dart';
import 'package:tuple/tuple.dart';
final exchangeFormStateProvider = final efEstimatesListProvider =
ChangeNotifierProvider<ExchangeFormState>((ref) => ExchangeFormState()); StateProvider.family<Tuple2<List<Estimate>, Range>?, String>(
(ref, exchangeName) => null);
final efRateTypeProvider =
StateProvider<ExchangeRateType>((ref) => ExchangeRateType.estimated);
final efExchangeProvider =
StateProvider<Exchange>((ref) => Exchange.defaultExchange);
final efExchangeProviderNameProvider =
StateProvider<String>((ref) => Exchange.defaultExchange.name);
final currentCombinedExchangeIdProvider = Provider<String>((ref) {
return "${ref.watch(efExchangeProvider).name}"
" (${ref.watch(efExchangeProviderNameProvider)})";
});
final efSendAmountProvider = StateProvider<Decimal?>((ref) => null);
final efReceiveAmountProvider = StateProvider<Decimal?>((ref) => null);
final efSendAmountStringProvider = StateProvider<String>((ref) {
return ref.watch(efSendAmountProvider)?.toStringAsFixed(8) ?? "";
});
final efReceiveAmountStringProvider = StateProvider<String>((ref) {
return ref.watch(efReceiveAmountProvider)?.toStringAsFixed(8) ?? "";
});
final efReversedProvider = StateProvider<bool>((ref) => false);
final efCurrencyPairProvider = ChangeNotifierProvider<ActivePair>(
(ref) => ActivePair(),
);
final efRangeProvider = StateProvider<Range?>((ref) {
final exchange = ref.watch(efExchangeProvider);
return ref.watch(efEstimatesListProvider(exchange.name))?.item2;
});
final efEstimateProvider = StateProvider<Estimate?>((ref) {
final exchange = ref.watch(efExchangeProvider);
final provider = ref.watch(efExchangeProviderNameProvider);
final reversed = ref.watch(efReversedProvider);
final fixedRate = ref.watch(efRateTypeProvider) == ExchangeRateType.fixed;
final matches =
ref.watch(efEstimatesListProvider(exchange.name))?.item1.where((e) {
return e.exchangeProvider == provider &&
e.fixedRate == fixedRate &&
e.reversed == reversed;
});
Estimate? result;
if (matches != null && matches.isNotEmpty) {
result = matches.first;
} else {
result = null;
}
return result;
});
final efCanExchangeProvider = StateProvider<bool>((ref) {
final Estimate? estimate = ref.watch(efEstimateProvider);
// final Decimal? amount = ref.watch(efReversedProvider)
// ? ref.watch(efSendAmountProvider)
// : ref.watch(efReceiveAmountProvider);
return estimate != null;
});
final efRefreshingProvider = StateProvider<bool>((ref) => false);
final efWarningProvider = StateProvider((ref) {
// if (ref.watch(efReversedProvider)) {
// final _receiveCurrency =
// ref.watch(efCurrencyPairProvider.select((value) => value.receive));
// final _receiveAmount = ref.watch(efReceiveAmountProvider);
// if (_receiveCurrency != null && _receiveAmount != null) {
// final range = ref.watch(efRangeProvider);
// if (range?.min != null &&
// _receiveAmount < range!.min! &&
// _receiveAmount > Decimal.zero) {
// return "Min receive amount ${range.min!.toString()} ${_receiveCurrency.ticker.toUpperCase()}";
// } else if (range?.max != null &&
// _receiveAmount > ref.watch(efRangeProvider)!.max!) {
// return "Max receive amount $range!.max!.toString()} ${_receiveCurrency.ticker.toUpperCase()}";
// }
// }
// } else {
// final _sendCurrency =
// ref.watch(efCurrencyPairProvider.select((value) => value.send));
// final _sendAmount = ref.watch(efSendAmountProvider);
// if (_sendCurrency != null && _sendAmount != null) {
// final range = ref.watch(efRangeProvider);
// if (range?.min != null &&
// _sendAmount < range!.min! &&
// _sendAmount > Decimal.zero) {
// return "Min send amount ${range.min!.toString()} ${_sendCurrency.ticker.toUpperCase()}";
// } else if (range?.max != null && _sendAmount > range!.max!) {
// return "Max send amount ${range.max!.toString()} ${_sendCurrency.ticker.toUpperCase()}";
// }
// }
// }
return "";
});

View file

@ -1,8 +1,8 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:stackwallet/db/hive/db.dart'; import 'package:stackwallet/db/hive/db.dart';
import 'package:stackwallet/models/exchange/active_pair.dart';
import 'package:stackwallet/models/exchange/aggregate_currency.dart'; import 'package:stackwallet/models/exchange/aggregate_currency.dart';
import 'package:stackwallet/models/exchange/exchange_form_state.dart';
import 'package:stackwallet/models/isar/exchange_cache/currency.dart'; import 'package:stackwallet/models/isar/exchange_cache/currency.dart';
import 'package:stackwallet/models/isar/exchange_cache/pair.dart'; import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart'; import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
@ -57,20 +57,29 @@ class ExchangeDataLoadingService {
); );
} }
Future<void> setCurrenciesIfEmpty(ExchangeFormState state) async { Future<void> setCurrenciesIfEmpty(
if (state.sendCurrency == null && state.receiveCurrency == null) { ActivePair? pair,
ExchangeRateType rateType,
) async {
if (pair?.send == null && pair?.receive == null) {
if (await isar.currencies.count() > 0) { if (await isar.currencies.count() > 0) {
final sendCurrency = await getAggregateCurrency( pair?.setSend(
"BTC", await getAggregateCurrency(
state.exchangeRateType, "BTC",
null, rateType,
null,
),
notifyListeners: false,
); );
final receiveCurrency = await getAggregateCurrency(
"XMR", pair?.setReceive(
state.exchangeRateType, await getAggregateCurrency(
null, "XMR",
rateType,
null,
),
notifyListeners: false,
); );
state.setCurrencies(sendCurrency, receiveCurrency);
} }
} }
} }