dev
dart 378 lines 17.2 KB
Raw
1 import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
2 import 'package:cake_wallet/src/screens/base_page.dart';
3 import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
4 import 'package:flutter/material.dart';
5 import 'package:flutter_mobx/flutter_mobx.dart';
6 import 'package:keyboard_actions/keyboard_actions.dart';
7 import 'package:mobx/mobx.dart';
8 import 'package:cake_wallet/generated/i18n.dart';
9 import 'package:cw_core/crypto_currency.dart';
10 import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
11 import 'package:cake_wallet/src/widgets/primary_button.dart';
12 import 'package:cake_wallet/src/widgets/scrollable_with_bottom_section.dart';
13 import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
14 import 'package:cake_wallet/core/amount_validator.dart';
15 import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
16
17 class ExchangeTemplatePage extends BasePage {
18 ExchangeTemplatePage(this.exchangeViewModel);
19
20 final ExchangeViewModel exchangeViewModel;
21 final depositKey = GlobalKey<ExchangeCardState>();
22 final receiveKey = GlobalKey<ExchangeCardState>();
23 final _formKey = GlobalKey<FormState>();
24 final _depositAmountFocus = FocusNode();
25 final _receiveAmountFocus = FocusNode();
26 var _isReactionsSet = false;
27
28 @override
29 bool get gradientAll => true;
30
31 @override
32 String get title => S.current.exchange_new_template;
33
34 @override
35 bool get extendBodyBehindAppBar => true;
36
37 @override
38 AppBarStyle get appBarStyle => AppBarStyle.transparent;
39
40 @override
41 Widget trailing(BuildContext context) =>
42 PresentProviderPicker(exchangeViewModel: exchangeViewModel);
43
44 @override
45 Widget body(BuildContext context) {
46 final arrowBottomPurple = Image.asset(
47 'assets/images/arrow_bottom_purple_icon.png',
48 color: Theme.of(context).colorScheme.primary,
49 height: 8,
50 );
51 final arrowBottomCakeGreen = Image.asset(
52 'assets/images/arrow_bottom_cake_green.png',
53 color: Theme.of(context).colorScheme.primary,
54 height: 8,
55 );
56
57 final depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr
58 ? exchangeViewModel.wallet.name
59 : null;
60 final receiveWalletName = exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
61 ? exchangeViewModel.wallet.name
62 : null;
63
64 WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
65
66 return KeyboardActions(
67 disableScroll: true,
68 config: KeyboardActionsConfig(
69 keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
70 keyboardBarColor: Theme.of(context).colorScheme.surfaceVariant,
71 nextFocus: false,
72 actions: [
73 KeyboardActionsItem(
74 focusNode: _depositAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()]),
75 KeyboardActionsItem(
76 focusNode: _receiveAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()])
77 ],
78 ),
79 child: Container(
80 color: Theme.of(context).colorScheme.surface,
81 child: Form(
82 key: _formKey,
83 child: ScrollableWithBottomSection(
84 contentPadding: EdgeInsets.only(bottom: 24),
85 content: Container(
86 padding: EdgeInsets.only(bottom: 32),
87 decoration: BoxDecoration(
88 borderRadius: BorderRadius.only(
89 bottomLeft: Radius.circular(24),
90 bottomRight: Radius.circular(24),
91 ),
92 color: Theme.of(context).colorScheme.surfaceContainer,
93 ),
94 child: FocusTraversalGroup(
95 policy: OrderedTraversalPolicy(),
96 child: Column(
97 children: <Widget>[
98 Container(
99 decoration: BoxDecoration(
100 borderRadius: BorderRadius.only(
101 bottomLeft: Radius.circular(24),
102 bottomRight: Radius.circular(24)),
103 color: Theme.of(context).colorScheme.surfaceContainerLow,
104 ),
105 padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
106 child: Observer(
107 builder: (_) => ExchangeCard(
108 cardInstanceName: 'deposit_exchange_template_card',
109 amountFocusNode: _depositAmountFocus,
110 key: depositKey,
111 title: S.of(context).you_will_send,
112 initialCurrency: exchangeViewModel.depositCurrency,
113 initialWalletName: depositWalletName ?? '',
114 initialAddress: exchangeViewModel.depositCurrency ==
115 exchangeViewModel.wallet.currency
116 ? exchangeViewModel.wallet.walletAddresses.addressForExchange
117 : exchangeViewModel.depositAddress,
118 initialIsAmountEditable: true,
119 initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
120 isAmountEstimated: false,
121 hasRefundAddress: true,
122 isMoneroWallet: exchangeViewModel.isMoneroWallet,
123 currencies: CryptoCurrency.all,
124 onCurrencySelected: (currency) =>
125 exchangeViewModel.changeDepositCurrency(currency: currency),
126 imageArrow: arrowBottomPurple,
127 currencyButtonColor: Colors.transparent,
128 addressButtonsColor:
129 Theme.of(context).colorScheme.surfaceContainerHighest,
130 borderColor: Theme.of(context).colorScheme.outlineVariant,
131 fillColor: Theme.of(context).colorScheme.surfaceContainer,
132 currencyValueValidator: AmountValidator(
133 currency: exchangeViewModel.depositCurrency,
134 amountParsingProxy: exchangeViewModel.amountParsingProxy,
135 ),
136 ),
137 ),
138 ),
139 Padding(
140 padding: EdgeInsets.only(top: 29, left: 24, right: 24),
141 child: Observer(
142 builder: (_) => ExchangeCard(
143 cardInstanceName: 'receive_exchange_template_card',
144 amountFocusNode: _receiveAmountFocus,
145 key: receiveKey,
146 title: S.of(context).you_will_get,
147 initialCurrency: exchangeViewModel.receiveCurrency,
148 initialWalletName: receiveWalletName ?? '',
149 initialAddress: exchangeViewModel.receiveCurrency ==
150 exchangeViewModel.wallet.currency
151 ? exchangeViewModel.wallet.walletAddresses.addressForExchange
152 : exchangeViewModel.receiveAddress,
153 initialIsAmountEditable: false,
154 isAmountEstimated: true,
155 isMoneroWallet: exchangeViewModel.isMoneroWallet,
156 currencies: exchangeViewModel.receiveCurrencies,
157 onCurrencySelected: (currency) =>
158 exchangeViewModel.changeReceiveCurrency(currency: currency),
159 imageArrow: arrowBottomCakeGreen,
160 currencyButtonColor: Colors.transparent,
161 addressButtonsColor:
162 Theme.of(context).colorScheme.surfaceContainerHighest,
163 borderColor: Theme.of(context).colorScheme.outlineVariant,
164 fillColor: Theme.of(context).colorScheme.surfaceContainerLow,
165 currencyValueValidator: AmountValidator(
166 currency: exchangeViewModel.receiveCurrency,
167 amountParsingProxy: exchangeViewModel.amountParsingProxy,
168 ),
169 ),
170 ),
171 )
172 ],
173 ),
174 ),
175 ),
176 bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
177 bottomSection: Column(children: <Widget>[
178 Padding(
179 padding: EdgeInsets.only(bottom: 15),
180 child: Observer(
181 builder: (_) => Center(
182 child: Text(
183 S.of(context).amount_is_estimate,
184 textAlign: TextAlign.center,
185 style: Theme.of(context).textTheme.bodySmall?.copyWith(
186 color: Theme.of(context).colorScheme.onSurfaceVariant,
187 fontWeight: FontWeight.w500,
188 ),
189 ),
190 ),
191 ),
192 ),
193 PrimaryButton(
194 onPressed: () {
195 if (_formKey.currentState != null && _formKey.currentState!.validate()) {
196 exchangeViewModel.addTemplate(
197 amount: exchangeViewModel.depositAmount,
198 depositCurrency: exchangeViewModel.depositCurrency.name,
199 depositCurrencyTitle: exchangeViewModel.depositCurrency.title +
200 ' ${exchangeViewModel.depositCurrency.tag ?? ''}',
201 receiveCurrency: exchangeViewModel.receiveCurrency.name,
202 receiveCurrencyTitle: exchangeViewModel.receiveCurrency.title +
203 ' ${exchangeViewModel.receiveCurrency.tag ?? ''}',
204 provider: exchangeViewModel.provider.toString(),
205 depositAddress: exchangeViewModel.depositAddress,
206 receiveAddress: exchangeViewModel.receiveAddress);
207 exchangeViewModel.updateTemplate();
208 Navigator.of(context).pop();
209 }
210 },
211 text: S.of(context).save,
212 color: Theme.of(context).colorScheme.primary,
213 textColor: Theme.of(context).colorScheme.onPrimary),
214 ]),
215 ))));
216 }
217
218 void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) {
219 if (_isReactionsSet) {
220 return;
221 }
222
223 final depositAddressController = depositKey.currentState!.addressController;
224 final depositAmountController = depositKey.currentState!.amountController;
225 final receiveAddressController = receiveKey.currentState!.addressController;
226 final receiveAmountController = receiveKey.currentState!.amountController;
227 final limitsState = exchangeViewModel.limitsState;
228
229 // FIXME: FIXME
230
231 // final limitsState = exchangeViewModel.limitsState;
232 //
233 // if (limitsState is LimitsLoadedSuccessfully) {
234 // final min = limitsState.limits.min != null
235 // ? limitsState.limits.min.toString()
236 // : null;
237 // final max = limitsState.limits.max != null
238 // ? limitsState.limits.max.toString()
239 // : null;
240 // final key = depositKey;
241 // key.currentState.changeLimits(min: min, max: max);
242 // }
243
244 _onCurrencyChange(exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
245 _onCurrencyChange(exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
246
247 reaction(
248 (_) => exchangeViewModel.wallet.name,
249 (String _) =>
250 _onWalletNameChange(exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
251
252 reaction(
253 (_) => exchangeViewModel.wallet.name,
254 (String _) =>
255 _onWalletNameChange(exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
256
257 reaction((_) => exchangeViewModel.receiveCurrency,
258 (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, receiveKey));
259
260 reaction((_) => exchangeViewModel.depositCurrency,
261 (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey));
262
263 reaction((_) => exchangeViewModel.depositAmount, (String amount) {
264 if (depositKey.currentState!.amountController.text != amount && amount != S.of(context).all) {
265 depositKey.currentState!.amountController.text = amount;
266 }
267 });
268
269 reaction((_) => exchangeViewModel.depositAddress, (String address) {
270 if (depositKey.currentState!.addressController.text != address) {
271 depositKey.currentState!.addressController.text = address;
272 }
273 });
274
275 reaction((_) => exchangeViewModel.isDepositAddressEnabled, (bool isEnabled) {
276 depositKey.currentState!.isAddressEditable(isEditable: isEnabled);
277 });
278
279 reaction((_) => exchangeViewModel.receiveAmount, (String amount) {
280 if (receiveKey.currentState!.amountController.text != amount) {
281 receiveKey.currentState!.amountController.text = amount;
282 }
283 });
284
285 reaction((_) => exchangeViewModel.receiveAddress, (String address) {
286 if (receiveKey.currentState!.addressController.text != address) {
287 receiveKey.currentState!.addressController.text = address;
288 }
289 });
290
291 reaction((_) => exchangeViewModel.provider, (ExchangeProvider? provider) {
292 receiveKey.currentState!.isAmountEditable(isEditable: false);
293 });
294
295 /*reaction((_) => exchangeViewModel.limitsState, (LimitsState state) {
296 String min;
297 String max;
298
299 if (state is LimitsLoadedSuccessfully) {
300 min = state.limits.min != null ? state.limits.min.toString() : null;
301 max = state.limits.max != null ? state.limits.max.toString() : null;
302 }
303
304 if (state is LimitsLoadedFailure) {
305 min = '0';
306 max = '0';
307 }
308
309 if (state is LimitsIsLoading) {
310 min = '...';
311 max = '...';
312 }
313
314 depositKey.currentState.changeLimits(min: min, max: max);
315 receiveKey.currentState.changeLimits(min: null, max: null);
316 });*/
317
318 depositAddressController
319 .addListener(() => exchangeViewModel.depositAddress = depositAddressController.text);
320
321 depositAmountController.addListener(() {
322 if (depositAmountController.text != exchangeViewModel.depositAmount &&
323 exchangeViewModel.depositAmount != S.of(context).all) {
324 exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
325 exchangeViewModel.isReceiveAmountEntered = false;
326 }
327 });
328
329 receiveAddressController
330 .addListener(() => exchangeViewModel.receiveAddress = receiveAddressController.text);
331
332 receiveAmountController.addListener(() {
333 if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
334 exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text);
335 exchangeViewModel.isReceiveAmountEntered = true;
336 }
337 });
338
339 reaction((_) => exchangeViewModel.wallet.walletAddresses.address, (String address) {
340 if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
341 depositKey.currentState!.changeAddress(address: address);
342 }
343
344 if (exchangeViewModel.receiveCurrency == CryptoCurrency.xmr) {
345 receiveKey.currentState!.changeAddress(address: address);
346 }
347 });
348
349 _isReactionsSet = true;
350 }
351
352 void _onCurrencyChange(CryptoCurrency currency, ExchangeViewModel exchangeViewModel,
353 GlobalKey<ExchangeCardState> key) {
354 final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
355
356 key.currentState!.changeSelectedCurrency(currency);
357 key.currentState!.changeWalletName(isCurrentTypeWallet ? exchangeViewModel.wallet.name : '');
358
359 key.currentState!.changeAddress(
360 address: isCurrentTypeWallet ? exchangeViewModel.wallet.walletAddresses.address : '');
361
362 key.currentState!.changeAmount(amount: '');
363 }
364
365 void _onWalletNameChange(ExchangeViewModel exchangeViewModel, CryptoCurrency currency,
366 GlobalKey<ExchangeCardState> key) {
367 final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
368
369 if (isCurrentTypeWallet) {
370 key.currentState!.changeWalletName(exchangeViewModel.wallet.name);
371 key.currentState!.addressController.text = exchangeViewModel.wallet.walletAddresses.address;
372 } else if (key.currentState!.addressController.text ==
373 exchangeViewModel.wallet.walletAddresses.address) {
374 key.currentState!.changeWalletName('');
375 key.currentState!.addressController.text = '';
376 }
377 }
378 }