From 757d48f793126b59103ea9d5505df072019fd300 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Wed, 23 Sep 2020 22:31:39 +0300 Subject: [PATCH 1/8] CAKE-45 | created wallet menu item and wallet menu alert; deleted wallet tile; added menuItems to wallet menu; reworked wallet list page (removed slider menu on wallet list screen and used alert instead of this) --- .../screens/wallet_list/wallet_list_page.dart | 151 ++++++++---------- lib/src/screens/wallet_list/wallet_menu.dart | 106 ++++-------- .../screens/wallet_list/wallet_menu_item.dart | 16 ++ .../widgets/wallet_menu_alert.dart | 112 +++++++++++++ .../wallet_list/widgets/wallet_tile.dart | 151 ------------------ 5 files changed, 230 insertions(+), 306 deletions(-) create mode 100644 lib/src/screens/wallet_list/wallet_menu_item.dart create mode 100644 lib/src/screens/wallet_list/widgets/wallet_menu_alert.dart delete mode 100644 lib/src/screens/wallet_list/widgets/wallet_tile.dart diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 0e743e1c9..4e78642c7 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -1,8 +1,8 @@ +import 'package:cake_wallet/src/screens/wallet_list/widgets/wallet_menu_alert.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; @@ -10,7 +10,6 @@ import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/screens/wallet_list/wallet_menu.dart'; -import 'package:cake_wallet/src/screens/wallet_list/widgets/wallet_tile.dart'; class WalletListPage extends BasePage { WalletListPage({this.walletListViewModel}); @@ -65,95 +64,83 @@ class WalletListBodyState extends State { itemCount: widget.walletListViewModel.wallets.length, itemBuilder: (__, index) { final wallet = widget.walletListViewModel.wallets[index]; - final screenWidth = MediaQuery.of(context).size.width; final walletMenu = WalletMenu(context, widget.walletListViewModel); final items = walletMenu.generateItemsForWalletMenu(wallet.isCurrent); - final colors = walletMenu - .generateColorsForWalletMenu(wallet.isCurrent); - final images = walletMenu - .generateImagesForWalletMenu(wallet.isCurrent); + final currentColor = wallet.isCurrent + ? Theme.of(context).accentTextTheme.subtitle.decorationColor + : Theme.of(context).backgroundColor; - return Container( - height: tileHeight, - width: double.infinity, - child: CustomScrollView( - scrollDirection: Axis.horizontal, - controller: scrollController, - slivers: [ - SliverPersistentHeader( - pinned: false, - floating: true, - delegate: WalletTile( - min: screenWidth - 170, - max: screenWidth, - image: _imageFor(type: wallet.type), - walletName: wallet.name, - walletAddress: '', //shortAddress, - isCurrent: wallet.isCurrent), - ), - SliverList( - delegate: - SliverChildBuilderDelegate((context, index) { - final item = items[index]; - final image = images[index]; - final firstColor = colors[index*2]; - final secondColor = colors[index*2 + 1]; - - final radius = index == 0 ? 10.0 : 0.0; - - return GestureDetector( - onTap: () { - scrollController.animateTo(0.0, - duration: Duration(milliseconds: 500), - curve: Curves.fastOutSlowIn); - walletMenu.action( - walletMenu.listItems.indexOf(item), - wallet, - wallet.isCurrent); - }, - child: Container( - height: tileHeight, - width: 80, - color: Theme.of(context).backgroundColor, + return GestureDetector( + onTap: () { + showDialog( + context: context, + builder: (dialogContext) { + return WalletMenuAlert( + wallet: wallet, + walletMenu: walletMenu, + items: items); + } + ); + }, + child: Container( + height: tileHeight, + width: double.infinity, + child: Row( + children: [ + Container( + height: tileHeight, + width: 4, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(4), + bottomRight: Radius.circular(4)), + color: currentColor + ), + ), + Expanded( child: Container( - padding: EdgeInsets.only(left: 5, right: 5), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(radius), - bottomLeft: Radius.circular(radius)), - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - firstColor, - secondColor - ] + height: tileHeight, + padding: EdgeInsets.only(left: 20, right: 20), + color: Theme.of(context).backgroundColor, + alignment: Alignment.centerLeft, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + _imageFor(type: wallet.type), + SizedBox(width: 10), + Text( + wallet.name, + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w600, + color: Theme.of(context).primaryTextTheme.title.color + ), ) - ), - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - image, - SizedBox(height: 2), - Text( - item, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 7, - fontWeight: FontWeight.w500, - color: Colors.white), - ) - ], - ), + ], ), ), + ), + Container( + height: tileHeight, + width: 10, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(10), + bottomLeft: Radius.circular(10)), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Theme.of(context).accentTextTheme.headline.color, + Theme.of(context).accentTextTheme.headline.backgroundColor + ] + ) ), - ); - }, childCount: items.length)) - ], - ), + ), + ], + ), + ) ); }), ), diff --git a/lib/src/screens/wallet_list/wallet_menu.dart b/lib/src/screens/wallet_list/wallet_menu.dart index a508541bd..e5745ccd8 100644 --- a/lib/src/screens/wallet_list/wallet_menu.dart +++ b/lib/src/screens/wallet_list/wallet_menu.dart @@ -1,10 +1,9 @@ +import 'package:cake_wallet/src/screens/wallet_list/wallet_menu_item.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; -import 'package:provider/provider.dart'; import 'package:cake_wallet/generated/i18n.dart'; -// import 'package:cake_wallet/src/stores/wallet_list/wallet_list_store.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cake_wallet/src/screens/auth/auth_page.dart'; import 'package:cake_wallet/palette.dart'; @@ -15,85 +14,46 @@ class WalletMenu { final WalletListViewModel walletListViewModel; final BuildContext context; - final List listItems = [ - S.current.wallet_list_load_wallet, - S.current.show_seed, - S.current.remove, - S.current.rescan + final List menuItems = [ + WalletMenuItem( + title: S.current.wallet_list_load_wallet, + firstGradientColor: Palette.cornflower, + secondGradientColor: Palette.royalBlue, + image: Image.asset('assets/images/load.png', + height: 24, width: 24, color: Colors.white)), + WalletMenuItem( + title: S.current.show_seed, + firstGradientColor: Palette.moderateOrangeYellow, + secondGradientColor: Palette.moderateOrange, + image: Image.asset('assets/images/eye_action.png', + height: 24, width: 24, color: Colors.white)), + WalletMenuItem( + title: S.current.remove, + firstGradientColor: Palette.lightRed, + secondGradientColor: Palette.persianRed, + image: Image.asset('assets/images/trash.png', + height: 24, width: 24, color: Colors.white)), + WalletMenuItem( + title: S.current.rescan, + firstGradientColor: Palette.shineGreen, + secondGradientColor: Palette.moderateGreen, + image: Image.asset('assets/images/scanner.png', + height: 24, width: 24, color: Colors.white)) ]; - final List firstColors = [ - Palette.cornflower, - Palette.moderateOrangeYellow, - Palette.lightRed, - Palette.shineGreen - ]; + List generateItemsForWalletMenu(bool isCurrentWallet) { + final items = List(); - final List secondColors = [ - Palette.royalBlue, - Palette.moderateOrange, - Palette.persianRed, - Palette.moderateGreen - ]; - - final List listImages = [ - Image.asset('assets/images/load.png', - height: 24, width: 24, color: Colors.white), - Image.asset('assets/images/eye_action.png', - height: 24, width: 24, color: Colors.white), - Image.asset('assets/images/trash.png', - height: 24, width: 24, color: Colors.white), - Image.asset('assets/images/scanner.png', - height: 24, width: 24, color: Colors.white) - ]; - - List generateItemsForWalletMenu(bool isCurrentWallet) { - final items = List(); - - if (!isCurrentWallet) items.add(listItems[0]); - if (isCurrentWallet) items.add(listItems[1]); - if (!isCurrentWallet) items.add(listItems[2]); - if (isCurrentWallet) items.add(listItems[3]); + if (!isCurrentWallet) items.add(menuItems[0]); + if (isCurrentWallet) items.add(menuItems[1]); + if (!isCurrentWallet) items.add(menuItems[2]); + if (isCurrentWallet) items.add(menuItems[3]); return items; } - List generateColorsForWalletMenu(bool isCurrentWallet) { - final colors = []; - - if (!isCurrentWallet) { - colors.add(firstColors[0]); - colors.add(secondColors[0]); - } - if (isCurrentWallet) { - colors.add(firstColors[1]); - colors.add(secondColors[1]); - } - if (!isCurrentWallet) { - colors.add(firstColors[2]); - colors.add(secondColors[2]); - } - if (isCurrentWallet) { - colors.add(firstColors[3]); - colors.add(secondColors[3]); - } - - return colors; - } - - List generateImagesForWalletMenu(bool isCurrentWallet) { - final images = []; - - if (!isCurrentWallet) images.add(listImages[0]); - if (isCurrentWallet) images.add(listImages[1]); - if (!isCurrentWallet) images.add(listImages[2]); - if (isCurrentWallet) images.add(listImages[3]); - - return images; - } - Future action( - int index, WalletListItem wallet, bool isCurrentWallet) async { + int index, WalletListItem wallet) async { switch (index) { case 0: await Navigator.of(context).pushNamed(Routes.auth, arguments: diff --git a/lib/src/screens/wallet_list/wallet_menu_item.dart b/lib/src/screens/wallet_list/wallet_menu_item.dart new file mode 100644 index 000000000..55e7189c5 --- /dev/null +++ b/lib/src/screens/wallet_list/wallet_menu_item.dart @@ -0,0 +1,16 @@ +import 'dart:ui'; +import 'package:flutter/cupertino.dart'; + +class WalletMenuItem { + WalletMenuItem({ + @required this.title, + @required this.firstGradientColor, + @required this.secondGradientColor, + @required this.image + }); + + final String title; + final Color firstGradientColor; + final Color secondGradientColor; + final Image image; +} \ No newline at end of file diff --git a/lib/src/screens/wallet_list/widgets/wallet_menu_alert.dart b/lib/src/screens/wallet_list/widgets/wallet_menu_alert.dart new file mode 100644 index 000000000..cafb27516 --- /dev/null +++ b/lib/src/screens/wallet_list/widgets/wallet_menu_alert.dart @@ -0,0 +1,112 @@ +import 'dart:ui'; +import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/src/screens/wallet_list/wallet_menu.dart'; +import 'package:cake_wallet/src/screens/wallet_list/wallet_menu_item.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/src/widgets/alert_background.dart'; +import 'package:cake_wallet/src/widgets/alert_close_button.dart'; + +class WalletMenuAlert extends StatelessWidget { + WalletMenuAlert({ + @required this.wallet, + @required this.walletMenu, + @required this.items + }); + + final WalletListItem wallet; + final WalletMenu walletMenu; + final List items; + final closeButton = Image.asset('assets/images/close.png', + color: Palette.darkBlueCraiola, + ); + + @override + Widget build(BuildContext context) { + return AlertBackground( + child: Stack( + alignment: Alignment.center, + children: [ + Padding( + padding: EdgeInsets.only( + left: 24, + right: 24, + ), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(14)), + child: Container( + color: Theme.of(context).textTheme.body2.decorationColor, + padding: EdgeInsets.only(left: 24), + child: ListView.separated( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: items.length, + separatorBuilder: (context, _) => Container( + height: 1, + color: Theme.of(context).accentTextTheme.subhead.backgroundColor, + ), + itemBuilder: (_, index) { + final item = items[index]; + + return GestureDetector( + onTap: () { + Navigator.of(context).pop(); + walletMenu.action( + walletMenu.menuItems.indexOf(item), + wallet); + }, + child: Container( + padding: EdgeInsets.only(top: 12, bottom: 12), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + height: 56, + width: 56, + decoration: BoxDecoration( + borderRadius: BorderRadius.all( + Radius.circular(12)), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + item.firstGradientColor, + item.secondGradientColor + ] + ) + ), + child: Center( + child: item.image, + ), + ), + SizedBox(width: 12), + Expanded( + child: Text( + item.title, + style: TextStyle( + color: Theme.of(context).primaryTextTheme.title.color, + fontSize: 18, + fontFamily: 'Poppins', + fontWeight: FontWeight.w500, + decoration: TextDecoration.none + ), + ) + ) + ], + ), + ), + ); + }, + ), + ), + ), + ), + AlertCloseButton(image: closeButton) + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/screens/wallet_list/widgets/wallet_tile.dart b/lib/src/screens/wallet_list/widgets/wallet_tile.dart deleted file mode 100644 index 4e4165341..000000000 --- a/lib/src/screens/wallet_list/widgets/wallet_tile.dart +++ /dev/null @@ -1,151 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; - -class WalletTile extends SliverPersistentHeaderDelegate { - WalletTile({ - @required this.min, - @required this.max, - @required this.image, - @required this.walletName, - @required this.walletAddress, - @required this.isCurrent - }); - - final double min; - final double max; - final Image image; - final String walletName; - final String walletAddress; - final bool isCurrent; - final double tileHeight = 60; - - @override - Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) { - var opacity = 1 - shrinkOffset / (max - min); - opacity = opacity >= 0 ? opacity : 0; - - var panelWidth = 10 * opacity; - panelWidth = panelWidth < 10 ? 0 : 10; - - final currentColor = isCurrent - ? Theme.of(context).accentTextTheme.subtitle.decorationColor - : Theme.of(context).backgroundColor; - - return Stack( - fit: StackFit.expand, - overflow: Overflow.visible, - children: [ - Positioned( - top: 0, - right: max - 4, - child: Container( - height: tileHeight, - width: 4, - decoration: BoxDecoration( - borderRadius: BorderRadius.only(topRight: Radius.circular(4), bottomRight: Radius.circular(4)), - color: currentColor - ), - ), - ), - Positioned( - top: 0, - right: 10, - child: Container( - height: tileHeight, - width: max - 14, - padding: EdgeInsets.only(left: 20, right: 20), - color: Theme.of(context).backgroundColor, - alignment: Alignment.centerLeft, - child: Row( - //mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - image, - SizedBox(width: 10), - Text( - walletName, - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.w600, - color: Theme.of(context).primaryTextTheme.title.color - ), - ) - ], - ), - /*Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - image, - SizedBox(width: 10), - Text( - walletName, - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.bold, - color: Theme.of(context).primaryTextTheme.title.color - ), - ) - ], - ), - isCurrent ? SizedBox(height: 5) : Offstage(), - isCurrent - ? Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox(width: 34), - Text( - walletAddress, - style: TextStyle( - fontSize: 12, - color: Theme.of(context).primaryTextTheme.caption.color - ), - ) - ], - ) - : Offstage() - ], - ),*/ - ), - ), - Positioned( - top: 0, - right: 0, - child: Opacity( - opacity: opacity, - child: Container( - height: tileHeight, - width: panelWidth, - decoration: BoxDecoration( - borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)), - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Theme.of(context).accentTextTheme.headline.color, - Theme.of(context).accentTextTheme.headline.backgroundColor - ] - ) - ), - ), - ) - ), - ], - ); - } - - @override - double get maxExtent => max; - - @override - double get minExtent => min; - - @override - bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true; - -} \ No newline at end of file From 83ef5c179db0352516caff7be118806fcc9ca422 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Thu, 24 Sep 2020 20:46:48 +0300 Subject: [PATCH 2/8] CAKE-45 | changed wallet list tile (wallet list page); changed wallet menu tile (wallet menu alert) --- .../screens/wallet_list/wallet_list_page.dart | 17 ----------------- .../wallet_list/widgets/wallet_menu_alert.dart | 8 ++++---- 2 files changed, 4 insertions(+), 21 deletions(-) diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 4e78642c7..694c338e4 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -121,23 +121,6 @@ class WalletListBodyState extends State { ), ), ), - Container( - height: tileHeight, - width: 10, - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), - bottomLeft: Radius.circular(10)), - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Theme.of(context).accentTextTheme.headline.color, - Theme.of(context).accentTextTheme.headline.backgroundColor - ] - ) - ), - ), ], ), ) diff --git a/lib/src/screens/wallet_list/widgets/wallet_menu_alert.dart b/lib/src/screens/wallet_list/widgets/wallet_menu_alert.dart index cafb27516..e5f68597f 100644 --- a/lib/src/screens/wallet_list/widgets/wallet_menu_alert.dart +++ b/lib/src/screens/wallet_list/widgets/wallet_menu_alert.dart @@ -57,18 +57,18 @@ class WalletMenuAlert extends StatelessWidget { wallet); }, child: Container( - padding: EdgeInsets.only(top: 12, bottom: 12), + height: 60, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ Container( - height: 56, - width: 56, + height: 32, + width: 32, decoration: BoxDecoration( borderRadius: BorderRadius.all( - Radius.circular(12)), + Radius.circular(4)), gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, From 360e04cc4ccaae197ba7e75c96d0ff1758dd7c1f Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Thu, 24 Sep 2020 21:58:07 +0300 Subject: [PATCH 3/8] CAKE-53 | added dai to cryptocurrencies list; added dai to currency picker; added dai logo to contact list page; added address validator for dai --- assets/images/dai.png | Bin 0 -> 73562 bytes lib/core/address_validator.dart | 4 ++ lib/entities/crypto_currency.dart | 40 ++++++++++-------- .../morphtoken_exchange_provider.dart | 1 - .../screens/contact/contact_list_page.dart | 3 ++ .../exchange/widgets/currency_picker.dart | 8 +--- pubspec.lock | 31 ++++++-------- 7 files changed, 43 insertions(+), 44 deletions(-) create mode 100644 assets/images/dai.png diff --git a/assets/images/dai.png b/assets/images/dai.png new file mode 100644 index 0000000000000000000000000000000000000000..698ffc48eb4142703b795be34e3965c7cba38604 GIT binary patch literal 73562 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 jCTQ-|$AkY3ei4e with Serializable { CryptoCurrency.bch, CryptoCurrency.bnb, CryptoCurrency.btc, + CryptoCurrency.dai, CryptoCurrency.dash, CryptoCurrency.eos, CryptoCurrency.eth, @@ -29,15 +30,16 @@ class CryptoCurrency extends EnumerableItem with Serializable { static const bch = CryptoCurrency(title: 'BCH', raw: 2); static const bnb = CryptoCurrency(title: 'BNB', raw: 3); static const btc = CryptoCurrency(title: 'BTC', raw: 4); - static const dash = CryptoCurrency(title: 'DASH', raw: 5); - static const eos = CryptoCurrency(title: 'EOS', raw: 6); - static const eth = CryptoCurrency(title: 'ETH', raw: 7); - static const ltc = CryptoCurrency(title: 'LTC', raw: 8); - static const nano = CryptoCurrency(title: 'NANO', raw: 9); - static const trx = CryptoCurrency(title: 'TRX', raw: 10); - static const usdt = CryptoCurrency(title: 'USDT', raw: 11); - static const xlm = CryptoCurrency(title: 'XLM', raw: 12); - static const xrp = CryptoCurrency(title: 'XRP', raw: 13); + static const dai = CryptoCurrency(title: 'DAI', raw: 5); + static const dash = CryptoCurrency(title: 'DASH', raw: 6); + static const eos = CryptoCurrency(title: 'EOS', raw: 7); + static const eth = CryptoCurrency(title: 'ETH', raw: 8); + static const ltc = CryptoCurrency(title: 'LTC', raw: 9); + static const nano = CryptoCurrency(title: 'NANO', raw: 10); + static const trx = CryptoCurrency(title: 'TRX', raw: 11); + static const usdt = CryptoCurrency(title: 'USDT', raw: 12); + static const xlm = CryptoCurrency(title: 'XLM', raw: 13); + static const xrp = CryptoCurrency(title: 'XRP', raw: 14); static CryptoCurrency deserialize({int raw}) { switch (raw) { @@ -52,22 +54,24 @@ class CryptoCurrency extends EnumerableItem with Serializable { case 4: return CryptoCurrency.btc; case 5: - return CryptoCurrency.dash; + return CryptoCurrency.dai; case 6: - return CryptoCurrency.eos; + return CryptoCurrency.dash; case 7: - return CryptoCurrency.eth; + return CryptoCurrency.eos; case 8: - return CryptoCurrency.ltc; + return CryptoCurrency.eth; case 9: - return CryptoCurrency.nano; + return CryptoCurrency.ltc; case 10: - return CryptoCurrency.trx; + return CryptoCurrency.nano; case 11: - return CryptoCurrency.usdt; + return CryptoCurrency.trx; case 12: - return CryptoCurrency.xlm; + return CryptoCurrency.usdt; case 13: + return CryptoCurrency.xlm; + case 14: return CryptoCurrency.xrp; default: return null; @@ -86,6 +90,8 @@ class CryptoCurrency extends EnumerableItem with Serializable { return CryptoCurrency.bnb; case 'btc': return CryptoCurrency.btc; + case 'dai': + return CryptoCurrency.dai; case 'dash': return CryptoCurrency.dash; case 'eos': diff --git a/lib/exchange/morphtoken/morphtoken_exchange_provider.dart b/lib/exchange/morphtoken/morphtoken_exchange_provider.dart index ec632cccb..6c4aade37 100644 --- a/lib/exchange/morphtoken/morphtoken_exchange_provider.dart +++ b/lib/exchange/morphtoken/morphtoken_exchange_provider.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'package:cake_wallet/core/amount_converter.dart'; -import 'package:cake_wallet/monero/monero_amount_format.dart'; import 'package:hive/hive.dart'; import 'package:cake_wallet/exchange/trade_not_found_exeption.dart'; import 'package:flutter/foundation.dart'; diff --git a/lib/src/screens/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index dc1d14b72..988246656 100644 --- a/lib/src/screens/contact/contact_list_page.dart +++ b/lib/src/screens/contact/contact_list_page.dart @@ -211,6 +211,9 @@ class ContactListPage extends BasePage { case CryptoCurrency.btc: image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24); break; + case CryptoCurrency.dai: + image = Image.asset('assets/images/dai.png', height: 24, width: 24); + break; case CryptoCurrency.dash: image = Image.asset('assets/images/dash.png', height: 24, width: 24); break; diff --git a/lib/src/screens/exchange/widgets/currency_picker.dart b/lib/src/screens/exchange/widgets/currency_picker.dart index 21ba5ce31..668f99e77 100644 --- a/lib/src/screens/exchange/widgets/currency_picker.dart +++ b/lib/src/screens/exchange/widgets/currency_picker.dart @@ -62,13 +62,7 @@ class CurrencyPicker extends StatelessWidget { physics: const NeverScrollableScrollPhysics(), crossAxisSpacing: 1, mainAxisSpacing: 1, - children: List.generate(15, (index) { - - if (index == 14) { - return Container( - color: Theme.of(context).accentTextTheme.title.color, - ); - } + children: List.generate(items.length, (index) { final item = items[index]; final isItemSelected = index == selectedAtIndex; diff --git a/pubspec.lock b/pubspec.lock index 9b14f0555..df34727ee 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,7 +42,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.2" + version: "2.4.1" auto_size_text: dependency: "direct main" description: @@ -210,7 +210,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.13" + version: "1.14.12" connectivity: dependency: "direct main" description: @@ -252,7 +252,7 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.5" + version: "2.1.4" csslib: dependency: transitive description: @@ -330,13 +330,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" - fake_async: - dependency: transitive - description: - name: fake_async - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" ffi: dependency: transitive description: @@ -505,7 +498,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.17" + version: "2.1.12" intl: dependency: "direct main" description: @@ -554,7 +547,7 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.8" + version: "0.12.6" meta: dependency: transitive description: @@ -624,7 +617,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.6.4" path_drawing: dependency: transitive description: @@ -687,7 +680,7 @@ packages: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "3.0.4" + version: "2.4.0" platform: dependency: transitive description: @@ -874,7 +867,7 @@ packages: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.5" + version: "1.9.3" stream_channel: dependency: transitive description: @@ -909,7 +902,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.17" + version: "0.2.15" time: dependency: transitive description: @@ -930,7 +923,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.1.6" url_launcher: dependency: "direct main" description: @@ -1021,7 +1014,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "4.5.1" + version: "3.6.1" yaml: dependency: "direct main" description: @@ -1030,5 +1023,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.9.0-14.0.dev <3.0.0" + dart: ">=2.7.0 <3.0.0" flutter: ">=1.12.13+hotfix.5 <2.0.0" From 34aa7fdc7667bc53fdc12ef21e562eb87d7c70c2 Mon Sep 17 00:00:00 2001 From: M Date: Fri, 25 Sep 2020 18:32:44 +0300 Subject: [PATCH 4/8] Fixes pre release. --- lib/monero/monero_wallet.dart | 2 +- .../screens/contact/contact_list_page.dart | 5 +- lib/src/screens/contact/contact_page.dart | 5 +- lib/src/screens/dashboard/wallet_menu.dart | 3 +- .../screens/dashboard/widgets/header_row.dart | 3 +- .../screens/disclaimer/disclaimer_page.dart | 3 +- lib/src/screens/exchange/exchange_page.dart | 5 +- .../exchange/widgets/exchange_card.dart | 3 +- .../widgets/present_provider_picker.dart | 3 +- .../exchange_trade/exchange_trade_page.dart | 9 +- .../screens/new_wallet/new_wallet_page.dart | 3 +- lib/src/screens/nodes/nodes_list_page.dart | 7 +- lib/src/screens/pin_code/pin_code_widget.dart | 2 +- lib/src/screens/receive/receive_page.dart | 85 ++++++++++--------- .../restore_wallet_from_keys_page.dart | 2 +- .../restore_wallet_from_seed_details.dart | 3 +- lib/src/screens/seed/wallet_seed_page.dart | 7 +- lib/src/screens/send/send_page.dart | 13 +-- lib/src/screens/settings/change_language.dart | 2 +- .../widgets/settings_picker_cell.dart | 3 +- .../setup_pin_code/setup_pin_code.dart | 7 +- lib/src/screens/wallet_list/wallet_menu.dart | 3 +- lib/src/widgets/seed_language_selector.dart | 3 +- lib/utils/show_pop_up.dart | 22 +++++ .../wallet_list/wallet_list_view_model.dart | 2 +- 25 files changed, 124 insertions(+), 81 deletions(-) create mode 100644 lib/utils/show_pop_up.dart diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index 6f0ea6d79..4a053b788 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -191,7 +191,7 @@ abstract class MoneroWalletBase extends WalletBase with Store { @override Future rescan({int height}) async { - // FIXME: Unimplemented + monero_wallet.rescanBlockchainAsync(); } void _setListeners() { diff --git a/lib/src/screens/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index dc1d14b72..8cae22f85 100644 --- a/lib/src/screens/contact/contact_list_page.dart +++ b/lib/src/screens/contact/contact_list_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; @@ -246,7 +247,7 @@ class ContactListPage extends BasePage { } Future showAlertDialog(BuildContext context) async { - return await showDialog( + return await showPopUp( context: context, builder: (BuildContext context) { return AlertWithTwoActions( @@ -261,7 +262,7 @@ class ContactListPage extends BasePage { Future showNameAndAddressDialog( BuildContext context, String name, String address) async { - return await showDialog( + return await showPopUp( context: context, builder: (BuildContext context) { return AlertWithTwoActions( diff --git a/lib/src/screens/contact/contact_page.dart b/lib/src/screens/contact/contact_page.dart index e73760ce1..a65a86e29 100644 --- a/lib/src/screens/contact/contact_page.dart +++ b/lib/src/screens/contact/contact_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -138,7 +139,7 @@ class ContactPage extends BasePage { } void _presentCurrencyPicker(BuildContext context) { - showDialog( + showPopUp( builder: (_) => CurrencyPicker( selectedAtIndex: contactViewModel.currencies.indexOf(contactViewModel.currency), @@ -150,7 +151,7 @@ class ContactPage extends BasePage { } void _onContactSavingFailure(BuildContext context, String error) { - showDialog( + showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( diff --git a/lib/src/screens/dashboard/wallet_menu.dart b/lib/src/screens/dashboard/wallet_menu.dart index 1eb3864d0..d06068c89 100644 --- a/lib/src/screens/dashboard/wallet_menu.dart +++ b/lib/src/screens/dashboard/wallet_menu.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; import 'package:provider/provider.dart'; @@ -72,7 +73,7 @@ class WalletMenu { } Future _presentReconnectAlert(BuildContext context) async { - await showDialog( + await showPopUp( context: context, builder: (BuildContext context) { return AlertWithTwoActions( diff --git a/lib/src/screens/dashboard/widgets/header_row.dart b/lib/src/screens/dashboard/widgets/header_row.dart index a878ff11f..5b63f7a0e 100644 --- a/lib/src/screens/dashboard/widgets/header_row.dart +++ b/lib/src/screens/dashboard/widgets/header_row.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/filter_widget.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; @@ -31,7 +32,7 @@ class HeaderRow extends StatelessWidget { ), GestureDetector( onTap: () { - showDialog( + showPopUp( context: context, builder: (context) => FilterWidget(dashboardViewModel: dashboardViewModel) ); diff --git a/lib/src/screens/disclaimer/disclaimer_page.dart b/lib/src/screens/disclaimer/disclaimer_page.dart index fde5677e7..b80a7a1fe 100644 --- a/lib/src/screens/disclaimer/disclaimer_page.dart +++ b/lib/src/screens/disclaimer/disclaimer_page.dart @@ -1,5 +1,6 @@ import 'dart:ui'; import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; @@ -51,7 +52,7 @@ class DisclaimerBodyState extends State { } Future _showAlertDialog(BuildContext context) async { - await showDialog( + await showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 1cabcc448..a602fc995 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/template_tile.dart'; import 'package:cake_wallet/src/widgets/trail_button.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -293,7 +294,7 @@ class ExchangePage extends BasePage { exchangeViewModel, template); }, onRemove: () { - showDialog( + showPopUp( context: context, builder: (dialogContext) { return AlertWithTwoActions( @@ -492,7 +493,7 @@ class ExchangePage extends BasePage { // reaction((_) => exchangeViewModel.tradeState, (ExchangeTradeState state) { // if (state is TradeIsCreatedFailure) { // WidgetsBinding.instance.addPostFrameCallback((_) { - // showDialog( + // showPopUp( // context: context, // builder: (BuildContext context) { // return AlertWithOneAction( diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index 1836ec912..28c7980f8 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -303,7 +304,7 @@ class ExchangeCardState extends State { } void _presentPicker(BuildContext context) { - showDialog( + showPopUp( builder: (_) => CurrencyPicker( selectedAtIndex: widget.currencies.indexOf(_selectedCurrency), items: widget.currencies, diff --git a/lib/src/screens/exchange/widgets/present_provider_picker.dart b/lib/src/screens/exchange/widgets/present_provider_picker.dart index b8154632e..f4cc7abf1 100644 --- a/lib/src/screens/exchange/widgets/present_provider_picker.dart +++ b/lib/src/screens/exchange/widgets/present_provider_picker.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/exchange/exchange_provider.dart'; @@ -72,7 +73,7 @@ class PresentProviderPicker extends StatelessWidget { } } - showDialog( + showPopUp( builder: (_) => Picker( items: items, images: images, diff --git a/lib/src/screens/exchange_trade/exchange_trade_page.dart b/lib/src/screens/exchange_trade/exchange_trade_page.dart index 545492e09..945bb298d 100644 --- a/lib/src/screens/exchange_trade/exchange_trade_page.dart +++ b/lib/src/screens/exchange_trade/exchange_trade_page.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart'; import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart'; import 'package:cake_wallet/src/widgets/standart_list_row.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart'; import 'package:mobx/mobx.dart'; import 'package:provider/provider.dart'; @@ -37,7 +38,7 @@ void showInformation(ExchangeTradeViewModel exchangeTradeViewModel, BuildContext : S.current.exchange_result_description( trade.amount ?? fetchingLabel, trade.from.toString()); - showDialog( + showPopUp( context: context, builder: (_) => InformationPage(information: information) ); @@ -255,7 +256,7 @@ class ExchangeTradeState extends State { reaction((_) => sendStore.state, (SendingState state) { if (state is SendingFailed) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( + showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( @@ -270,7 +271,7 @@ class ExchangeTradeState extends State { if (state is TransactionCreatedSuccessfully) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( + showPopUp( context: context, builder: (BuildContext context) { return AlertWithTwoActions( @@ -292,7 +293,7 @@ class ExchangeTradeState extends State { if (state is TransactionCommitted) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( + showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index 9512a3753..9300b9c3a 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -1,6 +1,7 @@ import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter/material.dart'; @@ -60,7 +61,7 @@ class _WalletNameFormState extends State { if (state is FailureState) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( + showPopUp( context: context, builder: (_) { return AlertWithOneAction( diff --git a/lib/src/screens/nodes/nodes_list_page.dart b/lib/src/screens/nodes/nodes_list_page.dart index dd713d430..84e87a3a5 100644 --- a/lib/src/screens/nodes/nodes_list_page.dart +++ b/lib/src/screens/nodes/nodes_list_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -29,7 +30,7 @@ class NodeListPage extends BasePage { minWidth: double.minPositive, child: FlatButton( onPressed: () async { - await showDialog( + await showPopUp( context: context, builder: (BuildContext context) { return AlertWithTwoActions( @@ -83,7 +84,7 @@ class NodeListPage extends BasePage { return; } - await showDialog( + await showPopUp( context: context, builder: (BuildContext context) { return AlertDialog( @@ -110,7 +111,7 @@ class NodeListPage extends BasePage { final dismissibleRow = Dismissible( key: Key('${node.keyIndex}'), confirmDismiss: (direction) async { - return await showDialog( + return await showPopUp( context: context, builder: (BuildContext context) { return AlertWithTwoActions( diff --git a/lib/src/screens/pin_code/pin_code_widget.dart b/lib/src/screens/pin_code/pin_code_widget.dart index e4e17dae3..6cb0a9714 100644 --- a/lib/src/screens/pin_code/pin_code_widget.dart +++ b/lib/src/screens/pin_code/pin_code_widget.dart @@ -266,7 +266,7 @@ class PinCodeState extends State { return; } - pin.substring(0, pin.length - 1); + setState(() => pin = pin.substring(0, pin.length - 1)); } String _changePinLengthText() { diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index fbae17324..b7b06fcdb 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -34,21 +35,19 @@ class ReceivePage extends BasePage { @override Widget Function(BuildContext, Widget) get rootWrapper => - (BuildContext context, Widget scaffold) => Container( + (BuildContext context, Widget scaffold) => Container( decoration: BoxDecoration( gradient: LinearGradient(colors: [ - Theme.of(context).accentColor, - Theme.of(context).scaffoldBackgroundColor, - Theme.of(context).primaryColor, - ], - begin: Alignment.topRight, - end: Alignment.bottomLeft)), + Theme.of(context).accentColor, + Theme.of(context).scaffoldBackgroundColor, + Theme.of(context).primaryColor, + ], begin: Alignment.topRight, end: Alignment.bottomLeft)), child: scaffold); @override Widget trailing(BuildContext context) { - final shareImage = Image.asset('assets/images/share.png', - color: Colors.white); + final shareImage = + Image.asset('assets/images/share.png', color: Colors.white); return SizedBox( height: 20.0, @@ -56,12 +55,12 @@ class ReceivePage extends BasePage { child: ButtonTheme( minWidth: double.minPositive, child: FlatButton( - highlightColor: Colors.transparent, - splashColor: Colors.transparent, - padding: EdgeInsets.all(0), - onPressed: () => Share.text(S.current.share_address, - addressListViewModel.address.address, 'text/plain'), - child: shareImage), + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + padding: EdgeInsets.all(0), + onPressed: () => Share.text(S.current.share_address, + addressListViewModel.address.address, 'text/plain'), + child: shareImage), ), ); } @@ -80,10 +79,9 @@ class ReceivePage extends BasePage { ), Observer( builder: (_) => ListView.separated( - separatorBuilder: (context, _) => - Container( - height: 1, - color: Theme.of(context).dividerColor), + padding: EdgeInsets.all(0), + separatorBuilder: (context, _) => Container( + height: 1, color: Theme.of(context).dividerColor), shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: addressListViewModel.items.length, @@ -93,9 +91,10 @@ class ReceivePage extends BasePage { if (item is WalletAccountListHeader) { cell = HeaderTile( - onTap: () async => await showDialog( + onTap: () async => await showPopUp( context: context, - builder: (_) => getIt.get()), + builder: (_) => + getIt.get()), title: addressListViewModel.accountLabel, icon: Icon( Icons.arrow_forward_ios, @@ -117,27 +116,31 @@ class ReceivePage extends BasePage { } if (item is WalletAddressListItem) { - cell = Observer( - builder: (_) { - final isCurrent = item.address == - addressListViewModel.address.address; - final backgroundColor = isCurrent - ? Theme.of(context).textTheme.display3.decorationColor - : Theme.of(context).textTheme.display2.decorationColor; - final textColor = isCurrent - ? Theme.of(context).textTheme.display3.color - : Theme.of(context).textTheme.display2.color; + cell = Observer(builder: (_) { + final isCurrent = item.address == + addressListViewModel.address.address; + final backgroundColor = isCurrent + ? Theme.of(context) + .textTheme + .display3 + .decorationColor + : Theme.of(context) + .textTheme + .display2 + .decorationColor; + final textColor = isCurrent + ? Theme.of(context).textTheme.display3.color + : Theme.of(context).textTheme.display2.color; - return AddressCell.fromItem(item, - isCurrent: isCurrent, - backgroundColor: backgroundColor, - textColor: textColor, - onTap: (_) => addressListViewModel.address = item, - onEdit: () => Navigator.of(context).pushNamed( - Routes.newSubaddress, - arguments: item)); - } - ); + return AddressCell.fromItem(item, + isCurrent: isCurrent, + backgroundColor: backgroundColor, + textColor: textColor, + onTap: (_) => addressListViewModel.address = item, + onEdit: () => Navigator.of(context).pushNamed( + Routes.newSubaddress, + arguments: item)); + }); } return index != 0 diff --git a/lib/src/screens/restore/restore_wallet_from_keys_page.dart b/lib/src/screens/restore/restore_wallet_from_keys_page.dart index f0e17ccf0..fa5efdff6 100644 --- a/lib/src/screens/restore/restore_wallet_from_keys_page.dart +++ b/lib/src/screens/restore/restore_wallet_from_keys_page.dart @@ -80,7 +80,7 @@ class _RestoreFromKeysFromState extends State { if (state is WalletRestorationFailure) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( + showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( diff --git a/lib/src/screens/restore/restore_wallet_from_seed_details.dart b/lib/src/screens/restore/restore_wallet_from_seed_details.dart index 5469a9f58..6a245884d 100644 --- a/lib/src/screens/restore/restore_wallet_from_seed_details.dart +++ b/lib/src/screens/restore/restore_wallet_from_seed_details.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -53,7 +54,7 @@ class _RestoreFromSeedDetailsFormState if (state is FailureState) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( + showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( diff --git a/lib/src/screens/seed/wallet_seed_page.dart b/lib/src/screens/seed/wallet_seed_page.dart index 6e6088bdd..92d3b8504 100644 --- a/lib/src/screens/seed/wallet_seed_page.dart +++ b/lib/src/screens/seed/wallet_seed_page.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -27,12 +28,12 @@ class WalletSeedPage extends BasePage { @override void onClose(BuildContext context) async { if (isNewWalletCreated) { - final confirmed = await showDialog(context: context, builder: (BuildContext context) { + final confirmed = await showPopUp(context: context, builder: (BuildContext context) { // FIXME: add translations return AlertWithTwoActions( alertTitle: 'Attention', - alertContent: 'Have you written it down? The seed is the only way to recover your wallet.', - leftButtonText: 'Not yet', + alertContent: 'The seed is the only way to recover your wallet. Have you written it down?', + leftButtonText: 'Go back', rightButtonText: 'Yes, I have', actionLeftButton: () => Navigator.of(context).pop(false), actionRightButton: () => Navigator.of(context).pop(true)); diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 0beddd831..d62b14ead 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -6,6 +6,7 @@ import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/trail_button.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/send/send_view_model_state.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -397,7 +398,7 @@ class SendPage extends BasePage { // getOpenaliasRecord(context); // }, // onRemove: () { - // showDialog( + // showPopUp( // context: context, // builder: (dialogContext) { // return AlertWithTwoActions( @@ -492,7 +493,7 @@ class SendPage extends BasePage { reaction((_) => sendViewModel.state, (ExecutionState state) { if (state is FailureState) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( + showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( @@ -506,7 +507,7 @@ class SendPage extends BasePage { if (state is ExecutedSuccessfullyState) { WidgetsBinding.instance.addPostFrameCallback((_) { - showDialog( + showPopUp( context: context, builder: (BuildContext context) { return ConfirmSendingAlert( @@ -521,7 +522,7 @@ class SendPage extends BasePage { actionLeftButton: () { Navigator.of(context).pop(); sendViewModel.commitTransaction(); - showDialog( + showPopUp( context: context, builder: (BuildContext context) { return Observer(builder: (_) { @@ -635,7 +636,7 @@ class SendPage extends BasePage { // if (isOpenalias) { // _addressController.text = sendViewModel.recordAddress; - // await showDialog( + // await showPopUp( // context: context, // builder: (BuildContext context) { // return AlertWithOneAction( @@ -653,7 +654,7 @@ class SendPage extends BasePage { // final items = TransactionPriority.all; // final selectedItem = items.indexOf(sendViewModel.transactionPriority); // - // await showDialog( + // await showPopUp( // builder: (_) => Picker( // items: items, // selectedAtIndex: selectedItem, diff --git a/lib/src/screens/settings/change_language.dart b/lib/src/screens/settings/change_language.dart index 78e6f09fb..3b06aefa8 100644 --- a/lib/src/screens/settings/change_language.dart +++ b/lib/src/screens/settings/change_language.dart @@ -39,7 +39,7 @@ class ChangeLanguage extends BasePage { // isSelected: isCurrent, // handler: (context) async { // if (!isCurrent) { - // await showDialog( + // await showPopUp( // context: context, // builder: (BuildContext context) { // return AlertWithTwoActions( diff --git a/lib/src/screens/settings/widgets/settings_picker_cell.dart b/lib/src/screens/settings/widgets/settings_picker_cell.dart index a77e76efd..e2c949683 100644 --- a/lib/src/screens/settings/widgets/settings_picker_cell.dart +++ b/lib/src/screens/settings/widgets/settings_picker_cell.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; @@ -16,7 +17,7 @@ class SettingsPickerCell extends StandardListRow { onTap: (BuildContext context) async { final selectedAtIndex = items.indexOf(selectedItem); - await showDialog( + await showPopUp( context: context, builder: (_) => Picker( items: items, diff --git a/lib/src/screens/setup_pin_code/setup_pin_code.dart b/lib/src/screens/setup_pin_code/setup_pin_code.dart index dad0c0f2f..6cd54625d 100644 --- a/lib/src/screens/setup_pin_code/setup_pin_code.dart +++ b/lib/src/screens/setup_pin_code/setup_pin_code.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -26,7 +27,7 @@ class SetupPinCodePage extends BasePage { } if (!pinCodeViewModel.isPinCodeCorrect) { - await showDialog( + await showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( @@ -43,7 +44,7 @@ class SetupPinCodePage extends BasePage { try { await pinCodeViewModel.setupPinCode(); - await showDialog( + await showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( @@ -60,7 +61,7 @@ class SetupPinCodePage extends BasePage { }); } catch (e) { // FIXME: Add translation for alert content text. - await showDialog( + await showPopUp( context: context, builder: (BuildContext context) { return AlertWithOneAction( diff --git a/lib/src/screens/wallet_list/wallet_menu.dart b/lib/src/screens/wallet_list/wallet_menu.dart index a508541bd..aa83d56e2 100644 --- a/lib/src/screens/wallet_list/wallet_menu.dart +++ b/lib/src/screens/wallet_list/wallet_menu.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; @@ -126,7 +127,7 @@ class WalletMenu { }); break; case 2: - final isComfirmed = await showDialog( + final isComfirmed = await showPopUp( context: context, builder: (BuildContext context) { return AlertWithTwoActions( diff --git a/lib/src/widgets/seed_language_selector.dart b/lib/src/widgets/seed_language_selector.dart index 74420d370..57011f323 100644 --- a/lib/src/widgets/seed_language_selector.dart +++ b/lib/src/widgets/seed_language_selector.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart'; @@ -35,7 +36,7 @@ class SeedLanguageSelectorState extends State { image: null, text: seedLocales[seedLanguages.indexOf(selected)], onTap: () async { - final selected = await showDialog( + final selected = await showPopUp( context: context, builder: (BuildContext context) => SeedLanguagePicker(key: _pickerKey, selected: this.selected)); diff --git a/lib/utils/show_pop_up.dart b/lib/utils/show_pop_up.dart new file mode 100644 index 000000000..a97fb3762 --- /dev/null +++ b/lib/utils/show_pop_up.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +Future showPopUp({ + @required BuildContext context, + WidgetBuilder builder, + bool barrierDismissible = true, + Color barrierColor, + bool useSafeArea = false, + bool useRootNavigator = true, + RouteSettings routeSettings, + Widget child, +}) { + return showDialog( + context: context, + builder: builder, + barrierDismissible: barrierDismissible, + barrierColor: barrierColor, + useSafeArea: useSafeArea, + useRootNavigator: useRootNavigator, + routeSettings: routeSettings, + child: child); +} diff --git a/lib/view_model/wallet_list/wallet_list_view_model.dart b/lib/view_model/wallet_list/wallet_list_view_model.dart index 3be5d164c..8bb102620 100644 --- a/lib/view_model/wallet_list/wallet_list_view_model.dart +++ b/lib/view_model/wallet_list/wallet_list_view_model.dart @@ -35,7 +35,7 @@ abstract class WalletListViewModelBase with Store { @action Future remove(WalletListItem wallet) async { - final walletService = getIt.get(); + final walletService = getIt.get(param1: wallet.type); await walletService.remove(wallet.name); await _walletInfoSource.delete(wallet.key); _updateList(); From 162e9e5712934d98671441a1d75b58aa27933841 Mon Sep 17 00:00:00 2001 From: M Date: Sat, 26 Sep 2020 14:17:39 +0300 Subject: [PATCH 5/8] Design fixes. --- .../dashboard/widgets/action_button.dart | 1 + .../dashboard/widgets/balance_page.dart | 68 ++++------ .../dashboard/widgets/transaction_raw.dart | 128 +++++++++--------- .../settings/settings_view_model.dart | 5 +- 4 files changed, 96 insertions(+), 106 deletions(-) diff --git a/lib/src/screens/dashboard/widgets/action_button.dart b/lib/src/screens/dashboard/widgets/action_button.dart index dc9a64bbf..9f9fbc418 100644 --- a/lib/src/screens/dashboard/widgets/action_button.dart +++ b/lib/src/screens/dashboard/widgets/action_button.dart @@ -36,6 +36,7 @@ class ActionButton extends StatelessWidget { child: image, ), ), + SizedBox(height: 15), Text( title, style: TextStyle(fontSize: 14, color: Colors.white), diff --git a/lib/src/screens/dashboard/widgets/balance_page.dart b/lib/src/screens/dashboard/widgets/balance_page.dart index 4cd3fe2a0..519f21960 100644 --- a/lib/src/screens/dashboard/widgets/balance_page.dart +++ b/lib/src/screens/dashboard/widgets/balance_page.dart @@ -18,45 +18,34 @@ class BalancePage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Observer( - builder: (_) { - return Text( - dashboardViewModel.wallet.currency.toString(), - style: TextStyle( - fontSize: 40, - fontWeight: FontWeight.bold, - color: Theme.of(context).indicatorColor, - height: 1 - ), - ); - } - ), - Observer( - builder: (_) { - return Text( - dashboardViewModel.balanceViewModel.cryptoBalance, - style: TextStyle( - fontSize: 54, - fontWeight: FontWeight.bold, - color: Colors.white, - height: 1 - ), - ); - } - ), - Observer( - builder: (_) { - return Text( - dashboardViewModel.balanceViewModel.fiatBalance, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w500, - color: Theme.of(context).indicatorColor, - height: 1 - ), - ); - } - ), + Observer(builder: (_) { + return Text( + dashboardViewModel.wallet.currency.toString(), + style: TextStyle( + fontSize: 40, + fontWeight: FontWeight.bold, + color: Theme.of(context).indicatorColor, + height: 1), + ); + }), + Observer(builder: (_) { + return Text(dashboardViewModel.balanceViewModel.cryptoBalance, + style: TextStyle( + fontSize: 54, + fontWeight: FontWeight.bold, + color: Colors.white, + height: 1), + textAlign: TextAlign.center); + }), + Observer(builder: (_) { + return Text(dashboardViewModel.balanceViewModel.fiatBalance, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Theme.of(context).indicatorColor, + height: 1), + textAlign: TextAlign.center); + }), ], ), ), @@ -64,4 +53,3 @@ class BalancePage extends StatelessWidget { ); } } - diff --git a/lib/src/screens/dashboard/widgets/transaction_raw.dart b/lib/src/screens/dashboard/widgets/transaction_raw.dart index 13e3bd2be..14d07a9fc 100644 --- a/lib/src/screens/dashboard/widgets/transaction_raw.dart +++ b/lib/src/screens/dashboard/widgets/transaction_raw.dart @@ -3,13 +3,13 @@ import 'package:cake_wallet/entities/transaction_direction.dart'; import 'package:cake_wallet/generated/i18n.dart'; class TransactionRow extends StatelessWidget { - TransactionRow({ - this.direction, - this.formattedDate, - this.formattedAmount, - this.formattedFiatAmount, - this.isPending, - @required this.onTap}); + TransactionRow( + {this.direction, + this.formattedDate, + this.formattedAmount, + this.formattedFiatAmount, + this.isPending, + @required this.onTap}); final VoidCallback onTap; final TransactionDirection direction; @@ -23,75 +23,73 @@ class TransactionRow extends StatelessWidget { return InkWell( onTap: onTap, child: Container( - height: 52, + height: 62, color: Colors.transparent, padding: EdgeInsets.only(left: 24, right: 24), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Container( - height: 36, - width: 36, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).textTheme.overline.decorationColor - ), - child: Image.asset( - direction == TransactionDirection.incoming - ? 'assets/images/down_arrow.png' - : 'assets/images/up_arrow.png'), - ), Expanded( - child: Padding( - padding: const EdgeInsets.only(left: 12), - child: Container( - height: 46, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.max, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - (direction == TransactionDirection.incoming + child: Container( + height: 56, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + (direction == TransactionDirection.incoming ? S.of(context).received : S.of(context).sent) + - (isPending ? S.of(context).pending : ''), + (isPending ? S.of(context).pending : ''), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Colors.white)), + Container( + decoration: BoxDecoration( + borderRadius: + BorderRadius.all(Radius.circular(10)), + color: (direction == + TransactionDirection.incoming + ? Colors.green.withOpacity(0.8) + : Theme.of(context) + .accentTextTheme + .body2 + .decorationColor + .withOpacity(0.8))), + padding: EdgeInsets.only( + top: 3, bottom: 3, left: 10, right: 10), + child: Text(formattedAmount, style: TextStyle( fontSize: 16, fontWeight: FontWeight.w500, - color: Colors.white - )), - Text(direction == TransactionDirection.incoming - ? formattedAmount - : '- ' + formattedAmount, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.white - )) - ]), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(formattedDate, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).textTheme - .overline.backgroundColor)), - Text(direction == TransactionDirection.incoming - ? formattedFiatAmount - : '- ' + formattedFiatAmount, - style: TextStyle( - fontSize: 14, - color: Theme.of(context).textTheme - .overline.backgroundColor)) - ]), - ], - ), - ), - )) + color: Colors.white))) + ]), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(formattedDate, + style: TextStyle( + fontSize: 14, + color: Theme.of(context) + .textTheme + .overline + .backgroundColor)), + Text(formattedFiatAmount, + style: TextStyle( + fontSize: 14, + color: Theme.of(context) + .textTheme + .overline + .backgroundColor)) + ]), + ], + ), + ), + ) ]), )); } diff --git a/lib/view_model/settings/settings_view_model.dart b/lib/view_model/settings/settings_view_model.dart index 61ffda19b..bac456e79 100644 --- a/lib/view_model/settings/settings_view_model.dart +++ b/lib/view_model/settings/settings_view_model.dart @@ -40,7 +40,9 @@ abstract class SettingsViewModelBase with Store { PickerListItem( title: S.current.settings_display_balance_as, items: BalanceDisplayMode.all, - selectedItem: () => balanceDisplayMode), + selectedItem: () => balanceDisplayMode, + onItemSelected: (BalanceDisplayMode mode) => + _settingsStore.balanceDisplayMode = mode), PickerListItem( title: S.current.settings_currency, items: FiatCurrency.all, @@ -204,6 +206,7 @@ abstract class SettingsViewModelBase with Store { @action void setFiatCurrency(FiatCurrency value) => _settingsStore.fiatCurrency = value; + @action void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; From dcdc411d41a281d85501177a99b4453b5f946322 Mon Sep 17 00:00:00 2001 From: M Date: Sat, 26 Sep 2020 15:19:33 +0300 Subject: [PATCH 6/8] Fixes --- lib/router.dart | 4 ++-- lib/src/screens/dashboard/dashboard_page.dart | 16 +++++-------- .../dashboard/widgets/balance_page.dart | 2 +- .../screens/wallet_list/wallet_list_page.dart | 3 ++- lib/src/screens/welcome/welcome_page.dart | 4 ++-- .../dashboard/balance_view_model.dart | 4 ++++ pubspec.lock | 23 ++++++++++++------- 7 files changed, 32 insertions(+), 24 deletions(-) diff --git a/lib/router.dart b/lib/router.dart index 5ae418395..5213758f2 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -67,7 +67,7 @@ class Router { .pushNamed(Routes.newWallet, arguments: type))); case Routes.newWallet: - final type = settings.arguments as WalletType; + final type = WalletType.monero; // settings.arguments as WalletType; final walletNewVM = getIt.get(param1: type); return CupertinoPageRoute( @@ -97,7 +97,7 @@ class Router { builder: (_) => RestoreOptionsPage(type: type)); case Routes.restoreWalletOptions: - final type = settings.arguments as WalletType; + final type = WalletType.monero; //settings.arguments as WalletType; return CupertinoPageRoute( builder: (_) => RestoreWalletOptionsPage( diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index 16cce7518..fecaec430 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -108,17 +108,13 @@ class DashboardPage extends BasePage { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ ActionButton( - image: sendImage, - title: S.of(context).send, - route: Routes.send, - alignment: Alignment.centerLeft, - ), + image: sendImage, + title: S.of(context).send, + route: Routes.send), ActionButton( - image: sendImage, - title: S.of(context).send, - route: Routes.send, - alignment: Alignment.centerLeft, - ), + image: exchangeImage, + title: S.of(context).exchange, + route: Routes.exchange), ], ), ) diff --git a/lib/src/screens/dashboard/widgets/balance_page.dart b/lib/src/screens/dashboard/widgets/balance_page.dart index 519f21960..e6c0ccef7 100644 --- a/lib/src/screens/dashboard/widgets/balance_page.dart +++ b/lib/src/screens/dashboard/widgets/balance_page.dart @@ -20,7 +20,7 @@ class BalancePage extends StatelessWidget { children: [ Observer(builder: (_) { return Text( - dashboardViewModel.wallet.currency.toString(), + dashboardViewModel.balanceViewModel.currency.toString(), style: TextStyle( fontSize: 40, fontWeight: FontWeight.bold, diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 694c338e4..0b845f3ed 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/src/screens/wallet_list/widgets/wallet_menu_alert.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -73,7 +74,7 @@ class WalletListBodyState extends State { return GestureDetector( onTap: () { - showDialog( + showPopUp( context: context, builder: (dialogContext) { return WalletMenuAlert( diff --git a/lib/src/screens/welcome/welcome_page.dart b/lib/src/screens/welcome/welcome_page.dart index 78cd04835..aa0d5b80c 100644 --- a/lib/src/screens/welcome/welcome_page.dart +++ b/lib/src/screens/welcome/welcome_page.dart @@ -103,7 +103,7 @@ class WelcomePage extends BasePage { Padding( padding: EdgeInsets.only(top: 24), child: PrimaryImageButton( - onPressed: () => Navigator.pushNamed(context, Routes.newWalletFromWelcome), + onPressed: () => Navigator.pushNamed(context, Routes.newWallet), image: newWalletImage, text: S.of(context).create_new, color: Theme.of(context).accentTextTheme.subtitle.decorationColor, @@ -113,7 +113,7 @@ class WelcomePage extends BasePage { Padding( padding: EdgeInsets.only(top: 10), child: PrimaryImageButton( - onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletOptionsFromWelcome), + onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletOptions), image: restoreWalletImage, text: S.of(context).restore_wallet, color: Theme.of(context).accentTextTheme.caption.color, diff --git a/lib/view_model/dashboard/balance_view_model.dart b/lib/view_model/dashboard/balance_view_model.dart index 23fd578f1..58978c946 100644 --- a/lib/view_model/dashboard/balance_view_model.dart +++ b/lib/view_model/dashboard/balance_view_model.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/core/wallet_base.dart'; +import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/monero/monero_wallet.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/calculate_fiat_amount.dart'; @@ -92,6 +93,9 @@ abstract class BalanceViewModelBase with Store { return null; } + @computed + CryptoCurrency get currency => appStore.wallet.currency; + String _getFiatBalance({double price, String cryptoAmount}) { if (cryptoAmount == null) { return '0.00'; diff --git a/pubspec.lock b/pubspec.lock index df34727ee..612b1ac91 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -42,7 +42,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.4.1" + version: "2.4.2" auto_size_text: dependency: "direct main" description: @@ -210,7 +210,7 @@ packages: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.14.12" + version: "1.14.13" connectivity: dependency: "direct main" description: @@ -330,6 +330,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.2" + fake_async: + dependency: transitive + description: + name: fake_async + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.0" ffi: dependency: transitive description: @@ -547,7 +554,7 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.6" + version: "0.12.8" meta: dependency: transitive description: @@ -617,7 +624,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.6.4" + version: "1.7.0" path_drawing: dependency: transitive description: @@ -867,7 +874,7 @@ packages: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.9.3" + version: "1.9.5" stream_channel: dependency: transitive description: @@ -902,7 +909,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.15" + version: "0.2.17" time: dependency: transitive description: @@ -923,7 +930,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.1.6" + version: "1.2.0" url_launcher: dependency: "direct main" description: @@ -1023,5 +1030,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.7.0 <3.0.0" + dart: ">=2.9.0-14.0.dev <3.0.0" flutter: ">=1.12.13+hotfix.5 <2.0.0" From 51cf11127c54149022b22c626cb3ba2207b5c742 Mon Sep 17 00:00:00 2001 From: M Date: Sat, 26 Sep 2020 22:17:31 +0300 Subject: [PATCH 7/8] Fixes --- lib/core/contact_service.dart | 4 +- lib/di.dart | 17 +- lib/entities/contact_record.dart | 40 ++++ lib/entities/node.dart | 3 + lib/entities/record.dart | 35 ++++ lib/reactions/bootstrap.dart | 2 + lib/reactions/on_current_node_change.dart | 17 ++ lib/router.dart | 3 +- .../screens/contact/contact_list_page.dart | 12 +- .../monero_accounts/widgets/account_tile.dart | 2 +- lib/src/screens/nodes/nodes_list_page.dart | 153 ++++++++------- lib/store/contact_list_store.dart | 6 +- lib/store/node_list_store.dart | 10 +- lib/store/settings_store.dart | 13 +- lib/utils/item_cell.dart | 11 +- lib/utils/mobx.dart | 178 +++++++++++------- .../contact_list/contact_list_view_model.dart | 9 +- .../contact_list/contact_view_model.dart | 12 +- .../node_list/node_list_view_model.dart | 52 +---- pubspec.lock | 35 ++-- pubspec.yaml | 2 +- 21 files changed, 362 insertions(+), 254 deletions(-) create mode 100644 lib/entities/contact_record.dart create mode 100644 lib/entities/record.dart create mode 100644 lib/reactions/on_current_node_change.dart diff --git a/lib/core/contact_service.dart b/lib/core/contact_service.dart index c7027fa8f..3f2a46203 100644 --- a/lib/core/contact_service.dart +++ b/lib/core/contact_service.dart @@ -22,7 +22,7 @@ class ContactService { if (index >= 0) { _forceUpdateContactListStore(); } else { - contactListStore.contacts.add(contact); + // contactListStore.contacts.add(contact); } } @@ -33,6 +33,6 @@ class ContactService { void _forceUpdateContactListStore() { contactListStore.contacts.clear(); - contactListStore.contacts.addAll(contactSource.values); + // contactListStore.contacts.addAll(contactSource.values); } } diff --git a/lib/di.dart b/lib/di.dart index 2010a6158..d6ccde3cc 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart'; import 'package:cake_wallet/core/contact_service.dart'; import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/entities/biometric_auth.dart'; +import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/monero/monero_wallet_service.dart'; import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/node.dart'; @@ -197,7 +198,8 @@ Future setup( getIt .registerFactoryParam( (onAuthFinished, closable) => AuthPage(getIt.get(), - onAuthenticationFinished: onAuthFinished, closable: closable ?? false)); + onAuthenticationFinished: onAuthFinished, + closable: closable ?? false)); getIt.registerFactory(() => DashboardPage( walletViewModel: getIt.get(), @@ -282,8 +284,8 @@ Future setup( getIt.registerFactory(() => WalletKeysPage(getIt.get())); - getIt.registerFactoryParam( - (Contact contact, _) => ContactViewModel( + getIt.registerFactoryParam( + (ContactRecord contact, _) => ContactViewModel( contactSource, getIt.get().wallet, contact: contact)); @@ -296,13 +298,14 @@ Future setup( (bool isEditable, _) => ContactListPage(getIt.get(), isEditable: isEditable)); - getIt.registerFactoryParam((Contact contact, _) => - ContactPage(getIt.get(param1: contact))); + getIt.registerFactoryParam( + (ContactRecord contact, _) => + ContactPage(getIt.get(param1: contact))); getIt.registerFactory(() { final appStore = getIt.get(); - return NodeListViewModel(appStore.nodeListStore, nodeSource, - appStore.wallet, appStore.settingsStore); + return NodeListViewModel( + nodeSource, appStore.wallet, appStore.settingsStore); }); getIt.registerFactory(() => NodeListPage(getIt.get())); diff --git a/lib/entities/contact_record.dart b/lib/entities/contact_record.dart new file mode 100644 index 000000000..c4f55cc5a --- /dev/null +++ b/lib/entities/contact_record.dart @@ -0,0 +1,40 @@ +import 'package:hive/hive.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/entities/contact.dart'; +import 'package:cake_wallet/entities/crypto_currency.dart'; +import 'package:cake_wallet/entities/record.dart'; + +part 'contact_record.g.dart'; + +class ContactRecord = ContactRecordBase with _$ContactRecord; + +abstract class ContactRecordBase extends Record with Store { + ContactRecordBase(Box source, Contact original) + : super(source, original); + + @observable + String name; + + @observable + String address; + + @observable + CryptoCurrency type; + + @override + void toBind(Contact original) { + reaction((_) => name, (String name) => original.name = name); + reaction((_) => address, (String address) => original.address = address); + reaction( + (_) => type, + (CryptoCurrency currency) => + original.updateCryptoCurrency(currency: currency)); + } + + @override + void fromBind(Contact original) { + name = original.name; + address = original.address; + type = original.type; + } +} diff --git a/lib/entities/node.dart b/lib/entities/node.dart index 74643112a..f79b8b2e0 100644 --- a/lib/entities/node.dart +++ b/lib/entities/node.dart @@ -38,6 +38,9 @@ class Node extends HiveObject with Keyable { @HiveField(3) int typeRaw; + @override + dynamic get keyIndex => key; + WalletType get type => deserializeFromInt(typeRaw); set type(WalletType type) => typeRaw = serializeToInt(type); diff --git a/lib/entities/record.dart b/lib/entities/record.dart new file mode 100644 index 000000000..3c21441cc --- /dev/null +++ b/lib/entities/record.dart @@ -0,0 +1,35 @@ +import 'dart:async'; + +import 'package:cake_wallet/utils/mobx.dart'; +import 'package:hive/hive.dart'; + +abstract class Record with Keyable { + Record(this._source, this.original) { + _listener?.cancel(); + _listener = _source.watch(key: original.key).listen((event) { + if (!event.deleted) { + fromBind(event.value as T); + } + }); + + fromBind(original); + toBind(original); + } + + dynamic get key => original.key; + + @override + dynamic get keyIndex => key; + + final T original; + + final Box _source; + + StreamSubscription _listener; + + void fromBind(T original); + + void toBind(T original); + + Future save() => original.save(); +} diff --git a/lib/reactions/bootstrap.dart b/lib/reactions/bootstrap.dart index 2ffb5d376..fef73c46f 100644 --- a/lib/reactions/bootstrap.dart +++ b/lib/reactions/bootstrap.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:cake_wallet/reactions/on_current_node_change.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/widgets.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -31,4 +32,5 @@ Future bootstrap(GlobalKey navigatorKey) async { startCurrentWalletChangeReaction( appStore, settingsStore, fiatConversionStore); startCurrentFiatChangeReaction(appStore, settingsStore); + startOnCurrentNodeChangeReaction(appStore); } diff --git a/lib/reactions/on_current_node_change.dart b/lib/reactions/on_current_node_change.dart new file mode 100644 index 000000000..c03bbac21 --- /dev/null +++ b/lib/reactions/on_current_node_change.dart @@ -0,0 +1,17 @@ +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/entities/node.dart'; +import 'package:cake_wallet/store/app_store.dart'; + +ReactionDisposer _onCurrentNodeChangeReaction; + +void startOnCurrentNodeChangeReaction(AppStore appStore) { + _onCurrentNodeChangeReaction?.reaction?.dispose(); + _onCurrentNodeChangeReaction = + reaction((_) => appStore.settingsStore.currentNode, (Node node) async { + try { + await appStore.wallet.connectToNode(node: node); + } catch (e) { + print(e.toString()); + } + }); +} diff --git a/lib/router.dart b/lib/router.dart index 5213758f2..cc660d9a0 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/contact_record.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; @@ -252,7 +253,7 @@ class Router { case Routes.addressBookAddContact: return CupertinoPageRoute( builder: (_) => - getIt.get(param1: settings.arguments as Contact)); + getIt.get(param1: settings.arguments as ContactRecord)); case Routes.showKeys: return MaterialPageRoute( diff --git a/lib/src/screens/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index 044484f49..37c37876f 100644 --- a/lib/src/screens/contact/contact_list_page.dart +++ b/lib/src/screens/contact/contact_list_page.dart @@ -164,17 +164,17 @@ class ContactListPage extends BasePage { final isDelete = await showAlertDialog(context) ?? false; - if (isDelete) { - await contactListViewModel - .delete(contact); - } + // if (isDelete) { + // await contactListViewModel + // .delete(contact); + // } }, ), ], dismissal: SlidableDismissal( child: SlidableDrawerDismissal(), - onDismissed: (actionType) async => - await contactListViewModel.delete(contact), + onDismissed: (actionType) async => null, + // await contactListViewModel.delete(contact), onWillDismiss: (actionType) async => showAlertDialog(context), ), diff --git a/lib/src/screens/monero_accounts/widgets/account_tile.dart b/lib/src/screens/monero_accounts/widgets/account_tile.dart index c39758bd5..50238a67c 100644 --- a/lib/src/screens/monero_accounts/widgets/account_tile.dart +++ b/lib/src/screens/monero_accounts/widgets/account_tile.dart @@ -31,7 +31,7 @@ class AccountTile extends StatelessWidget { accountName, style: TextStyle( fontSize: 18, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, fontFamily: 'Poppins', color: textColor, decoration: TextDecoration.none, diff --git a/lib/src/screens/nodes/nodes_list_page.dart b/lib/src/screens/nodes/nodes_list_page.dart index 84e87a3a5..b0bc8c15f 100644 --- a/lib/src/screens/nodes/nodes_list_page.dart +++ b/lib/src/screens/nodes/nodes_list_page.dart @@ -67,87 +67,86 @@ class NodeListPage extends BasePage { sectionCount: 2, context: context, itemBuilder: (_, sectionIndex, index) { - if (sectionIndex == 0) { - return NodeHeaderListRow( - title: S.of(context).add_new_node, - onTap: (_) async => - await Navigator.of(context).pushNamed(Routes.newNode)); - } + return Observer(builder: (_) { + if (sectionIndex == 0) { + return NodeHeaderListRow( + title: S.of(context).add_new_node, + onTap: (_) async => await Navigator.of(context) + .pushNamed(Routes.newNode)); + } - final node = nodeListViewModel.nodes[index]; - final nodeListRow = NodeListRow( - title: node.value.uri, - isSelected: node.isSelected, - isAlive: node.value.requestNode(), - onTap: (_) async { - if (node.isSelected) { - return; - } + final node = nodeListViewModel.nodes[index]; + final isSelected = node.keyIndex == + nodeListViewModel.settingsStore.currentNode.keyIndex; + final nodeListRow = NodeListRow( + title: node.uri, + isSelected: isSelected, + isAlive: node.requestNode(), + onTap: (_) async { + if (isSelected) { + return; + } - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: Text( - S.of(context).change_current_node(node.value.uri), - textAlign: TextAlign.center, + await showPopUp( + context: context, + builder: (BuildContext context) { + // FIXME: Add translation. + return AlertWithTwoActions( + alertTitle: 'Change current node', + alertContent: + S.of(context).change_current_node(node.uri), + leftButtonText: S.of(context).cancel, + rightButtonText: S.of(context).change, + actionLeftButton: () => + Navigator.of(context).pop(), + actionRightButton: () async { + await nodeListViewModel.setAsCurrent(node); + Navigator.of(context).pop(); + }); + }); + }); + + final dismissibleRow = Dismissible( + key: Key('${node.keyIndex}'), + confirmDismiss: (direction) async { + return await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).remove_node, + alertContent: S.of(context).remove_node_message, + rightButtonText: S.of(context).remove, + leftButtonText: S.of(context).cancel, + actionRightButton: () => + Navigator.pop(context, true), + actionLeftButton: () => + Navigator.pop(context, false)); + }); + }, + onDismissed: (direction) async => + nodeListViewModel.delete(node), + direction: DismissDirection.endToStart, + background: Container( + padding: EdgeInsets.only(right: 10.0), + alignment: AlignmentDirectional.centerEnd, + color: Palette.red, + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + const Icon( + CupertinoIcons.delete, + color: Colors.white, ), - actions: [ - FlatButton( - onPressed: () => Navigator.pop(context), - child: Text(S.of(context).cancel)), - FlatButton( - onPressed: () async { - Navigator.of(context).pop(); - await nodeListViewModel - .setAsCurrent(node.value); - }, - child: Text(S.of(context).change)), - ], - ); - }); - }); + Text( + S.of(context).delete, + style: TextStyle(color: Colors.white), + ) + ], + )), + child: nodeListRow); - final dismissibleRow = Dismissible( - key: Key('${node.keyIndex}'), - confirmDismiss: (direction) async { - return await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context).remove_node, - alertContent: S.of(context).remove_node_message, - rightButtonText: S.of(context).remove, - leftButtonText: S.of(context).cancel, - actionRightButton: () => - Navigator.pop(context, true), - actionLeftButton: () => - Navigator.pop(context, false)); - }); - }, - onDismissed: (direction) async => - nodeListViewModel.delete(node.value), - direction: DismissDirection.endToStart, - background: Container( - padding: EdgeInsets.only(right: 10.0), - alignment: AlignmentDirectional.centerEnd, - color: Palette.red, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - const Icon( - CupertinoIcons.delete, - color: Colors.white, - ), - Text( - S.of(context).delete, - style: TextStyle(color: Colors.white), - ) - ], - )), - child: nodeListRow); - - return node.isSelected ? nodeListRow : dismissibleRow; + return isSelected ? nodeListRow : dismissibleRow; + }); }, itemCounter: (int sectionIndex) { if (sectionIndex == 0) { diff --git a/lib/store/contact_list_store.dart b/lib/store/contact_list_store.dart index ca3d0bcf0..85194e0d6 100644 --- a/lib/store/contact_list_store.dart +++ b/lib/store/contact_list_store.dart @@ -1,12 +1,12 @@ import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/entities/contact.dart'; +import 'package:cake_wallet/entities/contact_record.dart'; part 'contact_list_store.g.dart'; class ContactListStore = ContactListStoreBase with _$ContactListStore; abstract class ContactListStoreBase with Store { - ContactListStoreBase() : contacts = ObservableList(); + ContactListStoreBase() : contacts = ObservableList(); - final ObservableList contacts; + final ObservableList contacts; } diff --git a/lib/store/node_list_store.dart b/lib/store/node_list_store.dart index 0ff958e3f..56fe0333a 100644 --- a/lib/store/node_list_store.dart +++ b/lib/store/node_list_store.dart @@ -22,17 +22,13 @@ abstract class NodeListStoreBase with Store { final nodeSource = getIt.get>(); _instance = NodeListStore(); - _instance.replaceValues(nodeSource.values); + _instance.nodes.clear(); + _instance.nodes.addAll(nodeSource.values); _onNodesSourceChange?.cancel(); - _onNodesSourceChange = bindBox(nodeSource, _instance.nodes); + _onNodesSourceChange = nodeSource.bindToList(_instance.nodes); return _instance; } final ObservableList nodes; - - void replaceValues(Iterable newNodes) { - nodes.clear(); - nodes.addAll(newNodes); - } } diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index e17ce2de9..d4f6bf95a 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -21,7 +21,6 @@ class SettingsStore = SettingsStoreBase with _$SettingsStore; abstract class SettingsStoreBase with Store { SettingsStoreBase( {@required SharedPreferences sharedPreferences, - @required Box nodeSource, @required FiatCurrency initialFiatCurrency, @required TransactionPriority initialTransactionPriority, @required BalanceDisplayMode initialBalanceDisplayMode, @@ -43,10 +42,9 @@ abstract class SettingsStoreBase with Store { pinCodeLength = initialPinLength; languageCode = initialLanguageCode; currentLocale = initialCurrentLocale; - itemHeaders = {}; + currentNode = nodes[WalletType.monero]; this.nodes = ObservableMap.of(nodes); _sharedPreferences = sharedPreferences; - _nodeSource = nodeSource; reaction( (_) => allowBiometricalAuthentication, @@ -58,6 +56,9 @@ abstract class SettingsStoreBase with Store { (_) => pinCodeLength, (int pinLength) => sharedPreferences.setInt( PreferencesKey.currentPinLength, pinLength)); + + reaction((_) => currentNode, + (Node node) => _saveCurrentNode(node, WalletType.monero)); } static const defaultPinLength = 4; @@ -88,7 +89,7 @@ abstract class SettingsStoreBase with Store { int pinCodeLength; @observable - Map itemHeaders; + Node currentNode; String languageCode; @@ -97,7 +98,6 @@ abstract class SettingsStoreBase with Store { String appVersion; SharedPreferences _sharedPreferences; - Box _nodeSource; ObservableMap nodes; @@ -150,7 +150,6 @@ abstract class SettingsStoreBase with Store { WalletType.monero: moneroNode, WalletType.bitcoin: bitcoinElectrumServer }, - nodeSource: nodeSource, appVersion: packageInfo.version, initialFiatCurrency: currentFiatCurrency, initialTransactionPriority: currentTransactionPriority, @@ -164,7 +163,7 @@ abstract class SettingsStoreBase with Store { initialCurrentLocale: initialCurrentLocale); } - Future setCurrentNode(Node node, WalletType walletType) async { + Future _saveCurrentNode(Node node, WalletType walletType) async { switch (walletType) { case WalletType.bitcoin: await _sharedPreferences.setInt( diff --git a/lib/utils/item_cell.dart b/lib/utils/item_cell.dart index 4aca57b46..150df6d50 100644 --- a/lib/utils/item_cell.dart +++ b/lib/utils/item_cell.dart @@ -1,11 +1,18 @@ import 'package:flutter/foundation.dart'; +import 'package:mobx/mobx.dart'; import 'package:cake_wallet/utils/mobx.dart'; +// part 'node_list_view_model.g.dart'; +// +// class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel; + class ItemCell with Keyable { - ItemCell(this.value, {@required this.isSelected, @required dynamic key}) { + ItemCell(this.value, {this.isSelectedBuilder, @required dynamic key}) { keyIndex = key; } final Item value; - final bool isSelected; + + bool get isSelected => isSelectedBuilder(value); + bool Function(Item item) isSelectedBuilder; } diff --git a/lib/utils/mobx.dart b/lib/utils/mobx.dart index cb2d810e5..b11922f8e 100644 --- a/lib/utils/mobx.dart +++ b/lib/utils/mobx.dart @@ -6,36 +6,6 @@ mixin Keyable { dynamic keyIndex; } -void connectWithTransform( - ObservableList source, ObservableList dest, Y Function(T) transform, - {bool Function(T) filter}) { - source.observe((ListChange change) { - change.elementChanges.forEach((change) { - switch (change.type) { - case OperationType.add: - if (filter?.call(change.newValue as T) ?? true) { - dest.add(transform(change.newValue as T)); - } - break; - case OperationType.remove: - // Hive could has equal index and key - dest.removeWhere( - (elem) => elem.keyIndex == (change.oldValue.key ?? change.index)); - break; - case OperationType.update: - for (var i = 0; i < dest.length; i++) { - final item = dest[i]; - - if (item.keyIndex == change.newValue.key) { - dest[i] = transform(change.newValue as T); - } - } - break; - } - }); - }); -} - void connectMapToListWithTransform( ObservableMap source, ObservableList dest, @@ -50,8 +20,8 @@ void connectMapToListWithTransform( break; case OperationType.remove: // Hive could has equal index and key - dest.removeWhere( - (elem) => elem.keyIndex == (change.key ?? change.newValue.keyIndex)); + dest.removeWhere((elem) => + elem.keyIndex == (change.key ?? change.newValue.keyIndex)); break; case OperationType.update: for (var i = 0; i < dest.length; i++) { @@ -66,54 +36,124 @@ void connectMapToListWithTransform( }); } -void connect( - ObservableList source, ObservableList dest) { - source.observe((ListChange change) { - source.observe((ListChange change) { - change.elementChanges.forEach((change) { - switch (change.type) { - case OperationType.add: - // if (filter?.call(change.newValue as T) ?? true) { - dest.add(change.newValue as T); - // } - break; - case OperationType.remove: - // Hive could has equal index and key - dest.removeWhere((elem) => - elem.keyIndex == (change.oldValue.key ?? change.index)); - break; - case OperationType.update: - for (var i = 0; i < dest.length; i++) { - final item = dest[i]; +typedef Filter = bool Function(T); +typedef Transform = Y Function(T); - if (item.keyIndex == change.newValue.key) { - dest[i] = change.newValue as T; - } - } - break; - } - }); - }); - }); +enum ChangeType { update, delete, add } + +class EntityChange { + EntityChange(this.value, this.type, {dynamic key}) : _key = key; + + dynamic get key => _key ?? value.keyIndex; + final T value; + final ChangeType type; + final dynamic _key; } -StreamSubscription bindBox( - Box source, ObservableList dest) { - return source.watch().listen((event) { +extension MobxBindable on Box { + StreamSubscription bindToList( + ObservableList dest, { + bool initialFire = false, + Filter filter, + }) { + if (initialFire) { + dest.addAll(values); + } + + return watch().listen((event) { + if (filter != null && !filter(event.value as T)) { + return; + } + + dest.acceptBoxChange(event); + }); + } + + StreamSubscription bindToListWithTransform( + ObservableList dest, + Transform transform, { + bool initialFire = false, + Filter filter, + }) { + if (initialFire) { + dest.addAll(values.map((value) => transform(value))); + } + + return watch().listen((event) { + if (filter != null && !filter(event.value as T)) { + return; + } + + dest.acceptBoxChange(event, transformed: transform(event.value as T)); + }); + } +} + +extension HiveBindable on ObservableList { + Stream> listen() { + // ignore: close_sinks + final controller = StreamController>(); + + observe((ListChange change) { + change.elementChanges.forEach((change) { + ChangeType type; + + switch (change.type) { + case OperationType.add: + type = ChangeType.add; + break; + case OperationType.remove: + type = ChangeType.delete; + break; + case OperationType.update: + type = ChangeType.update; + break; + } + + final value = change.newValue as T; + controller.add(EntityChange(value, type)); + }); + }); + + return controller.stream; + } + + StreamSubscription> bindToList(ObservableList dest) => + listen().listen((event) => dest.acceptEntityChange(event)); + + void acceptBoxChange(BoxEvent event, {T transformed}) { if (event.deleted) { - dest.removeWhere((el) => el.keyIndex == event.key); + removeWhere((el) => el.keyIndex == event.key); + } + + final dynamic value = transformed ?? event.value; + + if (value is T) { + final index = indexWhere((el) => el.keyIndex == value.keyIndex); + + if (index > -1) { + this.setAll(index, [value]); // FIXME: fixme + } else { + add(value); + } + } + } + + void acceptEntityChange(EntityChange event) { + if (event.type == ChangeType.delete) { + removeWhere((el) => el.keyIndex == event.key); } final dynamic value = event.value; if (value is T) { - final elIndex = dest.indexWhere((el) => el.keyIndex == value.keyIndex); + final index = indexWhere((el) => el.keyIndex == value.keyIndex); - if (elIndex > -1) { - dest[elIndex] = value; + if (index > -1) { + this.setAll(index, [value]); // FIXME: fixme } else { - dest.add(value); + add(value); } } - }); + } } diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index d222cb6dd..aa3a5e2c7 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:cake_wallet/entities/contact_record.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/core/contact_service.dart'; @@ -14,19 +15,21 @@ class ContactListViewModel = ContactListViewModelBase abstract class ContactListViewModelBase with Store { ContactListViewModelBase( this.addressBookStore, this.contactService, this.contactSource) { - _subscription = bindBox(contactSource, addressBookStore.contacts); + _subscription = contactSource.bindToListWithTransform(addressBookStore.contacts, + (Contact contact) => ContactRecord(contactSource, contact), + initialFire: true); } final ContactListStore addressBookStore; final ContactService contactService; final Box contactSource; - ObservableList get contacts => addressBookStore.contacts; + ObservableList get contacts => addressBookStore.contacts; StreamSubscription _subscription; void dispose() { - _subscription.cancel(); + // _subscription.cancel(); } Future delete(Contact contact) async => contactService.delete(contact); diff --git a/lib/view_model/contact_list/contact_view_model.dart b/lib/view_model/contact_list/contact_view_model.dart index 059b6a210..cb48260b5 100644 --- a/lib/view_model/contact_list/contact_view_model.dart +++ b/lib/view_model/contact_list/contact_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/contact_record.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/core/execution_state.dart'; @@ -11,7 +12,7 @@ part 'contact_view_model.g.dart'; class ContactViewModel = ContactViewModelBase with _$ContactViewModel; abstract class ContactViewModelBase with Store { - ContactViewModelBase(this._contacts, this._wallet, {Contact contact}) + ContactViewModelBase(this._contacts, this._wallet, {ContactRecord contact}) : state = InitialExecutionState(), currencies = CryptoCurrency.all, _contact = contact { @@ -41,7 +42,7 @@ abstract class ContactViewModelBase with Store { final List currencies; final WalletBase _wallet; final Box _contacts; - final Contact _contact; + final ContactRecord _contact; @action void reset() { @@ -57,9 +58,12 @@ abstract class ContactViewModelBase with Store { if (_contact != null) { _contact.name = name; _contact.address = address; - _contact.updateCryptoCurrency(currency: currency); - await _contacts.put(_contact.key, _contact); + _contact.type = currency; + await _contact.save(); + // await _contacts.put(_contact.key, _contact); } else { + // final contact = ContactRecordBase.create(_contacts, name, address, currency); + // await contact.save(); await _contacts .add(Contact(name: name, address: address, type: currency)); } diff --git a/lib/view_model/node_list/node_list_view_model.dart b/lib/view_model/node_list/node_list_view_model.dart index 4ca46c9fc..2231ff7c9 100644 --- a/lib/view_model/node_list/node_list_view_model.dart +++ b/lib/view_model/node_list/node_list_view_model.dart @@ -1,45 +1,29 @@ import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/core/wallet_base.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/node_list.dart'; -import 'package:cake_wallet/store/node_list_store.dart'; -import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/entities/default_settings_migration.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/utils/mobx.dart'; -import 'package:cake_wallet/utils/item_cell.dart'; part 'node_list_view_model.g.dart'; class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel; abstract class NodeListViewModelBase with Store { - NodeListViewModelBase( - this._nodeListStore, this._nodeSource, this._wallet, this._settingsStore) - : nodes = ObservableList>() { - final currentNode = _settingsStore.getCurrentNode(_wallet.type); - final values = _nodeListStore.nodes; - nodes.clear(); - nodes.addAll(values.where((Node node) => node.type == _wallet.type).map( - (Node val) => ItemCell(val, - isSelected: val.key == currentNode.key, key: val.key))); - connectWithTransform( - _nodeListStore.nodes, - nodes, - (Node val) => ItemCell(val, - isSelected: val.key == currentNode.key, key: val.key), - filter: (Node val) => val.type == _wallet.type); - reaction((_) => _settingsStore.nodes[_wallet.type], - (Node _) => _updateCurrentNode()); + NodeListViewModelBase(this._nodeSource, this._wallet, this.settingsStore) + : nodes = ObservableList() { + _nodeSource.bindToList(nodes, + filter: (Node val) => val.type == _wallet.type, initialFire: true); } - ObservableList> nodes; + final ObservableList nodes; + final SettingsStore settingsStore; final WalletBase _wallet; final Box _nodeSource; - final NodeListStore _nodeListStore; - final SettingsStore _settingsStore; Future reset() async { await resetToDefault(_nodeSource); @@ -64,24 +48,6 @@ abstract class NodeListViewModelBase with Store { Future delete(Node node) async => _nodeSource.delete(node.key); - Future setAsCurrent(Node node) async { - await _settingsStore.setCurrentNode(node, _wallet.type); - _updateCurrentNode(); - await _wallet.connectToNode(node: node); - } - - @action - void _updateCurrentNode() { - final currentNode = _settingsStore.getCurrentNode(_wallet.type); - - for (var i = 0; i < nodes.length; i++) { - final item = nodes[i]; - final isSelected = item.value.key == currentNode.key; - - if (item.isSelected != isSelected) { - nodes[i] = ItemCell(item.value, - isSelected: isSelected, key: item.keyIndex); - } - } - } + Future setAsCurrent(Node node) async => + settingsStore.currentNode = node; } diff --git a/pubspec.lock b/pubspec.lock index 612b1ac91..f9bbbbd26 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -217,7 +217,7 @@ packages: name: connectivity url: "https://pub.dartlang.org" source: hosted - version: "0.4.9+2" + version: "0.4.9+3" connectivity_for_web: dependency: transitive description: @@ -252,7 +252,7 @@ packages: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "2.1.4" + version: "2.1.5" csslib: dependency: transitive description: @@ -395,7 +395,7 @@ packages: name: flutter_plugin_android_lifecycle url: "https://pub.dartlang.org" source: hosted - version: "1.0.9" + version: "1.0.11" flutter_secure_storage: dependency: "direct main" description: @@ -470,7 +470,7 @@ packages: name: hive_generator url: "https://pub.dartlang.org" source: hosted - version: "0.7.1" + version: "0.7.2+1" html: dependency: transitive description: @@ -505,7 +505,7 @@ packages: name: image url: "https://pub.dartlang.org" source: hosted - version: "2.1.12" + version: "2.1.18" intl: dependency: "direct main" description: @@ -533,14 +533,14 @@ packages: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.1.0" local_auth: dependency: "direct main" description: name: local_auth url: "https://pub.dartlang.org" source: hosted - version: "0.6.3+1" + version: "0.6.3+2" logging: dependency: transitive description: @@ -645,7 +645,7 @@ packages: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "1.6.17" + version: "1.6.18" path_provider_linux: dependency: transitive description: @@ -687,7 +687,7 @@ packages: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" + version: "3.0.4" platform: dependency: transitive description: @@ -695,13 +695,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.1" - platform_detect: - dependency: transitive - description: - name: platform_detect - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.0" plugin_platform_interface: dependency: transitive description: @@ -785,14 +778,14 @@ packages: name: share url: "https://pub.dartlang.org" source: hosted - version: "0.6.5+1" + version: "0.6.5+2" shared_preferences: dependency: "direct main" description: name: shared_preferences url: "https://pub.dartlang.org" source: hosted - version: "0.5.11" + version: "0.5.12" shared_preferences_linux: dependency: transitive description: @@ -937,7 +930,7 @@ packages: name: url_launcher url: "https://pub.dartlang.org" source: hosted - version: "5.7.0" + version: "5.7.2" url_launcher_linux: dependency: transitive description: @@ -965,7 +958,7 @@ packages: name: url_launcher_web url: "https://pub.dartlang.org" source: hosted - version: "0.1.3+2" + version: "0.1.4+1" url_launcher_windows: dependency: transitive description: @@ -1021,7 +1014,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "3.6.1" + version: "4.5.1" yaml: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index f15f131c1..d6ac4bf73 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,7 +14,7 @@ description: Cake Wallet. version: 1.0.5+5 environment: - sdk: ">=2.2.2 <3.0.0" + sdk: ">=2.7.0 <3.0.0" dependencies: flutter: From e3678d2cf91e06d3ec0e32142e5c48ef80899f5f Mon Sep 17 00:00:00 2001 From: M Date: Mon, 28 Sep 2020 18:47:43 +0300 Subject: [PATCH 8/8] Fixes --- lib/core/contact_service.dart | 38 --- lib/di.dart | 31 +- lib/entities/language.dart | 38 --- lib/entities/language_service.dart | 26 ++ lib/entities/node.dart | 36 ++- lib/entities/record.dart | 3 +- lib/main.dart | 195 +++--------- lib/router.dart | 9 +- lib/src/reactions/set_reactions.dart | 98 ------ lib/src/screens/base_page.dart | 44 ++- .../screens/contact/contact_list_page.dart | 8 +- .../dashboard/create_dashboard_page.dart | 33 -- lib/src/screens/dashboard/dashboard_page.dart | 2 +- .../dashboard/widgets/balance_page.dart | 73 ++--- .../exchange/widgets/exchange_card.dart | 10 +- lib/src/screens/faq/faq_item.dart | 61 ++++ lib/src/screens/faq/faq_page.dart | 140 +------- lib/src/screens/send/send_page.dart | 299 +++++++++--------- lib/src/screens/settings/change_language.dart | 96 +++--- lib/src/widgets/alert_close_button.dart | 2 +- lib/store/app_store.dart | 4 - lib/store/contact_list_store.dart | 12 - lib/store/settings_store.dart | 24 +- lib/store/theme_changer_store.dart | 13 - lib/theme_changer.dart | 16 - lib/utils/date_formatter.dart | 2 +- lib/utils/mobx.dart | 6 +- .../contact_list/contact_list_view_model.dart | 24 +- .../contact_list/contact_view_model.dart | 9 +- lib/view_model/send/send_view_model.dart | 8 +- .../settings/settings_view_model.dart | 24 +- test/widget_test.dart | 2 +- 32 files changed, 482 insertions(+), 904 deletions(-) delete mode 100644 lib/core/contact_service.dart delete mode 100644 lib/entities/language.dart create mode 100644 lib/entities/language_service.dart delete mode 100644 lib/src/reactions/set_reactions.dart delete mode 100644 lib/src/screens/dashboard/create_dashboard_page.dart create mode 100644 lib/src/screens/faq/faq_item.dart delete mode 100644 lib/store/contact_list_store.dart delete mode 100644 lib/store/theme_changer_store.dart delete mode 100644 lib/theme_changer.dart diff --git a/lib/core/contact_service.dart b/lib/core/contact_service.dart deleted file mode 100644 index 3f2a46203..000000000 --- a/lib/core/contact_service.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:hive/hive.dart'; -import 'package:cake_wallet/store/contact_list_store.dart'; -import 'package:cake_wallet/entities/contact.dart'; - -class ContactService { - ContactService(this.contactSource, this.contactListStore) { - _forceUpdateContactListStore(); - } - - final Box contactSource; - final ContactListStore contactListStore; - - Future add(Contact contact) async { - await contactSource.add(contact); - // contactListStore.contacts.add(contact); - } - - Future update(Contact contact) async { - await contact.save(); - final index = contactListStore.contacts.indexOf(contact) ?? -1; - - if (index >= 0) { - _forceUpdateContactListStore(); - } else { - // contactListStore.contacts.add(contact); - } - } - - Future delete(Contact contact) async { - await contact.delete(); - contactListStore.contacts.remove(contact); - } - - void _forceUpdateContactListStore() { - contactListStore.contacts.clear(); - // contactListStore.contacts.addAll(contactSource.values); - } -} diff --git a/lib/di.dart b/lib/di.dart index d6ccde3cc..d133b6ed0 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1,5 +1,4 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart'; -import 'package:cake_wallet/core/contact_service.dart'; import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/entities/biometric_auth.dart'; import 'package:cake_wallet/entities/contact_record.dart'; @@ -8,22 +7,22 @@ import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/exchange/trade.dart'; -// import 'package:cake_wallet/src/domain/services/wallet_service.dart'; import 'package:cake_wallet/src/screens/contact/contact_list_page.dart'; import 'package:cake_wallet/src/screens/contact/contact_page.dart'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart'; +import 'package:cake_wallet/src/screens/faq/faq_page.dart'; import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart'; import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart'; import 'package:cake_wallet/src/screens/rescan/rescan_page.dart'; import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart'; import 'package:cake_wallet/src/screens/send/send_template_page.dart'; +import 'package:cake_wallet/src/screens/settings/change_language.dart'; import 'package:cake_wallet/src/screens/settings/settings.dart'; import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart'; -import 'package:cake_wallet/store/contact_list_store.dart'; import 'package:cake_wallet/store/node_list_store.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/core/auth_service.dart'; @@ -38,10 +37,7 @@ import 'package:cake_wallet/src/screens/receive/receive_page.dart'; import 'package:cake_wallet/src/screens/send/send_page.dart'; import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart'; -import 'package:cake_wallet/store/theme_changer_store.dart'; import 'package:cake_wallet/store/wallet_list_store.dart'; -import 'package:cake_wallet/utils/mobx.dart'; -import 'package:cake_wallet/theme_changer.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart'; @@ -62,11 +58,9 @@ import 'package:cake_wallet/view_model/wallet_keys_view_model.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:get_it/get_it.dart'; import 'package:hive/hive.dart'; -import 'package:mobx/mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart'; @@ -104,17 +98,13 @@ Future setup( getIt.registerSingleton(FlutterSecureStorage()); getIt.registerSingleton(AuthenticationStore()); getIt.registerSingleton(WalletListStore()); - getIt.registerSingleton(ContactListStore()); getIt.registerSingleton(NodeListStoreBase.instance); getIt.registerSingleton(settingsStore); getIt.registerSingleton(AppStore( authenticationStore: getIt.get(), walletList: getIt.get(), settingsStore: getIt.get(), - contactListStore: getIt.get(), nodeListStore: getIt.get())); - getIt.registerSingleton( - ContactService(contactSource, getIt.get().contactListStore)); getIt.registerSingleton(TradesStore( tradesSource: tradesSource, settingsStore: getIt.get())); getIt.registerSingleton(TradeFilterStore()); @@ -285,14 +275,10 @@ Future setup( getIt.registerFactory(() => WalletKeysPage(getIt.get())); getIt.registerFactoryParam( - (ContactRecord contact, _) => ContactViewModel( - contactSource, getIt.get().wallet, - contact: contact)); + (ContactRecord contact, _) => + ContactViewModel(contactSource, contact: contact)); - getIt.registerFactory(() => ContactListViewModel( - getIt.get().contactListStore, - getIt.get(), - contactSource)); + getIt.registerFactory(() => ContactListViewModel(contactSource)); getIt.registerFactoryParam( (bool isEditable, _) => ContactListPage(getIt.get(), @@ -366,9 +352,8 @@ Future setup( getIt.registerFactory(() => RescanViewModel(getIt.get().wallet)); getIt.registerFactory(() => RescanPage(getIt.get())); -} -void setupThemeChangerStore(ThemeChanger themeChanger) { - getIt.registerSingleton( - ThemeChangerStore(themeChanger: themeChanger)); + getIt.registerFactory(() => FaqPage(getIt.get())); + + getIt.registerFactory(() => LanguageListPage(getIt.get())); } diff --git a/lib/entities/language.dart b/lib/entities/language.dart deleted file mode 100644 index 16e1d1a6a..000000000 --- a/lib/entities/language.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:devicelocale/devicelocale.dart'; -import 'package:intl/intl.dart'; - -const Map languages = { - 'en': 'English', - 'de': 'Deutsch (German)', - 'es': 'Español (Spanish)', - 'hi': 'हिंदी (Hindi)', - 'ja': '日本 (Japanese)', - 'ko': '한국어 (Korean)', - 'nl': 'Nederlands (Dutch)', - 'pl': 'Polski (Polish)', - 'pt': 'Português (Portuguese)', - 'ru': 'Русский (Russian)', - 'uk': 'Українська (Ukrainian)', - 'zh': '中文 (Chinese)' -}; - -class Language with ChangeNotifier { - Language(this._currentLanguage); - - String _currentLanguage; - - String getCurrentLanguage() => _currentLanguage; - - void setCurrentLanguage(String language) { - _currentLanguage = language; - notifyListeners(); - } - - static Future localeDetection() async { - var locale = await Devicelocale.currentLocale; - locale = Intl.shortLocale(locale); - - return languages.keys.contains(locale) ? locale : 'en'; - } -} \ No newline at end of file diff --git a/lib/entities/language_service.dart b/lib/entities/language_service.dart new file mode 100644 index 000000000..4ea158353 --- /dev/null +++ b/lib/entities/language_service.dart @@ -0,0 +1,26 @@ +import 'package:devicelocale/devicelocale.dart'; +import 'package:intl/intl.dart'; + +class LanguageService { + static const Map list = { + 'en': 'English', + 'de': 'Deutsch (German)', + 'es': 'Español (Spanish)', + 'hi': 'हिंदी (Hindi)', + 'ja': '日本 (Japanese)', + 'ko': '한국어 (Korean)', + 'nl': 'Nederlands (Dutch)', + 'pl': 'Polski (Polish)', + 'pt': 'Português (Portuguese)', + 'ru': 'Русский (Russian)', + 'uk': 'Українська (Ukrainian)', + 'zh': '中文 (Chinese)' + }; + + static Future localeDetection() async { + var locale = await Devicelocale.currentLocale; + locale = Intl.shortLocale(locale); + + return list.keys.contains(locale) ? locale : 'en'; + } +} diff --git a/lib/entities/node.dart b/lib/entities/node.dart index f79b8b2e0..d96834dcb 100644 --- a/lib/entities/node.dart +++ b/lib/entities/node.dart @@ -61,24 +61,28 @@ class Node extends HiveObject with Keyable { } Future requestMoneroNode() async { - Map resBody; + try { + Map resBody; - if (login != null && password != null) { - final digestRequest = DigestRequest(); - final response = await digestRequest.request( - uri: uri, login: login, password: password); - resBody = response.data as Map; - } else { - final url = Uri.http(uri, '/json_rpc'); - final headers = {'Content-type': 'application/json'}; - final body = - json.encode({'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'}); - final response = - await http.post(url.toString(), headers: headers, body: body); - resBody = json.decode(response.body) as Map; + if (login != null && password != null) { + final digestRequest = DigestRequest(); + final response = await digestRequest.request( + uri: uri, login: login, password: password); + resBody = response.data as Map; + } else { + final url = Uri.http(uri, '/json_rpc'); + final headers = {'Content-type': 'application/json'}; + final body = + json.encode({'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'}); + final response = + await http.post(url.toString(), headers: headers, body: body); + resBody = json.decode(response.body) as Map; + } + + return !(resBody['result']['offline'] as bool); + } catch (_) { + return false; } - - return !(resBody['result']['offline'] as bool); } Future requestBitcoinElectrumServer() async { diff --git a/lib/entities/record.dart b/lib/entities/record.dart index 3c21441cc..1ca661684 100644 --- a/lib/entities/record.dart +++ b/lib/entities/record.dart @@ -5,6 +5,7 @@ import 'package:hive/hive.dart'; abstract class Record with Keyable { Record(this._source, this.original) { + key = original.key; _listener?.cancel(); _listener = _source.watch(key: original.key).listen((event) { if (!event.deleted) { @@ -16,7 +17,7 @@ abstract class Record with Keyable { toBind(original); } - dynamic get key => original.key; + dynamic key; @override dynamic get keyIndex => key; diff --git a/lib/main.dart b/lib/main.dart index 66cca43a5..4a49b646a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,66 +1,30 @@ -import 'package:cake_wallet/entities/fs_migration.dart'; -import 'package:cake_wallet/entities/transaction_description.dart'; -import 'package:cake_wallet/entities/transaction_description.dart'; -import 'package:cake_wallet/reactions/bootstrap.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/store/app_store.dart'; -import 'package:cake_wallet/store/authentication_store.dart'; -import 'package:cake_wallet/core/auth_service.dart'; -import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart'; -import 'package:cake_wallet/monero/monero_wallet_service.dart'; -import 'package:cake_wallet/core/wallet_creation_service.dart'; -import 'package:cake_wallet/di.dart'; -import 'package:cake_wallet/view_model/wallet_new_vm.dart'; -import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:get_it/get_it.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:provider/provider.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:cw_monero/wallet.dart' as monero_wallet; import 'package:cake_wallet/router.dart'; -import 'theme_changer.dart'; -import 'themes.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/reactions/bootstrap.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/store/authentication_store.dart'; +import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/get_encryption_key.dart'; import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/wallet_info.dart'; -import 'package:cake_wallet/exchange/trade.dart'; - -// import 'package:cake_wallet/monero/transaction_description.dart'; -import 'package:cake_wallet/src/reactions/set_reactions.dart'; - -// import 'package:cake_wallet/src/stores/login/login_store.dart'; -// import 'package:cake_wallet/src/stores/balance/balance_store.dart'; -// import 'package:cake_wallet/src/stores/sync/sync_store.dart'; -// import 'package:cake_wallet/src/stores/wallet/wallet_store.dart'; -// import 'package:cake_wallet/src/stores/send_template/send_template_store.dart'; -// import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart'; -import 'package:cake_wallet/src/screens/root/root.dart'; - -//import 'package:cake_wallet/src/stores/authentication/authentication_store.dart'; -// import 'package:cake_wallet/src/stores/settings/settings_store.dart'; -// import 'package:cake_wallet/src/stores/price/price_store.dart'; -// import 'package:cake_wallet/src/domain/services/user_service.dart'; -// import 'package:cake_wallet/src/domain/services/wallet_list_service.dart'; -import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/default_settings_migration.dart'; -import 'package:cake_wallet/entities/fiat_currency.dart'; -import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/entities/template.dart'; +import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/exchange_template.dart'; - -// import 'package:cake_wallet/src/domain/services/wallet_service.dart'; -// import 'package:cake_wallet/src/domain/services/fiat_convertation_service.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/entities/language.dart'; -// import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart'; - -bool isThemeChangerRegistered = false; +import 'package:cake_wallet/src/screens/root/root.dart'; final navigatorKey = GlobalKey(); @@ -94,41 +58,6 @@ void main() async { final templates = await Hive.openBox