CAKE-356 | added "Manage Yats" item to the settings page; reworked primary icon button; added yat address to qr widget; created yat_alert.dart, yat_webview_page.dart, yat_store.dart, yat_view_model.dart, yat_exception.dart, yat_record.dart
OleksandrSobol committed
Aug 20, 2021 at 16:26 UTC
92aece5e31fb5c4ba284ec9ee4ba4ee7755a2d49
18 files changed
+400
-14
assets/images/yat_crypto.png
Binary files /dev/null and b/assets/images/yat_crypto.png differ
assets/images/yat_logo.png
Binary files /dev/null and b/assets/images/yat_logo.png differ
lib/di.dart
+18
-1
@@ -45,6 +45,7 @@ import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.da
45
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
46
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
47
import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart';
48
+import 'package:cake_wallet/src/screens/yat/yat_webview_page.dart';
49
import 'package:cake_wallet/store/dashboard/orders_store.dart';
50
import 'package:cake_wallet/store/node_list_store.dart';
51
import 'package:cake_wallet/store/secret_store.dart';
@@ -62,6 +63,7 @@ import 'package:cake_wallet/src/screens/send/send_page.dart';
63
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
64
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
65
import 'package:cake_wallet/store/wallet_list_store.dart';
66
+import 'package:cake_wallet/store/yat_store.dart';
67
import 'package:cake_wallet/view_model/backup_view_model.dart';
68
import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
69
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
@@ -97,6 +99,7 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
99
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
100
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
101
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
102
+import 'package:cake_wallet/view_model/yat_view_model.dart';
103
import 'package:flutter/widgets.dart';
104
import 'package:get_it/get_it.dart';
105
import 'package:hive/hive.dart';
@@ -192,6 +195,7 @@ Future setup(
195
SendTemplateStore(templateSource: _templates));
196
getIt.registerSingleton<ExchangeTemplateStore>(
197
ExchangeTemplateStore(templateSource: _exchangeTemplates));
198
+ getIt.registerSingleton<YatStore>(YatStore());
199
200
final secretStore =
201
await SecretStoreBase.load(getIt.get<FlutterSecureStorage>());
@@ -235,7 +239,10 @@ Future setup(
239
});
240
241
getIt.registerFactory<WalletAddressListViewModel>(
238
- () => WalletAddressListViewModel(appStore: getIt.get<AppStore>()));
242
+ () => WalletAddressListViewModel(
243
+ appStore: getIt.get<AppStore>(),
244
+ yatStore: getIt.get<YatStore>()
245
+ ));
246
247
getIt.registerFactory(() => BalanceViewModel(
248
appStore: getIt.get<AppStore>(),
@@ -629,5 +636,15 @@ Future setup(
636
param2: unspentCoinsListViewModel));
637
});
638
639
+ getIt.registerFactory(() => YatViewModel(
640
+ yatStore: getIt.get<YatStore>()
641
+ ));
642
+
643
+ getIt.registerFactoryParam<YatWebViewPage, YatMode, void>((YatMode mode, _) =>
644
+ YatWebViewPage(
645
+ mode: mode,
646
+ yatViewModel: getIt.get<YatViewModel>(),
647
+ ));
648
+
649
_isSetupFinished = true;
650
}
lib/palette.dart
+3
@@ -46,6 +46,9 @@ class Palette {
46
static const Color protectiveBlue = Color.fromRGBO(33, 148, 255, 1.0);
47
static const Color darkBlue = Color.fromRGBO(109, 128, 178, 1.0);
48
static const Color paleCornflowerBlue = Color.fromRGBO(185, 196, 237, 1.0);
49
+ static const Color manatee = Color.fromRGBO(153, 161, 176, 1.0);
50
+ static const Color stateGray = Color.fromRGBO(68, 74, 89, 1.0);
51
+ static const Color frostySky = Color.fromRGBO(0, 184, 250, 1.0);
52
}
53
54
class PaletteDark {
lib/router.dart
+6
@@ -13,6 +13,7 @@ import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
13
import 'package:cake_wallet/src/screens/support/support_page.dart';
14
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
15
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
16
+import 'package:cake_wallet/src/screens/yat/yat_webview_page.dart';
17
import 'package:cake_wallet/store/settings_store.dart';
18
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
19
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
@@ -379,6 +380,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
380
getIt.get<UnspentCoinsDetailsPage>(
381
param1: args));
382
383
+ case Routes.yat:
384
+ return MaterialPageRoute<void>(
385
+ builder: (_) =>
386
+ getIt.get<YatWebViewPage>(param1: settings.arguments as YatMode));
387
+
388
default:
389
return MaterialPageRoute<void>(
390
builder: (_) => Scaffold(
lib/routes.dart
+1
@@ -57,4 +57,5 @@ class Routes {
57
static const buyWebView = '/buy_web_view';
58
static const unspentCoinsList = '/unspent_coins_list';
59
static const unspentCoinsDetails = '/unspent_coins_details';
60
+ static const yat = '/yat';
61
}
\ No newline at end of file
lib/src/screens/receive/widgets/qr_widget.dart
+43
-3
@@ -66,7 +66,7 @@ class QRWidget extends StatelessWidget {
66
]),
67
if (isAmountFieldShow)
68
Padding(
69
- padding: EdgeInsets.only(top: 20),
69
+ padding: EdgeInsets.only(top: 10),
70
child: Row(
71
children: <Widget>[
72
Expanded(
@@ -101,7 +101,7 @@ class QRWidget extends StatelessWidget {
101
),
102
),
103
Padding(
104
- padding: EdgeInsets.only(top: 20, bottom: 20),
104
+ padding: EdgeInsets.only(top: 16, bottom: 16),
105
child: Builder(
106
builder: (context) => Observer(
107
builder: (context) => GestureDetector(
@@ -133,7 +133,47 @@ class QRWidget extends StatelessWidget {
133
],
134
),
135
))),
136
- )
136
+ ),
137
+ Observer(builder: (_) {
138
+ return addressListViewModel.yatAddress.isNotEmpty
139
+ ? Padding(
140
+ padding: EdgeInsets.only(bottom: 10),
141
+ child: Builder(
142
+ builder: (context) => GestureDetector(
143
+ onTap: () {
144
+ Clipboard.setData(ClipboardData(
145
+ text: addressListViewModel.yatAddress));
146
+ showBar<void>(
147
+ context, S.of(context).copied_to_clipboard);
148
+ },
149
+ child: Column(
150
+ crossAxisAlignment: CrossAxisAlignment.center,
151
+ children: [
152
+ Text(
153
+ 'Yat Address',
154
+ textAlign: TextAlign.center,
155
+ style: TextStyle(
156
+ fontSize: 13,
157
+ fontWeight: FontWeight.normal,
158
+ color: Theme.of(context).accentTextTheme.
159
+ display3.backgroundColor),
160
+ ),
161
+ Padding(
162
+ padding: EdgeInsets.only(top: 5),
163
+ child: Text(
164
+ addressListViewModel.yatAddress,
165
+ textAlign: TextAlign.center,
166
+ style: TextStyle(
167
+ fontSize: 13,
168
+ ),
169
+ )
170
+ )
171
+ ]
172
+ )
173
+ )),
174
+ )
175
+ : Container();
176
+ })
177
],
178
);
179
}
lib/src/screens/yat/widgets/yat_bar.dart
new
+27
@@ -0,0 +1,27 @@
1
+import 'package:cake_wallet/src/screens/yat/widgets/yat_close_button.dart';
2
+import 'package:flutter/material.dart';
3
+
4
+class YatBar extends StatelessWidget {
5
+ YatBar({this.onClose});
6
+
7
+ final VoidCallback onClose;
8
+ final image = Image.asset('assets/images/yat_logo.png');
9
+
10
+ @override
11
+ Widget build(BuildContext context) {
12
+ return Stack(
13
+ alignment: Alignment.bottomCenter,
14
+ children: [
15
+ Positioned(
16
+ top: 0,
17
+ right: 0,
18
+ child: YatCloseButton(onClose: onClose)
19
+ ),
20
+ Positioned(
21
+ top: 16,
22
+ child: image
23
+ )
24
+ ]
25
+ );
26
+ }
27
+}
\ No newline at end of file
lib/src/screens/yat/widgets/yat_close_button.dart
new
+28
@@ -0,0 +1,28 @@
1
+import 'package:cake_wallet/palette.dart';
2
+import 'package:flutter/material.dart';
3
+
4
+class YatCloseButton extends StatelessWidget {
5
+ YatCloseButton({this.onClose});
6
+
7
+ final VoidCallback onClose;
8
+
9
+ @override
10
+ Widget build(BuildContext context) {
11
+ return GestureDetector(
12
+ onTap: onClose,
13
+ child: Container(
14
+ height: 28,
15
+ width: 28,
16
+ alignment: Alignment.center,
17
+ decoration: BoxDecoration(
18
+ color: Palette.manatee,
19
+ shape: BoxShape.circle
20
+ ),
21
+ child: Icon(
22
+ Icons.clear,
23
+ color: Colors.white,
24
+ size: 20)
25
+ )
26
+ );
27
+ }
28
+}
\ No newline at end of file
lib/src/screens/yat/yat_alert.dart
new
+106
@@ -0,0 +1,106 @@
1
+import 'package:cake_wallet/routes.dart';
2
+import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart';
3
+import 'package:cake_wallet/src/screens/yat/yat_webview_page.dart';
4
+import 'package:cake_wallet/src/widgets/primary_button.dart';
5
+import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
6
+import 'package:flutter/cupertino.dart';
7
+import 'package:flutter/material.dart';
8
+import 'package:cake_wallet/palette.dart';
9
+
10
+class YatAlert extends StatelessWidget {
11
+ static const aspectRatioImage = 1.133;
12
+ final image = Image.asset('assets/images/yat_crypto.png');
13
+
14
+ @override
15
+ Widget build(BuildContext context) {
16
+ final screenHeight = MediaQuery.of(context).size.height;
17
+ final screenWidth = MediaQuery.of(context).size.width;
18
+
19
+ return Container(
20
+ height: screenHeight,
21
+ width: screenWidth,
22
+ color: Colors.white,
23
+ child: ScrollableWithBottomSection(
24
+ contentPadding: EdgeInsets.only(top: 40, bottom: 40),
25
+ content: Column(
26
+ children: [
27
+ Container(
28
+ height: 90,
29
+ padding: EdgeInsets.only(left: 24, right: 24),
30
+ child: YatBar(onClose: () => Navigator.of(context).pop())
31
+ ),
32
+ AspectRatio(
33
+ aspectRatio: aspectRatioImage,
34
+ child: FittedBox(child: image, fit: BoxFit.fill)
35
+ ),
36
+ Container(
37
+ padding: EdgeInsets.only(left: 30, right: 30),
38
+ child: Column(
39
+ children: [
40
+ Text(
41
+ 'Send and receive crypto more easily with Yat',
42
+ textAlign: TextAlign.center,
43
+ style: TextStyle(
44
+ fontSize: 24,
45
+ fontWeight: FontWeight.bold,
46
+ fontFamily: 'Lato',
47
+ color: Colors.black,
48
+ decoration: TextDecoration.none,
49
+ )
50
+ ),
51
+ Padding(
52
+ padding: EdgeInsets.only(top: 20),
53
+ child: Text(
54
+ 'Cake Wallet users can now send and receive all their favorite currencies with a one-of-a-kind emoji-based username.',
55
+ textAlign: TextAlign.center,
56
+ style: TextStyle(
57
+ fontSize: 16,
58
+ fontWeight: FontWeight.normal,
59
+ fontFamily: 'Lato',
60
+ color: Colors.black,
61
+ decoration: TextDecoration.none,
62
+ )
63
+ )
64
+ )
65
+ ]
66
+ )
67
+ )
68
+ ]
69
+ ),
70
+ bottomSectionPadding: EdgeInsets.fromLTRB(24, 0, 24, 40),
71
+ bottomSection: Column(
72
+ crossAxisAlignment: CrossAxisAlignment.center,
73
+ children: [
74
+ PrimaryIconButton(
75
+ text: 'Get your Yat',
76
+ textColor: Colors.white,
77
+ color: Palette.protectiveBlue,
78
+ borderColor: Palette.protectiveBlue,
79
+ iconColor: Colors.white,
80
+ iconBackgroundColor: Colors.transparent,
81
+ iconData: CupertinoIcons
82
+ .arrow_up_right_square,
83
+ mainAxisAlignment: MainAxisAlignment.end,
84
+ onPressed: () => Navigator.of(context)
85
+ .popAndPushNamed(Routes.yat, arguments: YatMode.create)),
86
+ Padding(
87
+ padding: EdgeInsets.only(top: 24),
88
+ child: PrimaryIconButton(
89
+ text: 'Connect an existing Yat',
90
+ textColor: Colors.black,
91
+ color: Palette.blueAlice,
92
+ borderColor: Palette.blueAlice,
93
+ iconColor: Colors.black,
94
+ iconBackgroundColor: Colors.transparent,
95
+ iconData: CupertinoIcons
96
+ .arrow_up_right_square,
97
+ mainAxisAlignment: MainAxisAlignment.end,
98
+ onPressed: () => Navigator.of(context)
99
+ .popAndPushNamed(Routes.yat, arguments: YatMode.connect))
100
+ )
101
+ ]
102
+ ),
103
+ )
104
+ );
105
+ }
106
+}
\ No newline at end of file
lib/src/screens/yat/yat_webview_page.dart
new
+45
@@ -0,0 +1,45 @@
1
+import 'package:cake_wallet/palette.dart';
2
+import 'package:cake_wallet/src/screens/base_page.dart';
3
+import 'package:cake_wallet/view_model/yat_view_model.dart';
4
+import 'package:flutter/material.dart';
5
+import 'package:webview_flutter/webview_flutter.dart';
6
+
7
+enum YatMode {create, connect}
8
+
9
+class YatWebViewPage extends BasePage {
10
+ YatWebViewPage({this.yatViewModel, this.mode}) {
11
+ switch (mode) {
12
+ case YatMode.create:
13
+ url = _baseUrl + _createSuffix;
14
+ break;
15
+ case YatMode.connect:
16
+ url = _baseUrl + _signInSuffix;
17
+ break;
18
+ default:
19
+ url = _baseUrl + _createSuffix;
20
+ }
21
+ }
22
+
23
+ static const _baseUrl = 'https://y.at';
24
+ static const _signInSuffix = '/sign-in';
25
+ static const _createSuffix = '/create';
26
+
27
+ final YatMode mode;
28
+ final YatViewModel yatViewModel;
29
+
30
+ String url;
31
+
32
+ @override
33
+ String get title => 'Yat';
34
+
35
+ @override
36
+ Color get backgroundDarkColor => Colors.white;
37
+
38
+ @override
39
+ Color get titleColor => Palette.darkBlueCraiola;
40
+
41
+ @override
42
+ Widget body(BuildContext context) => WebView(
43
+ initialUrl: url,
44
+ javascriptMode: JavascriptMode.unrestricted);
45
+}
\ No newline at end of file
lib/src/widgets/primary_button.dart
+16
-9
@@ -110,6 +110,9 @@ class PrimaryIconButton extends StatelessWidget {
110
@required this.borderColor,
111
@required this.iconColor,
112
@required this.iconBackgroundColor,
113
+ @required this.textColor,
114
+ this.mainAxisAlignment = MainAxisAlignment.start,
115
+ this.radius = 26
116
});
117
118
final VoidCallback onPressed;
@@ -119,40 +122,44 @@ class PrimaryIconButton extends StatelessWidget {
122
final Color iconColor;
123
final Color iconBackgroundColor;
124
final String text;
125
+ final MainAxisAlignment mainAxisAlignment;
126
+ final Color textColor;
127
+ final double radius;
128
129
@override
130
Widget build(BuildContext context) {
131
return ButtonTheme(
132
minWidth: double.infinity,
127
- height: 56.0,
133
+ height: 52.0,
134
child: FlatButton(
135
onPressed: onPressed,
136
color: color,
137
shape: RoundedRectangleBorder(
138
side: BorderSide(color: borderColor),
133
- borderRadius: BorderRadius.circular(10.0)),
139
+ borderRadius: BorderRadius.circular(radius)),
140
child: Stack(
141
children: <Widget>[
142
Row(
137
- mainAxisAlignment: MainAxisAlignment.start,
143
+ mainAxisAlignment: mainAxisAlignment,
144
children: <Widget>[
145
Container(
140
- width: 28.0,
141
- height: 56.0,
146
+ width: 26.0,
147
+ height: 52.0,
148
decoration: BoxDecoration(
149
shape: BoxShape.circle, color: iconBackgroundColor),
144
- child: Icon(iconData, color: iconColor, size: 22.0),
150
+ child: Center(
151
+ child: Icon(iconData, color: iconColor, size: 22.0)
152
+ ),
153
),
154
],
155
),
156
Container(
149
- height: 56.0,
157
+ height: 52.0,
158
child: Center(
159
child: Text(text,
160
style: TextStyle(
161
fontSize: 16.0,
154
- color:
155
- Theme.of(context).primaryTextTheme.button.color)),
162
+ color: textColor)),
163
),
164
)
165
],
lib/store/yat_store.dart
new
+12
@@ -0,0 +1,12 @@
1
+import 'package:mobx/mobx.dart';
2
+
3
+part 'yat_store.g.dart';
4
+
5
+class YatStore = YatStoreBase with _$YatStore;
6
+
7
+abstract class YatStoreBase with Store {
8
+ YatStoreBase() : yatAddress = '';
9
+
10
+ @observable
11
+ String yatAddress;
12
+}
\ No newline at end of file
lib/view_model/settings/settings_view_model.dart
+12
@@ -1,3 +1,5 @@
1
+import 'package:cake_wallet/src/screens/yat/yat_alert.dart';
2
+import 'package:cake_wallet/utils/show_pop_up.dart';
3
import 'package:flutter/cupertino.dart';
4
import 'package:mobx/mobx.dart';
5
import 'package:package_info/package_info.dart';
@@ -154,6 +156,16 @@ abstract class SettingsViewModelBase with Store {
156
_settingsStore.currentTheme = theme)
157
],
158
[
159
+ RegularListItem(
160
+ title: 'Manage Yats',
161
+ handler: (BuildContext context) async {
162
+ await showPopUp<void>(
163
+ context: context,
164
+ builder: (BuildContext context) {
165
+ return YatAlert();
166
+ });
167
+ },
168
+ ),
169
RegularListItem(
170
title: S.current.settings_terms_and_conditions,
171
handler: (BuildContext context) =>
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+10
-1
@@ -1,3 +1,4 @@
1
+import 'package:cake_wallet/store/yat_store.dart';
2
import 'package:flutter/foundation.dart';
3
import 'package:mobx/mobx.dart';
4
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
@@ -59,7 +60,10 @@ class BitcoinURI extends PaymentURI {
60
}
61
62
abstract class WalletAddressListViewModelBase with Store {
62
- WalletAddressListViewModelBase({@required AppStore appStore}) {
63
+ WalletAddressListViewModelBase({
64
+ @required AppStore appStore,
65
+ @required this.yatStore
66
+ }) {
67
_appStore = appStore;
68
_wallet = _appStore.wallet;
69
hasAccounts = _wallet?.type == WalletType.monero;
@@ -150,6 +154,9 @@ abstract class WalletAddressListViewModelBase with Store {
154
@computed
155
bool get hasAddressList => _wallet.type == WalletType.monero;
156
157
+ @computed
158
+ String get yatAddress => yatStore.yatAddress;
159
+
160
@observable
161
WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
162
_wallet;
@@ -158,6 +165,8 @@ abstract class WalletAddressListViewModelBase with Store {
165
166
AppStore _appStore;
167
168
+ final YatStore yatStore;
169
+
170
ReactionDisposer _onWalletChangeReaction;
171
172
@action
lib/view_model/yat_view_model.dart
new
+37
@@ -0,0 +1,37 @@
1
+import 'package:cake_wallet/store/yat_store.dart';
2
+import 'package:flutter/foundation.dart';
3
+import 'package:mobx/mobx.dart';
4
+import 'dart:convert';
5
+import 'package:cake_wallet/yat/yat_exception.dart';
6
+import 'package:http/http.dart';
7
+
8
+part 'yat_view_model.g.dart';
9
+
10
+class YatViewModel = YatViewModelBase with _$YatViewModel;
11
+
12
+abstract class YatViewModelBase with Store {
13
+ YatViewModelBase({@required this.yatStore});
14
+
15
+ final YatStore yatStore;
16
+
17
+ Future<void> fetchCartInfo() async {
18
+ const _apiKey = ''; // FIXME
19
+
20
+ final url = 'https://api.y.at/cart';
21
+
22
+ final response = await get(
23
+ url,
24
+ headers: {
25
+ 'Accept': '*/*',
26
+ 'Authorization,X-Api-Key': _apiKey
27
+ }
28
+ );
29
+
30
+ if (response.statusCode != 200) {
31
+ throw YatException(text: response.body.toString());
32
+ }
33
+
34
+ final responseJSON = json.decode(response.body) as Map<String, dynamic>;
35
+ yatStore.yatAddress = responseJSON[''] as String; // FIXME
36
+ }
37
+}
\ No newline at end of file
lib/yat/yat_exception.dart
new
+10
@@ -0,0 +1,10 @@
1
+import 'package:flutter/foundation.dart';
2
+
3
+class YatException implements Exception {
4
+ YatException({@required this.text});
5
+
6
+ final String text;
7
+
8
+ @override
9
+ String toString() => 'Yat exception: $text';
10
+}
\ No newline at end of file
lib/yat/yat_record.dart
new
+26
@@ -0,0 +1,26 @@
1
+import 'dart:convert';
2
+import 'package:cake_wallet/yat/yat_exception.dart';
3
+import 'package:http/http.dart';
4
+
5
+Future<String> fetchYatAddress(String emojiId) async {
6
+ const _apiKey = ''; // FIXME
7
+ const _requestURL = 'https://api.y.at/emoji_id/';
8
+
9
+ final url = _requestURL + emojiId;
10
+
11
+ final response = await get(
12
+ url,
13
+ headers: {
14
+ 'Accept': '*/*',
15
+ 'Authorization': 'Bearer $_apiKey'
16
+ }
17
+ );
18
+
19
+ if (response.statusCode != 200) {
20
+ throw YatException(text: response.body.toString());
21
+ }
22
+
23
+ final responseJSON = json.decode(response.body) as Map<String, dynamic>;
24
+ final yatAddress = responseJSON[''] as String; // FIXME
25
+ return yatAddress;
26
+}
\ No newline at end of file