CAKE-360 | merged all yat branches into current; saving information about yat to connected wallet; fixed qr widget; applied yat, unstoppable domains and open alias to contact book

OleksandrSobol committed Oct 1, 2021 at 18:13 UTC f353442a2a298aaa45a184b768d4d1daca19afad
26 files changed +789 -113
assets/images/emoji_popup.png
Binary files /dev/null and b/assets/images/emoji_popup.png differ
lib/di.dart
+7 -3
@@ -62,7 +62,7 @@ import 'package:cake_wallet/src/screens/send/send_page.dart';
62 import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
63 import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
64 import 'package:cake_wallet/store/wallet_list_store.dart';
65 -import 'package:cake_wallet/store/yat_store.dart';
65 +import 'package:cake_wallet/store/yat/yat_store.dart';
66 import 'package:cake_wallet/view_model/backup_view_model.dart';
67 import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart';
68 import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
@@ -193,7 +193,9 @@ Future setup(
193 SendTemplateStore(templateSource: _templates));
194 getIt.registerSingleton<ExchangeTemplateStore>(
195 ExchangeTemplateStore(templateSource: _exchangeTemplates));
196 - getIt.registerSingleton<YatStore>(YatStore());
196 + getIt.registerSingleton<YatStore>(YatStore(
197 + appStore: getIt.get<AppStore>()
198 + ));
199
200 final secretStore =
201 await SecretStoreBase.load(getIt.get<FlutterSecureStorage>());
@@ -254,6 +256,7 @@ Future setup(
256 tradeFilterStore: getIt.get<TradeFilterStore>(),
257 transactionFilterStore: getIt.get<TransactionFilterStore>(),
258 settingsStore: settingsStore,
259 + yatStore: getIt.get<YatStore>(),
260 ordersStore: getIt.get<OrdersStore>()));
261
262 getIt.registerFactory<AuthService>(() => AuthService(
@@ -391,7 +394,8 @@ Future setup(
394
395 getIt.registerFactory(() {
396 final appStore = getIt.get<AppStore>();
394 - return SettingsViewModel(appStore.settingsStore, appStore.wallet);
397 + final yatStore = getIt.get<YatStore>();
398 + return SettingsViewModel(appStore.settingsStore, yatStore, appStore.wallet);
399 });
400
401 getIt.registerFactory(() => SettingsPage(getIt.get<SettingsViewModel>()));
lib/entities/parse_address_from_domain.dart
+1 -1
@@ -1,7 +1,7 @@
1 import 'package:cake_wallet/entities/openalias_record.dart';
2 import 'package:cake_wallet/entities/parsed_address.dart';
3 import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
4 -import 'package:cake_wallet/yat/yat_record.dart';
4 +import 'package:cake_wallet/store/yat/yat_store.dart';
5
6 const unstoppableDomains = [
7 'crypto',
lib/entities/wallet_info.dart
+17 -3
@@ -7,7 +7,8 @@ part 'wallet_info.g.dart';
7 @HiveType(typeId: WalletInfo.typeId)
8 class WalletInfo extends HiveObject {
9 WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight,
10 - this.timestamp, this.dirPath, this.path, this.address);
10 + this.timestamp, this.dirPath, this.path, this.address, this.yatEid,
11 + this.yatRefreshToken);
12
13 factory WalletInfo.external(
14 {@required String id,
@@ -18,9 +19,12 @@ class WalletInfo extends HiveObject {
19 @required DateTime date,
20 @required String dirPath,
21 @required String path,
21 - @required String address}) {
22 + @required String address,
23 + String yatEid ='',
24 + String yatRefreshToken = ''}) {
25 return WalletInfo(id, name, type, isRecovery, restoreHeight,
23 - date.millisecondsSinceEpoch ?? 0, dirPath, path, address);
26 + date.millisecondsSinceEpoch ?? 0, dirPath, path, address,
27 + yatEid, yatRefreshToken);
28 }
29
30 static const typeId = 4;
@@ -56,5 +60,15 @@ class WalletInfo extends HiveObject {
60 @HiveField(10)
61 Map<String, String> addresses;
62
63 + @HiveField(11)
64 + String yatEid;
65 +
66 + @HiveField(12)
67 + String yatRefreshToken;
68 +
69 + String get yatEmojiId => yatEid ?? '';
70 +
71 + String get yatToken => yatRefreshToken ?? '';
72 +
73 DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
74 }
lib/main.dart
+11 -9
@@ -2,7 +2,7 @@ import 'dart:async';
2 import 'package:cake_wallet/bitcoin/unspent_coins_info.dart';
3 import 'package:cake_wallet/entities/language_service.dart';
4 import 'package:cake_wallet/buy/order.dart';
5 -import 'package:cake_wallet/store/yat_store.dart';
5 +import 'package:cake_wallet/store/yat/yat_store.dart';
6 import 'package:flutter/foundation.dart';
7 import 'package:flutter/material.dart';
8 import 'package:flutter/services.dart';
@@ -190,8 +190,8 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
190 @override
191 void initState() {
192 super.initState();
193 - _handleIncomingLinks(yatStore);
194 - _handleInitialUri(yatStore);
193 + _handleIncomingLinks();
194 + _handleInitialUri();
195 }
196
197 @override
@@ -200,25 +200,25 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
200 super.dispose();
201 }
202
203 - Future<void> _handleInitialUri(YatStore yatStore) async {
203 + Future<void> _handleInitialUri() async {
204 try {
205 final uri = await getInitialUri();
206 if (uri == null) {
207 return;
208 }
209 if (!mounted) return;
210 - _fetchEmojiFromUri(uri, yatStore);
210 + _fetchEmojiFromUri(uri);
211 } catch (e) {
212 if (!mounted) return;
213 print(e.toString());
214 }
215 }
216
217 - void _handleIncomingLinks(YatStore yatStore) {
217 + void _handleIncomingLinks() {
218 if (!kIsWeb) {
219 stream = getUriLinksStream().listen((Uri uri) {
220 if (!mounted) return;
221 - _fetchEmojiFromUri(uri, yatStore);
221 + _fetchEmojiFromUri(uri);
222 }, onError: (Object error) {
223 if (!mounted) return;
224 print('Error: $error');
@@ -226,16 +226,18 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
226 }
227 }
228
229 - void _fetchEmojiFromUri(Uri uri, YatStore yatStore) {
229 + void _fetchEmojiFromUri(Uri uri) {
230 final queryParameters = uri.queryParameters;
231 if (queryParameters?.isEmpty ?? true) {
232 return;
233 }
234 final emoji = queryParameters['eid'];
235 - if (emoji?.isEmpty ?? true) {
235 + final refreshToken = queryParameters['refresh_token'];
236 + if ((emoji?.isEmpty ?? true)||(refreshToken?.isEmpty ?? true)) {
237 return;
238 }
239 yatStore.emoji = emoji;
240 + yatStore.refreshToken = refreshToken;
241 }
242
243 @override
lib/src/screens/contact/contact_page.dart
+4 -2
@@ -1,3 +1,4 @@
1 +import 'package:cake_wallet/core/validator.dart';
2 import 'package:cake_wallet/palette.dart';
3 import 'package:cake_wallet/utils/show_pop_up.dart';
4 import 'package:flutter/material.dart';
@@ -97,8 +98,9 @@ class ContactPage extends BasePage {
98 buttonColor: Theme.of(context).accentTextTheme.display2.color,
99 iconColor: PaletteDark.gray,
100 borderColor: Theme.of(context).primaryTextTheme.title.backgroundColor,
100 - validator: AddressValidator(
101 - type: contactViewModel.currency),
101 + validator: TextValidator()
102 + // AddressValidator(
103 + // type: contactViewModel.currency),
104 )),
105 )
106 ],
lib/src/screens/dashboard/dashboard_page.dart
+11 -1
@@ -1,6 +1,7 @@
1 import 'package:cake_wallet/entities/wallet_type.dart';
2 import 'package:cake_wallet/generated/i18n.dart';
3 import 'package:cake_wallet/routes.dart';
4 +import 'package:cake_wallet/src/screens/yat/yat_popup.dart';
5 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
6 import 'package:cake_wallet/themes/theme_base.dart';
7 import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -145,7 +146,7 @@ class DashboardPage extends BasePage {
146 ));
147 }
148
148 - void _setEffects(BuildContext context) {
149 + void _setEffects(BuildContext context) async {
150 if (_isEffectsInstalled) {
151 return;
152 }
@@ -155,6 +156,15 @@ class DashboardPage extends BasePage {
156 pages.add(BalancePage(dashboardViewModel: walletViewModel));
157 pages.add(TransactionsPage(dashboardViewModel: walletViewModel));
158
159 + await Future<void>.delayed(Duration(seconds: 1));
160 + await showPopUp<void>(
161 + context: context,
162 + builder: (BuildContext context) {
163 + return YatPopup(
164 + dashboardViewModel: walletViewModel,
165 + onClose: () => Navigator.of(context).pop());
166 + });
167 +
168 autorun((_) async {
169 if (!walletViewModel.isOutdatedElectrumWallet) {
170 return;
lib/src/screens/exchange/exchange_page.dart
+18
@@ -235,6 +235,15 @@ class ExchangePage extends BasePage {
235 await fetchParsedAddress(
236 context, domain, ticker);
237 },
238 + onPushAddressBookButton: (context) async {
239 + final domain =
240 + exchangeViewModel.depositAddress;
241 + final ticker = exchangeViewModel
242 + .depositCurrency.title.toLowerCase();
243 + exchangeViewModel.depositAddress =
244 + await fetchParsedAddress(
245 + context, domain, ticker);
246 + },
247 ),
248 ),
249 ),
@@ -291,6 +300,15 @@ class ExchangePage extends BasePage {
300 await fetchParsedAddress(
301 context, domain, ticker);
302 },
303 + onPushAddressBookButton: (context) async {
304 + final domain =
305 + exchangeViewModel.receiveAddress;
306 + final ticker = exchangeViewModel
307 + .receiveCurrency.title.toLowerCase();
308 + exchangeViewModel.receiveAddress =
309 + await fetchParsedAddress(
310 + context, domain, ticker);
311 + },
312 )),
313 )
314 ],
lib/src/screens/exchange/widgets/exchange_card.dart
+6 -1
@@ -34,7 +34,8 @@ class ExchangeCard extends StatefulWidget {
34 this.addressFocusNode,
35 this.hasAllAmount = false,
36 this.allAmount,
37 - this.onPushPasteButton})
37 + this.onPushPasteButton,
38 + this.onPushAddressBookButton})
39 : super(key: key);
40
41 final List<CryptoCurrency> currencies;
@@ -59,6 +60,7 @@ class ExchangeCard extends StatefulWidget {
60 final bool hasAllAmount;
61 final Function allAmount;
62 final Function(BuildContext context) onPushPasteButton;
63 + final Function(BuildContext context) onPushAddressBookButton;
64
65 @override
66 ExchangeCardState createState() => ExchangeCardState();
@@ -321,6 +323,7 @@ class ExchangeCardState extends State<ExchangeCard> {
323 buttonColor: widget.addressButtonsColor,
324 validator: widget.addressTextFieldValidator,
325 onPushPasteButton: widget.onPushPasteButton,
326 + onPushAddressBookButton: widget.onPushAddressBookButton
327 ),
328 )
329 : Padding(
@@ -366,6 +369,8 @@ class ExchangeCardState extends State<ExchangeCard> {
369 setState(() =>
370 addressController.text =
371 contact.address);
372 + widget.onPushAddressBookButton
373 + ?.call(context);
374 }
375 },
376 child: Container(
lib/src/screens/receive/widgets/qr_widget.dart
+2 -2
@@ -101,7 +101,7 @@ class QRWidget extends StatelessWidget {
101 ),
102 ),
103 Padding(
104 - padding: EdgeInsets.only(top: 16, bottom: 16),
104 + padding: EdgeInsets.only(top: 10, bottom: 10),
105 child: Builder(
106 builder: (context) => Observer(
107 builder: (context) => GestureDetector(
@@ -164,7 +164,7 @@ class QRWidget extends StatelessWidget {
164 addressListViewModel.emoji,
165 textAlign: TextAlign.center,
166 style: TextStyle(
167 - fontSize: 13,
167 + fontSize: 26,
168 ),
169 )
170 )
lib/src/screens/send/widgets/send_card.dart
+4 -2
@@ -156,8 +156,10 @@ class SendCardState extends State<SendCard>
156 output.resetParsedAddress();
157 await output.fetchParsedAddress(context);
158 },
159 - onPushAddressBookButton: (context) =>
160 - output.resetParsedAddress(),
159 + onPushAddressBookButton: (context) async {
160 + output.resetParsedAddress();
161 + await output.fetchParsedAddress(context);
162 + },
163 validator: validator,
164 );
165 }),
lib/src/screens/yat/widgets/first_introduction.dart new
+90
@@ -0,0 +1,90 @@
1 +import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart';
2 +import 'package:cake_wallet/src/screens/yat/widgets/yat_page_indicator.dart';
3 +import 'package:cake_wallet/src/widgets/primary_button.dart';
4 +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
5 +import 'package:flutter/cupertino.dart';
6 +import 'package:flutter/material.dart';
7 +import 'package:cake_wallet/palette.dart';
8 +import 'package:lottie/lottie.dart';
9 +
10 +class FirstIntroduction extends StatelessWidget {
11 + FirstIntroduction({this.onClose, this.onNext});
12 +
13 + static const aspectRatioImage = 1.133;
14 + final VoidCallback onClose;
15 + final VoidCallback onNext;
16 + final animation = Lottie.asset('assets/animation/anim1.json');
17 +
18 + @override
19 + Widget build(BuildContext context) {
20 + final screenHeight = MediaQuery.of(context).size.height;
21 + final screenWidth = MediaQuery.of(context).size.width;
22 +
23 + return Container(
24 + height: screenHeight,
25 + width: screenWidth,
26 + color: Colors.white,
27 + child: ScrollableWithBottomSection(
28 + contentPadding: EdgeInsets.only(top: 40, bottom: 40),
29 + content: Column(
30 + children: [
31 + Container(
32 + height: 45,
33 + padding: EdgeInsets.only(left: 24, right: 24),
34 + child: YatBar(onClose: () => Navigator.of(context).pop())
35 + ),
36 + animation,
37 + Container(
38 + padding: EdgeInsets.only(left: 30, right: 30),
39 + child: Column(
40 + children: [
41 + Text(
42 + 'Send and receive crypto more easily with Yat',
43 + textAlign: TextAlign.center,
44 + style: TextStyle(
45 + fontSize: 24,
46 + fontWeight: FontWeight.bold,
47 + fontFamily: 'Lato',
48 + color: Colors.black,
49 + decoration: TextDecoration.none,
50 + )
51 + ),
52 + Padding(
53 + padding: EdgeInsets.only(top: 20),
54 + child: Text(
55 + 'Cake Wallet users can now send and receive all their favorite currencies with a one-of-a-kind emoji-based username.',
56 + textAlign: TextAlign.center,
57 + style: TextStyle(
58 + fontSize: 16,
59 + fontWeight: FontWeight.normal,
60 + fontFamily: 'Lato',
61 + color: Colors.black,
62 + decoration: TextDecoration.none,
63 + )
64 + )
65 + )
66 + ]
67 + )
68 + )
69 + ]
70 + ),
71 + bottomSectionPadding: EdgeInsets.fromLTRB(24, 0, 24, 24),
72 + bottomSection: Column(
73 + crossAxisAlignment: CrossAxisAlignment.center,
74 + children: [
75 + PrimaryButton(
76 + text: 'Next',
77 + textColor: Colors.white,
78 + color: Palette.protectiveBlue,
79 + onPressed: onNext
80 + ),
81 + Padding(
82 + padding: EdgeInsets.only(top: 24),
83 + child: YatPageIndicator(filled: 0)
84 + )
85 + ]
86 + ),
87 + )
88 + );
89 + }
90 +}
\ No newline at end of file
lib/src/screens/yat/widgets/second_introduction.dart new
+89
@@ -0,0 +1,89 @@
1 +import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart';
2 +import 'package:cake_wallet/src/screens/yat/widgets/yat_page_indicator.dart';
3 +import 'package:cake_wallet/src/widgets/primary_button.dart';
4 +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
5 +import 'package:flutter/cupertino.dart';
6 +import 'package:flutter/material.dart';
7 +import 'package:cake_wallet/palette.dart';
8 +import 'package:lottie/lottie.dart';
9 +
10 +class SecondIntroduction extends StatelessWidget {
11 + SecondIntroduction({this.onClose, this.onNext});
12 +
13 + final VoidCallback onClose;
14 + final VoidCallback onNext;
15 + final animation = Lottie.asset('assets/animation/anim2.json');
16 +
17 + @override
18 + Widget build(BuildContext context) {
19 + final screenHeight = MediaQuery.of(context).size.height;
20 + final screenWidth = MediaQuery.of(context).size.width;
21 +
22 + return Container(
23 + height: screenHeight,
24 + width: screenWidth,
25 + color: Colors.white,
26 + child: ScrollableWithBottomSection(
27 + contentPadding: EdgeInsets.only(top: 40, bottom: 40),
28 + content: Column(
29 + children: [
30 + Container(
31 + height: 45,
32 + padding: EdgeInsets.only(left: 24, right: 24),
33 + child: YatBar(onClose: onClose)
34 + ),
35 + animation,
36 + Padding(
37 + padding: EdgeInsets.only(top: 40, left: 30, right: 30),
38 + child: Column(
39 + children: [
40 + Text(
41 + 'One emoji address to rule them all',
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 + 'Your Yat is a single unique emoji address that replaces all of your long hexadecimal addresses for all of your currencies.',
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, 24),
71 + bottomSection: Column(
72 + crossAxisAlignment: CrossAxisAlignment.center,
73 + children: [
74 + PrimaryButton(
75 + text: 'Next',
76 + textColor: Colors.white,
77 + color: Palette.protectiveBlue,
78 + onPressed: onNext
79 + ),
80 + Padding(
81 + padding: EdgeInsets.only(top: 24),
82 + child: YatPageIndicator(filled: 1)
83 + )
84 + ]
85 + ),
86 + )
87 + );
88 + }
89 +}
\ No newline at end of file
lib/src/screens/yat/widgets/third_introduction.dart new
+109
@@ -0,0 +1,109 @@
1 +import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart';
2 +import 'package:cake_wallet/src/screens/yat/widgets/yat_page_indicator.dart';
3 +import 'package:cake_wallet/src/widgets/primary_button.dart';
4 +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
5 +import 'package:flutter/cupertino.dart';
6 +import 'package:flutter/material.dart';
7 +import 'package:cake_wallet/palette.dart';
8 +import 'package:lottie/lottie.dart';
9 +
10 +class ThirdIntroduction extends StatelessWidget {
11 + ThirdIntroduction({this.onClose, this.onGet, this.onConnect});
12 +
13 + final VoidCallback onClose;
14 + final VoidCallback onGet;
15 + final VoidCallback onConnect;
16 + final animation = Lottie.asset('assets/animation/anim3.json');
17 +
18 + @override
19 + Widget build(BuildContext context) {
20 + final screenHeight = MediaQuery.of(context).size.height;
21 + final screenWidth = MediaQuery.of(context).size.width;
22 +
23 + return Container(
24 + height: screenHeight,
25 + width: screenWidth,
26 + color: Colors.white,
27 + child: ScrollableWithBottomSection(
28 + contentPadding: EdgeInsets.only(top: 40, bottom: 40),
29 + content: Column(
30 + children: [
31 + Container(
32 + height: 90,
33 + padding: EdgeInsets.only(left: 24, right: 24),
34 + child: YatBar(onClose: onClose)
35 + ),
36 + animation,
37 + Padding(
38 + padding: EdgeInsets.only(top: 40, left: 30, right: 30),
39 + child: Column(
40 + children: [
41 + Text(
42 + 'Yat plays nicely with others',
43 + textAlign: TextAlign.center,
44 + style: TextStyle(
45 + fontSize: 24,
46 + fontWeight: FontWeight.bold,
47 + fontFamily: 'Lato',
48 + color: Colors.black,
49 + decoration: TextDecoration.none,
50 + )
51 + ),
52 + Padding(
53 + padding: EdgeInsets.only(top: 20),
54 + child: Text(
55 + 'Yats live outside of Cake Wallet, too. Any wallet address on earth can be replaced with a Yat!',
56 + textAlign: TextAlign.center,
57 + style: TextStyle(
58 + fontSize: 16,
59 + fontWeight: FontWeight.normal,
60 + fontFamily: 'Lato',
61 + color: Colors.black,
62 + decoration: TextDecoration.none,
63 + )
64 + )
65 + )
66 + ]
67 + )
68 + ),
69 + ],
70 + ),
71 + bottomSectionPadding: EdgeInsets.fromLTRB(24, 0, 24, 24),
72 + bottomSection: Column(
73 + crossAxisAlignment: CrossAxisAlignment.center,
74 + children: [
75 + PrimaryIconButton(
76 + text: 'Get your Yat',
77 + textColor: Colors.white,
78 + color: Palette.protectiveBlue,
79 + borderColor: Palette.protectiveBlue,
80 + iconColor: Colors.white,
81 + iconBackgroundColor: Colors.transparent,
82 + iconData: CupertinoIcons
83 + .arrow_up_right_square,
84 + mainAxisAlignment: MainAxisAlignment.end,
85 + onPressed: onGet),
86 + Padding(
87 + padding: EdgeInsets.only(top: 12),
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: onConnect)
99 + ),
100 + Padding(
101 + padding: EdgeInsets.only(top: 24),
102 + child: YatPageIndicator(filled: 2)
103 + )
104 + ]
105 + ),
106 + )
107 + );
108 + }
109 +}
\ No newline at end of file
lib/src/screens/yat/widgets/yat_page_indicator.dart new
+33
@@ -0,0 +1,33 @@
1 +import 'package:flutter/material.dart';
2 +import 'package:cake_wallet/palette.dart';
3 +
4 +class YatPageIndicator extends StatelessWidget {
5 + YatPageIndicator({this.filled});
6 +
7 + final int filled;
8 +
9 + @override
10 + Widget build(BuildContext context) {
11 + return Container(
12 + width: 44,
13 + child: Row(
14 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
15 + children: List.generate(3, (index) {
16 + final size = 8.0;
17 + final isFilled = index == filled;
18 +
19 + return Container(
20 + height: size,
21 + width: size,
22 + decoration: BoxDecoration(
23 + shape: BoxShape.circle,
24 + color: isFilled
25 + ? Palette.frostySky
26 + : Palette.stateGray.withOpacity(0.1)
27 + )
28 + );
29 + })
30 + )
31 + );
32 + }
33 +}
\ No newline at end of file
lib/src/screens/yat/yat_alert.dart
+14 -44
@@ -1,8 +1,7 @@
1 -import 'package:cake_wallet/core/wallet_base.dart';
2 -import 'package:cake_wallet/entities/wallet_type.dart';
1 import 'package:cake_wallet/src/screens/yat/widgets/yat_bar.dart';
2 import 'package:cake_wallet/src/widgets/primary_button.dart';
3 import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
4 +import 'package:cake_wallet/store/yat/yat_store.dart';
5 import 'package:flutter/cupertino.dart';
6 import 'package:flutter/material.dart';
7 import 'package:cake_wallet/palette.dart';
@@ -11,22 +10,13 @@ import 'package:cake_wallet/generated/i18n.dart';
10 import 'package:lottie/lottie.dart';
11
12 class YatAlert extends StatelessWidget {
14 - YatAlert({@required this.wallet, this.isYatDevMode = false})
15 - : baseUrl = isYatDevMode ? _baseDevUrl : _baseReleaseUrl,
16 - address = wallet.walletAddresses.address;
13 + YatAlert(this.yatStore)
14 + : baseUrl = isYatDevMode ? baseDevUrl : baseReleaseUrl;
15
18 - final WalletBase wallet;
19 - final bool isYatDevMode;
20 - final String address;
16 + final YatStore yatStore;
17 final String baseUrl;
18 static const aspectRatioImage = 1.133;
23 - static const _baseDevUrl = 'https://yat.fyi';
24 - static const _baseReleaseUrl = 'https://y.at';
25 - static const _signInSuffix = '/partner/CW/link-email';
26 - static const _createSuffix = '/create';
27 - static const _queryParameter = '?addresses=';
28 - final image = Image.asset('assets/images/yat_crypto.png');
29 - final anim = Lottie.asset('assets/animation/anim1.json');
19 + final animation = Lottie.asset('assets/animation/anim1.json');
20
21 @override
22 Widget build(BuildContext context) {
@@ -42,15 +32,11 @@ class YatAlert extends StatelessWidget {
32 content: Column(
33 children: [
34 Container(
45 - height: 45, // 90
35 + height: 45,
36 padding: EdgeInsets.only(left: 24, right: 24),
37 child: YatBar(onClose: () => Navigator.of(context).pop())
38 ),
49 - anim,
50 - // AspectRatio(
51 - // aspectRatio: aspectRatioImage,
52 - // child: FittedBox(child: image, fit: BoxFit.fill)
53 - // ),
39 + animation,
40 Container(
41 padding: EdgeInsets.only(left: 30, right: 30),
42 child: Column(
@@ -100,7 +86,7 @@ class YatAlert extends StatelessWidget {
86 .arrow_up_right_square,
87 mainAxisAlignment: MainAxisAlignment.end,
88 onPressed: () {
103 - final url = baseUrl + _createSuffix;
89 + final url = baseUrl + createSuffix;
90 launch(url);
91 }),
92 Padding(
@@ -116,8 +102,12 @@ class YatAlert extends StatelessWidget {
102 .arrow_up_right_square,
103 mainAxisAlignment: MainAxisAlignment.end,
104 onPressed: () {
119 - final url = baseUrl + _signInSuffix + _queryParameter +
120 - _defineTag() + '%3D' + address;
105 + String url = baseUrl + signInSuffix;
106 + final parameters =
107 + yatStore.defineQueryParameters();
108 + if (parameters.isNotEmpty) {
109 + url += queryParameter + parameters;
110 + }
111 launch(url);
112 })
113 )
@@ -126,24 +116,4 @@ class YatAlert extends StatelessWidget {
116 )
117 );
118 }
129 -
130 - String _defineTag() {
131 - String tag;
132 - switch (wallet.type) {
133 - case WalletType.monero:
134 - tag = address.startsWith('4')
135 - ? '0x1001'
136 - : '0x1002';
137 - break;
138 - case WalletType.bitcoin:
139 - tag = '0x1003';
140 - break;
141 - case WalletType.litecoin:
142 - tag = '0x3fff';
143 - break;
144 - default:
145 - tag = '0x3fff';
146 - }
147 - return tag;
148 - }
119 }
\ No newline at end of file
lib/src/screens/yat/yat_popup.dart new
+178
@@ -0,0 +1,178 @@
1 +import 'package:cake_wallet/palette.dart';
2 +import 'package:cake_wallet/src/screens/yat/widgets/first_introduction.dart';
3 +import 'package:cake_wallet/src/screens/yat/widgets/second_introduction.dart';
4 +import 'package:cake_wallet/src/screens/yat/widgets/third_introduction.dart';
5 +import 'package:cake_wallet/src/screens/yat/widgets/yat_close_button.dart';
6 +import 'package:cake_wallet/src/widgets/alert_background.dart';
7 +import 'package:cake_wallet/src/widgets/primary_button.dart';
8 +import 'package:cake_wallet/store/yat/yat_store.dart';
9 +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
10 +import 'package:flutter/material.dart';
11 +import 'package:animate_do/animate_do.dart';
12 +import 'package:flutter_mobx/flutter_mobx.dart';
13 +import 'package:url_launcher/url_launcher.dart';
14 +
15 +class YatPopup extends StatelessWidget {
16 + YatPopup({this.dashboardViewModel, this.onClose})
17 + : baseUrl = isYatDevMode ? baseDevUrl : baseReleaseUrl;
18 +
19 + static const durationInMilliseconds = 250;
20 +
21 + final DashboardViewModel dashboardViewModel;
22 + final VoidCallback onClose;
23 + final String baseUrl;
24 + final image = Image.asset('assets/images/emoji_popup.png');
25 +
26 + @override
27 + Widget build(BuildContext context) {
28 + final screenWidth = MediaQuery.of(context).size.width;
29 +
30 + return Stack(
31 + clipBehavior: Clip.none,
32 + alignment: Alignment.bottomCenter,
33 + children: [
34 + AlertBackground(
35 + child: Container()
36 + ),
37 + SlideInUp(
38 + from: 420,
39 + duration: Duration(milliseconds: durationInMilliseconds),
40 + child: ClipRRect(
41 + borderRadius: BorderRadius.only(
42 + topLeft: Radius.circular(24),
43 + topRight: Radius.circular(24)),
44 + child: Container(
45 + height: 420,
46 + color: Colors.white,
47 + padding: EdgeInsets.fromLTRB(24, 15, 24, 24),
48 + child: Column(
49 + mainAxisSize: MainAxisSize.max,
50 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
51 + children: [
52 + Column(
53 + children: [
54 + Row(
55 + mainAxisSize: MainAxisSize.max,
56 + mainAxisAlignment: MainAxisAlignment.end,
57 + children: [
58 + YatCloseButton(onClose: onClose)
59 + ]
60 + ),
61 + Row(
62 + mainAxisSize: MainAxisSize.max,
63 + mainAxisAlignment: MainAxisAlignment.center,
64 + children: [
65 + Container(
66 + height: 64,
67 + width: 165,
68 + alignment: Alignment.center,
69 + decoration: BoxDecoration(
70 + color: Colors.white,
71 + borderRadius: BorderRadius
72 + .all(Radius.circular(32)),
73 + boxShadow: [
74 + BoxShadow(
75 + color: Colors.black.withOpacity(0.05),
76 + blurRadius: 15,
77 + offset: Offset(0, 5)
78 + )
79 + ]
80 + ),
81 + child: image
82 + )
83 + ]
84 + )
85 + ]
86 + ),
87 + Container(
88 + padding: EdgeInsets.only(left: 6, right: 6),
89 + child: Column(
90 + children: [
91 + Text(
92 + 'Your wallet address can be emojified.',
93 + textAlign: TextAlign.center,
94 + style: TextStyle(
95 + fontSize: 24,
96 + fontWeight: FontWeight.bold,
97 + fontFamily: 'Lato',
98 + color: Colors.black,
99 + decoration: TextDecoration.none,
100 + )
101 + ),
102 + Padding(
103 + padding: EdgeInsets.only(top: 20),
104 + child: Text(
105 + 'You can now send and receive crypto in Cake Wallet with your Yat - a short, emoji-based username. Manage Yats at any time on the settings screen',
106 + textAlign: TextAlign.center,
107 + style: TextStyle(
108 + fontSize: 16,
109 + fontWeight: FontWeight.normal,
110 + fontFamily: 'Lato',
111 + color: Colors.black,
112 + decoration: TextDecoration.none,
113 + )
114 + )
115 + )
116 + ]
117 + )
118 + ),
119 + PrimaryButton(
120 + text: 'Learn More',
121 + textColor: Colors.white,
122 + color: Palette.protectiveBlue,
123 + onPressed: () => dashboardViewModel
124 + .isShowFirstYatIntroduction = true
125 + )
126 + ],
127 + )
128 + )
129 + ),
130 + ),
131 + Observer(builder: (_) => dashboardViewModel.isShowFirstYatIntroduction
132 + ? SlideInRight(
133 + from: screenWidth,
134 + duration: Duration(milliseconds: durationInMilliseconds),
135 + child: FirstIntroduction(
136 + onClose: onClose,
137 + onNext: () => dashboardViewModel
138 + .isShowSecondYatIntroduction = true
139 + ))
140 + : Container()
141 + ),
142 + Observer(builder: (_) => dashboardViewModel.isShowSecondYatIntroduction
143 + ? SlideInRight(
144 + from: screenWidth,
145 + duration: Duration(milliseconds: durationInMilliseconds),
146 + child: SecondIntroduction(
147 + onClose: onClose,
148 + onNext: () => dashboardViewModel
149 + .isShowThirdYatIntroduction = true
150 + ))
151 + : Container()
152 + ),
153 + Observer(builder: (_) => dashboardViewModel.isShowThirdYatIntroduction
154 + ? SlideInRight(
155 + from: screenWidth,
156 + duration: Duration(milliseconds: durationInMilliseconds),
157 + child: ThirdIntroduction(
158 + onClose: onClose,
159 + onGet: () {
160 + final url = baseUrl + createSuffix;
161 + launch(url);
162 + },
163 + onConnect: () {
164 + String url = baseUrl + signInSuffix;
165 + final parameters = dashboardViewModel
166 + .yatStore.defineQueryParameters();
167 + if (parameters.isNotEmpty) {
168 + url += queryParameter + parameters;
169 + }
170 + launch(url);
171 + }
172 + ))
173 + : Container()
174 + )
175 + ],
176 + );
177 + }
178 +}
\ No newline at end of file
lib/store/yat/yat_exception.dart renamed
lib/store/yat/yat_store.dart new
+166
@@ -0,0 +1,166 @@
1 +import 'package:cake_wallet/core/transaction_history.dart';
2 +import 'package:cake_wallet/core/wallet_base.dart';
3 +import 'package:cake_wallet/entities/balance.dart';
4 +import 'package:cake_wallet/entities/transaction_info.dart';
5 +import 'package:cake_wallet/store/app_store.dart';
6 +import 'package:flutter/foundation.dart';
7 +import 'package:mobx/mobx.dart';
8 +import 'package:cake_wallet/bitcoin/electrum_wallet.dart';
9 +import 'package:cake_wallet/entities/wallet_type.dart';
10 +import 'package:cake_wallet/monero/monero_subaddress_list.dart';
11 +import 'package:cake_wallet/monero/monero_wallet.dart';
12 +import 'dart:convert';
13 +import 'package:cake_wallet/store/yat/yat_exception.dart';
14 +import 'package:http/http.dart';
15 +
16 +part 'yat_store.g.dart';
17 +
18 +const baseDevUrl = 'https://yat.fyi';
19 +const baseReleaseUrl = 'https://y.at';
20 +const signInSuffix = '/partner/CW/link-email';
21 +const createSuffix = '/create';
22 +const queryParameter = '?addresses=';
23 +const requestDevUrl = 'https://a.yat.fyi/emoji_id/';
24 +const requestReleaseUrl = 'https://a.y.at/emoji_id/';
25 +const isYatDevMode = true;
26 +
27 +Future<List<String>> fetchYatAddress(String emojiId, String ticker) async {
28 + final requestURL = isYatDevMode ? requestDevUrl : requestReleaseUrl;
29 + final url = requestURL + emojiId + '/' + ticker.toUpperCase();
30 + final response = await get(url);
31 +
32 + if (response.statusCode != 200) {
33 + throw YatException(text: response.body.toString());
34 + }
35 +
36 + final responseJSON = json.decode(response.body) as Map<String, dynamic>;
37 + final result = responseJSON['result'] as List<dynamic>;
38 +
39 + if (result?.isEmpty ?? true) {
40 + return [];
41 + }
42 +
43 + final List<String> addresses = [];
44 +
45 + for (var elem in result) {
46 + final yatAddress = elem['data'] as String;
47 + if (yatAddress?.isNotEmpty ?? false) {
48 + addresses.add(yatAddress);
49 + }
50 + }
51 +
52 + return addresses;
53 +}
54 +
55 +class YatStore = YatStoreBase with _$YatStore;
56 +
57 +abstract class YatStoreBase with Store {
58 + YatStoreBase({@required this.appStore}) {
59 + _wallet ??= appStore.wallet;
60 + emoji = _wallet?.walletInfo?.yatEmojiId ?? '';
61 + refreshToken = _wallet?.walletInfo?.yatToken ?? '';
62 + reaction((_) => appStore.wallet, _onWalletChange);
63 + reaction((_) => emoji, (String emoji) => _onEmojiChange());
64 + }
65 +
66 + AppStore appStore;
67 +
68 + @observable
69 + String emoji;
70 +
71 + @observable
72 + String refreshToken;
73 +
74 + @observable
75 + WalletBase<Balance, TransactionHistoryBase<TransactionInfo>, TransactionInfo>
76 + _wallet;
77 +
78 + @action
79 + void _onWalletChange(
80 + WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
81 + TransactionInfo>
82 + wallet) {
83 + this._wallet = wallet;
84 + emoji = wallet?.walletInfo?.yatEmojiId ?? '';
85 + refreshToken = wallet?.walletInfo?.yatToken ?? '';
86 + }
87 +
88 + @action
89 + void _onEmojiChange() {
90 + try {
91 + final walletInfo = _wallet.walletInfo;
92 +
93 + if (walletInfo == null) {
94 + return;
95 + }
96 +
97 + walletInfo.yatEid = emoji;
98 + walletInfo.yatRefreshToken = refreshToken;
99 +
100 + if (walletInfo.isInBox) {
101 + walletInfo.save();
102 + }
103 + } catch (e) {
104 + print(e.toString());
105 + }
106 + }
107 +
108 + String defineQueryParameters() {
109 + String parameters = '';
110 + switch (_wallet.type) {
111 + case WalletType.monero:
112 + final wallet = _wallet as MoneroWallet;
113 + final subaddressList = MoneroSubaddressList();
114 + var isFirstAddress = true;
115 +
116 + wallet.walletAddresses.accountList.accounts.forEach((account) {
117 + subaddressList.update(accountIndex: account.id);
118 + subaddressList.subaddresses.forEach((subaddress) {
119 + if (!isFirstAddress) {
120 + parameters += '%7C';
121 + } else {
122 + isFirstAddress = !isFirstAddress;
123 + }
124 +
125 + parameters += subaddress.address.startsWith('4')
126 + ? '0x1001%3D'
127 + : '0x1002%3D';
128 +
129 + parameters += subaddress.address;
130 + });
131 + });
132 + break;
133 + case WalletType.bitcoin:
134 + final wallet = _wallet as ElectrumWallet;
135 + var isFirstAddress = true;
136 +
137 + wallet.walletAddresses.addresses.forEach((record) {
138 + if (!isFirstAddress) {
139 + parameters += '%7C';
140 + } else {
141 + isFirstAddress = !isFirstAddress;
142 + }
143 +
144 + parameters += '0x1003%3D' + record.address;
145 + });
146 + break;
147 + case WalletType.litecoin:
148 + final wallet = _wallet as ElectrumWallet;
149 + var isFirstAddress = true;
150 +
151 + wallet.walletAddresses.addresses.forEach((record) {
152 + if (!isFirstAddress) {
153 + parameters += '%7C';
154 + } else {
155 + isFirstAddress = !isFirstAddress;
156 + }
157 +
158 + parameters += '0x3fff%3D' + record.address;
159 + });
160 + break;
161 + default:
162 + parameters = '';
163 + }
164 + return parameters;
165 + }
166 +}
\ No newline at end of file
lib/store/yat_store.dart deleted
-12
@@ -1,12 +0,0 @@
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() : emoji = '';
9 -
10 - @observable
11 - String emoji;
12 -}
\ No newline at end of file
lib/view_model/dashboard/dashboard_view_model.dart
+16
@@ -12,6 +12,7 @@ import 'package:cake_wallet/entities/transaction_info.dart';
12 import 'package:cake_wallet/exchange/exchange_provider_description.dart';
13 import 'package:cake_wallet/store/settings_store.dart';
14 import 'package:cake_wallet/store/dashboard/orders_store.dart';
15 +import 'package:cake_wallet/store/yat/yat_store.dart';
16 import 'package:cake_wallet/utils/mobx.dart';
17 import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
18 import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
@@ -46,6 +47,7 @@ abstract class DashboardViewModelBase with Store {
47 this.tradeFilterStore,
48 this.transactionFilterStore,
49 this.settingsStore,
50 + this.yatStore,
51 this.ordersStore}) {
52 filterItems = {
53 S.current.transactions: [
@@ -86,6 +88,9 @@ abstract class DashboardViewModelBase with Store {
88 type = wallet.type;
89 isOutdatedElectrumWallet =
90 wallet.type == WalletType.bitcoin && wallet.seed.split(' ').length < 24;
91 + isShowFirstYatIntroduction = false;
92 + isShowSecondYatIntroduction = false;
93 + isShowThirdYatIntroduction = false;
94 final _wallet = wallet;
95
96 if (_wallet is MoneroWallet) {
@@ -147,6 +152,15 @@ abstract class DashboardViewModelBase with Store {
152 @observable
153 String subname;
154
155 + @observable
156 + bool isShowFirstYatIntroduction;
157 +
158 + @observable
159 + bool isShowSecondYatIntroduction;
160 +
161 + @observable
162 + bool isShowThirdYatIntroduction;
163 +
164 @computed
165 String get address => wallet.walletAddresses.address;
166
@@ -208,6 +222,8 @@ abstract class DashboardViewModelBase with Store {
222
223 SettingsStore settingsStore;
224
225 + YatStore yatStore;
226 +
227 TradesStore tradesStore;
228
229 OrdersStore ordersStore;
lib/view_model/settings/settings_view_model.dart
+4 -1
@@ -1,4 +1,5 @@
1 import 'package:cake_wallet/src/screens/yat/yat_alert.dart';
2 +import 'package:cake_wallet/store/yat/yat_store.dart';
3 import 'package:cake_wallet/utils/show_pop_up.dart';
4 import 'package:flutter/cupertino.dart';
5 import 'package:mobx/mobx.dart';
@@ -50,6 +51,7 @@ List<TransactionPriority> priorityForWalletType(WalletType type) {
51 abstract class SettingsViewModelBase with Store {
52 SettingsViewModelBase(
53 this._settingsStore,
54 + this._yatStore,
55 WalletBase<Balance, TransactionHistoryBase<TransactionInfo>,
56 TransactionInfo>
57 wallet)
@@ -162,7 +164,7 @@ abstract class SettingsViewModelBase with Store {
164 await showPopUp<void>(
165 context: context,
166 builder: (BuildContext context) {
165 - return YatAlert(wallet: wallet, isYatDevMode: true);
167 + return YatAlert(_yatStore);
168 });
169 },
170 ),
@@ -213,6 +215,7 @@ abstract class SettingsViewModelBase with Store {
215 final Map<String, String> itemHeaders;
216 List<List<SettingsListItem>> sections;
217 final SettingsStore _settingsStore;
218 + final YatStore _yatStore;
219 final WalletType _walletType;
220 final BiometricAuth _biometricAuth;
221
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+1 -1
@@ -1,4 +1,4 @@
1 -import 'package:cake_wallet/store/yat_store.dart';
1 +import 'package:cake_wallet/store/yat/yat_store.dart';
2 import 'package:flutter/foundation.dart';
3 import 'package:mobx/mobx.dart';
4 import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
lib/yat/yat_record.dart deleted
-31
@@ -1,31 +0,0 @@
1 -import 'dart:convert';
2 -import 'package:cake_wallet/yat/yat_exception.dart';
3 -import 'package:http/http.dart';
4 -
5 -Future<List<String>> fetchYatAddress(String emojiId, String ticker) async {
6 - const _requestURL = 'https://a.y.at/emoji_id/';
7 - final url = _requestURL + emojiId + '/' + ticker.toUpperCase();
8 - final response = await get(url);
9 -
10 - if (response.statusCode != 200) {
11 - throw YatException(text: response.body.toString());
12 - }
13 -
14 - final responseJSON = json.decode(response.body) as Map<String, dynamic>;
15 - final result = responseJSON['result'] as List<dynamic>;
16 -
17 - if (result?.isEmpty ?? true) {
18 - return [];
19 - }
20 -
21 - final List<String> addresses = [];
22 -
23 - for (var elem in result) {
24 - final yatAddress = elem['data'] as String;
25 - if (yatAddress?.isNotEmpty ?? false) {
26 - addresses.add(yatAddress);
27 - }
28 - }
29 -
30 - return addresses;
31 -}
\ No newline at end of file
pubspec.lock
+7
@@ -15,6 +15,13 @@ packages:
15 url: "https://pub.dartlang.org"
16 source: hosted
17 version: "0.41.2"
18 + animate_do:
19 + dependency: "direct main"
20 + description:
21 + name: animate_do
22 + url: "https://pub.dartlang.org"
23 + source: hosted
24 + version: "2.0.0"
25 archive:
26 dependency: "direct main"
27 description:
pubspec.yaml
+1
@@ -59,6 +59,7 @@ dependencies:
59 flutter_spinkit: ^5.0.0
60 uni_links: ^0.4.0
61 lottie: ^0.7.0
62 + animate_do: ^2.0.0
63
64 # The following adds the Cupertino Icons font to your application.
65 # Use with the CupertinoIcons class for iOS style icons.