CAKE-158 | made account tile slidable; added onEdit function to account tile; added ability to edit wallet account name; added isFirstAddress parameter to wallet_address_list_view_model.dart and made first address in the address list not editable (receive page); wrapped wallet account name in the wallet menu title by observer; added _moneroTransactionsChangeReaction to dashboard_view_model.dart

OleksandrSobol committed Nov 17, 2020 at 20:16 UTC 23abacd2526c9d898422de3abc055f89fd9070bb
11 files changed +97 -11
lib/core/monero_account_label_validator.dart
+1 -1
@@ -7,7 +7,7 @@ class MoneroLabelValidator extends TextValidator {
7 MoneroLabelValidator({@required CryptoCurrency type})
8 : super(
9 errorMessage: S.current.error_text_account_name,
10 - pattern: '^[a-zA-Z0-9_]{1,15}\$',
10 + pattern: '^[a-zA-Z0-9_ ]{1,15}\$',
11 minLength: 1,
12 maxLength: 15);
13 }
lib/di.dart
+16 -2
@@ -48,6 +48,7 @@ import 'package:cake_wallet/store/wallet_list_store.dart';
48 import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
49 import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
50 import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
51 +import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
52 import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
53 import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
54 import 'package:cake_wallet/view_model/rescan_view_model.dart';
@@ -272,7 +273,7 @@ Future setup(
273 getIt.registerFactory(() => MoneroAccountListPage(
274 accountListViewModel: getIt.get<MoneroAccountListViewModel>()));
275
275 - getIt.registerFactory(() {
276 + /*getIt.registerFactory(() {
277 final wallet = getIt.get<AppStore>().wallet;
278
279 if (wallet is MoneroWallet) {
@@ -285,7 +286,20 @@ Future setup(
286
287 getIt.registerFactory(() => MoneroAccountEditOrCreatePage(
288 moneroAccountCreationViewModel:
288 - getIt.get<MoneroAccountEditOrCreateViewModel>()));
289 + getIt.get<MoneroAccountEditOrCreateViewModel>()));*/
290 +
291 + getIt.registerFactoryParam<MoneroAccountEditOrCreateViewModel,
292 + AccountListItem, void>(
293 + (AccountListItem account, _) =>
294 + MoneroAccountEditOrCreateViewModel((
295 + getIt.get<AppStore>().wallet as MoneroWallet).accountList,
296 + accountListItem: account));
297 +
298 + getIt.registerFactoryParam<MoneroAccountEditOrCreatePage,
299 + AccountListItem, void>((AccountListItem account, _) =>
300 + MoneroAccountEditOrCreatePage(
301 + moneroAccountCreationViewModel:
302 + getIt.get<MoneroAccountEditOrCreateViewModel>(param1: account)));
303
304 getIt.registerFactory(() {
305 final appStore = getIt.get<AppStore>();
lib/router.dart
+3 -1
@@ -3,6 +3,7 @@ import 'package:cake_wallet/entities/transaction_description.dart';
3 import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
4 import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
5 import 'package:cake_wallet/store/settings_store.dart';
6 +import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
7 import 'package:flutter/cupertino.dart';
8 import 'package:flutter/material.dart';
9 import 'package:cake_wallet/routes.dart';
@@ -261,7 +262,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
262
263 case Routes.accountCreation:
264 return CupertinoPageRoute<String>(
264 - builder: (_) => getIt.get<MoneroAccountEditOrCreatePage>());
265 + builder: (_) => getIt.get<MoneroAccountEditOrCreatePage>(
266 + param1: settings.arguments as AccountListItem));
267
268 case Routes.addressBook:
269 return MaterialPageRoute<void>(
lib/src/screens/dashboard/widgets/menu_widget.dart
+3 -2
@@ -5,6 +5,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
5 import 'package:cake_wallet/entities/wallet_type.dart';
6 import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
7 import 'package:flutter/rendering.dart';
8 +import 'package:flutter_mobx/flutter_mobx.dart';
9
10 // FIXME: terrible design.
11
@@ -147,7 +148,7 @@ class MenuWidgetState extends State<MenuWidget> {
148 ),
149 if (widget.dashboardViewModel.subname !=
150 null)
150 - Text(
151 + Observer(builder: (_) => Text(
152 widget.dashboardViewModel.subname,
153 style: TextStyle(
154 color: Theme.of(context)
@@ -156,7 +157,7 @@ class MenuWidgetState extends State<MenuWidget> {
157 .decorationColor,
158 fontWeight: FontWeight.w500,
159 fontSize: 12),
159 - )
160 + ))
161 ],
162 ),
163 ))
lib/src/screens/monero_accounts/monero_account_list_page.dart
+6 -1
@@ -103,7 +103,12 @@ class MoneroAccountListPage extends StatelessWidget {
103 accountListViewModel
104 .select(account);
105 Navigator.of(context).pop();
106 - });
106 + },
107 + onEdit: () async =>
108 + await Navigator.of(context)
109 + .pushNamed(
110 + Routes.accountCreation,
111 + arguments: account));
112 },
113 ),
114 isAlwaysShowScrollThumb
lib/src/screens/monero_accounts/widgets/account_tile.dart
+19 -3
@@ -1,15 +1,19 @@
1 import 'package:flutter/material.dart';
2 +import 'package:flutter_slidable/flutter_slidable.dart';
3 +import 'package:cake_wallet/generated/i18n.dart';
4
5 class AccountTile extends StatelessWidget {
6 AccountTile({
7 @required this.isCurrent,
8 @required this.accountName,
7 - @required this.onTap
9 + @required this.onTap,
10 + @required this.onEdit
11 });
12
13 final bool isCurrent;
14 final String accountName;
12 - final VoidCallback onTap;
15 + final Function() onTap;
16 + final Function() onEdit;
17
18 @override
19 Widget build(BuildContext context) {
@@ -20,7 +24,7 @@ class AccountTile extends StatelessWidget {
24 ? Theme.of(context).textTheme.subtitle.color
25 : Theme.of(context).textTheme.display4.color;
26
23 - return GestureDetector(
27 + final Widget cell = GestureDetector(
28 onTap: onTap,
29 child: Container(
30 height: 77,
@@ -39,5 +43,17 @@ class AccountTile extends StatelessWidget {
43 ),
44 ),
45 );
46 +
47 + return isCurrent ? cell : Slidable(
48 + key: Key(accountName),
49 + child: cell,
50 + actionPane: SlidableDrawerActionPane(),
51 + secondaryActions: <Widget>[
52 + IconSlideAction(
53 + caption: S.of(context).edit,
54 + color: Colors.blue,
55 + icon: Icons.edit,
56 + onTap: () => onEdit?.call())
57 + ]);
58 }
59 }
\ No newline at end of file
lib/src/screens/receive/receive_page.dart
+3
@@ -116,6 +116,8 @@ class ReceivePage extends BasePage {
116 }
117
118 if (item is WalletAddressListItem) {
119 + final isFirst = addressListViewModel.isFirstAddress;
120 + addressListViewModel.isFirstAddress = false;
121 cell = Observer(builder: (_) {
122 final isCurrent = item.address ==
123 addressListViewModel.address.address;
@@ -134,6 +136,7 @@ class ReceivePage extends BasePage {
136
137 return AddressCell.fromItem(item,
138 isCurrent: isCurrent,
139 + isFirstAddress: isFirst,
140 backgroundColor: backgroundColor,
141 textColor: textColor,
142 onTap: (_) => addressListViewModel.setAddress(item),
lib/src/screens/receive/widgets/address_cell.dart
+5 -1
@@ -6,6 +6,7 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i
6 class AddressCell extends StatelessWidget {
7 factory AddressCell.fromItem(WalletAddressListItem item,
8 {@required bool isCurrent,
9 + @required bool isFirstAddress,
10 @required Color backgroundColor,
11 @required Color textColor,
12 Function(String) onTap,
@@ -14,6 +15,7 @@ class AddressCell extends StatelessWidget {
15 address: item.address,
16 name: item.name,
17 isCurrent: isCurrent,
18 + isFirstAddress: isFirstAddress,
19 backgroundColor: backgroundColor,
20 textColor: textColor,
21 onTap: onTap,
@@ -23,6 +25,7 @@ class AddressCell extends StatelessWidget {
25 {@required this.address,
26 @required this.name,
27 @required this.isCurrent,
28 + @required this.isFirstAddress,
29 @required this.backgroundColor,
30 @required this.textColor,
31 this.onTap,
@@ -31,6 +34,7 @@ class AddressCell extends StatelessWidget {
34 final String address;
35 final String name;
36 final bool isCurrent;
37 + final bool isFirstAddress;
38 final Color backgroundColor;
39 final Color textColor;
40 final Function(String) onTap;
@@ -56,7 +60,7 @@ class AddressCell extends StatelessWidget {
60 ),
61 ));
62
59 - return isCurrent
63 + return (isCurrent || isFirstAddress)
64 ? cell
65 : Slidable(
66 key: Key(address),
lib/view_model/dashboard/dashboard_view_model.dart
+36
@@ -1,6 +1,8 @@
1 import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
2 import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
3 +import 'package:cake_wallet/entities/transaction_history.dart';
4 import 'package:cake_wallet/monero/account.dart';
5 +import 'package:cake_wallet/monero/monero_transaction_history.dart';
6 import 'package:cake_wallet/monero/monero_wallet.dart';
7 import 'package:cake_wallet/entities/balance_display_mode.dart';
8 import 'package:cake_wallet/entities/crypto_currency.dart';
@@ -85,6 +87,10 @@ abstract class DashboardViewModelBase with Store {
87 _onMoneroAccountChangeReaction = reaction((_) => _wallet.account,
88 (Account account) => _onMoneroAccountChange(_wallet));
89
90 + _onMoneroTransactionsChangeReaction = reaction((_) => _wallet.transactionHistory,
91 + (MoneroTransactionHistory transactionHistory) =>
92 + _onMoneroTransactionsChange(_wallet));
93 +
94 final _accountTransactions = _wallet
95 .transactionHistory.transactions.values
96 .where((tx) => tx.accountIndex == _wallet.account.id).toList();
@@ -187,6 +193,8 @@ abstract class DashboardViewModelBase with Store {
193
194 ReactionDisposer _onMoneroAccountChangeReaction;
195
196 + ReactionDisposer _onMoneroTransactionsChangeReaction;
197 +
198 Future<void> reconnect() async {
199 final node = appStore.settingsStore.getCurrentNode(wallet.type);
200 await wallet.connectToNode(node: node);
@@ -199,6 +207,18 @@ abstract class DashboardViewModelBase with Store {
207 transactions.clear();
208
209 if (wallet is MoneroWallet) {
210 + subname = wallet.account?.label;
211 +
212 + _onMoneroAccountChangeReaction?.reaction?.dispose();
213 + _onMoneroTransactionsChangeReaction?.reaction?.dispose();
214 +
215 + _onMoneroAccountChangeReaction = reaction((_) => wallet.account,
216 + (Account account) => _onMoneroAccountChange(wallet));
217 +
218 + _onMoneroTransactionsChangeReaction = reaction((_) => wallet.transactionHistory,
219 + (MoneroTransactionHistory transactionHistory) =>
220 + _onMoneroTransactionsChange(wallet));
221 +
222 final _accountTransactions = wallet
223 .transactionHistory.transactions.values
224 .where((tx) => tx.accountIndex == wallet.account.id).toList();
@@ -219,6 +239,22 @@ abstract class DashboardViewModelBase with Store {
239
240 @action
241 void _onMoneroAccountChange(MoneroWallet wallet) {
242 + subname = wallet.account?.label;
243 + transactions.clear();
244 +
245 + final _accountTransactions = wallet
246 + .transactionHistory.transactions.values
247 + .where((tx) => tx.accountIndex == wallet.account.id).toList();
248 +
249 + transactions.addAll(_accountTransactions
250 + .map((transaction) => TransactionListItem(
251 + transaction: transaction,
252 + balanceViewModel: balanceViewModel,
253 + settingsStore: appStore.settingsStore)));
254 + }
255 +
256 + @action
257 + void _onMoneroTransactionsChange(MoneroWallet wallet) {
258 transactions.clear();
259
260 final _accountTransactions = wallet
lib/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart
+1
@@ -13,6 +13,7 @@ abstract class MoneroAccountEditOrCreateViewModelBase with Store {
13 {AccountListItem accountListItem})
14 : state = InitialExecutionState(),
15 isEdit = accountListItem != null,
16 + label = accountListItem?.label??'',
17 _accountListItem = accountListItem;
18
19 final bool isEdit;
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+4
@@ -67,6 +67,9 @@ abstract class WalletAddressListViewModelBase with Store {
67 @observable
68 String amount;
69
70 + @observable
71 + bool isFirstAddress;
72 +
73 @computed
74 WalletType get type => _wallet.type;
75
@@ -97,6 +100,7 @@ abstract class WalletAddressListViewModelBase with Store {
100 final addressList = ObservableList<ListItem>();
101
102 if (wallet is MoneroWallet) {
103 + isFirstAddress = true;
104 addressList.addAll(wallet.subaddressList.subaddresses.map((subaddress) =>
105 WalletAddressListItem(
106 id: subaddress.id,