Merge branch 'main' into CW-292-Save-historical-fiat-API-rate

This commit is contained in:
Serhii 2023-06-11 14:33:50 +03:00
commit 5d6d9b9589
9 changed files with 516 additions and 436 deletions

View file

@ -106,7 +106,6 @@ jobs:
echo "const moonPayApiKey = '${{ secrets.MOON_PAY_API_KEY }}';" >> lib/.secrets.g.dart echo "const moonPayApiKey = '${{ secrets.MOON_PAY_API_KEY }}';" >> lib/.secrets.g.dart
echo "const moonPaySecretKey = '${{ secrets.MOON_PAY_SECRET_KEY }}';" >> lib/.secrets.g.dart echo "const moonPaySecretKey = '${{ secrets.MOON_PAY_SECRET_KEY }}';" >> lib/.secrets.g.dart
echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart echo "const sideShiftAffiliateId = '${{ secrets.SIDE_SHIFT_AFFILIATE_ID }}';" >> lib/.secrets.g.dart
echo "const sideShiftApiKey = '${{ secrets.SIDE_SHIFT_API_KEY }}';" >> lib/.secrets.g.dart
echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart echo "const simpleSwapApiKey = '${{ secrets.SIMPLE_SWAP_API_KEY }}';" >> lib/.secrets.g.dart
echo "const simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart echo "const simpleSwapApiKeyDesktop = '${{ secrets.SIMPLE_SWAP_API_KEY_DESKTOP }}';" >> lib/.secrets.g.dart
echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart echo "const onramperApiKey = '${{ secrets.ONRAMPER_API_KEY }}';" >> lib/.secrets.g.dart

View file

