1
+import 'package:cake_wallet/core/auth_service.dart';
2
import 'package:cake_wallet/di.dart';
3
import 'package:cake_wallet/src/screens/exchange/widgets/desktop_exchange_cards_section.dart';
4
import 'package:cake_wallet/src/screens/exchange/widgets/mobile_exchange_cards_section.dart';
38
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
39
40
class ExchangePage extends BasePage {
40
- ExchangePage(this.exchangeViewModel) {
41
+ ExchangePage(this.exchangeViewModel, this.authService) {
42
depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
43
? exchangeViewModel.wallet.name
44
: null;
48
}
49
50
final ExchangeViewModel exchangeViewModel;
51
+ final AuthService authService;
52
final depositKey = GlobalKey<ExchangeCardState>();
53
final receiveKey = GlobalKey<ExchangeCardState>();
54
final _formKey = GlobalKey<FormState>();
91
92
@override
93
Widget middle(BuildContext context) => Row(
92
- mainAxisAlignment: MainAxisAlignment.center,
93
- children: [
94
- Padding(
95
- padding: const EdgeInsets.only(right:6.0),
96
- child: Observer(builder: (_) => SyncIndicatorIcon(isSynced: exchangeViewModel.status is SyncedSyncStatus),)
97
- ),
98
- PresentProviderPicker(exchangeViewModel: exchangeViewModel)
99
- ],
100
- );
101
-
94
+ mainAxisAlignment: MainAxisAlignment.center,
95
+ children: [
96
+ Padding(
97
+ padding: const EdgeInsets.only(right: 6.0),
98
+ child: Observer(
99
+ builder: (_) =>
100
+ SyncIndicatorIcon(isSynced: exchangeViewModel.status is SyncedSyncStatus),
101
+ )),
102
+ PresentProviderPicker(exchangeViewModel: exchangeViewModel)
103
+ ],
104
+ );
105
106
@override
107
Widget trailing(BuildContext context) => TrailButton(
113
114
@override
115
Widget? leading(BuildContext context) {
113
- final _backButton = Icon(Icons.arrow_back_ios,
116
+ final _backButton = Icon(
117
+ Icons.arrow_back_ios,
118
color: titleColor,
119
size: 16,
120
);
117
- final _closeButton = currentTheme.type == ThemeType.dark
118
- ? closeButtonImageDarkTheme : closeButtonImage;
121
+ final _closeButton =
122
+ currentTheme.type == ThemeType.dark ? closeButtonImageDarkTheme : closeButtonImage;
123
124
bool isMobileView = ResponsiveLayoutUtil.instance.isMobile;
125
130
child: ButtonTheme(
131
minWidth: double.minPositive,
132
child: Semantics(
129
- label: !isMobileView
130
- ? S.of(context).close
131
- : S.of(context).seed_alert_back,
133
+ label: !isMobileView ? S.of(context).close : S.of(context).seed_alert_back,
134
child: TextButton(
135
style: ButtonStyle(
134
- overlayColor: MaterialStateColor.resolveWith(
135
- (states) => Colors.transparent),
136
+ overlayColor: MaterialStateColor.resolveWith((states) => Colors.transparent),
137
),
138
onPressed: () => onClose(context),
139
child: !isMobileView ? _closeButton : _backButton,
146
147
@override
148
Widget body(BuildContext context) {
148
- WidgetsBinding.instance
149
- .addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
149
+ WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
150
151
return KeyboardActions(
152
disableScroll: true,
153
config: KeyboardActionsConfig(
154
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
155
- keyboardBarColor:
156
- Theme.of(context).accentTextTheme!.bodyLarge!.backgroundColor!,
155
+ keyboardBarColor: Theme.of(context).accentTextTheme.bodyLarge!.backgroundColor!,
156
nextFocus: false,
157
actions: [
158
KeyboardActionsItem(
160
- focusNode: _depositAmountFocus,
161
- toolbarButtons: [(_) => KeyboardDoneButton()]),
159
+ focusNode: _depositAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()]),
160
KeyboardActionsItem(
163
- focusNode: _receiveAmountFocus,
164
- toolbarButtons: [(_) => KeyboardDoneButton()])
161
+ focusNode: _receiveAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()])
162
]),
163
child: Container(
164
color: Theme.of(context).colorScheme.background,
166
key: _formKey,
167
child: ScrollableWithBottomSection(
168
contentPadding: EdgeInsets.only(bottom: 24),
172
- content: Observer(builder: (_) => Column(
173
- children: <Widget>[
174
- _exchangeCardsSection(context),
175
- Padding(
176
- padding: EdgeInsets.only(top: 12, left: 24),
177
- child: Row(
178
- mainAxisAlignment: MainAxisAlignment.start,
179
- children: [
180
- StandardCheckbox(
181
- value: exchangeViewModel.isFixedRateMode,
182
- caption: S.of(context).fixed_rate,
183
- onChanged: (value) =>
184
- exchangeViewModel.isFixedRateMode = value,
185
- ),
186
- ],
187
- )
188
- ),
189
- SizedBox(height: 30),
190
- _buildTemplateSection(context)
169
+ content: Observer(
170
+ builder: (_) => Column(
171
+ children: <Widget>[
172
+ _exchangeCardsSection(context),
173
+ Padding(
174
+ padding: EdgeInsets.only(top: 12, left: 24),
175
+ child: Row(
176
+ mainAxisAlignment: MainAxisAlignment.start,
177
+ children: [
178
+ StandardCheckbox(
179
+ value: exchangeViewModel.isFixedRateMode,
180
+ caption: S.of(context).fixed_rate,
181
+ onChanged: (value) => exchangeViewModel.isFixedRateMode = value,
182
+ ),
183
+ ],
184
+ )),
185
+ SizedBox(height: 30),
186
+ _buildTemplateSection(context)
187
],
188
),
189
),
194
- bottomSectionPadding:
195
- EdgeInsets.only(left: 24, right: 24, bottom: 24),
190
+ bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
191
bottomSection: Column(children: <Widget>[
192
Padding(
193
padding: EdgeInsets.only(bottom: 15),
205
textAlign: TextAlign.center,
206
style: TextStyle(
207
color: Theme.of(context)
213
- .primaryTextTheme!
214
- .displayLarge!
208
+ .primaryTextTheme.displayLarge!
209
.decorationColor!,
210
fontWeight: FontWeight.w500,
211
fontSize: 12),
217
builder: (_) => LoadingPrimaryButton(
218
text: S.of(context).exchange,
219
onPressed: () {
226
- if (_formKey.currentState != null && _formKey.currentState!.validate()) {
227
- if ((exchangeViewModel.depositCurrency ==
228
- CryptoCurrency.xmr) &&
229
- (!(exchangeViewModel.status
230
- is SyncedSyncStatus))) {
220
+ if (_formKey.currentState != null &&
221
+ _formKey.currentState!.validate()) {
222
+ if ((exchangeViewModel.depositCurrency == CryptoCurrency.xmr) &&
223
+ (!(exchangeViewModel.status is SyncedSyncStatus))) {
224
showPopUp<void>(
225
context: context,
226
builder: (BuildContext context) {
227
return AlertWithOneAction(
228
alertTitle: S.of(context).exchange,
236
- alertContent: S
237
- .of(context)
238
- .exchange_sync_alert_content,
229
+ alertContent: S.of(context).exchange_sync_alert_content,
230
buttonText: S.of(context).ok,
240
- buttonAction: () =>
241
- Navigator.of(context).pop());
231
+ buttonAction: () => Navigator.of(context).pop());
232
});
233
} else {
244
- exchangeViewModel.createTrade();
234
+ final check = exchangeViewModel.shouldDisplayTOTP();
235
+ authService.authenticateAction(
236
+ context,
237
+ conditionToDetermineIfToUse2FA: check,
238
+ onAuthSuccess: (value) {
239
+ if (value) {
240
+ exchangeViewModel.createTrade();
241
+ }
242
+ },
243
+ );
244
}
245
}
246
},
248
- color: Theme.of(context).accentTextTheme!.bodyLarge!.color!,
247
+ color: Theme.of(context).accentTextTheme.bodyLarge!.color!,
248
textColor: Colors.white,
249
isDisabled: exchangeViewModel.selectedProviders.isEmpty,
250
isLoading: exchangeViewModel.tradeState is TradeIsCreating)),
263
child: Observer(
264
builder: (_) {
265
final templates = exchangeViewModel.templates;
267
-
266
+
267
return Row(
268
children: <Widget>[
269
AddTemplateButton(
292
builder: (dialogContext) {
293
return AlertWithTwoActions(
294
alertTitle: S.of(context).template,
296
- alertContent:
297
- S.of(context).confirm_delete_template,
295
+ alertContent: S.of(context).confirm_delete_template,
296
rightButtonText: S.of(context).delete,
297
leftButtonText: S.of(context).cancel,
298
actionRightButton: () {
299
Navigator.of(dialogContext).pop();
302
- exchangeViewModel.removeTemplate(
303
- template: template);
300
+ exchangeViewModel.removeTemplate(template: template);
301
exchangeViewModel.updateTemplate();
302
},
306
- actionLeftButton: () =>
307
- Navigator.of(dialogContext).pop());
303
+ actionLeftButton: () => Navigator.of(dialogContext).pop());
304
});
305
},
306
);
314
);
315
}
316
321
- void applyTemplate(BuildContext context,
322
- ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
317
+ void applyTemplate(
318
+ BuildContext context, ExchangeViewModel exchangeViewModel, ExchangeTemplate template) async {
319
exchangeViewModel.changeDepositCurrency(
320
currency: CryptoCurrency.fromString(template.depositCurrency));
321
exchangeViewModel.changeReceiveCurrency(
329
330
var domain = template.depositAddress;
331
var ticker = template.depositCurrency.toLowerCase();
336
- exchangeViewModel.depositAddress =
337
- await fetchParsedAddress(context, domain, ticker);
332
+ exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, ticker);
333
334
domain = template.receiveAddress;
335
ticker = template.receiveCurrency.toLowerCase();
341
- exchangeViewModel.receiveAddress =
342
- await fetchParsedAddress(context, domain, ticker);
336
+ exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, ticker);
337
}
338
345
- void _setReactions(
346
- BuildContext context, ExchangeViewModel exchangeViewModel) {
339
+ void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) {
340
if (_isReactionsSet) {
341
return;
342
}
343
351
- if (exchangeViewModel.isLowFee) {
344
+ if (exchangeViewModel.isLowFee) {
345
_showFeeAlert(context);
346
}
347
352
final limitsState = exchangeViewModel.limitsState;
353
354
if (limitsState is LimitsLoadedSuccessfully) {
362
- final min = limitsState.limits.min != null
363
- ? limitsState.limits.min.toString()
364
- : null;
365
- final max = limitsState.limits.max != null
366
- ? limitsState.limits.max.toString()
367
- : null;
368
- final key = exchangeViewModel.isFixedRateMode
369
- ? receiveKey
370
- : depositKey;
355
+ final min = limitsState.limits.min != null ? limitsState.limits.min.toString() : null;
356
+ final max = limitsState.limits.max != null ? limitsState.limits.max.toString() : null;
357
+ final key = exchangeViewModel.isFixedRateMode ? receiveKey : depositKey;
358
key.currentState!.changeLimits(min: min, max: max);
359
}
360
374
- _onCurrencyChange(
375
- exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
376
- _onCurrencyChange(
377
- exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
361
+ _onCurrencyChange(exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
362
+ _onCurrencyChange(exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
363
364
reaction(
365
(_) => exchangeViewModel.wallet.name,
381
- (String _) => _onWalletNameChange(
382
- exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
366
+ (String _) =>
367
+ _onWalletNameChange(exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
368
369
reaction(
370
(_) => exchangeViewModel.wallet.name,
386
- (String _) => _onWalletNameChange(
387
- exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
371
+ (String _) =>
372
+ _onWalletNameChange(exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
373
389
- reaction(
390
- (_) => exchangeViewModel.receiveCurrency,
391
- (CryptoCurrency currency) =>
392
- _onCurrencyChange(currency, exchangeViewModel, receiveKey));
374
+ reaction((_) => exchangeViewModel.receiveCurrency,
375
+ (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, receiveKey));
376
394
- reaction(
395
- (_) => exchangeViewModel.depositCurrency,
396
- (CryptoCurrency currency) =>
397
- _onCurrencyChange(currency, exchangeViewModel, depositKey));
377
+ reaction((_) => exchangeViewModel.depositCurrency,
378
+ (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey));
379
380
reaction((_) => exchangeViewModel.depositAmount, (String amount) {
381
if (depositKey.currentState!.amountController.text != amount) {
389
}
390
});
391
411
- reaction((_) => exchangeViewModel.isDepositAddressEnabled,
412
- (bool isEnabled) {
392
+ reaction((_) => exchangeViewModel.isDepositAddressEnabled, (bool isEnabled) {
393
depositKey.currentState!.isAddressEditable(isEditable: isEnabled);
394
});
395
405
}
406
});
407
428
- reaction((_) => exchangeViewModel.isReceiveAddressEnabled,
429
- (bool isEnabled) {
408
+ reaction((_) => exchangeViewModel.isReceiveAddressEnabled, (bool isEnabled) {
409
receiveKey.currentState!.isAddressEditable(isEditable: isEnabled);
410
});
411
433
- reaction((_) => exchangeViewModel.isReceiveAmountEditable,
434
- (bool isReceiveAmountEditable) {
412
+ reaction((_) => exchangeViewModel.isReceiveAmountEditable, (bool isReceiveAmountEditable) {
413
receiveKey.currentState!.isAmountEditable(isEditable: isReceiveAmountEditable);
414
});
415
461
}
462
});
463
486
- depositAddressController.addListener(
487
- () => exchangeViewModel.depositAddress = depositAddressController.text);
464
+ depositAddressController
465
+ .addListener(() => exchangeViewModel.depositAddress = depositAddressController.text);
466
467
depositAmountController.addListener(() {
468
if (depositAmountController.text != exchangeViewModel.depositAmount) {
491
- _depositAmountDebounce.run(() {
469
+ _depositAmountDebounce.run(() {
470
exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
471
exchangeViewModel.isReceiveAmountEntered = false;
472
});
473
}
474
});
475
498
- receiveAddressController.addListener(
499
- () => exchangeViewModel.receiveAddress = receiveAddressController.text);
476
+ receiveAddressController
477
+ .addListener(() => exchangeViewModel.receiveAddress = receiveAddressController.text);
478
479
receiveAmountController.addListener(() {
480
if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
485
}
486
});
487
510
- reaction((_) => exchangeViewModel.wallet.walletAddresses.address,
511
- (String address) {
488
+ reaction((_) => exchangeViewModel.wallet.walletAddresses.address, (String address) {
489
if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
490
depositKey.currentState!.changeAddress(address: address);
491
}
496
});
497
498
_depositAddressFocus.addListener(() async {
522
- if (!_depositAddressFocus.hasFocus &&
523
- depositAddressController.text.isNotEmpty) {
499
+ if (!_depositAddressFocus.hasFocus && depositAddressController.text.isNotEmpty) {
500
final domain = depositAddressController.text;
501
final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
526
- exchangeViewModel.depositAddress =
527
- await fetchParsedAddress(context, domain, ticker);
502
+ exchangeViewModel.depositAddress = await fetchParsedAddress(context, domain, ticker);
503
}
504
});
505
506
_receiveAddressFocus.addListener(() async {
532
- if (!_receiveAddressFocus.hasFocus &&
533
- receiveAddressController.text.isNotEmpty) {
507
+ if (!_receiveAddressFocus.hasFocus && receiveAddressController.text.isNotEmpty) {
508
final domain = receiveAddressController.text;
509
final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
536
- exchangeViewModel.receiveAddress =
537
- await fetchParsedAddress(context, domain, ticker);
510
+ exchangeViewModel.receiveAddress = await fetchParsedAddress(context, domain, ticker);
511
}
512
});
513
527
_isReactionsSet = true;
528
}
529
557
- void _onCurrencyChange(CryptoCurrency currency,
558
- ExchangeViewModel exchangeViewModel, GlobalKey<ExchangeCardState> key) {
530
+ void _onCurrencyChange(CryptoCurrency currency, ExchangeViewModel exchangeViewModel,
531
+ GlobalKey<ExchangeCardState> key) {
532
final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
533
534
key.currentState!.changeSelectedCurrency(currency);
562
- key.currentState!.changeWalletName(
563
- isCurrentTypeWallet ? exchangeViewModel.wallet.name : '');
535
+ key.currentState!.changeWalletName(isCurrentTypeWallet ? exchangeViewModel.wallet.name : '');
536
537
key.currentState!.changeAddress(
566
- address: isCurrentTypeWallet
567
- ? exchangeViewModel.wallet.walletAddresses.address : '');
538
+ address: isCurrentTypeWallet ? exchangeViewModel.wallet.walletAddresses.address : '');
539
540
key.currentState!.changeAmount(amount: '');
541
}
542
572
- void _onWalletNameChange(ExchangeViewModel exchangeViewModel,
573
- CryptoCurrency currency, GlobalKey<ExchangeCardState> key) {
543
+ void _onWalletNameChange(ExchangeViewModel exchangeViewModel, CryptoCurrency currency,
544
+ GlobalKey<ExchangeCardState> key) {
545
final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
546
547
if (isCurrentTypeWallet) {
548
key.currentState!.changeWalletName(exchangeViewModel.wallet.name);
578
- key.currentState!.addressController.text =
579
- exchangeViewModel.wallet.walletAddresses.address;
549
+ key.currentState!.addressController.text = exchangeViewModel.wallet.walletAddresses.address;
550
} else if (key.currentState!.addressController.text ==
551
exchangeViewModel.wallet.walletAddresses.address) {
552
key.currentState!.changeWalletName('');
554
}
555
}
556
587
- Future<String> fetchParsedAddress(
588
- BuildContext context, String domain, String ticker) async {
557
+ Future<String> fetchParsedAddress(BuildContext context, String domain, String ticker) async {
558
final parsedAddress = await getIt.get<AddressResolver>().resolve(domain, ticker);
559
final address = await extractAddressFromParsed(context, parsedAddress);
560
return address;
563
void _showFeeAlert(BuildContext context) async {
564
await Future<void>.delayed(Duration(seconds: 1));
565
final confirmed = await showPopUp<bool>(
597
- context: context,
598
- builder: (dialogContext) {
599
- return AlertWithTwoActions(
600
- alertTitle: S.of(context).low_fee,
601
- alertContent: S.of(context).low_fee_alert,
602
- leftButtonText: S.of(context).ignor,
603
- rightButtonText: S.of(context).use_suggested,
604
- actionLeftButton: () => Navigator.of(dialogContext).pop(false),
605
- actionRightButton: () => Navigator.of(dialogContext).pop(true));
606
- }) ?? false;
566
+ context: context,
567
+ builder: (dialogContext) {
568
+ return AlertWithTwoActions(
569
+ alertTitle: S.of(context).low_fee,
570
+ alertContent: S.of(context).low_fee_alert,
571
+ leftButtonText: S.of(context).ignor,
572
+ rightButtonText: S.of(context).use_suggested,
573
+ actionLeftButton: () => Navigator.of(dialogContext).pop(false),
574
+ actionRightButton: () => Navigator.of(dialogContext).pop(true));
575
+ }) ??
576
+ false;
577
if (confirmed) {
578
exchangeViewModel.setDefaultTransactionPriority();
579
}
582
void disposeBestRateSync() => exchangeViewModel.bestRateSync.cancel();
583
584
Widget _exchangeCardsSection(BuildContext context) {
615
- final firstExchangeCard = Observer(builder: (_) => ExchangeCard(
616
- onDispose: disposeBestRateSync,
617
- hasAllAmount: exchangeViewModel.hasAllAmount,
618
- allAmount: exchangeViewModel.hasAllAmount
619
- ? () => exchangeViewModel.calculateDepositAllAmount()
620
- : null,
621
- amountFocusNode: _depositAmountFocus,
622
- addressFocusNode: _depositAddressFocus,
623
- key: depositKey,
624
- title: S.of(context).you_will_send,
625
- initialCurrency: exchangeViewModel.depositCurrency,
626
- initialWalletName: depositWalletName ?? '',
627
- initialAddress:
628
- exchangeViewModel.depositCurrency == exchangeViewModel.wallet.currency
629
- ? exchangeViewModel.wallet.walletAddresses.address
630
- : exchangeViewModel.depositAddress,
631
- initialIsAmountEditable: true,
632
- initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
633
- isAmountEstimated: false,
634
- hasRefundAddress: true,
635
- isMoneroWallet: exchangeViewModel.isMoneroWallet,
636
- currencies: exchangeViewModel.depositCurrencies,
637
- onCurrencySelected: (currency) {
638
- // FIXME: need to move it into view model
639
- if (currency == CryptoCurrency.xmr &&
640
- exchangeViewModel.wallet.type != WalletType.monero) {
641
- showPopUp<void>(
642
- context: context,
643
- builder: (dialogContext) {
644
- return AlertWithOneAction(
645
- alertTitle: S.of(context).error,
646
- alertContent:
647
- S.of(context).exchange_incorrect_current_wallet_for_xmr,
648
- buttonText: S.of(context).ok,
649
- buttonAction: () => Navigator.of(dialogContext).pop());
650
- });
651
- return;
652
- }
653
-
654
- exchangeViewModel.changeDepositCurrency(currency: currency);
655
- },
656
- imageArrow: arrowBottomPurple,
657
- currencyButtonColor: Colors.transparent,
658
- addressButtonsColor: Theme.of(context).focusColor!,
659
- borderColor: Theme.of(context).primaryTextTheme!.bodyLarge!.color!,
660
- currencyValueValidator: (value) {
661
- return !exchangeViewModel.isFixedRateMode
662
- ? AmountValidator(
663
- isAutovalidate: true,
664
- currency: exchangeViewModel.depositCurrency,
665
- minValue: exchangeViewModel.limits.min.toString(),
666
- maxValue: exchangeViewModel.limits.max.toString(),
667
- ).call(value)
668
- : null;
669
- },
670
- addressTextFieldValidator:
671
- AddressValidator(type: exchangeViewModel.depositCurrency),
672
- onPushPasteButton: (context) async {
673
- final domain = exchangeViewModel.depositAddress;
674
- final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
675
- exchangeViewModel.depositAddress =
676
- await fetchParsedAddress(context, domain, ticker);
677
- },
678
- onPushAddressBookButton: (context) async {
679
- final domain = exchangeViewModel.depositAddress;
680
- final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
681
- exchangeViewModel.depositAddress =
682
- await fetchParsedAddress(context, domain, ticker);
683
- },
684
- ));
685
-
686
- final secondExchangeCard = Observer(builder: (_) => ExchangeCard(
687
- onDispose: disposeBestRateSync,
688
- amountFocusNode: _receiveAmountFocus,
689
- addressFocusNode: _receiveAddressFocus,
690
- key: receiveKey,
691
- title: S.of(context).you_will_get,
692
- initialCurrency: exchangeViewModel.receiveCurrency,
693
- initialWalletName: receiveWalletName ?? '',
694
- initialAddress:
695
- exchangeViewModel.receiveCurrency == exchangeViewModel.wallet.currency
696
- ? exchangeViewModel.wallet.walletAddresses.address
697
- : exchangeViewModel.receiveAddress,
698
- initialIsAmountEditable: exchangeViewModel.isReceiveAmountEditable,
699
- initialIsAddressEditable: exchangeViewModel.isReceiveAddressEnabled,
700
- isAmountEstimated: true,
701
- isMoneroWallet: exchangeViewModel.isMoneroWallet,
702
- currencies: exchangeViewModel.receiveCurrencies,
703
- onCurrencySelected: (currency) =>
704
- exchangeViewModel.changeReceiveCurrency(currency: currency),
705
- imageArrow: arrowBottomCakeGreen,
706
- currencyButtonColor: Colors.transparent,
707
- addressButtonsColor: Theme.of(context).focusColor!,
708
- borderColor:
709
- Theme.of(context).primaryTextTheme!.bodyLarge!.decorationColor!,
710
- currencyValueValidator: (value) {
711
- return exchangeViewModel.isFixedRateMode
712
- ? AmountValidator(
713
- isAutovalidate: true,
714
- currency: exchangeViewModel.receiveCurrency,
715
- minValue: exchangeViewModel.limits.min.toString(),
716
- maxValue: exchangeViewModel.limits.max.toString(),
717
- ).call(value)
718
- : null;
719
- },
720
- addressTextFieldValidator:
721
- AddressValidator(type: exchangeViewModel.receiveCurrency),
722
- onPushPasteButton: (context) async {
723
- final domain = exchangeViewModel.receiveAddress;
724
- final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
725
- exchangeViewModel.receiveAddress =
726
- await fetchParsedAddress(context, domain, ticker);
727
- },
728
- onPushAddressBookButton: (context) async {
729
- final domain = exchangeViewModel.receiveAddress;
730
- final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
731
- exchangeViewModel.receiveAddress =
732
- await fetchParsedAddress(context, domain, ticker);
733
- },
734
- ));
585
+ final firstExchangeCard = Observer(
586
+ builder: (_) => ExchangeCard(
587
+ onDispose: disposeBestRateSync,
588
+ hasAllAmount: exchangeViewModel.hasAllAmount,
589
+ allAmount: exchangeViewModel.hasAllAmount
590
+ ? () => exchangeViewModel.calculateDepositAllAmount()
591
+ : null,
592
+ amountFocusNode: _depositAmountFocus,
593
+ addressFocusNode: _depositAddressFocus,
594
+ key: depositKey,
595
+ title: S.of(context).you_will_send,
596
+ initialCurrency: exchangeViewModel.depositCurrency,
597
+ initialWalletName: depositWalletName ?? '',
598
+ initialAddress: exchangeViewModel.depositCurrency == exchangeViewModel.wallet.currency
599
+ ? exchangeViewModel.wallet.walletAddresses.address
600
+ : exchangeViewModel.depositAddress,
601
+ initialIsAmountEditable: true,
602
+ initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
603
+ isAmountEstimated: false,
604
+ hasRefundAddress: true,
605
+ isMoneroWallet: exchangeViewModel.isMoneroWallet,
606
+ currencies: exchangeViewModel.depositCurrencies,
607
+ onCurrencySelected: (currency) {
608
+ // FIXME: need to move it into view model
609
+ if (currency == CryptoCurrency.xmr &&
610
+ exchangeViewModel.wallet.type != WalletType.monero) {
611
+ showPopUp<void>(
612
+ context: context,
613
+ builder: (dialogContext) {
614
+ return AlertWithOneAction(
615
+ alertTitle: S.of(context).error,
616
+ alertContent: S.of(context).exchange_incorrect_current_wallet_for_xmr,
617
+ buttonText: S.of(context).ok,
618
+ buttonAction: () => Navigator.of(dialogContext).pop());
619
+ });
620
+ return;
621
+ }
622
+
623
+ exchangeViewModel.changeDepositCurrency(currency: currency);
624
+ },
625
+ imageArrow: arrowBottomPurple,
626
+ currencyButtonColor: Colors.transparent,
627
+ addressButtonsColor: Theme.of(context).focusColor,
628
+ borderColor: Theme.of(context).primaryTextTheme.bodyLarge!.color!,
629
+ currencyValueValidator: (value) {
630
+ return !exchangeViewModel.isFixedRateMode
631
+ ? AmountValidator(
632
+ isAutovalidate: true,
633
+ currency: exchangeViewModel.depositCurrency,
634
+ minValue: exchangeViewModel.limits.min.toString(),
635
+ maxValue: exchangeViewModel.limits.max.toString(),
636
+ ).call(value)
637
+ : null;
638
+ },
639
+ addressTextFieldValidator: AddressValidator(type: exchangeViewModel.depositCurrency),
640
+ onPushPasteButton: (context) async {
641
+ final domain = exchangeViewModel.depositAddress;
642
+ final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
643
+ exchangeViewModel.depositAddress =
644
+ await fetchParsedAddress(context, domain, ticker);
645
+ },
646
+ onPushAddressBookButton: (context) async {
647
+ final domain = exchangeViewModel.depositAddress;
648
+ final ticker = exchangeViewModel.depositCurrency.title.toLowerCase();
649
+ exchangeViewModel.depositAddress =
650
+ await fetchParsedAddress(context, domain, ticker);
651
+ },
652
+ ));
653
+
654
+ final secondExchangeCard = Observer(
655
+ builder: (_) => ExchangeCard(
656
+ onDispose: disposeBestRateSync,
657
+ amountFocusNode: _receiveAmountFocus,
658
+ addressFocusNode: _receiveAddressFocus,
659
+ key: receiveKey,
660
+ title: S.of(context).you_will_get,
661
+ initialCurrency: exchangeViewModel.receiveCurrency,
662
+ initialWalletName: receiveWalletName ?? '',
663
+ initialAddress: exchangeViewModel.receiveCurrency == exchangeViewModel.wallet.currency
664
+ ? exchangeViewModel.wallet.walletAddresses.address
665
+ : exchangeViewModel.receiveAddress,
666
+ initialIsAmountEditable: exchangeViewModel.isReceiveAmountEditable,
667
+ initialIsAddressEditable: exchangeViewModel.isReceiveAddressEnabled,
668
+ isAmountEstimated: true,
669
+ isMoneroWallet: exchangeViewModel.isMoneroWallet,
670
+ currencies: exchangeViewModel.receiveCurrencies,
671
+ onCurrencySelected: (currency) =>
672
+ exchangeViewModel.changeReceiveCurrency(currency: currency),
673
+ imageArrow: arrowBottomCakeGreen,
674
+ currencyButtonColor: Colors.transparent,
675
+ addressButtonsColor: Theme.of(context).focusColor,
676
+ borderColor: Theme.of(context).primaryTextTheme.bodyLarge!.decorationColor!,
677
+ currencyValueValidator: (value) {
678
+ return exchangeViewModel.isFixedRateMode
679
+ ? AmountValidator(
680
+ isAutovalidate: true,
681
+ currency: exchangeViewModel.receiveCurrency,
682
+ minValue: exchangeViewModel.limits.min.toString(),
683
+ maxValue: exchangeViewModel.limits.max.toString(),
684
+ ).call(value)
685
+ : null;
686
+ },
687
+ addressTextFieldValidator: AddressValidator(type: exchangeViewModel.receiveCurrency),
688
+ onPushPasteButton: (context) async {
689
+ final domain = exchangeViewModel.receiveAddress;
690
+ final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
691
+ exchangeViewModel.receiveAddress =
692
+ await fetchParsedAddress(context, domain, ticker);
693
+ },
694
+ onPushAddressBookButton: (context) async {
695
+ final domain = exchangeViewModel.receiveAddress;
696
+ final ticker = exchangeViewModel.receiveCurrency.title.toLowerCase();
697
+ exchangeViewModel.receiveAddress =
698
+ await fetchParsedAddress(context, domain, ticker);
699
+ },
700
+ ));
701
702
if (ResponsiveLayoutUtil.instance.isMobile) {
703
return MobileExchangeCardsSection(