CWA-212 | created exchange template and exchange template store; added exchange cards to exchange template page; added exchange templates to exchange page; added provider icons to provider picker

Oleksandr Sobol committed May 14, 2020 at 20:29 UTC b1f8658eec857d0d4d2e3ffbaf4c85d67c86567b
14 files changed +537 -265
assets/images/2.0x/morph_icon.png
Binary files a/assets/images/2.0x/morph_icon.png and b/assets/images/2.0x/morph_icon.png differ
assets/images/3.0x/morph_icon.png
Binary files a/assets/images/3.0x/morph_icon.png and b/assets/images/3.0x/morph_icon.png differ
assets/images/morph_icon.png
Binary files a/assets/images/morph_icon.png and b/assets/images/morph_icon.png differ
lib/main.dart
+6
@@ -22,6 +22,7 @@ import 'package:cake_wallet/src/stores/balance/balance_store.dart';
22 import 'package:cake_wallet/src/stores/sync/sync_store.dart';
23 import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
24 import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
25 +import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
26 import 'package:cake_wallet/src/screens/root/root.dart';
27 import 'package:cake_wallet/src/stores/authentication/authentication_store.dart';
28 import 'package:cake_wallet/src/stores/settings/settings_store.dart';
@@ -34,6 +35,7 @@ import 'package:cake_wallet/src/domain/common/fiat_currency.dart';
35 import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
36 import 'package:cake_wallet/src/domain/common/wallet_type.dart';
37 import 'package:cake_wallet/src/domain/common/template.dart';
38 +import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
39 import 'package:cake_wallet/src/domain/services/wallet_service.dart';
40 import 'package:cake_wallet/generated/i18n.dart';
41 import 'package:cake_wallet/src/domain/common/language.dart';
@@ -51,6 +53,7 @@ void main() async {
53 Hive.registerAdapter(WalletInfoAdapter(), 4);
54 Hive.registerAdapter(WalletTypeAdapter(), 5);
55 Hive.registerAdapter(TemplateAdapter(), 6);
56 + Hive.registerAdapter(ExchangeTemplateAdapter(), 7);
57
58 final secureStorage = FlutterSecureStorage();
59 final transactionDescriptionsBoxKey = await getEncryptionKey(
@@ -69,6 +72,7 @@ void main() async {
72 await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
73 final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
74 final templates = await Hive.openBox<Template>(Template.boxName);
75 + final exchangeTemplates = await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
76
77 final sharedPreferences = await SharedPreferences.getInstance();
78 final walletService = WalletService();
@@ -106,6 +110,7 @@ void main() async {
110 sharedPreferences: sharedPreferences, walletsService: walletListService);
111 final seedLanguageStore = SeedLanguageStore();
112 final sendTemplateStore = SendTemplateStore(templateSource: templates);
113 + final exchangeTemplateStore = ExchangeTemplateStore(templateSource: exchangeTemplates);
114
115 setReactions(
116 settingsStore: settingsStore,
@@ -133,6 +138,7 @@ void main() async {
138 Provider(create: (_) => trades),
139 Provider(create: (_) => seedLanguageStore),
140 Provider(create: (_) => sendTemplateStore),
141 + Provider(create: (_) => exchangeTemplateStore),
142 ], child: CakeWalletApp()));
143 }
144
lib/router.dart
+20
@@ -88,6 +88,7 @@ import 'package:cake_wallet/src/screens/dashboard/create_dashboard_page.dart';
88 import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
89 import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
90 import 'package:cake_wallet/src/screens/send/send_template_page.dart';
91 +import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
92
93 class Router {
94 static Route<dynamic> generateRoute(
@@ -470,6 +471,25 @@ class Router {
471 }),
472 ], child: ExchangePage()));
473
474 + case Routes.exchangeTemplate:
475 + return MaterialPageRoute<void>(
476 + builder: (_) => Provider(create: (_) {
477 + final xmrtoprovider = XMRTOExchangeProvider();
478 +
479 + return ExchangeStore(
480 + initialProvider: xmrtoprovider,
481 + initialDepositCurrency: CryptoCurrency.xmr,
482 + initialReceiveCurrency: CryptoCurrency.btc,
483 + trades: trades,
484 + providerList: [
485 + xmrtoprovider,
486 + ChangeNowExchangeProvider(),
487 + MorphTokenExchangeProvider(trades: trades)
488 + ],
489 + walletStore: walletStore);
490 + }, child: ExchangeTemplatePage(),)
491 + );
492 +
493 case Routes.settings:
494 return MaterialPageRoute<void>(
495 builder: (_) => Provider(
lib/routes.dart
+1
@@ -45,4 +45,5 @@ class Routes {
45 static const changeLanguage = '/change_language';
46 static const newWalletType = '/new_wallet_type';
47 static const sendTemplate = '/send_template';
48 + static const exchangeTemplate = '/exchange_template';
49 }
\ No newline at end of file
lib/src/domain/exchange/exchange_template.dart new
+35
@@ -0,0 +1,35 @@
1 +import 'package:hive/hive.dart';
2 +
3 +part 'exchange_template.g.dart';
4 +
5 +@HiveType()
6 +class ExchangeTemplate extends HiveObject {
7 + ExchangeTemplate({
8 + this.amount,
9 + this.depositCurrency,
10 + this.receiveCurrency,
11 + this.provider,
12 + this.depositAddress,
13 + this.receiveAddress
14 + });
15 +
16 + static const boxName = 'ExchangeTemplate';
17 +
18 + @HiveField(0)
19 + String amount;
20 +
21 + @HiveField(1)
22 + String depositCurrency;
23 +
24 + @HiveField(2)
25 + String receiveCurrency;
26 +
27 + @HiveField(3)
28 + String provider;
29 +
30 + @HiveField(4)
31 + String depositAddress;
32 +
33 + @HiveField(5)
34 + String receiveAddress;
35 +}
\ No newline at end of file
lib/src/screens/exchange/exchange_page.dart
+70 -119
@@ -1,4 +1,7 @@
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';
6 import 'package:flutter/cupertino.dart';
7 import 'package:flutter/material.dart';
@@ -10,7 +13,6 @@ import 'package:cake_wallet/routes.dart';
13 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';
13 -import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.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';
@@ -22,6 +24,7 @@ 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';
27 +import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
28
29 class ExchangePage extends BasePage {
30 @override
@@ -100,10 +103,26 @@ class ExchangePage extends BasePage {
103 final exchangeStore = Provider.of<ExchangeStore>(context);
104 final items = exchangeStore.providersForCurrentPair();
105 final selectedItem = items.indexOf(exchangeStore.provider);
106 + final images = List<Image>();
107 +
108 + for (ExchangeProvider provider in items) {
109 + switch (provider.description) {
110 + case ExchangeProviderDescription.xmrto:
111 + images.add(Image.asset('assets/images/xmr_btc.png'));
112 + break;
113 + case ExchangeProviderDescription.changeNow:
114 + images.add(Image.asset('assets/images/change_now.png'));
115 + break;
116 + case ExchangeProviderDescription.morphToken:
117 + images.add(Image.asset('assets/images/morph_icon.png'));
118 + break;
119 + }
120 + }
121
122 showDialog<void>(
123 builder: (_) => ProviderPicker(
124 items: items,
125 + images: images,
126 selectedAtIndex: selectedItem,
127 title: S.of(context).change_exchange_provider,
128 onItemSelected: (ExchangeProvider provider) =>
@@ -138,6 +157,7 @@ class ExchangeFormState extends State<ExchangeForm> {
157 Widget build(BuildContext context) {
158 final exchangeStore = Provider.of<ExchangeStore>(context);
159 final walletStore = Provider.of<WalletStore>(context);
160 + final exchangeTemplateStore = Provider.of<ExchangeTemplateStore>(context);
161
162 final depositWalletName =
163 exchangeStore.depositCurrency == CryptoCurrency.xmr
@@ -175,7 +195,7 @@ class ExchangeFormState extends State<ExchangeForm> {
195 initialAddress:
196 exchangeStore.depositCurrency == walletStore.type
197 ? walletStore.address
178 - : null,
198 + : exchangeStore.depositAddress,
199 initialIsAmountEditable: true,
200 initialIsAddressEditable: true,
201 isAmountEstimated: false,
@@ -208,7 +228,7 @@ class ExchangeFormState extends State<ExchangeForm> {
228 initialAddress:
229 exchangeStore.receiveCurrency == walletStore.type
230 ? walletStore.address
211 - : null,
231 + : exchangeStore.receiveAddress,
232 initialIsAmountEditable: false,
233 initialIsAddressEditable: true,
234 isAmountEstimated: true,
@@ -257,15 +277,17 @@ class ExchangeFormState extends State<ExchangeForm> {
277 padding: EdgeInsets.only(left: 24),
278 child: Observer(
279 builder: (_) {
280 + final itemCount = exchangeTemplateStore.templates.length + 1;
281
282 return ListView.builder(
283 scrollDirection: Axis.horizontal,
263 - itemCount: 1,
284 + itemCount: itemCount,
285 itemBuilder: (context, index) {
286
287 if (index == 0) {
288 return GestureDetector(
268 - onTap: () {},
289 + onTap: () => Navigator.of(context)
290 + .pushNamed(Routes.exchangeTemplate),
291 child: Container(
292 padding: EdgeInsets.only(right: 10),
293 child: DottedBorder(
@@ -299,7 +321,16 @@ class ExchangeFormState extends State<ExchangeForm> {
321
322 index -= 1;
323
302 - return Container();
324 + final template = exchangeTemplateStore.templates[index];
325 +
326 + return TemplateTile(
327 + amount: template.amount,
328 + from: template.depositCurrency,
329 + to: template.receiveCurrency,
330 + onTap: () {
331 + applyTemplate(exchangeStore, template);
332 + }
333 + );
334 }
335 );
336 }
@@ -307,88 +338,6 @@ class ExchangeFormState extends State<ExchangeForm> {
338 )
339 ],
340 ),
310 -
311 - /*SingleChildScrollView(
312 - child: Column(
313 - mainAxisAlignment: MainAxisAlignment.spaceAround,
314 - children: <Widget>[
315 - Padding(
316 - padding: EdgeInsets.only(top: 10, bottom: 20),
317 - child: Text(
318 - S.of(context).you_will_send,
319 - style: TextStyle(
320 - fontWeight: FontWeight.bold,
321 - fontSize: 18,
322 - height: 1.1,
323 - color: Theme.of(context).primaryTextTheme.title.color),
324 - ),
325 - ),
326 - ExchangeCard(
327 - key: depositKey,
328 - initialCurrency: exchangeStore.depositCurrency,
329 - initialWalletName: depositWalletName,
330 - initialAddress:
331 - exchangeStore.depositCurrency == walletStore.type
332 - ? walletStore.address
333 - : null,
334 - initialIsAmountEditable: true,
335 - initialIsAddressEditable: true,
336 - isAmountEstimated: false,
337 - currencies: CryptoCurrency.all,
338 - /*onCurrencySelected: (currency) =>
339 - exchangeStore.changeDepositCurrency(currency: currency),*/
340 - imageArrow: arrowBottomPurple,
341 - currencyValueValidator: (value) {
342 - exchangeStore.validateCryptoCurrency(value);
343 - return exchangeStore.errorMessage;
344 - },
345 - addressTextFieldValidator: (value) {
346 - exchangeStore.validateAddress(value,
347 - cryptoCurrency: exchangeStore.depositCurrency);
348 - return exchangeStore.errorMessage;
349 - },
350 - ),
351 - SizedBox(height: 35),
352 - Padding(
353 - padding: EdgeInsets.only(bottom: 20),
354 - child: Text(
355 - S.of(context).you_will_get,
356 - style: TextStyle(
357 - fontWeight: FontWeight.bold,
358 - fontSize: 18,
359 - height: 1.1,
360 - color:
361 - Theme.of(context).primaryTextTheme.title.color),
362 - )),
363 - Observer(
364 - builder: (_) => ExchangeCard(
365 - key: receiveKey,
366 - initialCurrency: exchangeStore.receiveCurrency,
367 - initialWalletName: receiveWalletName,
368 - initialAddress:
369 - exchangeStore.receiveCurrency == walletStore.type
370 - ? walletStore.address
371 - : null,
372 - initialIsAmountEditable: false,
373 - initialIsAddressEditable: true,
374 - isAmountEstimated: true,
375 - currencies: CryptoCurrency.all,
376 - /*onCurrencySelected: (currency) => exchangeStore
377 - .changeReceiveCurrency(currency: currency),*/
378 - imageArrow: arrowBottomCakeGreen,
379 - currencyValueValidator: (value) {
380 - exchangeStore.validateCryptoCurrency(value);
381 - return exchangeStore.errorMessage;
382 - },
383 - addressTextFieldValidator: (value) {
384 - exchangeStore.validateAddress(value,
385 - cryptoCurrency: exchangeStore.receiveCurrency);
386 - return exchangeStore.errorMessage;
387 - },
388 - )),
389 - ],
390 - ),
391 - ),*/
341 bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
342 bottomSection: Column(children: <Widget>[
343 Padding(
@@ -421,42 +370,32 @@ class ExchangeFormState extends State<ExchangeForm> {
370 textColor: Colors.white,
371 isLoading: exchangeStore.tradeState is TradeIsCreating,
372 )),
424 - /*Observer(builder: (_) {
425 - final title = exchangeStore.provider.description.title;
426 - var imageSrc = '';
427 -
428 - switch (exchangeStore.provider.description) {
429 - case ExchangeProviderDescription.xmrto:
430 - imageSrc = 'assets/images/xmr_btc.png';
431 - break;
432 - case ExchangeProviderDescription.changeNow:
433 - imageSrc = 'assets/images/change_now.png';
434 - break;
435 - case ExchangeProviderDescription.morphToken:
436 - imageSrc = 'assets/images/morph_icon.png';
437 - break;
438 - }
439 -
440 - return Padding(
441 - padding: EdgeInsets.only(top: 20, bottom: 20),
442 - child: Row(
443 - mainAxisAlignment: MainAxisAlignment.center,
444 - children: <Widget>[
445 - Image.asset(imageSrc),
446 - SizedBox(width: 10),
447 - Text(
448 - S.of(context).powered_by(title),
449 - style: TextStyle(fontSize: 14, color: Palette.powered),
450 - )
451 - ],
452 - ),
453 - );
454 - })*/
373 ]),
374 )),
375 );
376 }
377
378 + void applyTemplate(ExchangeStore store, ExchangeTemplate template) {
379 + store.changeDepositCurrency(currency: CryptoCurrency.fromString(template.depositCurrency));
380 + store.changeReceiveCurrency(currency: CryptoCurrency.fromString(template.receiveCurrency));
381 +
382 + switch (template.provider) {
383 + case 'XMR.TO':
384 + store.changeProvider(provider: store.providerList[0]);
385 + break;
386 + case 'ChangeNOW':
387 + store.changeProvider(provider: store.providerList[1]);
388 + break;
389 + case 'MorphToken':
390 + store.changeProvider(provider: store.providerList[2]);
391 + break;
392 + }
393 +
394 + store.changeDepositAmount(amount: template.amount);
395 + store.depositAddress = template.depositAddress;
396 + store.receiveAddress = template.receiveAddress;
397 + }
398 +
399 void _setReactions(
400 BuildContext context, ExchangeStore store, WalletStore walletStore) {
401 if (_isReactionsSet) {
@@ -509,6 +448,12 @@ class ExchangeFormState extends State<ExchangeForm> {
448 }
449 });
450
451 + reaction((_) => store.depositAddress, (String address) {
452 + if (depositKey.currentState.addressController.text != address) {
453 + depositKey.currentState.addressController.text = address;
454 + }
455 + });
456 +
457 reaction((_) => store.receiveAmount, (String amount) {
458 if (receiveKey.currentState.amountController.text !=
459 store.receiveAmount) {
@@ -516,6 +461,12 @@ class ExchangeFormState extends State<ExchangeForm> {
461 }
462 });
463
464 + reaction((_) => store.receiveAddress, (String address) {
465 + if (receiveKey.currentState.addressController.text != address) {
466 + receiveKey.currentState.addressController.text = address;
467 + }
468 + });
469 +
470 reaction((_) => store.provider, (ExchangeProvider provider) {
471 receiveKey.currentState.isAddressEditable(isEditable: true);
472 receiveKey.currentState.isAmountEditable(isEditable: false);
lib/src/screens/exchange/exchange_template_page.dart
+331 -4
@@ -1,5 +1,4 @@
1 import 'dart:ui';
2 -import 'package:dotted_border/dotted_border.dart';
2 import 'package:flutter/cupertino.dart';
3 import 'package:flutter/material.dart';
4 import 'package:flutter_mobx/flutter_mobx.dart';
@@ -10,7 +9,6 @@ import 'package:cake_wallet/routes.dart';
9 import 'package:cake_wallet/generated/i18n.dart';
10 import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
11 import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
13 -import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
12 import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
13 import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
14 import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
@@ -22,6 +20,7 @@ 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/screens/exchange/widgets/provider_picker.dart';
23 +import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
24
25 class ExchangeTemplatePage extends BasePage {
26 @override
@@ -31,7 +30,7 @@ class ExchangeTemplatePage extends BasePage {
30 Color get backgroundColor => PaletteDark.walletCardSubAddressField;
31
32 final Image arrowBottom =
34 - Image.asset('assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 8);
33 + Image.asset('assets/images/arrow_bottom_purple_icon.png', color: Colors.white, height: 6);
34
35 @override
36 Widget trailing(BuildContext context) {
@@ -97,8 +96,336 @@ class ExchangeTemplateForm extends StatefulWidget{
96 }
97
98 class ExchangeTemplateFormState extends State<ExchangeTemplateForm> {
99 + final depositKey = GlobalKey<ExchangeCardState>();
100 + final receiveKey = GlobalKey<ExchangeCardState>();
101 + final _formKey = GlobalKey<FormState>();
102 + var _isReactionsSet = false;
103 +
104 + final Image arrowBottomPurple = Image.asset(
105 + 'assets/images/arrow_bottom_purple_icon.png',
106 + color: Colors.white,
107 + height: 8,
108 + );
109 + final Image arrowBottomCakeGreen = Image.asset(
110 + 'assets/images/arrow_bottom_cake_green.png',
111 + color: Colors.white,
112 + height: 8,
113 + );
114 +
115 @override
116 Widget build(BuildContext context) {
102 - return Container();
117 + final exchangeStore = Provider.of<ExchangeStore>(context);
118 + final walletStore = Provider.of<WalletStore>(context);
119 + final exchangeTemplateStore = Provider.of<ExchangeTemplateStore>(context);
120 +
121 + final depositWalletName =
122 + exchangeStore.depositCurrency == CryptoCurrency.xmr
123 + ? walletStore.name
124 + : null;
125 + final receiveWalletName =
126 + exchangeStore.receiveCurrency == CryptoCurrency.xmr
127 + ? walletStore.name
128 + : null;
129 +
130 + WidgetsBinding.instance.addPostFrameCallback(
131 + (_) => _setReactions(context, exchangeStore, walletStore));
132 +
133 + return Container(
134 + color: PaletteDark.historyPanel,
135 + child: Form(
136 + key: _formKey,
137 + child: ScrollableWithBottomSection(
138 + contentPadding: EdgeInsets.only(bottom: 24),
139 + content: Column(
140 + children: <Widget>[
141 + TopPanel(
142 + color: PaletteDark.menuList,
143 + edgeInsets: EdgeInsets.only(bottom: 24),
144 + widget: Column(
145 + children: <Widget>[
146 + TopPanel(
147 + color: PaletteDark.walletCardSubAddressField,
148 + widget: Observer(
149 + builder: (_) => ExchangeCard(
150 + key: depositKey,
151 + title: S.of(context).you_will_send,
152 + initialCurrency: exchangeStore.depositCurrency,
153 + initialWalletName: depositWalletName,
154 + initialAddress:
155 + exchangeStore.depositCurrency == walletStore.type
156 + ? walletStore.address
157 + : null,
158 + initialIsAmountEditable: true,
159 + initialIsAddressEditable: true,
160 + isAmountEstimated: false,
161 + currencies: CryptoCurrency.all,
162 + onCurrencySelected: (currency) =>
163 + exchangeStore.changeDepositCurrency(currency: currency),
164 + imageArrow: arrowBottomPurple,
165 + currencyButtonColor: PaletteDark.walletCardSubAddressField,
166 + addressButtonsColor: PaletteDark.menuList,
167 + currencyValueValidator: (value) {
168 + exchangeStore.validateCryptoCurrency(value);
169 + return exchangeStore.errorMessage;
170 + },
171 + addressTextFieldValidator: (value) {
172 + exchangeStore.validateAddress(value,
173 + cryptoCurrency: exchangeStore.depositCurrency);
174 + return exchangeStore.errorMessage;
175 + },
176 + ),
177 + )
178 + ),
179 + Padding(
180 + padding: EdgeInsets.only(top: 32, left: 24, right: 24),
181 + child: Observer(
182 + builder: (_) => ExchangeCard(
183 + key: receiveKey,
184 + title: S.of(context).you_will_get,
185 + initialCurrency: exchangeStore.receiveCurrency,
186 + initialWalletName: receiveWalletName,
187 + initialAddress:
188 + exchangeStore.receiveCurrency == walletStore.type
189 + ? walletStore.address
190 + : null,
191 + initialIsAmountEditable: false,
192 + initialIsAddressEditable: true,
193 + isAmountEstimated: true,
194 + currencies: CryptoCurrency.all,
195 + onCurrencySelected: (currency) => exchangeStore
196 + .changeReceiveCurrency(currency: currency),
197 + imageArrow: arrowBottomCakeGreen,
198 + currencyButtonColor: PaletteDark.menuList,
199 + currencyValueValidator: (value) {
200 + exchangeStore.validateCryptoCurrency(value);
201 + return exchangeStore.errorMessage;
202 + },
203 + addressTextFieldValidator: (value) {
204 + exchangeStore.validateAddress(value,
205 + cryptoCurrency: exchangeStore.receiveCurrency);
206 + return exchangeStore.errorMessage;
207 + },
208 + )),
209 + )
210 + ],
211 + )
212 + ),
213 + ],
214 + ),
215 + bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
216 + bottomSection: Column(children: <Widget>[
217 + Padding(
218 + padding: EdgeInsets.only(bottom: 15),
219 + child: Observer(builder: (_) {
220 + final description =
221 + exchangeStore.provider is XMRTOExchangeProvider
222 + ? S.of(context).amount_is_guaranteed
223 + : S.of(context).amount_is_estimate;
224 + return Center(
225 + child: Text(
226 + description,
227 + style: TextStyle(
228 + color: PaletteDark.walletCardText,
229 + fontSize: 12
230 + ),
231 + ),
232 + );
233 + }),
234 + ),
235 + PrimaryButton(
236 + onPressed: () {
237 + if (_formKey.currentState.validate()) {
238 + exchangeTemplateStore.addTemplate(
239 + amount: exchangeStore.depositAmount,
240 + depositCurrency: exchangeStore.depositCurrency.toString(),
241 + receiveCurrency: exchangeStore.receiveCurrency.toString(),
242 + provider: exchangeStore.provider.toString(),
243 + depositAddress: exchangeStore.depositAddress,
244 + receiveAddress: exchangeStore.receiveAddress
245 + );
246 + exchangeTemplateStore.update();
247 + Navigator.of(context).pop();
248 + }
249 + },
250 + text: S.of(context).save,
251 + color: Colors.green,
252 + textColor: Colors.white
253 + ),
254 + ]),
255 + )),
256 + );
257 + }
258 +
259 + void _setReactions(
260 + BuildContext context, ExchangeStore store, WalletStore walletStore) {
261 + if (_isReactionsSet) {
262 + return;
263 + }
264 +
265 + final depositAddressController = depositKey.currentState.addressController;
266 + final depositAmountController = depositKey.currentState.amountController;
267 + final receiveAddressController = receiveKey.currentState.addressController;
268 + final receiveAmountController = receiveKey.currentState.amountController;
269 + final limitsState = store.limitsState;
270 +
271 + if (limitsState is LimitsLoadedSuccessfully) {
272 + final min = limitsState.limits.min != null
273 + ? limitsState.limits.min.toString()
274 + : null;
275 + final max = limitsState.limits.max != null
276 + ? limitsState.limits.max.toString()
277 + : null;
278 + final key = depositKey;
279 + key.currentState.changeLimits(min: min, max: max);
280 + }
281 +
282 + _onCurrencyChange(store.receiveCurrency, walletStore, receiveKey);
283 + _onCurrencyChange(store.depositCurrency, walletStore, depositKey);
284 +
285 + reaction(
286 + (_) => walletStore.name,
287 + (String _) => _onWalletNameChange(
288 + walletStore, store.receiveCurrency, receiveKey));
289 +
290 + reaction(
291 + (_) => walletStore.name,
292 + (String _) => _onWalletNameChange(
293 + walletStore, store.depositCurrency, depositKey));
294 +
295 + reaction(
296 + (_) => store.receiveCurrency,
297 + (CryptoCurrency currency) =>
298 + _onCurrencyChange(currency, walletStore, receiveKey));
299 +
300 + reaction(
301 + (_) => store.depositCurrency,
302 + (CryptoCurrency currency) =>
303 + _onCurrencyChange(currency, walletStore, depositKey));
304 +
305 + reaction((_) => store.depositAmount, (String amount) {
306 + if (depositKey.currentState.amountController.text != amount) {
307 + depositKey.currentState.amountController.text = amount;
308 + }
309 + });
310 +
311 + reaction((_) => store.receiveAmount, (String amount) {
312 + if (receiveKey.currentState.amountController.text !=
313 + store.receiveAmount) {
314 + receiveKey.currentState.amountController.text = amount;
315 + }
316 + });
317 +
318 + reaction((_) => store.provider, (ExchangeProvider provider) {
319 + receiveKey.currentState.isAddressEditable(isEditable: true);
320 + receiveKey.currentState.isAmountEditable(isEditable: false);
321 + depositKey.currentState.isAddressEditable(isEditable: true);
322 + depositKey.currentState.isAmountEditable(isEditable: true);
323 +
324 + receiveKey.currentState.changeIsAmountEstimated(true);
325 + });
326 +
327 + reaction((_) => store.tradeState, (ExchangeTradeState state) {
328 + if (state is TradeIsCreatedFailure) {
329 + WidgetsBinding.instance.addPostFrameCallback((_) {
330 + showDialog<void>(
331 + context: context,
332 + builder: (BuildContext context) {
333 + return AlertDialog(
334 + title: Text(S.of(context).error),
335 + content: Text(state.error),
336 + actions: <Widget>[
337 + FlatButton(
338 + child: Text(S.of(context).ok),
339 + onPressed: () => Navigator.of(context).pop())
340 + ],
341 + );
342 + });
343 + });
344 + }
345 + if (state is TradeIsCreatedSuccessfully) {
346 + Navigator.of(context)
347 + .pushNamed(Routes.exchangeConfirm, arguments: state.trade);
348 + }
349 + });
350 +
351 + reaction((_) => store.limitsState, (LimitsState state) {
352 + String min;
353 + String max;
354 +
355 + if (state is LimitsLoadedSuccessfully) {
356 + min = state.limits.min != null ? state.limits.min.toString() : null;
357 + max = state.limits.max != null ? state.limits.max.toString() : null;
358 + }
359 +
360 + if (state is LimitsLoadedFailure) {
361 + min = '0';
362 + max = '0';
363 + }
364 +
365 + if (state is LimitsIsLoading) {
366 + min = '...';
367 + max = '...';
368 + }
369 +
370 + depositKey.currentState.changeLimits(min: min, max: max);
371 + receiveKey.currentState.changeLimits(min: null, max: null);
372 + });
373 +
374 + depositAddressController.addListener(
375 + () => store.depositAddress = depositAddressController.text);
376 +
377 + depositAmountController.addListener(() {
378 + if (depositAmountController.text != store.depositAmount) {
379 + store.changeDepositAmount(amount: depositAmountController.text);
380 + }
381 + });
382 +
383 + receiveAddressController.addListener(
384 + () => store.receiveAddress = receiveAddressController.text);
385 +
386 + receiveAmountController.addListener(() {
387 + if (receiveAmountController.text != store.receiveAmount) {
388 + store.changeReceiveAmount(amount: receiveAmountController.text);
389 + }
390 + });
391 +
392 + reaction((_) => walletStore.address, (String address) {
393 + if (store.depositCurrency == CryptoCurrency.xmr) {
394 + depositKey.currentState.changeAddress(address: address);
395 + }
396 +
397 + if (store.receiveCurrency == CryptoCurrency.xmr) {
398 + receiveKey.currentState.changeAddress(address: address);
399 + }
400 + });
401 +
402 + _isReactionsSet = true;
403 + }
404 +
405 + void _onCurrencyChange(CryptoCurrency currency, WalletStore walletStore,
406 + GlobalKey<ExchangeCardState> key) {
407 + final isCurrentTypeWallet = currency == walletStore.type;
408 +
409 + key.currentState.changeSelectedCurrency(currency);
410 + key.currentState
411 + .changeWalletName(isCurrentTypeWallet ? walletStore.name : null);
412 +
413 + key.currentState
414 + .changeAddress(address: isCurrentTypeWallet ? walletStore.address : '');
415 +
416 + key.currentState.changeAmount(amount: '');
417 + }
418 +
419 + void _onWalletNameChange(WalletStore walletStore, CryptoCurrency currency,
420 + GlobalKey<ExchangeCardState> key) {
421 + final isCurrentTypeWallet = currency == walletStore.type;
422 +
423 + if (isCurrentTypeWallet) {
424 + key.currentState.changeWalletName(walletStore.name);
425 + key.currentState.addressController.text = walletStore.address;
426 + } else if (key.currentState.addressController.text == walletStore.address) {
427 + key.currentState.changeWalletName(null);
428 + key.currentState.addressController.text = null;
429 + }
430 }
431 }
\ No newline at end of file
lib/src/screens/exchange/widgets/exchange_card.dart
+1 -128
@@ -148,7 +148,7 @@ class ExchangeCardState extends State<ExchangeCard> {
148 validator: widget.currencyValueValidator
149 ),
150 Positioned(
151 - bottom: 8,
151 + top: 8,
152 right: 0,
153 child: Container(
154 height: 32,
@@ -222,133 +222,6 @@ class ExchangeCardState extends State<ExchangeCard> {
222 validator: widget.addressTextFieldValidator,
223 ),
224 )
225 -
226 - /*Container(
227 - child: Row(
228 - crossAxisAlignment: CrossAxisAlignment.center,
229 - children: <Widget>[
230 - Container(
231 - height: 52,
232 - width: 90,
233 - child: InkWell(
234 - onTap: () => _presentPicker(context),
235 - child: Column(
236 - crossAxisAlignment: CrossAxisAlignment.start,
237 - mainAxisAlignment: MainAxisAlignment.spaceBetween,
238 - children: <Widget>[
239 - Row(
240 - mainAxisAlignment: MainAxisAlignment.spaceBetween,
241 - children: <Widget>[
242 - Text(_selectedCurrency.toString(),
243 - style: TextStyle(
244 - fontWeight: FontWeight.bold,
245 - fontSize: 24,
246 - color: Theme.of(context)
247 - .primaryTextTheme
248 - .title
249 - .color)),
250 - widget.imageArrow
251 - ]),
252 - _walletName != null
253 - ? Text(_walletName,
254 - style: TextStyle(
255 - fontSize: 12,
256 - color: Palette.wildDarkBlue))
257 - : SizedBox(),
258 - ]),
259 - ),
260 - ),
261 - SizedBox(width: 10),
262 - Flexible(
263 - child: Column(
264 - children: [
265 - TextFormField(
266 - style: TextStyle(fontSize: 23, height: 1.21),
267 - controller: amountController,
268 - enabled: _isAmountEditable,
269 - textAlign: TextAlign.right,
270 - keyboardType: TextInputType.numberWithOptions(
271 - signed: false, decimal: true),
272 - inputFormatters: [
273 - BlacklistingTextInputFormatter(
274 - RegExp('[\\-|\\ |\\,]'))
275 - ],
276 - decoration: InputDecoration(
277 - hintStyle: TextStyle(
278 - color: Theme.of(context).cardTheme.color,
279 - fontSize: 23,
280 - height: 1.21),
281 - hintText: '0.00000000',
282 - focusedBorder: UnderlineInputBorder(
283 - borderSide: BorderSide(
284 - color: Palette.cakeGreen, width: 2.0)),
285 - enabledBorder: UnderlineInputBorder(
286 - borderSide: BorderSide(
287 - color: _isAmountEditable
288 - ? Palette.deepPurple
289 - : Theme.of(context).focusColor,
290 - width: 1.0))),
291 - validator: widget.currencyValueValidator),
292 - SizedBox(height: 5),
293 - SizedBox(
294 - height: 15,
295 - width: double.infinity,
296 - child: Container(
297 - child: Row(
298 - mainAxisAlignment: MainAxisAlignment.end,
299 - children: <Widget>[
300 - _min != null
301 - ? Text(
302 - S.of(context).min_value(
303 - _min, _selectedCurrency.toString()),
304 - style: TextStyle(
305 - fontSize: 10,
306 - height: 1.2,
307 - color: Theme.of(context)
308 - .primaryTextTheme
309 - .subtitle
310 - .color),
311 - )
312 - : SizedBox(),
313 - _min != null ? SizedBox(width: 10) : SizedBox(),
314 - _max != null
315 - ? Text(
316 - S.of(context).max_value(
317 - _max, _selectedCurrency.toString()),
318 - style: TextStyle(
319 - fontSize: 10,
320 - height: 1.2,
321 - color: Theme.of(context)
322 - .primaryTextTheme
323 - .subtitle
324 - .color))
325 - : SizedBox(),
326 - ]),
327 - ),
328 - ),
329 - ],
330 - ),
331 - ),
332 - ]),
333 - ),
334 - SizedBox(height: 10),
335 - AddressTextField(
336 - controller: addressController,
337 - isActive: _isAddressEditable,
338 - options: _isAddressEditable
339 - ? _walletName != null
340 - ? [
341 - AddressTextFieldOption.qrCode,
342 - AddressTextFieldOption.addressBook,
343 - AddressTextFieldOption.subaddressList
344 - ]
345 - : [
346 - AddressTextFieldOption.qrCode,
347 - AddressTextFieldOption.addressBook,
348 - ]
349 - : [],
350 - validator: widget.addressTextFieldValidator,
351 - )*/
225 ]),
226 );
227 }
lib/src/screens/exchange/widgets/provider_picker.dart
+22 -9
@@ -8,12 +8,14 @@ class ProviderPicker extends StatelessWidget {
8 ProviderPicker({
9 @required this.selectedAtIndex,
10 @required this.items,
11 + @required this.images,
12 @required this.title,
13 @required this.onItemSelected,
14 });
15
16 final int selectedAtIndex;
17 final List<ExchangeProvider> items;
18 + final List<Image> images;
19 final String title;
20 final Function(ExchangeProvider) onItemSelected;
21
@@ -61,6 +63,7 @@ class ProviderPicker extends StatelessWidget {
63 itemCount: items == null ? 0 : items.length,
64 itemBuilder: (context, index) {
65 final item = items[index];
66 + final image = images[index];
67 final isItemSelected = index == selectedAtIndex;
68
69 final color = isItemSelected
@@ -81,16 +84,26 @@ class ProviderPicker extends StatelessWidget {
84 child: Container(
85 height: 77,
86 padding: EdgeInsets.only(left: 24, right: 24),
84 - alignment: Alignment.center,
87 color: color,
86 - child: Text(
87 - item.toString(),
88 - style: TextStyle(
89 - fontSize: 18,
90 - fontWeight: FontWeight.bold,
91 - color: textColor,
92 - decoration: TextDecoration.none,
93 - ),
88 + child: Row(
89 + mainAxisSize: MainAxisSize.min,
90 + mainAxisAlignment: MainAxisAlignment.start,
91 + crossAxisAlignment: CrossAxisAlignment.center,
92 + children: <Widget>[
93 + image,
94 + Padding(
95 + padding: EdgeInsets.only(left: 12),
96 + child: Text(
97 + item.toString(),
98 + style: TextStyle(
99 + fontSize: 18,
100 + fontWeight: FontWeight.bold,
101 + color: textColor,
102 + decoration: TextDecoration.none,
103 + ),
104 + ),
105 + )
106 + ],
107 ),
108 ),
109 );
lib/src/screens/send/send_page.dart
+1 -1
@@ -357,7 +357,7 @@ class SendFormState extends State<SendForm> {
357 if (index == 0) {
358 return GestureDetector(
359 onTap: () => Navigator.of(context)
360 - .pushNamed(Routes.sendTemplate, arguments: sendStore),
360 + .pushNamed(Routes.sendTemplate),
361 child: Container(
362 padding: EdgeInsets.only(right: 10),
363 child: DottedBorder(
lib/src/stores/exchange/exchange_store.dart
+10 -4
@@ -33,6 +33,10 @@ abstract class ExchangeStoreBase with Store {
33 provider = initialProvider;
34 depositCurrency = initialDepositCurrency;
35 receiveCurrency = initialReceiveCurrency;
36 + depositAmount = '';
37 + receiveAmount = '';
38 + depositAddress = '';
39 + receiveAddress = '';
40 limitsState = LimitsInitialState();
41 tradeState = ExchangeTradeStateInitial();
42 _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12;
@@ -63,6 +67,12 @@ abstract class ExchangeStoreBase with Store {
67 @observable
68 String receiveAmount;
69
70 + @observable
71 + String depositAddress;
72 +
73 + @observable
74 + String receiveAddress;
75 +
76 @observable
77 bool isValid;
78
@@ -71,10 +81,6 @@ abstract class ExchangeStoreBase with Store {
81
82 Box<Trade> trades;
83
74 - String depositAddress;
75 -
76 - String receiveAddress;
77 -
84 WalletStore walletStore;
85
86 Limits limits;
lib/src/stores/exchange_template/exchange_template_store.dart new
+40
@@ -0,0 +1,40 @@
1 +import 'dart:async';
2 +import 'package:mobx/mobx.dart';
3 +import 'package:hive/hive.dart';
4 +import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
5 +
6 +part 'exchange_template_store.g.dart';
7 +
8 +class ExchangeTemplateStore = ExchangeTemplateBase with _$ExchangeTemplateStore;
9 +
10 +abstract class ExchangeTemplateBase with Store {
11 + ExchangeTemplateBase({this.templateSource}) {
12 + templates = ObservableList<ExchangeTemplate>();
13 + update();
14 + }
15 +
16 + @observable
17 + ObservableList<ExchangeTemplate> templates;
18 +
19 + Box<ExchangeTemplate> templateSource;
20 +
21 + @action
22 + void update() =>
23 + templates.replaceRange(0, templates.length, templateSource.values.toList());
24 +
25 + @action
26 + Future addTemplate({String amount, String depositCurrency, String receiveCurrency,
27 + String provider, String depositAddress, String receiveAddress}) async {
28 + final template = ExchangeTemplate(
29 + amount: amount,
30 + depositCurrency: depositCurrency,
31 + receiveCurrency: receiveCurrency,
32 + provider: provider,
33 + depositAddress: depositAddress,
34 + receiveAddress: receiveAddress);
35 + await templateSource.add(template);
36 + }
37 +
38 + @action
39 + Future remove({ExchangeTemplate template}) async => await template.delete();
40 +}
\ No newline at end of file