@ -19,10 +19,10 @@ class SideShiftExchangeProvider extends ExchangeProvider {
static const affiliateId = secrets.sideShiftAffiliateId; static const affiliateId = secrets.sideShiftAffiliateId;
static const apiBaseUrl = 'https://sideshift.ai/api'; static const apiBaseUrl = 'https://sideshift.ai/api';
static const rangePath = '/v1/pairs'; static const rangePath = '/v2/pair';
static const orderPath = '/v1/orders'; static const orderPath = '/v2/shifts';
static const quotePath = '/v1/quotes'; static const quotePath = '/v2/quotes';
static const permissionPath = '/v1/permissions'; static const permissionPath = '/v2/permissions';
static const List<CryptoCurrency> _notSupported = [ static const List<CryptoCurrency> _notSupported = [
CryptoCurrency.xhv, CryptoCurrency.xhv,
@ -36,23 +36,22 @@ class SideShiftExchangeProvider extends ExchangeProvider {
CryptoCurrency.scrt, CryptoCurrency.scrt,
CryptoCurrency.stx, CryptoCurrency.stx,
CryptoCurrency.bttc, CryptoCurrency.bttc,
CryptoCurrency.usdt,
CryptoCurrency.eos,
]; ];
static List<ExchangePair> _supportedPairs() { static List<ExchangePair> _supportedPairs() {
final supportedCurrencies = CryptoCurrency.all final supportedCurrencies =
.where((element) => !_notSupported.contains(element)) CryptoCurrency.all.where((element) => !_notSupported.contains(element)).toList();
.toList();
return supportedCurrencies return supportedCurrencies
.map((i) => supportedCurrencies .map((i) => supportedCurrencies.map((k) => ExchangePair(from: i, to: k, reverse: true)))
.map((k) => ExchangePair(from: i, to: k, reverse: true)))
.expand((i) => i) .expand((i) => i)
.toList(); .toList();
} }
@override @override
ExchangeProviderDescription get description => ExchangeProviderDescription get description => ExchangeProviderDescription.sideShift;
ExchangeProviderDescription.sideShift;
@override @override
Future<double> fetchRate( Future<double> fetchRate(
@ -65,17 +64,18 @@ class SideShiftExchangeProvider extends ExchangeProvider {
if (amount == 0) { if (amount == 0) {
return 0.0; return 0.0;
} }
final fromCurrency = _normalizeCryptoCurrency(from);
final toCurrency = _normalizeCryptoCurrency(to); final fromCurrency = from.title.toLowerCase();
final url = final toCurrency = to.title.toLowerCase();
apiBaseUrl + rangePath + '/' + fromCurrency + '/' + toCurrency; final depositNetwork = _networkFor(from);
final settleNetwork = _networkFor(to);
final url = "$apiBaseUrl$rangePath/$fromCurrency-$depositNetwork/$toCurrency-$settleNetwork";
final uri = Uri.parse(url); final uri = Uri.parse(url);
final response = await get(uri); final response = await get(uri);
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final rate = double.parse(responseJSON['rate'] as String); final rate = double.parse(responseJSON['rate'] as String);
final max = double.parse(responseJSON['max'] as String);
if (amount > max) return 0.00;
return rate; return rate;
} catch (_) { } catch (_) {
@ -101,25 +101,38 @@ class SideShiftExchangeProvider extends ExchangeProvider {
} }
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final canCreateOrder = responseJSON['createOrder'] as bool; final cancreateShift = responseJSON['createShift'] as bool;
final canCreateQuote = responseJSON['createQuote'] as bool; return cancreateShift;
return canCreateOrder && canCreateQuote;
} }
@override @override
Future<Trade> createTrade( Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
{required TradeRequest request, required bool isFixedRateMode}) async {
final _request = request as SideShiftRequest; final _request = request as SideShiftRequest;
final quoteId = await _createQuote(_request); String url = '';
final url = apiBaseUrl + orderPath; final depositCoin = request.depositMethod.title.toLowerCase();
final headers = {'Content-Type': 'application/json'}; final settleCoin = request.settleMethod.title.toLowerCase();
final body = { final body = {
'type': 'fixed',
'quoteId': quoteId,
'affiliateId': affiliateId, 'affiliateId': affiliateId,
'settleAddress': _request.settleAddress, 'settleAddress': _request.settleAddress,
'refundAddress': _request.refundAddress 'refundAddress': _request.refundAddress,
}; };
if (isFixedRateMode) {
final quoteId = await _createQuote(_request);
body['quoteId'] = quoteId;
url = apiBaseUrl + orderPath + '/fixed';
} else {
url = apiBaseUrl + orderPath + '/variable';
final depositNetwork = _networkFor(request.depositMethod);
final settleNetwork = _networkFor(request.settleMethod);
body["depositCoin"] = depositCoin;
body["settleCoin"] = settleCoin;
body["settleNetwork"] = settleNetwork;
body["depositNetwork"] = depositNetwork;
}
final headers = {'Content-Type': 'application/json'};
final uri = Uri.parse(url); final uri = Uri.parse(url);
final response = await post(uri, headers: headers, body: json.encode(body)); final response = await post(uri, headers: headers, body: json.encode(body));
@ -136,8 +149,9 @@ class SideShiftExchangeProvider extends ExchangeProvider {
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final id = responseJSON['id'] as String; final id = responseJSON['id'] as String;
final inputAddress = responseJSON['depositAddress']['address'] as String; final inputAddress = responseJSON['depositAddress'] as String;
final settleAddress = responseJSON['settleAddress']['address'] as String; final settleAddress = responseJSON['settleAddress'] as String;
final depositAmount = responseJSON['depositAmount'] as String?;
return Trade( return Trade(
id: id, id: id,
@ -147,7 +161,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
inputAddress: inputAddress, inputAddress: inputAddress,
refundAddress: settleAddress, refundAddress: settleAddress,
state: TradeState.created, state: TradeState.created,
amount: _request.depositAmount, amount: depositAmount ?? _request.depositAmount,
payoutAddress: settleAddress, payoutAddress: settleAddress,
createdAt: DateTime.now(), createdAt: DateTime.now(),
); );
@ -156,13 +170,17 @@ class SideShiftExchangeProvider extends ExchangeProvider {
Future<String> _createQuote(SideShiftRequest request) async { Future<String> _createQuote(SideShiftRequest request) async {
final url = apiBaseUrl + quotePath; final url = apiBaseUrl + quotePath;
final headers = {'Content-Type': 'application/json'}; final headers = {'Content-Type': 'application/json'};
final depositMethod = _normalizeCryptoCurrency(request.depositMethod); final depositMethod = request.depositMethod.title.toLowerCase();
final settleMethod = _normalizeCryptoCurrency(request.settleMethod); final settleMethod = request.settleMethod.title.toLowerCase();
final depositNetwork = _networkFor(request.depositMethod);
final settleNetwork = _networkFor(request.settleMethod);
final body = { final body = {
'depositMethod': depositMethod, 'depositCoin': depositMethod,
'settleMethod': settleMethod, 'settleCoin': settleMethod,
'affiliateId': affiliateId, 'affiliateId': affiliateId,
'depositAmount': request.depositAmount, 'settleAmount': request.depositAmount,
'settleNetwork': settleNetwork,
'depositNetwork': depositNetwork,
}; };
final uri = Uri.parse(url); final uri = Uri.parse(url);
final response = await post(uri, headers: headers, body: json.encode(body)); final response = await post(uri, headers: headers, body: json.encode(body));
@ -189,9 +207,15 @@ class SideShiftExchangeProvider extends ExchangeProvider {
{required CryptoCurrency from, {required CryptoCurrency from,
required CryptoCurrency to, required CryptoCurrency to,
required bool isFixedRateMode}) async { required bool isFixedRateMode}) async {
final fromCurrency = _normalizeCryptoCurrency(from); final fromCurrency = isFixedRateMode ? to : from;
final toCurrency = _normalizeCryptoCurrency(to); final toCurrency = isFixedRateMode ? from : to;
final url = apiBaseUrl + rangePath + '/' + fromCurrency + '/' + toCurrency;
final fromNetwork = _networkFor(fromCurrency);
final toNetwork = _networkFor(toCurrency);
final url =
"$apiBaseUrl$rangePath/${fromCurrency.title.toLowerCase()}-$fromNetwork/${toCurrency.title.toLowerCase()}-$toNetwork";
final uri = Uri.parse(url); final uri = Uri.parse(url);
final response = await get(uri); final response = await get(uri);
@ -210,6 +234,14 @@ class SideShiftExchangeProvider extends ExchangeProvider {
final min = double.tryParse(responseJSON['min'] as String? ?? ''); final min = double.tryParse(responseJSON['min'] as String? ?? '');
final max = double.tryParse(responseJSON['max'] as String? ?? ''); final max = double.tryParse(responseJSON['max'] as String? ?? '');
if (isFixedRateMode) {
final currentRate = double.parse(responseJSON['rate'] as String);
return Limits(
min: min != null ? (min * currentRate) : null,
max: max != null ? (max * currentRate) : null,
);
}
return Limits(min: min, max: max); return Limits(min: min, max: max);
} }
@ -227,8 +259,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final error = responseJSON['error']['message'] as String; final error = responseJSON['error']['message'] as String;
throw TradeNotFoundException(id, throw TradeNotFoundException(id, provider: description, description: error);
provider: description, description: error);
} }
if (response.statusCode != 200) { if (response.statusCode != 200) {
@ -236,24 +267,21 @@ class SideShiftExchangeProvider extends ExchangeProvider {
} }
final responseJSON = json.decode(response.body) as Map<String, dynamic>; final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final fromCurrency = responseJSON['depositMethodId'] as String; final fromCurrency = responseJSON['depositCoin'] as String;
final from = CryptoCurrency.fromString(fromCurrency); final from = CryptoCurrency.fromString(fromCurrency);
final toCurrency = responseJSON['settleMethodId'] as String; final toCurrency = responseJSON['settleCoin'] as String;
final to = CryptoCurrency.fromString(toCurrency); final to = CryptoCurrency.fromString(toCurrency);
final inputAddress = responseJSON['depositAddress']['address'] as String; final inputAddress = responseJSON['depositAddress'] as String;
final expectedSendAmount = responseJSON['depositAmount'].toString(); final expectedSendAmount = responseJSON['depositAmount'] as String?;
final deposits = responseJSON['deposits'] as List?; final status = responseJSON['status'] as String?;
final settleAddress = responseJSON['settleAddress']['address'] as String; final settleAddress = responseJSON['settleAddress'] as String;
TradeState? state; TradeState? state;
String? status;
if (deposits?.isNotEmpty ?? false) {
status = deposits![0]['status'] as String?;
}
state = TradeState.deserialize(raw: status ?? 'created'); state = TradeState.deserialize(raw: status ?? 'created');
final isVariable = (responseJSON['type'] as String) == 'variable';
final expiredAtRaw = responseJSON['expiresAtISO'] as String; final expiredAtRaw = responseJSON['expiresAt'] as String;
final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal(); final expiredAt = isVariable ? null : DateTime.tryParse(expiredAtRaw)?.toLocal();
return Trade( return Trade(
id: id, id: id,
@ -261,11 +289,10 @@ class SideShiftExchangeProvider extends ExchangeProvider {
to: to, to: to,
provider: description, provider: description,
inputAddress: inputAddress, inputAddress: inputAddress,
amount: expectedSendAmount, amount: expectedSendAmount ?? '',
state: state, state: state,
expiredAt: expiredAt, expiredAt: expiredAt,
payoutAddress: settleAddress payoutAddress: settleAddress);
);
} }
@override @override
@ -280,28 +307,25 @@ class SideShiftExchangeProvider extends ExchangeProvider {
@override @override
String get title => 'SideShift'; String get title => 'SideShift';
static String _normalizeCryptoCurrency(CryptoCurrency currency) { String _networkFor(CryptoCurrency currency) =>
switch (currency) { currency.tag != null ? _normalizeTag(currency.tag!) : 'mainnet';
case CryptoCurrency.zaddr:
return 'zaddr'; String _normalizeTag(String tag) {
case CryptoCurrency.zec: switch (tag) {
return 'zec'; case 'ETH':
case CryptoCurrency.bnb: return 'ethereum';
return currency.tag!.toLowerCase(); case 'TRX':
case CryptoCurrency.usdterc20: return 'tron';
return 'usdtErc20'; case 'LN':
case CryptoCurrency.usdttrc20: return 'lightning';
return 'usdtTrc20'; case 'POLY':
case CryptoCurrency.usdcpoly:
return 'usdcpolygon';
case CryptoCurrency.usdcsol:
return 'usdcsol';
case CryptoCurrency.maticpoly:
return 'polygon'; return 'polygon';
case CryptoCurrency.btcln: case 'ZEC':
return 'ln'; return 'zcash';
case 'AVAXC':
return 'avax';
default: default:
return currency.title.toLowerCase(); return tag.toLowerCase();
} }
} }
} }

View file

@ -26,7 +26,12 @@ class TransactionsPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return GestureDetector(
onLongPress: () => dashboardViewModel.balanceViewModel.isReversing =
!dashboardViewModel.balanceViewModel.isReversing,
onLongPressUp: () => dashboardViewModel.balanceViewModel.isReversing =
!dashboardViewModel.balanceViewModel.isReversing,
child: Container(
color: ResponsiveLayoutUtil.instance.isMobile(context) color: ResponsiveLayoutUtil.instance.isMobile(context)
? null ? null
: Theme.of(context).colorScheme.background, : Theme.of(context).colorScheme.background,
@ -79,17 +84,20 @@ class TransactionsPage extends StatelessWidget {
final transactionInfo = item.transaction; final transactionInfo = item.transaction;
return AnonpayTransactionRow( return AnonpayTransactionRow(
onTap: () => Navigator.of(context) onTap: () => Navigator.of(context).pushNamed(
.pushNamed(Routes.anonPayDetailsPage, arguments: transactionInfo), Routes.anonPayDetailsPage,
arguments: transactionInfo),
currency: transactionInfo.fiatAmount != null currency: transactionInfo.fiatAmount != null
? transactionInfo.fiatEquiv ?? '' ? transactionInfo.fiatEquiv ?? ''
: CryptoCurrency.fromFullName(transactionInfo.coinTo) : CryptoCurrency.fromFullName(
transactionInfo.coinTo)
.name .name
.toUpperCase(), .toUpperCase(),
provider: transactionInfo.provider, provider: transactionInfo.provider,
amount: transactionInfo.fiatAmount?.toString() ?? amount: transactionInfo.fiatAmount?.toString() ??
(transactionInfo.amountTo?.toString() ?? ''), (transactionInfo.amountTo?.toString() ?? ''),
createdAt: DateFormat('HH:mm').format(transactionInfo.createdAt), createdAt: DateFormat('HH:mm')
.format(transactionInfo.createdAt),
); );
} }
@ -98,13 +106,16 @@ class TransactionsPage extends StatelessWidget {
return Observer( return Observer(
builder: (_) => TradeRow( builder: (_) => TradeRow(
onTap: () => Navigator.of(context) onTap: () => Navigator.of(context).pushNamed(
.pushNamed(Routes.tradeDetails, arguments: trade), Routes.tradeDetails,
arguments: trade),
provider: trade.provider, provider: trade.provider,
from: trade.from, from: trade.from,
to: trade.to, to: trade.to,
createdAtFormattedDate: trade.createdAt != null createdAtFormattedDate:
? DateFormat('HH:mm').format(trade.createdAt!) trade.createdAt != null
? DateFormat('HH:mm')
.format(trade.createdAt!)
: null, : null,
formattedAmount: item.tradeFormattedAmount)); formattedAmount: item.tradeFormattedAmount));
} }
@ -115,12 +126,13 @@ class TransactionsPage extends StatelessWidget {
return Observer( return Observer(
builder: (_) => OrderRow( builder: (_) => OrderRow(
onTap: () => Navigator.of(context) onTap: () => Navigator.of(context)
.pushNamed(Routes.orderDetails, arguments: order), .pushNamed(Routes.orderDetails,
arguments: order),
provider: order.provider, provider: order.provider,
from: order.from!, from: order.from!,
to: order.to!, to: order.to!,
createdAtFormattedDate: createdAtFormattedDate: DateFormat('HH:mm')
DateFormat('HH:mm').format(order.createdAt), .format(order.createdAt),
formattedAmount: item.orderFormattedAmount, formattedAmount: item.orderFormattedAmount,
)); ));
} }
@ -132,12 +144,16 @@ class TransactionsPage extends StatelessWidget {
S.of(context).placeholder_transactions, S.of(context).placeholder_transactions,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14,
color: Theme.of(context).primaryTextTheme!.labelSmall!.decorationColor!), color: Theme.of(context)
.primaryTextTheme
.labelSmall!
.decorationColor!),
), ),
); );
})) }))
], ],
), ),
),
); );
} }
} }

