CAKE-20 | updated exchange and exchange template pages; created exchange_view_model and applied to exchange and exchange template pages; applied new design to menu widget; changed text validator and amount validator
Oleksandr Sobol committed
Jul 31, 2020 at 18:29 UTC
33209c3cd621cfbc4e7b28b00b193f3019f53d76
56 files changed
+857
-525
assets/images/2.0x/eye_menu.png
Binary files /dev/null and b/assets/images/2.0x/eye_menu.png differ
assets/images/2.0x/key_menu.png
Binary files /dev/null and b/assets/images/2.0x/key_menu.png differ
assets/images/2.0x/monero_menu.png
Binary files /dev/null and b/assets/images/2.0x/monero_menu.png differ
assets/images/2.0x/nodes_menu.png
Binary files /dev/null and b/assets/images/2.0x/nodes_menu.png differ
assets/images/2.0x/open_book_menu.png
Binary files /dev/null and b/assets/images/2.0x/open_book_menu.png differ
assets/images/2.0x/reconnect_menu.png
Binary files /dev/null and b/assets/images/2.0x/reconnect_menu.png differ
assets/images/2.0x/settings_menu.png
Binary files /dev/null and b/assets/images/2.0x/settings_menu.png differ
assets/images/2.0x/wallet_menu.png
Binary files /dev/null and b/assets/images/2.0x/wallet_menu.png differ
assets/images/3.0x/eye_menu.png
Binary files /dev/null and b/assets/images/3.0x/eye_menu.png differ
assets/images/3.0x/key_menu.png
Binary files /dev/null and b/assets/images/3.0x/key_menu.png differ
assets/images/3.0x/monero_menu.png
Binary files /dev/null and b/assets/images/3.0x/monero_menu.png differ
assets/images/3.0x/nodes_menu.png
Binary files /dev/null and b/assets/images/3.0x/nodes_menu.png differ
assets/images/3.0x/open_book_menu.png
Binary files /dev/null and b/assets/images/3.0x/open_book_menu.png differ
assets/images/3.0x/reconnect_menu.png
Binary files /dev/null and b/assets/images/3.0x/reconnect_menu.png differ
assets/images/3.0x/settings_menu.png
Binary files /dev/null and b/assets/images/3.0x/settings_menu.png differ
assets/images/3.0x/wallet_menu.png
Binary files /dev/null and b/assets/images/3.0x/wallet_menu.png differ
assets/images/eye_menu.png
Binary files /dev/null and b/assets/images/eye_menu.png differ
assets/images/key_menu.png
Binary files /dev/null and b/assets/images/key_menu.png differ
assets/images/monero_menu.png
Binary files /dev/null and b/assets/images/monero_menu.png differ
assets/images/nodes_menu.png
Binary files /dev/null and b/assets/images/nodes_menu.png differ
assets/images/open_book_menu.png
Binary files /dev/null and b/assets/images/open_book_menu.png differ
assets/images/reconnect_menu.png
Binary files /dev/null and b/assets/images/reconnect_menu.png differ
assets/images/settings_menu.png
Binary files /dev/null and b/assets/images/settings_menu.png differ
assets/images/wallet_menu.png
Binary files /dev/null and b/assets/images/wallet_menu.png differ
lib/core/amount_validator.dart
+2
-1
@@ -3,10 +3,11 @@ import 'package:cake_wallet/generated/i18n.dart';
3
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
4
5
class AmountValidator extends TextValidator {
6
- AmountValidator({WalletType type})
6
+ AmountValidator({WalletType type, bool isAutovalidate = false})
7
: super(
8
errorMessage: S.current.error_text_amount,
9
pattern: _pattern(type),
10
+ isAutovalidate: isAutovalidate,
11
minLength: 0,
12
maxLength: 0);
13
lib/core/validator.dart
+3
-1
@@ -16,18 +16,20 @@ class TextValidator extends Validator<String> {
16
this.maxLength,
17
this.pattern,
18
this.length,
19
+ this.isAutovalidate = false,
20
String errorMessage})
21
: super(errorMessage: errorMessage);
22
23
final int minLength;
24
final int maxLength;
25
final List<int> length;
26
+ final bool isAutovalidate;
27
String pattern;
28
29
@override
30
bool isValid(String value) {
31
if (value == null || value.isEmpty) {
30
- return false;
32
+ return isAutovalidate ? true : false;
33
}
34
35
return value.length > (minLength ?? 0) &&
lib/di.dart
+16
@@ -10,6 +10,8 @@ import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
10
import 'package:cake_wallet/src/screens/send/send_template_page.dart';
11
import 'package:cake_wallet/src/screens/settings/settings.dart';
12
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
13
+import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
14
+import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
15
import 'package:cake_wallet/store/contact_list_store.dart';
16
import 'package:cake_wallet/store/node_list_store.dart';
17
import 'package:cake_wallet/store/settings_store.dart';
@@ -42,6 +44,7 @@ import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
44
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
45
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
46
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
47
+import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
48
import 'package:flutter/foundation.dart';
49
import 'package:get_it/get_it.dart';
50
import 'package:hive/hive.dart';
@@ -299,4 +302,17 @@ Future setup(
302
303
getIt.registerFactory(
304
() => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>()));
305
+
306
+ getIt.registerFactory(() =>
307
+ ExchangeViewModel(
308
+ wallet: getIt.get<AppStore>().wallet,
309
+ exchangeTemplateStore: getIt.get<ExchangeTemplateStore>(),
310
+ trades: tradesSource
311
+ ));
312
+
313
+ getIt.registerFactory(() =>
314
+ ExchangePage(getIt.get<ExchangeViewModel>()));
315
+
316
+ getIt.registerFactory(() =>
317
+ ExchangeTemplatePage(getIt.get<ExchangeViewModel>()));
318
}
lib/generated/i18n.dart
+23
@@ -135,6 +135,7 @@ class S implements WidgetsLocalizations {
135
String get reconnect => "Reconnect";
136
String get reconnect_alert_text => "Are you sure to reconnect?";
137
String get reconnection => "Reconnection";
138
+ String get refund_address => "Refund address";
139
String get remove => "Remove";
140
String get remove_node => "Remove node";
141
String get remove_node_message => "Are you sure that you want to remove selected node?";
@@ -372,6 +373,8 @@ class $de extends S {
373
@override
374
String get trade_state_underpaid => "Unterbezahlt";
375
@override
376
+ String get refund_address => "Rückerstattungsadresse";
377
+ @override
378
String get welcome => "Willkommen zu";
379
@override
380
String get share_address => "Adresse teilen ";
@@ -998,6 +1001,8 @@ class $hi extends S {
1001
@override
1002
String get trade_state_underpaid => "के तहत भुगतान किया";
1003
@override
1004
+ String get refund_address => "वापसी का पता";
1005
+ @override
1006
String get welcome => "स्वागत हे सेवा मेरे";
1007
@override
1008
String get share_address => "पता साझा करें";
@@ -1624,6 +1629,8 @@ class $ru extends S {
1629
@override
1630
String get trade_state_underpaid => "Недоплаченная";
1631
@override
1632
+ String get refund_address => "Адрес возврата";
1633
+ @override
1634
String get welcome => "Приветствуем в";
1635
@override
1636
String get share_address => "Поделиться адресом";
@@ -2250,6 +2257,8 @@ class $ko extends S {
2257
@override
2258
String get trade_state_underpaid => "미지급";
2259
@override
2260
+ String get refund_address => "환불 주소";
2261
+ @override
2262
String get welcome => "환영 에";
2263
@override
2264
String get share_address => "주소 공유";
@@ -2876,6 +2885,8 @@ class $pt extends S {
2885
@override
2886
String get trade_state_underpaid => "Parcialmente paga";
2887
@override
2888
+ String get refund_address => "Endereço de reembolso";
2889
+ @override
2890
String get welcome => "Bem-vindo ao";
2891
@override
2892
String get share_address => "Compartilhar endereço";
@@ -3502,6 +3513,8 @@ class $uk extends S {
3513
@override
3514
String get trade_state_underpaid => "Недоплачена";
3515
@override
3516
+ String get refund_address => "Адреса повернення коштів";
3517
+ @override
3518
String get welcome => "Вітаємо в";
3519
@override
3520
String get share_address => "Поділитися адресою";
@@ -4128,6 +4141,8 @@ class $ja extends S {
4141
@override
4142
String get trade_state_underpaid => "支払不足";
4143
@override
4144
+ String get refund_address => "払い戻し住所";
4145
+ @override
4146
String get welcome => "ようこそ に";
4147
@override
4148
String get share_address => "住所を共有する";
@@ -4758,6 +4773,8 @@ class $pl extends S {
4773
@override
4774
String get trade_state_underpaid => "Niedopłacone";
4775
@override
4776
+ String get refund_address => "Adres zwrotu";
4777
+ @override
4778
String get welcome => "Witamy w";
4779
@override
4780
String get share_address => "Udostępnij adres";
@@ -5384,6 +5401,8 @@ class $es extends S {
5401
@override
5402
String get trade_state_underpaid => "Poco pagado";
5403
@override
5404
+ String get refund_address => "Dirección de reembolso";
5405
+ @override
5406
String get welcome => "Bienvenido";
5407
@override
5408
String get share_address => "Compartir dirección";
@@ -6010,6 +6029,8 @@ class $nl extends S {
6029
@override
6030
String get trade_state_underpaid => "Slecht betaald";
6031
@override
6032
+ String get refund_address => "Adres voor terugbetaling";
6033
+ @override
6034
String get welcome => "Welkom bij";
6035
@override
6036
String get share_address => "Deel adres";
@@ -6636,6 +6657,8 @@ class $zh extends S {
6657
@override
6658
String get trade_state_underpaid => "支付不足";
6659
@override
6660
+ String get refund_address => "退款地址";
6661
+ @override
6662
String get welcome => "歡迎來到";
6663
@override
6664
String get share_address => "分享地址";
lib/palette.dart
+8
-2
@@ -23,8 +23,8 @@ class PaletteDark {
23
static const Color lightDistantBlue = Color.fromRGBO(81, 96, 147, 1.0); // borderCardColor
24
static const Color gray = Color.fromRGBO(140, 153, 201, 1.0); // walletCardText
25
static const Color violetBlue = Color.fromRGBO(51, 63, 104, 1.0); // walletCardAddressField
26
- static const Color moderateBlue = Color.fromRGBO(63, 77, 122, 1.0); // walletCardSubAddressField
27
- static const Color darkNightBlue = Color.fromRGBO(33, 43, 73, 1.0); // historyPanel
26
+ //static const Color moderateBlue = Color.fromRGBO(63, 77, 122, 1.0); // walletCardSubAddressField
27
+ //static const Color darkNightBlue = Color.fromRGBO(33, 43, 73, 1.0); // historyPanel
28
static const Color pigeonBlue = Color.fromRGBO(91, 112, 146, 1.0); // historyPanelText
29
static const Color moderateNightBlue = Color.fromRGBO(39, 53, 96, 1.0); // historyPanelButton
30
static const Color headerNightBlue = Color.fromRGBO(41, 52, 84, 1.0); // menuHeader
@@ -46,6 +46,12 @@ class PaletteDark {
46
static const Color lightBlueGrey = Color.fromRGBO(125, 141, 183, 1.0);
47
static const Color lightVioletBlue = Color.fromRGBO(56, 71, 109, 1.0);
48
static const Color darkVioletBlue = Color.fromRGBO(49, 60, 96, 1.0);
49
+ static const Color wildVioletBlue = Color.fromRGBO(45, 60, 97, 1.0);
50
+ static const Color darkNightBlue = Color.fromRGBO(33, 45, 76, 1.0);
51
+ static const Color blueGrey = Color.fromRGBO(87, 98, 138, 1.0);
52
+ static const Color moderateBlue = Color.fromRGBO(60, 73, 118, 1.0);
53
+ static const Color deepPurpleBlue = Color.fromRGBO(19, 29, 56, 1.0);
54
+ static const Color lightOceanBlue = Color.fromRGBO(30, 42, 73, 1.0);
55
56
// FIXME: Rename.
57
static const Color eee = Color.fromRGBO(236, 239, 245, 1.0);
lib/router.dart
+4
-37
@@ -411,45 +411,12 @@ class Router {
411
walletRestorationFromSeedVM: walletRestorationFromSeedVM));
412
413
case Routes.exchange:
414
- return MaterialPageRoute<void>(
415
- builder: (_) => MultiProvider(providers: [
416
- Provider(create: (_) {
417
- final xmrtoprovider = XMRTOExchangeProvider();
418
-
419
- return ExchangeStore(
420
- initialProvider: xmrtoprovider,
421
- initialDepositCurrency: CryptoCurrency.xmr,
422
- initialReceiveCurrency: CryptoCurrency.btc,
423
- trades: trades,
424
- providerList: [
425
- xmrtoprovider,
426
- ChangeNowExchangeProvider(),
427
- MorphTokenExchangeProvider(trades: trades)
428
- ],
429
- walletStore: walletStore);
430
- }),
431
- ], child: ExchangePage()));
414
+ return CupertinoPageRoute<void>(
415
+ builder: (_) => getIt.get<ExchangePage>());
416
417
case Routes.exchangeTemplate:
434
- return MaterialPageRoute<void>(
435
- builder: (_) => Provider(
436
- create: (_) {
437
- final xmrtoprovider = XMRTOExchangeProvider();
438
-
439
- return ExchangeStore(
440
- initialProvider: xmrtoprovider,
441
- initialDepositCurrency: CryptoCurrency.xmr,
442
- initialReceiveCurrency: CryptoCurrency.btc,
443
- trades: trades,
444
- providerList: [
445
- xmrtoprovider,
446
- ChangeNowExchangeProvider(),
447
- MorphTokenExchangeProvider(trades: trades)
448
- ],
449
- walletStore: walletStore);
450
- },
451
- child: ExchangeTemplatePage(),
452
- ));
418
+ return CupertinoPageRoute<void>(
419
+ builder: (_) => getIt.get<ExchangeTemplatePage>());
420
421
case Routes.settings:
422
return MaterialPageRoute<void>(
lib/src/screens/base_page.dart
+8
@@ -19,10 +19,14 @@ abstract class BasePage extends StatelessWidget {
19
20
bool get resizeToAvoidBottomPadding => true;
21
22
+ Widget get endDrawer => null;
23
+
24
AppBarStyle get appBarStyle => AppBarStyle.regular;
25
26
Widget Function(BuildContext, Widget) get rootWrapper => null;
27
28
+ final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
29
+
30
final _backArrowImage = Image.asset('assets/images/back_arrow.png',
31
color: Colors.white);
32
final _backArrowImageDarkTheme =
@@ -32,6 +36,8 @@ abstract class BasePage extends StatelessWidget {
36
final _closeButtonImageDarkTheme =
37
Image.asset('assets/images/close_button_dark_theme.png');
38
39
+ void onOpenEndDrawer() => _scaffoldKey.currentState.openEndDrawer();
40
+
41
void onClose(BuildContext context) => Navigator.of(context).pop();
42
43
Widget leading(BuildContext context) {
@@ -123,9 +129,11 @@ abstract class BasePage extends StatelessWidget {
129
final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme;
130
131
final root = Scaffold(
132
+ key: _scaffoldKey,
133
backgroundColor:
134
_isDarkTheme ? backgroundDarkColor : backgroundLightColor,
135
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
136
+ endDrawer: endDrawer,
137
appBar: appBar(context),
138
body: body(context), //SafeArea(child: ),
139
floatingActionButton: floatingActionButton(context));
lib/src/screens/dashboard/dashboard_page.dart
+7
-8
@@ -23,6 +23,12 @@ class DashboardPage extends BasePage {
23
@override
24
Color get backgroundDarkColor => PaletteDark.backgroundColor;
25
26
+ @override
27
+ Widget get endDrawer => MenuWidget(
28
+ name: walletViewModel.name,
29
+ subname: walletViewModel.subname,
30
+ type: walletViewModel.type);
31
+
32
@override
33
Widget middle(BuildContext context) {
34
return SyncIndicator(dashboardViewModel: walletViewModel);
@@ -40,14 +46,7 @@ class DashboardPage extends BasePage {
46
highlightColor: Colors.transparent,
47
splashColor: Colors.transparent,
48
padding: EdgeInsets.all(0),
43
- onPressed: () async {
44
- await showDialog<void>(
45
- builder: (_) => MenuWidget(
46
- name: walletViewModel.name,
47
- subname: walletViewModel.subname,
48
- type: walletViewModel.type),
49
- context: context);
50
- },
49
+ onPressed: () => onOpenEndDrawer(),
50
child: menuButton
51
)
52
);
lib/src/screens/dashboard/wallet_menu.dart
+7
-7
@@ -20,13 +20,13 @@ class WalletMenu {
20
];
21
22
final List<Image> images = [
23
- Image.asset('assets/images/reconnect.png'),
24
- Image.asset('assets/images/wallet.png'),
25
- Image.asset('assets/images/nodes.png'),
26
- Image.asset('assets/images/eye.png'),
27
- Image.asset('assets/images/key.png'),
28
- Image.asset('assets/images/open_book.png'),
29
- Image.asset('assets/images/settings.png'),
23
+ Image.asset('assets/images/reconnect_menu.png', height: 16, width: 16),
24
+ Image.asset('assets/images/wallet_menu.png', height: 16, width: 16),
25
+ Image.asset('assets/images/nodes_menu.png', height: 16, width: 16),
26
+ Image.asset('assets/images/eye_menu.png', height: 16, width: 16),
27
+ Image.asset('assets/images/key_menu.png', height: 16, width: 16),
28
+ Image.asset('assets/images/open_book_menu.png', height: 16, width: 16),
29
+ Image.asset('assets/images/settings_menu.png', height: 16, width: 16),
30
];
31
32
final BuildContext context;
lib/src/screens/dashboard/widgets/menu_widget.dart
+123
-172
@@ -1,4 +1,5 @@
1
import 'dart:ui';
2
+import 'package:cake_wallet/palette.dart';
3
import 'package:flutter/material.dart';
4
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
5
import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
@@ -15,7 +16,7 @@ class MenuWidget extends StatefulWidget {
16
}
17
18
class MenuWidgetState extends State<MenuWidget> {
18
- final moneroIcon = Image.asset('assets/images/monero.png');
19
+ final moneroIcon = Image.asset('assets/images/monero_menu.png');
20
final bitcoinIcon = Image.asset('assets/images/bitcoin.png');
21
final largeScreen = 731;
22
@@ -36,10 +37,10 @@ class MenuWidgetState extends State<MenuWidget> {
37
screenHeight = 0;
38
opacity = 0;
39
39
- headerHeight = 120;
40
+ headerHeight = 125;
41
tileHeight = 75;
42
fromTopEdge = 50;
42
- fromBottomEdge = 30;
43
+ fromBottomEdge = 21;//30;
44
45
super.initState();
46
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
@@ -66,189 +67,139 @@ class MenuWidgetState extends State<MenuWidget> {
67
@override
68
Widget build(BuildContext context) {
69
final walletMenu = WalletMenu(context);
69
-// final walletStore = Provider.of<WalletStore>(context);
70
final itemCount = walletMenu.items.length;
71
72
- return Row(
73
- mainAxisSize: MainAxisSize.max,
74
- crossAxisAlignment: CrossAxisAlignment.center,
75
- children: <Widget>[
76
- Padding(
72
+ return SafeArea(
73
+ child: Row(
74
+ mainAxisSize: MainAxisSize.max,
75
+ crossAxisAlignment: CrossAxisAlignment.center,
76
+ children: <Widget>[
77
+ Padding(
78
padding: EdgeInsets.only(left: 24),
79
child: Container(
80
height: 60,
81
width: 4,
82
decoration: BoxDecoration(
82
- borderRadius: BorderRadius.all(Radius.circular(2)),
83
- color: Theme.of(context).hintColor),
83
+ borderRadius: BorderRadius.all(Radius.circular(2)),
84
+ color: Theme.of(context).hintColor),
85
)),
85
- SizedBox(width: 12),
86
- Expanded(
87
- child: GestureDetector(
88
- onTap: () => null,
89
- child: Container(
90
- width: menuWidth,
91
- height: double.infinity,
92
- decoration: BoxDecoration(
86
+ SizedBox(width: 12),
87
+ Expanded(
88
+ child: ClipRRect(
89
borderRadius: BorderRadius.only(
94
- topLeft: Radius.circular(24),
95
- bottomLeft: Radius.circular(24)),
96
- color: Theme.of(context).primaryTextTheme.display1.color),
97
- child: ClipRRect(
98
- borderRadius: BorderRadius.only(
90
topLeft: Radius.circular(24),
91
bottomLeft: Radius.circular(24)),
101
- child: ListView.separated(
102
- itemBuilder: (_, index) {
103
- if (index == 0) {
104
- return Container(
105
- height: headerHeight,
106
- padding: EdgeInsets.only(
107
- left: 24,
108
- top: fromTopEdge,
109
- right: 24,
110
- bottom: fromBottomEdge),
111
- decoration: BoxDecoration(
112
- borderRadius:
113
- BorderRadius.only(topLeft: Radius.circular(24)),
114
- color: Theme.of(context)
115
- .primaryTextTheme
116
- .display2
117
- .color),
118
- child: Row(
119
- mainAxisAlignment: MainAxisAlignment.start,
120
- children: <Widget>[
121
- _iconFor(type: widget.type),
122
- SizedBox(width: 16),
123
- Expanded(
92
+ child: Container(
93
+ width: menuWidth,
94
+ height: double.infinity,
95
+ color: PaletteDark.deepPurpleBlue,
96
+ child: SingleChildScrollView(
97
+ child: Column(
98
+ children: <Widget>[
99
+ Container(
100
+ height: headerHeight,
101
+ padding: EdgeInsets.only(
102
+ left: 24,
103
+ top: fromTopEdge,
104
+ right: 24,
105
+ bottom: fromBottomEdge),
106
+ child: Row(
107
+ mainAxisAlignment: MainAxisAlignment.start,
108
+ children: <Widget>[
109
+ _iconFor(type: widget.type),
110
+ SizedBox(width: 12),
111
+ Expanded(
112
+ child: Container(
113
+ height: 42,
114
+ child: Column(
115
+ crossAxisAlignment: CrossAxisAlignment.start,
116
+ mainAxisAlignment: widget.subname != null
117
+ ? MainAxisAlignment.spaceBetween
118
+ : MainAxisAlignment.center,
119
+ children: <Widget>[
120
+ Text(
121
+ widget.name,
122
+ style: TextStyle(
123
+ color: Colors.white,
124
+ fontSize: 16,
125
+ fontWeight: FontWeight.bold),
126
+ ),
127
+ if (widget.subname != null)
128
+ Text(
129
+ widget.subname,
130
+ style: TextStyle(
131
+ color: PaletteDark.darkCyanBlue,
132
+ fontWeight: FontWeight.w500,
133
+ fontSize: 12),
134
+ )
135
+ ],
136
+ ),
137
+ ))
138
+ ],
139
+ ),
140
+ ),
141
+ Container(
142
+ height: 1,
143
+ color: PaletteDark.lightOceanBlue,
144
+ ),
145
+ ListView.separated(
146
+ shrinkWrap: true,
147
+ physics: NeverScrollableScrollPhysics(),
148
+ itemBuilder: (_, index) {
149
+
150
+ final item = walletMenu.items[index];
151
+ final image = walletMenu.images[index] ?? Offstage();
152
+ final isLastTile = index == itemCount - 1;
153
+
154
+ return GestureDetector(
155
+ onTap: () {
156
+ Navigator.of(context).pop();
157
+ walletMenu.action(index);
158
+ },
159
child: Container(
125
- height: 40,
126
- child: Column(
127
- crossAxisAlignment: CrossAxisAlignment.start,
128
- mainAxisAlignment: widget.subname != null
129
- ? MainAxisAlignment.spaceBetween
130
- : MainAxisAlignment.center,
131
- children: <Widget>[
132
- Text(
133
- widget.name,
134
- style: TextStyle(
135
- color: Theme.of(context)
136
- .primaryTextTheme
137
- .title
138
- .color,
139
- decoration: TextDecoration.none,
140
- fontFamily: 'Avenir Next',
141
- fontSize: 20,
142
- fontWeight: FontWeight.bold),
160
+ height: isLastTile
161
+ ? headerHeight
162
+ : tileHeight,
163
+ padding: isLastTile
164
+ ? EdgeInsets.only(
165
+ left: 24,
166
+ right: 24,
167
+ top: fromBottomEdge,
168
+ bottom: fromTopEdge)
169
+ : EdgeInsets.only(left: 24, right: 24),
170
+ alignment: isLastTile ? Alignment.topLeft : null,
171
+ child: Row(
172
+ mainAxisAlignment: MainAxisAlignment.start,
173
+ crossAxisAlignment: CrossAxisAlignment.center,
174
+ children: <Widget>[
175
+ image,
176
+ SizedBox(width: 16),
177
+ Expanded(
178
+ child: Text(
179
+ item,
180
+ style: TextStyle(
181
+ color: Colors.white,
182
+ fontSize: 16,
183
+ fontWeight: FontWeight.bold),
184
+ ))
185
+ ],
186
),
144
- if (widget.subname != null)
145
- Text(
146
- widget.subname,
147
- style: TextStyle(
148
- color: Theme.of(context)
149
- .primaryTextTheme
150
- .caption
151
- .color,
152
- decoration: TextDecoration.none,
153
- fontFamily: 'Avenir Next',
154
- fontSize: 12),
155
- )
156
- ],
157
- ),
158
- ))
159
- ],
160
- ),
161
- );
162
- }
163
-
164
- index -= 1;
165
- final item = walletMenu.items[index];
166
- final image = walletMenu.images[index] ?? Offstage();
167
-
168
- return GestureDetector(
169
- onTap: () {
170
- Navigator.of(context).pop();
171
- walletMenu.action(index);
172
- },
173
- child: index == itemCount - 1
174
- ? Container(
175
- height: headerHeight,
176
- padding: EdgeInsets.only(
177
- left: 24,
178
- right: 24,
179
- top: fromBottomEdge,
180
- bottom: fromTopEdge),
181
- alignment: Alignment.topLeft,
182
- decoration: BoxDecoration(
183
- borderRadius: BorderRadius.only(
184
- bottomLeft: Radius.circular(24)),
185
- color: Theme.of(context)
186
- .primaryTextTheme
187
- .display1
188
- .color,
189
- ),
190
- child: Row(
191
- mainAxisAlignment: MainAxisAlignment.start,
192
- crossAxisAlignment: CrossAxisAlignment.center,
193
- children: <Widget>[
194
- image,
195
- SizedBox(width: 16),
196
- Expanded(
197
- child: Text(
198
- item,
199
- style: TextStyle(
200
- decoration: TextDecoration.none,
201
- color: Theme.of(context)
202
- .primaryTextTheme
203
- .title
204
- .color,
205
- fontFamily: 'Avenir Next',
206
- fontSize: 20,
207
- fontWeight: FontWeight.bold),
208
- ))
209
- ],
210
- ),
211
- )
212
- : Container(
213
- height: tileHeight,
214
- padding: EdgeInsets.only(left: 24, right: 24),
215
- color: Theme.of(context)
216
- .primaryTextTheme
217
- .display1
218
- .color,
219
- child: Row(
220
- mainAxisAlignment: MainAxisAlignment.start,
221
- crossAxisAlignment: CrossAxisAlignment.center,
222
- children: <Widget>[
223
- image,
224
- SizedBox(width: 16),
225
- Expanded(
226
- child: Text(
227
- item,
228
- style: TextStyle(
229
- decoration: TextDecoration.none,
230
- color: Theme.of(context)
231
- .primaryTextTheme
232
- .title
233
- .color,
234
- fontFamily: 'Avenir Next',
235
- fontSize: 20,
236
- fontWeight: FontWeight.bold),
237
- ))
238
- ],
239
- ),
187
+ )
188
+ );
189
+ },
190
+ separatorBuilder: (_, index) => Container(
191
+ height: 1,
192
+ color: PaletteDark.lightOceanBlue,
193
),
241
- );
242
- },
243
- separatorBuilder: (_, index) => Container(
244
- height: 1,
245
- color: Theme.of(context).dividerColor,
246
- ),
247
- itemCount: itemCount + 1),
248
- ),
249
- ),
250
- ))
251
- ],
194
+ itemCount: itemCount)
195
+ ],
196
+ ),
197
+ ),
198
+ ),
199
+ )
200
+ )
201
+ ],
202
+ )
203
);
204
}
205
lib/src/screens/exchange/exchange_page.dart
+15
-41
@@ -1,66 +1,40 @@
1
import 'dart:ui';
2
import 'package:flutter/cupertino.dart';
3
import 'package:flutter/material.dart';
4
-import 'package:provider/provider.dart';
4
import 'package:cake_wallet/palette.dart';
5
import 'package:cake_wallet/generated/i18n.dart';
7
-import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
8
-import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
6
import 'package:cake_wallet/src/screens/base_page.dart';
10
-import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
7
import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
8
import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
9
import 'package:cake_wallet/src/widgets/trail_button.dart';
10
+import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
11
12
class ExchangePage extends BasePage {
16
- @override
17
- String get title => S.current.exchange;
13
+ ExchangePage(this.exchangeViewModel);
14
19
- @override
20
- Color get backgroundLightColor => Palette.darkLavender;
15
+ final ExchangeViewModel exchangeViewModel;
16
17
@override
23
- Color get backgroundDarkColor => PaletteDark.moderateBlue;
18
+ String get title => S.current.exchange;
19
20
@override
26
- Widget middle(BuildContext context) {
27
- final exchangeStore = Provider.of<ExchangeStore>(context);
28
-
29
- return PresentProviderPicker(exchangeStore: exchangeStore);
30
- }
21
+ Color get backgroundLightColor => PaletteDark.wildVioletBlue;
22
23
@override
33
- Widget trailing(BuildContext context) {
34
- final exchangeStore = Provider.of<ExchangeStore>(context);
35
-
36
- return TrailButton(
37
- caption: S.of(context).reset,
38
- onPressed: () => exchangeStore.reset()
39
- );
40
- }
24
+ Color get backgroundDarkColor => PaletteDark.wildVioletBlue;
25
26
@override
43
- Widget body(BuildContext context) => ExchangeForm();
44
-}
27
+ Widget middle(BuildContext context) =>
28
+ PresentProviderPicker(exchangeViewModel: exchangeViewModel);
29
46
-class ExchangeForm extends StatefulWidget {
30
@override
48
- State<StatefulWidget> createState() => ExchangeFormState();
49
-}
50
-
51
-class ExchangeFormState extends State<ExchangeForm> {
31
+ Widget trailing(BuildContext context) =>
32
+ TrailButton(
33
+ caption: S.of(context).reset,
34
+ onPressed: () => exchangeViewModel.reset()
35
+ );
36
37
@override
54
- Widget build(BuildContext context) {
55
- final exchangeStore = Provider.of<ExchangeStore>(context);
56
- final walletStore = Provider.of<WalletStore>(context);
57
- final exchangeTemplateStore = Provider.of<ExchangeTemplateStore>(context);
58
-
59
- return BaseExchangeWidget(
60
- exchangeStore: exchangeStore,
61
- walletStore: walletStore,
62
- exchangeTemplateStore: exchangeTemplateStore,
63
- isTemplate: false
64
- );
65
- }
38
+ Widget body(BuildContext context) =>
39
+ BaseExchangeWidget(exchangeViewModel: exchangeViewModel);
40
}
lib/src/screens/exchange/exchange_template_page.dart
+10
-33
@@ -1,55 +1,32 @@
1
import 'dart:ui';
2
import 'package:flutter/cupertino.dart';
3
import 'package:flutter/material.dart';
4
-import 'package:provider/provider.dart';
4
import 'package:cake_wallet/palette.dart';
6
-import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
7
-import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
5
import 'package:cake_wallet/src/screens/base_page.dart';
6
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';
7
import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
8
import 'package:cake_wallet/generated/i18n.dart';
9
+import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
10
11
class ExchangeTemplatePage extends BasePage {
15
- @override
16
- String get title => S.current.exchange_new_template;
12
+ ExchangeTemplatePage(this.exchangeViewModel);
13
18
- @override
19
- Color get backgroundLightColor => Palette.darkLavender;
14
+ final ExchangeViewModel exchangeViewModel;
15
16
@override
22
- Color get backgroundDarkColor => PaletteDark.moderateBlue;
17
+ String get title => S.current.exchange_new_template;
18
19
@override
25
- Widget trailing(BuildContext context) {
26
- final exchangeStore = Provider.of<ExchangeStore>(context);
27
-
28
- return PresentProviderPicker(exchangeStore: exchangeStore);
29
- }
20
+ Color get backgroundLightColor => PaletteDark.wildVioletBlue;
21
22
@override
32
- Widget body(BuildContext context) => ExchangeTemplateForm();
33
-}
23
+ Color get backgroundDarkColor => PaletteDark.wildVioletBlue;
24
35
-class ExchangeTemplateForm extends StatefulWidget{
25
@override
37
- ExchangeTemplateFormState createState() => ExchangeTemplateFormState();
38
-}
39
-
40
-class ExchangeTemplateFormState extends State<ExchangeTemplateForm> {
26
+ Widget trailing(BuildContext context) =>
27
+ PresentProviderPicker(exchangeViewModel: exchangeViewModel);
28
29
@override
43
- Widget build(BuildContext context) {
44
- final exchangeStore = Provider.of<ExchangeStore>(context);
45
- final walletStore = Provider.of<WalletStore>(context);
46
- final exchangeTemplateStore = Provider.of<ExchangeTemplateStore>(context);
47
-
48
- return BaseExchangeWidget(
49
- exchangeStore: exchangeStore,
50
- walletStore: walletStore,
51
- exchangeTemplateStore: exchangeTemplateStore,
52
- isTemplate: true
53
- );
54
- }
30
+ Widget body(BuildContext context) =>
31
+ BaseExchangeWidget(exchangeViewModel: exchangeViewModel, isTemplate: true);
32
}
\ No newline at end of file
lib/src/screens/exchange/widgets/base_exchange_widget.dart
+197
-185
@@ -1,5 +1,7 @@
1
import 'dart:ui';
2
+import 'package:cake_wallet/palette.dart';
3
import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
4
+import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
5
import 'package:cake_wallet/src/widgets/template_tile.dart';
6
import 'package:dotted_border/dotted_border.dart';
7
import 'package:flutter/cupertino.dart';
@@ -12,49 +14,37 @@ import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
14
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
15
import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
16
import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
15
-import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
16
-import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
17
import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
18
import 'package:cake_wallet/src/widgets/primary_button.dart';
19
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
20
import 'package:cake_wallet/src/widgets/top_panel.dart';
21
-import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
21
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
22
+import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
23
24
class BaseExchangeWidget extends StatefulWidget {
25
BaseExchangeWidget({
26
- @ required this.exchangeStore,
27
- @ required this.walletStore,
28
- @ required this.exchangeTemplateStore,
29
- @ required this.isTemplate,
26
+ @ required this.exchangeViewModel,
27
+ this.isTemplate = false,
28
});
29
32
- final ExchangeStore exchangeStore;
33
- final WalletStore walletStore;
34
- final ExchangeTemplateStore exchangeTemplateStore;
30
+ final ExchangeViewModel exchangeViewModel;
31
final bool isTemplate;
32
33
@override
34
BaseExchangeWidgetState createState() =>
35
BaseExchangeWidgetState(
40
- exchangeStore: exchangeStore,
41
- walletStore: walletStore,
42
- exchangeTemplateStore: exchangeTemplateStore,
36
+ exchangeViewModel: exchangeViewModel,
37
isTemplate: isTemplate
38
);
39
}
40
41
class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
42
BaseExchangeWidgetState({
49
- @ required this.exchangeStore,
50
- @ required this.walletStore,
51
- @ required this.exchangeTemplateStore,
43
+ @ required this.exchangeViewModel,
44
@ required this.isTemplate,
45
});
46
55
- final ExchangeStore exchangeStore;
56
- final WalletStore walletStore;
57
- final ExchangeTemplateStore exchangeTemplateStore;
47
+ final ExchangeViewModel exchangeViewModel;
48
final bool isTemplate;
49
50
final depositKey = GlobalKey<ExchangeCardState>();
@@ -64,31 +54,31 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
54
55
@override
56
Widget build(BuildContext context) {
67
- final Image arrowBottomPurple = Image.asset(
57
+ final arrowBottomPurple = Image.asset(
58
'assets/images/arrow_bottom_purple_icon.png',
69
- color: Theme.of(context).primaryTextTheme.title.color,
59
+ color: Colors.white,
60
height: 8,
61
);
72
- final Image arrowBottomCakeGreen = Image.asset(
62
+ final arrowBottomCakeGreen = Image.asset(
63
'assets/images/arrow_bottom_cake_green.png',
74
- color: Theme.of(context).primaryTextTheme.title.color,
64
+ color: Colors.white,
65
height: 8,
66
);
67
68
final depositWalletName =
79
- exchangeStore.depositCurrency == CryptoCurrency.xmr
80
- ? walletStore.name
69
+ exchangeViewModel.depositCurrency == CryptoCurrency.xmr
70
+ ? exchangeViewModel.wallet.name
71
: null;
72
final receiveWalletName =
83
- exchangeStore.receiveCurrency == CryptoCurrency.xmr
84
- ? walletStore.name
73
+ exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
74
+ ? exchangeViewModel.wallet.name
75
: null;
76
77
WidgetsBinding.instance.addPostFrameCallback(
88
- (_) => _setReactions(context, exchangeStore, walletStore));
78
+ (_) => _setReactions(context, exchangeViewModel));
79
80
return Container(
91
- color: Theme.of(context).backgroundColor,
81
+ color: PaletteDark.backgroundColor,
82
child: Form(
83
key: _formKey,
84
child: ScrollableWithBottomSection(
@@ -96,73 +86,60 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
86
content: Column(
87
children: <Widget>[
88
TopPanel(
99
- color: Theme.of(context).accentTextTheme.title.backgroundColor,
100
- edgeInsets: EdgeInsets.only(bottom: 24),
89
+ color: PaletteDark.darkNightBlue,
90
+ edgeInsets: EdgeInsets.only(bottom: 32),
91
widget: Column(
92
children: <Widget>[
93
TopPanel(
104
- color: Theme.of(context).accentTextTheme.title.color,
94
+ edgeInsets: EdgeInsets.fromLTRB(24, 29, 24, 32),
95
+ color: PaletteDark.wildVioletBlue,
96
widget: Observer(
97
builder: (_) => ExchangeCard(
98
key: depositKey,
99
title: S.of(context).you_will_send,
109
- initialCurrency: exchangeStore.depositCurrency,
100
+ initialCurrency: exchangeViewModel.depositCurrency,
101
initialWalletName: depositWalletName,
102
initialAddress:
112
- exchangeStore.depositCurrency == walletStore.type
113
- ? walletStore.address
114
- : exchangeStore.depositAddress,
103
+ exchangeViewModel.depositCurrency == exchangeViewModel.wallet.currency
104
+ ? exchangeViewModel.wallet.address
105
+ : exchangeViewModel.depositAddress,
106
initialIsAmountEditable: true,
116
- initialIsAddressEditable: exchangeStore.isDepositAddressEnabled,
107
+ initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
108
isAmountEstimated: false,
109
currencies: CryptoCurrency.all,
110
onCurrencySelected: (currency) =>
120
- exchangeStore.changeDepositCurrency(currency: currency),
111
+ exchangeViewModel.changeDepositCurrency(currency: currency),
112
imageArrow: arrowBottomPurple,
122
- currencyButtonColor: Theme.of(context).accentTextTheme.title.color,
123
- addressButtonsColor: Theme.of(context).accentTextTheme.title.backgroundColor,
124
- currencyValueValidator: (value) {
125
- exchangeStore.validateCryptoCurrency(value);
126
- return exchangeStore.errorMessage;
127
- },
128
- addressTextFieldValidator: (value) {
129
- exchangeStore.validateAddress(value,
130
- cryptoCurrency: exchangeStore.depositCurrency);
131
- return exchangeStore.errorMessage;
132
- },
113
+ currencyButtonColor: PaletteDark.wildVioletBlue,
114
+ addressButtonsColor: PaletteDark.moderateBlue,
115
+ currencyValueValidator: exchangeViewModel.amountValidator,
116
+ addressTextFieldValidator: exchangeViewModel.addressValidator,
117
),
118
)
119
),
120
Padding(
137
- padding: EdgeInsets.only(top: 32, left: 24, right: 24),
121
+ padding: EdgeInsets.only(top: 29, left: 24, right: 24),
122
child: Observer(
123
builder: (_) => ExchangeCard(
124
key: receiveKey,
125
title: S.of(context).you_will_get,
142
- initialCurrency: exchangeStore.receiveCurrency,
126
+ initialCurrency: exchangeViewModel.receiveCurrency,
127
initialWalletName: receiveWalletName,
128
initialAddress:
145
- exchangeStore.receiveCurrency == walletStore.type
146
- ? walletStore.address
147
- : exchangeStore.receiveAddress,
129
+ exchangeViewModel.receiveCurrency == exchangeViewModel.wallet.currency
130
+ ? exchangeViewModel.wallet.address
131
+ : exchangeViewModel.receiveAddress,
132
initialIsAmountEditable: false,
149
- initialIsAddressEditable: exchangeStore.isReceiveAddressEnabled,
133
+ initialIsAddressEditable: exchangeViewModel.isReceiveAddressEnabled,
134
isAmountEstimated: true,
135
currencies: CryptoCurrency.all,
152
- onCurrencySelected: (currency) => exchangeStore
136
+ onCurrencySelected: (currency) => exchangeViewModel
137
.changeReceiveCurrency(currency: currency),
138
imageArrow: arrowBottomCakeGreen,
155
- currencyButtonColor: Theme.of(context).accentTextTheme.title.backgroundColor,
156
- addressButtonsColor: Theme.of(context).accentTextTheme.title.color,
157
- currencyValueValidator: (value) {
158
- exchangeStore.validateCryptoCurrency(value);
159
- return exchangeStore.errorMessage;
160
- },
161
- addressTextFieldValidator: (value) {
162
- exchangeStore.validateAddress(value,
163
- cryptoCurrency: exchangeStore.receiveCurrency);
164
- return exchangeStore.errorMessage;
165
- },
139
+ currencyButtonColor: PaletteDark.darkNightBlue,
140
+ addressButtonsColor: PaletteDark.moderateBlue,
141
+ currencyValueValidator: exchangeViewModel.amountValidator,
142
+ addressTextFieldValidator: exchangeViewModel.addressValidator,
143
)),
144
)
145
],
@@ -172,7 +149,7 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
149
? Offstage()
150
: Padding(
151
padding: EdgeInsets.only(
175
- top: 32,
152
+ top: 30,
153
left: 24,
154
bottom: 24
155
),
@@ -184,7 +161,7 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
161
style: TextStyle(
162
fontSize: 18,
163
fontWeight: FontWeight.w600,
187
- color: Theme.of(context).primaryTextTheme.caption.color
164
+ color: PaletteDark.darkCyanBlue
165
),
166
)
167
],
@@ -196,66 +173,90 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
173
height: 40,
174
width: double.infinity,
175
padding: EdgeInsets.only(left: 24),
199
- child: Observer(
200
- builder: (_) {
201
- final itemCount = exchangeTemplateStore.templates.length + 1;
202
-
203
- return ListView.builder(
204
- scrollDirection: Axis.horizontal,
205
- itemCount: itemCount,
206
- itemBuilder: (context, index) {
207
-
208
- if (index == 0) {
209
- return GestureDetector(
210
- onTap: () => Navigator.of(context)
211
- .pushNamed(Routes.exchangeTemplate),
212
- child: Container(
213
- padding: EdgeInsets.only(right: 10),
214
- child: DottedBorder(
215
- borderType: BorderType.RRect,
216
- dashPattern: [8, 4],
217
- color: Theme.of(context).accentTextTheme.title.backgroundColor,
218
- strokeWidth: 2,
219
- radius: Radius.circular(20),
220
- child: Container(
221
- height: 40,
222
- width: 75,
223
- padding: EdgeInsets.only(left: 10, right: 10),
224
- alignment: Alignment.center,
225
- decoration: BoxDecoration(
226
- borderRadius: BorderRadius.all(Radius.circular(20)),
227
- color: Colors.transparent,
228
- ),
229
- child: Text(
230
- S.of(context).send_new,
231
- style: TextStyle(
232
- fontSize: 14,
233
- fontWeight: FontWeight.w600,
234
- color: Theme.of(context).primaryTextTheme.caption.color
235
- ),
236
- ),
237
- )
176
+ child: SingleChildScrollView(
177
+ scrollDirection: Axis.horizontal,
178
+ child: Row(
179
+ children: <Widget>[
180
+ GestureDetector(
181
+ onTap: () => Navigator.of(context)
182
+ .pushNamed(Routes.exchangeTemplate),
183
+ child: Container(
184
+ padding: EdgeInsets.only(left: 1, right: 10),
185
+ child: DottedBorder(
186
+ borderType: BorderType.RRect,
187
+ dashPattern: [6, 4],
188
+ color: PaletteDark.darkCyanBlue,
189
+ strokeWidth: 2,
190
+ radius: Radius.circular(20),
191
+ child: Container(
192
+ height: 34,
193
+ width: 75,
194
+ padding: EdgeInsets.only(left: 10, right: 10),
195
+ alignment: Alignment.center,
196
+ decoration: BoxDecoration(
197
+ borderRadius: BorderRadius.all(Radius.circular(20)),
198
+ color: Colors.transparent,
199
+ ),
200
+ child: Text(
201
+ S.of(context).send_new,
202
+ style: TextStyle(
203
+ fontSize: 14,
204
+ fontWeight: FontWeight.w600,
205
+ color: PaletteDark.darkCyanBlue
206
),
207
),
240
- );
241
- }
242
-
243
- index -= 1;
244
-
245
- final template = exchangeTemplateStore.templates[index];
246
-
247
- return TemplateTile(
248
- amount: template.amount,
249
- from: template.depositCurrency,
250
- to: template.receiveCurrency,
251
- onTap: () {
252
- applyTemplate(exchangeStore, template);
208
+ )
209
+ ),
210
+ ),
211
+ ),
212
+ Observer(
213
+ builder: (_) {
214
+ final templates = exchangeViewModel.templates;
215
+ final itemCount = exchangeViewModel.templates.length;
216
+
217
+ return ListView.builder(
218
+ scrollDirection: Axis.horizontal,
219
+ shrinkWrap: true,
220
+ physics: NeverScrollableScrollPhysics(),
221
+ itemCount: itemCount,
222
+ itemBuilder: (context, index) {
223
+ final template = templates[index];
224
+
225
+ return TemplateTile(
226
+ key: UniqueKey(),
227
+ amount: template.amount,
228
+ from: template.depositCurrency,
229
+ to: template.receiveCurrency,
230
+ onTap: () {
231
+ applyTemplate(exchangeViewModel, template);
232
+ },
233
+ onRemove: () {
234
+ showDialog<void>(
235
+ context: context,
236
+ builder: (dialogContext) {
237
+ return AlertWithTwoActions(
238
+ alertTitle: S.of(context).template,
239
+ alertContent: S.of(context).confirm_delete_template,
240
+ leftButtonText: S.of(context).delete,
241
+ rightButtonText: S.of(context).cancel,
242
+ actionLeftButton: () {
243
+ Navigator.of(dialogContext).pop();
244
+ exchangeViewModel.exchangeTemplateStore.remove(template: template);
245
+ exchangeViewModel.exchangeTemplateStore.update();
246
+ },
247
+ actionRightButton: () => Navigator.of(dialogContext).pop()
248
+ );
249
+ }
250
+ );
251
+ },
252
+ );
253
}
254
);
255
}
256
- );
257
- }
258
- ),
256
+ ),
257
+ ],
258
+ )
259
+ )
260
)
261
],
262
),
@@ -265,14 +266,15 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
266
padding: EdgeInsets.only(bottom: 15),
267
child: Observer(builder: (_) {
268
final description =
268
- exchangeStore.provider is XMRTOExchangeProvider
269
+ exchangeViewModel.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(
275
- color: Theme.of(context).primaryTextTheme.caption.color,
276
+ color: PaletteDark.darkCyanBlue,
277
+ fontWeight: FontWeight.w500,
278
fontSize: 12
279
),
280
),
@@ -283,15 +285,15 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
285
? PrimaryButton(
286
onPressed: () {
287
if (_formKey.currentState.validate()) {
286
- exchangeTemplateStore.addTemplate(
287
- amount: exchangeStore.depositAmount,
288
- depositCurrency: exchangeStore.depositCurrency.toString(),
289
- receiveCurrency: exchangeStore.receiveCurrency.toString(),
290
- provider: exchangeStore.provider.toString(),
291
- depositAddress: exchangeStore.depositAddress,
292
- receiveAddress: exchangeStore.receiveAddress
288
+ exchangeViewModel.exchangeTemplateStore.addTemplate(
289
+ amount: exchangeViewModel.depositAmount,
290
+ depositCurrency: exchangeViewModel.depositCurrency.toString(),
291
+ receiveCurrency: exchangeViewModel.receiveCurrency.toString(),
292
+ provider: exchangeViewModel.provider.toString(),
293
+ depositAddress: exchangeViewModel.depositAddress,
294
+ receiveAddress: exchangeViewModel.receiveAddress
295
);
294
- exchangeTemplateStore.update();
296
+ exchangeViewModel.exchangeTemplateStore.update();
297
Navigator.of(context).pop();
298
}
299
},
@@ -304,41 +306,47 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
306
text: S.of(context).exchange,
307
onPressed: () {
308
if (_formKey.currentState.validate()) {
307
- exchangeStore.createTrade();
309
+ exchangeViewModel.createTrade();
310
}
311
},
312
color: Colors.blue,
313
textColor: Colors.white,
312
- isLoading: exchangeStore.tradeState is TradeIsCreating,
314
+ isLoading: exchangeViewModel.tradeState is TradeIsCreating,
315
)),
316
]),
317
)),
318
);
319
}
320
319
- void applyTemplate(ExchangeStore store, ExchangeTemplate template) {
320
- store.changeDepositCurrency(currency: CryptoCurrency.fromString(template.depositCurrency));
321
- store.changeReceiveCurrency(currency: CryptoCurrency.fromString(template.receiveCurrency));
321
+ void applyTemplate(ExchangeViewModel exchangeViewModel,
322
+ ExchangeTemplate template) {
323
+ exchangeViewModel.changeDepositCurrency(
324
+ currency: CryptoCurrency.fromString(template.depositCurrency));
325
+ exchangeViewModel.changeReceiveCurrency(
326
+ currency: CryptoCurrency.fromString(template.receiveCurrency));
327
328
switch (template.provider) {
329
case 'XMR.TO':
325
- store.changeProvider(provider: store.providerList[0]);
330
+ exchangeViewModel.changeProvider(
331
+ provider: exchangeViewModel.providerList[0]);
332
break;
333
case 'ChangeNOW':
328
- store.changeProvider(provider: store.providerList[1]);
334
+ exchangeViewModel.changeProvider(
335
+ provider: exchangeViewModel.providerList[1]);
336
break;
337
case 'MorphToken':
331
- store.changeProvider(provider: store.providerList[2]);
338
+ exchangeViewModel.changeProvider(
339
+ provider: exchangeViewModel.providerList[2]);
340
break;
341
}
342
335
- store.changeDepositAmount(amount: template.amount);
336
- store.depositAddress = template.depositAddress;
337
- store.receiveAddress = template.receiveAddress;
343
+ exchangeViewModel.changeDepositAmount(amount: template.amount);
344
+ exchangeViewModel.depositAddress = template.depositAddress;
345
+ exchangeViewModel.receiveAddress = template.receiveAddress;
346
}
347
348
void _setReactions(
341
- BuildContext context, ExchangeStore store, WalletStore walletStore) {
349
+ BuildContext context, ExchangeViewModel exchangeViewModel) {
350
if (_isReactionsSet) {
351
return;
352
}
@@ -347,7 +355,7 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
355
final depositAmountController = depositKey.currentState.amountController;
356
final receiveAddressController = receiveKey.currentState.addressController;
357
final receiveAmountController = receiveKey.currentState.amountController;
350
- final limitsState = store.limitsState;
358
+ final limitsState = exchangeViewModel.limitsState;
359
360
if (limitsState is LimitsLoadedSuccessfully) {
361
final min = limitsState.limits.min != null
@@ -360,63 +368,62 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
368
key.currentState.changeLimits(min: min, max: max);
369
}
370
363
- _onCurrencyChange(store.receiveCurrency, walletStore, receiveKey);
364
- _onCurrencyChange(store.depositCurrency, walletStore, depositKey);
371
+ _onCurrencyChange(exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
372
+ _onCurrencyChange(exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
373
374
reaction(
367
- (_) => walletStore.name,
375
+ (_) => exchangeViewModel.wallet.name,
376
(String _) => _onWalletNameChange(
369
- walletStore, store.receiveCurrency, receiveKey));
377
+ exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
378
379
reaction(
372
- (_) => walletStore.name,
380
+ (_) => exchangeViewModel.wallet.name,
381
(String _) => _onWalletNameChange(
374
- walletStore, store.depositCurrency, depositKey));
382
+ exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
383
384
reaction(
377
- (_) => store.receiveCurrency,
385
+ (_) => exchangeViewModel.receiveCurrency,
386
(CryptoCurrency currency) =>
379
- _onCurrencyChange(currency, walletStore, receiveKey));
387
+ _onCurrencyChange(currency, exchangeViewModel, receiveKey));
388
389
reaction(
382
- (_) => store.depositCurrency,
390
+ (_) => exchangeViewModel.depositCurrency,
391
(CryptoCurrency currency) =>
384
- _onCurrencyChange(currency, walletStore, depositKey));
392
+ _onCurrencyChange(currency, exchangeViewModel, depositKey));
393
386
- reaction((_) => store.depositAmount, (String amount) {
394
+ reaction((_) => exchangeViewModel.depositAmount, (String amount) {
395
if (depositKey.currentState.amountController.text != amount) {
396
depositKey.currentState.amountController.text = amount;
397
}
398
});
399
392
- reaction((_) => store.depositAddress, (String address) {
400
+ reaction((_) => exchangeViewModel.depositAddress, (String address) {
401
if (depositKey.currentState.addressController.text != address) {
402
depositKey.currentState.addressController.text = address;
403
}
404
});
405
398
- reaction((_) => store.isDepositAddressEnabled, (bool isEnabled) {
406
+ reaction((_) => exchangeViewModel.isDepositAddressEnabled, (bool isEnabled) {
407
depositKey.currentState.isAddressEditable(isEditable: isEnabled);
408
});
409
402
- reaction((_) => store.receiveAmount, (String amount) {
403
- if (receiveKey.currentState.amountController.text !=
404
- store.receiveAmount) {
410
+ reaction((_) => exchangeViewModel.receiveAmount, (String amount) {
411
+ if (receiveKey.currentState.amountController.text != amount) {
412
receiveKey.currentState.amountController.text = amount;
413
}
414
});
415
409
- reaction((_) => store.receiveAddress, (String address) {
416
+ reaction((_) => exchangeViewModel.receiveAddress, (String address) {
417
if (receiveKey.currentState.addressController.text != address) {
418
receiveKey.currentState.addressController.text = address;
419
}
420
});
421
415
- reaction((_) => store.isReceiveAddressEnabled, (bool isEnabled) {
422
+ reaction((_) => exchangeViewModel.isReceiveAddressEnabled, (bool isEnabled) {
423
receiveKey.currentState.isAddressEditable(isEditable: isEnabled);
424
});
425
419
- reaction((_) => store.tradeState, (ExchangeTradeState state) {
426
+ reaction((_) => exchangeViewModel.tradeState, (ExchangeTradeState state) {
427
if (state is TradeIsCreatedFailure) {
428
WidgetsBinding.instance.addPostFrameCallback((_) {
429
showDialog<void>(
@@ -437,7 +444,7 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
444
}
445
});
446
440
- reaction((_) => store.limitsState, (LimitsState state) {
447
+ reaction((_) => exchangeViewModel.limitsState, (LimitsState state) {
448
String min;
449
String max;
450
@@ -461,29 +468,29 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
468
});
469
470
depositAddressController.addListener(
464
- () => store.depositAddress = depositAddressController.text);
471
+ () => exchangeViewModel.depositAddress = depositAddressController.text);
472
473
depositAmountController.addListener(() {
467
- if (depositAmountController.text != store.depositAmount) {
468
- store.changeDepositAmount(amount: depositAmountController.text);
474
+ if (depositAmountController.text != exchangeViewModel.depositAmount) {
475
+ exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
476
}
477
});
478
479
receiveAddressController.addListener(
473
- () => store.receiveAddress = receiveAddressController.text);
480
+ () => exchangeViewModel.receiveAddress = receiveAddressController.text);
481
482
receiveAmountController.addListener(() {
476
- if (receiveAmountController.text != store.receiveAmount) {
477
- store.changeReceiveAmount(amount: receiveAmountController.text);
483
+ if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
484
+ exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text);
485
}
486
});
487
481
- reaction((_) => walletStore.address, (String address) {
482
- if (store.depositCurrency == CryptoCurrency.xmr) {
488
+ reaction((_) => exchangeViewModel.wallet.address, (String address) {
489
+ if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
490
depositKey.currentState.changeAddress(address: address);
491
}
492
486
- if (store.receiveCurrency == CryptoCurrency.xmr) {
493
+ if (exchangeViewModel.receiveCurrency == CryptoCurrency.xmr) {
494
receiveKey.currentState.changeAddress(address: address);
495
}
496
});
@@ -491,28 +498,33 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
498
_isReactionsSet = true;
499
}
500
494
- void _onCurrencyChange(CryptoCurrency currency, WalletStore walletStore,
501
+ void _onCurrencyChange(CryptoCurrency currency,
502
+ ExchangeViewModel exchangeViewModel,
503
GlobalKey<ExchangeCardState> key) {
496
- final isCurrentTypeWallet = currency == walletStore.type;
504
+ final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
505
506
key.currentState.changeSelectedCurrency(currency);
507
key.currentState
500
- .changeWalletName(isCurrentTypeWallet ? walletStore.name : null);
508
+ .changeWalletName(isCurrentTypeWallet
509
+ ? exchangeViewModel.wallet.name : null);
510
511
key.currentState
503
- .changeAddress(address: isCurrentTypeWallet ? walletStore.address : '');
512
+ .changeAddress(address: isCurrentTypeWallet
513
+ ? exchangeViewModel.wallet.address : '');
514
515
key.currentState.changeAmount(amount: '');
516
}
517
508
- void _onWalletNameChange(WalletStore walletStore, CryptoCurrency currency,
518
+ void _onWalletNameChange(ExchangeViewModel exchangeViewModel,
519
+ CryptoCurrency currency,
520
GlobalKey<ExchangeCardState> key) {
510
- final isCurrentTypeWallet = currency == walletStore.type;
521
+ final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
522
523
if (isCurrentTypeWallet) {
513
- key.currentState.changeWalletName(walletStore.name);
514
- key.currentState.addressController.text = walletStore.address;
515
- } else if (key.currentState.addressController.text == walletStore.address) {
524
+ key.currentState.changeWalletName(exchangeViewModel.wallet.name);
525
+ key.currentState.addressController.text = exchangeViewModel.wallet.address;
526
+ } else if (key.currentState.addressController.text ==
527
+ exchangeViewModel.wallet.address) {
528
key.currentState.changeWalletName(null);
529
key.currentState.addressController.text = null;
530
}
lib/src/screens/exchange/widgets/exchange_card.dart
+90
-20
@@ -5,6 +5,7 @@ import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
5
import 'package:cake_wallet/src/widgets/address_text_field.dart';
6
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
7
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
8
+import 'package:cake_wallet/palette.dart';
9
10
class ExchangeCard extends StatefulWidget {
11
ExchangeCard(
@@ -57,6 +58,10 @@ class ExchangeCardState extends State<ExchangeCard> {
58
bool _isAddressEditable;
59
bool _isAmountEstimated;
60
61
+ final copyImage = Image.asset('assets/images/copy_content.png',
62
+ height: 16, width: 16,
63
+ color: Colors.white);
64
+
65
@override
66
void initState() {
67
_title = widget.title;
@@ -114,6 +119,7 @@ class ExchangeCardState extends State<ExchangeCard> {
119
width: double.infinity,
120
color: Colors.transparent,
121
child: Column(
122
+ crossAxisAlignment: CrossAxisAlignment.start,
123
children: <Widget>[
124
Row(
125
mainAxisAlignment: MainAxisAlignment.start,
@@ -123,13 +129,13 @@ class ExchangeCardState extends State<ExchangeCard> {
129
style: TextStyle(
130
fontSize: 18,
131
fontWeight: FontWeight.w600,
126
- color: Theme.of(context).primaryTextTheme.caption.color
132
+ color: PaletteDark.lightBlueGrey
133
),
134
)
135
],
136
),
137
Padding(
132
- padding: EdgeInsets.only(top: 10),
138
+ padding: EdgeInsets.only(top: 20),
139
child: Stack(
140
children: <Widget>[
141
BaseTextFormField(
@@ -143,7 +149,20 @@ class ExchangeCardState extends State<ExchangeCard> {
149
RegExp('[\\-|\\ |\\,]'))
150
],
151
hintText: '0.0000',
146
- validator: widget.currencyValueValidator
152
+ borderColor: PaletteDark.blueGrey,
153
+ textStyle: TextStyle(
154
+ fontSize: 16,
155
+ fontWeight: FontWeight.w600,
156
+ color: Colors.white
157
+ ),
158
+ placeholderTextStyle: TextStyle(
159
+ fontSize: 16,
160
+ fontWeight: FontWeight.w600,
161
+ color: PaletteDark.lightBlueGrey
162
+ ),
163
+ validator: _isAmountEditable
164
+ ? widget.currencyValueValidator
165
+ : null
166
),
167
Positioned(
168
top: 8,
@@ -163,7 +182,7 @@ class ExchangeCardState extends State<ExchangeCard> {
182
style: TextStyle(
183
fontWeight: FontWeight.w600,
184
fontSize: 16,
166
- color: Theme.of(context).primaryTextTheme.title.color)),
185
+ color: Colors.white)),
186
Padding(
187
padding: EdgeInsets.only(left: 5),
188
child: widget.imageArrow,
@@ -181,45 +200,96 @@ class ExchangeCardState extends State<ExchangeCard> {
200
mainAxisAlignment: MainAxisAlignment.start,
201
children: <Widget>[
202
_min != null
184
- ? Text(
203
+ ? Text(
204
S.of(context).min_value(
205
_min, _selectedCurrency.toString()),
206
style: TextStyle(
207
fontSize: 10,
208
height: 1.2,
190
- color: Theme.of(context).primaryTextTheme.caption.color),
209
+ color: PaletteDark.lightBlueGrey),
210
)
192
- : Offstage(),
211
+ : Offstage(),
212
_min != null ? SizedBox(width: 10) : Offstage(),
213
_max != null
195
- ? Text(
214
+ ? Text(
215
S.of(context).max_value(
216
_max, _selectedCurrency.toString()),
217
style: TextStyle(
218
fontSize: 10,
219
height: 1.2,
201
- color: Theme.of(context).primaryTextTheme.caption.color))
202
- : Offstage(),
220
+ color: PaletteDark.lightBlueGrey))
221
+ : Offstage(),
222
]),
223
),
224
Padding(
206
- padding: EdgeInsets.only(top: 10),
207
- child: AddressTextField(
225
+ padding: EdgeInsets.only(top: 20),
226
+ child: Text(
227
+ _isAddressEditable
228
+ ? S.of(context).widgets_address
229
+ : S.of(context).refund_address,
230
+ style: TextStyle(
231
+ fontSize: 14,
232
+ fontWeight: FontWeight.w500,
233
+ color: PaletteDark.lightBlueGrey
234
+ ),
235
+ )
236
+ ),
237
+ _isAddressEditable
238
+ ? AddressTextField(
239
controller: addressController,
209
- isActive: _isAddressEditable,
210
- options: _isAddressEditable
211
- ? _walletName != null
212
- ? []
213
- : [
240
+ options: [
241
+ AddressTextFieldOption.paste,
242
AddressTextFieldOption.qrCode,
243
AddressTextFieldOption.addressBook,
216
- ]
217
- : [],
244
+ ],
245
+ placeholder: '',
246
isBorderExist: false,
247
+ textStyle: TextStyle(
248
+ fontSize: 16,
249
+ fontWeight: FontWeight.w600,
250
+ color: Colors.white),
251
buttonColor: widget.addressButtonsColor,
252
validator: widget.addressTextFieldValidator,
221
- ),
253
)
254
+ : Padding(
255
+ padding: EdgeInsets.only(top: 10),
256
+ child: Builder(
257
+ builder: (context) => GestureDetector(
258
+ onTap: () {
259
+ Clipboard.setData(ClipboardData(
260
+ text: addressController.text));
261
+ Scaffold.of(context).showSnackBar(SnackBar(
262
+ content: Text(
263
+ S.of(context).copied_to_clipboard,
264
+ style: TextStyle(color: Colors.white),
265
+ ),
266
+ backgroundColor: Colors.green,
267
+ duration: Duration(milliseconds: 500),
268
+ ));
269
+ },
270
+ child: Row(
271
+ mainAxisSize: MainAxisSize.max,
272
+ children: <Widget>[
273
+ Expanded(
274
+ child: Text(
275
+ addressController.text,
276
+ maxLines: 1,
277
+ overflow: TextOverflow.ellipsis,
278
+ style: TextStyle(
279
+ fontSize: 16,
280
+ fontWeight: FontWeight.w600,
281
+ color: Colors.white),
282
+ ),
283
+ ),
284
+ Padding(
285
+ padding: EdgeInsets.only(left: 16),
286
+ child: copyImage,
287
+ )
288
+ ],
289
+ ),
290
+ )
291
+ ),
292
+ ),
293
]),
294
);
295
}
lib/src/screens/exchange/widgets/present_provider_picker.dart
+17
-16
@@ -1,21 +1,22 @@
1
import 'package:flutter/material.dart';
2
-import 'package:cake_wallet/src/stores/exchange/exchange_store.dart';
2
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
3
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
4
import 'package:flutter_mobx/flutter_mobx.dart';
5
import 'package:cake_wallet/generated/i18n.dart';
6
import 'package:cake_wallet/src/widgets/picker.dart';
7
+import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
8
+import 'package:cake_wallet/palette.dart';
9
10
class PresentProviderPicker extends StatelessWidget {
10
- PresentProviderPicker({@required this.exchangeStore});
11
+ PresentProviderPicker({@required this.exchangeViewModel});
12
12
- final ExchangeStore exchangeStore;
13
+ final ExchangeViewModel exchangeViewModel;
14
15
@override
16
Widget build(BuildContext context) {
16
- final Image arrowBottom =
17
+ final arrowBottom =
18
Image.asset('assets/images/arrow_bottom_purple_icon.png',
18
- color: Theme.of(context).primaryTextTheme.title.color,
19
+ color: Colors.white,
20
height: 6);
21
22
return FlatButton(
@@ -33,19 +34,19 @@ class PresentProviderPicker extends StatelessWidget {
34
Text(S.of(context).exchange,
35
style: TextStyle(
36
fontSize: 16.0,
36
- fontWeight: FontWeight.w400,
37
- color: Theme.of(context).primaryTextTheme.title.color)),
37
+ fontWeight: FontWeight.w600,
38
+ color: Colors.white)),
39
Observer(
39
- builder: (_) => Text('${exchangeStore.provider.title}',
40
+ builder: (_) => Text('${exchangeViewModel.provider.title}',
41
style: TextStyle(
42
fontSize: 10.0,
42
- fontWeight: FontWeight.w400,
43
- color: Theme.of(context).primaryTextTheme.caption.color)))
43
+ fontWeight: FontWeight.w500,
44
+ color: PaletteDark.lightBlueGrey)))
45
],
46
),
47
SizedBox(width: 5),
48
Padding(
48
- padding: EdgeInsets.only(top: 8),
49
+ padding: EdgeInsets.only(top: 12),
50
child: arrowBottom,
51
)
52
],
@@ -54,11 +55,11 @@ class PresentProviderPicker extends StatelessWidget {
55
}
56
57
void _presentProviderPicker(BuildContext context) {
57
- final items = exchangeStore.providersForCurrentPair();
58
- final selectedItem = items.indexOf(exchangeStore.provider);
59
- final images = List<Image>();
58
+ final items = exchangeViewModel.providersForCurrentPair();
59
+ final selectedItem = items.indexOf(exchangeViewModel.provider);
60
+ final images = <Image>[];
61
61
- for (ExchangeProvider provider in items) {
62
+ for (var provider in items) {
63
switch (provider.description) {
64
case ExchangeProviderDescription.xmrto:
65
images.add(Image.asset('assets/images/xmr_btc.png'));
@@ -79,7 +80,7 @@ class PresentProviderPicker extends StatelessWidget {
80
selectedAtIndex: selectedItem,
81
title: S.of(context).change_exchange_provider,
82
onItemSelected: (ExchangeProvider provider) =>
82
- exchangeStore.changeProvider(provider: provider)),
83
+ exchangeViewModel.changeProvider(provider: provider)),
84
context: context);
85
}
86
}
\ No newline at end of file
lib/src/screens/receive/receive_page.dart
+1
-1
@@ -125,7 +125,7 @@ class ReceivePage extends BasePage {
125
.headline5
126
.color
127
.withOpacity(0.4),
128
- validator: AmountValidator(),
128
+ validator: AmountValidator(isAutovalidate: true),
129
autovalidate: true,
130
placeholderTextStyle: TextStyle(
131
color: Theme.of(context)
lib/src/widgets/base_text_form_field.dart
+4
@@ -72,6 +72,10 @@ class BaseTextFormField extends StatelessWidget {
72
borderSide: BorderSide(
73
color: borderColor ?? Theme.of(context).dividerColor,
74
width: 1.0)),
75
+ disabledBorder: UnderlineInputBorder(
76
+ borderSide: BorderSide(
77
+ color: borderColor ?? Theme.of(context).dividerColor,
78
+ width: 1.0)),
79
enabledBorder: UnderlineInputBorder(
80
borderSide: BorderSide(
81
color: borderColor ?? Theme.of(context).dividerColor,
lib/src/widgets/nav_bar.dart
+2
@@ -71,6 +71,8 @@ class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
71
EdgeInsetsDirectional.only(bottom: _paddingBottom, top: paddingTop),
72
child: CupertinoNavigationBar(
73
leading: leading,
74
+ automaticallyImplyLeading: false,
75
+ automaticallyImplyMiddle: false,
76
middle: middle,
77
trailing: trailing,
78
backgroundColor: backgroundColor,
lib/src/widgets/trail_button.dart
+2
-1
@@ -1,4 +1,5 @@
1
import 'package:flutter/material.dart';
2
+import 'package:cake_wallet/palette.dart';
3
4
class TrailButton extends StatelessWidget {
5
TrailButton({
@@ -21,7 +22,7 @@ class TrailButton extends StatelessWidget {
22
child: Text(
23
caption,
24
style: TextStyle(
24
- color: Theme.of(context).primaryTextTheme.caption.color,
25
+ color: PaletteDark.lightBlueGrey,
26
fontWeight: FontWeight.w500,
27
fontSize: 14),
28
),
lib/view_model/exchange/exchange_view_model.dart
new
+306
@@ -0,0 +1,306 @@
1
+import 'package:cake_wallet/core/address_validator.dart';
2
+import 'package:cake_wallet/core/amount_validator.dart';
3
+import 'package:cake_wallet/core/template_validator.dart';
4
+import 'package:cake_wallet/core/validator.dart';
5
+import 'package:cake_wallet/core/wallet_base.dart';
6
+import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
7
+import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
8
+import 'package:cake_wallet/src/domain/exchange/limits.dart';
9
+import 'package:cake_wallet/src/domain/exchange/trade.dart';
10
+import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
11
+import 'package:intl/intl.dart';
12
+import 'package:mobx/mobx.dart';
13
+import 'package:cake_wallet/generated/i18n.dart';
14
+import 'package:hive/hive.dart';
15
+import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
16
+import 'package:cake_wallet/src/domain/exchange/changenow/changenow_exchange_provider.dart';
17
+import 'package:cake_wallet/src/domain/exchange/changenow/changenow_request.dart';
18
+import 'package:cake_wallet/src/domain/exchange/trade_request.dart';
19
+import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
20
+import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_trade_request.dart';
21
+import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart';
22
+import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_request.dart';
23
+import 'package:cake_wallet/store/templates/exchange_template_store.dart';
24
+import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
25
+
26
+part 'exchange_view_model.g.dart';
27
+
28
+class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel;
29
+
30
+abstract class ExchangeViewModelBase with Store {
31
+ ExchangeViewModelBase({this.wallet, this.trades, this.exchangeTemplateStore}) {
32
+ providerList = [
33
+ XMRTOExchangeProvider(),
34
+ ChangeNowExchangeProvider(),
35
+ MorphTokenExchangeProvider(trades: trades)
36
+ ];
37
+
38
+ provider = providerList[ 0 ];
39
+
40
+ depositCurrency = CryptoCurrency.xmr;
41
+ receiveCurrency = CryptoCurrency.btc;
42
+ isDepositAddressEnabled = !(depositCurrency == wallet.currency);
43
+ isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
44
+ depositAmount = '';
45
+ receiveAmount = '';
46
+ depositAddress = '';
47
+ receiveAddress = '';
48
+ limitsState = LimitsInitialState();
49
+ tradeState = ExchangeTradeStateInitial();
50
+ _cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12;
51
+ loadLimits();
52
+ }
53
+
54
+ final WalletBase wallet;
55
+ final Box<Trade> trades;
56
+ final ExchangeTemplateStore exchangeTemplateStore;
57
+
58
+ @observable
59
+ ExchangeProvider provider;
60
+
61
+ @observable
62
+ List<ExchangeProvider> providerList;
63
+
64
+ @observable
65
+ CryptoCurrency depositCurrency;
66
+
67
+ @observable
68
+ CryptoCurrency receiveCurrency;
69
+
70
+ @observable
71
+ LimitsState limitsState;
72
+
73
+ @observable
74
+ ExchangeTradeState tradeState;
75
+
76
+ @observable
77
+ String depositAmount;
78
+
79
+ @observable
80
+ String receiveAmount;
81
+
82
+ @observable
83
+ String depositAddress;
84
+
85
+ @observable
86
+ String receiveAddress;
87
+
88
+ @observable
89
+ bool isDepositAddressEnabled;
90
+
91
+ @observable
92
+ bool isReceiveAddressEnabled;
93
+
94
+ @observable
95
+ bool isValid;
96
+
97
+ @observable
98
+ String errorMessage;
99
+
100
+ Limits limits;
101
+
102
+ NumberFormat _cryptoNumberFormat;
103
+
104
+ Validator get amountValidator => AmountValidator(type: wallet.type);
105
+
106
+ Validator get addressValidator => AddressValidator(type: wallet.currency);
107
+
108
+ Validator get templateValidator => TemplateValidator();
109
+
110
+ @computed
111
+ ObservableList<ExchangeTemplate> get templates =>
112
+ exchangeTemplateStore.templates;
113
+
114
+ @action
115
+ void changeProvider({ExchangeProvider provider}) {
116
+ this.provider = provider;
117
+ depositAmount = '';
118
+ receiveAmount = '';
119
+ loadLimits();
120
+ }
121
+
122
+ @action
123
+ void changeDepositCurrency({CryptoCurrency currency}) {
124
+ depositCurrency = currency;
125
+ _onPairChange();
126
+ isDepositAddressEnabled = !(depositCurrency == wallet.currency);
127
+ isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
128
+ }
129
+
130
+ @action
131
+ void changeReceiveCurrency({CryptoCurrency currency}) {
132
+ receiveCurrency = currency;
133
+ _onPairChange();
134
+ isDepositAddressEnabled = !(depositCurrency == wallet.currency);
135
+ isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
136
+ }
137
+
138
+ @action
139
+ void changeReceiveAmount({String amount}) {
140
+ receiveAmount = amount;
141
+
142
+ if (amount == null || amount.isEmpty) {
143
+ depositAmount = '';
144
+ receiveAmount = '';
145
+ return;
146
+ }
147
+
148
+ final _amount = double.parse(amount) ?? 0;
149
+
150
+ provider
151
+ .calculateAmount(
152
+ from: depositCurrency, to: receiveCurrency, amount: _amount)
153
+ .then((amount) => _cryptoNumberFormat.format(amount).toString().replaceAll(RegExp("\\,"), ""))
154
+ .then((amount) => depositAmount = amount);
155
+ }
156
+
157
+ @action
158
+ void changeDepositAmount({String amount}) {
159
+ depositAmount = amount;
160
+
161
+ if (amount == null || amount.isEmpty) {
162
+ depositAmount = '';
163
+ receiveAmount = '';
164
+ return;
165
+ }
166
+
167
+ final _amount = double.parse(amount);
168
+ provider
169
+ .calculateAmount(
170
+ from: depositCurrency, to: receiveCurrency, amount: _amount)
171
+ .then((amount) => _cryptoNumberFormat.format(amount).toString().replaceAll(RegExp("\\,"), ""))
172
+ .then((amount) => receiveAmount = amount);
173
+ }
174
+
175
+ @action
176
+ Future loadLimits() async {
177
+ limitsState = LimitsIsLoading();
178
+
179
+ try {
180
+ limits = await provider.fetchLimits(
181
+ from: depositCurrency, to: receiveCurrency);
182
+ limitsState = LimitsLoadedSuccessfully(limits: limits);
183
+ } catch (e) {
184
+ limitsState = LimitsLoadedFailure(error: e.toString());
185
+ }
186
+ }
187
+
188
+ @action
189
+ Future createTrade() async {
190
+ TradeRequest request;
191
+ String amount;
192
+ CryptoCurrency currency;
193
+
194
+ if (provider is XMRTOExchangeProvider) {
195
+ request = XMRTOTradeRequest(
196
+ from: depositCurrency,
197
+ to: receiveCurrency,
198
+ amount: depositAmount,
199
+ address: receiveAddress,
200
+ refundAddress: depositAddress);
201
+ amount = depositAmount;
202
+ currency = depositCurrency;
203
+ }
204
+
205
+ if (provider is ChangeNowExchangeProvider) {
206
+ request = ChangeNowRequest(
207
+ from: depositCurrency,
208
+ to: receiveCurrency,
209
+ amount: depositAmount,
210
+ refundAddress: depositAddress,
211
+ address: receiveAddress);
212
+ amount = depositAmount;
213
+ currency = depositCurrency;
214
+ }
215
+
216
+ if (provider is MorphTokenExchangeProvider) {
217
+ request = MorphTokenRequest(
218
+ from: depositCurrency,
219
+ to: receiveCurrency,
220
+ amount: depositAmount,
221
+ refundAddress: depositAddress,
222
+ address: receiveAddress);
223
+ amount = depositAmount;
224
+ currency = depositCurrency;
225
+ }
226
+
227
+ if (limitsState is LimitsLoadedSuccessfully && amount != null) {
228
+ if (double.parse(amount) < limits.min) {
229
+ tradeState = TradeIsCreatedFailure(error: S.current.error_text_minimal_limit('${provider.description}',
230
+ '${limits.min}', currency.toString()));
231
+ } else if (limits.max != null && double.parse(amount) > limits.max) {
232
+ tradeState = TradeIsCreatedFailure(error: S.current.error_text_maximum_limit('${provider.description}',
233
+ '${limits.max}', currency.toString()));
234
+ } else {
235
+ try {
236
+ tradeState = TradeIsCreating();
237
+ final trade = await provider.createTrade(request: request);
238
+ trade.walletId = wallet.id;
239
+ await trades.add(trade);
240
+ tradeState = TradeIsCreatedSuccessfully(trade: trade);
241
+ } catch (e) {
242
+ tradeState = TradeIsCreatedFailure(error: e.toString());
243
+ }
244
+ }
245
+ } else {
246
+ tradeState = TradeIsCreatedFailure(error: S.current.error_text_limits_loading_failed("${provider.description}"));
247
+ }
248
+
249
+ }
250
+
251
+ @action
252
+ void reset() {
253
+ depositAmount = '';
254
+ receiveAmount = '';
255
+ depositCurrency = CryptoCurrency.xmr;
256
+ receiveCurrency = CryptoCurrency.btc;
257
+ depositAddress = depositCurrency == wallet.currency ? wallet.address : '';
258
+ receiveAddress = receiveCurrency == wallet.currency ? wallet.address : '';
259
+ isDepositAddressEnabled = !(depositCurrency == wallet.currency);
260
+ isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
261
+ _onPairChange();
262
+ }
263
+
264
+ List<ExchangeProvider> providersForCurrentPair() {
265
+ return _providersForPair(from: depositCurrency, to: receiveCurrency);
266
+ }
267
+
268
+ List<ExchangeProvider> _providersForPair(
269
+ {CryptoCurrency from, CryptoCurrency to}) {
270
+ final providers = providerList
271
+ .where((provider) => provider.pairList
272
+ .where((pair) =>
273
+ pair.from == depositCurrency && pair.to == receiveCurrency)
274
+ .isNotEmpty)
275
+ .toList();
276
+
277
+ return providers;
278
+ }
279
+
280
+ void _onPairChange() {
281
+ final isPairExist = provider.pairList
282
+ .where((pair) =>
283
+ pair.from == depositCurrency && pair.to == receiveCurrency)
284
+ .isNotEmpty;
285
+
286
+ if (!isPairExist) {
287
+ final provider =
288
+ _providerForPair(from: depositCurrency, to: receiveCurrency);
289
+
290
+ if (provider != null) {
291
+ changeProvider(provider: provider);
292
+ }
293
+ }
294
+
295
+ depositAmount = '';
296
+ receiveAmount = '';
297
+
298
+ loadLimits();
299
+ }
300
+
301
+ ExchangeProvider _providerForPair({CryptoCurrency from, CryptoCurrency to}) {
302
+ final providers = _providersForPair(from: from, to: to);
303
+ return providers.isNotEmpty ? providers[0] : null;
304
+ }
305
+
306
+}
\ No newline at end of file
res/values/strings_de.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "Austausch",
63
"clear" : "klar",
64
+ "refund_address" : "Rückerstattungsadresse",
65
"change_exchange_provider" : "Wechseln Sie den Exchange-Anbieter",
66
"you_will_send" : "Du wirst senden",
67
"you_will_get" : "Sie erhalten",
res/values/strings_en.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "Exchange",
63
"clear" : "Clear",
64
+ "refund_address" : "Refund address",
65
"change_exchange_provider" : "Change Exchange Provider",
66
"you_will_send" : "You will send",
67
"you_will_get" : "You will get",
res/values/strings_es.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "Intercambiar",
63
"clear" : "Claro",
64
+ "refund_address" : "Dirección de reembolso",
65
"change_exchange_provider" : "Cambiar proveedor de intercambio",
66
"you_will_send" : "Enviarás",
67
"you_will_get" : "Conseguirás",
res/values/strings_hi.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "अदला बदली",
63
"clear" : "स्पष्ट",
64
+ "refund_address" : "वापसी का पता",
65
"change_exchange_provider" : "एक्सचेंज प्रदाता बदलें",
66
"you_will_send" : "तुम भेजोगे",
67
"you_will_get" : "आपको मिल जायेगा",
res/values/strings_ja.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "交換する",
63
"clear" : "クリア",
64
+ "refund_address" : "払い戻し住所",
65
"change_exchange_provider" : "Exchangeプロバイダーの変更",
66
"you_will_send" : "送ります",
67
"you_will_get" : "あなたが取得します",
res/values/strings_ko.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "교환",
63
"clear" : "명확한",
64
+ "refund_address" : "환불 주소",
65
"change_exchange_provider" : "교환 공급자 변경",
66
"you_will_send" : "보내드립니다",
67
"you_will_get" : "당신은 얻을 것이다",
res/values/strings_nl.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "Uitwisseling",
63
"clear" : "Duidelijk",
64
+ "refund_address" : "Adres voor terugbetaling",
65
"change_exchange_provider" : "Wijzig Exchange Provider",
66
"you_will_send" : "Je zal versturen",
67
"you_will_get" : "Je zult krijgen",
res/values/strings_pl.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "Wymieniać się",
63
"clear" : "Wyczyść",
64
+ "refund_address" : "Adres zwrotu",
65
"change_exchange_provider" : "Zmień dostawcę programu Exchange",
66
"you_will_send" : "Wyślesz",
67
"you_will_get" : "Dostaniesz",
res/values/strings_pt.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "Trocar",
63
"clear" : "Limpar",
64
+ "refund_address" : "Endereço de reembolso",
65
"change_exchange_provider" : "Alterar o provedor de troca",
66
"you_will_send" : "Você enviará",
67
"you_will_get" : "Você receberá",
res/values/strings_ru.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "Обмен",
63
"clear" : "Очистить",
64
+ "refund_address" : "Адрес возврата",
65
"change_exchange_provider" : "Изменить провайдера обмена",
66
"you_will_send" : "Вы отправите",
67
"you_will_get" : "Вы получите",
res/values/strings_uk.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "Обмін",
63
"clear" : "Очистити",
64
+ "refund_address" : "Адреса повернення коштів",
65
"change_exchange_provider" : "Змінити провайдера обміну",
66
"you_will_send" : "Ви відправите",
67
"you_will_get" : "Ви отримаєте",
res/values/strings_zh.arb
+1
@@ -61,6 +61,7 @@
61
62
"exchange" : "交换",
63
"clear" : "明确",
64
+ "refund_address" : "退款地址",
65
"change_exchange_provider" : "更改交易所提供商",
66
"you_will_send" : "您将发送",
67
"you_will_get" : "你会得到",