Cw 426 replace trash and swipe with edit icons (#974)

* feat: Replace trash and swipe with edit icons on node list item - replaces yellow Test button with red Delete node button with confirmation on the edit node page * feat: make node indicator icons bigger (figma comment) * feat: Replace trash and swipe with edit icons on wallet list page and create wallet_edit_page.dart * fix: make delete buttons red * fix: make wallet name wrap when it is too long * refactor: improve logic & fix observer not refreshing * fix: add string * feat: remove the confirmation pop-up for switching between wallets - which was another item on the jira issue * fix: remove slideable widgets from node list * feat: add edit button to currently selected node & disable deleting if selected * fix: rename wallet also renames to new wallet files * feat: make sure edits can't overlap existing names * fix: improve rename flow, fix electrum transactions refresh & add delete old logic * fix: also fix rename for monero & haven * refactor: fix identations * refactor: dont declare the current wallet twice * refactor: missing newWalletInfo.id * fix: dont unnecessarily load the current wallet * fix: remove unnecessary reaction * feat: make save button disabled until the text is changed * feat: make walletEditViewModel and make state useful for pending actions * fix: add back reaction for desktop flow * - Remove un-necessary code - Format Edit page --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>

Rafael Saes committed Jul 12, 2023 at 20:20 UTC d4969633b04af1a63ccbf90070beeb3b2e614d5e
48 files changed +676 -255
cw_bitcoin/lib/bitcoin_wallet_service.dart
+26 -3
@@ -52,9 +52,32 @@ class BitcoinWalletService extends WalletService<
52 }
53
54 @override
55 - Future<void> remove(String wallet) async =>
56 - File(await pathForWalletDir(name: wallet, type: WalletType.bitcoin))
57 - .delete(recursive: true);
55 + Future<void> remove(String wallet) async {
56 + File(await pathForWalletDir(name: wallet, type: getType()))
57 + .delete(recursive: true);
58 + final walletInfo = walletInfoSource.values.firstWhereOrNull(
59 + (info) => info.id == WalletBase.idFor(wallet, getType()))!;
60 + await walletInfoSource.delete(walletInfo.key);
61 + }
62 +
63 + @override
64 + Future<void> rename(String currentName, String password, String newName) async {
65 + final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
66 + (info) => info.id == WalletBase.idFor(currentName, getType()))!;
67 + final currentWallet = await BitcoinWalletBase.open(
68 + password: password,
69 + name: currentName,
70 + walletInfo: currentWalletInfo,
71 + unspentCoinsInfo: unspentCoinsInfoSource);
72 +
73 + await currentWallet.renameWalletFiles(newName);
74 +
75 + final newWalletInfo = currentWalletInfo;
76 + newWalletInfo.id = WalletBase.idFor(newName, getType());
77 + newWalletInfo.name = newName;
78 +
79 + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
80 + }
81
82 @override
83 Future<BitcoinWallet> restoreFromKeys(
cw_bitcoin/lib/electrum_transaction_history.dart
+3 -3
@@ -9,7 +9,7 @@ import 'package:cw_bitcoin/electrum_transaction_info.dart';
9
10 part 'electrum_transaction_history.g.dart';
11
12 -const _transactionsHistoryFileName = 'transactions.json';
12 +const transactionsHistoryFileName = 'transactions.json';
13
14 class ElectrumTransactionHistory = ElectrumTransactionHistoryBase
15 with _$ElectrumTransactionHistory;
@@ -42,7 +42,7 @@ abstract class ElectrumTransactionHistoryBase
42 try {
43 final dirPath =
44 await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
45 - final path = '$dirPath/$_transactionsHistoryFileName';
45 + final path = '$dirPath/$transactionsHistoryFileName';
46 final data =
47 json.encode({'height': _height, 'transactions': transactions});
48 await writeData(path: path, password: _password, data: data);
@@ -59,7 +59,7 @@ abstract class ElectrumTransactionHistoryBase
59 Future<Map<String, dynamic>> _read() async {
60 final dirPath =
61 await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
62 - final path = '$dirPath/$_transactionsHistoryFileName';
62 + final path = '$dirPath/$transactionsHistoryFileName';
63 final content = await read(path: path, password: _password);
64 return json.decode(content) as Map<String, dynamic>;
65 }
cw_bitcoin/lib/electrum_wallet.dart
+23
@@ -1,5 +1,6 @@
1 import 'dart:async';
2 import 'dart:convert';
3 +import 'dart:io';
4 import 'dart:math';
5 import 'package:cw_core/unspent_coins_info.dart';
6 import 'package:hive/hive.dart';
@@ -430,6 +431,28 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
431 await transactionHistory.save();
432 }
433
434 + Future<void> renameWalletFiles(String newWalletName) async {
435 + final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type);
436 + final currentWalletFile = File(currentWalletPath);
437 +
438 + final currentDirPath =
439 + await pathForWalletDir(name: walletInfo.name, type: type);
440 + final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName');
441 +
442 + // Copies current wallet files into new wallet name's dir and files
443 + if (currentWalletFile.existsSync()) {
444 + final newWalletPath = await pathForWallet(name: newWalletName, type: type);
445 + await currentWalletFile.copy(newWalletPath);
446 + }
447 + if (currentTransactionsFile.existsSync()) {
448 + final newDirPath = await pathForWalletDir(name: newWalletName, type: type);
449 + await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName');
450 + }
451 +
452 + // Delete old name's dir and files
453 + await Directory(currentDirPath).delete(recursive: true);
454 + }
455 +
456 @override
457 Future<void> changePassword(String password) async {
458 _password = password;
cw_bitcoin/lib/litecoin_wallet_service.dart
+26 -3
@@ -53,9 +53,32 @@ class LitecoinWalletService extends WalletService<
53 }
54
55 @override
56 - Future<void> remove(String wallet) async =>
57 - File(await pathForWalletDir(name: wallet, type: getType()))
58 - .delete(recursive: true);
56 + Future<void> remove(String wallet) async {
57 + File(await pathForWalletDir(name: wallet, type: getType()))
58 + .delete(recursive: true);
59 + final walletInfo = walletInfoSource.values.firstWhereOrNull(
60 + (info) => info.id == WalletBase.idFor(wallet, getType()))!;
61 + await walletInfoSource.delete(walletInfo.key);
62 + }
63 +
64 + @override
65 + Future<void> rename(String currentName, String password, String newName) async {
66 + final currentWalletInfo = walletInfoSource.values.firstWhereOrNull(
67 + (info) => info.id == WalletBase.idFor(currentName, getType()))!;
68 + final currentWallet = await LitecoinWalletBase.open(
69 + password: password,
70 + name: currentName,
71 + walletInfo: currentWalletInfo,
72 + unspentCoinsInfo: unspentCoinsInfoSource);
73 +
74 + await currentWallet.renameWalletFiles(newName);
75 +
76 + final newWalletInfo = currentWalletInfo;
77 + newWalletInfo.id = WalletBase.idFor(newName, getType());
78 + newWalletInfo.name = newName;
79 +
80 + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
81 + }
82
83 @override
84 Future<LitecoinWallet> restoreFromKeys(
cw_core/lib/wallet_service.dart
+2
@@ -17,4 +17,6 @@ abstract class WalletService<N extends WalletCredentials,
17 Future<bool> isWalletExit(String name);
18
19 Future<void> remove(String wallet);
20 +
21 + Future<void> rename(String name, String password, String newName);
22 }
cw_haven/lib/haven_wallet.dart
+25
@@ -1,5 +1,7 @@
1 import 'dart:async';
2 +import 'dart:io';
3 import 'package:cw_core/crypto_currency.dart';
4 +import 'package:cw_core/pathForWallet.dart';
5 import 'package:cw_core/transaction_priority.dart';
6 import 'package:cw_haven/haven_transaction_creation_credentials.dart';
7 import 'package:cw_core/monero_amount_format.dart';
@@ -251,6 +253,29 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
253 await haven_wallet.store();
254 }
255
256 + Future<void> renameWalletFiles(String newWalletName) async {
257 + final currentWalletPath = await pathForWallet(name: name, type: type);
258 + final currentCacheFile = File(currentWalletPath);
259 + final currentKeysFile = File('$currentWalletPath.keys');
260 + final currentAddressListFile = File('$currentWalletPath.address.txt');
261 +
262 + final newWalletPath = await pathForWallet(name: newWalletName, type: type);
263 +
264 + // Copies current wallet files into new wallet name's dir and files
265 + if (currentCacheFile.existsSync()) {
266 + await currentCacheFile.copy(newWalletPath);
267 + }
268 + if (currentKeysFile.existsSync()) {
269 + await currentKeysFile.copy('$newWalletPath.keys');
270 + }
271 + if (currentAddressListFile.existsSync()) {
272 + await currentAddressListFile.copy('$newWalletPath.address.txt');
273 + }
274 +
275 + // Delete old name's dir and files
276 + await Directory(currentWalletPath).delete(recursive: true);
277 + }
278 +
279 @override
280 Future<void> changePassword(String password) async {
281 haven_wallet.setPasswordSync(password);
cw_haven/lib/haven_wallet_service.dart
+20
@@ -149,6 +149,26 @@ class HavenWalletService extends WalletService<
149 if (isExist) {
150 await file.delete(recursive: true);
151 }
152 +
153 + final walletInfo = walletInfoSource.values
154 + .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
155 + await walletInfoSource.delete(walletInfo.key);
156 + }
157 +
158 + @override
159 + Future<void> rename(
160 + String currentName, String password, String newName) async {
161 + final currentWalletInfo = walletInfoSource.values.firstWhere(
162 + (info) => info.id == WalletBase.idFor(currentName, getType()));
163 + final currentWallet = HavenWallet(walletInfo: currentWalletInfo);
164 +
165 + await currentWallet.renameWalletFiles(newName);
166 +
167 + final newWalletInfo = currentWalletInfo;
168 + newWalletInfo.id = WalletBase.idFor(newName, getType());
169 + newWalletInfo.name = newName;
170 +
171 + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
172 }
173
174 @override
cw_monero/lib/monero_wallet.dart
+25 -1
@@ -1,4 +1,6 @@
1 import 'dart:async';
2 +import 'dart:io';
3 +import 'package:cw_core/pathForWallet.dart';
4 import 'package:cw_core/transaction_priority.dart';
5 import 'package:cw_core/monero_amount_format.dart';
6 import 'package:cw_monero/monero_transaction_creation_exception.dart';
@@ -6,7 +8,6 @@ import 'package:cw_monero/monero_transaction_info.dart';
8 import 'package:cw_monero/monero_wallet_addresses.dart';
9 import 'package:cw_core/monero_wallet_utils.dart';
10 import 'package:cw_monero/api/structs/pending_transaction.dart';
9 -import 'package:flutter/foundation.dart';
11 import 'package:mobx/mobx.dart';
12 import 'package:cw_monero/api/transaction_history.dart'
13 as monero_transaction_history;
@@ -267,6 +268,29 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
268 await monero_wallet.store();
269 }
270
271 + Future<void> renameWalletFiles(String newWalletName) async {
272 + final currentWalletPath = await pathForWallet(name: name, type: type);
273 + final currentCacheFile = File(currentWalletPath);
274 + final currentKeysFile = File('$currentWalletPath.keys');
275 + final currentAddressListFile = File('$currentWalletPath.address.txt');
276 +
277 + final newWalletPath = await pathForWallet(name: newWalletName, type: type);
278 +
279 + // Copies current wallet files into new wallet name's dir and files
280 + if (currentCacheFile.existsSync()) {
281 + await currentCacheFile.copy(newWalletPath);
282 + }
283 + if (currentKeysFile.existsSync()) {
284 + await currentKeysFile.copy('$newWalletPath.keys');
285 + }
286 + if (currentAddressListFile.existsSync()) {
287 + await currentAddressListFile.copy('$newWalletPath.address.txt');
288 + }
289 +
290 + // Delete old name's dir and files
291 + await Directory(currentWalletPath).delete(recursive: true);
292 + }
293 +
294 @override
295 Future<void> changePassword(String password) async {
296 monero_wallet.setPasswordSync(password);
cw_monero/lib/monero_wallet_service.dart
+20
@@ -146,6 +146,26 @@ class MoneroWalletService extends WalletService<
146 if (isExist) {
147 await file.delete(recursive: true);
148 }
149 +
150 + final walletInfo = walletInfoSource.values
151 + .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
152 + await walletInfoSource.delete(walletInfo.key);
153 + }
154 +
155 + @override
156 + Future<void> rename(
157 + String currentName, String password, String newName) async {
158 + final currentWalletInfo = walletInfoSource.values.firstWhere(
159 + (info) => info.id == WalletBase.idFor(currentName, getType()));
160 + final currentWallet = MoneroWallet(walletInfo: currentWalletInfo);
161 +
162 + await currentWallet.renameWalletFiles(newName);
163 +
164 + final newWalletInfo = currentWalletInfo;
165 + newWalletInfo.id = WalletBase.idFor(newName, getType());
166 + newWalletInfo.name = newName;
167 +
168 + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo);
169 }
170
171 @override
lib/core/key_service.dart
+7
@@ -21,4 +21,11 @@ class KeyService {
21
22 await _secureStorage.write(key: key, value: encodedPassword);
23 }
24 +
25 + Future<void> deleteWalletPassword({required String walletName}) async {
26 + final key = generateStoreKeyFor(
27 + key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
28 +
29 + await _secureStorage.delete(key: key);
30 + }
31 }
lib/core/wallet_loading_service.dart
+54 -39
@@ -7,43 +7,58 @@ import 'package:cw_core/wallet_type.dart';
7 import 'package:shared_preferences/shared_preferences.dart';
8
9 class WalletLoadingService {
10 - WalletLoadingService(
11 - this.sharedPreferences,
12 - this.keyService,
13 - this.walletServiceFactory);
14 -
15 - final SharedPreferences sharedPreferences;
16 - final KeyService keyService;
17 - final WalletService Function(WalletType type) walletServiceFactory;
18 -
19 - Future<WalletBase> load(WalletType type, String name) async {
20 - final walletService = walletServiceFactory.call(type);
21 - final password = await keyService.getWalletPassword(walletName: name);
22 - final wallet = await walletService.openWallet(name, password);
23 -
24 - if (type == WalletType.monero) {
25 - await updateMoneroWalletPassword(wallet);
26 - }
27 -
28 - return wallet;
29 - }
30 -
31 - Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
32 - final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name);
33 - var isPasswordUpdated = sharedPreferences.getBool(key) ?? false;
34 -
35 - if (isPasswordUpdated) {
36 - return;
37 - }
38 -
39 - final password = generateWalletPassword();
40 - // Save new generated password with backup key for case where
41 - // wallet will change password, but it will fail to update in secure storage
42 - final bakWalletName = '#__${wallet.name}_bak__#';
43 - await keyService.saveWalletPassword(walletName: bakWalletName, password: password);
44 - await wallet.changePassword(password);
45 - await keyService.saveWalletPassword(walletName: wallet.name, password: password);
46 - isPasswordUpdated = true;
47 - await sharedPreferences.setBool(key, isPasswordUpdated);
48 - }
10 + WalletLoadingService(
11 + this.sharedPreferences, this.keyService, this.walletServiceFactory);
12 +
13 + final SharedPreferences sharedPreferences;
14 + final KeyService keyService;
15 + final WalletService Function(WalletType type) walletServiceFactory;
16 +
17 + Future<void> renameWallet(
18 + WalletType type, String name, String newName) async {
19 + final walletService = walletServiceFactory.call(type);
20 + final password = await keyService.getWalletPassword(walletName: name);
21 +
22 + // Save the current wallet's password to the new wallet name's key
23 + await keyService.saveWalletPassword(
24 + walletName: newName, password: password);
25 + // Delete previous wallet name from keyService to keep only new wallet's name
26 + // otherwise keeps duplicate (old and new names)
27 + await keyService.deleteWalletPassword(walletName: name);
28 +
29 + await walletService.rename(name, password, newName);
30 + }
31 +
32 + Future<WalletBase> load(WalletType type, String name) async {
33 + final walletService = walletServiceFactory.call(type);
34 + final password = await keyService.getWalletPassword(walletName: name);
35 + final wallet = await walletService.openWallet(name, password);
36 +
37 + if (type == WalletType.monero) {
38 + await updateMoneroWalletPassword(wallet);
39 + }
40 +
41 + return wallet;
42 + }
43 +
44 + Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
45 + final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name);
46 + var isPasswordUpdated = sharedPreferences.getBool(key) ?? false;
47 +
48 + if (isPasswordUpdated) {
49 + return;
50 + }
51 +
52 + final password = generateWalletPassword();
53 + // Save new generated password with backup key for case where
54 + // wallet will change password, but it will fail to update in secure storage
55 + final bakWalletName = '#__${wallet.name}_bak__#';
56 + await keyService.saveWalletPassword(
57 + walletName: bakWalletName, password: password);
58 + await wallet.changePassword(password);
59 + await keyService.saveWalletPassword(
60 + walletName: wallet.name, password: password);
61 + isPasswordUpdated = true;
62 + await sharedPreferences.setBool(key, isPasswordUpdated);
63 + }
64 }
\ No newline at end of file
lib/di.dart
+18
@@ -31,6 +31,7 @@ import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
31 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
32 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
33 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
34 +import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
35 import 'package:cake_wallet/themes/theme_list.dart';
36 import 'package:cake_wallet/utils/device_info.dart';
37 import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
@@ -67,6 +68,8 @@ import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart
68 import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
69 import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
70 import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
71 +import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
72 +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
73 import 'package:cw_core/unspent_coins_info.dart';
74 import 'package:cake_wallet/core/backup_service.dart';
75 import 'package:cw_core/wallet_service.dart';
@@ -566,6 +569,21 @@ Future setup({
569 authService: getIt.get<AuthService>(),
570 ));
571
572 + getIt.registerFactoryParam<WalletEditViewModel, WalletListViewModel, void>(
573 + (WalletListViewModel walletListViewModel, _) => WalletEditViewModel(
574 + walletListViewModel, getIt.get<WalletLoadingService>()));
575 +
576 + getIt.registerFactoryParam<WalletEditPage, List<dynamic>, void>((args, _) {
577 + final walletListViewModel = args.first as WalletListViewModel;
578 + final editingWallet = args.last as WalletListItem;
579 + return WalletEditPage(
580 + walletEditViewModel: getIt.get<WalletEditViewModel>(param1: walletListViewModel),
581 + authService: getIt.get<AuthService>(),
582 + walletNewVM: getIt.get<WalletNewVM>(param1: editingWallet.type),
583 + editingWallet: editingWallet);
584 + });
585 +
586 +
587 getIt.registerFactory(() {
588 final wallet = getIt.get<AppStore>().wallet!;
589
lib/router.dart
+8
@@ -45,6 +45,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
45 import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
46 import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
47 import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
48 +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
49 import 'package:cake_wallet/wallet_type_utils.dart';
50 import 'package:flutter/cupertino.dart';
51 import 'package:flutter/material.dart';
@@ -63,6 +64,7 @@ import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
64 import 'package:cake_wallet/src/screens/receive/receive_page.dart';
65 import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
66 import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
67 +import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
68 import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
69 import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
70 import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
@@ -260,6 +262,12 @@ Route<dynamic> createRoute(RouteSettings settings) {
262 return MaterialPageRoute<void>(
263 fullscreenDialog: true, builder: (_) => getIt.get<WalletListPage>());
264
265 + case Routes.walletEdit:
266 + return MaterialPageRoute<void>(
267 + fullscreenDialog: true,
268 + builder: (_) => getIt.get<WalletEditPage>(
269 + param1: settings.arguments as List<dynamic>));
270 +
271 case Routes.auth:
272 return MaterialPageRoute<void>(
273 fullscreenDialog: true,
lib/routes.dart
+1
@@ -11,6 +11,7 @@ class Routes {
11 static const transactionDetails = '/transaction_info';
12 static const receive = '/receive';
13 static const newSubaddress = '/new_subaddress';
14 + static const walletEdit = '/walletEdit';
15 static const disclaimer = '/disclaimer';
16 static const readDisclaimer = '/read_disclaimer';
17 static const seedLanguage = '/seed_language';
lib/src/screens/nodes/node_create_or_edit_page.dart
+29 -9
@@ -1,6 +1,8 @@
1 import 'package:cake_wallet/core/execution_state.dart';
2 +import 'package:cake_wallet/palette.dart';
3 import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
4 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
5 +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
6 import 'package:cake_wallet/utils/show_pop_up.dart';
7 import 'package:cw_core/node.dart';
8 import 'package:flutter/material.dart';
@@ -122,17 +124,35 @@ class NodeCreateOrEditPage extends BasePage {
124 padding: EdgeInsets.only(right: 8.0),
125 child: LoadingPrimaryButton(
126 onPressed: () async {
125 - if (_formKey.currentState != null && !_formKey.currentState!.validate()) {
126 - return;
127 - }
127 + final confirmed = await showPopUp<bool>(
128 + context: context,
129 + builder: (BuildContext context) {
130 + return AlertWithTwoActions(
131 + alertTitle:
132 + S.of(context).remove_node,
133 + alertContent: S
134 + .of(context)
135 + .remove_node_message,
136 + rightButtonText:
137 + S.of(context).remove,
138 + leftButtonText:
139 + S.of(context).cancel,
140 + actionRightButton: () =>
141 + Navigator.pop(context, true),
142 + actionLeftButton: () =>
143 + Navigator.pop(context, false));
144 + }) ??
145 + false;
146
129 - await nodeCreateOrEditViewModel.connect();
147 + if (confirmed) {
148 + await editingNode!.delete();
149 + Navigator.of(context).pop();
150 + }
151 },
131 - isLoading: nodeCreateOrEditViewModel
132 - .connectionState is IsExecutingState,
133 - text: S.of(context).node_test,
134 - isDisabled: !nodeCreateOrEditViewModel.isReady,
135 - color: Colors.orange,
152 + text: S.of(context).delete,
153 + isDisabled: !nodeCreateOrEditViewModel.isReady ||
154 + (isSelected ?? false),
155 + color: Palette.red,
156 textColor: Colors.white),
157 )),
158 Flexible(
lib/src/screens/nodes/widgets/node_indicator.dart
+2 -2
@@ -9,8 +9,8 @@ class NodeIndicator extends StatelessWidget {
9 @override
10 Widget build(BuildContext context) {
11 return Container(
12 - width: 8.0,
13 - height: 8.0,
12 + width: 12.0,
13 + height: 12.0,
14 decoration: BoxDecoration(
15 shape: BoxShape.circle, color: isLive ? Palette.green : Palette.red),
16 );
lib/src/screens/nodes/widgets/node_list_row.dart
+25 -5
@@ -1,21 +1,23 @@
1 +import 'package:cake_wallet/routes.dart';
2 import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart';
3 import 'package:cake_wallet/src/widgets/standard_list.dart';
4 +import 'package:cw_core/node.dart';
5 import 'package:flutter/material.dart';
6
7 class NodeListRow extends StandardListRow {
8 NodeListRow(
9 {required String title,
10 + required this.node,
11 required void Function(BuildContext context) onTap,
9 - required bool isSelected,
10 - required this.isAlive})
12 + required bool isSelected})
13 : super(title: title, onTap: onTap, isSelected: isSelected);
14
13 - final Future<bool> isAlive;
15 + final Node node;
16
17 @override
16 - Widget buildTrailing(BuildContext context) {
18 + Widget buildLeading(BuildContext context) {
19 return FutureBuilder(
18 - future: isAlive,
20 + future: node.requestNode(),
21 builder: (context, snapshot) {
22 switch (snapshot.connectionState) {
23 case ConnectionState.done:
@@ -25,6 +27,24 @@ class NodeListRow extends StandardListRow {
27 }
28 });
29 }
30 +
31 + @override
32 + Widget buildTrailing(BuildContext context) {
33 + return GestureDetector(
34 + onTap: () => Navigator.of(context).pushNamed(Routes.newNode,
35 + arguments: {'editingNode': node, 'isSelected': isSelected}),
36 + child: Container(
37 + padding: EdgeInsets.all(10),
38 + decoration: BoxDecoration(
39 + shape: BoxShape.circle,
40 + color: Theme.of(context)
41 + .textTheme
42 + .headlineMedium!
43 + .decorationColor!),
44 + child: Icon(Icons.edit,
45 + size: 14,
46 + color: Theme.of(context).textTheme.headlineMedium!.color!)));
47 + }
48 }
49
50 class NodeHeaderListRow extends StandardListRow {
lib/src/screens/settings/connection_sync_page.dart
+28 -81
@@ -12,7 +12,6 @@ import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart';
12 import 'package:cake_wallet/src/widgets/standard_list.dart';
13 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
14 import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
15 -import 'package:flutter_slidable/flutter_slidable.dart';
15
16 class ConnectionSyncPage extends BasePage {
17 ConnectionSyncPage(this.nodeListViewModel, this.dashboardViewModel);
@@ -64,49 +63,37 @@ class ConnectionSyncPage extends BasePage {
63 itemBuilder: (_, sectionIndex, index) {
64 final node = nodeListViewModel.nodes[index];
65 final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
67 - final nodeListRow = Semantics(
68 - label: 'Slidable',
69 - selected: isSelected,
70 - enabled: !isSelected,
71 - child: NodeListRow(
72 - title: node.uriRaw,
73 - isSelected: isSelected,
74 - isAlive: node.requestNode(),
75 - onTap: (_) async {
76 - if (isSelected) {
77 - return;
78 - }
66 + final nodeListRow = NodeListRow(
67 + title: node.uriRaw,
68 + node: node,
69 + isSelected: isSelected,
70 + onTap: (_) async {
71 + if (isSelected) {
72 + return;
73 + }
74
80 - await showPopUp<void>(
81 - context: context,
82 - builder: (BuildContext context) {
83 - return AlertWithTwoActions(
84 - alertTitle:
85 - S.of(context).change_current_node_title,
86 - alertContent: nodeListViewModel
87 - .getAlertContent(node.uriRaw),
88 - leftButtonText: S.of(context).cancel,
89 - rightButtonText: S.of(context).change,
90 - actionLeftButton: () =>
91 - Navigator.of(context).pop(),
92 - actionRightButton: () async {
93 - await nodeListViewModel.setAsCurrent(node);
94 - Navigator.of(context).pop();
95 - },
96 - );
97 - });
98 - },
99 - ),
75 + await showPopUp<void>(
76 + context: context,
77 + builder: (BuildContext context) {
78 + return AlertWithTwoActions(
79 + alertTitle:
80 + S.of(context).change_current_node_title,
81 + alertContent: nodeListViewModel
82 + .getAlertContent(node.uriRaw),
83 + leftButtonText: S.of(context).cancel,
84 + rightButtonText: S.of(context).change,
85 + actionLeftButton: () =>
86 + Navigator.of(context).pop(),
87 + actionRightButton: () async {
88 + await nodeListViewModel.setAsCurrent(node);
89 + Navigator.of(context).pop();
90 + },
91 + );
92 + });
93 + },
94 );
95
102 - final dismissibleRow = Slidable(
103 - key: Key('${node.keyIndex}'),
104 - startActionPane: _actionPane(context, node, isSelected),
105 - endActionPane: _actionPane(context, node, isSelected),
106 - child: nodeListRow,
107 - );
108 -
109 - return dismissibleRow;
96 + return nodeListRow;
97 },
98 ),
99 );
@@ -134,44 +121,4 @@ class ConnectionSyncPage extends BasePage {
121 },
122 );
123 }
137 -
138 - ActionPane _actionPane(BuildContext context, Node node, bool isSelected) => ActionPane(
139 - motion: const ScrollMotion(),
140 - extentRatio: isSelected ? 0.3 : 0.6,
141 - children: [
142 - if (!isSelected)
143 - SlidableAction(
144 - onPressed: (context) async {
145 - final confirmed = await showPopUp<bool>(
146 - context: context,
147 - builder: (BuildContext context) {
148 - return AlertWithTwoActions(
149 - alertTitle: S.of(context).remove_node,
150 - alertContent: S.of(context).remove_node_message,
151 - rightButtonText: S.of(context).remove,
152 - leftButtonText: S.of(context).cancel,
153 - actionRightButton: () => Navigator.pop(context, true),
154 - actionLeftButton: () => Navigator.pop(context, false));
155 - }) ??
156 - false;
157 -
158 - if (confirmed) {
159 - await nodeListViewModel.delete(node);
160 - }
161 - },
162 - backgroundColor: Colors.red,
163 - foregroundColor: Colors.white,
164 - icon: CupertinoIcons.delete,
165 - label: S.of(context).delete,
166 - ),
167 - SlidableAction(
168 - onPressed: (_) => Navigator.of(context).pushNamed(Routes.newNode,
169 - arguments: {'editingNode': node, 'isSelected': isSelected}),
170 - backgroundColor: Colors.blue,
171 - foregroundColor: Colors.white,
172 - icon: Icons.edit,
173 - label: S.of(context).edit,
174 - ),
175 - ],
176 - );
124 }
lib/src/screens/wallet/wallet_edit_page.dart new
+175
@@ -0,0 +1,175 @@
1 +import 'package:another_flushbar/flushbar.dart';
2 +import 'package:cake_wallet/core/auth_service.dart';
3 +import 'package:cake_wallet/core/wallet_name_validator.dart';
4 +import 'package:cake_wallet/palette.dart';
5 +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
6 +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
7 +import 'package:cake_wallet/utils/show_bar.dart';
8 +import 'package:cake_wallet/utils/show_pop_up.dart';
9 +import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
10 +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
11 +import 'package:cake_wallet/view_model/wallet_new_vm.dart';
12 +import 'package:flutter/material.dart';
13 +import 'package:cake_wallet/generated/i18n.dart';
14 +import 'package:cake_wallet/src/widgets/primary_button.dart';
15 +import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
16 +import 'package:cake_wallet/src/screens/base_page.dart';
17 +import 'package:flutter_mobx/flutter_mobx.dart';
18 +
19 +class WalletEditPage extends BasePage {
20 + WalletEditPage(
21 + {required this.walletEditViewModel,
22 + required this.editingWallet,
23 + required this.walletNewVM,
24 + required this.authService})
25 + : _formKey = GlobalKey<FormState>(),
26 + _labelController = TextEditingController(),
27 + super() {
28 + _labelController.text = editingWallet.name;
29 + _labelController.addListener(() => walletEditViewModel.newName = _labelController.text);
30 + }
31 +
32 + final GlobalKey<FormState> _formKey;
33 + final TextEditingController _labelController;
34 +
35 + final WalletEditViewModel walletEditViewModel;
36 + final WalletNewVM walletNewVM;
37 + final WalletListItem editingWallet;
38 + final AuthService authService;
39 +
40 + @override
41 + String get title => S.current.wallet_list_edit_wallet;
42 +
43 + Flushbar<void>? _progressBar;
44 +
45 + @override
46 + Widget body(BuildContext context) {
47 + return Form(
48 + key: _formKey,
49 + child: Container(
50 + padding: EdgeInsets.all(24.0),
51 + child: Column(
52 + children: <Widget>[
53 + Expanded(
54 + child: Center(
55 + child: BaseTextFormField(
56 + controller: _labelController,
57 + hintText: S.of(context).wallet_list_wallet_name,
58 + validator: WalletNameValidator()))),
59 + Observer(
60 + builder: (_) {
61 + final isLoading = walletEditViewModel.state is WalletEditRenamePending ||
62 + walletEditViewModel.state is WalletEditDeletePending;
63 +
64 + return Row(
65 + children: <Widget>[
66 + Flexible(
67 + child: Container(
68 + padding: EdgeInsets.only(right: 8.0),
69 + child: LoadingPrimaryButton(
70 + isDisabled: isLoading,
71 + onPressed: () => _removeWallet(context),
72 + text: S.of(context).delete,
73 + color: Palette.red,
74 + textColor: Colors.white),
75 + ),
76 + ),
77 + Flexible(
78 + child: Container(
79 + padding: EdgeInsets.only(left: 8.0),
80 + child: LoadingPrimaryButton(
81 + onPressed: () async {
82 + if (_formKey.currentState?.validate() ?? false) {
83 + if (walletNewVM.nameExists(walletEditViewModel.newName)) {
84 + showPopUp<void>(
85 + context: context,
86 + builder: (_) {
87 + return AlertWithOneAction(
88 + alertTitle: '',
89 + alertContent: S.of(context).wallet_name_exists,
90 + buttonText: S.of(context).ok,
91 + buttonAction: () => Navigator.of(context).pop(),
92 + );
93 + },
94 + );
95 + } else {
96 + try {
97 + await walletEditViewModel.changeName(editingWallet);
98 + Navigator.of(context).pop();
99 + walletEditViewModel.resetState();
100 + } catch (e) {}
101 + }
102 + }
103 + },
104 + text: S.of(context).save,
105 + color: Theme.of(context).accentTextTheme.bodyLarge!.color!,
106 + textColor: Colors.white,
107 + isDisabled: walletEditViewModel.newName.isEmpty || isLoading,
108 + ),
109 + ),
110 + )
111 + ],
112 + );
113 + },
114 + )
115 + ],
116 + ),
117 + ),
118 + );
119 + }
120 +
121 + Future<void> _removeWallet(BuildContext context) async {
122 + authService.authenticateAction(context, onAuthSuccess: (isAuthenticatedSuccessfully) async {
123 + if (!isAuthenticatedSuccessfully) {
124 + return;
125 + }
126 +
127 + _onSuccessfulAuth(context);
128 + });
129 + }
130 +
131 + void _onSuccessfulAuth(BuildContext context) async {
132 + bool confirmed = false;
133 +
134 + await showPopUp<void>(
135 + context: context,
136 + builder: (BuildContext dialogContext) {
137 + return AlertWithTwoActions(
138 + alertTitle: S.of(context).delete_wallet,
139 + alertContent: S.of(context).delete_wallet_confirm_message(editingWallet.name),
140 + leftButtonText: S.of(context).cancel,
141 + rightButtonText: S.of(context).delete,
142 + actionLeftButton: () => Navigator.of(dialogContext).pop(),
143 + actionRightButton: () {
144 + confirmed = true;
145 + Navigator.of(dialogContext).pop();
146 + });
147 + });
148 +
149 + if (confirmed) {
150 + Navigator.of(context).pop();
151 +
152 + try {
153 + changeProcessText(context, S.of(context).wallet_list_removing_wallet(editingWallet.name));
154 + await walletEditViewModel.remove(editingWallet);
155 + hideProgressText();
156 + } catch (e) {
157 + changeProcessText(
158 + context,
159 + S.of(context).wallet_list_failed_to_remove(editingWallet.name, e.toString()),
160 + );
161 + }
162 + }
163 + }
164 +
165 + void changeProcessText(BuildContext context, String text) {
166 + _progressBar = createBar<void>(text, duration: null)..show(context);
167 + }
168 +
169 + Future<void> hideProgressText() async {
170 + await Future.delayed(Duration(milliseconds: 50), () {
171 + _progressBar?.dismiss();
172 + _progressBar = null;
173 + });
174 + }
175 +}
lib/src/screens/wallet_list/wallet_list_page.dart
+43 -93
@@ -6,7 +6,6 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
6 import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
7 import 'package:another_flushbar/flushbar.dart';
8 import 'package:flutter/material.dart';
9 -import 'package:flutter/cupertino.dart';
9 import 'package:flutter_mobx/flutter_mobx.dart';
10 import 'package:cake_wallet/routes.dart';
11 import 'package:cake_wallet/generated/i18n.dart';
@@ -15,7 +14,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
14 import 'package:cake_wallet/src/widgets/primary_button.dart';
15 import 'package:cake_wallet/src/screens/base_page.dart';
16 import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
18 -import 'package:flutter_slidable/flutter_slidable.dart';
17 import 'package:cake_wallet/wallet_type_utils.dart';
18
19 class WalletListPage extends BasePage {
@@ -77,31 +75,7 @@ class WalletListBodyState extends State<WalletListBody> {
75 ? Theme.of(context).accentTextTheme!.titleSmall!.decorationColor!
76 : Theme.of(context).colorScheme.background;
77 final row = GestureDetector(
80 - onTap: () async {
81 - if (wallet.isCurrent || !wallet.isEnabled) {
82 - return;
83 - }
84 -
85 - final confirmed = await showPopUp<bool>(
86 - context: context,
87 - builder: (dialogContext) {
88 - return AlertWithTwoActions(
89 - alertTitle: S.of(context).change_wallet_alert_title,
90 - alertContent:
91 - S.of(context).change_wallet_alert_content(wallet.name),
92 - leftButtonText: S.of(context).cancel,
93 - rightButtonText: S.of(context).change,
94 - actionLeftButton: () =>
95 - Navigator.of(dialogContext).pop(false),
96 - actionRightButton: () =>
97 - Navigator.of(dialogContext).pop(true));
98 - }) ??
99 - false;
100 -
101 - if (confirmed) {
102 - await _loadWallet(wallet);
103 - }
104 - },
78 + onTap: () => wallet.isCurrent ? null : _loadWallet(wallet),
79 child: Container(
80 height: tileHeight,
81 width: double.infinity,
@@ -129,16 +103,21 @@ class WalletListBodyState extends State<WalletListBody> {
103 ? _imageFor(type: wallet.type)
104 : nonWalletTypeIcon,
105 SizedBox(width: 10),
132 - Text(
133 - wallet.name,
134 - style: TextStyle(
106 + Flexible(
107 + child: Text(
108 + wallet.name,
109 + maxLines: null,
110 + softWrap: true,
111 + style: TextStyle(
112 fontSize: 22,
113 fontWeight: FontWeight.w500,
114 color: Theme.of(context)
115 .primaryTextTheme
116 .titleLarge!
140 - .color!),
141 - )
117 + .color!,
118 + ),
119 + ),
120 + ),
121 ],
122 ),
123 ),
@@ -149,12 +128,38 @@ class WalletListBodyState extends State<WalletListBody> {
128
129 return wallet.isCurrent
130 ? row
152 - : Slidable(
153 - key: Key('${wallet.key}'),
154 - startActionPane: _actionPane(wallet),
155 - endActionPane: _actionPane(wallet),
156 - child: row,
157 - );
131 + : Row(children: [
132 + Expanded(child: row),
133 + GestureDetector(
134 + onTap: () => Navigator.of(context).pushNamed(
135 + Routes.walletEdit,
136 + arguments: [widget.walletListViewModel, wallet]),
137 + child: Container(
138 + padding: EdgeInsets.only(right: 20),
139 + child: Center(
140 + child: Container(
141 + height: 40,
142 + width: 44,
143 + padding: EdgeInsets.all(10),
144 + decoration: BoxDecoration(
145 + shape: BoxShape.circle,
146 + color: Theme.of(context)
147 + .textTheme
148 + .headlineMedium!
149 + .decorationColor!),
150 + child: Icon(
151 + Icons.edit,
152 + size: 14,
153 + color: Theme.of(context)
154 + .textTheme
155 + .headlineMedium!
156 + .color!,
157 + ),
158 + ),
159 + ),
160 + ),
161 + )
162 + ]);
163 }),
164 ),
165 ),
@@ -226,47 +231,6 @@ class WalletListBodyState extends State<WalletListBody> {
231 });
232 }
233
229 - Future<void> _removeWallet(WalletListItem wallet) async {
230 - widget.authService.authenticateAction(context,
231 - onAuthSuccess: (isAuthenticatedSuccessfully) async {
232 - if (!isAuthenticatedSuccessfully) {
233 - return;
234 - }
235 - _onSuccessfulAuth(wallet);
236 - });
237 - }
238 -
239 - void _onSuccessfulAuth(WalletListItem wallet) async {
240 - bool confirmed = false;
241 - await showPopUp<void>(
242 - context: context,
243 - builder: (BuildContext context) {
244 - return AlertWithTwoActions(
245 - alertTitle: S.of(context).delete_wallet,
246 - alertContent: S.of(context).delete_wallet_confirm_message(wallet.name),
247 - leftButtonText: S.of(context).cancel,
248 - rightButtonText: S.of(context).delete,
249 - actionLeftButton: () => Navigator.of(context).pop(),
250 - actionRightButton: () {
251 - confirmed = true;
252 - Navigator.of(context).pop();
253 - },
254 - );
255 - });
256 -
257 - if (confirmed) {
258 - try {
259 - changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name));
260 - await widget.walletListViewModel.remove(wallet);
261 - hideProgressText();
262 - } catch (e) {
263 - changeProcessText(
264 - S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()),
265 - );
266 - }
267 - }
268 - }
269 -
234 void changeProcessText(String text) {
235 _progressBar = createBar<void>(text, duration: null)..show(context);
236 }
@@ -277,18 +241,4 @@ class WalletListBodyState extends State<WalletListBody> {
241 _progressBar = null;
242 });
243 }
280 -
281 - ActionPane _actionPane(WalletListItem wallet) => ActionPane(
282 - motion: const ScrollMotion(),
283 - extentRatio: 0.3,
284 - children: [
285 - SlidableAction(
286 - onPressed: (_) => _removeWallet(wallet),
287 - backgroundColor: Colors.red,
288 - foregroundColor: Colors.white,
289 - icon: CupertinoIcons.delete,
290 - label: S.of(context).delete,
291 - ),
292 - ],
293 - );
244 }
lib/src/widgets/standard_list.dart
+1 -1
@@ -231,7 +231,7 @@ class SectionStandardList extends StatelessWidget {
231 return Container();
232 }
233
234 - return StandardListSeparator(padding: EdgeInsets.only(left: 24));
234 + return StandardListSeparator(padding: dividerPadding);
235 },
236 itemCount: totalRows.length,
237 itemBuilder: (_, index) => totalRows[index]);
lib/view_model/wallet_list/wallet_edit_view_model.dart new
+55
@@ -0,0 +1,55 @@
1 +import 'package:cake_wallet/core/wallet_loading_service.dart';
2 +import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
3 +import 'package:mobx/mobx.dart';
4 +import 'package:cake_wallet/di.dart';
5 +import 'package:cw_core/wallet_service.dart';
6 +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
7 +
8 +part 'wallet_edit_view_model.g.dart';
9 +
10 +class WalletEditViewModel = WalletEditViewModelBase with _$WalletEditViewModel;
11 +
12 +abstract class WalletEditViewModelState {}
13 +
14 +class WalletEditViewModelInitialState extends WalletEditViewModelState {}
15 +
16 +class WalletEditRenamePending extends WalletEditViewModelState {}
17 +
18 +class WalletEditDeletePending extends WalletEditViewModelState {}
19 +
20 +abstract class WalletEditViewModelBase with Store {
21 + WalletEditViewModelBase(this._walletListViewModel, this._walletLoadingService)
22 + : state = WalletEditViewModelInitialState(),
23 + newName = '';
24 +
25 + @observable
26 + WalletEditViewModelState state;
27 +
28 + @observable
29 + String newName;
30 +
31 + final WalletListViewModel _walletListViewModel;
32 + final WalletLoadingService _walletLoadingService;
33 +
34 + @action
35 + Future<void> changeName(WalletListItem walletItem) async {
36 + state = WalletEditRenamePending();
37 + await _walletLoadingService.renameWallet(
38 + walletItem.type, walletItem.name, newName);
39 + _walletListViewModel.updateList();
40 + }
41 +
42 + @action
43 + Future<void> remove(WalletListItem wallet) async {
44 + state = WalletEditDeletePending();
45 + final walletService = getIt.get<WalletService>(param1: wallet.type);
46 + await walletService.remove(wallet.name);
47 + resetState();
48 + _walletListViewModel.updateList();
49 + }
50 +
51 + @action
52 + void resetState() {
53 + state = WalletEditViewModelInitialState();
54 + }
55 +}
lib/view_model/wallet_list/wallet_list_view_model.dart
+9 -15
@@ -1,10 +1,9 @@
1 import 'package:cake_wallet/core/auth_service.dart';
2 import 'package:cake_wallet/core/wallet_loading_service.dart';
3 +import 'package:cw_core/wallet_base.dart';
4 import 'package:hive/hive.dart';
5 import 'package:mobx/mobx.dart';
5 -import 'package:cake_wallet/di.dart';
6 import 'package:cake_wallet/store/app_store.dart';
7 -import 'package:cw_core/wallet_service.dart';
7 import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
8 import 'package:cw_core/wallet_info.dart';
9 import 'package:cw_core/wallet_type.dart';
@@ -21,8 +20,8 @@ abstract class WalletListViewModelBase with Store {
20 this._walletLoadingService,
21 this._authService,
22 ) : wallets = ObservableList<WalletListItem>() {
24 - _updateList();
25 - reaction((_) => _appStore.wallet, (_) => _updateList());
23 + updateList();
24 + reaction((_) => _appStore.wallet, (_) => updateList());
25 }
26
27 @observable
@@ -37,20 +36,14 @@ abstract class WalletListViewModelBase with Store {
36
37 @action
38 Future<void> loadWallet(WalletListItem walletItem) async {
40 - final wallet = await _walletLoadingService.load(walletItem.type, walletItem.name);
39 + final wallet =
40 + await _walletLoadingService.load(walletItem.type, walletItem.name);
41 +
42 _appStore.changeCurrentWallet(wallet);
42 - _updateList();
43 }
44
45 @action
46 - Future<void> remove(WalletListItem wallet) async {
47 - final walletService = getIt.get<WalletService>(param1: wallet.type);
48 - await walletService.remove(wallet.name);
49 - await _walletInfoSource.delete(wallet.key);
50 - _updateList();
51 - }
52 -
53 - void _updateList() {
46 + void updateList() {
47 wallets.clear();
48 wallets.addAll(
49 _walletInfoSource.values.map(
@@ -58,7 +51,8 @@ abstract class WalletListViewModelBase with Store {
51 name: info.name,
52 type: info.type,
53 key: info.key,
61 - isCurrent: info.name == _appStore.wallet!.name && info.type == _appStore.wallet!.type,
54 + isCurrent: info.name == _appStore.wallet!.name &&
55 + info.type == _appStore.wallet!.type,
56 isEnabled: availableWalletTypes.contains(info.type),
57 ),
58 ),
res/values/strings_ar.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "عناوين المستلم",
251 "wallet_list_title": "محفظة Monero",
252 "wallet_list_create_new_wallet": "إنشاء محفظة جديدة",
253 + "wallet_list_edit_wallet" : "تحرير المحفظة",
254 + "wallet_list_wallet_name" : "اسم المحفظة",
255 "wallet_list_restore_wallet": "استعادة المحفظة",
256 "wallet_list_load_wallet": "تحميل المحفظة",
257 "wallet_list_loading_wallet": "جار تحميل محفظة ${wallet_name}",
res/values/strings_bg.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Адрес на получател",
251 "wallet_list_title": "Monero портфейл",
252 "wallet_list_create_new_wallet": "Създаване на нов портфейл",
253 + "wallet_list_edit_wallet" : "Редактиране на портфейла",
254 + "wallet_list_wallet_name" : "Име на портфейла",
255 "wallet_list_restore_wallet": "Възстановяване на портфейл",
256 "wallet_list_load_wallet": "Зареждане на портфейл",
257 "wallet_list_loading_wallet": "Зареждане на портфейл ${wallet_name}",
res/values/strings_cs.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Adresa příjemce",
251 "wallet_list_title": "Monero Wallet",
252 "wallet_list_create_new_wallet": "Vytvořit novou peněženku",
253 + "wallet_list_edit_wallet" : "Upravit peněženku",
254 + "wallet_list_wallet_name" : "Název peněženky",
255 "wallet_list_restore_wallet": "Obnovit peněženku",
256 "wallet_list_load_wallet": "Načíst peněženku",
257 "wallet_list_loading_wallet": "Načítám ${wallet_name} peněženku",
res/values/strings_de.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Empfängeradressen",
251 "wallet_list_title": "Monero-Wallet",
252 "wallet_list_create_new_wallet": "Neue Wallet erstellen",
253 + "wallet_list_edit_wallet" : "Wallet bearbeiten",
254 + "wallet_list_wallet_name" : "Wallet namen",
255 "wallet_list_restore_wallet": "Wallet wiederherstellen",
256 "wallet_list_load_wallet": "Wallet laden",
257 "wallet_list_loading_wallet": "Wallet ${wallet_name} wird geladen",
res/values/strings_en.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Recipient addresses",
251 "wallet_list_title": "Monero Wallet",
252 "wallet_list_create_new_wallet": "Create New Wallet",
253 + "wallet_list_edit_wallet" : "Edit wallet",
254 + "wallet_list_wallet_name" : "Wallet name",
255 "wallet_list_restore_wallet": "Restore Wallet",
256 "wallet_list_load_wallet": "Load wallet",
257 "wallet_list_loading_wallet": "Loading ${wallet_name} wallet",
res/values/strings_es.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Direcciones de destinatarios",
251 "wallet_list_title": "Monedero Monero",
252 "wallet_list_create_new_wallet": "Crear nueva billetera",
253 + "wallet_list_edit_wallet" : "Editar billetera",
254 + "wallet_list_wallet_name" : "Nombre de la billetera",
255 "wallet_list_restore_wallet": "Restaurar billetera",
256 "wallet_list_load_wallet": "Billetera de carga",
257 "wallet_list_loading_wallet": "Billetera ${wallet_name} de carga",
res/values/strings_fr.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Adresse du bénéficiaire",
251 "wallet_list_title": "Portefeuille (Wallet) Monero",
252 "wallet_list_create_new_wallet": "Créer un Nouveau Portefeuille (Wallet)",
253 + "wallet_list_edit_wallet" : "Modifier le portefeuille",
254 + "wallet_list_wallet_name" : "Nom du portefeuille",
255 "wallet_list_restore_wallet": "Restaurer un Portefeuille (Wallet)",
256 "wallet_list_load_wallet": "Charger un Portefeuille (Wallet)",
257 "wallet_list_loading_wallet": "Chargement du portefeuille (wallet) ${wallet_name}",
res/values/strings_ha.arb
+3
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Adireshin masu amfani",
251 "wallet_list_title": "Monero walat",
252 "wallet_list_create_new_wallet": "Ƙirƙiri Sabon Wallet",
253 + "wallet_list_edit_wallet" : "Gyara walat",
254 + "wallet_list_wallet_name" : "Sunan walat",
255 "wallet_list_restore_wallet": "Maida Wallet",
256 "wallet_list_load_wallet": "Ana loda wallet na Monero",
257 "wallet_list_loading_wallet": "Ana loda ${wallet_name} walat",
@@ -617,3 +619,4 @@
619 "share": "Raba",
620 "slidable": "Mai iya zamewa"
621 }
622 +
res/values/strings_hi.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "प्राप्तकर्ता के पते",
251 "wallet_list_title": "Monero बटुआ",
252 "wallet_list_create_new_wallet": "नया बटुआ बनाएँ",
253 + "wallet_list_edit_wallet" : "बटुआ संपादित करें",
254 + "wallet_list_wallet_name" : "बटुआ नाम",
255 "wallet_list_restore_wallet": "वॉलेट को पुनर्स्थापित करें",
256 "wallet_list_load_wallet": "वॉलेट लोड करें",
257 "wallet_list_loading_wallet": "लोड हो रहा है ${wallet_name} बटुआ",
res/values/strings_hr.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Adrese primatelja",
251 "wallet_list_title": "Monero novčanik",
252 "wallet_list_create_new_wallet": "Izradi novi novčanik",
253 + "wallet_list_edit_wallet" : "Uredi novčanik",
254 + "wallet_list_wallet_name" : "Naziv novčanika",
255 "wallet_list_restore_wallet": "Oporavi novčanik",
256 "wallet_list_load_wallet": "Učitaj novčanik",
257 "wallet_list_loading_wallet": "Učitavanje novčanika ${wallet_name}",
res/values/strings_id.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Alamat Penerima",
251 "wallet_list_title": "Dompet Monero",
252 "wallet_list_create_new_wallet": "Buat Dompet Baru",
253 + "wallet_list_edit_wallet" : "Edit dompet",
254 + "wallet_list_wallet_name" : "Nama dompet",
255 "wallet_list_restore_wallet": "Pulihkan Dompet",
256 "wallet_list_load_wallet": "Muat dompet",
257 "wallet_list_loading_wallet": "Memuat ${wallet_name} dompet",
res/values/strings_it.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Indirizzi dei destinatari",
251 "wallet_list_title": "Portafoglio Monero",
252 "wallet_list_create_new_wallet": "Crea Nuovo Portafoglio",
253 + "wallet_list_edit_wallet" : "Modifica portafoglio",
254 + "wallet_list_wallet_name" : "Nome del portafoglio",
255 "wallet_list_restore_wallet": "Recupera Portafoglio",
256 "wallet_list_load_wallet": "Caricamento Portafoglio",
257 "wallet_list_loading_wallet": "Caricamento portafoglio ${wallet_name}",
res/values/strings_ja.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "受信者のアドレス",
251 "wallet_list_title": "Monero 財布",
252 "wallet_list_create_new_wallet": "新しいウォレットを作成",
253 + "wallet_list_edit_wallet" : "ウォレットを編集する",
254 + "wallet_list_wallet_name" : "ウォレット名",
255 "wallet_list_restore_wallet": "ウォレットを復元",
256 "wallet_list_load_wallet": "ウォレットをロード",
257 "wallet_list_loading_wallet": "読み込み中 ${wallet_name} 財布",
res/values/strings_ko.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "받는 사람 주소",
251 "wallet_list_title": "모네로 월렛",
252 "wallet_list_create_new_wallet": "새 월렛 만들기",
253 + "wallet_list_edit_wallet" : "지갑 수정",
254 + "wallet_list_wallet_name" : "지갑 이름",
255 "wallet_list_restore_wallet": "월렛 복원",
256 "wallet_list_load_wallet": "지갑로드",
257 "wallet_list_loading_wallet": "로딩 ${wallet_name} 지갑",
res/values/strings_my.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "လက်ခံသူလိပ်စာများ",
251 "wallet_list_title": "Monero ပိုက်ဆံအိတ်",
252 "wallet_list_create_new_wallet": "Wallet အသစ်ဖန်တီးပါ။",
253 + "wallet_list_edit_wallet" : "ပိုက်ဆံအိတ်ကို တည်းဖြတ်ပါ။",
254 + "wallet_list_wallet_name" : "ပိုက်ဆံအိတ်နာမည်",
255 "wallet_list_restore_wallet": "ပိုက်ဆံအိတ်ကို ပြန်ယူပါ။",
256 "wallet_list_load_wallet": "ပိုက်ဆံအိတ်ကို တင်ပါ။",
257 "wallet_list_loading_wallet": "${wallet_name} ပိုက်ဆံအိတ်ကို ဖွင့်နေသည်။",
res/values/strings_nl.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Adressen van ontvangers",
251 "wallet_list_title": "Monero portemonnee",
252 "wallet_list_create_new_wallet": "Maak een nieuwe portemonnee",
253 + "wallet_list_edit_wallet" : "Portemonnee bewerken",
254 + "wallet_list_wallet_name" : "Portemonnee naam",
255 "wallet_list_restore_wallet": "Portemonnee herstellen",
256 "wallet_list_load_wallet": "Portemonnee laden",
257 "wallet_list_loading_wallet": "Bezig met laden ${wallet_name} portemonnee",
res/values/strings_pl.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Adres odbiorcy",
251 "wallet_list_title": "Portfel Monero",
252 "wallet_list_create_new_wallet": "Utwórz nowy portfel",
253 + "wallet_list_edit_wallet" : "Edytuj portfel",
254 + "wallet_list_wallet_name" : "Nazwa portfela",
255 "wallet_list_restore_wallet": "Przywróć portfel",
256 "wallet_list_load_wallet": "Załaduj portfel",
257 "wallet_list_loading_wallet": "Ładuję ${wallet_name} portfel",
res/values/strings_pt.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Endereços de destinatários",
251 "wallet_list_title": "Carteira Monero",
252 "wallet_list_create_new_wallet": "Criar nova carteira",
253 + "wallet_list_edit_wallet" : "Editar carteira",
254 + "wallet_list_wallet_name" : "Nome da carteira",
255 "wallet_list_restore_wallet": "Restaurar carteira",
256 "wallet_list_load_wallet": "Abrir carteira",
257 "wallet_list_loading_wallet": "Abrindo a carteira ${wallet_name}",
res/values/strings_ru.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Адреса получателей",
251 "wallet_list_title": "Monero Кошелёк",
252 "wallet_list_create_new_wallet": "Создать новый кошелёк",
253 + "wallet_list_edit_wallet" : "Изменить кошелек",
254 + "wallet_list_wallet_name" : "Имя кошелька",
255 "wallet_list_restore_wallet": "Восстановить кошелёк",
256 "wallet_list_load_wallet": "Загрузка кошелька",
257 "wallet_list_loading_wallet": "Загрузка ${wallet_name} кошелька",
res/values/strings_th.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "ที่อยู่ผู้รับ",
251 "wallet_list_title": "กระเป๋า Monero",
252 "wallet_list_create_new_wallet": "สร้างกระเป๋าใหม่",
253 + "wallet_list_edit_wallet" : "แก้ไขกระเป๋าสตางค์",
254 + "wallet_list_wallet_name" : "ชื่อกระเป๋าสตางค์",
255 "wallet_list_restore_wallet": "กู้กระเป๋า",
256 "wallet_list_load_wallet": "โหลดกระเป๋า",
257 "wallet_list_loading_wallet": "กำลังโหลดกระเป๋า ${wallet_name}",
res/values/strings_tr.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Alıcı adres",
251 "wallet_list_title": "Monero Cüzdanı",
252 "wallet_list_create_new_wallet": "Yeni Cüzdan Oluştur",
253 + "wallet_list_edit_wallet" : "Cüzdanı düzenle",
254 + "wallet_list_wallet_name" : "Cüzdan adı",
255 "wallet_list_restore_wallet": "Cüzdanı Geri Yükle",
256 "wallet_list_load_wallet": "Cüzdanı yükle",
257 "wallet_list_loading_wallet": "${wallet_name} cüzdanı yükleniyor",
res/values/strings_uk.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Адреси одержувачів",
251 "wallet_list_title": "Monero Гаманець",
252 "wallet_list_create_new_wallet": "Створити новий гаманець",
253 + "wallet_list_edit_wallet" : "Редагувати гаманець",
254 + "wallet_list_wallet_name" : "Назва гаманця",
255 "wallet_list_restore_wallet": "Відновити гаманець",
256 "wallet_list_load_wallet": "Завантаження гаманця",
257 "wallet_list_loading_wallet": "Завантаження ${wallet_name} гаманця",
res/values/strings_ur.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "وصول کنندگان کے پتے",
251 "wallet_list_title": "Monero والیٹ",
252 "wallet_list_create_new_wallet": "نیا والیٹ بنائیں",
253 + "wallet_list_edit_wallet" : "بٹوے میں ترمیم کریں۔",
254 + "wallet_list_wallet_name" : "بٹوے کا نام",
255 "wallet_list_restore_wallet": "والیٹ کو بحال کریں۔",
256 "wallet_list_load_wallet": "پرس لوڈ کریں۔",
257 "wallet_list_loading_wallet": "${wallet_name} والیٹ لوڈ ہو رہا ہے۔",
res/values/strings_yo.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "Àwọn àdírẹ́sì olùgbà",
251 "wallet_list_title": "Àpamọ́wọ́ Monero",
252 "wallet_list_create_new_wallet": "Ṣe àpamọ́wọ́ títun",
253 + "wallet_list_edit_wallet" : "Ṣatunkọ apamọwọ",
254 + "wallet_list_wallet_name" : "Orukọ apamọwọ",
255 "wallet_list_restore_wallet": "Restore àpamọ́wọ́",
256 "wallet_list_load_wallet": "Load àpamọ́wọ́",
257 "wallet_list_loading_wallet": "Ń ṣí àpamọ́wọ́ ${wallet_name}",
res/values/strings_zh.arb
+2
@@ -250,6 +250,8 @@
250 "transaction_details_recipient_address": "收件人地址",
251 "wallet_list_title": "Monero 钱包",
252 "wallet_list_create_new_wallet": "创建新钱包",
253 + "wallet_list_edit_wallet" : "编辑钱包",
254 + "wallet_list_wallet_name" : "钱包名称",
255 "wallet_list_restore_wallet": "恢复钱包",
256 "wallet_list_load_wallet": "加载钱包",
257 "wallet_list_loading_wallet": "载入中 ${wallet_name} 钱包",