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