newdesign | blocked address text fields in exchange pages for wallet address; refactored exchange page and exchange template page; added base exchange widget and present provider picker

Oleksandr Sobol committed May 18, 2020 at 20:23 UTC 8769e9302558bd4b6d874841cf51e0f4b28e3fc6
19 files changed +680 -926
lib/generated/i18n.dart
+23
@@ -85,6 +85,7 @@ class S implements WidgetsLocalizations {
85 String get error_text_xmr => "XMR value can't exceed available balance.\nThe number of fraction digits must be less or equal to 12";
86 String get estimated => "Estimated";
87 String get exchange => "Exchange";
88 + String get exchange_new_template => "New template";
89 String get exchange_result_write_down_ID => "*Please copy or write down your ID shown above.";
90 String get exchange_result_write_down_trade_id => "Please copy or write down the trade ID to continue.";
91 String get expired => "Expired";
@@ -869,6 +870,8 @@ class $de extends S {
870 @override
871 String get add_new_word => "Neues Wort hinzufügen";
872 @override
873 + String get exchange_new_template => "Neue Vorlage";
874 + @override
875 String get digit_pin => "-stelliger PIN";
876 @override
877 String get first_wallet_text => "tolle Brieftasche zum Monero";
@@ -1479,6 +1482,8 @@ class $hi extends S {
1482 @override
1483 String get add_new_word => "नया शब्द जोड़ें";
1484 @override
1485 + String get exchange_new_template => "नया टेम्पलेट";
1486 + @override
1487 String get digit_pin => "-अंक पिन";
1488 @override
1489 String get first_wallet_text => "बहुत बढ़िया बटुआ के लिये Monero";
@@ -2089,6 +2094,8 @@ class $ru extends S {
2094 @override
2095 String get add_new_word => "Добавить новое слово";
2096 @override
2097 + String get exchange_new_template => "Новый шаблон";
2098 + @override
2099 String get digit_pin => "-значный PIN";
2100 @override
2101 String get first_wallet_text => "В самом удобном кошельке для Monero";
@@ -2699,6 +2706,8 @@ class $ko extends S {
2706 @override
2707 String get add_new_word => "새로운 단어 추가";
2708 @override
2709 + String get exchange_new_template => "새 템플릿";
2710 + @override
2711 String get digit_pin => "숫자 PIN";
2712 @override
2713 String get first_wallet_text => "멋진 지갑 에 대한 Monero";
@@ -3309,6 +3318,8 @@ class $pt extends S {
3318 @override
3319 String get add_new_word => "Adicionar nova palavra";
3320 @override
3321 + String get exchange_new_template => "Novo modelo";
3322 + @override
3323 String get digit_pin => "dígitos";
3324 @override
3325 String get first_wallet_text => "Uma fantástica carteira para Monero";
@@ -3919,6 +3930,8 @@ class $uk extends S {
3930 @override
3931 String get add_new_word => "Добавити нове слово";
3932 @override
3933 + String get exchange_new_template => "Новий шаблон";
3934 + @override
3935 String get digit_pin => "-значний PIN";
3936 @override
3937 String get first_wallet_text => "В самому зручному гаманці для Monero";
@@ -4529,6 +4542,8 @@ class $ja extends S {
4542 @override
4543 String get add_new_word => "新しい単語を追加";
4544 @override
4545 + String get exchange_new_template => "新しいテンプレート";
4546 + @override
4547 String get digit_pin => "桁ピン";
4548 @override
4549 String get first_wallet_text => "素晴らしい財布 ために Monero";
@@ -5143,6 +5158,8 @@ class $pl extends S {
5158 @override
5159 String get add_new_word => "Dodaj nowe słowo";
5160 @override
5161 + String get exchange_new_template => "Nowy szablon";
5162 + @override
5163 String get digit_pin => "-znak PIN";
5164 @override
5165 String get first_wallet_text => "Niesamowity portfel dla Monero";
@@ -5753,6 +5770,8 @@ class $es extends S {
5770 @override
5771 String get add_new_word => "Agregar palabra nueva";
5772 @override
5773 + String get exchange_new_template => "Nueva plantilla";
5774 + @override
5775 String get digit_pin => "-dígito PIN";
5776 @override
5777 String get first_wallet_text => "Impresionante billetera para Monero";
@@ -6363,6 +6382,8 @@ class $nl extends S {
6382 @override
6383 String get add_new_word => "Nieuw woord toevoegen";
6384 @override
6385 + String get exchange_new_template => "Nieuwe sjabloon";
6386 + @override
6387 String get digit_pin => "-cijferige PIN";
6388 @override
6389 String get first_wallet_text => "Geweldige portemonnee fvoor Monero";
@@ -6973,6 +6994,8 @@ class $zh extends S {
6994 @override
6995 String get add_new_word => "添加新词";
6996 @override
6997 + String get exchange_new_template => "新範本";
6998 + @override
6999 String get digit_pin => "数字别针";
7000 @override
7001 String get first_wallet_text => "很棒的钱包 对于 Monero";
lib/src/screens/exchange/exchange_page.dart
+8 -516
@@ -1,31 +1,15 @@
1 import 'dart:ui';
2 -import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
3 -import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
4 -import 'package:cake_wallet/src/widgets/template_tile.dart';
5 -import 'package:dotted_border/dotted_border.dart';
2 import 'package:flutter/cupertino.dart';
3 import 'package:flutter/material.dart';
8 -import 'package:flutter_mobx/flutter_mobx.dart';
9 -import 'package:mobx/mobx.dart';
4 import 'package:provider/provider.dart';
5 import 'package:cake_wallet/palette.dart';
12 -import 'package:cake_wallet/routes.dart';
6 import 'package:cake_wallet/generated/i18n.dart';
14 -import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
15 -import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
16 -import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
17 -import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
18 -import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
7 import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
8 import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
9 import 'package:cake_wallet/src/screens/base_page.dart';
22 -import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
23 -import 'package:cake_wallet/src/widgets/primary_button.dart';
24 -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
25 -import 'package:cake_wallet/src/widgets/top_panel.dart';
26 -import 'package:cake_wallet/src/screens/exchange/widgets/provider_picker.dart';
10 import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
28 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
11 +import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
12 +import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
13
14 class ExchangePage extends BasePage {
15 @override
@@ -34,46 +18,11 @@ class ExchangePage extends BasePage {
18 @override
19 Color get backgroundColor => PaletteDark.walletCardSubAddressField;
20
37 - final Image arrowBottom =
38 - Image.asset('assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 6);
39 -
21 @override
22 Widget middle(BuildContext context) {
23 final exchangeStore = Provider.of<ExchangeStore>(context);
24
44 - return FlatButton(
45 - onPressed: () => _presentProviderPicker(context),
46 - highlightColor: Colors.transparent,
47 - splashColor: Colors.transparent,
48 - child: Row(
49 - mainAxisSize: MainAxisSize.min,
50 - crossAxisAlignment: CrossAxisAlignment.start,
51 - children: <Widget>[
52 - Column(
53 - crossAxisAlignment: CrossAxisAlignment.center,
54 - mainAxisSize: MainAxisSize.min,
55 - children: <Widget>[
56 - Text(S.of(context).exchange,
57 - style: TextStyle(
58 - fontSize: 16.0,
59 - fontWeight: FontWeight.w400,
60 - color: Colors.white)),
61 - Observer(
62 - builder: (_) => Text('${exchangeStore.provider.title}',
63 - style: TextStyle(
64 - fontSize: 10.0,
65 - fontWeight: FontWeight.w400,
66 - color:PaletteDark.walletCardText)))
67 - ],
68 - ),
69 - SizedBox(width: 5),
70 - Padding(
71 - padding: EdgeInsets.only(top: 8),
72 - child: arrowBottom,
73 - )
74 - ],
75 - )
76 - );
25 + return PresentProviderPicker(exchangeStore: exchangeStore);
26 }
27
28 @override
@@ -99,37 +48,6 @@ class ExchangePage extends BasePage {
48
49 @override
50 Widget body(BuildContext context) => ExchangeForm();
102 -
103 - void _presentProviderPicker(BuildContext context) {
104 - final exchangeStore = Provider.of<ExchangeStore>(context);
105 - final items = exchangeStore.providersForCurrentPair();
106 - final selectedItem = items.indexOf(exchangeStore.provider);
107 - final images = List<Image>();
108 -
109 - for (ExchangeProvider provider in items) {
110 - switch (provider.description) {
111 - case ExchangeProviderDescription.xmrto:
112 - images.add(Image.asset('assets/images/xmr_btc.png'));
113 - break;
114 - case ExchangeProviderDescription.changeNow:
115 - images.add(Image.asset('assets/images/change_now.png'));
116 - break;
117 - case ExchangeProviderDescription.morphToken:
118 - images.add(Image.asset('assets/images/morph_icon.png'));
119 - break;
120 - }
121 - }
122 -
123 - showDialog<void>(
124 - builder: (_) => ProviderPicker(
125 - items: items,
126 - images: images,
127 - selectedAtIndex: selectedItem,
128 - title: S.of(context).change_exchange_provider,
129 - onItemSelected: (ExchangeProvider provider) =>
130 - exchangeStore.changeProvider(provider: provider)),
131 - context: context);
132 - }
51 }
52
53 class ExchangeForm extends StatefulWidget {
@@ -138,21 +56,6 @@ class ExchangeForm extends StatefulWidget {
56 }
57
58 class ExchangeFormState extends State<ExchangeForm> {
141 - final depositKey = GlobalKey<ExchangeCardState>();
142 - final receiveKey = GlobalKey<ExchangeCardState>();
143 - final _formKey = GlobalKey<FormState>();
144 - var _isReactionsSet = false;
145 -
146 - final Image arrowBottomPurple = Image.asset(
147 - 'assets/images/arrow_bottom_purple_icon.png',
148 - color: Colors.white,
149 - height: 8,
150 - );
151 - final Image arrowBottomCakeGreen = Image.asset(
152 - 'assets/images/arrow_bottom_cake_green.png',
153 - color: Colors.white,
154 - height: 8,
155 - );
59
60 @override
61 Widget build(BuildContext context) {
@@ -160,422 +63,11 @@ class ExchangeFormState extends State<ExchangeForm> {
63 final walletStore = Provider.of<WalletStore>(context);
64 final exchangeTemplateStore = Provider.of<ExchangeTemplateStore>(context);
65
163 - final depositWalletName =
164 - exchangeStore.depositCurrency == CryptoCurrency.xmr
165 - ? walletStore.name
166 - : null;
167 - final receiveWalletName =
168 - exchangeStore.receiveCurrency == CryptoCurrency.xmr
169 - ? walletStore.name
170 - : null;
171 -
172 - WidgetsBinding.instance.addPostFrameCallback(
173 - (_) => _setReactions(context, exchangeStore, walletStore));
174 -
175 - return Container(
176 - color: PaletteDark.historyPanel,
177 - child: Form(
178 - key: _formKey,
179 - child: ScrollableWithBottomSection(
180 - contentPadding: EdgeInsets.only(bottom: 24),
181 - content: Column(
182 - children: <Widget>[
183 - TopPanel(
184 - color: PaletteDark.menuList,
185 - edgeInsets: EdgeInsets.only(bottom: 24),
186 - widget: Column(
187 - children: <Widget>[
188 - TopPanel(
189 - color: PaletteDark.walletCardSubAddressField,
190 - widget: Observer(
191 - builder: (_) => ExchangeCard(
192 - key: depositKey,
193 - title: S.of(context).you_will_send,
194 - initialCurrency: exchangeStore.depositCurrency,
195 - initialWalletName: depositWalletName,
196 - initialAddress:
197 - exchangeStore.depositCurrency == walletStore.type
198 - ? walletStore.address
199 - : exchangeStore.depositAddress,
200 - initialIsAmountEditable: true,
201 - initialIsAddressEditable: true,
202 - isAmountEstimated: false,
203 - currencies: CryptoCurrency.all,
204 - onCurrencySelected: (currency) =>
205 - exchangeStore.changeDepositCurrency(currency: currency),
206 - imageArrow: arrowBottomPurple,
207 - currencyButtonColor: PaletteDark.walletCardSubAddressField,
208 - addressButtonsColor: PaletteDark.menuList,
209 - currencyValueValidator: (value) {
210 - exchangeStore.validateCryptoCurrency(value);
211 - return exchangeStore.errorMessage;
212 - },
213 - addressTextFieldValidator: (value) {
214 - exchangeStore.validateAddress(value,
215 - cryptoCurrency: exchangeStore.depositCurrency);
216 - return exchangeStore.errorMessage;
217 - },
218 - ),
219 - )
220 - ),
221 - Padding(
222 - padding: EdgeInsets.only(top: 32, left: 24, right: 24),
223 - child: Observer(
224 - builder: (_) => ExchangeCard(
225 - key: receiveKey,
226 - title: S.of(context).you_will_get,
227 - initialCurrency: exchangeStore.receiveCurrency,
228 - initialWalletName: receiveWalletName,
229 - initialAddress:
230 - exchangeStore.receiveCurrency == walletStore.type
231 - ? walletStore.address
232 - : exchangeStore.receiveAddress,
233 - initialIsAmountEditable: false,
234 - initialIsAddressEditable: true,
235 - isAmountEstimated: true,
236 - currencies: CryptoCurrency.all,
237 - onCurrencySelected: (currency) => exchangeStore
238 - .changeReceiveCurrency(currency: currency),
239 - imageArrow: arrowBottomCakeGreen,
240 - currencyButtonColor: PaletteDark.menuList,
241 - currencyValueValidator: (value) {
242 - exchangeStore.validateCryptoCurrency(value);
243 - return exchangeStore.errorMessage;
244 - },
245 - addressTextFieldValidator: (value) {
246 - exchangeStore.validateAddress(value,
247 - cryptoCurrency: exchangeStore.receiveCurrency);
248 - return exchangeStore.errorMessage;
249 - },
250 - )),
251 - )
252 - ],
253 - )
254 - ),
255 - Padding(
256 - padding: EdgeInsets.only(
257 - top: 32,
258 - left: 24,
259 - bottom: 24
260 - ),
261 - child: Row(
262 - mainAxisAlignment: MainAxisAlignment.start,
263 - children: <Widget>[
264 - Text(
265 - S.of(context).send_templates,
266 - style: TextStyle(
267 - fontSize: 18,
268 - fontWeight: FontWeight.w600,
269 - color: PaletteDark.walletCardText
270 - ),
271 - )
272 - ],
273 - ),
274 - ),
275 - Container(
276 - height: 40,
277 - width: double.infinity,
278 - padding: EdgeInsets.only(left: 24),
279 - child: Observer(
280 - builder: (_) {
281 - final itemCount = exchangeTemplateStore.templates.length + 1;
282 -
283 - return ListView.builder(
284 - scrollDirection: Axis.horizontal,
285 - itemCount: itemCount,
286 - itemBuilder: (context, index) {
287 -
288 - if (index == 0) {
289 - return GestureDetector(
290 - onTap: () => Navigator.of(context)
291 - .pushNamed(Routes.exchangeTemplate),
292 - child: Container(
293 - padding: EdgeInsets.only(right: 10),
294 - child: DottedBorder(
295 - borderType: BorderType.RRect,
296 - dashPattern: [8, 4],
297 - color: PaletteDark.menuList,
298 - strokeWidth: 2,
299 - radius: Radius.circular(20),
300 - child: Container(
301 - height: 40,
302 - width: 75,
303 - padding: EdgeInsets.only(left: 10, right: 10),
304 - alignment: Alignment.center,
305 - decoration: BoxDecoration(
306 - borderRadius: BorderRadius.all(Radius.circular(20)),
307 - color: Colors.transparent,
308 - ),
309 - child: Text(
310 - S.of(context).send_new,
311 - style: TextStyle(
312 - fontSize: 14,
313 - fontWeight: FontWeight.w600,
314 - color: PaletteDark.walletCardText
315 - ),
316 - ),
317 - )
318 - ),
319 - ),
320 - );
321 - }
322 -
323 - index -= 1;
324 -
325 - final template = exchangeTemplateStore.templates[index];
326 -
327 - return TemplateTile(
328 - amount: template.amount,
329 - from: template.depositCurrency,
330 - to: template.receiveCurrency,
331 - onTap: () {
332 - applyTemplate(exchangeStore, template);
333 - }
334 - );
335 - }
336 - );
337 - }
338 - ),
339 - )
340 - ],
341 - ),
342 - bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
343 - bottomSection: Column(children: <Widget>[
344 - Padding(
345 - padding: EdgeInsets.only(bottom: 15),
346 - child: Observer(builder: (_) {
347 - final description =
348 - exchangeStore.provider is XMRTOExchangeProvider
349 - ? S.of(context).amount_is_guaranteed
350 - : S.of(context).amount_is_estimate;
351 - return Center(
352 - child: Text(
353 - description,
354 - style: TextStyle(
355 - color: PaletteDark.walletCardText,
356 - fontSize: 12
357 - ),
358 - ),
359 - );
360 - }),
361 - ),
362 - Observer(
363 - builder: (_) => LoadingPrimaryButton(
364 - text: S.of(context).exchange,
365 - onPressed: () {
366 - if (_formKey.currentState.validate()) {
367 - exchangeStore.createTrade();
368 - }
369 - },
370 - color: Colors.blue,
371 - textColor: Colors.white,
372 - isLoading: exchangeStore.tradeState is TradeIsCreating,
373 - )),
374 - ]),
375 - )),
66 + return BaseExchangeWidget(
67 + exchangeStore: exchangeStore,
68 + walletStore: walletStore,
69 + exchangeTemplateStore: exchangeTemplateStore,
70 + isTemplate: false
71 );
72 }
378 -
379 - void applyTemplate(ExchangeStore store, ExchangeTemplate template) {
380 - store.changeDepositCurrency(currency: CryptoCurrency.fromString(template.depositCurrency));
381 - store.changeReceiveCurrency(currency: CryptoCurrency.fromString(template.receiveCurrency));
382 -
383 - switch (template.provider) {
384 - case 'XMR.TO':
385 - store.changeProvider(provider: store.providerList[0]);
386 - break;
387 - case 'ChangeNOW':
388 - store.changeProvider(provider: store.providerList[1]);
389 - break;
390 - case 'MorphToken':
391 - store.changeProvider(provider: store.providerList[2]);
392 - break;
393 - }
394 -
395 - store.changeDepositAmount(amount: template.amount);
396 - store.depositAddress = template.depositAddress;
397 - store.receiveAddress = template.receiveAddress;
398 - }
399 -
400 - void _setReactions(
401 - BuildContext context, ExchangeStore store, WalletStore walletStore) {
402 - if (_isReactionsSet) {
403 - return;
404 - }
405 -
406 - final depositAddressController = depositKey.currentState.addressController;
407 - final depositAmountController = depositKey.currentState.amountController;
408 - final receiveAddressController = receiveKey.currentState.addressController;
409 - final receiveAmountController = receiveKey.currentState.amountController;
410 - final limitsState = store.limitsState;
411 -
412 - if (limitsState is LimitsLoadedSuccessfully) {
413 - final min = limitsState.limits.min != null
414 - ? limitsState.limits.min.toString()
415 - : null;
416 - final max = limitsState.limits.max != null
417 - ? limitsState.limits.max.toString()
418 - : null;
419 - final key = depositKey;
420 - key.currentState.changeLimits(min: min, max: max);
421 - }
422 -
423 - _onCurrencyChange(store.receiveCurrency, walletStore, receiveKey);
424 - _onCurrencyChange(store.depositCurrency, walletStore, depositKey);
425 -
426 - reaction(
427 - (_) => walletStore.name,
428 - (String _) => _onWalletNameChange(
429 - walletStore, store.receiveCurrency, receiveKey));
430 -
431 - reaction(
432 - (_) => walletStore.name,
433 - (String _) => _onWalletNameChange(
434 - walletStore, store.depositCurrency, depositKey));
435 -
436 - reaction(
437 - (_) => store.receiveCurrency,
438 - (CryptoCurrency currency) =>
439 - _onCurrencyChange(currency, walletStore, receiveKey));
440 -
441 - reaction(
442 - (_) => store.depositCurrency,
443 - (CryptoCurrency currency) =>
444 - _onCurrencyChange(currency, walletStore, depositKey));
445 -
446 - reaction((_) => store.depositAmount, (String amount) {
447 - if (depositKey.currentState.amountController.text != amount) {
448 - depositKey.currentState.amountController.text = amount;
449 - }
450 - });
451 -
452 - reaction((_) => store.depositAddress, (String address) {
453 - if (depositKey.currentState.addressController.text != address) {
454 - depositKey.currentState.addressController.text = address;
455 - }
456 - });
457 -
458 - reaction((_) => store.receiveAmount, (String amount) {
459 - if (receiveKey.currentState.amountController.text !=
460 - store.receiveAmount) {
461 - receiveKey.currentState.amountController.text = amount;
462 - }
463 - });
464 -
465 - reaction((_) => store.receiveAddress, (String address) {
466 - if (receiveKey.currentState.addressController.text != address) {
467 - receiveKey.currentState.addressController.text = address;
468 - }
469 - });
470 -
471 - reaction((_) => store.provider, (ExchangeProvider provider) {
472 - receiveKey.currentState.isAddressEditable(isEditable: true);
473 - receiveKey.currentState.isAmountEditable(isEditable: false);
474 - depositKey.currentState.isAddressEditable(isEditable: true);
475 - depositKey.currentState.isAmountEditable(isEditable: true);
476 -
477 - receiveKey.currentState.changeIsAmountEstimated(true);
478 - });
479 -
480 - reaction((_) => store.tradeState, (ExchangeTradeState state) {
481 - if (state is TradeIsCreatedFailure) {
482 - WidgetsBinding.instance.addPostFrameCallback((_) {
483 - showDialog<void>(
484 - context: context,
485 - builder: (BuildContext context) {
486 - return AlertWithOneAction(
487 - alertTitle: S.of(context).error,
488 - alertContent: state.error,
489 - buttonText: S.of(context).ok,
490 - buttonAction: () => Navigator.of(context).pop()
491 - );
492 - });
493 - });
494 - }
495 - if (state is TradeIsCreatedSuccessfully) {
496 - Navigator.of(context)
497 - .pushNamed(Routes.exchangeConfirm, arguments: state.trade);
498 - }
499 - });
500 -
501 - reaction((_) => store.limitsState, (LimitsState state) {
502 - String min;
503 - String max;
504 -
505 - if (state is LimitsLoadedSuccessfully) {
506 - min = state.limits.min != null ? state.limits.min.toString() : null;
507 - max = state.limits.max != null ? state.limits.max.toString() : null;
508 - }
509 -
510 - if (state is LimitsLoadedFailure) {
511 - min = '0';
512 - max = '0';
513 - }
514 -
515 - if (state is LimitsIsLoading) {
516 - min = '...';
517 - max = '...';
518 - }
519 -
520 - depositKey.currentState.changeLimits(min: min, max: max);
521 - receiveKey.currentState.changeLimits(min: null, max: null);
522 - });
523 -
524 - depositAddressController.addListener(
525 - () => store.depositAddress = depositAddressController.text);
526 -
527 - depositAmountController.addListener(() {
528 - if (depositAmountController.text != store.depositAmount) {
529 - store.changeDepositAmount(amount: depositAmountController.text);
530 - }
531 - });
532 -
533 - receiveAddressController.addListener(
534 - () => store.receiveAddress = receiveAddressController.text);
535 -
536 - receiveAmountController.addListener(() {
537 - if (receiveAmountController.text != store.receiveAmount) {
538 - store.changeReceiveAmount(amount: receiveAmountController.text);
539 - }
540 - });
541 -
542 - reaction((_) => walletStore.address, (String address) {
543 - if (store.depositCurrency == CryptoCurrency.xmr) {
544 - depositKey.currentState.changeAddress(address: address);
545 - }
546 -
547 - if (store.receiveCurrency == CryptoCurrency.xmr) {
548 - receiveKey.currentState.changeAddress(address: address);
549 - }
550 - });
551 -
552 - _isReactionsSet = true;
553 - }
554 -
555 - void _onCurrencyChange(CryptoCurrency currency, WalletStore walletStore,
556 - GlobalKey<ExchangeCardState> key) {
557 - final isCurrentTypeWallet = currency == walletStore.type;
558 -
559 - key.currentState.changeSelectedCurrency(currency);
560 - key.currentState
561 - .changeWalletName(isCurrentTypeWallet ? walletStore.name : null);
562 -
563 - key.currentState
564 - .changeAddress(address: isCurrentTypeWallet ? walletStore.address : '');
565 -
566 - key.currentState.changeAmount(amount: '');
567 - }
568 -
569 - void _onWalletNameChange(WalletStore walletStore, CryptoCurrency currency,
570 - GlobalKey<ExchangeCardState> key) {
571 - final isCurrentTypeWallet = currency == walletStore.type;
572 -
573 - if (isCurrentTypeWallet) {
574 - key.currentState.changeWalletName(walletStore.name);
575 - key.currentState.addressController.text = walletStore.address;
576 - } else if (key.currentState.addressController.text == walletStore.address) {
577 - key.currentState.changeWalletName(null);
578 - key.currentState.addressController.text = null;
579 - }
580 - }
73 }
lib/src/screens/exchange/exchange_template_page.dart
+10 -406
@@ -1,106 +1,28 @@
1 import 'dart:ui';
2 -import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
2 import 'package:flutter/cupertino.dart';
3 import 'package:flutter/material.dart';
5 -import 'package:flutter_mobx/flutter_mobx.dart';
6 -import 'package:mobx/mobx.dart';
4 import 'package:provider/provider.dart';
5 import 'package:cake_wallet/palette.dart';
9 -import 'package:cake_wallet/routes.dart';
10 -import 'package:cake_wallet/generated/i18n.dart';
11 -import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
12 -import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
13 -import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
14 -import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
15 -import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
6 import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
7 import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
8 import 'package:cake_wallet/src/screens/base_page.dart';
19 -import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
20 -import 'package:cake_wallet/src/widgets/primary_button.dart';
21 -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
22 -import 'package:cake_wallet/src/widgets/top_panel.dart';
23 -import 'package:cake_wallet/src/screens/exchange/widgets/provider_picker.dart';
9 +import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
10 import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
11 +import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
12 +import 'package:cake_wallet/generated/i18n.dart';
13
14 class ExchangeTemplatePage extends BasePage {
15 @override
28 - String get title => 'New template';
16 + String get title => S.current.exchange_new_template;
17
18 @override
19 Color get backgroundColor => PaletteDark.walletCardSubAddressField;
20
33 - final Image arrowBottom =
34 - Image.asset('assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 6);
35 -
21 @override
22 Widget trailing(BuildContext context) {
23 final exchangeStore = Provider.of<ExchangeStore>(context);
24
40 - return FlatButton(
41 - onPressed: () => _presentProviderPicker(context),
42 - highlightColor: Colors.transparent,
43 - splashColor: Colors.transparent,
44 - child: Row(
45 - mainAxisSize: MainAxisSize.min,
46 - crossAxisAlignment: CrossAxisAlignment.start,
47 - children: <Widget>[
48 - Column(
49 - crossAxisAlignment: CrossAxisAlignment.center,
50 - mainAxisSize: MainAxisSize.min,
51 - children: <Widget>[
52 - Text(S.of(context).exchange,
53 - style: TextStyle(
54 - fontSize: 16.0,
55 - fontWeight: FontWeight.w400,
56 - color: Colors.white)),
57 - Observer(
58 - builder: (_) => Text('${exchangeStore.provider.title}',
59 - style: TextStyle(
60 - fontSize: 10.0,
61 - fontWeight: FontWeight.w400,
62 - color:PaletteDark.walletCardText)))
63 - ],
64 - ),
65 - SizedBox(width: 5),
66 - Padding(
67 - padding: EdgeInsets.only(top: 8),
68 - child: arrowBottom,
69 - )
70 - ],
71 - )
72 - );
73 - }
74 -
75 - void _presentProviderPicker(BuildContext context) {
76 - final exchangeStore = Provider.of<ExchangeStore>(context);
77 - final items = exchangeStore.providersForCurrentPair();
78 - final selectedItem = items.indexOf(exchangeStore.provider);
79 - final images = List<Image>();
80 -
81 - for (ExchangeProvider provider in items) {
82 - switch (provider.description) {
83 - case ExchangeProviderDescription.xmrto:
84 - images.add(Image.asset('assets/images/xmr_btc.png'));
85 - break;
86 - case ExchangeProviderDescription.changeNow:
87 - images.add(Image.asset('assets/images/change_now.png'));
88 - break;
89 - case ExchangeProviderDescription.morphToken:
90 - images.add(Image.asset('assets/images/morph_icon.png'));
91 - break;
92 - }
93 - }
94 -
95 - showDialog<void>(
96 - builder: (_) => ProviderPicker(
97 - items: items,
98 - images: images,
99 - selectedAtIndex: selectedItem,
100 - title: S.of(context).change_exchange_provider,
101 - onItemSelected: (ExchangeProvider provider) =>
102 - exchangeStore.changeProvider(provider: provider)),
103 - context: context);
25 + return PresentProviderPicker(exchangeStore: exchangeStore);
26 }
27
28 @override
@@ -113,21 +35,6 @@ class ExchangeTemplateForm extends StatefulWidget{
35 }
36
37 class ExchangeTemplateFormState extends State<ExchangeTemplateForm> {
116 - final depositKey = GlobalKey<ExchangeCardState>();
117 - final receiveKey = GlobalKey<ExchangeCardState>();
118 - final _formKey = GlobalKey<FormState>();
119 - var _isReactionsSet = false;
120 -
121 - final Image arrowBottomPurple = Image.asset(
122 - 'assets/images/arrow_bottom_purple_icon.png',
123 - color: Colors.white,
124 - height: 8,
125 - );
126 - final Image arrowBottomCakeGreen = Image.asset(
127 - 'assets/images/arrow_bottom_cake_green.png',
128 - color: Colors.white,
129 - height: 8,
130 - );
38
39 @override
40 Widget build(BuildContext context) {
@@ -135,314 +42,11 @@ class ExchangeTemplateFormState extends State<ExchangeTemplateForm> {
42 final walletStore = Provider.of<WalletStore>(context);
43 final exchangeTemplateStore = Provider.of<ExchangeTemplateStore>(context);
44
138 - final depositWalletName =
139 - exchangeStore.depositCurrency == CryptoCurrency.xmr
140 - ? walletStore.name
141 - : null;
142 - final receiveWalletName =
143 - exchangeStore.receiveCurrency == CryptoCurrency.xmr
144 - ? walletStore.name
145 - : null;
146 -
147 - WidgetsBinding.instance.addPostFrameCallback(
148 - (_) => _setReactions(context, exchangeStore, walletStore));
149 -
150 - return Container(
151 - color: PaletteDark.historyPanel,
152 - child: Form(
153 - key: _formKey,
154 - child: ScrollableWithBottomSection(
155 - contentPadding: EdgeInsets.only(bottom: 24),
156 - content: Column(
157 - children: <Widget>[
158 - TopPanel(
159 - color: PaletteDark.menuList,
160 - edgeInsets: EdgeInsets.only(bottom: 24),
161 - widget: Column(
162 - children: <Widget>[
163 - TopPanel(
164 - color: PaletteDark.walletCardSubAddressField,
165 - widget: Observer(
166 - builder: (_) => ExchangeCard(
167 - key: depositKey,
168 - title: S.of(context).you_will_send,
169 - initialCurrency: exchangeStore.depositCurrency,
170 - initialWalletName: depositWalletName,
171 - initialAddress:
172 - exchangeStore.depositCurrency == walletStore.type
173 - ? walletStore.address
174 - : null,
175 - initialIsAmountEditable: true,
176 - initialIsAddressEditable: true,
177 - isAmountEstimated: false,
178 - currencies: CryptoCurrency.all,
179 - onCurrencySelected: (currency) =>
180 - exchangeStore.changeDepositCurrency(currency: currency),
181 - imageArrow: arrowBottomPurple,
182 - currencyButtonColor: PaletteDark.walletCardSubAddressField,
183 - addressButtonsColor: PaletteDark.menuList,
184 - currencyValueValidator: (value) {
185 - exchangeStore.validateCryptoCurrency(value);
186 - return exchangeStore.errorMessage;
187 - },
188 - addressTextFieldValidator: (value) {
189 - exchangeStore.validateAddress(value,
190 - cryptoCurrency: exchangeStore.depositCurrency);
191 - return exchangeStore.errorMessage;
192 - },
193 - ),
194 - )
195 - ),
196 - Padding(
197 - padding: EdgeInsets.only(top: 32, left: 24, right: 24),
198 - child: Observer(
199 - builder: (_) => ExchangeCard(
200 - key: receiveKey,
201 - title: S.of(context).you_will_get,
202 - initialCurrency: exchangeStore.receiveCurrency,
203 - initialWalletName: receiveWalletName,
204 - initialAddress:
205 - exchangeStore.receiveCurrency == walletStore.type
206 - ? walletStore.address
207 - : null,
208 - initialIsAmountEditable: false,
209 - initialIsAddressEditable: true,
210 - isAmountEstimated: true,
211 - currencies: CryptoCurrency.all,
212 - onCurrencySelected: (currency) => exchangeStore
213 - .changeReceiveCurrency(currency: currency),
214 - imageArrow: arrowBottomCakeGreen,
215 - currencyButtonColor: PaletteDark.menuList,
216 - currencyValueValidator: (value) {
217 - exchangeStore.validateCryptoCurrency(value);
218 - return exchangeStore.errorMessage;
219 - },
220 - addressTextFieldValidator: (value) {
221 - exchangeStore.validateAddress(value,
222 - cryptoCurrency: exchangeStore.receiveCurrency);
223 - return exchangeStore.errorMessage;
224 - },
225 - )),
226 - )
227 - ],
228 - )
229 - ),
230 - ],
231 - ),
232 - bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
233 - bottomSection: Column(children: <Widget>[
234 - Padding(
235 - padding: EdgeInsets.only(bottom: 15),
236 - child: Observer(builder: (_) {
237 - final description =
238 - exchangeStore.provider is XMRTOExchangeProvider
239 - ? S.of(context).amount_is_guaranteed
240 - : S.of(context).amount_is_estimate;
241 - return Center(
242 - child: Text(
243 - description,
244 - style: TextStyle(
245 - color: PaletteDark.walletCardText,
246 - fontSize: 12
247 - ),
248 - ),
249 - );
250 - }),
251 - ),
252 - PrimaryButton(
253 - onPressed: () {
254 - if (_formKey.currentState.validate()) {
255 - exchangeTemplateStore.addTemplate(
256 - amount: exchangeStore.depositAmount,
257 - depositCurrency: exchangeStore.depositCurrency.toString(),
258 - receiveCurrency: exchangeStore.receiveCurrency.toString(),
259 - provider: exchangeStore.provider.toString(),
260 - depositAddress: exchangeStore.depositAddress,
261 - receiveAddress: exchangeStore.receiveAddress
262 - );
263 - exchangeTemplateStore.update();
264 - Navigator.of(context).pop();
265 - }
266 - },
267 - text: S.of(context).save,
268 - color: Colors.green,
269 - textColor: Colors.white
270 - ),
271 - ]),
272 - )),
45 + return BaseExchangeWidget(
46 + exchangeStore: exchangeStore,
47 + walletStore: walletStore,
48 + exchangeTemplateStore: exchangeTemplateStore,
49 + isTemplate: true
50 );
51 }
275 -
276 - void _setReactions(
277 - BuildContext context, ExchangeStore store, WalletStore walletStore) {
278 - if (_isReactionsSet) {
279 - return;
280 - }
281 -
282 - final depositAddressController = depositKey.currentState.addressController;
283 - final depositAmountController = depositKey.currentState.amountController;
284 - final receiveAddressController = receiveKey.currentState.addressController;
285 - final receiveAmountController = receiveKey.currentState.amountController;
286 - final limitsState = store.limitsState;
287 -
288 - if (limitsState is LimitsLoadedSuccessfully) {
289 - final min = limitsState.limits.min != null
290 - ? limitsState.limits.min.toString()
291 - : null;
292 - final max = limitsState.limits.max != null
293 - ? limitsState.limits.max.toString()
294 - : null;
295 - final key = depositKey;
296 - key.currentState.changeLimits(min: min, max: max);
297 - }
298 -
299 - _onCurrencyChange(store.receiveCurrency, walletStore, receiveKey);
300 - _onCurrencyChange(store.depositCurrency, walletStore, depositKey);
301 -
302 - reaction(
303 - (_) => walletStore.name,
304 - (String _) => _onWalletNameChange(
305 - walletStore, store.receiveCurrency, receiveKey));
306 -
307 - reaction(
308 - (_) => walletStore.name,
309 - (String _) => _onWalletNameChange(
310 - walletStore, store.depositCurrency, depositKey));
311 -
312 - reaction(
313 - (_) => store.receiveCurrency,
314 - (CryptoCurrency currency) =>
315 - _onCurrencyChange(currency, walletStore, receiveKey));
316 -
317 - reaction(
318 - (_) => store.depositCurrency,
319 - (CryptoCurrency currency) =>
320 - _onCurrencyChange(currency, walletStore, depositKey));
321 -
322 - reaction((_) => store.depositAmount, (String amount) {
323 - if (depositKey.currentState.amountController.text != amount) {
324 - depositKey.currentState.amountController.text = amount;
325 - }
326 - });
327 -
328 - reaction((_) => store.receiveAmount, (String amount) {
329 - if (receiveKey.currentState.amountController.text !=
330 - store.receiveAmount) {
331 - receiveKey.currentState.amountController.text = amount;
332 - }
333 - });
334 -
335 - reaction((_) => store.provider, (ExchangeProvider provider) {
336 - receiveKey.currentState.isAddressEditable(isEditable: true);
337 - receiveKey.currentState.isAmountEditable(isEditable: false);
338 - depositKey.currentState.isAddressEditable(isEditable: true);
339 - depositKey.currentState.isAmountEditable(isEditable: true);
340 -
341 - receiveKey.currentState.changeIsAmountEstimated(true);
342 - });
343 -
344 - reaction((_) => store.tradeState, (ExchangeTradeState state) {
345 - if (state is TradeIsCreatedFailure) {
346 - WidgetsBinding.instance.addPostFrameCallback((_) {
347 - showDialog<void>(
348 - context: context,
349 - builder: (BuildContext context) {
350 - return AlertDialog(
351 - title: Text(S.of(context).error),
352 - content: Text(state.error),
353 - actions: <Widget>[
354 - FlatButton(
355 - child: Text(S.of(context).ok),
356 - onPressed: () => Navigator.of(context).pop())
357 - ],
358 - );
359 - });
360 - });
361 - }
362 - if (state is TradeIsCreatedSuccessfully) {
363 - Navigator.of(context)
364 - .pushNamed(Routes.exchangeConfirm, arguments: state.trade);
365 - }
366 - });
367 -
368 - reaction((_) => store.limitsState, (LimitsState state) {
369 - String min;
370 - String max;
371 -
372 - if (state is LimitsLoadedSuccessfully) {
373 - min = state.limits.min != null ? state.limits.min.toString() : null;
374 - max = state.limits.max != null ? state.limits.max.toString() : null;
375 - }
376 -
377 - if (state is LimitsLoadedFailure) {
378 - min = '0';
379 - max = '0';
380 - }
381 -
382 - if (state is LimitsIsLoading) {
383 - min = '...';
384 - max = '...';
385 - }
386 -
387 - depositKey.currentState.changeLimits(min: min, max: max);
388 - receiveKey.currentState.changeLimits(min: null, max: null);
389 - });
390 -
391 - depositAddressController.addListener(
392 - () => store.depositAddress = depositAddressController.text);
393 -
394 - depositAmountController.addListener(() {
395 - if (depositAmountController.text != store.depositAmount) {
396 - store.changeDepositAmount(amount: depositAmountController.text);
397 - }
398 - });
399 -
400 - receiveAddressController.addListener(
401 - () => store.receiveAddress = receiveAddressController.text);
402 -
403 - receiveAmountController.addListener(() {
404 - if (receiveAmountController.text != store.receiveAmount) {
405 - store.changeReceiveAmount(amount: receiveAmountController.text);
406 - }
407 - });
408 -
409 - reaction((_) => walletStore.address, (String address) {
410 - if (store.depositCurrency == CryptoCurrency.xmr) {
411 - depositKey.currentState.changeAddress(address: address);
412 - }
413 -
414 - if (store.receiveCurrency == CryptoCurrency.xmr) {
415 - receiveKey.currentState.changeAddress(address: address);
416 - }
417 - });
418 -
419 - _isReactionsSet = true;
420 - }
421 -
422 - void _onCurrencyChange(CryptoCurrency currency, WalletStore walletStore,
423 - GlobalKey<ExchangeCardState> key) {
424 - final isCurrentTypeWallet = currency == walletStore.type;
425 -
426 - key.currentState.changeSelectedCurrency(currency);
427 - key.currentState
428 - .changeWalletName(isCurrentTypeWallet ? walletStore.name : null);
429 -
430 - key.currentState
431 - .changeAddress(address: isCurrentTypeWallet ? walletStore.address : '');
432 -
433 - key.currentState.changeAmount(amount: '');
434 - }
435 -
436 - void _onWalletNameChange(WalletStore walletStore, CryptoCurrency currency,
437 - GlobalKey<ExchangeCardState> key) {
438 - final isCurrentTypeWallet = currency == walletStore.type;
439 -
440 - if (isCurrentTypeWallet) {
441 - key.currentState.changeWalletName(walletStore.name);
442 - key.currentState.addressController.text = walletStore.address;
443 - } else if (key.currentState.addressController.text == walletStore.address) {
444 - key.currentState.changeWalletName(null);
445 - key.currentState.addressController.text = null;
446 - }
447 - }
52 }
\ No newline at end of file
lib/src/screens/exchange/widgets/base_exchange_widget.dart new
+521
@@ -0,0 +1,521 @@
1 +import 'dart:ui';
2 +import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
3 +import 'package:cake_wallet/src/widgets/template_tile.dart';
4 +import 'package:dotted_border/dotted_border.dart';
5 +import 'package:flutter/cupertino.dart';
6 +import 'package:flutter/material.dart';
7 +import 'package:flutter_mobx/flutter_mobx.dart';
8 +import 'package:mobx/mobx.dart';
9 +import 'package:cake_wallet/palette.dart';
10 +import 'package:cake_wallet/routes.dart';
11 +import 'package:cake_wallet/generated/i18n.dart';
12 +import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
13 +import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
14 +import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
15 +import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
16 +import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
17 +import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
18 +import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
19 +import 'package:cake_wallet/src/widgets/primary_button.dart';
20 +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
21 +import 'package:cake_wallet/src/widgets/top_panel.dart';
22 +import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
23 +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
24 +
25 +class BaseExchangeWidget extends StatefulWidget {
26 + BaseExchangeWidget({
27 + @ required this.exchangeStore,
28 + @ required this.walletStore,
29 + @ required this.exchangeTemplateStore,
30 + @ required this.isTemplate,
31 + });
32 +
33 + final ExchangeStore exchangeStore;
34 + final WalletStore walletStore;
35 + final ExchangeTemplateStore exchangeTemplateStore;
36 + final bool isTemplate;
37 +
38 + @override
39 + BaseExchangeWidgetState createState() =>
40 + BaseExchangeWidgetState(
41 + exchangeStore: exchangeStore,
42 + walletStore: walletStore,
43 + exchangeTemplateStore: exchangeTemplateStore,
44 + isTemplate: isTemplate
45 + );
46 +}
47 +
48 +class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
49 + BaseExchangeWidgetState({
50 + @ required this.exchangeStore,
51 + @ required this.walletStore,
52 + @ required this.exchangeTemplateStore,
53 + @ required this.isTemplate,
54 + });
55 +
56 + final ExchangeStore exchangeStore;
57 + final WalletStore walletStore;
58 + final ExchangeTemplateStore exchangeTemplateStore;
59 + final bool isTemplate;
60 +
61 + final depositKey = GlobalKey<ExchangeCardState>();
62 + final receiveKey = GlobalKey<ExchangeCardState>();
63 + final _formKey = GlobalKey<FormState>();
64 + var _isReactionsSet = false;
65 +
66 + final Image arrowBottomPurple = Image.asset(
67 + 'assets/images/arrow_bottom_purple_icon.png',
68 + color: Colors.white,
69 + height: 8,
70 + );
71 + final Image arrowBottomCakeGreen = Image.asset(
72 + 'assets/images/arrow_bottom_cake_green.png',
73 + color: Colors.white,
74 + height: 8,
75 + );
76 +
77 + @override
78 + Widget build(BuildContext context) {
79 +
80 + final depositWalletName =
81 + exchangeStore.depositCurrency == CryptoCurrency.xmr
82 + ? walletStore.name
83 + : null;
84 + final receiveWalletName =
85 + exchangeStore.receiveCurrency == CryptoCurrency.xmr
86 + ? walletStore.name
87 + : null;
88 +
89 + WidgetsBinding.instance.addPostFrameCallback(
90 + (_) => _setReactions(context, exchangeStore, walletStore));
91 +
92 + return Container(
93 + color: PaletteDark.historyPanel,
94 + child: Form(
95 + key: _formKey,
96 + child: ScrollableWithBottomSection(
97 + contentPadding: EdgeInsets.only(bottom: 24),
98 + content: Column(
99 + children: <Widget>[
100 + TopPanel(
101 + color: PaletteDark.menuList,
102 + edgeInsets: EdgeInsets.only(bottom: 24),
103 + widget: Column(
104 + children: <Widget>[
105 + TopPanel(
106 + color: PaletteDark.walletCardSubAddressField,
107 + widget: Observer(
108 + builder: (_) => ExchangeCard(
109 + key: depositKey,
110 + title: S.of(context).you_will_send,
111 + initialCurrency: exchangeStore.depositCurrency,
112 + initialWalletName: depositWalletName,
113 + initialAddress:
114 + exchangeStore.depositCurrency == walletStore.type
115 + ? walletStore.address
116 + : exchangeStore.depositAddress,
117 + initialIsAmountEditable: true,
118 + initialIsAddressEditable: exchangeStore.isDepositAddressEnabled,
119 + isAmountEstimated: false,
120 + currencies: CryptoCurrency.all,
121 + onCurrencySelected: (currency) =>
122 + exchangeStore.changeDepositCurrency(currency: currency),
123 + imageArrow: arrowBottomPurple,
124 + currencyButtonColor: PaletteDark.walletCardSubAddressField,
125 + addressButtonsColor: PaletteDark.menuList,
126 + currencyValueValidator: (value) {
127 + exchangeStore.validateCryptoCurrency(value);
128 + return exchangeStore.errorMessage;
129 + },
130 + addressTextFieldValidator: (value) {
131 + exchangeStore.validateAddress(value,
132 + cryptoCurrency: exchangeStore.depositCurrency);
133 + return exchangeStore.errorMessage;
134 + },
135 + ),
136 + )
137 + ),
138 + Padding(
139 + padding: EdgeInsets.only(top: 32, left: 24, right: 24),
140 + child: Observer(
141 + builder: (_) => ExchangeCard(
142 + key: receiveKey,
143 + title: S.of(context).you_will_get,
144 + initialCurrency: exchangeStore.receiveCurrency,
145 + initialWalletName: receiveWalletName,
146 + initialAddress:
147 + exchangeStore.receiveCurrency == walletStore.type
148 + ? walletStore.address
149 + : exchangeStore.receiveAddress,
150 + initialIsAmountEditable: false,
151 + initialIsAddressEditable: exchangeStore.isReceiveAddressEnabled,
152 + isAmountEstimated: true,
153 + currencies: CryptoCurrency.all,
154 + onCurrencySelected: (currency) => exchangeStore
155 + .changeReceiveCurrency(currency: currency),
156 + imageArrow: arrowBottomCakeGreen,
157 + currencyButtonColor: PaletteDark.menuList,
158 + currencyValueValidator: (value) {
159 + exchangeStore.validateCryptoCurrency(value);
160 + return exchangeStore.errorMessage;
161 + },
162 + addressTextFieldValidator: (value) {
163 + exchangeStore.validateAddress(value,
164 + cryptoCurrency: exchangeStore.receiveCurrency);
165 + return exchangeStore.errorMessage;
166 + },
167 + )),
168 + )
169 + ],
170 + )
171 + ),
172 + isTemplate
173 + ? Offstage()
174 + : Padding(
175 + padding: EdgeInsets.only(
176 + top: 32,
177 + left: 24,
178 + bottom: 24
179 + ),
180 + child: Row(
181 + mainAxisAlignment: MainAxisAlignment.start,
182 + children: <Widget>[
183 + Text(
184 + S.of(context).send_templates,
185 + style: TextStyle(
186 + fontSize: 18,
187 + fontWeight: FontWeight.w600,
188 + color: PaletteDark.walletCardText
189 + ),
190 + )
191 + ],
192 + ),
193 + ),
194 + isTemplate
195 + ? Offstage()
196 + : Container(
197 + height: 40,
198 + width: double.infinity,
199 + padding: EdgeInsets.only(left: 24),
200 + child: Observer(
201 + builder: (_) {
202 + final itemCount = exchangeTemplateStore.templates.length + 1;
203 +
204 + return ListView.builder(
205 + scrollDirection: Axis.horizontal,
206 + itemCount: itemCount,
207 + itemBuilder: (context, index) {
208 +
209 + if (index == 0) {
210 + return GestureDetector(
211 + onTap: () => Navigator.of(context)
212 + .pushNamed(Routes.exchangeTemplate),
213 + child: Container(
214 + padding: EdgeInsets.only(right: 10),
215 + child: DottedBorder(
216 + borderType: BorderType.RRect,
217 + dashPattern: [8, 4],
218 + color: PaletteDark.menuList,
219 + strokeWidth: 2,
220 + radius: Radius.circular(20),
221 + child: Container(
222 + height: 40,
223 + width: 75,
224 + padding: EdgeInsets.only(left: 10, right: 10),
225 + alignment: Alignment.center,
226 + decoration: BoxDecoration(
227 + borderRadius: BorderRadius.all(Radius.circular(20)),
228 + color: Colors.transparent,
229 + ),
230 + child: Text(
231 + S.of(context).send_new,
232 + style: TextStyle(
233 + fontSize: 14,
234 + fontWeight: FontWeight.w600,
235 + color: PaletteDark.walletCardText
236 + ),
237 + ),
238 + )
239 + ),
240 + ),
241 + );
242 + }
243 +
244 + index -= 1;
245 +
246 + final template = exchangeTemplateStore.templates[index];
247 +
248 + return TemplateTile(
249 + amount: template.amount,
250 + from: template.depositCurrency,
251 + to: template.receiveCurrency,
252 + onTap: () {
253 + applyTemplate(exchangeStore, template);
254 + }
255 + );
256 + }
257 + );
258 + }
259 + ),
260 + )
261 + ],
262 + ),
263 + bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
264 + bottomSection: Column(children: <Widget>[
265 + Padding(
266 + padding: EdgeInsets.only(bottom: 15),
267 + child: Observer(builder: (_) {
268 + final description =
269 + exchangeStore.provider is XMRTOExchangeProvider
270 + ? S.of(context).amount_is_guaranteed
271 + : S.of(context).amount_is_estimate;
272 + return Center(
273 + child: Text(
274 + description,
275 + style: TextStyle(
276 + color: PaletteDark.walletCardText,
277 + fontSize: 12
278 + ),
279 + ),
280 + );
281 + }),
282 + ),
283 + isTemplate
284 + ? PrimaryButton(
285 + onPressed: () {
286 + if (_formKey.currentState.validate()) {
287 + exchangeTemplateStore.addTemplate(
288 + amount: exchangeStore.depositAmount,
289 + depositCurrency: exchangeStore.depositCurrency.toString(),
290 + receiveCurrency: exchangeStore.receiveCurrency.toString(),
291 + provider: exchangeStore.provider.toString(),
292 + depositAddress: exchangeStore.depositAddress,
293 + receiveAddress: exchangeStore.receiveAddress
294 + );
295 + exchangeTemplateStore.update();
296 + Navigator.of(context).pop();
297 + }
298 + },
299 + text: S.of(context).save,
300 + color: Colors.green,
301 + textColor: Colors.white
302 + )
303 + : Observer(
304 + builder: (_) => LoadingPrimaryButton(
305 + text: S.of(context).exchange,
306 + onPressed: () {
307 + if (_formKey.currentState.validate()) {
308 + exchangeStore.createTrade();
309 + }
310 + },
311 + color: Colors.blue,
312 + textColor: Colors.white,
313 + isLoading: exchangeStore.tradeState is TradeIsCreating,
314 + )),
315 + ]),
316 + )),
317 + );
318 + }
319 +
320 + void applyTemplate(ExchangeStore store, ExchangeTemplate template) {
321 + store.changeDepositCurrency(currency: CryptoCurrency.fromString(template.depositCurrency));
322 + store.changeReceiveCurrency(currency: CryptoCurrency.fromString(template.receiveCurrency));
323 +
324 + switch (template.provider) {
325 + case 'XMR.TO':
326 + store.changeProvider(provider: store.providerList[0]);
327 + break;
328 + case 'ChangeNOW':
329 + store.changeProvider(provider: store.providerList[1]);
330 + break;
331 + case 'MorphToken':
332 + store.changeProvider(provider: store.providerList[2]);
333 + break;
334 + }
335 +
336 + store.changeDepositAmount(amount: template.amount);
337 + store.depositAddress = template.depositAddress;
338 + store.receiveAddress = template.receiveAddress;
339 + }
340 +
341 + void _setReactions(
342 + BuildContext context, ExchangeStore store, WalletStore walletStore) {
343 + if (_isReactionsSet) {
344 + return;
345 + }
346 +
347 + final depositAddressController = depositKey.currentState.addressController;
348 + final depositAmountController = depositKey.currentState.amountController;
349 + final receiveAddressController = receiveKey.currentState.addressController;
350 + final receiveAmountController = receiveKey.currentState.amountController;
351 + final limitsState = store.limitsState;
352 +
353 + if (limitsState is LimitsLoadedSuccessfully) {
354 + final min = limitsState.limits.min != null
355 + ? limitsState.limits.min.toString()
356 + : null;
357 + final max = limitsState.limits.max != null
358 + ? limitsState.limits.max.toString()
359 + : null;
360 + final key = depositKey;
361 + key.currentState.changeLimits(min: min, max: max);
362 + }
363 +
364 + _onCurrencyChange(store.receiveCurrency, walletStore, receiveKey);
365 + _onCurrencyChange(store.depositCurrency, walletStore, depositKey);
366 +
367 + reaction(
368 + (_) => walletStore.name,
369 + (String _) => _onWalletNameChange(
370 + walletStore, store.receiveCurrency, receiveKey));
371 +
372 + reaction(
373 + (_) => walletStore.name,
374 + (String _) => _onWalletNameChange(
375 + walletStore, store.depositCurrency, depositKey));
376 +
377 + reaction(
378 + (_) => store.receiveCurrency,
379 + (CryptoCurrency currency) =>
380 + _onCurrencyChange(currency, walletStore, receiveKey));
381 +
382 + reaction(
383 + (_) => store.depositCurrency,
384 + (CryptoCurrency currency) =>
385 + _onCurrencyChange(currency, walletStore, depositKey));
386 +
387 + reaction((_) => store.depositAmount, (String amount) {
388 + if (depositKey.currentState.amountController.text != amount) {
389 + depositKey.currentState.amountController.text = amount;
390 + }
391 + });
392 +
393 + reaction((_) => store.depositAddress, (String address) {
394 + if (depositKey.currentState.addressController.text != address) {
395 + depositKey.currentState.addressController.text = address;
396 + }
397 + });
398 +
399 + reaction((_) => store.isDepositAddressEnabled, (bool isEnabled) {
400 + depositKey.currentState.isAddressEditable(isEditable: isEnabled);
401 + });
402 +
403 + reaction((_) => store.receiveAmount, (String amount) {
404 + if (receiveKey.currentState.amountController.text !=
405 + store.receiveAmount) {
406 + receiveKey.currentState.amountController.text = amount;
407 + }
408 + });
409 +
410 + reaction((_) => store.receiveAddress, (String address) {
411 + if (receiveKey.currentState.addressController.text != address) {
412 + receiveKey.currentState.addressController.text = address;
413 + }
414 + });
415 +
416 + reaction((_) => store.isReceiveAddressEnabled, (bool isEnabled) {
417 + receiveKey.currentState.isAddressEditable(isEditable: isEnabled);
418 + });
419 +
420 + reaction((_) => store.tradeState, (ExchangeTradeState state) {
421 + if (state is TradeIsCreatedFailure) {
422 + WidgetsBinding.instance.addPostFrameCallback((_) {
423 + showDialog<void>(
424 + context: context,
425 + builder: (BuildContext context) {
426 + return AlertWithOneAction(
427 + alertTitle: S.of(context).error,
428 + alertContent: state.error,
429 + buttonText: S.of(context).ok,
430 + buttonAction: () => Navigator.of(context).pop()
431 + );
432 + });
433 + });
434 + }
435 + if (state is TradeIsCreatedSuccessfully) {
436 + Navigator.of(context)
437 + .pushNamed(Routes.exchangeConfirm, arguments: state.trade);
438 + }
439 + });
440 +
441 + reaction((_) => store.limitsState, (LimitsState state) {
442 + String min;
443 + String max;
444 +
445 + if (state is LimitsLoadedSuccessfully) {
446 + min = state.limits.min != null ? state.limits.min.toString() : null;
447 + max = state.limits.max != null ? state.limits.max.toString() : null;
448 + }
449 +
450 + if (state is LimitsLoadedFailure) {
451 + min = '0';
452 + max = '0';
453 + }
454 +
455 + if (state is LimitsIsLoading) {
456 + min = '...';
457 + max = '...';
458 + }
459 +
460 + depositKey.currentState.changeLimits(min: min, max: max);
461 + receiveKey.currentState.changeLimits(min: null, max: null);
462 + });
463 +
464 + depositAddressController.addListener(
465 + () => store.depositAddress = depositAddressController.text);
466 +
467 + depositAmountController.addListener(() {
468 + if (depositAmountController.text != store.depositAmount) {
469 + store.changeDepositAmount(amount: depositAmountController.text);
470 + }
471 + });
472 +
473 + receiveAddressController.addListener(
474 + () => store.receiveAddress = receiveAddressController.text);
475 +
476 + receiveAmountController.addListener(() {
477 + if (receiveAmountController.text != store.receiveAmount) {
478 + store.changeReceiveAmount(amount: receiveAmountController.text);
479 + }
480 + });
481 +
482 + reaction((_) => walletStore.address, (String address) {
483 + if (store.depositCurrency == CryptoCurrency.xmr) {
484 + depositKey.currentState.changeAddress(address: address);
485 + }
486 +
487 + if (store.receiveCurrency == CryptoCurrency.xmr) {
488 + receiveKey.currentState.changeAddress(address: address);
489 + }
490 + });
491 +
492 + _isReactionsSet = true;
493 + }
494 +
495 + void _onCurrencyChange(CryptoCurrency currency, WalletStore walletStore,
496 + GlobalKey<ExchangeCardState> key) {
497 + final isCurrentTypeWallet = currency == walletStore.type;
498 +
499 + key.currentState.changeSelectedCurrency(currency);
500 + key.currentState
501 + .changeWalletName(isCurrentTypeWallet ? walletStore.name : null);
502 +
503 + key.currentState
504 + .changeAddress(address: isCurrentTypeWallet ? walletStore.address : '');
505 +
506 + key.currentState.changeAmount(amount: '');
507 + }
508 +
509 + void _onWalletNameChange(WalletStore walletStore, CryptoCurrency currency,
510 + GlobalKey<ExchangeCardState> key) {
511 + final isCurrentTypeWallet = currency == walletStore.type;
512 +
513 + if (isCurrentTypeWallet) {
514 + key.currentState.changeWalletName(walletStore.name);
515 + key.currentState.addressController.text = walletStore.address;
516 + } else if (key.currentState.addressController.text == walletStore.address) {
517 + key.currentState.changeWalletName(null);
518 + key.currentState.addressController.text = null;
519 + }
520 + }
521 +}
\ No newline at end of file
lib/src/screens/exchange/widgets/present_provider_picker.dart new
+84
@@ -0,0 +1,84 @@
1 +import 'package:flutter/material.dart';
2 +import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
3 +import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
4 +import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
5 +import 'package:cake_wallet/src/screens/exchange/widgets/provider_picker.dart';
6 +import 'package:flutter_mobx/flutter_mobx.dart';
7 +import 'package:cake_wallet/generated/i18n.dart';
8 +import 'package:cake_wallet/palette.dart';
9 +
10 +class PresentProviderPicker extends StatelessWidget {
11 + PresentProviderPicker({@required this.exchangeStore});
12 +
13 + final ExchangeStore exchangeStore;
14 +
15 + final Image arrowBottom =
16 + Image.asset('assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 6);
17 +
18 + @override
19 + Widget build(BuildContext context) {
20 + return FlatButton(
21 + onPressed: () => _presentProviderPicker(context),
22 + highlightColor: Colors.transparent,
23 + splashColor: Colors.transparent,
24 + child: Row(
25 + mainAxisSize: MainAxisSize.min,
26 + crossAxisAlignment: CrossAxisAlignment.start,
27 + children: <Widget>[
28 + Column(
29 + crossAxisAlignment: CrossAxisAlignment.center,
30 + mainAxisSize: MainAxisSize.min,
31 + children: <Widget>[
32 + Text(S.of(context).exchange,
33 + style: TextStyle(
34 + fontSize: 16.0,
35 + fontWeight: FontWeight.w400,
36 + color: Colors.white)),
37 + Observer(
38 + builder: (_) => Text('${exchangeStore.provider.title}',
39 + style: TextStyle(
40 + fontSize: 10.0,
41 + fontWeight: FontWeight.w400,
42 + color:PaletteDark.walletCardText)))
43 + ],
44 + ),
45 + SizedBox(width: 5),
46 + Padding(
47 + padding: EdgeInsets.only(top: 8),
48 + child: arrowBottom,
49 + )
50 + ],
51 + )
52 + );
53 + }
54 +
55 + void _presentProviderPicker(BuildContext context) {
56 + final items = exchangeStore.providersForCurrentPair();
57 + final selectedItem = items.indexOf(exchangeStore.provider);
58 + final images = List<Image>();
59 +
60 + for (ExchangeProvider provider in items) {
61 + switch (provider.description) {
62 + case ExchangeProviderDescription.xmrto:
63 + images.add(Image.asset('assets/images/xmr_btc.png'));
64 + break;
65 + case ExchangeProviderDescription.changeNow:
66 + images.add(Image.asset('assets/images/change_now.png'));
67 + break;
68 + case ExchangeProviderDescription.morphToken:
69 + images.add(Image.asset('assets/images/morph_icon.png'));
70 + break;
71 + }
72 + }
73 +
74 + showDialog<void>(
75 + builder: (_) => ProviderPicker(
76 + items: items,
77 + images: images,
78 + selectedAtIndex: selectedItem,
79 + title: S.of(context).change_exchange_provider,
80 + onItemSelected: (ExchangeProvider provider) =>
81 + exchangeStore.changeProvider(provider: provider)),
82 + context: context);
83 + }
84 +}
\ No newline at end of file
lib/src/stores/exchange/exchange_store.dart
+17 -4
@@ -33,6 +33,8 @@ abstract class ExchangeStoreBase with Store {
33 provider = initialProvider;
34 depositCurrency = initialDepositCurrency;
35 receiveCurrency = initialReceiveCurrency;
36 + isDepositAddressEnabled = !(depositCurrency == walletStore.type);
37 + isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
38 depositAmount = '';
39 receiveAmount = '';
40 depositAddress = '';
@@ -73,6 +75,12 @@ abstract class ExchangeStoreBase with Store {
75 @observable
76 String receiveAddress;
77
78 + @observable
79 + bool isDepositAddressEnabled;
80 +
81 + @observable
82 + bool isReceiveAddressEnabled;
83 +
84 @observable
85 bool isValid;
86
@@ -99,12 +107,16 @@ abstract class ExchangeStoreBase with Store {
107 void changeDepositCurrency({CryptoCurrency currency}) {
108 depositCurrency = currency;
109 _onPairChange();
110 + isDepositAddressEnabled = !(depositCurrency == walletStore.type);
111 + isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
112 }
113
114 @action
115 void changeReceiveCurrency({CryptoCurrency currency}) {
116 receiveCurrency = currency;
117 _onPairChange();
118 + isDepositAddressEnabled = !(depositCurrency == walletStore.type);
119 + isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
120 }
121
122 @action
@@ -224,12 +236,13 @@ abstract class ExchangeStoreBase with Store {
236 void reset() {
237 depositAmount = '';
238 receiveAmount = '';
227 - depositAddress = '';
228 - receiveAddress = '';
229 - provider = XMRTOExchangeProvider();
239 depositCurrency = CryptoCurrency.xmr;
240 receiveCurrency = CryptoCurrency.btc;
232 - loadLimits();
241 + depositAddress = depositCurrency == walletStore.type ? walletStore.address : '';
242 + receiveAddress = receiveCurrency == walletStore.type ? walletStore.address : '';
243 + isDepositAddressEnabled = !(depositCurrency == walletStore.type);
244 + isReceiveAddressEnabled = !(receiveCurrency == walletStore.type);
245 + _onPairChange();
246 }
247
248 List<ExchangeProvider> providersForCurrentPair() {
lib/src/widgets/address_text_field.dart
+5
@@ -124,6 +124,11 @@ class AddressTextField extends StatelessWidget {
124 color: PaletteDark.walletCardSubAddressField,
125 width: 1.0))
126 : InputBorder.none,
127 + disabledBorder: isBorderExist
128 + ? UnderlineInputBorder(
129 + borderSide:
130 + BorderSide(color: PaletteDark.walletCardSubAddressField, width: 1.0))
131 + : InputBorder.none,
132 enabledBorder: isBorderExist
133 ? UnderlineInputBorder(
134 borderSide:
res/values/strings_de.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "Abgelaufen",
97 "time" : "${minutes}m ${seconds}s",
98 "send_xmr" : "Senden XMR",
99 + "exchange_new_template" : "Neue Vorlage",
100
101 "faq" : "FAQ",
102
res/values/strings_en.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "Expired",
97 "time" : "${minutes}m ${seconds}s",
98 "send_xmr" : "Send XMR",
99 + "exchange_new_template" : "New template",
100
101 "faq" : "FAQ",
102
res/values/strings_es.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "Muerto",
97 "time" : "${minutes}m ${seconds}s",
98 "send_xmr" : "Enviar XMR",
99 + "exchange_new_template" : "Nueva plantilla",
100
101 "faq" : "FAQ",
102
res/values/strings_hi.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "समय सीमा समाप्त",
97 "time" : "${minutes}m ${seconds}s",
98 "send_xmr" : "संदेश XMR",
99 + "exchange_new_template" : "नया टेम्पलेट",
100
101 "faq" : "FAQ",
102
res/values/strings_ja.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "期限切れ",
97 "time" : "${minutes}m ${seconds}s",
98 "send_xmr" : "送る XMR",
99 + "exchange_new_template" : "新しいテンプレート",
100
101 "faq" : "FAQ",
102
res/values/strings_ko.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "만료",
97 "time" : "${minutes}m ${seconds}s",
98 "send_xmr" : "보내다 XMR",
99 + "exchange_new_template" : "새 템플릿",
100
101 "faq" : "FAQ",
102
res/values/strings_nl.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "Verlopen",
97 "time" : "${minutes}m ${seconds}s",
98 "send_xmr" : "Sturen XMR",
99 + "exchange_new_template" : "Nieuwe sjabloon",
100
101 "faq" : "FAQ",
102
res/values/strings_pl.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "Przedawniony",
97 "time" : "${minutes}m ${seconds}s",
98 "send_xmr" : "Wysłać XMR",
99 + "exchange_new_template" : "Nowy szablon",
100
101 "faq" : "FAQ",
102
res/values/strings_pt.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "Expirada",
97 "time" : "${minutes}m ${seconds}s",
98 "send_xmr" : "Enviar XMR",
99 + "exchange_new_template" : "Novo modelo",
100
101 "faq" : "FAQ",
102
res/values/strings_ru.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "Истекает",
97 "time" : "${minutes}мин ${seconds}сек",
98 "send_xmr" : "Отправить XMR",
99 + "exchange_new_template" : "Новый шаблон",
100
101 "faq" : "FAQ",
102
res/values/strings_uk.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "Закінчується",
97 "time" : "${minutes}хв ${seconds}сек",
98 "send_xmr" : "Відправити XMR",
99 + "exchange_new_template" : "Новий шаблон",
100
101 "faq" : "FAQ",
102
res/values/strings_zh.arb
+1
@@ -96,6 +96,7 @@
96 "expired" : "已过期",
97 "time" : "${minutes}m ${seconds}s",
98 "send_xmr" : "发送 XMR",
99 + "exchange_new_template" : "新範本",
100
101 "faq" : "FAQ",
102