mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-17 01:37:54 +00:00
Merge branch 'exchange_refactor' into paynyms
# Conflicts: # test/pages/send_view/send_view_test.mocks.dart # test/widget_tests/managed_favorite_test.mocks.dart # test/widget_tests/table_view/table_view_row_test.mocks.dart # test/widget_tests/wallet_card_test.mocks.dart # test/widget_tests/wallet_info_row/sub_widgets/wallet_info_row_balance_future_test.mocks.dart # test/widget_tests/wallet_info_row/wallet_info_row_test.mocks.dart
This commit is contained in:
commit
d4db845494
56 changed files with 9190 additions and 6229 deletions
1
assets/svg/exchange_icons/mb_blue.svg
Normal file
1
assets/svg/exchange_icons/mb_blue.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 8.5 KiB |
1
assets/svg/exchange_icons/mb_green.svg
Normal file
1
assets/svg/exchange_icons/mb_green.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 8.5 KiB |
|
@ -1,4 +1,3 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/exceptions/main_db/main_db_exception.dart';
|
||||
import 'package:stackwallet/exceptions/sw_exception.dart';
|
||||
|
@ -30,7 +29,8 @@ class MainDB {
|
|||
AddressLabelSchema,
|
||||
],
|
||||
directory: (await StackFileSystem.applicationIsarDirectory()).path,
|
||||
inspector: kDebugMode,
|
||||
// inspector: kDebugMode,
|
||||
inspector: false,
|
||||
name: "wallet_data",
|
||||
);
|
||||
return true;
|
||||
|
|
5
lib/exceptions/exchange/pair_unavailable_exception.dart
Normal file
5
lib/exceptions/exchange/pair_unavailable_exception.dart
Normal file
|
@ -0,0 +1,5 @@
|
|||
import 'package:stackwallet/exceptions/exchange/exchange_exception.dart';
|
||||
|
||||
class PairUnavailableException extends ExchangeException {
|
||||
PairUnavailableException(super.message, super.type);
|
||||
}
|
|
@ -43,7 +43,6 @@ import 'package:stackwallet/providers/ui/color_theme_provider.dart';
|
|||
import 'package:stackwallet/route_generator.dart';
|
||||
// import 'package:stackwallet/services/buy/buy_data_loading_service.dart';
|
||||
import 'package:stackwallet/services/debug_service.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/locale_service.dart';
|
||||
import 'package:stackwallet/services/node_service.dart';
|
||||
|
@ -291,11 +290,16 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
|
|||
// TODO: this should probably run unawaited. Keep commented out for now as proper community nodes ui hasn't been implemented yet
|
||||
// unawaited(_nodeService.updateCommunityNodes());
|
||||
|
||||
print("================================================");
|
||||
print("${ref.read(prefsChangeNotifierProvider).externalCalls}");
|
||||
print("${await ref.read(prefsChangeNotifierProvider).isExternalCallsSet()}");
|
||||
print("================================================");
|
||||
// run without awaiting
|
||||
if (ref.read(prefsChangeNotifierProvider).externalCalls &&
|
||||
await ref.read(prefsChangeNotifierProvider).isExternalCallsSet()) {
|
||||
if (Constants.enableExchange) {
|
||||
unawaited(ExchangeDataLoadingService().loadAll(ref));
|
||||
await ExchangeDataLoadingService.instance.init();
|
||||
unawaited(ExchangeDataLoadingService.instance.loadAll());
|
||||
}
|
||||
// if (Constants.enableBuy) {
|
||||
// unawaited(BuyDataLoadingService().loadAll(ref));
|
||||
|
@ -328,7 +332,6 @@ 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?;
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
import 'package:stackwallet/models/exchange/response_objects/currency.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/pair.dart';
|
||||
|
||||
class CNAvailableCurrencies {
|
||||
final List<Currency> currencies = [];
|
||||
final List<Pair> pairs = [];
|
||||
final List<FixedRateMarket> markets = [];
|
||||
|
||||
void updateCurrencies(List<Currency> newCurrencies) {
|
||||
currencies.clear();
|
||||
currencies.addAll(newCurrencies);
|
||||
}
|
||||
|
||||
void updateFloatingPairs(List<Pair> newPairs) {
|
||||
pairs.clear();
|
||||
pairs.addAll(newPairs);
|
||||
}
|
||||
|
||||
void updateMarkets(List<FixedRateMarket> newMarkets) {
|
||||
markets.clear();
|
||||
markets.addAll(newMarkets);
|
||||
}
|
||||
}
|
|
@ -1,113 +1,143 @@
|
|||
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:flutter/foundation.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/estimate.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/currency.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
|
||||
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange.dart';
|
||||
// import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.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/utilities/logger.dart';
|
||||
|
||||
class ExchangeFormState extends ChangeNotifier {
|
||||
Exchange? _exchange;
|
||||
Exchange? get exchange => _exchange;
|
||||
set exchange(Exchange? value) {
|
||||
_exchange = value;
|
||||
_onExchangeTypeChanged();
|
||||
Exchange get exchange => _exchange ??= Exchange.defaultExchange;
|
||||
|
||||
ExchangeRateType _exchangeRateType = ExchangeRateType.estimated;
|
||||
ExchangeRateType get exchangeRateType => _exchangeRateType;
|
||||
set exchangeRateType(ExchangeRateType exchangeRateType) {
|
||||
_exchangeRateType = exchangeRateType;
|
||||
//
|
||||
}
|
||||
|
||||
ExchangeRateType _exchangeType = ExchangeRateType.estimated;
|
||||
ExchangeRateType get exchangeType => _exchangeType;
|
||||
set exchangeType(ExchangeRateType value) {
|
||||
_exchangeType = value;
|
||||
_onExchangeRateTypeChanged();
|
||||
Estimate? _estimate;
|
||||
Estimate? get estimate => _estimate;
|
||||
|
||||
bool _reversed = false;
|
||||
bool get reversed => _reversed;
|
||||
set reversed(bool reversed) {
|
||||
_reversed = reversed;
|
||||
//
|
||||
}
|
||||
|
||||
bool reversed = false;
|
||||
Decimal? _rate;
|
||||
Decimal? get rate => _rate;
|
||||
// set rate(Decimal? rate) {
|
||||
// _rate = rate;
|
||||
// //
|
||||
// }
|
||||
|
||||
Decimal? fromAmount;
|
||||
Decimal? toAmount;
|
||||
Decimal? _sendAmount;
|
||||
Decimal? get sendAmount => _sendAmount;
|
||||
// set sendAmount(Decimal? sendAmount) {
|
||||
// _sendAmount = sendAmount;
|
||||
// //
|
||||
// }
|
||||
|
||||
Decimal? minAmount;
|
||||
Decimal? maxAmount;
|
||||
|
||||
Decimal? rate;
|
||||
Estimate? estimate;
|
||||
|
||||
FixedRateMarket? _market;
|
||||
FixedRateMarket? get market => _market;
|
||||
|
||||
Currency? _from;
|
||||
Currency? _to;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ExchangeFormState: {_exchange: $_exchange, _exchangeType: $_exchangeType, reversed: $reversed, fromAmount: $fromAmount, toAmount: $toAmount, minAmount: $minAmount, maxAmount: $maxAmount, rate: $rate, estimate: $estimate, _market: $_market, _from: $_from, _to: $_to, _onError: $_onError}';
|
||||
Decimal? _receiveAmount;
|
||||
Decimal? get receiveAmount => _receiveAmount;
|
||||
set receiveAmount(Decimal? receiveAmount) {
|
||||
_receiveAmount = receiveAmount;
|
||||
//
|
||||
}
|
||||
|
||||
String? get fromTicker {
|
||||
switch (exchangeType) {
|
||||
case ExchangeRateType.estimated:
|
||||
return _from?.ticker;
|
||||
case ExchangeRateType.fixed:
|
||||
switch (exchange?.name) {
|
||||
// case SimpleSwapExchange.exchangeName:
|
||||
// return _from?.ticker;
|
||||
case ChangeNowExchange.exchangeName:
|
||||
return market?.from;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Currency? _sendCurrency;
|
||||
Currency? get sendCurrency => _sendCurrency;
|
||||
// set sendCurrency(Currency? sendCurrency) {
|
||||
// _sendCurrency = sendCurrency;
|
||||
// //
|
||||
// }
|
||||
|
||||
String? get toTicker {
|
||||
switch (exchangeType) {
|
||||
case ExchangeRateType.estimated:
|
||||
return _to?.ticker;
|
||||
case ExchangeRateType.fixed:
|
||||
switch (exchange?.name) {
|
||||
// case SimpleSwapExchange.exchangeName:
|
||||
// return _to?.ticker;
|
||||
case ChangeNowExchange.exchangeName:
|
||||
return market?.to;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Currency? _receiveCurrency;
|
||||
Currency? get receiveCurrency => _receiveCurrency;
|
||||
// set receiveCurrency(Currency? receiveCurrency) {
|
||||
// _receiveCurrency = receiveCurrency;
|
||||
// //
|
||||
// }
|
||||
|
||||
void Function(String)? _onError;
|
||||
Decimal? _minSendAmount;
|
||||
Decimal? get minSendAmount => _minSendAmount;
|
||||
// set minSendAmount(Decimal? minSendAmount) {
|
||||
// _minSendAmount = minSendAmount;
|
||||
// //
|
||||
// }
|
||||
|
||||
Currency? get from => _from;
|
||||
Currency? get to => _to;
|
||||
Decimal? _minReceiveAmount;
|
||||
Decimal? get minReceiveAmount => _minReceiveAmount;
|
||||
// set minReceiveAmount(Decimal? minReceiveAmount) {
|
||||
// _minReceiveAmount = minReceiveAmount;
|
||||
// //
|
||||
// }
|
||||
|
||||
void setCurrencies(Currency from, Currency to) {
|
||||
_from = from;
|
||||
_to = to;
|
||||
Decimal? _maxSendAmount;
|
||||
Decimal? get maxSendAmount => _maxSendAmount;
|
||||
// set maxSendAmount(Decimal? maxSendAmount) {
|
||||
// _maxSendAmount = maxSendAmount;
|
||||
// //
|
||||
// }
|
||||
|
||||
Decimal? _maxReceiveAmount;
|
||||
Decimal? get maxReceiveAmount => _maxReceiveAmount;
|
||||
// set maxReceiveAmount(Decimal? maxReceiveAmount) {
|
||||
// _maxReceiveAmount = maxReceiveAmount;
|
||||
// //
|
||||
// }
|
||||
|
||||
//============================================================================
|
||||
// computed properties
|
||||
//============================================================================
|
||||
|
||||
String? get fromTicker => _sendCurrency?.ticker;
|
||||
String? get toTicker => _receiveCurrency?.ticker;
|
||||
|
||||
String get fromAmountString => _sendAmount?.toStringAsFixed(8) ?? "";
|
||||
String get toAmountString => _receiveAmount?.toStringAsFixed(8) ?? "";
|
||||
|
||||
bool get canExchange {
|
||||
return sendCurrency != null &&
|
||||
receiveCurrency != null &&
|
||||
sendAmount != null &&
|
||||
sendAmount! >= Decimal.zero &&
|
||||
receiveAmount != null &&
|
||||
rate != null &&
|
||||
rate! >= Decimal.zero &&
|
||||
exchange.name == sendCurrency!.exchangeName &&
|
||||
exchange.name == receiveCurrency!.exchangeName &&
|
||||
warning.isEmpty;
|
||||
}
|
||||
|
||||
String get warning {
|
||||
if (reversed) {
|
||||
if (toTicker != null && toAmount != null) {
|
||||
if (minAmount != null &&
|
||||
toAmount! < minAmount! &&
|
||||
toAmount! > Decimal.zero) {
|
||||
return "Minimum amount ${minAmount!.toString()} ${toTicker!.toUpperCase()}";
|
||||
} else if (maxAmount != null && toAmount! > maxAmount!) {
|
||||
return "Maximum amount ${maxAmount!.toString()} ${toTicker!.toUpperCase()}";
|
||||
if (_receiveCurrency != null && _receiveAmount != null) {
|
||||
if (_minReceiveAmount != null &&
|
||||
_receiveAmount! < _minReceiveAmount! &&
|
||||
_receiveAmount! > Decimal.zero) {
|
||||
return "Min receive amount ${_minReceiveAmount!.toString()} ${_receiveCurrency!.ticker.toUpperCase()}";
|
||||
} else if (_maxReceiveAmount != null &&
|
||||
_receiveAmount! > _maxReceiveAmount!) {
|
||||
return "Max receive amount ${_maxReceiveAmount!.toString()} ${_receiveCurrency!.ticker.toUpperCase()}";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (fromTicker != null && fromAmount != null) {
|
||||
if (minAmount != null &&
|
||||
fromAmount! < minAmount! &&
|
||||
fromAmount! > Decimal.zero) {
|
||||
return "Minimum amount ${minAmount!.toString()} ${fromTicker!.toUpperCase()}";
|
||||
} else if (maxAmount != null && fromAmount! > maxAmount!) {
|
||||
return "Maximum amount ${maxAmount!.toString()} ${fromTicker!.toUpperCase()}";
|
||||
if (_sendCurrency != null && _sendAmount != null) {
|
||||
if (_minSendAmount != null &&
|
||||
_sendAmount! < _minSendAmount! &&
|
||||
_sendAmount! > Decimal.zero) {
|
||||
return "Min send amount ${_minSendAmount!.toString()} ${_sendCurrency!.ticker.toUpperCase()}";
|
||||
} else if (_maxSendAmount != null && _sendAmount! > _maxSendAmount!) {
|
||||
return "Max send amount ${_maxSendAmount!.toString()} ${_sendCurrency!.ticker.toUpperCase()}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -115,254 +145,374 @@ class ExchangeFormState extends ChangeNotifier {
|
|||
return "";
|
||||
}
|
||||
|
||||
String get fromAmountString => fromAmount?.toStringAsFixed(8) ?? "";
|
||||
String get toAmountString => toAmount?.toStringAsFixed(8) ?? "";
|
||||
//============================================================================
|
||||
// public state updaters
|
||||
//============================================================================
|
||||
|
||||
bool get canExchange {
|
||||
if (exchange?.name == ChangeNowExchange.exchangeName &&
|
||||
exchangeType == ExchangeRateType.fixed) {
|
||||
return _market != null &&
|
||||
fromAmount != null &&
|
||||
toAmount != null &&
|
||||
warning.isEmpty;
|
||||
} else {
|
||||
return fromAmount != null &&
|
||||
fromAmount != Decimal.zero &&
|
||||
toAmount != null &&
|
||||
rate != 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;
|
||||
Future<void> updateExchange({
|
||||
required Exchange exchange,
|
||||
required bool shouldUpdateData,
|
||||
required bool shouldNotifyListeners,
|
||||
}) async {
|
||||
_exchange = exchange;
|
||||
if (shouldUpdateData) {
|
||||
if (_sendCurrency != null) {
|
||||
_sendCurrency = await ExchangeDataLoadingService
|
||||
.instance.isar.currencies
|
||||
.where()
|
||||
.exchangeNameEqualTo(exchange.name)
|
||||
.filter()
|
||||
.tickerEqualTo(_sendCurrency!.ticker)
|
||||
.and()
|
||||
.group((q) => exchangeRateType == ExchangeRateType.fixed
|
||||
? q
|
||||
.rateTypeEqualTo(SupportedRateType.both)
|
||||
.or()
|
||||
.rateTypeEqualTo(SupportedRateType.fixed)
|
||||
: q
|
||||
.rateTypeEqualTo(SupportedRateType.both)
|
||||
.or()
|
||||
.rateTypeEqualTo(SupportedRateType.estimated))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
await updateRanges(shouldNotifyListeners: false);
|
||||
|
||||
await updateEstimate(
|
||||
shouldNotifyListeners: false,
|
||||
reversed: reversed,
|
||||
);
|
||||
|
||||
//todo: check if print needed
|
||||
// debugPrint(
|
||||
// "_updated TO: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$fromAmount _toAmount=$toAmount rate:$rate for: $exchange");
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
//todo: check if print needed
|
||||
// debugPrint(
|
||||
// "_updated FROM: _from=${_from!.ticker} _to=${_to!.ticker} _fromAmount=$fromAmount _toAmount=$toAmount rate:$rate for: $exchange");
|
||||
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 (_sendCurrency == null) {
|
||||
switch (exchange.name) {
|
||||
case ChangeNowExchange.exchangeName:
|
||||
_sendCurrency = _cachedSendCN;
|
||||
break;
|
||||
case MajesticBankExchange.exchangeName:
|
||||
_sendCurrency = _cachedSendMB;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_receiveCurrency != null) {
|
||||
_receiveCurrency = await ExchangeDataLoadingService
|
||||
.instance.isar.currencies
|
||||
.where()
|
||||
.exchangeNameEqualTo(exchange.name)
|
||||
.filter()
|
||||
.tickerEqualTo(_receiveCurrency!.ticker)
|
||||
.and()
|
||||
.group((q) => exchangeRateType == ExchangeRateType.fixed
|
||||
? q
|
||||
.rateTypeEqualTo(SupportedRateType.both)
|
||||
.or()
|
||||
.rateTypeEqualTo(SupportedRateType.fixed)
|
||||
: q
|
||||
.rateTypeEqualTo(SupportedRateType.both)
|
||||
.or()
|
||||
.rateTypeEqualTo(SupportedRateType.estimated))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
if (_receiveCurrency == null) {
|
||||
switch (exchange.name) {
|
||||
case ChangeNowExchange.exchangeName:
|
||||
_receiveCurrency = _cachedReceivingCN;
|
||||
break;
|
||||
case MajesticBankExchange.exchangeName:
|
||||
_receiveCurrency = _cachedReceivingMB;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_updateCachedCurrencies(
|
||||
exchangeName: exchange.name,
|
||||
send: _sendCurrency,
|
||||
receiving: _receiveCurrency,
|
||||
);
|
||||
|
||||
await _updateRangesAndEstimate(
|
||||
shouldNotifyListeners: false,
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldNotifyListeners) {
|
||||
notifyListeners();
|
||||
_notify();
|
||||
}
|
||||
}
|
||||
|
||||
void _onExchangeRateTypeChanged() {
|
||||
print("_onExchangeRateTypeChanged");
|
||||
updateRanges(shouldNotifyListeners: true).then(
|
||||
(_) => updateEstimate(
|
||||
shouldNotifyListeners: true,
|
||||
reversed: reversed,
|
||||
),
|
||||
void setCurrencies(Currency from, Currency to) {
|
||||
_sendCurrency = from;
|
||||
_receiveCurrency = to;
|
||||
_updateCachedCurrencies(
|
||||
exchangeName: exchange.name,
|
||||
send: _sendCurrency,
|
||||
receiving: _receiveCurrency,
|
||||
);
|
||||
}
|
||||
|
||||
void _onExchangeTypeChanged() {
|
||||
updateRanges(shouldNotifyListeners: true).then(
|
||||
(_) => updateEstimate(
|
||||
shouldNotifyListeners: true,
|
||||
reversed: reversed,
|
||||
),
|
||||
);
|
||||
void reset({
|
||||
required bool shouldNotifyListeners,
|
||||
}) {
|
||||
_exchange = null;
|
||||
_reversed = false;
|
||||
_rate = null;
|
||||
_sendAmount = null;
|
||||
_receiveAmount = null;
|
||||
_sendCurrency = null;
|
||||
_receiveCurrency = null;
|
||||
_minSendAmount = null;
|
||||
_minReceiveAmount = null;
|
||||
_maxSendAmount = null;
|
||||
_maxReceiveAmount = null;
|
||||
|
||||
if (shouldNotifyListeners) {
|
||||
_notify();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateRanges({required bool shouldNotifyListeners}) async {
|
||||
Future<void> setSendAmountAndCalculateReceiveAmount(
|
||||
Decimal? newSendAmount,
|
||||
bool shouldNotifyListeners,
|
||||
) async {
|
||||
if (newSendAmount == null) {
|
||||
// todo: check if this breaks things and stuff
|
||||
_receiveAmount = null;
|
||||
_sendAmount = null;
|
||||
} else {
|
||||
if (newSendAmount <= Decimal.zero) {
|
||||
_receiveAmount = Decimal.zero;
|
||||
}
|
||||
|
||||
_sendAmount = newSendAmount;
|
||||
_reversed = false;
|
||||
|
||||
await _updateRangesAndEstimate(
|
||||
shouldNotifyListeners: false,
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldNotifyListeners) {
|
||||
_notify();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setReceivingAmountAndCalculateSendAmount(
|
||||
Decimal? newReceiveAmount,
|
||||
bool shouldNotifyListeners,
|
||||
) async {
|
||||
if (newReceiveAmount == null) {
|
||||
// todo: check if this breaks things and stuff
|
||||
_receiveAmount = null;
|
||||
_sendAmount = null;
|
||||
} else {
|
||||
if (newReceiveAmount <= Decimal.zero) {
|
||||
_sendAmount = Decimal.zero;
|
||||
}
|
||||
|
||||
_receiveAmount = newReceiveAmount;
|
||||
_reversed = true;
|
||||
|
||||
await _updateRangesAndEstimate(
|
||||
shouldNotifyListeners: false,
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldNotifyListeners) {
|
||||
_notify();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateSendCurrency(
|
||||
Currency sendCurrency,
|
||||
bool shouldNotifyListeners,
|
||||
) async {
|
||||
try {
|
||||
_sendCurrency = sendCurrency;
|
||||
_minSendAmount = null;
|
||||
_maxSendAmount = null;
|
||||
|
||||
_updateCachedCurrencies(
|
||||
exchangeName: exchange.name,
|
||||
send: _sendCurrency,
|
||||
receiving: _receiveCurrency,
|
||||
);
|
||||
|
||||
if (_receiveCurrency == null) {
|
||||
_rate = null;
|
||||
} else {
|
||||
await _updateRangesAndEstimate(
|
||||
shouldNotifyListeners: false,
|
||||
);
|
||||
}
|
||||
if (shouldNotifyListeners) {
|
||||
_notify();
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateReceivingCurrency(
|
||||
Currency receiveCurrency,
|
||||
bool shouldNotifyListeners,
|
||||
) async {
|
||||
try {
|
||||
_receiveCurrency = receiveCurrency;
|
||||
_minReceiveAmount = null;
|
||||
_maxReceiveAmount = null;
|
||||
|
||||
_updateCachedCurrencies(
|
||||
exchangeName: exchange.name,
|
||||
send: _sendCurrency,
|
||||
receiving: _receiveCurrency,
|
||||
);
|
||||
|
||||
if (_sendCurrency == null) {
|
||||
_rate = null;
|
||||
} else {
|
||||
await _updateRangesAndEstimate(
|
||||
shouldNotifyListeners: false,
|
||||
);
|
||||
}
|
||||
if (shouldNotifyListeners) {
|
||||
_notify();
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> swap({
|
||||
required bool shouldNotifyListeners,
|
||||
}) async {
|
||||
final Decimal? temp = sendAmount;
|
||||
_sendAmount = receiveAmount;
|
||||
_receiveAmount = temp;
|
||||
|
||||
_minSendAmount = null;
|
||||
_maxSendAmount = null;
|
||||
_minReceiveAmount = null;
|
||||
_maxReceiveAmount = null;
|
||||
|
||||
final Currency? tmp = sendCurrency;
|
||||
_sendCurrency = receiveCurrency;
|
||||
_receiveCurrency = tmp;
|
||||
|
||||
_updateCachedCurrencies(
|
||||
exchangeName: exchange.name,
|
||||
send: _sendCurrency,
|
||||
receiving: _receiveCurrency,
|
||||
);
|
||||
|
||||
await _updateRangesAndEstimate(
|
||||
shouldNotifyListeners: false,
|
||||
);
|
||||
|
||||
if (shouldNotifyListeners) {
|
||||
_notify();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> refresh() => _updateRangesAndEstimate(
|
||||
shouldNotifyListeners: true,
|
||||
);
|
||||
|
||||
//============================================================================
|
||||
// private state updaters
|
||||
//============================================================================
|
||||
|
||||
Future<void> _updateRangesAndEstimate({
|
||||
required bool shouldNotifyListeners,
|
||||
}) async {
|
||||
try {
|
||||
await _updateRanges(shouldNotifyListeners: false);
|
||||
await _updateEstimate(shouldNotifyListeners: false);
|
||||
if (shouldNotifyListeners) {
|
||||
_notify();
|
||||
}
|
||||
} catch (_) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _updateRanges({
|
||||
required bool shouldNotifyListeners,
|
||||
}) async {
|
||||
// if (exchange?.name == SimpleSwapExchange.exchangeName) {
|
||||
// reversed = false;
|
||||
// }
|
||||
final _fromTicker = reversed ? toTicker : fromTicker;
|
||||
final _toTicker = reversed ? fromTicker : toTicker;
|
||||
if (_fromTicker == null || _toTicker == null) {
|
||||
final _send = sendCurrency;
|
||||
final _receive = receiveCurrency;
|
||||
if (_send == null || _receive == null) {
|
||||
Logging.instance.log(
|
||||
"Tried to $runtimeType.updateRanges where (from: $_fromTicker || to: $_toTicker) for: $exchange",
|
||||
"Tried to $runtimeType.updateRanges where ( $_send || $_receive) for: $exchange",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
return;
|
||||
}
|
||||
final response = await exchange?.getRange(
|
||||
_fromTicker,
|
||||
_toTicker,
|
||||
exchangeType == ExchangeRateType.fixed,
|
||||
final response = await exchange.getRange(
|
||||
_send.ticker,
|
||||
_receive.ticker,
|
||||
exchangeRateType == ExchangeRateType.fixed,
|
||||
);
|
||||
|
||||
if (response?.value == null) {
|
||||
if (response.value == null) {
|
||||
Logging.instance.log(
|
||||
"Tried to $runtimeType.updateRanges for: $exchange where response: $response",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
return;
|
||||
}
|
||||
final responseReversed = await exchange.getRange(
|
||||
_receive.ticker,
|
||||
_send.ticker,
|
||||
exchangeRateType == ExchangeRateType.fixed,
|
||||
);
|
||||
|
||||
final range = response!.value!;
|
||||
if (responseReversed.value == null) {
|
||||
Logging.instance.log(
|
||||
"Tried to $runtimeType.updateRanges for: $exchange where response: $responseReversed",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
minAmount = range.min;
|
||||
maxAmount = range.max;
|
||||
final range = response.value!;
|
||||
final rangeReversed = responseReversed.value!;
|
||||
|
||||
_minSendAmount = range.min;
|
||||
_maxSendAmount = range.max;
|
||||
_minReceiveAmount = rangeReversed.min;
|
||||
_maxReceiveAmount = rangeReversed.max;
|
||||
|
||||
//todo: check if print needed
|
||||
// debugPrint(
|
||||
// "updated range for: $exchange for $_fromTicker-$_toTicker: $range");
|
||||
|
||||
if (shouldNotifyListeners) {
|
||||
notifyListeners();
|
||||
_notify();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateEstimate({
|
||||
Future<void> _updateEstimate({
|
||||
required bool shouldNotifyListeners,
|
||||
required bool reversed,
|
||||
}) async {
|
||||
// if (exchange?.name == SimpleSwapExchange.exchangeName) {
|
||||
// reversed = false;
|
||||
// }
|
||||
final amount = reversed ? toAmount : fromAmount;
|
||||
if (fromTicker == null ||
|
||||
toTicker == null ||
|
||||
final amount = reversed ? receiveAmount : sendAmount;
|
||||
if (sendCurrency == null ||
|
||||
receiveCurrency == null ||
|
||||
amount == null ||
|
||||
amount <= Decimal.zero) {
|
||||
Logging.instance.log(
|
||||
"Tried to $runtimeType.updateEstimate for: $exchange where (from: $fromTicker || to: $toTicker || amount: $amount)",
|
||||
"Tried to $runtimeType.updateEstimate for: $exchange where (from: $sendCurrency || to: $receiveCurrency || amount: $amount)",
|
||||
level: LogLevel.Info,
|
||||
);
|
||||
return;
|
||||
}
|
||||
final response = await exchange?.getEstimate(
|
||||
fromTicker!,
|
||||
toTicker!,
|
||||
final response = await exchange.getEstimate(
|
||||
sendCurrency!.ticker,
|
||||
receiveCurrency!.ticker,
|
||||
amount,
|
||||
exchangeType == ExchangeRateType.fixed,
|
||||
exchangeRateType == ExchangeRateType.fixed,
|
||||
reversed,
|
||||
);
|
||||
|
||||
if (response?.value == null) {
|
||||
if (response.value == null) {
|
||||
Logging.instance.log(
|
||||
"Tried to $runtimeType.updateEstimate for: $exchange where response: $response",
|
||||
level: LogLevel.Info,
|
||||
|
@ -370,63 +520,73 @@ class ExchangeFormState extends ChangeNotifier {
|
|||
return;
|
||||
}
|
||||
|
||||
estimate = response!.value!;
|
||||
_estimate = response.value!;
|
||||
|
||||
if (reversed) {
|
||||
fromAmount = estimate!.estimatedAmount;
|
||||
_sendAmount = _estimate!.estimatedAmount;
|
||||
} else {
|
||||
toAmount = estimate!.estimatedAmount;
|
||||
_receiveAmount = _estimate!.estimatedAmount;
|
||||
}
|
||||
|
||||
rate = (toAmount! / fromAmount!).toDecimal(scaleOnInfinitePrecision: 12);
|
||||
_rate =
|
||||
(receiveAmount! / sendAmount!).toDecimal(scaleOnInfinitePrecision: 12);
|
||||
|
||||
//todo: check if print needed
|
||||
// debugPrint(
|
||||
// "updated estimate for: $exchange for $fromTicker-$toTicker: $estimate");
|
||||
|
||||
if (shouldNotifyListeners) {
|
||||
notifyListeners();
|
||||
_notify();
|
||||
}
|
||||
}
|
||||
|
||||
void setOnError({
|
||||
required void Function(String)? onError,
|
||||
bool shouldNotifyListeners = false,
|
||||
//============================================================================
|
||||
|
||||
Currency? _cachedReceivingMB;
|
||||
Currency? _cachedSendMB;
|
||||
Currency? _cachedReceivingCN;
|
||||
Currency? _cachedSendCN;
|
||||
|
||||
void _updateCachedCurrencies({
|
||||
required String exchangeName,
|
||||
required Currency? send,
|
||||
required Currency? receiving,
|
||||
}) {
|
||||
_onError = onError;
|
||||
if (shouldNotifyListeners) {
|
||||
notifyListeners();
|
||||
switch (exchangeName) {
|
||||
case ChangeNowExchange.exchangeName:
|
||||
_cachedSendCN = send ?? _cachedSendCN;
|
||||
_cachedReceivingCN = receiving ?? _cachedReceivingCN;
|
||||
break;
|
||||
case MajesticBankExchange.exchangeName:
|
||||
_cachedSendMB = send ?? _cachedSendMB;
|
||||
_cachedReceivingMB = receiving ?? _cachedReceivingMB;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> swap({FixedRateMarket? market}) async {
|
||||
final Decimal? newToAmount = fromAmount;
|
||||
final Decimal? newFromAmount = toAmount;
|
||||
|
||||
fromAmount = newFromAmount;
|
||||
toAmount = newToAmount;
|
||||
|
||||
minAmount = null;
|
||||
maxAmount = null;
|
||||
|
||||
if (exchangeType == ExchangeRateType.fixed &&
|
||||
exchange?.name == ChangeNowExchange.exchangeName) {
|
||||
await updateMarket(market, false);
|
||||
} else {
|
||||
final Currency? newTo = from;
|
||||
final Currency? newFrom = to;
|
||||
|
||||
_to = newTo;
|
||||
_from = newFrom;
|
||||
|
||||
await updateRanges(shouldNotifyListeners: false);
|
||||
|
||||
await updateEstimate(
|
||||
shouldNotifyListeners: false,
|
||||
reversed: reversed,
|
||||
);
|
||||
}
|
||||
|
||||
void _notify() {
|
||||
debugPrint("ExFState NOTIFY: ${toString()}");
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "{"
|
||||
"\n\t exchange: $exchange,"
|
||||
"\n\t exchangeRateType: $exchangeRateType,"
|
||||
"\n\t sendCurrency: $sendCurrency,"
|
||||
"\n\t receiveCurrency: $receiveCurrency,"
|
||||
"\n\t rate: $rate,"
|
||||
"\n\t reversed: $reversed,"
|
||||
"\n\t sendAmount: $sendAmount,"
|
||||
"\n\t receiveAmount: $receiveAmount,"
|
||||
"\n\t estimate: $estimate,"
|
||||
"\n\t minSendAmount: $minSendAmount,"
|
||||
"\n\t maxSendAmount: $maxSendAmount,"
|
||||
"\n\t minReceiveAmount: $minReceiveAmount,"
|
||||
"\n\t maxReceiveAmount: $maxReceiveAmount,"
|
||||
"\n\t canExchange: $canExchange,"
|
||||
"\n\t warning: $warning,"
|
||||
"\n}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
|
||||
class Pair {
|
||||
final String from;
|
||||
final String fromNetwork;
|
||||
|
||||
final String to;
|
||||
final String toNetwork;
|
||||
|
||||
final bool fixedRate;
|
||||
final bool floatingRate;
|
||||
|
||||
Pair({
|
||||
required this.from,
|
||||
required this.fromNetwork,
|
||||
required this.to,
|
||||
required this.toNetwork,
|
||||
required this.fixedRate,
|
||||
required this.floatingRate,
|
||||
});
|
||||
|
||||
factory Pair.fromMap(Map<String, dynamic> map) {
|
||||
try {
|
||||
return Pair(
|
||||
from: map["from"] as String,
|
||||
fromNetwork: map["fromNetwork"] as String,
|
||||
to: map["to"] as String,
|
||||
toNetwork: map["toNetwork"] as String,
|
||||
fixedRate: map["fixedRate"] as bool,
|
||||
floatingRate: map["floatingRate"] as bool,
|
||||
);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("Pair.fromMap(): $e\n$s", level: LogLevel.Error);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"from": from,
|
||||
"fromNetwork": fromNetwork,
|
||||
"to": to,
|
||||
"toNetwork": toNetwork,
|
||||
"fixedRate": fixedRate,
|
||||
"floatingRate": floatingRate,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is Pair &&
|
||||
from == other.from &&
|
||||
fromNetwork == other.fromNetwork &&
|
||||
to == other.to &&
|
||||
toNetwork == other.toNetwork &&
|
||||
fixedRate == other.fixedRate &&
|
||||
floatingRate == other.floatingRate;
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(
|
||||
from,
|
||||
fromNetwork,
|
||||
to,
|
||||
toNetwork,
|
||||
fixedRate,
|
||||
floatingRate,
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() => "Pair: ${toMap()}";
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
import 'package:stackwallet/models/exchange/response_objects/currency.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/pair.dart';
|
||||
|
||||
class SPAvailableCurrencies {
|
||||
final List<Currency> floatingRateCurrencies = [];
|
||||
final List<Currency> fixedRateCurrencies = [];
|
||||
|
||||
final List<Pair> floatingRatePairs = [];
|
||||
final List<Pair> fixedRatePairs = [];
|
||||
|
||||
void updateFloatingCurrencies(List<Currency> newCurrencies) {
|
||||
floatingRateCurrencies.clear();
|
||||
floatingRateCurrencies.addAll(newCurrencies);
|
||||
}
|
||||
|
||||
void updateFixedCurrencies(List<Currency> newCurrencies) {
|
||||
fixedRateCurrencies.clear();
|
||||
fixedRateCurrencies.addAll(newCurrencies);
|
||||
}
|
||||
|
||||
void updateFloatingPairs(List<Pair> newPairs) {
|
||||
floatingRatePairs.clear();
|
||||
floatingRatePairs.addAll(newPairs);
|
||||
}
|
||||
|
||||
void updateFixedPairs(List<Pair> newPairs) {
|
||||
fixedRatePairs.clear();
|
||||
fixedRatePairs.addAll(newPairs);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,21 @@
|
|||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
|
||||
part 'currency.g.dart';
|
||||
|
||||
@Collection(accessor: "currencies")
|
||||
class Currency {
|
||||
Id? id;
|
||||
|
||||
@Index()
|
||||
final String exchangeName;
|
||||
|
||||
/// Currency ticker
|
||||
@Index(composite: [
|
||||
CompositeIndex("exchangeName"),
|
||||
CompositeIndex("name"),
|
||||
])
|
||||
final String ticker;
|
||||
|
||||
/// Currency name
|
||||
|
@ -11,57 +27,57 @@ class Currency {
|
|||
/// Currency logo url
|
||||
final String image;
|
||||
|
||||
/// Indicates if a currency has an Extra ID
|
||||
final bool hasExternalId;
|
||||
|
||||
/// external id if it exists
|
||||
final String? externalId;
|
||||
|
||||
/// Indicates if a currency is a fiat currency (EUR, USD)
|
||||
final bool isFiat;
|
||||
|
||||
/// Indicates if a currency is popular
|
||||
final bool featured;
|
||||
|
||||
/// Indicates if a currency is stable
|
||||
final bool isStable;
|
||||
|
||||
/// Indicates if a currency is available on a fixed-rate flow
|
||||
final bool supportsFixedRate;
|
||||
@enumerated
|
||||
final SupportedRateType rateType;
|
||||
|
||||
/// (Optional - based on api call) Indicates whether the pair is
|
||||
/// currently supported by change now
|
||||
final bool? isAvailable;
|
||||
|
||||
@Index()
|
||||
final bool isStackCoin;
|
||||
|
||||
Currency({
|
||||
required this.exchangeName,
|
||||
required this.ticker,
|
||||
required this.name,
|
||||
required this.network,
|
||||
required this.image,
|
||||
required this.hasExternalId,
|
||||
this.externalId,
|
||||
required this.isFiat,
|
||||
required this.featured,
|
||||
required this.isStable,
|
||||
required this.supportsFixedRate,
|
||||
required this.rateType,
|
||||
this.isAvailable,
|
||||
required this.isStackCoin,
|
||||
});
|
||||
|
||||
factory Currency.fromJson(Map<String, dynamic> json) {
|
||||
factory Currency.fromJson(
|
||||
Map<String, dynamic> json, {
|
||||
required String exchangeName,
|
||||
required SupportedRateType rateType,
|
||||
}) {
|
||||
try {
|
||||
final ticker = (json["ticker"] as String).toUpperCase();
|
||||
|
||||
return Currency(
|
||||
ticker: json["ticker"] as String,
|
||||
exchangeName: exchangeName,
|
||||
ticker: ticker,
|
||||
name: json["name"] as String,
|
||||
network: json["network"] as String? ?? "",
|
||||
image: json["image"] as String,
|
||||
hasExternalId: json["hasExternalId"] as bool,
|
||||
externalId: json["externalId"] as String?,
|
||||
isFiat: json["isFiat"] as bool,
|
||||
featured: json["featured"] as bool,
|
||||
isStable: json["isStable"] as bool,
|
||||
supportsFixedRate: json["supportsFixedRate"] as bool,
|
||||
rateType: rateType,
|
||||
isAvailable: json["isAvailable"] as bool?,
|
||||
);
|
||||
isStackCoin:
|
||||
json["isStackCoin"] as bool? ?? Currency.checkIsStackCoin(ticker),
|
||||
)..id = json["id"] as int?;
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
|
@ -69,55 +85,60 @@ class Currency {
|
|||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = {
|
||||
"id": id,
|
||||
"exchangeName": exchangeName,
|
||||
"ticker": ticker,
|
||||
"name": name,
|
||||
"network": network,
|
||||
"image": image,
|
||||
"hasExternalId": hasExternalId,
|
||||
"externalId": externalId,
|
||||
"isFiat": isFiat,
|
||||
"featured": featured,
|
||||
"isStable": isStable,
|
||||
"supportsFixedRate": supportsFixedRate,
|
||||
"rateType": rateType,
|
||||
"isAvailable": isAvailable,
|
||||
"isStackCoin": isStackCoin,
|
||||
};
|
||||
|
||||
if (isAvailable != null) {
|
||||
map["isAvailable"] = isAvailable!;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
Currency copyWith({
|
||||
Id? id,
|
||||
String? exchangeName,
|
||||
String? ticker,
|
||||
String? name,
|
||||
String? network,
|
||||
String? image,
|
||||
bool? hasExternalId,
|
||||
String? externalId,
|
||||
bool? isFiat,
|
||||
bool? featured,
|
||||
bool? isStable,
|
||||
bool? supportsFixedRate,
|
||||
SupportedRateType? rateType,
|
||||
bool? isAvailable,
|
||||
bool? isStackCoin,
|
||||
}) {
|
||||
return Currency(
|
||||
exchangeName: exchangeName ?? this.exchangeName,
|
||||
ticker: ticker ?? this.ticker,
|
||||
name: name ?? this.name,
|
||||
network: network ?? this.network,
|
||||
image: image ?? this.image,
|
||||
hasExternalId: hasExternalId ?? this.hasExternalId,
|
||||
externalId: externalId ?? this.externalId,
|
||||
isFiat: isFiat ?? this.isFiat,
|
||||
featured: featured ?? this.featured,
|
||||
isStable: isStable ?? this.isStable,
|
||||
supportsFixedRate: supportsFixedRate ?? this.supportsFixedRate,
|
||||
rateType: rateType ?? this.rateType,
|
||||
isAvailable: isAvailable ?? this.isAvailable,
|
||||
);
|
||||
isStackCoin: isStackCoin ?? this.isStackCoin,
|
||||
)..id = id ?? this.id;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Currency: ${toJson()}";
|
||||
}
|
||||
|
||||
static bool checkIsStackCoin(String ticker) {
|
||||
try {
|
||||
coinFromTickerCaseInsensitive(ticker);
|
||||
return true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
1938
lib/models/isar/exchange_cache/currency.g.dart
Normal file
1938
lib/models/isar/exchange_cache/currency.g.dart
Normal file
File diff suppressed because it is too large
Load diff
61
lib/models/isar/exchange_cache/pair.dart
Normal file
61
lib/models/isar/exchange_cache/pair.dart
Normal file
|
@ -0,0 +1,61 @@
|
|||
import 'package:isar/isar.dart';
|
||||
|
||||
part 'pair.g.dart';
|
||||
|
||||
// embedded enum // no not modify
|
||||
enum SupportedRateType { fixed, estimated, both }
|
||||
|
||||
@collection
|
||||
class Pair {
|
||||
Pair({
|
||||
required this.exchangeName,
|
||||
required this.from,
|
||||
required this.to,
|
||||
required this.rateType,
|
||||
});
|
||||
|
||||
Id? id;
|
||||
|
||||
@Index()
|
||||
final String exchangeName;
|
||||
|
||||
@Index(composite: [
|
||||
CompositeIndex("exchangeName"),
|
||||
CompositeIndex("to"),
|
||||
])
|
||||
final String from;
|
||||
|
||||
final String to;
|
||||
|
||||
@enumerated
|
||||
final SupportedRateType rateType;
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"id": id,
|
||||
"exchangeName": exchangeName,
|
||||
"from": from,
|
||||
"to": to,
|
||||
"rateType": rateType,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(other) =>
|
||||
other is Pair &&
|
||||
exchangeName == other.exchangeName &&
|
||||
from == other.from &&
|
||||
to == other.to &&
|
||||
rateType == other.rateType;
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(
|
||||
exchangeName,
|
||||
from,
|
||||
to,
|
||||
rateType,
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() => "Pair: ${toMap()}";
|
||||
}
|
1214
lib/models/isar/exchange_cache/pair.g.dart
Normal file
1214
lib/models/isar/exchange_cache/pair.g.dart
Normal file
File diff suppressed because it is too large
Load diff
|
@ -252,12 +252,15 @@ bool isStackCoin(String? ticker) {
|
|||
}
|
||||
}
|
||||
|
||||
Widget? getIconForTicker(String ticker) {
|
||||
Widget? getIconForTicker(
|
||||
String ticker, {
|
||||
double size = 20,
|
||||
}) {
|
||||
String? iconAsset = /*isStackCoin(ticker)
|
||||
?*/
|
||||
Assets.svg.iconFor(coin: coinFromTickerCaseInsensitive(ticker));
|
||||
// : Assets.svg.buyIconFor(ticker);
|
||||
return (iconAsset != null)
|
||||
? SvgPicture.asset(iconAsset, height: 20, width: 20)
|
||||
? SvgPicture.asset(iconAsset, height: size, width: size)
|
||||
: null;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,502 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/currency.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
|
||||
import 'package:stackwallet/pages/buy_view/sub_widgets/crypto_selection_view.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
class ExchangeCurrencySelectionView extends StatefulWidget {
|
||||
const ExchangeCurrencySelectionView({
|
||||
Key? key,
|
||||
required this.exchangeName,
|
||||
required this.willChange,
|
||||
required this.paired,
|
||||
required this.isFixedRate,
|
||||
required this.willChangeIsSend,
|
||||
}) : super(key: key);
|
||||
|
||||
final String exchangeName;
|
||||
final Currency? willChange;
|
||||
final Currency? paired;
|
||||
final bool isFixedRate;
|
||||
final bool willChangeIsSend;
|
||||
|
||||
@override
|
||||
State<ExchangeCurrencySelectionView> createState() =>
|
||||
_ExchangeCurrencySelectionViewState();
|
||||
}
|
||||
|
||||
class _ExchangeCurrencySelectionViewState
|
||||
extends State<ExchangeCurrencySelectionView> {
|
||||
late TextEditingController _searchController;
|
||||
final _searchFocusNode = FocusNode();
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
List<Currency> _currencies = [];
|
||||
List<Pair> pairs = [];
|
||||
|
||||
bool _loaded = false;
|
||||
String _searchString = "";
|
||||
|
||||
Future<T> _showUpdatingCurrencies<T>({
|
||||
required Future<T> whileFuture,
|
||||
}) async {
|
||||
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: "Loading currencies",
|
||||
eventBus: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final result = await whileFuture;
|
||||
|
||||
if (mounted) {
|
||||
Navigator.of(context, rootNavigator: isDesktop).pop();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<List<Currency>> _loadCurrencies() async {
|
||||
if (widget.paired == null) {
|
||||
return await _getCurrencies();
|
||||
}
|
||||
|
||||
final pairs = await _loadAvailablePairs();
|
||||
List<Currency> currencies = [];
|
||||
for (final pair in pairs) {
|
||||
final currency =
|
||||
await _getCurrency(widget.willChangeIsSend ? pair.from : pair.to);
|
||||
if (currency != null) {
|
||||
currencies.add(currency);
|
||||
}
|
||||
}
|
||||
|
||||
return currencies;
|
||||
}
|
||||
|
||||
Future<Currency?> _getCurrency(String ticker) {
|
||||
return ExchangeDataLoadingService.instance.isar.currencies
|
||||
.where()
|
||||
.exchangeNameEqualTo(widget.exchangeName)
|
||||
.filter()
|
||||
.tickerEqualTo(ticker, caseSensitive: false)
|
||||
.group((q) => widget.isFixedRate
|
||||
? q
|
||||
.rateTypeEqualTo(SupportedRateType.both)
|
||||
.or()
|
||||
.rateTypeEqualTo(SupportedRateType.fixed)
|
||||
: q
|
||||
.rateTypeEqualTo(SupportedRateType.both)
|
||||
.or()
|
||||
.rateTypeEqualTo(SupportedRateType.estimated))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
Future<List<Pair>> _loadAvailablePairs() {
|
||||
final query = ExchangeDataLoadingService.instance.isar.pairs
|
||||
.where()
|
||||
.exchangeNameEqualTo(widget.exchangeName)
|
||||
.filter()
|
||||
.group((q) => widget.isFixedRate
|
||||
? q
|
||||
.rateTypeEqualTo(SupportedRateType.both)
|
||||
.or()
|
||||
.rateTypeEqualTo(SupportedRateType.fixed)
|
||||
: q
|
||||
.rateTypeEqualTo(SupportedRateType.both)
|
||||
.or()
|
||||
.rateTypeEqualTo(SupportedRateType.estimated))
|
||||
.and()
|
||||
.group((q) => widget.willChangeIsSend
|
||||
? q.toEqualTo(widget.paired!.ticker, caseSensitive: false)
|
||||
: q.fromEqualTo(widget.paired!.ticker, caseSensitive: false));
|
||||
|
||||
if (widget.willChangeIsSend) {
|
||||
return query.sortByFrom().findAll();
|
||||
} else {
|
||||
return query.sortByTo().findAll();
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Currency>> _getCurrencies() async {
|
||||
return ExchangeDataLoadingService.instance.isar.currencies
|
||||
.where()
|
||||
.exchangeNameEqualTo(widget.exchangeName)
|
||||
.filter()
|
||||
.group((q) => widget.isFixedRate
|
||||
? q
|
||||
.rateTypeEqualTo(SupportedRateType.both)
|
||||
.or()
|
||||
.rateTypeEqualTo(SupportedRateType.fixed)
|
||||
: q
|
||||
.rateTypeEqualTo(SupportedRateType.both)
|
||||
.or()
|
||||
.rateTypeEqualTo(SupportedRateType.estimated))
|
||||
.sortByIsStackCoin()
|
||||
.thenByName()
|
||||
.findAll();
|
||||
}
|
||||
|
||||
List<Currency> filter(String text) {
|
||||
if (text.isEmpty) {
|
||||
return _currencies;
|
||||
}
|
||||
|
||||
if (widget.paired == null) {
|
||||
return _currencies
|
||||
.where((e) =>
|
||||
e.name.toLowerCase().contains(text.toLowerCase()) ||
|
||||
e.ticker.toLowerCase().contains(text.toLowerCase()))
|
||||
.toList(growable: false);
|
||||
} else {
|
||||
return _currencies
|
||||
.where((e) =>
|
||||
e.ticker.toLowerCase() != widget.paired!.ticker.toLowerCase() &&
|
||||
(e.name.toLowerCase().contains(text.toLowerCase()) ||
|
||||
e.ticker.toLowerCase().contains(text.toLowerCase())))
|
||||
.toList(growable: false);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_searchController = TextEditingController();
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
_searchFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!_loaded) {
|
||||
_loaded = true;
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
_currencies =
|
||||
await _showUpdatingCurrencies(whileFuture: _loadCurrencies());
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 50));
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
"Choose a coin to exchange",
|
||||
style: STextStyles.pageTitleH2(context),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: [
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autofocus: isDesktop,
|
||||
autocorrect: !isDesktop,
|
||||
enableSuggestions: !isDesktop,
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: (value) => setState(() => _searchString = value),
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search",
|
||||
_searchFocusNode,
|
||||
context,
|
||||
desktopMed: isDesktop,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_searchController.text = "";
|
||||
_searchString = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
"Popular coins",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Flexible(
|
||||
child: Builder(builder: (context) {
|
||||
final coins = Coin.values.where((e) =>
|
||||
e.ticker.toLowerCase() !=
|
||||
widget.paired?.ticker.toLowerCase());
|
||||
|
||||
final items = filter(_searchString)
|
||||
.where((e) => coins
|
||||
.where((coin) =>
|
||||
coin.ticker.toLowerCase() == e.ticker.toLowerCase())
|
||||
.isNotEmpty)
|
||||
.toList(growable: false);
|
||||
|
||||
return RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
primary: isDesktop ? false : null,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (builderContext, index) {
|
||||
final bool hasImageUrl =
|
||||
items[index].image.startsWith("http");
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(items[index]);
|
||||
},
|
||||
child: RoundedWhiteContainer(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: isStackCoin(items[index].ticker)
|
||||
? getIconForTicker(
|
||||
items[index].ticker,
|
||||
size: 24,
|
||||
)
|
||||
: hasImageUrl
|
||||
? SvgPicture.network(
|
||||
items[index].image,
|
||||
width: 24,
|
||||
height: 24,
|
||||
placeholderBuilder: (_) =>
|
||||
const LoadingIndicator(),
|
||||
)
|
||||
: const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
items[index].name,
|
||||
style: STextStyles.largeMedium14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
items[index].ticker.toUpperCase(),
|
||||
style: STextStyles.smallMed12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
"All coins",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Flexible(
|
||||
child: Builder(builder: (context) {
|
||||
final filtered = filter(_searchString);
|
||||
return RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
primary: isDesktop ? false : null,
|
||||
itemCount: filtered.length,
|
||||
itemBuilder: (builderContext, index) {
|
||||
final bool hasImageUrl =
|
||||
filtered[index].image.startsWith("http");
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(filtered[index]);
|
||||
},
|
||||
child: RoundedWhiteContainer(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: isStackCoin(filtered[index].ticker)
|
||||
? getIconForTicker(
|
||||
filtered[index].ticker,
|
||||
size: 24,
|
||||
)
|
||||
: hasImageUrl
|
||||
? SvgPicture.network(
|
||||
filtered[index].image,
|
||||
width: 24,
|
||||
height: 24,
|
||||
placeholderBuilder: (_) =>
|
||||
const LoadingIndicator(),
|
||||
)
|
||||
: const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
filtered[index].name,
|
||||
style: STextStyles.largeMedium14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
filtered[index].ticker.toUpperCase(),
|
||||
style: STextStyles.smallMed12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,384 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/currency.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class FixedRateMarketPairCoinSelectionView extends ConsumerStatefulWidget {
|
||||
const FixedRateMarketPairCoinSelectionView({
|
||||
Key? key,
|
||||
required this.markets,
|
||||
required this.currencies,
|
||||
required this.isFrom,
|
||||
}) : super(key: key);
|
||||
|
||||
final List<FixedRateMarket> markets;
|
||||
final List<Currency> currencies;
|
||||
final bool isFrom;
|
||||
|
||||
@override
|
||||
ConsumerState<FixedRateMarketPairCoinSelectionView> createState() =>
|
||||
_FixedRateMarketPairCoinSelectionViewState();
|
||||
}
|
||||
|
||||
class _FixedRateMarketPairCoinSelectionViewState
|
||||
extends ConsumerState<FixedRateMarketPairCoinSelectionView> {
|
||||
late TextEditingController _searchController;
|
||||
final _searchFocusNode = FocusNode();
|
||||
|
||||
late final List<FixedRateMarket> markets;
|
||||
late List<FixedRateMarket> _markets;
|
||||
|
||||
late final bool isFrom;
|
||||
|
||||
Tuple2<String, String> _imageUrlAndNameFor(String ticker) {
|
||||
final matches = widget.currencies.where(
|
||||
(element) => element.ticker.toLowerCase() == ticker.toLowerCase());
|
||||
|
||||
if (matches.isNotEmpty) {
|
||||
return Tuple2(matches.first.image, matches.first.name);
|
||||
}
|
||||
return Tuple2("", ticker);
|
||||
}
|
||||
|
||||
void filter(String text) {
|
||||
setState(() {
|
||||
_markets = [
|
||||
...markets.where((e) {
|
||||
final String ticker = isFrom ? e.from : e.to;
|
||||
final __currencies = widget.currencies
|
||||
.where((e) => e.ticker.toLowerCase() == ticker.toLowerCase());
|
||||
if (__currencies.isNotEmpty) {
|
||||
return __currencies.first.name
|
||||
.toLowerCase()
|
||||
.contains(text.toLowerCase()) ||
|
||||
ticker.toLowerCase().contains(text.toLowerCase());
|
||||
}
|
||||
return ticker.toLowerCase().contains(text.toLowerCase());
|
||||
})
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_searchController = TextEditingController();
|
||||
isFrom = widget.isFrom;
|
||||
|
||||
markets = [...widget.markets];
|
||||
if (isFrom) {
|
||||
markets.sort(
|
||||
(a, b) => a.from.toLowerCase().compareTo(b.from.toLowerCase()),
|
||||
);
|
||||
for (Coin coin in Coin.values.reversed) {
|
||||
int index = markets.indexWhere((element) =>
|
||||
element.from.toLowerCase() == coin.ticker.toLowerCase());
|
||||
if (index > 0) {
|
||||
final market = markets.removeAt(index);
|
||||
markets.insert(0, market);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
markets.sort(
|
||||
(a, b) => a.to.toLowerCase().compareTo(b.to.toLowerCase()),
|
||||
);
|
||||
for (Coin coin in Coin.values.reversed) {
|
||||
int index = markets.indexWhere(
|
||||
(element) => element.to.toLowerCase() == coin.ticker.toLowerCase());
|
||||
if (index > 0) {
|
||||
final market = markets.removeAt(index);
|
||||
markets.insert(0, market);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_markets = [...markets];
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
_searchFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 50));
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
"Choose a coin to exchange",
|
||||
style: STextStyles.pageTitleH2(context),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autofocus: isDesktop,
|
||||
autocorrect: !isDesktop,
|
||||
enableSuggestions: !isDesktop,
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: filter,
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search",
|
||||
_searchFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_searchController.text = "";
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
"Popular coins",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Flexible(
|
||||
child: Builder(builder: (context) {
|
||||
final items = _markets
|
||||
.where((e) => Coin.values
|
||||
.where((coin) =>
|
||||
coin.ticker.toLowerCase() ==
|
||||
(isFrom ? e.from.toLowerCase() : e.to.toLowerCase()))
|
||||
.isNotEmpty)
|
||||
.toList(growable: false);
|
||||
|
||||
return RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
primary: isDesktop ? false : null,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (builderContext, index) {
|
||||
final String ticker =
|
||||
isFrom ? items[index].from : items[index].to;
|
||||
|
||||
final tuple = _imageUrlAndNameFor(ticker);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(ticker);
|
||||
},
|
||||
child: RoundedWhiteContainer(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: SvgPicture.network(
|
||||
tuple.item1,
|
||||
width: 24,
|
||||
height: 24,
|
||||
placeholderBuilder: (_) =>
|
||||
const LoadingIndicator(),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
tuple.item2,
|
||||
style: STextStyles.largeMedium14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
ticker.toUpperCase(),
|
||||
style: STextStyles.smallMed12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
"All coins",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Flexible(
|
||||
child: RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
primary: isDesktop ? false : null,
|
||||
itemCount: _markets.length,
|
||||
itemBuilder: (builderContext, index) {
|
||||
final String ticker =
|
||||
isFrom ? _markets[index].from : _markets[index].to;
|
||||
|
||||
final tuple = _imageUrlAndNameFor(ticker);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(ticker);
|
||||
},
|
||||
child: RoundedWhiteContainer(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: SvgPicture.network(
|
||||
tuple.item1,
|
||||
width: 24,
|
||||
height: 24,
|
||||
placeholderBuilder: (_) =>
|
||||
const LoadingIndicator(),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
tuple.item2,
|
||||
style: STextStyles.largeMedium14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
ticker.toUpperCase(),
|
||||
style: STextStyles.smallMed12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,334 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/currency.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
import 'package:stackwallet/widgets/loading_indicator.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
import 'package:stackwallet/widgets/stack_text_field.dart';
|
||||
import 'package:stackwallet/widgets/textfield_icon_button.dart';
|
||||
|
||||
class FloatingRateCurrencySelectionView extends StatefulWidget {
|
||||
const FloatingRateCurrencySelectionView({
|
||||
Key? key,
|
||||
required this.currencies,
|
||||
}) : super(key: key);
|
||||
|
||||
final List<Currency> currencies;
|
||||
|
||||
@override
|
||||
State<FloatingRateCurrencySelectionView> createState() =>
|
||||
_FloatingRateCurrencySelectionViewState();
|
||||
}
|
||||
|
||||
class _FloatingRateCurrencySelectionViewState
|
||||
extends State<FloatingRateCurrencySelectionView> {
|
||||
late TextEditingController _searchController;
|
||||
final _searchFocusNode = FocusNode();
|
||||
|
||||
late final List<Currency> currencies;
|
||||
late List<Currency> _currencies;
|
||||
|
||||
void filter(String text) {
|
||||
setState(() {
|
||||
_currencies = [
|
||||
...currencies.where((e) =>
|
||||
e.name.toLowerCase().contains(text.toLowerCase()) ||
|
||||
e.ticker.toLowerCase().contains(text.toLowerCase()))
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_searchController = TextEditingController();
|
||||
|
||||
currencies = [...widget.currencies];
|
||||
currencies.sort(
|
||||
(a, b) => a.ticker.toLowerCase().compareTo(b.ticker.toLowerCase()));
|
||||
for (Coin coin in Coin.values.reversed) {
|
||||
int index = currencies.indexWhere((element) =>
|
||||
element.ticker.toLowerCase() == coin.ticker.toLowerCase());
|
||||
if (index > 0) {
|
||||
final currency = currencies.removeAt(index);
|
||||
currencies.insert(0, currency);
|
||||
}
|
||||
}
|
||||
|
||||
_currencies = [...currencies];
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_searchController.dispose();
|
||||
_searchFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isDesktop = Util.isDesktop;
|
||||
return ConditionalParent(
|
||||
condition: !isDesktop,
|
||||
builder: (child) {
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor:
|
||||
Theme.of(context).extension<StackColors>()!.background,
|
||||
appBar: AppBar(
|
||||
leading: AppBarBackButton(
|
||||
onPressed: () async {
|
||||
if (FocusScope.of(context).hasFocus) {
|
||||
FocusScope.of(context).unfocus();
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 50));
|
||||
}
|
||||
if (mounted) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
title: Text(
|
||||
"Choose a coin to exchange",
|
||||
style: STextStyles.pageTitleH2(context),
|
||||
),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
),
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: isDesktop ? MainAxisSize.min : MainAxisSize.max,
|
||||
children: [
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
child: TextField(
|
||||
autofocus: isDesktop,
|
||||
autocorrect: !isDesktop,
|
||||
enableSuggestions: !isDesktop,
|
||||
controller: _searchController,
|
||||
focusNode: _searchFocusNode,
|
||||
onChanged: filter,
|
||||
style: STextStyles.field(context),
|
||||
decoration: standardInputDecoration(
|
||||
"Search",
|
||||
_searchFocusNode,
|
||||
context,
|
||||
desktopMed: isDesktop,
|
||||
).copyWith(
|
||||
prefixIcon: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10,
|
||||
vertical: 16,
|
||||
),
|
||||
child: SvgPicture.asset(
|
||||
Assets.svg.search,
|
||||
width: 16,
|
||||
height: 16,
|
||||
),
|
||||
),
|
||||
suffixIcon: _searchController.text.isNotEmpty
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
children: [
|
||||
TextFieldIconButton(
|
||||
child: const XIcon(),
|
||||
onTap: () async {
|
||||
setState(() {
|
||||
_searchController.text = "";
|
||||
});
|
||||
filter("");
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Text(
|
||||
"Popular coins",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Flexible(
|
||||
child: Builder(builder: (context) {
|
||||
final items = _currencies
|
||||
.where((e) => Coin.values
|
||||
.where((coin) =>
|
||||
coin.ticker.toLowerCase() == e.ticker.toLowerCase())
|
||||
.isNotEmpty)
|
||||
.toList(growable: false);
|
||||
|
||||
return RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
primary: isDesktop ? false : null,
|
||||
itemCount: items.length,
|
||||
itemBuilder: (builderContext, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(items[index]);
|
||||
},
|
||||
child: RoundedWhiteContainer(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: SvgPicture.network(
|
||||
items[index].image,
|
||||
width: 24,
|
||||
height: 24,
|
||||
placeholderBuilder: (_) =>
|
||||
const LoadingIndicator(),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
items[index].name,
|
||||
style: STextStyles.largeMedium14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
items[index].ticker.toUpperCase(),
|
||||
style: STextStyles.smallMed12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
"All coins",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
Flexible(
|
||||
child: RoundedWhiteContainer(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
primary: isDesktop ? false : null,
|
||||
itemCount: _currencies.length,
|
||||
itemBuilder: (builderContext, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(_currencies[index]);
|
||||
},
|
||||
child: RoundedWhiteContainer(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: SvgPicture.network(
|
||||
_currencies[index].image,
|
||||
width: 24,
|
||||
height: 24,
|
||||
placeholderBuilder: (_) =>
|
||||
const LoadingIndicator(),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
_currencies[index].name,
|
||||
style: STextStyles.largeMedium14(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(
|
||||
_currencies[index].ticker.toUpperCase(),
|
||||
style: STextStyles.smallMed12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -8,6 +8,7 @@ import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
|
|||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_3_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
|
||||
import 'package:stackwallet/utilities/address_utils.dart';
|
||||
import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
|
@ -123,6 +124,10 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final supportsRefund =
|
||||
ref.watch(currentExchangeNameStateProvider.state).state !=
|
||||
MajesticBankExchange.exchangeName;
|
||||
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
|
@ -217,8 +222,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
setState(() {
|
||||
enableNext =
|
||||
_toController.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
(_refundController
|
||||
.text.isNotEmpty ||
|
||||
!supportsRefund);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -291,8 +297,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
(_refundController.text
|
||||
.isNotEmpty ||
|
||||
!supportsRefund);
|
||||
});
|
||||
},
|
||||
child: const XIcon(),
|
||||
|
@ -318,8 +325,10 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
enableNext = _toController
|
||||
.text
|
||||
.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
(_refundController
|
||||
.text
|
||||
.isNotEmpty ||
|
||||
!supportsRefund);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -367,8 +376,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
(_refundController.text
|
||||
.isNotEmpty ||
|
||||
!supportsRefund);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -396,8 +406,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
(_refundController.text
|
||||
.isNotEmpty ||
|
||||
!supportsRefund);
|
||||
});
|
||||
} else {
|
||||
_toController.text =
|
||||
|
@ -408,8 +419,9 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
(_refundController.text
|
||||
.isNotEmpty ||
|
||||
!supportsRefund);
|
||||
});
|
||||
}
|
||||
} on PlatformException catch (e, s) {
|
||||
|
@ -440,133 +452,230 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Refund Wallet (required)",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
if (isStackCoin(model.sendTicker))
|
||||
CustomTextButton(
|
||||
text: "Choose from Stack",
|
||||
onTap: () {
|
||||
try {
|
||||
final coin =
|
||||
coinFromTickerCaseInsensitive(
|
||||
model.sendTicker,
|
||||
);
|
||||
Navigator.of(context)
|
||||
.pushNamed(
|
||||
ChooseFromStackView.routeName,
|
||||
arguments: coin,
|
||||
)
|
||||
.then((value) async {
|
||||
if (value is String) {
|
||||
final manager = ref
|
||||
.read(
|
||||
walletsChangeNotifierProvider)
|
||||
.getManager(value);
|
||||
if (supportsRefund)
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Refund Wallet (required)",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
if (isStackCoin(model.sendTicker))
|
||||
CustomTextButton(
|
||||
text: "Choose from Stack",
|
||||
onTap: () {
|
||||
try {
|
||||
final coin =
|
||||
coinFromTickerCaseInsensitive(
|
||||
model.sendTicker,
|
||||
);
|
||||
Navigator.of(context)
|
||||
.pushNamed(
|
||||
ChooseFromStackView.routeName,
|
||||
arguments: coin,
|
||||
)
|
||||
.then((value) async {
|
||||
if (value is String) {
|
||||
final manager = ref
|
||||
.read(
|
||||
walletsChangeNotifierProvider)
|
||||
.getManager(value);
|
||||
|
||||
_refundController.text =
|
||||
manager.walletName;
|
||||
model.refundAddress = await manager
|
||||
.currentReceivingAddress;
|
||||
}
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController.text.isNotEmpty;
|
||||
_refundController.text =
|
||||
manager.walletName;
|
||||
model.refundAddress = await manager
|
||||
.currentReceivingAddress;
|
||||
}
|
||||
setState(() {
|
||||
enableNext =
|
||||
_toController.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
.log("$e\n$s", level: LogLevel.Info);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e\n$s",
|
||||
level: LogLevel.Info);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key(
|
||||
"refundExchangeStep2ViewAddressFieldKey"),
|
||||
controller: _refundController,
|
||||
readOnly: false,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
// inputFormatters: <TextInputFormatter>[
|
||||
// FilteringTextInputFormatter.allow(RegExp("[a-zA-Z0-9]{34}")),
|
||||
// ],
|
||||
toolbarOptions: const ToolbarOptions(
|
||||
copy: false,
|
||||
cut: false,
|
||||
paste: true,
|
||||
selectAll: false,
|
||||
if (supportsRefund)
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
if (supportsRefund)
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(
|
||||
Constants.size.circularBorderRadius,
|
||||
),
|
||||
focusNode: _refundFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
onChanged: (value) {
|
||||
setState(() {});
|
||||
},
|
||||
decoration: standardInputDecoration(
|
||||
"Enter ${model.sendTicker.toUpperCase()} refund address",
|
||||
_refundFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
top: 6,
|
||||
bottom: 8,
|
||||
right: 5,
|
||||
child: TextField(
|
||||
key: const Key(
|
||||
"refundExchangeStep2ViewAddressFieldKey"),
|
||||
controller: _refundController,
|
||||
readOnly: false,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
// inputFormatters: <TextInputFormatter>[
|
||||
// FilteringTextInputFormatter.allow(RegExp("[a-zA-Z0-9]{34}")),
|
||||
// ],
|
||||
toolbarOptions: const ToolbarOptions(
|
||||
copy: false,
|
||||
cut: false,
|
||||
paste: true,
|
||||
selectAll: false,
|
||||
),
|
||||
suffixIcon: Padding(
|
||||
padding: _refundController.text.isEmpty
|
||||
? const EdgeInsets.only(right: 16)
|
||||
: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_refundController.text.isNotEmpty
|
||||
? TextFieldIconButton(
|
||||
key: const Key(
|
||||
"sendViewClearAddressFieldButtonKey"),
|
||||
onTap: () {
|
||||
_refundController.text = "";
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
focusNode: _refundFocusNode,
|
||||
style: STextStyles.field(context),
|
||||
onChanged: (value) {
|
||||
setState(() {});
|
||||
},
|
||||
decoration: standardInputDecoration(
|
||||
"Enter ${model.sendTicker.toUpperCase()} refund address",
|
||||
_refundFocusNode,
|
||||
context,
|
||||
).copyWith(
|
||||
contentPadding: const EdgeInsets.only(
|
||||
left: 16,
|
||||
top: 6,
|
||||
bottom: 8,
|
||||
right: 5,
|
||||
),
|
||||
suffixIcon: Padding(
|
||||
padding: _refundController.text.isEmpty
|
||||
? const EdgeInsets.only(right: 16)
|
||||
: const EdgeInsets.only(right: 0),
|
||||
child: UnconstrainedBox(
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
_refundController.text.isNotEmpty
|
||||
? TextFieldIconButton(
|
||||
key: const Key(
|
||||
"sendViewClearAddressFieldButtonKey"),
|
||||
onTap: () {
|
||||
_refundController.text = "";
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text
|
||||
.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
},
|
||||
child: const XIcon(),
|
||||
)
|
||||
: TextFieldIconButton(
|
||||
key: const Key(
|
||||
"sendViewPasteAddressFieldButtonKey"),
|
||||
onTap: () async {
|
||||
final ClipboardData? data =
|
||||
await clipboard.getData(
|
||||
Clipboard
|
||||
.kTextPlain);
|
||||
if (data?.text != null &&
|
||||
data!
|
||||
.text!.isNotEmpty) {
|
||||
final content =
|
||||
data.text!.trim();
|
||||
|
||||
_refundController.text =
|
||||
content;
|
||||
model.refundAddress =
|
||||
_refundController
|
||||
.text;
|
||||
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text
|
||||
.isNotEmpty &&
|
||||
_refundController
|
||||
.text
|
||||
.isNotEmpty;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: _refundController
|
||||
.text.isEmpty
|
||||
? const ClipboardIcon()
|
||||
: const XIcon(),
|
||||
),
|
||||
if (_refundController.text.isEmpty)
|
||||
TextFieldIconButton(
|
||||
key: const Key(
|
||||
"sendViewAddressBookButtonKey"),
|
||||
onTap: () {
|
||||
ref
|
||||
.read(
|
||||
exchangeFlowIsActiveStateProvider
|
||||
.state)
|
||||
.state = true;
|
||||
Navigator.of(context)
|
||||
.pushNamed(
|
||||
AddressBookView.routeName,
|
||||
)
|
||||
.then((_) {
|
||||
ref
|
||||
.read(
|
||||
exchangeFlowIsActiveStateProvider
|
||||
.state)
|
||||
.state = false;
|
||||
final address = ref
|
||||
.read(
|
||||
exchangeFromAddressBookAddressStateProvider
|
||||
.state)
|
||||
.state;
|
||||
if (address.isNotEmpty) {
|
||||
_refundController.text =
|
||||
address;
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
}
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
},
|
||||
child: const XIcon(),
|
||||
)
|
||||
: TextFieldIconButton(
|
||||
key: const Key(
|
||||
"sendViewPasteAddressFieldButtonKey"),
|
||||
onTap: () async {
|
||||
final ClipboardData? data =
|
||||
await clipboard.getData(
|
||||
Clipboard.kTextPlain);
|
||||
if (data?.text != null &&
|
||||
data!.text!.isNotEmpty) {
|
||||
final content =
|
||||
data.text!.trim();
|
||||
});
|
||||
},
|
||||
child: const AddressBookIcon(),
|
||||
),
|
||||
if (_refundController.text.isEmpty)
|
||||
TextFieldIconButton(
|
||||
key: const Key(
|
||||
"sendViewScanQrButtonKey"),
|
||||
onTap: () async {
|
||||
try {
|
||||
final qrResult =
|
||||
await scanner.scan();
|
||||
|
||||
final results =
|
||||
AddressUtils.parseUri(
|
||||
qrResult.rawContent);
|
||||
if (results.isNotEmpty) {
|
||||
// auto fill address
|
||||
_refundController.text =
|
||||
content;
|
||||
results["address"] ??
|
||||
"";
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text
|
||||
.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
} else {
|
||||
_refundController.text =
|
||||
qrResult.rawContent;
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
|
@ -578,116 +687,33 @@ class _Step2ViewState extends ConsumerState<Step2View> {
|
|||
.text.isNotEmpty;
|
||||
});
|
||||
}
|
||||
},
|
||||
child: _refundController
|
||||
.text.isEmpty
|
||||
? const ClipboardIcon()
|
||||
: const XIcon(),
|
||||
),
|
||||
if (_refundController.text.isEmpty)
|
||||
TextFieldIconButton(
|
||||
key: const Key(
|
||||
"sendViewAddressBookButtonKey"),
|
||||
onTap: () {
|
||||
ref
|
||||
.read(
|
||||
exchangeFlowIsActiveStateProvider
|
||||
.state)
|
||||
.state = true;
|
||||
Navigator.of(context)
|
||||
.pushNamed(
|
||||
AddressBookView.routeName,
|
||||
)
|
||||
.then((_) {
|
||||
ref
|
||||
.read(
|
||||
exchangeFlowIsActiveStateProvider
|
||||
.state)
|
||||
.state = false;
|
||||
final address = ref
|
||||
.read(
|
||||
exchangeFromAddressBookAddressStateProvider
|
||||
.state)
|
||||
.state;
|
||||
if (address.isNotEmpty) {
|
||||
_refundController.text =
|
||||
address;
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
} on PlatformException catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
}
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
});
|
||||
},
|
||||
child: const AddressBookIcon(),
|
||||
),
|
||||
if (_refundController.text.isEmpty)
|
||||
TextFieldIconButton(
|
||||
key: const Key(
|
||||
"sendViewScanQrButtonKey"),
|
||||
onTap: () async {
|
||||
try {
|
||||
final qrResult =
|
||||
await scanner.scan();
|
||||
|
||||
final results =
|
||||
AddressUtils.parseUri(
|
||||
qrResult.rawContent);
|
||||
if (results.isNotEmpty) {
|
||||
// auto fill address
|
||||
_refundController.text =
|
||||
results["address"] ?? "";
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
} else {
|
||||
_refundController.text =
|
||||
qrResult.rawContent;
|
||||
model.refundAddress =
|
||||
_refundController.text;
|
||||
|
||||
setState(() {
|
||||
enableNext = _toController
|
||||
.text.isNotEmpty &&
|
||||
_refundController
|
||||
.text.isNotEmpty;
|
||||
});
|
||||
}
|
||||
} on PlatformException catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Failed to get camera permissions while trying to scan qr code in SendView: $e\n$s",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: const QrCodeIcon(),
|
||||
),
|
||||
],
|
||||
},
|
||||
child: const QrCodeIcon(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
child: Text(
|
||||
"In case something goes wrong during the exchange, we might need a refund address so we can return your coins back to you.",
|
||||
style: STextStyles.label(context),
|
||||
if (supportsRefund)
|
||||
const SizedBox(
|
||||
height: 6,
|
||||
),
|
||||
if (supportsRefund)
|
||||
RoundedWhiteContainer(
|
||||
child: Text(
|
||||
"In case something goes wrong during the exchange, we might need a refund address so we can return your coins back to you.",
|
||||
style: STextStyles.label(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Row(
|
||||
children: [
|
||||
|
|
|
@ -7,9 +7,11 @@ import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
|||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_4_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/step_row.dart';
|
||||
import 'package:stackwallet/providers/exchange/current_exchange_name_state_provider.dart';
|
||||
import 'package:stackwallet/providers/exchange/exchange_provider.dart';
|
||||
import 'package:stackwallet/providers/global/trades_service_provider.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
||||
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
|
||||
import 'package:stackwallet/services/notifications_api.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
|
@ -51,6 +53,10 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final supportsRefund =
|
||||
ref.watch(currentExchangeNameStateProvider.state).state !=
|
||||
MajesticBankExchange.exchangeName;
|
||||
|
||||
return Background(
|
||||
child: Scaffold(
|
||||
backgroundColor: Theme.of(context).extension<StackColors>()!.background,
|
||||
|
@ -174,27 +180,29 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
RoundedWhiteContainer(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Refund ${model.sendTicker.toUpperCase()} address",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
model.refundAddress!,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
)
|
||||
],
|
||||
if (supportsRefund)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (supportsRefund)
|
||||
RoundedWhiteContainer(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
"Refund ${model.sendTicker.toUpperCase()} address",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
model.refundAddress!,
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
@ -259,8 +267,9 @@ class _Step3ViewState extends ConsumerState<Step3View> {
|
|||
addressTo:
|
||||
model.recipientAddress!,
|
||||
extraId: null,
|
||||
addressRefund:
|
||||
model.refundAddress!,
|
||||
addressRefund: supportsRefund
|
||||
? model.refundAddress!
|
||||
: "",
|
||||
refundExtraId: "",
|
||||
rateId: model.rateId,
|
||||
reversed: model.reversed,
|
||||
|
|
|
@ -2,11 +2,12 @@ import 'package:decimal/decimal.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.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/providers/providers.dart';
|
||||
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
||||
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
|
@ -59,10 +60,11 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
ChangeNowExchange.exchangeName) {
|
||||
ref.read(currentExchangeNameStateProvider.state).state =
|
||||
ChangeNowExchange.exchangeName;
|
||||
ref.read(exchangeFormStateProvider).exchange =
|
||||
Exchange.fromName(ref
|
||||
.read(currentExchangeNameStateProvider.state)
|
||||
.state);
|
||||
ref.read(exchangeFormStateProvider).updateExchange(
|
||||
exchange: ref.read(exchangeProvider),
|
||||
shouldUpdateData: true,
|
||||
shouldNotifyListeners: true,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
|
@ -88,18 +90,22 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
groupValue: ref
|
||||
.watch(currentExchangeNameStateProvider.state)
|
||||
.state,
|
||||
onChanged: (value) {
|
||||
if (value is String) {
|
||||
ref
|
||||
.read(
|
||||
currentExchangeNameStateProvider.state)
|
||||
.state = value;
|
||||
ref.read(exchangeFormStateProvider).exchange =
|
||||
Exchange.fromName(ref
|
||||
.read(currentExchangeNameStateProvider
|
||||
.state)
|
||||
.state);
|
||||
}
|
||||
onChanged: (_) {
|
||||
// if (value is String) {
|
||||
// ref
|
||||
// .read(
|
||||
// currentExchangeNameStateProvider.state)
|
||||
// .state = value;
|
||||
// ref
|
||||
// .read(exchangeFormStateProvider(ref
|
||||
// .read(prefsChangeNotifierProvider)
|
||||
// .exchangeRateType))
|
||||
// .exchange =
|
||||
// Exchange.fromName(ref
|
||||
// .read(currentExchangeNameStateProvider
|
||||
// .state)
|
||||
// .state);
|
||||
// }
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -109,10 +115,14 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 5.0),
|
||||
child: SvgPicture.asset(
|
||||
Assets.exchange.changeNow,
|
||||
child: SizedBox(
|
||||
width: isDesktop ? 32 : 24,
|
||||
height: isDesktop ? 32 : 24,
|
||||
child: SvgPicture.asset(
|
||||
Assets.exchange.changeNow,
|
||||
width: isDesktop ? 32 : 24,
|
||||
height: isDesktop ? 32 : 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
|
@ -139,7 +149,7 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
fromAmount != null &&
|
||||
fromAmount! > Decimal.zero)
|
||||
FutureBuilder(
|
||||
future: ChangeNowExchange().getEstimate(
|
||||
future: ChangeNowExchange.instance.getEstimate(
|
||||
from!,
|
||||
to!,
|
||||
reversed ? toAmount! : fromAmount!,
|
||||
|
@ -194,6 +204,254 @@ class ExchangeProviderOptions extends ConsumerWidget {
|
|||
.textSubtitle1,
|
||||
),
|
||||
);
|
||||
} else if (snapshot.data?.exception
|
||||
is PairUnavailableException) {
|
||||
return Text(
|
||||
"Unsupported pair",
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"$runtimeType failed to fetch rate for ChangeNOW: ${snapshot.data}",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
return Text(
|
||||
"Failed to fetch rate",
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: const [
|
||||
"Loading",
|
||||
"Loading.",
|
||||
"Loading..",
|
||||
"Loading...",
|
||||
],
|
||||
style: STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
if (!(from != null &&
|
||||
to != null &&
|
||||
toAmount != null &&
|
||||
toAmount! > Decimal.zero &&
|
||||
fromAmount != null &&
|
||||
fromAmount! > Decimal.zero))
|
||||
Text(
|
||||
"n/a",
|
||||
style: STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isDesktop)
|
||||
Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).extension<StackColors>()!.background,
|
||||
),
|
||||
if (!isDesktop)
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
ConditionalParent(
|
||||
condition: isDesktop,
|
||||
builder: (child) => MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
child: child,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (ref.read(currentExchangeNameStateProvider.state).state !=
|
||||
MajesticBankExchange.exchangeName) {
|
||||
ref.read(currentExchangeNameStateProvider.state).state =
|
||||
MajesticBankExchange.exchangeName;
|
||||
ref.read(exchangeFormStateProvider).updateExchange(
|
||||
exchange: ref.read(exchangeProvider),
|
||||
shouldUpdateData: true,
|
||||
shouldNotifyListeners: true,
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Padding(
|
||||
padding: isDesktop
|
||||
? const EdgeInsets.all(16)
|
||||
: const EdgeInsets.all(0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: Padding(
|
||||
padding:
|
||||
EdgeInsets.only(top: isDesktop ? 20.0 : 15.0),
|
||||
child: Radio(
|
||||
activeColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.radioButtonIconEnabled,
|
||||
value: MajesticBankExchange.exchangeName,
|
||||
groupValue: ref
|
||||
.watch(currentExchangeNameStateProvider.state)
|
||||
.state,
|
||||
onChanged: (_) {
|
||||
// if (value is String) {
|
||||
// ref
|
||||
// .read(
|
||||
// currentExchangeNameStateProvider.state)
|
||||
// .state = value;
|
||||
// ref
|
||||
// .read(exchangeFormStateProvider(ref
|
||||
// .read(prefsChangeNotifierProvider)
|
||||
// .exchangeRateType))
|
||||
// .exchange =
|
||||
// Exchange.fromName(ref
|
||||
// .read(currentExchangeNameStateProvider
|
||||
// .state)
|
||||
// .state);
|
||||
// }
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 14,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 5.0),
|
||||
child: SizedBox(
|
||||
width: isDesktop ? 32 : 24,
|
||||
height: isDesktop ? 32 : 24,
|
||||
child: SvgPicture.asset(
|
||||
Assets.exchange.majesticBankBlue,
|
||||
width: isDesktop ? 32 : 24,
|
||||
height: isDesktop ? 32 : 24,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
MajesticBankExchange.exchangeName,
|
||||
style: STextStyles.titleBold12(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textDark2,
|
||||
),
|
||||
),
|
||||
if (from != null &&
|
||||
to != null &&
|
||||
toAmount != null &&
|
||||
toAmount! > Decimal.zero &&
|
||||
fromAmount != null &&
|
||||
fromAmount! > Decimal.zero)
|
||||
FutureBuilder(
|
||||
future:
|
||||
MajesticBankExchange.instance.getEstimate(
|
||||
from!,
|
||||
to!,
|
||||
reversed ? toAmount! : fromAmount!,
|
||||
fixedRate,
|
||||
reversed,
|
||||
),
|
||||
builder: (context,
|
||||
AsyncSnapshot<ExchangeResponse<Estimate>>
|
||||
snapshot) {
|
||||
if (snapshot.connectionState ==
|
||||
ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
final estimate = snapshot.data?.value;
|
||||
if (estimate != null) {
|
||||
Decimal rate;
|
||||
if (estimate.reversed) {
|
||||
rate = (toAmount! /
|
||||
estimate.estimatedAmount)
|
||||
.toDecimal(
|
||||
scaleOnInfinitePrecision: 12);
|
||||
} else {
|
||||
rate = (estimate.estimatedAmount /
|
||||
fromAmount!)
|
||||
.toDecimal(
|
||||
scaleOnInfinitePrecision: 12);
|
||||
}
|
||||
Coin coin;
|
||||
try {
|
||||
coin =
|
||||
coinFromTickerCaseInsensitive(to!);
|
||||
} catch (_) {
|
||||
coin = Coin.bitcoin;
|
||||
}
|
||||
|
||||
return Text(
|
||||
"1 ${from!.toUpperCase()} ~ ${Format.localizedStringAsFixed(
|
||||
value: rate,
|
||||
locale: ref.watch(
|
||||
localeServiceChangeNotifierProvider
|
||||
.select(
|
||||
(value) => value.locale),
|
||||
),
|
||||
decimalPlaces:
|
||||
Constants.decimalPlacesForCoin(
|
||||
coin),
|
||||
)} ${to!.toUpperCase()}",
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
);
|
||||
} else if (snapshot.data?.exception
|
||||
is PairUnavailableException) {
|
||||
return Text(
|
||||
"Unsupported pair",
|
||||
style:
|
||||
STextStyles.itemSubtitle12(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textSubtitle1,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"$runtimeType failed to fetch rate for ChangeNOW: ${snapshot.data}",
|
||||
|
|
|
@ -21,21 +21,21 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
debugPrint("BUILD: $runtimeType");
|
||||
final isDesktop = Util.isDesktop;
|
||||
|
||||
final estimated = ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.exchangeRateType)) ==
|
||||
ExchangeRateType.estimated;
|
||||
|
||||
return Toggle(
|
||||
onValueChanged: (value) {
|
||||
if (!estimated) {
|
||||
if (value) {
|
||||
ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
ExchangeRateType.fixed;
|
||||
onChanged?.call(ExchangeRateType.fixed);
|
||||
} else {
|
||||
ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
ExchangeRateType.estimated;
|
||||
onChanged?.call(ExchangeRateType.estimated);
|
||||
} else {
|
||||
onChanged?.call(ExchangeRateType.fixed);
|
||||
}
|
||||
},
|
||||
isOn: !estimated,
|
||||
isOn: ref.watch(prefsChangeNotifierProvider
|
||||
.select((value) => value.exchangeRateType)) ==
|
||||
ExchangeRateType.fixed,
|
||||
onColor: isDesktop
|
||||
? Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
|
@ -56,178 +56,5 @@ class RateTypeToggle extends ConsumerWidget {
|
|||
offIcon: Assets.svg.lock,
|
||||
offText: "Fixed rate",
|
||||
);
|
||||
//
|
||||
// return RoundedContainer(
|
||||
// padding: const EdgeInsets.all(0),
|
||||
// color: isDesktop
|
||||
// ? Theme.of(context).extension<StackColors>()!.buttonBackSecondary
|
||||
// : Theme.of(context).extension<StackColors>()!.popupBG,
|
||||
// child: Row(
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: ConditionalParent(
|
||||
// condition: isDesktop,
|
||||
// builder: (child) => MouseRegion(
|
||||
// cursor: estimated
|
||||
// ? SystemMouseCursors.basic
|
||||
// : SystemMouseCursors.click,
|
||||
// child: child,
|
||||
// ),
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// if (!estimated) {
|
||||
// ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
// ExchangeRateType.estimated;
|
||||
// onChanged?.call(ExchangeRateType.estimated);
|
||||
// }
|
||||
// },
|
||||
// child: RoundedContainer(
|
||||
// padding: isDesktop
|
||||
// ? const EdgeInsets.all(17)
|
||||
// : const EdgeInsets.all(12),
|
||||
// color: estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textFieldDefaultBG
|
||||
// : Colors.transparent,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// SvgPicture.asset(
|
||||
// Assets.svg.lockOpen,
|
||||
// width: 12,
|
||||
// height: 14,
|
||||
// color: isDesktop
|
||||
// ? estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .accentColorBlue
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .buttonTextSecondary
|
||||
// : estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textDark
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textSubtitle1,
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// width: 5,
|
||||
// ),
|
||||
// Text(
|
||||
// "Estimate rate",
|
||||
// style: isDesktop
|
||||
// ? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
// .copyWith(
|
||||
// color: estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .accentColorBlue
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .buttonTextSecondary,
|
||||
// )
|
||||
// : STextStyles.smallMed12(context).copyWith(
|
||||
// color: estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textDark
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textSubtitle1,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: ConditionalParent(
|
||||
// condition: isDesktop,
|
||||
// builder: (child) => MouseRegion(
|
||||
// cursor: !estimated
|
||||
// ? SystemMouseCursors.basic
|
||||
// : SystemMouseCursors.click,
|
||||
// child: child,
|
||||
// ),
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// if (estimated) {
|
||||
// ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
// ExchangeRateType.fixed;
|
||||
// onChanged?.call(ExchangeRateType.fixed);
|
||||
// }
|
||||
// },
|
||||
// child: RoundedContainer(
|
||||
// padding: isDesktop
|
||||
// ? const EdgeInsets.all(17)
|
||||
// : const EdgeInsets.all(12),
|
||||
// color: !estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textFieldDefaultBG
|
||||
// : Colors.transparent,
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.center,
|
||||
// children: [
|
||||
// SvgPicture.asset(
|
||||
// Assets.svg.lock,
|
||||
// width: 12,
|
||||
// height: 14,
|
||||
// color: isDesktop
|
||||
// ? !estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .accentColorBlue
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .buttonTextSecondary
|
||||
// : !estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textDark
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textSubtitle1,
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// width: 5,
|
||||
// ),
|
||||
// Text(
|
||||
// "Fixed rate",
|
||||
// style: isDesktop
|
||||
// ? STextStyles.desktopTextExtraExtraSmall(context)
|
||||
// .copyWith(
|
||||
// color: !estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .accentColorBlue
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .buttonTextSecondary,
|
||||
// )
|
||||
// : STextStyles.smallMed12(context).copyWith(
|
||||
// color: !estimated
|
||||
// ? Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textDark
|
||||
// : Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textSubtitle1,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/buy_view/buy_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_loading_overlay.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_view.dart';
|
||||
import 'package:stackwallet/pages/home_view/sub_widgets/home_view_button_bar.dart';
|
||||
import 'package:stackwallet/pages/notification_views/notifications_view.dart';
|
||||
|
@ -12,13 +11,10 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/global_set
|
|||
import 'package:stackwallet/pages/settings_views/global_settings_view/hidden_settings.dart';
|
||||
import 'package:stackwallet/pages/wallets_view/wallets_view.dart';
|
||||
import 'package:stackwallet/providers/global/notifications_provider.dart';
|
||||
import 'package:stackwallet/providers/global/prefs_provider.dart';
|
||||
import 'package:stackwallet/providers/ui/home_view_index_provider.dart';
|
||||
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
|
@ -45,7 +41,6 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
|
||||
bool _exitEnabled = false;
|
||||
|
||||
final _exchangeDataLoadingService = ExchangeDataLoadingService();
|
||||
// final _buyDataLoadingService = BuyDataLoadingService();
|
||||
|
||||
Future<bool> _onWillPop() async {
|
||||
|
@ -84,16 +79,6 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
return _exitEnabled;
|
||||
}
|
||||
|
||||
void _loadCNData() {
|
||||
// unawaited future
|
||||
if (ref.read(prefsChangeNotifierProvider).externalCalls) {
|
||||
_exchangeDataLoadingService.loadAll(ref);
|
||||
} else {
|
||||
Logging.instance.log("User does not want to use external calls",
|
||||
level: LogLevel.Info);
|
||||
}
|
||||
}
|
||||
|
||||
// void _loadSimplexData() {
|
||||
// // unawaited future
|
||||
// if (ref.read(prefsChangeNotifierProvider).externalCalls) {
|
||||
|
@ -123,9 +108,9 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
Stack(
|
||||
children: [
|
||||
const ExchangeView(),
|
||||
ExchangeLoadingOverlayView(
|
||||
unawaitedLoad: _loadCNData,
|
||||
),
|
||||
// ExchangeLoadingOverlayView(
|
||||
// unawaitedLoad: _loadCNData,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
if (Constants.enableBuy)
|
||||
|
@ -331,9 +316,9 @@ class _HomeViewState extends ConsumerState<HomeView> {
|
|||
_ref.listen(homeViewPageIndexStateProvider,
|
||||
(previous, next) {
|
||||
if (next is int && next >= 0 && next <= 2) {
|
||||
if (next == 1) {
|
||||
_exchangeDataLoadingService.loadAll(ref);
|
||||
}
|
||||
// if (next == 1) {
|
||||
// _exchangeDataLoadingService.loadAll(ref);
|
||||
// }
|
||||
// if (next == 2) {
|
||||
// _buyDataLoadingService.loadAll(ref);
|
||||
// }
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_data_loading_service.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/stack_dialog.dart';
|
||||
|
||||
class HomeViewButtonBar extends ConsumerStatefulWidget {
|
||||
const HomeViewButtonBar({Key? key}) : super(key: key);
|
||||
|
@ -19,16 +17,16 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
|
|||
|
||||
@override
|
||||
void initState() {
|
||||
ref.read(exchangeFormStateProvider).setOnError(
|
||||
onError: (String message) => showDialog<dynamic>(
|
||||
context: context,
|
||||
barrierDismissible: true,
|
||||
builder: (_) => StackDialog(
|
||||
title: "Exchange API Call Failed",
|
||||
message: message,
|
||||
),
|
||||
),
|
||||
);
|
||||
// ref.read(exchangeFormStateProvider).setOnError(
|
||||
// onError: (String message) => showDialog<dynamic>(
|
||||
// context: context,
|
||||
// barrierDismissible: true,
|
||||
// builder: (_) => StackDialog(
|
||||
// title: "Exchange API Call Failed",
|
||||
// message: message,
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
super.initState();
|
||||
}
|
||||
|
||||
|
@ -106,7 +104,7 @@ class _HomeViewButtonBarState extends ConsumerState<HomeViewButtonBar> {
|
|||
// DateTime now = DateTime.now();
|
||||
// if (ref.read(prefsChangeNotifierProvider).externalCalls) {
|
||||
// print("loading?");
|
||||
await ExchangeDataLoadingService().loadAll(ref);
|
||||
// await ExchangeDataLoadingService().loadAll(ref);
|
||||
// }
|
||||
// if (now.difference(_lastRefreshed) > _refreshInterval) {
|
||||
// await ExchangeDataLoadingService().loadAll(ref);
|
||||
|
|
|
@ -5,14 +5,13 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/providers/global/debug_service_provider.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_api.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/background.dart';
|
||||
import 'package:stackwallet/widgets/rounded_white_container.dart';
|
||||
|
||||
import '../../../services/exchange/majestic_bank/majestic_bank_api.dart';
|
||||
|
||||
class HiddenSettings extends StatelessWidget {
|
||||
const HiddenSettings({Key? key}) : super(key: key);
|
||||
|
||||
|
@ -157,8 +156,8 @@ class HiddenSettings extends StatelessWidget {
|
|||
Consumer(builder: (_, ref, __) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
final x =
|
||||
await MajesticBankAPI.instance.getLimits();
|
||||
final x = await MajesticBankAPI.instance
|
||||
.getLimit(fromCurrency: 'btc');
|
||||
print(x);
|
||||
},
|
||||
child: RoundedWhiteContainer(
|
||||
|
|
|
@ -230,8 +230,13 @@ class _StackPrivacyCalls extends ConsumerState<StackPrivacyCalls> {
|
|||
value: isEasy)
|
||||
.then((_) {
|
||||
if (isEasy) {
|
||||
unawaited(ExchangeDataLoadingService()
|
||||
.loadAll(ref));
|
||||
unawaited(
|
||||
ExchangeDataLoadingService.instance
|
||||
.init()
|
||||
.then((_) => ExchangeDataLoadingService
|
||||
.instance
|
||||
.loadAll()),
|
||||
);
|
||||
// unawaited(
|
||||
// BuyDataLoadingService().loadAll(ref));
|
||||
ref
|
||||
|
|
|
@ -7,7 +7,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/notifications/show_flush_bar.dart';
|
||||
import 'package:stackwallet/pages/buy_view/buy_in_wallet_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/wallet_initiated_exchange_view.dart';
|
||||
import 'package:stackwallet/pages/home_view/home_view.dart';
|
||||
import 'package:stackwallet/pages/notification_views/notifications_view.dart';
|
||||
|
@ -30,14 +29,12 @@ import 'package:stackwallet/services/coins/manager.dart';
|
|||
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
|
||||
import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart';
|
||||
import 'package:stackwallet/services/event_bus/global_event_bus.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/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
import 'package:stackwallet/widgets/background.dart';
|
||||
|
@ -80,8 +77,6 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
late StreamSubscription<dynamic> _syncStatusSubscription;
|
||||
late StreamSubscription<dynamic> _nodeStatusSubscription;
|
||||
|
||||
final _cnLoadingService = ExchangeDataLoadingService();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
walletId = widget.walletId;
|
||||
|
@ -231,7 +226,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
}
|
||||
|
||||
void _onExchangePressed(BuildContext context) async {
|
||||
unawaited(_cnLoadingService.loadAll(ref));
|
||||
// too expensive
|
||||
// unawaited(ExchangeDataLoadingService.instance.loadAll(ref));
|
||||
|
||||
final coin = ref.read(managerProvider).coin;
|
||||
|
||||
|
@ -250,35 +246,33 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
ref.read(currentExchangeNameStateProvider.state).state =
|
||||
ChangeNowExchange.exchangeName;
|
||||
final walletId = ref.read(managerProvider).walletId;
|
||||
ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
ExchangeRateType.estimated;
|
||||
|
||||
ref.read(exchangeFormStateProvider).exchange = ref.read(exchangeProvider);
|
||||
ref.read(exchangeFormStateProvider).exchangeType =
|
||||
ExchangeRateType.estimated;
|
||||
|
||||
final currencies = ref
|
||||
.read(availableChangeNowCurrenciesProvider)
|
||||
.currencies
|
||||
.where((element) =>
|
||||
element.ticker.toLowerCase() == coin.ticker.toLowerCase());
|
||||
|
||||
if (currencies.isNotEmpty) {
|
||||
ref.read(exchangeFormStateProvider).setCurrencies(
|
||||
currencies.first,
|
||||
ref
|
||||
.read(availableChangeNowCurrenciesProvider)
|
||||
.currencies
|
||||
.firstWhere(
|
||||
(element) =>
|
||||
element.ticker.toLowerCase() !=
|
||||
coin.ticker.toLowerCase(),
|
||||
),
|
||||
);
|
||||
}
|
||||
// ref.read(currentExchangeNameStateProvider.state).state =
|
||||
// ChangeNowExchange.exchangeName;
|
||||
// final walletId = ref.read(managerProvider).walletId;
|
||||
// ref.read(prefsChangeNotifierProvider).exchangeRateType =
|
||||
// ExchangeRateType.estimated;
|
||||
//
|
||||
// final currencies = ref
|
||||
// .read(availableChangeNowCurrenciesProvider)
|
||||
// .currencies
|
||||
// .where((element) =>
|
||||
// element.ticker.toLowerCase() == coin.ticker.toLowerCase());
|
||||
//
|
||||
// if (currencies.isNotEmpty) {
|
||||
// ref
|
||||
// .read(exchangeFormStateProvider(ExchangeRateType.estimated))
|
||||
// .setCurrencies(
|
||||
// currencies.first,
|
||||
// ref
|
||||
// .read(availableChangeNowCurrenciesProvider)
|
||||
// .currencies
|
||||
// .firstWhere(
|
||||
// (element) =>
|
||||
// element.ticker.toLowerCase() !=
|
||||
// coin.ticker.toLowerCase(),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
if (mounted) {
|
||||
unawaited(
|
||||
|
@ -311,7 +305,7 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
);
|
||||
final firoWallet = ref.read(managerProvider).wallet as FiroWallet;
|
||||
|
||||
final publicBalance = await firoWallet.availablePublicBalance();
|
||||
final publicBalance = firoWallet.availablePublicBalance();
|
||||
if (publicBalance <= Decimal.zero) {
|
||||
shouldPop = true;
|
||||
if (mounted) {
|
||||
|
@ -363,12 +357,13 @@ class _WalletViewState extends ConsumerState<WalletView> {
|
|||
|
||||
void _loadCNData() {
|
||||
// unawaited future
|
||||
if (ref.read(prefsChangeNotifierProvider).externalCalls) {
|
||||
_cnLoadingService.loadAll(ref, coin: ref.read(managerProvider).coin);
|
||||
} else {
|
||||
Logging.instance.log("User does not want to use external calls",
|
||||
level: LogLevel.Info);
|
||||
}
|
||||
// if (ref.read(prefsChangeNotifierProvider).externalCalls) {
|
||||
ExchangeDataLoadingService.instance.loadAll();
|
||||
// .loadAll(ref, coin: ref.read(managerProvider).coin);
|
||||
// } else {
|
||||
// Logging.instance.log("User does not want to use external calls",
|
||||
// level: LogLevel.Info);
|
||||
// }
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -169,7 +169,9 @@ class _StackPrivacyDialog extends ConsumerState<StackPrivacyDialog> {
|
|||
value: isEasy)
|
||||
.then((_) {
|
||||
if (isEasy) {
|
||||
unawaited(ExchangeDataLoadingService().loadAll(ref));
|
||||
unawaited(
|
||||
ExchangeDataLoadingService.instance.loadAll(),
|
||||
);
|
||||
ref
|
||||
.read(priceAnd24hChangeNotifierProvider)
|
||||
.start(true);
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/exchange/change_now/cn_available_currencies.dart';
|
||||
|
||||
final availableChangeNowCurrenciesProvider = Provider<CNAvailableCurrencies>(
|
||||
(ref) => CNAvailableCurrencies(),
|
||||
);
|
|
@ -1,6 +0,0 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/exchange/simpleswap/sp_available_currencies.dart';
|
||||
|
||||
final availableSimpleswapCurrenciesProvider = Provider<SPAvailableCurrencies>(
|
||||
(ref) => SPAvailableCurrencies(),
|
||||
);
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange.dart';
|
||||
|
||||
final currentExchangeNameStateProvider = StateProvider<String>(
|
||||
(ref) => ChangeNowExchange.exchangeName,
|
||||
(ref) => Exchange.defaultExchange.name,
|
||||
);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/exchange/exchange_form_state.dart';
|
||||
|
||||
final exchangeFormStateProvider = ChangeNotifierProvider<ExchangeFormState>(
|
||||
(ref) => ExchangeFormState(),
|
||||
);
|
||||
final exchangeFormStateProvider =
|
||||
ChangeNotifierProvider<ExchangeFormState>((ref) => ExchangeFormState());
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
export './buy/buy_form_state_provider.dart';
|
||||
export './buy/simplex_initial_load_status.dart';
|
||||
export './buy/simplex_provider.dart';
|
||||
export './exchange/available_changenow_currencies_provider.dart';
|
||||
export './exchange/available_simpleswap_currencies_provider.dart';
|
||||
export './exchange/changenow_initial_load_status.dart';
|
||||
export './exchange/current_exchange_name_state_provider.dart';
|
||||
export './exchange/exchange_flow_is_active_state_provider.dart';
|
||||
|
|
|
@ -29,7 +29,6 @@ import 'package:stackwallet/pages/buy_view/buy_quote_preview.dart';
|
|||
import 'package:stackwallet/pages/buy_view/buy_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_loading_overlay.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_1_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_2_view.dart';
|
||||
import 'package:stackwallet/pages/exchange_view/exchange_step_views/step_3_view.dart';
|
||||
|
@ -907,7 +906,7 @@ class RouteGenerator {
|
|||
return _routeError("${settings.name} invalid args: ${args.toString()}");
|
||||
|
||||
case WalletInitiatedExchangeView.routeName:
|
||||
if (args is Tuple3<String, Coin, VoidCallback>) {
|
||||
if (args is Tuple2<String, Coin>) {
|
||||
return getRoute(
|
||||
shouldUseMaterialRoute: useMaterialPageRoute,
|
||||
builder: (_) => Stack(
|
||||
|
@ -916,9 +915,9 @@ class RouteGenerator {
|
|||
walletId: args.item1,
|
||||
coin: args.item2,
|
||||
),
|
||||
ExchangeLoadingOverlayView(
|
||||
unawaitedLoad: args.item3,
|
||||
),
|
||||
// ExchangeLoadingOverlayView(
|
||||
// unawaitedLoad: args.item3,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
settings: RouteSettings(
|
||||
|
|
389
lib/services/exchange/TMP.dart
Normal file
389
lib/services/exchange/TMP.dart
Normal file
|
@ -0,0 +1,389 @@
|
|||
// import 'package:decimal/decimal.dart';
|
||||
// import 'package:flutter/foundation.dart';
|
||||
// import 'package:stackwallet/models/exchange/response_objects/currency.dart';
|
||||
// import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart';
|
||||
// import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
|
||||
// import 'package:stackwallet/services/exchange/exchange.dart';
|
||||
// import 'package:stackwallet/utilities/logger.dart';
|
||||
//
|
||||
// class ExchangeFormState extends ChangeNotifier {
|
||||
// ExchangeFormState(this.exchangeRateType);
|
||||
// final ExchangeRateType exchangeRateType;
|
||||
//
|
||||
// Exchange? _exchange;
|
||||
// Exchange get exchange =>
|
||||
// _exchange ??= ChangeNowExchange(); // default to change now
|
||||
// set exchange(Exchange value) {
|
||||
// _exchange = value;
|
||||
// _updateRangesAndEstimate(
|
||||
// shouldNotifyListeners: true,
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// bool _reversed = false;
|
||||
// bool get reversed => _reversed;
|
||||
// // set reversed(bool reversed) {
|
||||
// // _reversed = reversed;
|
||||
// // //
|
||||
// // }
|
||||
//
|
||||
// Decimal? _rate;
|
||||
// Decimal? get rate => _rate;
|
||||
// // set rate(Decimal? rate) {
|
||||
// // _rate = rate;
|
||||
// // //
|
||||
// // }
|
||||
//
|
||||
// Decimal? _sendAmount;
|
||||
// Decimal? get sendAmount => _sendAmount;
|
||||
// // set sendAmount(Decimal? sendAmount) {
|
||||
// // _sendAmount = sendAmount;
|
||||
// // //
|
||||
// // }
|
||||
//
|
||||
// Decimal? _receiveAmount;
|
||||
// Decimal? get receiveAmount => _receiveAmount;
|
||||
// // set receiveAmount(Decimal? receiveAmount) {
|
||||
// // _receiveAmount = receiveAmount;
|
||||
// // //
|
||||
// // }
|
||||
//
|
||||
// Currency? _sendCurrency;
|
||||
// Currency? get sendCurrency => _sendCurrency;
|
||||
// // set sendCurrency(Currency? sendCurrency) {
|
||||
// // _sendCurrency = sendCurrency;
|
||||
// // //
|
||||
// // }
|
||||
//
|
||||
// Currency? _receiveCurrency;
|
||||
// Currency? get receiveCurrency => _receiveCurrency;
|
||||
// // set receiveCurrency(Currency? receiveCurrency) {
|
||||
// // _receiveCurrency = receiveCurrency;
|
||||
// // //
|
||||
// // }
|
||||
//
|
||||
// Decimal? _minSendAmount;
|
||||
// Decimal? get minSendAmount => _minSendAmount;
|
||||
// // set minSendAmount(Decimal? minSendAmount) {
|
||||
// // _minSendAmount = minSendAmount;
|
||||
// // //
|
||||
// // }
|
||||
//
|
||||
// Decimal? _minReceiveAmount;
|
||||
// Decimal? get minReceiveAmount => _minReceiveAmount;
|
||||
// // set minReceiveAmount(Decimal? minReceiveAmount) {
|
||||
// // _minReceiveAmount = minReceiveAmount;
|
||||
// // //
|
||||
// // }
|
||||
//
|
||||
// Decimal? _maxSendAmount;
|
||||
// Decimal? get maxSendAmount => _maxSendAmount;
|
||||
// // set maxSendAmount(Decimal? maxSendAmount) {
|
||||
// // _maxSendAmount = maxSendAmount;
|
||||
// // //
|
||||
// // }
|
||||
//
|
||||
// Decimal? _maxReceiveAmount;
|
||||
// Decimal? get maxReceiveAmount => _maxReceiveAmount;
|
||||
// // set maxReceiveAmount(Decimal? maxReceiveAmount) {
|
||||
// // _maxReceiveAmount = maxReceiveAmount;
|
||||
// // //
|
||||
// // }
|
||||
//
|
||||
// //============================================================================
|
||||
// // computed properties
|
||||
// //============================================================================
|
||||
//
|
||||
// String? get fromTicker => _sendCurrency?.ticker;
|
||||
//
|
||||
// String? get toTicker => _receiveCurrency?.ticker;
|
||||
//
|
||||
// String get warning {
|
||||
// if (reversed) {
|
||||
// if (_receiveCurrency != null && _receiveAmount != null) {
|
||||
// if (_minReceiveAmount != null &&
|
||||
// _receiveAmount! < _minReceiveAmount! &&
|
||||
// _receiveAmount! > Decimal.zero) {
|
||||
// return "Minimum amount ${_minReceiveAmount!.toString()} ${_receiveCurrency!.ticker.toUpperCase()}";
|
||||
// } else if (_maxReceiveAmount != null &&
|
||||
// _receiveAmount! > _maxReceiveAmount!) {
|
||||
// return "Maximum amount ${_maxReceiveAmount!.toString()} ${_receiveCurrency!.ticker.toUpperCase()}";
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// if (_sendCurrency != null && _sendAmount != null) {
|
||||
// if (_minSendAmount != null &&
|
||||
// _sendAmount! < _minSendAmount! &&
|
||||
// _sendAmount! > Decimal.zero) {
|
||||
// return "Minimum amount ${_minSendAmount!.toString()} ${_sendCurrency!.ticker.toUpperCase()}";
|
||||
// } else if (_maxSendAmount != null && _sendAmount! > _maxSendAmount!) {
|
||||
// return "Maximum amount ${_maxSendAmount!.toString()} ${_sendCurrency!.ticker.toUpperCase()}";
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return "";
|
||||
// }
|
||||
//
|
||||
// //============================================================================
|
||||
// // public state updaters
|
||||
// //============================================================================
|
||||
//
|
||||
// void reset(bool shouldNotifyListeners) {
|
||||
// _exchange = null;
|
||||
// _reversed = false;
|
||||
// _rate = null;
|
||||
// _sendAmount = null;
|
||||
// _receiveAmount = null;
|
||||
// _sendCurrency = null;
|
||||
// _receiveCurrency = null;
|
||||
// _minSendAmount = null;
|
||||
// _minReceiveAmount = null;
|
||||
// _maxSendAmount = null;
|
||||
// _maxReceiveAmount = null;
|
||||
//
|
||||
// if (shouldNotifyListeners) {
|
||||
// notifyListeners();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Future<void> setFromAmountAndCalculateToAmount(
|
||||
// Decimal? newSendAmount,
|
||||
// bool shouldNotifyListeners,
|
||||
// ) async {
|
||||
// if (newSendAmount == null) {
|
||||
// // todo: check if this breaks things and stuff
|
||||
// _receiveAmount = null;
|
||||
// _sendAmount = null;
|
||||
// } else {
|
||||
// if (newSendAmount <= Decimal.zero) {
|
||||
// _receiveAmount = Decimal.zero;
|
||||
// }
|
||||
//
|
||||
// _sendAmount = newSendAmount;
|
||||
// _reversed = false;
|
||||
//
|
||||
// await _updateRangesAndEstimate(
|
||||
// shouldNotifyListeners: false,
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// if (shouldNotifyListeners) {
|
||||
// notifyListeners();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Future<void> setToAmountAndCalculateFromAmount(
|
||||
// Decimal? newReceiveAmount,
|
||||
// bool shouldNotifyListeners,
|
||||
// ) async {
|
||||
// if (newReceiveAmount == null) {
|
||||
// // todo: check if this breaks things and stuff
|
||||
// _receiveAmount = null;
|
||||
// _sendAmount = null;
|
||||
// } else {
|
||||
// if (newReceiveAmount <= Decimal.zero) {
|
||||
// _sendAmount = Decimal.zero;
|
||||
// }
|
||||
//
|
||||
// _receiveAmount = newReceiveAmount;
|
||||
// _reversed = true;
|
||||
//
|
||||
// await _updateRangesAndEstimate(
|
||||
// shouldNotifyListeners: false,
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// if (shouldNotifyListeners) {
|
||||
// notifyListeners();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Future<void> updateFrom(
|
||||
// Currency sendCurrency,
|
||||
// bool shouldNotifyListeners,
|
||||
// ) async {
|
||||
// try {
|
||||
// _sendCurrency = sendCurrency;
|
||||
// if (_receiveCurrency == null) {
|
||||
// _rate = null;
|
||||
// } else {
|
||||
// await _updateRangesAndEstimate(
|
||||
// shouldNotifyListeners: false,
|
||||
// );
|
||||
// }
|
||||
// } catch (e, s) {
|
||||
// Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||
// }
|
||||
// if (shouldNotifyListeners) {
|
||||
// notifyListeners();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Future<void> updateTo(
|
||||
// Currency receiveCurrency,
|
||||
// bool shouldNotifyListeners,
|
||||
// ) async {
|
||||
// try {
|
||||
// _receiveCurrency = receiveCurrency;
|
||||
//
|
||||
// if (_sendCurrency == null) {
|
||||
// _rate = null;
|
||||
// } else {
|
||||
// await _updateRangesAndEstimate(
|
||||
// shouldNotifyListeners: false,
|
||||
// );
|
||||
// }
|
||||
// } catch (e, s) {
|
||||
// Logging.instance.log("$e\n$s", level: LogLevel.Error);
|
||||
// }
|
||||
// if (shouldNotifyListeners) {
|
||||
// notifyListeners();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Future<void> swap(
|
||||
// {required bool shouldNotifyListeners,}) async {
|
||||
// final Decimal? temp = sendAmount;
|
||||
// _sendAmount = receiveAmount;
|
||||
// _receiveAmount = temp;
|
||||
//
|
||||
// _minSendAmount = null;
|
||||
// _maxSendAmount = null;
|
||||
// _minReceiveAmount = null;
|
||||
// _maxReceiveAmount = null;
|
||||
//
|
||||
// final Currency? tmp = sendCurrency;
|
||||
// _sendCurrency = receiveCurrency;
|
||||
// _receiveCurrency = tmp;
|
||||
//
|
||||
// await _updateRangesAndEstimate(
|
||||
// shouldNotifyListeners: false,
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// //============================================================================
|
||||
// // private state updaters
|
||||
// //============================================================================
|
||||
//
|
||||
// Future<void> _updateRangesAndEstimate(
|
||||
// {required bool shouldNotifyListeners,}) async {
|
||||
// await _updateRanges(shouldNotifyListeners: false);
|
||||
// await _updateEstimate(shouldNotifyListeners: false);
|
||||
// if (shouldNotifyListeners) {
|
||||
// notifyListeners();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Future<void> _updateRanges({required bool shouldNotifyListeners,}) async {
|
||||
// // if (exchange?.name == SimpleSwapExchange.exchangeName) {
|
||||
// // reversed = false;
|
||||
// // }
|
||||
// final _send = sendCurrency;
|
||||
// final _receive = receiveCurrency;
|
||||
// if (_send == null || _receive == null) {
|
||||
// Logging.instance.log(
|
||||
// "Tried to $runtimeType.updateRanges where ( $_send || $_receive) for: $exchange",
|
||||
// level: LogLevel.Info,
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
// final response = await exchange.getRange(
|
||||
// _send.ticker,
|
||||
// _receive.ticker,
|
||||
// exchangeRateType == ExchangeRateType.fixed,
|
||||
// );
|
||||
//
|
||||
// if (response.value == null) {
|
||||
// Logging.instance.log(
|
||||
// "Tried to $runtimeType.updateRanges for: $exchange where response: $response",
|
||||
// level: LogLevel.Info,
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
// final responseReversed = await exchange.getRange(
|
||||
// _receive.ticker,
|
||||
// _send.ticker,
|
||||
// exchangeRateType == ExchangeRateType.fixed,
|
||||
// );
|
||||
//
|
||||
// if (responseReversed.value == null) {
|
||||
// Logging.instance.log(
|
||||
// "Tried to $runtimeType.updateRanges for: $exchange where response: $responseReversed",
|
||||
// level: LogLevel.Info,
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// final range = response.value!;
|
||||
// final rangeReversed = responseReversed.value!;
|
||||
//
|
||||
// _minSendAmount = range.min;
|
||||
// _maxSendAmount = range.max;
|
||||
// _minReceiveAmount = rangeReversed.min;
|
||||
// _maxReceiveAmount = rangeReversed.max;
|
||||
//
|
||||
// //todo: check if print needed
|
||||
// // debugPrint(
|
||||
// // "updated range for: $exchange for $_fromTicker-$_toTicker: $range");
|
||||
//
|
||||
// if (shouldNotifyListeners) {
|
||||
// notifyListeners();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Future<void> _updateEstimate({
|
||||
// required bool shouldNotifyListeners,
|
||||
// }) async {
|
||||
// // if (exchange?.name == SimpleSwapExchange.exchangeName) {
|
||||
// // reversed = false;
|
||||
// // }
|
||||
// final amount = reversed ? receiveAmount : sendAmount;
|
||||
// if (sendCurrency == null ||
|
||||
// receiveCurrency == null ||
|
||||
// amount == null ||
|
||||
// amount <= Decimal.zero) {
|
||||
// Logging.instance.log(
|
||||
// "Tried to $runtimeType.updateEstimate for: $exchange where (from: $sendCurrency || to: $receiveCurrency || amount: $amount)",
|
||||
// level: LogLevel.Info,
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
// final response = await exchange.getEstimate(
|
||||
// sendCurrency!.ticker,
|
||||
// receiveCurrency!.ticker,
|
||||
// amount,
|
||||
// exchangeRateType == ExchangeRateType.fixed,
|
||||
// reversed,
|
||||
// );
|
||||
//
|
||||
// if (response.value == null) {
|
||||
// Logging.instance.log(
|
||||
// "Tried to $runtimeType.updateEstimate for: $exchange where response: $response",
|
||||
// level: LogLevel.Info,
|
||||
// );
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// final estimate = response.value!;
|
||||
//
|
||||
// if (reversed) {
|
||||
// _sendAmount = estimate.estimatedAmount;
|
||||
// } else {
|
||||
// _receiveAmount = estimate.estimatedAmount;
|
||||
// }
|
||||
//
|
||||
// _rate =
|
||||
// (receiveAmount! / sendAmount!).toDecimal(scaleOnInfinitePrecision: 12);
|
||||
//
|
||||
// //todo: check if print needed
|
||||
// // debugPrint(
|
||||
// // "updated estimate for: $exchange for $fromTicker-$toTicker: $estimate");
|
||||
//
|
||||
// if (shouldNotifyListeners) {
|
||||
// notifyListeners();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// }
|
|
@ -4,18 +4,21 @@ import 'package:decimal/decimal.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:stackwallet/exceptions/exchange/exchange_exception.dart';
|
||||
import 'package:stackwallet/exceptions/exchange/pair_unavailable_exception.dart';
|
||||
import 'package:stackwallet/external_api_keys.dart';
|
||||
import 'package:stackwallet/models/exchange/change_now/cn_exchange_estimate.dart';
|
||||
import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.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/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/models/exchange/response_objects/pair.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/range.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/currency.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/exchange_response.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
class ChangeNowAPI {
|
||||
static const String scheme = "https";
|
||||
|
@ -127,7 +130,9 @@ class ChangeNowAPI {
|
|||
|
||||
try {
|
||||
final result = await compute(
|
||||
_parseAvailableCurrenciesJson, jsonArray as List<dynamic>);
|
||||
_parseAvailableCurrenciesJson,
|
||||
Tuple2(jsonArray as List<dynamic>, fixedRate == true),
|
||||
);
|
||||
return result;
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("getAvailableCurrencies exception: $e\n$s",
|
||||
|
@ -152,14 +157,23 @@ class ChangeNowAPI {
|
|||
}
|
||||
|
||||
ExchangeResponse<List<Currency>> _parseAvailableCurrenciesJson(
|
||||
List<dynamic> jsonArray) {
|
||||
Tuple2<List<dynamic>, bool> args,
|
||||
) {
|
||||
try {
|
||||
List<Currency> currencies = [];
|
||||
|
||||
for (final json in jsonArray) {
|
||||
for (final json in args.item1) {
|
||||
try {
|
||||
currencies
|
||||
.add(Currency.fromJson(Map<String, dynamic>.from(json as Map)));
|
||||
final map = Map<String, dynamic>.from(json as Map);
|
||||
currencies.add(
|
||||
Currency.fromJson(
|
||||
map,
|
||||
rateType: (map["supportsFixedRate"] as bool)
|
||||
? SupportedRateType.both
|
||||
: SupportedRateType.estimated,
|
||||
exchangeName: ChangeNowExchange.exchangeName,
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
return ExchangeResponse(
|
||||
exception: ExchangeException("Failed to serialize $json",
|
||||
|
@ -199,8 +213,16 @@ class ChangeNowAPI {
|
|||
try {
|
||||
for (final json in jsonArray) {
|
||||
try {
|
||||
currencies
|
||||
.add(Currency.fromJson(Map<String, dynamic>.from(json as Map)));
|
||||
final map = Map<String, dynamic>.from(json as Map);
|
||||
currencies.add(
|
||||
Currency.fromJson(
|
||||
map,
|
||||
rateType: (map["supportsFixedRate"] as bool)
|
||||
? SupportedRateType.both
|
||||
: SupportedRateType.estimated,
|
||||
exchangeName: ChangeNowExchange.exchangeName,
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
return ExchangeResponse(
|
||||
exception: ExchangeException(
|
||||
|
@ -329,8 +351,27 @@ class ChangeNowAPI {
|
|||
final json = await _makeGetRequest(uri);
|
||||
|
||||
try {
|
||||
final value = EstimatedExchangeAmount.fromJson(
|
||||
Map<String, dynamic>.from(json as Map));
|
||||
final map = Map<String, dynamic>.from(json as Map);
|
||||
|
||||
if (map["error"] != null) {
|
||||
if (map["error"] == "pair_is_inactive") {
|
||||
return ExchangeResponse(
|
||||
exception: PairUnavailableException(
|
||||
map["message"] as String,
|
||||
ExchangeExceptionType.generic,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return ExchangeResponse(
|
||||
exception: ExchangeException(
|
||||
map["message"] as String,
|
||||
ExchangeExceptionType.generic,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final value = EstimatedExchangeAmount.fromJson(map);
|
||||
return ExchangeResponse(
|
||||
value: Estimate(
|
||||
estimatedAmount: value.estimatedAmount,
|
||||
|
@ -823,12 +864,10 @@ class ChangeNowAPI {
|
|||
final List<String> stringPair = (json as String).split("_");
|
||||
pairs.add(
|
||||
Pair(
|
||||
exchangeName: ChangeNowExchange.exchangeName,
|
||||
from: stringPair[0],
|
||||
to: stringPair[1],
|
||||
fromNetwork: "",
|
||||
toNetwork: "",
|
||||
fixedRate: false,
|
||||
floatingRate: true,
|
||||
rateType: SupportedRateType.estimated,
|
||||
),
|
||||
);
|
||||
} catch (_) {
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
import 'package:decimal/decimal.dart';
|
||||
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.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/pair.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/range.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/currency.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
|
||||
import 'package:stackwallet/services/exchange/change_now/change_now_api.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class ChangeNowExchange extends Exchange {
|
||||
ChangeNowExchange._();
|
||||
|
||||
static ChangeNowExchange? _instance;
|
||||
static ChangeNowExchange get instance => _instance ??= ChangeNowExchange._();
|
||||
|
||||
static const exchangeName = "ChangeNOW";
|
||||
|
||||
@override
|
||||
|
@ -85,8 +90,29 @@ class ChangeNowExchange extends Exchange {
|
|||
|
||||
@override
|
||||
Future<ExchangeResponse<List<Pair>>> getAllPairs(bool fixedRate) async {
|
||||
// TODO: implement getAllPairs
|
||||
throw UnimplementedError();
|
||||
if (fixedRate) {
|
||||
final markets =
|
||||
await ChangeNowAPI.instance.getAvailableFixedRateMarkets();
|
||||
|
||||
if (markets.value == null) {
|
||||
return ExchangeResponse(exception: markets.exception);
|
||||
}
|
||||
|
||||
final List<Pair> pairs = [];
|
||||
for (final market in markets.value!) {
|
||||
pairs.add(
|
||||
Pair(
|
||||
exchangeName: ChangeNowExchange.exchangeName,
|
||||
from: market.from,
|
||||
to: market.to,
|
||||
rateType: SupportedRateType.fixed,
|
||||
),
|
||||
);
|
||||
}
|
||||
return ExchangeResponse(value: pairs);
|
||||
} else {
|
||||
return await ChangeNowAPI.instance.getAvailableFloatingRatePairs();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,20 +1,25 @@
|
|||
import 'package:decimal/decimal.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/pair.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/range.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/currency.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/exchange_response.dart';
|
||||
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
|
||||
|
||||
abstract class Exchange {
|
||||
static Exchange get defaultExchange => ChangeNowExchange.instance;
|
||||
|
||||
static Exchange fromName(String name) {
|
||||
switch (name) {
|
||||
case ChangeNowExchange.exchangeName:
|
||||
return ChangeNowExchange();
|
||||
return ChangeNowExchange.instance;
|
||||
case SimpleSwapExchange.exchangeName:
|
||||
return SimpleSwapExchange();
|
||||
return SimpleSwapExchange.instance;
|
||||
case MajesticBankExchange.exchangeName:
|
||||
return MajesticBankExchange.instance;
|
||||
default:
|
||||
throw ArgumentError("Unknown exchange name");
|
||||
}
|
||||
|
|
|
@ -1,200 +1,225 @@
|
|||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/providers/providers.dart';
|
||||
import 'package:stackwallet/services/exchange/change_now/change_now_api.dart';
|
||||
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/currency.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/majestic_bank/majestic_bank_exchange.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/stack_file_system.dart';
|
||||
|
||||
class ExchangeDataLoadingService {
|
||||
Future<void> loadAll(WidgetRef ref, {Coin? coin}) async {
|
||||
try {
|
||||
await Future.wait([
|
||||
_loadFixedRateMarkets(ref, coin: coin),
|
||||
_loadChangeNowStandardCurrencies(ref, coin: coin),
|
||||
loadSimpleswapFixedRateCurrencies(ref),
|
||||
loadSimpleswapFloatingRateCurrencies(ref),
|
||||
]);
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("ExchangeDataLoadingService.loadAll failed: $e\n$s",
|
||||
level: LogLevel.Error);
|
||||
}
|
||||
ExchangeDataLoadingService._();
|
||||
static final ExchangeDataLoadingService _instance =
|
||||
ExchangeDataLoadingService._();
|
||||
static ExchangeDataLoadingService get instance => _instance;
|
||||
|
||||
Isar? _isar;
|
||||
Isar get isar => _isar!;
|
||||
|
||||
Future<void> init() async {
|
||||
if (_isar != null && isar.isOpen) return;
|
||||
_isar = await Isar.open(
|
||||
[
|
||||
CurrencySchema,
|
||||
PairSchema,
|
||||
],
|
||||
directory: (await StackFileSystem.applicationIsarDirectory()).path,
|
||||
inspector: kDebugMode,
|
||||
name: "exchange_cache",
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadFixedRateMarkets(WidgetRef ref, {Coin? coin}) async {
|
||||
if (ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state ==
|
||||
ChangeNowLoadStatus.loading) {
|
||||
// already in progress so just
|
||||
return;
|
||||
}
|
||||
bool _locked = false;
|
||||
|
||||
ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state =
|
||||
ChangeNowLoadStatus.loading;
|
||||
Future<void> loadAll() async {
|
||||
print("LOADINGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG: LOCKED=$_locked");
|
||||
if (!_locked) {
|
||||
_locked = true;
|
||||
print("LOADINGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG");
|
||||
final time = DateTime.now();
|
||||
try {
|
||||
await Future.wait([
|
||||
_loadChangeNowCurrencies(),
|
||||
_loadChangeNowFixedRatePairs(),
|
||||
_loadChangeNowEstimatedRatePairs(),
|
||||
// loadSimpleswapFixedRateCurrencies(ref),
|
||||
// loadSimpleswapFloatingRateCurrencies(ref),
|
||||
loadMajesticBankCurrencies(),
|
||||
]);
|
||||
|
||||
final response3 =
|
||||
await ChangeNowAPI.instance.getAvailableFixedRateMarkets();
|
||||
if (response3.value != null) {
|
||||
ref
|
||||
.read(availableChangeNowCurrenciesProvider)
|
||||
.updateMarkets(response3.value!);
|
||||
|
||||
if (ref.read(exchangeFormStateProvider).market == null) {
|
||||
String fromTicker = "btc";
|
||||
String toTicker = "xmr";
|
||||
|
||||
if (coin != null) {
|
||||
fromTicker = coin.ticker.toLowerCase();
|
||||
}
|
||||
|
||||
final matchingMarkets = response3.value!
|
||||
.where((e) => e.to == toTicker && e.from == fromTicker);
|
||||
if (matchingMarkets.isNotEmpty) {
|
||||
await ref
|
||||
.read(exchangeFormStateProvider)
|
||||
.updateMarket(matchingMarkets.first, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"Failed to load changeNOW fixed rate markets: ${response3.exception?.message}",
|
||||
level: LogLevel.Error);
|
||||
|
||||
ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state =
|
||||
ChangeNowLoadStatus.failed;
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state =
|
||||
ChangeNowLoadStatus.success;
|
||||
}
|
||||
|
||||
Future<void> _loadChangeNowStandardCurrencies(
|
||||
WidgetRef ref, {
|
||||
Coin? coin,
|
||||
}) async {
|
||||
if (ref
|
||||
.read(changeNowEstimatedInitialLoadStatusStateProvider.state)
|
||||
.state ==
|
||||
ChangeNowLoadStatus.loading) {
|
||||
// already in progress so just
|
||||
return;
|
||||
}
|
||||
|
||||
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
|
||||
ChangeNowLoadStatus.loading;
|
||||
|
||||
final response = await ChangeNowAPI.instance.getAvailableCurrencies();
|
||||
final response2 =
|
||||
await ChangeNowAPI.instance.getAvailableFloatingRatePairs();
|
||||
if (response.value != null) {
|
||||
ref
|
||||
.read(availableChangeNowCurrenciesProvider)
|
||||
.updateCurrencies(response.value!);
|
||||
|
||||
if (response2.value != null) {
|
||||
ref
|
||||
.read(availableChangeNowCurrenciesProvider)
|
||||
.updateFloatingPairs(response2.value!);
|
||||
|
||||
String fromTicker = "btc";
|
||||
String toTicker = "xmr";
|
||||
|
||||
if (coin != null) {
|
||||
fromTicker = coin.ticker.toLowerCase();
|
||||
}
|
||||
|
||||
if (response.value!.length > 1) {
|
||||
if (ref.read(exchangeFormStateProvider).from == null) {
|
||||
if (response.value!
|
||||
.where((e) => e.ticker == fromTicker)
|
||||
.isNotEmpty) {
|
||||
await ref.read(exchangeFormStateProvider).updateFrom(
|
||||
response.value!.firstWhere((e) => e.ticker == fromTicker),
|
||||
false);
|
||||
}
|
||||
}
|
||||
if (ref.read(exchangeFormStateProvider).to == null) {
|
||||
if (response.value!.where((e) => e.ticker == toTicker).isNotEmpty) {
|
||||
await ref.read(exchangeFormStateProvider).updateTo(
|
||||
response.value!.firstWhere((e) => e.ticker == toTicker),
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print(
|
||||
"LOADINGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG done in ${DateTime.now().difference(time).inSeconds} seconds");
|
||||
} catch (e, s) {
|
||||
Logging.instance.log(
|
||||
"Failed to load changeNOW available floating rate pairs: ${response2.exception?.message}",
|
||||
"ExchangeDataLoadingService.loadAll failed: $e\n$s",
|
||||
level: LogLevel.Error);
|
||||
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
|
||||
ChangeNowLoadStatus.failed;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"Failed to load changeNOW currencies: ${response.exception?.message}",
|
||||
level: LogLevel.Error);
|
||||
await Future<void>.delayed(const Duration(seconds: 3));
|
||||
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
|
||||
ChangeNowLoadStatus.failed;
|
||||
return;
|
||||
_locked = false;
|
||||
}
|
||||
|
||||
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
|
||||
ChangeNowLoadStatus.success;
|
||||
}
|
||||
|
||||
Future<void> loadSimpleswapFloatingRateCurrencies(WidgetRef ref) async {
|
||||
final exchange = SimpleSwapExchange();
|
||||
Future<void> _loadChangeNowCurrencies() async {
|
||||
final exchange = ChangeNowExchange.instance;
|
||||
final responseCurrencies = await exchange.getAllCurrencies(false);
|
||||
if (responseCurrencies.value != null) {
|
||||
await isar.writeTxn(() async {
|
||||
final idsToDelete = await isar.currencies
|
||||
.where()
|
||||
.exchangeNameEqualTo(ChangeNowExchange.exchangeName)
|
||||
.idProperty()
|
||||
.findAll();
|
||||
await isar.currencies.deleteAll(idsToDelete);
|
||||
await isar.currencies.putAll(responseCurrencies.value!);
|
||||
});
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"Failed to load changeNOW currencies: ${responseCurrencies.exception?.message}",
|
||||
level: LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadChangeNowFixedRatePairs() async {
|
||||
final exchange = ChangeNowExchange.instance;
|
||||
|
||||
final responsePairs = await exchange.getAllPairs(true);
|
||||
|
||||
if (responsePairs.value != null) {
|
||||
await isar.writeTxn(() async {
|
||||
final idsToDelete2 = await isar.pairs
|
||||
.where()
|
||||
.exchangeNameEqualTo(ChangeNowExchange.exchangeName)
|
||||
.filter()
|
||||
.rateTypeEqualTo(SupportedRateType.fixed)
|
||||
.idProperty()
|
||||
.findAll();
|
||||
await isar.pairs.deleteAll(idsToDelete2);
|
||||
await isar.pairs.putAll(responsePairs.value!);
|
||||
});
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"Failed to load changeNOW available fixed rate pairs: ${responsePairs.exception?.message}",
|
||||
level: LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadChangeNowEstimatedRatePairs() async {
|
||||
final exchange = ChangeNowExchange.instance;
|
||||
|
||||
final responsePairs = await exchange.getAllPairs(false);
|
||||
|
||||
if (responsePairs.value != null) {
|
||||
await isar.writeTxn(() async {
|
||||
final idsToDelete = await isar.pairs
|
||||
.where()
|
||||
.exchangeNameEqualTo(ChangeNowExchange.exchangeName)
|
||||
.filter()
|
||||
.rateTypeEqualTo(SupportedRateType.estimated)
|
||||
.idProperty()
|
||||
.findAll();
|
||||
await isar.pairs.deleteAll(idsToDelete);
|
||||
await isar.pairs.putAll(responsePairs.value!);
|
||||
});
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"Failed to load changeNOW available floating rate pairs: ${responsePairs.exception?.message}",
|
||||
level: LogLevel.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
//
|
||||
// Future<void> loadSimpleswapFloatingRateCurrencies(WidgetRef ref) async {
|
||||
// final exchange = SimpleSwapExchange();
|
||||
// final responseCurrencies = await exchange.getAllCurrencies(false);
|
||||
//
|
||||
// if (responseCurrencies.value != null) {
|
||||
// ref
|
||||
// .read(availableSimpleswapCurrenciesProvider)
|
||||
// .updateFloatingCurrencies(responseCurrencies.value!);
|
||||
//
|
||||
// final responsePairs = await exchange.getAllPairs(false);
|
||||
//
|
||||
// if (responsePairs.value != null) {
|
||||
// ref
|
||||
// .read(availableSimpleswapCurrenciesProvider)
|
||||
// .updateFloatingPairs(responsePairs.value!);
|
||||
// } else {
|
||||
// Logging.instance.log(
|
||||
// "loadSimpleswapFloatingRateCurrencies: $responsePairs",
|
||||
// level: LogLevel.Warning,
|
||||
// );
|
||||
// }
|
||||
// } else {
|
||||
// Logging.instance.log(
|
||||
// "loadSimpleswapFloatingRateCurrencies: $responseCurrencies",
|
||||
// level: LogLevel.Warning,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Future<void> loadSimpleswapFixedRateCurrencies(WidgetRef ref) async {
|
||||
// final exchange = SimpleSwapExchange();
|
||||
// final responseCurrencies = await exchange.getAllCurrencies(true);
|
||||
//
|
||||
// if (responseCurrencies.value != null) {
|
||||
// ref
|
||||
// .read(availableSimpleswapCurrenciesProvider)
|
||||
// .updateFixedCurrencies(responseCurrencies.value!);
|
||||
//
|
||||
// final responsePairs = await exchange.getAllPairs(true);
|
||||
//
|
||||
// if (responsePairs.value != null) {
|
||||
// ref
|
||||
// .read(availableSimpleswapCurrenciesProvider)
|
||||
// .updateFixedPairs(responsePairs.value!);
|
||||
// } else {
|
||||
// Logging.instance.log(
|
||||
// "loadSimpleswapFixedRateCurrencies: $responsePairs",
|
||||
// level: LogLevel.Warning,
|
||||
// );
|
||||
// }
|
||||
// } else {
|
||||
// Logging.instance.log(
|
||||
// "loadSimpleswapFixedRateCurrencies: $responseCurrencies",
|
||||
// level: LogLevel.Warning,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> loadMajesticBankCurrencies() async {
|
||||
final exchange = MajesticBankExchange.instance;
|
||||
final responseCurrencies = await exchange.getAllCurrencies(false);
|
||||
|
||||
if (responseCurrencies.value != null) {
|
||||
ref
|
||||
.read(availableSimpleswapCurrenciesProvider)
|
||||
.updateFloatingCurrencies(responseCurrencies.value!);
|
||||
|
||||
final responsePairs = await exchange.getAllPairs(false);
|
||||
|
||||
if (responsePairs.value != null) {
|
||||
ref
|
||||
.read(availableSimpleswapCurrenciesProvider)
|
||||
.updateFloatingPairs(responsePairs.value!);
|
||||
await isar.writeTxn(() async {
|
||||
final idsToDelete = await isar.currencies
|
||||
.where()
|
||||
.exchangeNameEqualTo(MajesticBankExchange.exchangeName)
|
||||
.idProperty()
|
||||
.findAll();
|
||||
await isar.currencies.deleteAll(idsToDelete);
|
||||
await isar.currencies.putAll(responseCurrencies.value!);
|
||||
|
||||
final idsToDelete2 = await isar.pairs
|
||||
.where()
|
||||
.exchangeNameEqualTo(MajesticBankExchange.exchangeName)
|
||||
.idProperty()
|
||||
.findAll();
|
||||
await isar.pairs.deleteAll(idsToDelete2);
|
||||
await isar.pairs.putAll(responsePairs.value!);
|
||||
});
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"loadSimpleswapFloatingRateCurrencies: $responsePairs",
|
||||
"loadMajesticBankCurrencies: $responsePairs",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"loadSimpleswapFloatingRateCurrencies: $responseCurrencies",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> loadSimpleswapFixedRateCurrencies(WidgetRef ref) async {
|
||||
final exchange = SimpleSwapExchange();
|
||||
final responseCurrencies = await exchange.getAllCurrencies(true);
|
||||
|
||||
if (responseCurrencies.value != null) {
|
||||
ref
|
||||
.read(availableSimpleswapCurrenciesProvider)
|
||||
.updateFixedCurrencies(responseCurrencies.value!);
|
||||
|
||||
final responsePairs = await exchange.getAllPairs(true);
|
||||
|
||||
if (responsePairs.value != null) {
|
||||
ref
|
||||
.read(availableSimpleswapCurrenciesProvider)
|
||||
.updateFixedPairs(responsePairs.value!);
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"loadSimpleswapFixedRateCurrencies: $responsePairs",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
Logging.instance.log(
|
||||
"loadSimpleswapFixedRateCurrencies: $responseCurrencies",
|
||||
"loadMajesticBankCurrencies: $responseCurrencies",
|
||||
level: LogLevel.Warning,
|
||||
);
|
||||
}
|
||||
|
|
1
lib/services/exchange/exchange_service.dart
Normal file
1
lib/services/exchange/exchange_service.dart
Normal file
|
@ -0,0 +1 @@
|
|||
class ExchangeService {}
|
|
@ -3,6 +3,8 @@ import 'dart:convert';
|
|||
import 'package:decimal/decimal.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:stackwallet/exceptions/exchange/exchange_exception.dart';
|
||||
import 'package:stackwallet/exceptions/exchange/pair_unavailable_exception.dart';
|
||||
import 'package:stackwallet/external_api_keys.dart';
|
||||
import 'package:stackwallet/models/exchange/majestic_bank/mb_limit.dart';
|
||||
import 'package:stackwallet/models/exchange/majestic_bank/mb_order.dart';
|
||||
import 'package:stackwallet/models/exchange/majestic_bank/mb_order_calculation.dart';
|
||||
|
@ -15,7 +17,6 @@ class MajesticBankAPI {
|
|||
static const String scheme = "https";
|
||||
static const String authority = "majesticbank.sc";
|
||||
static const String version = "v1";
|
||||
static const String refCode = "fixme";
|
||||
|
||||
MajesticBankAPI._();
|
||||
|
||||
|
@ -39,7 +40,6 @@ class MajesticBankAPI {
|
|||
);
|
||||
|
||||
code = response.statusCode;
|
||||
print(response.body);
|
||||
|
||||
final parsed = jsonDecode(response.body);
|
||||
|
||||
|
@ -180,6 +180,28 @@ class MajesticBankAPI {
|
|||
try {
|
||||
final jsonObject = await _makeGetRequest(uri);
|
||||
final map = Map<String, dynamic>.from(jsonObject as Map);
|
||||
|
||||
if (map["error"] != null) {
|
||||
final errorMessage = map["extra"] as String?;
|
||||
if (errorMessage != null &&
|
||||
errorMessage.startsWith("Bad") &&
|
||||
errorMessage.endsWith("currency symbol")) {
|
||||
return ExchangeResponse(
|
||||
exception: PairUnavailableException(
|
||||
errorMessage,
|
||||
ExchangeExceptionType.generic,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return ExchangeResponse(
|
||||
exception: ExchangeException(
|
||||
errorMessage ?? "Error: ${map["error"]}",
|
||||
ExchangeExceptionType.generic,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final result = MBOrderCalculation(
|
||||
fromCurrency: map["from_currency"] as String,
|
||||
fromAmount: Decimal.parse(map["from_amount"].toString()),
|
||||
|
@ -189,8 +211,9 @@ class MajesticBankAPI {
|
|||
|
||||
return ExchangeResponse(value: result);
|
||||
} catch (e, s) {
|
||||
Logging.instance
|
||||
.log("calculateOrder exception: $e\n$s", level: LogLevel.Error);
|
||||
Logging.instance.log(
|
||||
"calculateOrder $fromCurrency-$receiveCurrency exception: $e\n$s",
|
||||
level: LogLevel.Error);
|
||||
return ExchangeResponse(
|
||||
exception: ExchangeException(
|
||||
e.toString(),
|
||||
|
@ -211,7 +234,7 @@ class MajesticBankAPI {
|
|||
"from_currency": fromCurrency,
|
||||
"receive_currency": receiveCurrency,
|
||||
"receive_address": receiveAddress,
|
||||
"referral_code": refCode,
|
||||
"referral_code": kMajesticBankRefCode,
|
||||
};
|
||||
|
||||
final uri = _buildUri(endpoint: "exchange", params: params);
|
||||
|
@ -260,7 +283,7 @@ class MajesticBankAPI {
|
|||
"from_currency": fromCurrency,
|
||||
"receive_currency": receiveCurrency,
|
||||
"receive_address": receiveAddress,
|
||||
"referral_code": refCode,
|
||||
"referral_code": kMajesticBankRefCode,
|
||||
};
|
||||
|
||||
if (reversed) {
|
||||
|
|
|
@ -2,18 +2,24 @@ import 'package:decimal/decimal.dart';
|
|||
import 'package:stackwallet/exceptions/exchange/exchange_exception.dart';
|
||||
import 'package:stackwallet/exceptions/exchange/majestic_bank/mb_exception.dart';
|
||||
import 'package:stackwallet/models/exchange/majestic_bank/mb_order.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/pair.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/range.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/currency.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
||||
import 'package:stackwallet/services/exchange/majestic_bank/majestic_bank_api.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class MajesticBankExchange extends Exchange {
|
||||
static const exchangeName = "MajesticBank";
|
||||
MajesticBankExchange._();
|
||||
|
||||
static MajesticBankExchange? _instance;
|
||||
static MajesticBankExchange get instance =>
|
||||
_instance ??= MajesticBankExchange._();
|
||||
|
||||
static const exchangeName = "Majestic Bank";
|
||||
|
||||
@override
|
||||
Future<ExchangeResponse<Trade>> createTrade({
|
||||
|
@ -102,15 +108,15 @@ class MajesticBankExchange extends Exchange {
|
|||
|
||||
for (final limit in limits) {
|
||||
final currency = Currency(
|
||||
exchangeName: MajesticBankExchange.exchangeName,
|
||||
ticker: limit.currency,
|
||||
name: limit.currency,
|
||||
name: limit.currency, // todo: get full coin name
|
||||
network: "",
|
||||
image: "",
|
||||
hasExternalId: false,
|
||||
isFiat: false,
|
||||
featured: false,
|
||||
isStable: false,
|
||||
supportsFixedRate: true,
|
||||
rateType: SupportedRateType.both,
|
||||
isAvailable: true,
|
||||
isStackCoin: Currency.checkIsStackCoin(limit.currency),
|
||||
);
|
||||
currencies.add(currency);
|
||||
}
|
||||
|
@ -130,12 +136,10 @@ class MajesticBankExchange extends Exchange {
|
|||
|
||||
for (final rate in rates) {
|
||||
final pair = Pair(
|
||||
exchangeName: MajesticBankExchange.exchangeName,
|
||||
from: rate.fromCurrency,
|
||||
fromNetwork: "",
|
||||
to: rate.toCurrency,
|
||||
toNetwork: "",
|
||||
fixedRate: true,
|
||||
floatingRate: true,
|
||||
rateType: SupportedRateType.both,
|
||||
);
|
||||
pairs.add(pair);
|
||||
}
|
||||
|
|
|
@ -6,10 +6,10 @@ import 'package:http/http.dart' as http;
|
|||
import 'package:stackwallet/exceptions/exchange/exchange_exception.dart';
|
||||
import 'package:stackwallet/external_api_keys.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/pair.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/range.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||
import 'package:stackwallet/models/exchange/simpleswap/sp_currency.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
||||
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
|
@ -272,12 +272,10 @@ class SimpleSwapAPI {
|
|||
for (final to in entry.value as List) {
|
||||
pairs.add(
|
||||
Pair(
|
||||
exchangeName: SimpleSwapExchange.exchangeName,
|
||||
from: from,
|
||||
fromNetwork: "",
|
||||
to: to as String,
|
||||
toNetwork: "",
|
||||
fixedRate: args.item2,
|
||||
floatingRate: !args.item2,
|
||||
rateType: SupportedRateType.estimated,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
import 'package:decimal/decimal.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/pair.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/range.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/currency.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
||||
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_api.dart';
|
||||
|
||||
class SimpleSwapExchange extends Exchange {
|
||||
SimpleSwapExchange._();
|
||||
|
||||
static SimpleSwapExchange? _instance;
|
||||
static SimpleSwapExchange get instance =>
|
||||
_instance ??= SimpleSwapExchange._();
|
||||
|
||||
static const exchangeName = "SimpleSwap";
|
||||
|
||||
@override
|
||||
|
@ -47,18 +53,22 @@ class SimpleSwapExchange extends Exchange {
|
|||
await SimpleSwapAPI.instance.getAllCurrencies(fixedRate: fixedRate);
|
||||
if (response.value != null) {
|
||||
final List<Currency> currencies = response.value!
|
||||
.map((e) => Currency(
|
||||
ticker: e.symbol,
|
||||
name: e.name,
|
||||
network: e.network,
|
||||
image: e.image,
|
||||
hasExternalId: e.hasExtraId,
|
||||
externalId: e.extraId,
|
||||
isFiat: false,
|
||||
featured: false,
|
||||
isStable: false,
|
||||
supportsFixedRate: fixedRate,
|
||||
))
|
||||
.map(
|
||||
(e) => Currency(
|
||||
exchangeName: exchangeName,
|
||||
ticker: e.symbol,
|
||||
name: e.name,
|
||||
network: e.network,
|
||||
image: e.image,
|
||||
externalId: e.extraId,
|
||||
isFiat: false,
|
||||
rateType: fixedRate
|
||||
? SupportedRateType.both
|
||||
: SupportedRateType.estimated,
|
||||
isAvailable: true,
|
||||
isStackCoin: Currency.checkIsStackCoin(e.symbol),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
return ExchangeResponse<List<Currency>>(
|
||||
value: currencies,
|
||||
|
|
|
@ -6,9 +6,7 @@ import 'package:stackwallet/exceptions/electrumx/no_such_transaction.dart';
|
|||
import 'package:stackwallet/hive/db.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart';
|
||||
import 'package:stackwallet/models/notification_model.dart';
|
||||
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
||||
import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart';
|
||||
import 'package:stackwallet/services/node_service.dart';
|
||||
import 'package:stackwallet/services/notifications_api.dart';
|
||||
import 'package:stackwallet/services/trade_service.dart';
|
||||
|
@ -16,6 +14,8 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
|||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
|
||||
import 'exchange/exchange.dart';
|
||||
|
||||
class NotificationsService extends ChangeNotifier {
|
||||
late NodeService nodeService;
|
||||
late TradesService tradesService;
|
||||
|
@ -196,15 +196,12 @@ class NotificationsService extends ChangeNotifier {
|
|||
}
|
||||
final oldTrade = trades.first;
|
||||
late final ExchangeResponse<Trade> response;
|
||||
switch (oldTrade.exchangeName) {
|
||||
case SimpleSwapExchange.exchangeName:
|
||||
response = await SimpleSwapExchange().updateTrade(oldTrade);
|
||||
break;
|
||||
case ChangeNowExchange.exchangeName:
|
||||
response = await ChangeNowExchange().updateTrade(oldTrade);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
|
||||
try {
|
||||
final exchange = Exchange.fromName(oldTrade.exchangeName);
|
||||
response = await exchange.updateTrade(oldTrade);
|
||||
} catch (_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.value == null) {
|
||||
|
|
|
@ -26,6 +26,8 @@ class _EXCHANGE {
|
|||
|
||||
String get changeNow => "assets/svg/exchange_icons/change_now_logo_1.svg";
|
||||
String get simpleSwap => "assets/svg/exchange_icons/simpleswap-icon.svg";
|
||||
String get majesticBankBlue => "assets/svg/exchange_icons/mb_blue.svg";
|
||||
String get majesticBankGreen => "assets/svg/exchange_icons/mb_green.svg";
|
||||
}
|
||||
|
||||
class _BUY {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:stackwallet/pages/buy_view/sub_widgets/crypto_selection_view.dart';
|
||||
import 'package:stackwallet/utilities/assets.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/theme/stack_colors.dart';
|
||||
|
@ -156,12 +157,18 @@ class _ExchangeTextFieldState extends State<ExchangeTextField> {
|
|||
),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final image = widget.image;
|
||||
|
||||
if (image != null && image.isNotEmpty) {
|
||||
if (isStackCoin(widget.ticker)) {
|
||||
return Center(
|
||||
child: getIconForTicker(
|
||||
widget.ticker!,
|
||||
size: 18,
|
||||
),
|
||||
);
|
||||
} else if (widget.image != null &&
|
||||
widget.image!.isNotEmpty) {
|
||||
return Center(
|
||||
child: SvgPicture.network(
|
||||
image,
|
||||
widget.image!,
|
||||
height: 18,
|
||||
placeholderBuilder: (_) => Container(
|
||||
width: 18,
|
||||
|
@ -240,150 +247,3 @@ class _ExchangeTextFieldState extends State<ExchangeTextField> {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// experimental UNUSED
|
||||
// class ExchangeTextField extends StatefulWidget {
|
||||
// const ExchangeTextField({
|
||||
// Key? key,
|
||||
// this.borderRadius = 0,
|
||||
// this.background,
|
||||
// required this.controller,
|
||||
// this.buttonColor,
|
||||
// required this.focusNode,
|
||||
// this.buttonContent,
|
||||
// required this.textStyle,
|
||||
// this.onButtonTap,
|
||||
// this.onChanged,
|
||||
// this.onSubmitted,
|
||||
// }) : super(key: key);
|
||||
//
|
||||
// final double borderRadius;
|
||||
// final Color? background;
|
||||
// final Color? buttonColor;
|
||||
// final Widget? buttonContent;
|
||||
// final TextEditingController controller;
|
||||
// final FocusNode focusNode;
|
||||
// final TextStyle textStyle;
|
||||
// final VoidCallback? onButtonTap;
|
||||
// final void Function(String)? onChanged;
|
||||
// final void Function(String)? onSubmitted;
|
||||
//
|
||||
// @override
|
||||
// State<ExchangeTextField> createState() => _ExchangeTextFieldState();
|
||||
// }
|
||||
//
|
||||
// class _ExchangeTextFieldState extends State<ExchangeTextField> {
|
||||
// late final TextEditingController controller;
|
||||
// late final FocusNode focusNode;
|
||||
// late final TextStyle textStyle;
|
||||
//
|
||||
// late final double borderRadius;
|
||||
//
|
||||
// late final Color? background;
|
||||
// late final Color? buttonColor;
|
||||
// late final Widget? buttonContent;
|
||||
// late final VoidCallback? onButtonTap;
|
||||
// late final void Function(String)? onChanged;
|
||||
// late final void Function(String)? onSubmitted;
|
||||
//
|
||||
// @override
|
||||
// void initState() {
|
||||
// borderRadius = widget.borderRadius;
|
||||
// background = widget.background;
|
||||
// buttonColor = widget.buttonColor;
|
||||
// controller = widget.controller;
|
||||
// focusNode = widget.focusNode;
|
||||
// buttonContent = widget.buttonContent;
|
||||
// textStyle = widget.textStyle;
|
||||
// onButtonTap = widget.onButtonTap;
|
||||
// onChanged = widget.onChanged;
|
||||
// onSubmitted = widget.onSubmitted;
|
||||
//
|
||||
// super.initState();
|
||||
// }
|
||||
//
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Container(
|
||||
// decoration: BoxDecoration(
|
||||
// color: background,
|
||||
// borderRadius: BorderRadius.circular(borderRadius),
|
||||
// ),
|
||||
// child: IntrinsicHeight(
|
||||
// child: Row(
|
||||
// crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
// children: [
|
||||
// Expanded(
|
||||
// child: MouseRegion(
|
||||
// cursor: SystemMouseCursors.text,
|
||||
// child: GestureDetector(
|
||||
// onTap: () {
|
||||
// //
|
||||
// },
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.only(
|
||||
// left: 16,
|
||||
// top: 18,
|
||||
// bottom: 17,
|
||||
// ),
|
||||
// child: IgnorePointer(
|
||||
// ignoring: true,
|
||||
// child: EditableText(
|
||||
// controller: controller,
|
||||
// focusNode: focusNode,
|
||||
// style: textStyle,
|
||||
// onChanged: onChanged,
|
||||
// onSubmitted: onSubmitted,
|
||||
// onEditingComplete: () => print("lol"),
|
||||
// autocorrect: false,
|
||||
// enableSuggestions: false,
|
||||
// keyboardType: const TextInputType.numberWithOptions(
|
||||
// signed: false,
|
||||
// decimal: true,
|
||||
// ),
|
||||
// inputFormatters: [
|
||||
// // regex to validate a crypto amount with 8 decimal places
|
||||
// TextInputFormatter.withFunction((oldValue,
|
||||
// newValue) =>
|
||||
// RegExp(r'^([0-9]*[,.]?[0-9]{0,8}|[,.][0-9]{0,8})$')
|
||||
// .hasMatch(newValue.text)
|
||||
// ? newValue
|
||||
// : oldValue),
|
||||
// ],
|
||||
// cursorColor: textStyle.color ??
|
||||
// Theme.of(context).backgroundColor,
|
||||
// backgroundCursorColor: background ?? Colors.transparent,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// MouseRegion(
|
||||
// cursor: SystemMouseCursors.click,
|
||||
// child: GestureDetector(
|
||||
// onTap: () => onButtonTap?.call(),
|
||||
// child: Container(
|
||||
// decoration: BoxDecoration(
|
||||
// color: buttonColor,
|
||||
// borderRadius: BorderRadius.horizontal(
|
||||
// right: Radius.circular(
|
||||
// borderRadius,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// child: Padding(
|
||||
// padding: const EdgeInsets.symmetric(
|
||||
// horizontal: 16,
|
||||
// ),
|
||||
// child: buttonContent,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -344,6 +344,8 @@ flutter:
|
|||
# exchange icons
|
||||
- assets/svg/exchange_icons/change_now_logo_1.svg
|
||||
- assets/svg/exchange_icons/simpleswap-icon.svg
|
||||
- assets/svg/exchange_icons/mb_green.svg
|
||||
- assets/svg/exchange_icons/mb_blue.svg
|
||||
|
||||
# theme selectors
|
||||
- assets/svg/dark-theme.svg
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,17 +15,16 @@ import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart
|
|||
as _i20;
|
||||
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart'
|
||||
as _i21;
|
||||
import 'package:stackwallet/models/exchange/response_objects/currency.dart'
|
||||
as _i14;
|
||||
import 'package:stackwallet/models/exchange/response_objects/estimate.dart'
|
||||
as _i17;
|
||||
import 'package:stackwallet/models/exchange/response_objects/fixed_rate_market.dart'
|
||||
as _i19;
|
||||
import 'package:stackwallet/models/exchange/response_objects/pair.dart' as _i22;
|
||||
import 'package:stackwallet/models/exchange/response_objects/range.dart'
|
||||
as _i16;
|
||||
import 'package:stackwallet/models/exchange/response_objects/trade.dart'
|
||||
as _i10;
|
||||
import 'package:stackwallet/models/isar/exchange_cache/currency.dart' as _i14;
|
||||
import 'package:stackwallet/models/isar/exchange_cache/pair.dart' as _i22;
|
||||
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'
|
||||
as _i5;
|
||||
import 'package:stackwallet/services/exchange/change_now/change_now_api.dart'
|
||||
|
|
|
@ -5,13 +5,12 @@ import 'package:flutter_test/flutter_test.dart';
|
|||
import 'package:http/http.dart';
|
||||
import 'package:mockito/annotations.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.dart';
|
||||
import 'package:stackwallet/exceptions/exchange/exchange_exception.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/response_objects/estimate.dart';
|
||||
import 'package:stackwallet/models/exchange/response_objects/pair.dart';
|
||||
import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
|
||||
import 'package:stackwallet/services/exchange/change_now/change_now_api.dart';
|
||||
import 'package:stackwallet/services/exchange/exchange_response.dart';
|
||||
|
||||
import 'change_now_sample_data.dart';
|
||||
import 'change_now_test.mocks.dart';
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue