diff --git a/lib/core/monero_account_label_validator.dart b/lib/core/monero_account_label_validator.dart index dcbac668f..2bc0fcccc 100644 --- a/lib/core/monero_account_label_validator.dart +++ b/lib/core/monero_account_label_validator.dart @@ -7,7 +7,7 @@ class MoneroLabelValidator extends TextValidator { MoneroLabelValidator({@required CryptoCurrency type}) : super( errorMessage: S.current.error_text_account_name, - pattern: '^[a-zA-Z0-9_]{1,15}\$', + pattern: '^[a-zA-Z0-9_ ]{1,15}\$', minLength: 1, maxLength: 15); } diff --git a/lib/di.dart b/lib/di.dart index 69b48056c..fd73da8fc 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -49,6 +49,7 @@ import 'package:cake_wallet/store/wallet_list_store.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'; +import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/rescan_view_model.dart'; @@ -274,7 +275,7 @@ Future setup( getIt.registerFactory(() => MoneroAccountListPage( accountListViewModel: getIt.get())); - getIt.registerFactory(() { + /*getIt.registerFactory(() { final wallet = getIt.get().wallet; if (wallet is MoneroWallet) { @@ -287,7 +288,20 @@ Future setup( getIt.registerFactory(() => MoneroAccountEditOrCreatePage( moneroAccountCreationViewModel: - getIt.get())); + getIt.get()));*/ + + getIt.registerFactoryParam( + (AccountListItem account, _) => + MoneroAccountEditOrCreateViewModel(( + getIt.get().wallet as MoneroWallet).accountList, + accountListItem: account)); + + getIt.registerFactoryParam((AccountListItem account, _) => + MoneroAccountEditOrCreatePage( + moneroAccountCreationViewModel: + getIt.get(param1: account))); getIt.registerFactory(() { final appStore = getIt.get(); diff --git a/lib/monero/monero_wallet.dart b/lib/monero/monero_wallet.dart index bf912a4fd..c7e2cff44 100644 --- a/lib/monero/monero_wallet.dart +++ b/lib/monero/monero_wallet.dart @@ -40,6 +40,10 @@ abstract class MoneroWalletBase extends WalletBase with Store { fullBalance: monero_wallet.getFullBalance(accountIndex: 0), unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0)); _onAccountChangeReaction = reaction((_) => account, (Account account) { + balance = MoneroBalance( + fullBalance: monero_wallet.getFullBalance(accountIndex: account.id), + unlockedBalance: + monero_wallet.getUnlockedBalance(accountIndex: account.id)); subaddressList.update(accountIndex: account.id); subaddress = subaddressList.subaddresses.first; address = subaddress.address; @@ -94,7 +98,7 @@ abstract class MoneroWalletBase extends WalletBase with Store { bool _isSavingAfterNewTransaction; Future init() async { - await accountList.update(); + accountList.update(); account = accountList.accounts.first; subaddressList.update(accountIndex: account.id ?? 0); subaddress = subaddressList.getAll().first; @@ -255,6 +259,7 @@ abstract class MoneroWalletBase extends WalletBase with Store { monero_wallet.rescanBlockchainAsync(); await startSync(); _askForUpdateBalance(); + accountList.update(); await _askForUpdateTransactionHistory(); await save(); await walletInfo.save(); @@ -362,17 +367,20 @@ abstract class MoneroWalletBase extends WalletBase with Store { void _onNewBlock(int height, int blocksLeft, double ptc) async { if (walletInfo.isRecovery) { - _askForUpdateTransactionHistory(); + await _askForUpdateTransactionHistory(); _askForUpdateBalance(); + accountList.update(); } if (blocksLeft < 100) { + await _askForUpdateTransactionHistory(); _askForUpdateBalance(); + accountList.update(); syncStatus = SyncedSyncStatus(); await _afterSyncSave(); if (walletInfo.isRecovery) { - setAsRecovered(); + await setAsRecovered(); } } else { syncStatus = SyncingSyncStatus(blocksLeft, ptc); diff --git a/lib/router.dart b/lib/router.dart index e9174e694..ef38c97f8 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; @@ -262,7 +263,8 @@ Route createRoute(RouteSettings settings) { case Routes.accountCreation: return CupertinoPageRoute( - builder: (_) => getIt.get()); + builder: (_) => getIt.get( + param1: settings.arguments as AccountListItem)); case Routes.addressBook: return MaterialPageRoute( diff --git a/lib/src/screens/dashboard/widgets/menu_widget.dart b/lib/src/screens/dashboard/widgets/menu_widget.dart index 7701d0ad3..f8de5ba8d 100644 --- a/lib/src/screens/dashboard/widgets/menu_widget.dart +++ b/lib/src/screens/dashboard/widgets/menu_widget.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; // FIXME: terrible design. @@ -147,7 +148,7 @@ class MenuWidgetState extends State { ), if (widget.dashboardViewModel.subname != null) - Text( + Observer(builder: (_) => Text( widget.dashboardViewModel.subname, style: TextStyle( color: Theme.of(context) @@ -156,7 +157,7 @@ class MenuWidgetState extends State { .decorationColor, fontWeight: FontWeight.w500, fontSize: 12), - ) + )) ], ), )) diff --git a/lib/src/screens/monero_accounts/monero_account_list_page.dart b/lib/src/screens/monero_accounts/monero_account_list_page.dart index 623f3cb90..47404e443 100644 --- a/lib/src/screens/monero_accounts/monero_account_list_page.dart +++ b/lib/src/screens/monero_accounts/monero_account_list_page.dart @@ -103,7 +103,12 @@ class MoneroAccountListPage extends StatelessWidget { accountListViewModel .select(account); Navigator.of(context).pop(); - }); + }, + onEdit: () async => + await Navigator.of(context) + .pushNamed( + Routes.accountCreation, + arguments: account)); }, ), isAlwaysShowScrollThumb diff --git a/lib/src/screens/monero_accounts/widgets/account_tile.dart b/lib/src/screens/monero_accounts/widgets/account_tile.dart index eb9c7f25d..f07539333 100644 --- a/lib/src/screens/monero_accounts/widgets/account_tile.dart +++ b/lib/src/screens/monero_accounts/widgets/account_tile.dart @@ -1,15 +1,19 @@ import 'package:flutter/material.dart'; +import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:cake_wallet/generated/i18n.dart'; class AccountTile extends StatelessWidget { AccountTile({ @required this.isCurrent, @required this.accountName, - @required this.onTap + @required this.onTap, + @required this.onEdit }); final bool isCurrent; final String accountName; - final VoidCallback onTap; + final Function() onTap; + final Function() onEdit; @override Widget build(BuildContext context) { @@ -20,7 +24,7 @@ class AccountTile extends StatelessWidget { ? Theme.of(context).textTheme.subtitle.color : Theme.of(context).textTheme.display4.color; - return GestureDetector( + final Widget cell = GestureDetector( onTap: onTap, child: Container( height: 77, @@ -39,5 +43,17 @@ class AccountTile extends StatelessWidget { ), ), ); + + return isCurrent ? cell : Slidable( + key: Key(accountName), + child: cell, + actionPane: SlidableDrawerActionPane(), + secondaryActions: [ + IconSlideAction( + caption: S.of(context).edit, + color: Colors.blue, + icon: Icons.edit, + onTap: () => onEdit?.call()) + ]); } } \ No newline at end of file diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index 30d2b7cea..5126a1527 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -117,7 +117,7 @@ class ReceivePage extends BasePage { Icons.arrow_forward_ios, size: 14, color: - Theme.of(context).textTheme.display1.color, + Theme.of(context).textTheme.display1.color, )); } @@ -130,7 +130,7 @@ class ReceivePage extends BasePage { Icons.add, size: 20, color: - Theme.of(context).textTheme.display1.color, + Theme.of(context).textTheme.display1.color, )); } @@ -140,13 +140,13 @@ class ReceivePage extends BasePage { addressListViewModel.address.address; final backgroundColor = isCurrent ? Theme.of(context) - .textTheme - .display3 - .decorationColor + .textTheme + .display3 + .decorationColor : Theme.of(context) - .textTheme - .display2 - .decorationColor; + .textTheme + .display2 + .decorationColor; final textColor = isCurrent ? Theme.of(context).textTheme.display3.color : Theme.of(context).textTheme.display2.color; @@ -155,8 +155,7 @@ class ReceivePage extends BasePage { isCurrent: isCurrent, backgroundColor: backgroundColor, textColor: textColor, - onTap: (_) => - addressListViewModel.setAddress(item), + onTap: (_) => addressListViewModel.setAddress(item), onEdit: () => Navigator.of(context).pushNamed( Routes.newSubaddress, arguments: item)); @@ -166,11 +165,11 @@ class ReceivePage extends BasePage { return index != 0 ? cell : ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(30), - topRight: Radius.circular(30)), - child: cell, - ); + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30), + topRight: Radius.circular(30)), + child: cell, + ); })), ], ), diff --git a/lib/src/screens/receive/widgets/address_cell.dart b/lib/src/screens/receive/widgets/address_cell.dart index 6306ae209..703067dcf 100644 --- a/lib/src/screens/receive/widgets/address_cell.dart +++ b/lib/src/screens/receive/widgets/address_cell.dart @@ -14,6 +14,7 @@ class AddressCell extends StatelessWidget { address: item.address, name: item.name, isCurrent: isCurrent, + isPrimary: item.isPrimary, backgroundColor: backgroundColor, textColor: textColor, onTap: onTap, @@ -23,6 +24,7 @@ class AddressCell extends StatelessWidget { {@required this.address, @required this.name, @required this.isCurrent, + @required this.isPrimary, @required this.backgroundColor, @required this.textColor, this.onTap, @@ -31,6 +33,7 @@ class AddressCell extends StatelessWidget { final String address; final String name; final bool isCurrent; + final bool isPrimary; final Color backgroundColor; final Color textColor; final Function(String) onTap; @@ -56,7 +59,7 @@ class AddressCell extends StatelessWidget { ), )); - return isCurrent + return (isCurrent || isPrimary) ? cell : Slidable( key: Key(address), diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index e76a663ec..1c632396a 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -1,5 +1,10 @@ import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; +import 'package:cake_wallet/entities/transaction_history.dart'; +import 'package:cake_wallet/monero/account.dart'; +import 'package:cake_wallet/monero/monero_balance.dart'; +import 'package:cake_wallet/monero/monero_transaction_history.dart'; +import 'package:cake_wallet/monero/monero_transaction_info.dart'; import 'package:cake_wallet/monero/monero_wallet.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart'; import 'package:cake_wallet/entities/crypto_currency.dart'; @@ -74,27 +79,45 @@ abstract class DashboardViewModelBase with Store { wallet ??= appStore.wallet; type = wallet.type; - transactions = ObservableList.of(wallet - .transactionHistory.transactions.values - .map((transaction) => TransactionListItem( - transaction: transaction, - balanceViewModel: balanceViewModel, - settingsStore: appStore.settingsStore))); - _reaction = reaction((_) => appStore.wallet, _onWalletChange); - // FIXME: fixme - connectMapToListWithTransform( - appStore.wallet.transactionHistory.transactions, - transactions, - (TransactionInfo val) => TransactionListItem( - transaction: val, - balanceViewModel: balanceViewModel, - settingsStore: appStore.settingsStore)); final _wallet = wallet; if (_wallet is MoneroWallet) { subname = _wallet.account?.label; + + _onMoneroAccountChangeReaction = reaction((_) => _wallet.account, + (Account account) => _onMoneroAccountChange(_wallet)); + + _onMoneroBalanceChangeReaction = reaction((_) => + _wallet.balance, (MoneroBalance balance) => + _onMoneroTransactionsUpdate(_wallet)); + + final _accountTransactions = _wallet + .transactionHistory.transactions.values + .where((tx) => tx.accountIndex == _wallet.account.id).toList(); + + transactions = ObservableList.of(_accountTransactions + .map((transaction) => TransactionListItem( + transaction: transaction, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore))); + } else { + transactions = ObservableList.of(wallet + .transactionHistory.transactions.values + .map((transaction) => TransactionListItem( + transaction: transaction, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore))); + + // FIXME: fixme + connectMapToListWithTransform( + appStore.wallet.transactionHistory.transactions, + transactions, + (TransactionInfo val) => TransactionListItem( + transaction: val, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore)); } } @@ -170,6 +193,10 @@ abstract class DashboardViewModelBase with Store { ReactionDisposer _reaction; + ReactionDisposer _onMoneroAccountChangeReaction; + + ReactionDisposer _onMoneroBalanceChangeReaction; + Future reconnect() async { final node = appStore.settingsStore.getCurrentNode(wallet.type); await wallet.connectToNode(node: node); @@ -179,11 +206,50 @@ abstract class DashboardViewModelBase with Store { void _onWalletChange(WalletBase wallet) { this.wallet = wallet; name = wallet.name; + + if (wallet is MoneroWallet) { + subname = wallet.account?.label; + + _onMoneroAccountChangeReaction?.reaction?.dispose(); + _onMoneroBalanceChangeReaction?.reaction?.dispose(); + + _onMoneroAccountChangeReaction = reaction((_) => wallet.account, + (Account account) => _onMoneroAccountChange(wallet)); + + _onMoneroBalanceChangeReaction = reaction((_) => + wallet.balance, (MoneroBalance balance) => + _onMoneroTransactionsUpdate(wallet)); + + _onMoneroTransactionsUpdate(wallet); + } else { + transactions.clear(); + + transactions.addAll(wallet.transactionHistory.transactions.values.map( + (transaction) => TransactionListItem( + transaction: transaction, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore))); + } + } + + @action + void _onMoneroAccountChange(MoneroWallet wallet) { + subname = wallet.account?.label; + _onMoneroTransactionsUpdate(wallet); + } + + @action + void _onMoneroTransactionsUpdate(MoneroWallet wallet) { transactions.clear(); - transactions.addAll(wallet.transactionHistory.transactions.values.map( - (transaction) => TransactionListItem( - transaction: transaction, - balanceViewModel: balanceViewModel, - settingsStore: appStore.settingsStore))); + + final _accountTransactions = wallet + .transactionHistory.transactions.values + .where((tx) => tx.accountIndex == wallet.account.id).toList(); + + transactions.addAll(_accountTransactions + .map((transaction) => TransactionListItem( + transaction: transaction, + balanceViewModel: balanceViewModel, + settingsStore: appStore.settingsStore))); } } diff --git a/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart index 33e9dd1e5..d32f8eadd 100644 --- a/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart +++ b/lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart @@ -13,6 +13,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store { {AccountListItem accountListItem}) : state = InitialExecutionState(), isEdit = accountListItem != null, + label = accountListItem?.label??'', _accountListItem = accountListItem; final bool isEdit; diff --git a/lib/view_model/wallet_address_list/wallet_address_list_item.dart b/lib/view_model/wallet_address_list/wallet_address_list_item.dart index c6d8915ab..286cb8f94 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_item.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_item.dart @@ -2,10 +2,11 @@ import 'package:flutter/foundation.dart'; import 'package:cake_wallet/utils/list_item.dart'; class WalletAddressListItem extends ListItem { - const WalletAddressListItem({@required this.address, this.name, this.id}) - : super(); + const WalletAddressListItem({@required this.address, @required this.isPrimary, + this.name, this.id}) : super(); final int id; + final bool isPrimary; final String address; final String name; diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index e8c897bfc..aca1ef808 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -97,16 +97,28 @@ abstract class WalletAddressListViewModelBase with Store { final addressList = ObservableList(); if (wallet is MoneroWallet) { - addressList.addAll(wallet.subaddressList.subaddresses.map((subaddress) => - WalletAddressListItem( - id: subaddress.id, - name: subaddress.label, - address: subaddress.address))); + final primaryAddress = wallet.subaddressList.subaddresses.first; + addressList.addAll(wallet.subaddressList.subaddresses.map((subaddress) { + final isPrimary = subaddress == primaryAddress; + + return WalletAddressListItem( + id: subaddress.id, + isPrimary: isPrimary, + name: subaddress.label, + address: subaddress.address); + })); } if (wallet is BitcoinWallet) { - final bitcoinAddresses = wallet.addresses.map((addr) => - WalletAddressListItem(name: addr.label, address: addr.address)); + final primaryAddress = wallet.addresses.first; + final bitcoinAddresses = wallet.addresses.map((addr) { + final isPrimary = addr == primaryAddress; + + return WalletAddressListItem( + isPrimary: isPrimary, + name: addr.label, + address: addr.address); + }); addressList.addAll(bitcoinAddresses); }