View file

@ -145,7 +145,12 @@ class SendPage extends BasePage {
Widget body(BuildContext context) { Widget body(BuildContext context) {
_setEffects(context); _setEffects(context);
return Form( return GestureDetector(
onLongPress: () => sendViewModel.balanceViewModel.isReversing =
!sendViewModel.balanceViewModel.isReversing,
onLongPressUp: () => sendViewModel.balanceViewModel.isReversing =
!sendViewModel.balanceViewModel.isReversing,
child: Form(
key: _formKey, key: _formKey,
child: ScrollableWithBottomSection( child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24), contentPadding: EdgeInsets.only(bottom: 24),
@ -174,8 +179,8 @@ class SendPage extends BasePage {
}, },
)), )),
Padding( Padding(
padding: padding: EdgeInsets.only(
EdgeInsets.only(top: 10, left: 24, right: 24, bottom: 10), top: 10, left: 24, right: 24, bottom: 10),
child: Container( child: Container(
height: 10, height: 10,
child: Observer( child: Observer(
@ -192,10 +197,12 @@ class SendPage extends BasePage {
dotWidth: 6.0, dotWidth: 6.0,
dotHeight: 6.0, dotHeight: 6.0,
dotColor: Theme.of(context) dotColor: Theme.of(context)
.primaryTextTheme!.displaySmall! .primaryTextTheme
!.displaySmall!
.backgroundColor!, .backgroundColor!,
activeDotColor: Theme.of(context) activeDotColor: Theme.of(context)
.primaryTextTheme!.displayMedium! .primaryTextTheme
!.displayMedium!
.backgroundColor!), .backgroundColor!),
) )
: Offstage(); : Offstage();
@ -218,7 +225,8 @@ class SendPage extends BasePage {
return Row( return Row(
children: <Widget>[ children: <Widget>[
AddTemplateButton( AddTemplateButton(
onTap: () => Navigator.of(context).pushNamed(Routes.sendTemplate), onTap: () => Navigator.of(context)
.pushNamed(Routes.sendTemplate),
currentTemplatesLength: templates.length, currentTemplatesLength: templates.length,
), ),
ListView.builder( ListView.builder(
@ -231,35 +239,52 @@ class SendPage extends BasePage {
return TemplateTile( return TemplateTile(
key: UniqueKey(), key: UniqueKey(),
to: template.name, to: template.name,
amount: template.isCurrencySelected ? template.amount : template.amountFiat, amount: template.isCurrencySelected
from: template.isCurrencySelected ? template.cryptoCurrency : template.fiatCurrency, ? template.amount
: template.amountFiat,
from: template.isCurrencySelected
? template.cryptoCurrency
: template.fiatCurrency,
onTap: () async { onTap: () async {
final fiatFromTemplate = FiatCurrency.all.singleWhere((element) => element.title == template.fiatCurrency); final fiatFromTemplate = FiatCurrency
.all
.singleWhere((element) =>
element.title ==
template.fiatCurrency);
final output = _defineCurrentOutput(); final output = _defineCurrentOutput();
output.address = template.address; output.address = template.address;
if(template.isCurrencySelected){ if (template.isCurrencySelected) {
output.setCryptoAmount(template.amount); output
}else{ .setCryptoAmount(template.amount);
sendViewModel.setFiatCurrency(fiatFromTemplate); } else {
output.setFiatAmount(template.amountFiat); sendViewModel.setFiatCurrency(
fiatFromTemplate);
output.setFiatAmount(
template.amountFiat);
} }
output.resetParsedAddress(); output.resetParsedAddress();
await output.fetchParsedAddress(context); await output
.fetchParsedAddress(context);
}, },
onRemove: () { onRemove: () {
showPopUp<void>( showPopUp<void>(
context: context, context: context,
builder: (dialogContext) { builder: (dialogContext) {
return AlertWithTwoActions( return AlertWithTwoActions(
alertTitle: S.of(context).template, alertTitle:
S.of(context).template,
alertContent: S alertContent: S
.of(context) .of(context)
.confirm_delete_template, .confirm_delete_template,
rightButtonText: S.of(context).delete, rightButtonText:
leftButtonText: S.of(context).cancel, S.of(context).delete,
leftButtonText:
S.of(context).cancel,
actionRightButton: () { actionRightButton: () {
Navigator.of(dialogContext).pop(); Navigator.of(dialogContext)
sendViewModel.sendTemplateViewModel .pop();
sendViewModel
.sendTemplateViewModel
.removeTemplate( .removeTemplate(
template: template); template: template);
}, },
@ -286,19 +311,19 @@ class SendPage extends BasePage {
bottomSection: Column( bottomSection: Column(
children: [ children: [
if (sendViewModel.hasCurrecyChanger) if (sendViewModel.hasCurrecyChanger)
Observer(builder: (_) => Observer(
Padding( builder: (_) => Padding(
padding: EdgeInsets.only(bottom: 12), padding: EdgeInsets.only(bottom: 12),
child: PrimaryButton( child: PrimaryButton(
onPressed: () => presentCurrencyPicker(context), onPressed: () => presentCurrencyPicker(context),
text: 'Change your asset (${sendViewModel.selectedCryptoCurrency})', text:
'Change your asset (${sendViewModel.selectedCryptoCurrency})',
color: Colors.transparent, color: Colors.transparent,
textColor: Theme.of(context) textColor: Theme.of(context)
.accentTextTheme!.displaySmall! .accentTextTheme
!.displaySmall!
.decorationColor!, .decorationColor!,
) ))),
)
),
if (sendViewModel.hasMultiRecipient) if (sendViewModel.hasMultiRecipient)
Padding( Padding(
padding: EdgeInsets.only(bottom: 12), padding: EdgeInsets.only(bottom: 12),
@ -306,24 +331,28 @@ class SendPage extends BasePage {
onPressed: () { onPressed: () {
sendViewModel.addOutput(); sendViewModel.addOutput();
Future.delayed(const Duration(milliseconds: 250), () { Future.delayed(const Duration(milliseconds: 250), () {
controller.jumpToPage(sendViewModel.outputs.length - 1); controller
.jumpToPage(sendViewModel.outputs.length - 1);
}); });
}, },
text: S.of(context).add_receiver, text: S.of(context).add_receiver,
color: Colors.transparent, color: Colors.transparent,
textColor: Theme.of(context) textColor: Theme.of(context)
.accentTextTheme!.displaySmall! .accentTextTheme
!.displaySmall!
.decorationColor!, .decorationColor!,
isDottedBorder: true, isDottedBorder: true,
borderColor: Theme.of(context) borderColor: Theme.of(context)
.primaryTextTheme!.displaySmall! .primaryTextTheme
!.displaySmall!
.decorationColor!, .decorationColor!,
)), )),
Observer( Observer(
builder: (_) { builder: (_) {
return LoadingPrimaryButton( return LoadingPrimaryButton(
onPressed: () async { onPressed: () async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) { if (_formKey.currentState != null &&
!_formKey.currentState!.validate()) {
if (sendViewModel.outputs.length > 1) { if (sendViewModel.outputs.length > 1) {
showErrorValidationAlert(context); showErrorValidationAlert(context);
} }
@ -333,7 +362,8 @@ class SendPage extends BasePage {
final notValidItems = sendViewModel.outputs final notValidItems = sendViewModel.outputs
.where((item) => .where((item) =>
item.address.isEmpty || item.cryptoAmount.isEmpty) item.address.isEmpty ||
item.cryptoAmount.isEmpty)
.toList(); .toList();
if (notValidItems.isNotEmpty ?? false) { if (notValidItems.isNotEmpty ?? false) {
@ -342,10 +372,10 @@ class SendPage extends BasePage {
} }
await sendViewModel.createTransaction(); await sendViewModel.createTransaction();
}, },
text: S.of(context).send, text: S.of(context).send,
color: Theme.of(context).accentTextTheme!.bodyLarge!.color!, color:
Theme.of(context).accentTextTheme!.bodyLarge!.color!,
textColor: Colors.white, textColor: Colors.white,
isLoading: sendViewModel.state is IsExecutingState || isLoading: sendViewModel.state is IsExecutingState ||
sendViewModel.state is TransactionCommitting, sendViewModel.state is TransactionCommitting,
@ -355,6 +385,7 @@ class SendPage extends BasePage {
) )
], ],
)), )),
),
); );
} }
@ -389,10 +420,12 @@ class SendPage extends BasePage {
amount: S.of(context).send_amount, amount: S.of(context).send_amount,
amountValue: amountValue:
sendViewModel.pendingTransaction!.amountFormatted, sendViewModel.pendingTransaction!.amountFormatted,
fiatAmountValue: sendViewModel.pendingTransactionFiatAmountFormatted, fiatAmountValue:
sendViewModel.pendingTransactionFiatAmountFormatted,
fee: S.of(context).send_fee, fee: S.of(context).send_fee,
feeValue: sendViewModel.pendingTransaction!.feeFormatted, feeValue: sendViewModel.pendingTransaction!.feeFormatted,
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmountFormatted, feeFiatAmount: sendViewModel
.pendingTransactionFeeFiatAmountFormatted,
outputs: sendViewModel.outputs, outputs: sendViewModel.outputs,
rightButtonText: S.of(context).ok, rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel, leftButtonText: S.of(context).cancel,
@ -413,7 +446,8 @@ class SendPage extends BasePage {
return AlertWithOneAction( return AlertWithOneAction(
alertTitle: '', alertTitle: '',
alertContent: S.of(context).send_success( alertContent: S.of(context).send_success(
sendViewModel.selectedCryptoCurrency.toString()), sendViewModel.selectedCryptoCurrency
.toString()),
buttonText: S.of(context).ok, buttonText: S.of(context).ok,
buttonAction: () { buttonAction: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
@ -466,10 +500,12 @@ class SendPage extends BasePage {
builder: (_) => Picker( builder: (_) => Picker(
items: sendViewModel.currencies, items: sendViewModel.currencies,
displayItem: (Object item) => item.toString(), displayItem: (Object item) => item.toString(),
selectedAtIndex: sendViewModel.currencies.indexOf(sendViewModel.selectedCryptoCurrency), selectedAtIndex: sendViewModel.currencies
.indexOf(sendViewModel.selectedCryptoCurrency),
title: S.of(context).please_select, title: S.of(context).please_select,
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
onItemSelected: (CryptoCurrency cur) => sendViewModel.selectedCryptoCurrency = cur, onItemSelected: (CryptoCurrency cur) =>
sendViewModel.selectedCryptoCurrency = cur,
), ),
context: context); context: context);
} }

View file

@ -149,12 +149,26 @@ class WalletListBodyState extends State<WalletListBody> {
return wallet.isCurrent return wallet.isCurrent
? row ? row
: Slidable( : Row(children: [
key: Key('${wallet.key}'), Expanded(child: row),
startActionPane: _actionPane(wallet), GestureDetector(
endActionPane: _actionPane(wallet), onTap: () => _removeWallet(wallet),
child: row, child: Container(
); height: 40,
width: 44,
padding: EdgeInsets.only(right: 20),
child: Center(
child: Image.asset('assets/images/trash.png',
height: 16,
width: 16,
color: Theme.of(context)
.primaryTextTheme
.titleLarge!
.color),
),
),
)
]);
}), }),
), ),
), ),
@ -277,18 +291,4 @@ class WalletListBodyState extends State<WalletListBody> {
_progressBar = null; _progressBar = null;
}); });
} }
ActionPane _actionPane(WalletListItem wallet) => ActionPane(
motion: const ScrollMotion(),
extentRatio: 0.3,
children: [
SlidableAction(
onPressed: (_) => _removeWallet(wallet),
backgroundColor: Colors.red,
foregroundColor: Colors.white,
icon: CupertinoIcons.delete,
label: S.of(context).delete,
),
],
);
} }

View file

@ -13,7 +13,6 @@ import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
import 'package:cw_core/keyable.dart'; import 'package:cw_core/keyable.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
class TransactionListItem extends ActionListItem with Keyable { class TransactionListItem extends ActionListItem with Keyable {
TransactionListItem( TransactionListItem(
{required this.transaction, {required this.transaction,
@ -28,7 +27,7 @@ class TransactionListItem extends ActionListItem with Keyable {
FiatCurrency get fiatCurrency => settingsStore.fiatCurrency; FiatCurrency get fiatCurrency => settingsStore.fiatCurrency;
BalanceDisplayMode get displayMode => settingsStore.balanceDisplayMode; BalanceDisplayMode get displayMode => balanceViewModel.displayMode;
@override @override
dynamic get keyIndex => transaction.id; dynamic get keyIndex => transaction.id;

View file

@ -114,6 +114,10 @@ abstract class ExchangeTradeViewModelBase with Store {
updatedTrade.createdAt = trade.createdAt; updatedTrade.createdAt = trade.createdAt;
} }
if (updatedTrade.amount.isEmpty) {
updatedTrade.amount = trade.amount;
}
trade = updatedTrade; trade = updatedTrade;
_updateItems(); _updateItems();
@ -123,7 +127,8 @@ abstract class ExchangeTradeViewModelBase with Store {
} }
void _updateItems() { void _updateItems() {
final tagFrom = tradesStore.trade!.from.tag != null ? '${tradesStore.trade!.from.tag}' + ' ' : ''; final tagFrom =
tradesStore.trade!.from.tag != null ? '${tradesStore.trade!.from.tag}' + ' ' : '';
final tagTo = tradesStore.trade!.to.tag != null ? '${tradesStore.trade!.to.tag}' + ' ' : ''; final tagTo = tradesStore.trade!.to.tag != null ? '${tradesStore.trade!.to.tag}' + ' ' : '';
items.clear(); items.clear();
items.add(ExchangeTradeItem( items.add(ExchangeTradeItem(

View file

@ -443,7 +443,9 @@ abstract class ExchangeViewModelBase with Store {
request = SideShiftRequest( request = SideShiftRequest(
depositMethod: depositCurrency, depositMethod: depositCurrency,
settleMethod: receiveCurrency, settleMethod: receiveCurrency,
depositAmount: depositAmount.replaceAll(',', '.'), depositAmount: isFixedRateMode
? receiveAmount.replaceAll(',', '.')
: depositAmount.replaceAll(',', '.'),
settleAddress: receiveAddress, settleAddress: receiveAddress,
refundAddress: depositAddress, refundAddress: depositAddress,
); );

View file

@ -20,7 +20,6 @@ class SecretKey {
SecretKey('moonPayApiKey', () => ''), SecretKey('moonPayApiKey', () => ''),
SecretKey('moonPaySecretKey', () => ''), SecretKey('moonPaySecretKey', () => ''),
SecretKey('sideShiftAffiliateId', () => ''), SecretKey('sideShiftAffiliateId', () => ''),
SecretKey('sideShiftApiKey', () => ''),
SecretKey('simpleSwapApiKey', () => ''), SecretKey('simpleSwapApiKey', () => ''),
SecretKey('simpleSwapApiKeyDesktop', () => ''), SecretKey('simpleSwapApiKeyDesktop', () => ''),
SecretKey('anypayToken', () => ''), SecretKey('anypayToken', () => ''),