From d4969633b04af1a63ccbf90070beeb3b2e614d5e Mon Sep 17 00:00:00 2001 From: Rafael Saes <76502841+saltrafael@users.noreply.github.com> Date: Wed, 12 Jul 2023 20:20:11 -0300 Subject: [PATCH] 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 --- cw_bitcoin/lib/bitcoin_wallet_service.dart | 29 ++- .../lib/electrum_transaction_history.dart | 6 +- cw_bitcoin/lib/electrum_wallet.dart | 23 +++ cw_bitcoin/lib/litecoin_wallet_service.dart | 29 ++- cw_core/lib/wallet_service.dart | 2 + cw_haven/lib/haven_wallet.dart | 25 +++ cw_haven/lib/haven_wallet_service.dart | 20 ++ cw_monero/lib/monero_wallet.dart | 26 ++- cw_monero/lib/monero_wallet_service.dart | 20 ++ lib/core/key_service.dart | 7 + lib/core/wallet_loading_service.dart | 81 ++++---- lib/di.dart | 18 ++ lib/router.dart | 8 + lib/routes.dart | 1 + .../nodes/node_create_or_edit_page.dart | 38 +++- .../screens/nodes/widgets/node_indicator.dart | 4 +- .../screens/nodes/widgets/node_list_row.dart | 30 ++- .../settings/connection_sync_page.dart | 109 +++-------- lib/src/screens/wallet/wallet_edit_page.dart | 175 ++++++++++++++++++ .../screens/wallet_list/wallet_list_page.dart | 136 +++++--------- lib/src/widgets/standard_list.dart | 2 +- .../wallet_list/wallet_edit_view_model.dart | 55 ++++++ .../wallet_list/wallet_list_view_model.dart | 24 +-- res/values/strings_ar.arb | 2 + res/values/strings_bg.arb | 2 + res/values/strings_cs.arb | 2 + res/values/strings_de.arb | 2 + res/values/strings_en.arb | 2 + res/values/strings_es.arb | 2 + res/values/strings_fr.arb | 2 + res/values/strings_ha.arb | 3 + res/values/strings_hi.arb | 2 + res/values/strings_hr.arb | 2 + res/values/strings_id.arb | 2 + res/values/strings_it.arb | 2 + res/values/strings_ja.arb | 2 + res/values/strings_ko.arb | 2 + res/values/strings_my.arb | 2 + res/values/strings_nl.arb | 2 + res/values/strings_pl.arb | 2 + res/values/strings_pt.arb | 2 + res/values/strings_ru.arb | 2 + res/values/strings_th.arb | 2 + res/values/strings_tr.arb | 2 + res/values/strings_uk.arb | 2 + res/values/strings_ur.arb | 2 + res/values/strings_yo.arb | 2 + res/values/strings_zh.arb | 2 + 48 files changed, 670 insertions(+), 249 deletions(-) create mode 100644 lib/src/screens/wallet/wallet_edit_page.dart create mode 100644 lib/view_model/wallet_list/wallet_edit_view_model.dart diff --git a/cw_bitcoin/lib/bitcoin_wallet_service.dart b/cw_bitcoin/lib/bitcoin_wallet_service.dart index 398d68fc2..bfadaf2a3 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_service.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_service.dart @@ -52,9 +52,32 @@ class BitcoinWalletService extends WalletService< } @override - Future remove(String wallet) async => - File(await pathForWalletDir(name: wallet, type: WalletType.bitcoin)) - .delete(recursive: true); + Future remove(String wallet) async { + File(await pathForWalletDir(name: wallet, type: getType())) + .delete(recursive: true); + final walletInfo = walletInfoSource.values.firstWhereOrNull( + (info) => info.id == WalletBase.idFor(wallet, getType()))!; + await walletInfoSource.delete(walletInfo.key); + } + + @override + Future rename(String currentName, String password, String newName) async { + final currentWalletInfo = walletInfoSource.values.firstWhereOrNull( + (info) => info.id == WalletBase.idFor(currentName, getType()))!; + final currentWallet = await BitcoinWalletBase.open( + password: password, + name: currentName, + walletInfo: currentWalletInfo, + unspentCoinsInfo: unspentCoinsInfoSource); + + await currentWallet.renameWalletFiles(newName); + + final newWalletInfo = currentWalletInfo; + newWalletInfo.id = WalletBase.idFor(newName, getType()); + newWalletInfo.name = newName; + + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); + } @override Future restoreFromKeys( diff --git a/cw_bitcoin/lib/electrum_transaction_history.dart b/cw_bitcoin/lib/electrum_transaction_history.dart index 8720dd82c..553795470 100644 --- a/cw_bitcoin/lib/electrum_transaction_history.dart +++ b/cw_bitcoin/lib/electrum_transaction_history.dart @@ -9,7 +9,7 @@ import 'package:cw_bitcoin/electrum_transaction_info.dart'; part 'electrum_transaction_history.g.dart'; -const _transactionsHistoryFileName = 'transactions.json'; +const transactionsHistoryFileName = 'transactions.json'; class ElectrumTransactionHistory = ElectrumTransactionHistoryBase with _$ElectrumTransactionHistory; @@ -42,7 +42,7 @@ abstract class ElectrumTransactionHistoryBase try { final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); - final path = '$dirPath/$_transactionsHistoryFileName'; + final path = '$dirPath/$transactionsHistoryFileName'; final data = json.encode({'height': _height, 'transactions': transactions}); await writeData(path: path, password: _password, data: data); @@ -59,7 +59,7 @@ abstract class ElectrumTransactionHistoryBase Future> _read() async { final dirPath = await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); - final path = '$dirPath/$_transactionsHistoryFileName'; + final path = '$dirPath/$transactionsHistoryFileName'; final content = await read(path: path, password: _password); return json.decode(content) as Map; } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 3f953b8e1..aadf87572 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'dart:math'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:hive/hive.dart'; @@ -430,6 +431,28 @@ abstract class ElectrumWalletBase extends WalletBase renameWalletFiles(String newWalletName) async { + final currentWalletPath = await pathForWallet(name: walletInfo.name, type: type); + final currentWalletFile = File(currentWalletPath); + + final currentDirPath = + await pathForWalletDir(name: walletInfo.name, type: type); + final currentTransactionsFile = File('$currentDirPath/$transactionsHistoryFileName'); + + // Copies current wallet files into new wallet name's dir and files + if (currentWalletFile.existsSync()) { + final newWalletPath = await pathForWallet(name: newWalletName, type: type); + await currentWalletFile.copy(newWalletPath); + } + if (currentTransactionsFile.existsSync()) { + final newDirPath = await pathForWalletDir(name: newWalletName, type: type); + await currentTransactionsFile.copy('$newDirPath/$transactionsHistoryFileName'); + } + + // Delete old name's dir and files + await Directory(currentDirPath).delete(recursive: true); + } + @override Future changePassword(String password) async { _password = password; diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index 2093647fd..b13ac7a7f 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -53,9 +53,32 @@ class LitecoinWalletService extends WalletService< } @override - Future remove(String wallet) async => - File(await pathForWalletDir(name: wallet, type: getType())) - .delete(recursive: true); + Future remove(String wallet) async { + File(await pathForWalletDir(name: wallet, type: getType())) + .delete(recursive: true); + final walletInfo = walletInfoSource.values.firstWhereOrNull( + (info) => info.id == WalletBase.idFor(wallet, getType()))!; + await walletInfoSource.delete(walletInfo.key); + } + + @override + Future rename(String currentName, String password, String newName) async { + final currentWalletInfo = walletInfoSource.values.firstWhereOrNull( + (info) => info.id == WalletBase.idFor(currentName, getType()))!; + final currentWallet = await LitecoinWalletBase.open( + password: password, + name: currentName, + walletInfo: currentWalletInfo, + unspentCoinsInfo: unspentCoinsInfoSource); + + await currentWallet.renameWalletFiles(newName); + + final newWalletInfo = currentWalletInfo; + newWalletInfo.id = WalletBase.idFor(newName, getType()); + newWalletInfo.name = newName; + + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); + } @override Future restoreFromKeys( diff --git a/cw_core/lib/wallet_service.dart b/cw_core/lib/wallet_service.dart index f66f39583..5e216e225 100644 --- a/cw_core/lib/wallet_service.dart +++ b/cw_core/lib/wallet_service.dart @@ -17,4 +17,6 @@ abstract class WalletService isWalletExit(String name); Future remove(String wallet); + + Future rename(String name, String password, String newName); } diff --git a/cw_haven/lib/haven_wallet.dart b/cw_haven/lib/haven_wallet.dart index 2a72f078f..ecd2a4b73 100644 --- a/cw_haven/lib/haven_wallet.dart +++ b/cw_haven/lib/haven_wallet.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'dart:io'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_haven/haven_transaction_creation_credentials.dart'; import 'package:cw_core/monero_amount_format.dart'; @@ -251,6 +253,29 @@ abstract class HavenWalletBase extends WalletBase renameWalletFiles(String newWalletName) async { + final currentWalletPath = await pathForWallet(name: name, type: type); + final currentCacheFile = File(currentWalletPath); + final currentKeysFile = File('$currentWalletPath.keys'); + final currentAddressListFile = File('$currentWalletPath.address.txt'); + + final newWalletPath = await pathForWallet(name: newWalletName, type: type); + + // Copies current wallet files into new wallet name's dir and files + if (currentCacheFile.existsSync()) { + await currentCacheFile.copy(newWalletPath); + } + if (currentKeysFile.existsSync()) { + await currentKeysFile.copy('$newWalletPath.keys'); + } + if (currentAddressListFile.existsSync()) { + await currentAddressListFile.copy('$newWalletPath.address.txt'); + } + + // Delete old name's dir and files + await Directory(currentWalletPath).delete(recursive: true); + } + @override Future changePassword(String password) async { haven_wallet.setPasswordSync(password); diff --git a/cw_haven/lib/haven_wallet_service.dart b/cw_haven/lib/haven_wallet_service.dart index 137ade78f..0bc20d2a0 100644 --- a/cw_haven/lib/haven_wallet_service.dart +++ b/cw_haven/lib/haven_wallet_service.dart @@ -149,6 +149,26 @@ class HavenWalletService extends WalletService< if (isExist) { await file.delete(recursive: true); } + + final walletInfo = walletInfoSource.values + .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType())); + await walletInfoSource.delete(walletInfo.key); + } + + @override + Future rename( + String currentName, String password, String newName) async { + final currentWalletInfo = walletInfoSource.values.firstWhere( + (info) => info.id == WalletBase.idFor(currentName, getType())); + final currentWallet = HavenWallet(walletInfo: currentWalletInfo); + + await currentWallet.renameWalletFiles(newName); + + final newWalletInfo = currentWalletInfo; + newWalletInfo.id = WalletBase.idFor(newName, getType()); + newWalletInfo.name = newName; + + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); } @override diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index eea490ba9..6eb866796 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -1,4 +1,6 @@ import 'dart:async'; +import 'dart:io'; +import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_monero/monero_transaction_creation_exception.dart'; @@ -6,7 +8,6 @@ import 'package:cw_monero/monero_transaction_info.dart'; import 'package:cw_monero/monero_wallet_addresses.dart'; import 'package:cw_core/monero_wallet_utils.dart'; import 'package:cw_monero/api/structs/pending_transaction.dart'; -import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; import 'package:cw_monero/api/transaction_history.dart' as monero_transaction_history; @@ -267,6 +268,29 @@ abstract class MoneroWalletBase extends WalletBase renameWalletFiles(String newWalletName) async { + final currentWalletPath = await pathForWallet(name: name, type: type); + final currentCacheFile = File(currentWalletPath); + final currentKeysFile = File('$currentWalletPath.keys'); + final currentAddressListFile = File('$currentWalletPath.address.txt'); + + final newWalletPath = await pathForWallet(name: newWalletName, type: type); + + // Copies current wallet files into new wallet name's dir and files + if (currentCacheFile.existsSync()) { + await currentCacheFile.copy(newWalletPath); + } + if (currentKeysFile.existsSync()) { + await currentKeysFile.copy('$newWalletPath.keys'); + } + if (currentAddressListFile.existsSync()) { + await currentAddressListFile.copy('$newWalletPath.address.txt'); + } + + // Delete old name's dir and files + await Directory(currentWalletPath).delete(recursive: true); + } + @override Future changePassword(String password) async { monero_wallet.setPasswordSync(password); diff --git a/cw_monero/lib/monero_wallet_service.dart b/cw_monero/lib/monero_wallet_service.dart index 095fe83bb..6539d58a5 100644 --- a/cw_monero/lib/monero_wallet_service.dart +++ b/cw_monero/lib/monero_wallet_service.dart @@ -146,6 +146,26 @@ class MoneroWalletService extends WalletService< if (isExist) { await file.delete(recursive: true); } + + final walletInfo = walletInfoSource.values + .firstWhere((info) => info.id == WalletBase.idFor(wallet, getType())); + await walletInfoSource.delete(walletInfo.key); + } + + @override + Future rename( + String currentName, String password, String newName) async { + final currentWalletInfo = walletInfoSource.values.firstWhere( + (info) => info.id == WalletBase.idFor(currentName, getType())); + final currentWallet = MoneroWallet(walletInfo: currentWalletInfo); + + await currentWallet.renameWalletFiles(newName); + + final newWalletInfo = currentWalletInfo; + newWalletInfo.id = WalletBase.idFor(newName, getType()); + newWalletInfo.name = newName; + + await walletInfoSource.put(currentWalletInfo.key, newWalletInfo); } @override diff --git a/lib/core/key_service.dart b/lib/core/key_service.dart index 1fe99623e..337f1ef21 100644 --- a/lib/core/key_service.dart +++ b/lib/core/key_service.dart @@ -21,4 +21,11 @@ class KeyService { await _secureStorage.write(key: key, value: encodedPassword); } + + Future deleteWalletPassword({required String walletName}) async { + final key = generateStoreKeyFor( + key: SecretStoreKey.moneroWalletPassword, walletName: walletName); + + await _secureStorage.delete(key: key); + } } diff --git a/lib/core/wallet_loading_service.dart b/lib/core/wallet_loading_service.dart index 761c6acce..2f5e6c342 100644 --- a/lib/core/wallet_loading_service.dart +++ b/lib/core/wallet_loading_service.dart @@ -7,43 +7,58 @@ import 'package:cw_core/wallet_type.dart'; import 'package:shared_preferences/shared_preferences.dart'; class WalletLoadingService { - WalletLoadingService( - this.sharedPreferences, - this.keyService, - this.walletServiceFactory); - - final SharedPreferences sharedPreferences; - final KeyService keyService; - final WalletService Function(WalletType type) walletServiceFactory; + WalletLoadingService( + this.sharedPreferences, this.keyService, this.walletServiceFactory); - Future load(WalletType type, String name) async { - final walletService = walletServiceFactory.call(type); - final password = await keyService.getWalletPassword(walletName: name); - final wallet = await walletService.openWallet(name, password); + final SharedPreferences sharedPreferences; + final KeyService keyService; + final WalletService Function(WalletType type) walletServiceFactory; - if (type == WalletType.monero) { - await updateMoneroWalletPassword(wallet); - } + Future renameWallet( + WalletType type, String name, String newName) async { + final walletService = walletServiceFactory.call(type); + final password = await keyService.getWalletPassword(walletName: name); - return wallet; - } + // Save the current wallet's password to the new wallet name's key + await keyService.saveWalletPassword( + walletName: newName, password: password); + // Delete previous wallet name from keyService to keep only new wallet's name + // otherwise keeps duplicate (old and new names) + await keyService.deleteWalletPassword(walletName: name); - Future updateMoneroWalletPassword(WalletBase wallet) async { - final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name); - var isPasswordUpdated = sharedPreferences.getBool(key) ?? false; + await walletService.rename(name, password, newName); + } - if (isPasswordUpdated) { - return; - } + Future load(WalletType type, String name) async { + final walletService = walletServiceFactory.call(type); + final password = await keyService.getWalletPassword(walletName: name); + final wallet = await walletService.openWallet(name, password); - final password = generateWalletPassword(); - // Save new generated password with backup key for case where - // wallet will change password, but it will fail to update in secure storage - final bakWalletName = '#__${wallet.name}_bak__#'; - await keyService.saveWalletPassword(walletName: bakWalletName, password: password); - await wallet.changePassword(password); - await keyService.saveWalletPassword(walletName: wallet.name, password: password); - isPasswordUpdated = true; - await sharedPreferences.setBool(key, isPasswordUpdated); - } + if (type == WalletType.monero) { + await updateMoneroWalletPassword(wallet); + } + + return wallet; + } + + Future updateMoneroWalletPassword(WalletBase wallet) async { + final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name); + var isPasswordUpdated = sharedPreferences.getBool(key) ?? false; + + if (isPasswordUpdated) { + return; + } + + final password = generateWalletPassword(); + // Save new generated password with backup key for case where + // wallet will change password, but it will fail to update in secure storage + final bakWalletName = '#__${wallet.name}_bak__#'; + await keyService.saveWalletPassword( + walletName: bakWalletName, password: password); + await wallet.changePassword(password); + await keyService.saveWalletPassword( + walletName: wallet.name, password: password); + isPasswordUpdated = true; + await sharedPreferences.setBool(key, isPasswordUpdated); + } } \ No newline at end of file diff --git a/lib/di.dart b/lib/di.dart index 76813f475..b7b012925 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -31,6 +31,7 @@ import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; +import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart'; import 'package:cake_wallet/themes/theme_list.dart'; import 'package:cake_wallet/utils/device_info.dart'; 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 import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cake_wallet/core/backup_service.dart'; import 'package:cw_core/wallet_service.dart'; @@ -566,6 +569,21 @@ Future setup({ authService: getIt.get(), )); + getIt.registerFactoryParam( + (WalletListViewModel walletListViewModel, _) => WalletEditViewModel( + walletListViewModel, getIt.get())); + + getIt.registerFactoryParam, void>((args, _) { + final walletListViewModel = args.first as WalletListViewModel; + final editingWallet = args.last as WalletListItem; + return WalletEditPage( + walletEditViewModel: getIt.get(param1: walletListViewModel), + authService: getIt.get(), + walletNewVM: getIt.get(param1: editingWallet.type), + editingWallet: editingWallet); + }); + + getIt.registerFactory(() { final wallet = getIt.get().wallet!; diff --git a/lib/router.dart b/lib/router.dart index 39dd75628..74043a191 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -45,6 +45,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_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_create_or_edit_view_model.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -63,6 +64,7 @@ import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart'; import 'package:cake_wallet/src/screens/receive/receive_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/src/screens/wallet/wallet_edit_page.dart'; import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart'; import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart'; import 'package:cake_wallet/src/screens/restore/restore_options_page.dart'; @@ -260,6 +262,12 @@ Route createRoute(RouteSettings settings) { return MaterialPageRoute( fullscreenDialog: true, builder: (_) => getIt.get()); + case Routes.walletEdit: + return MaterialPageRoute( + fullscreenDialog: true, + builder: (_) => getIt.get( + param1: settings.arguments as List)); + case Routes.auth: return MaterialPageRoute( fullscreenDialog: true, diff --git a/lib/routes.dart b/lib/routes.dart index a7a50c433..762a7d2f5 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -11,6 +11,7 @@ class Routes { static const transactionDetails = '/transaction_info'; static const receive = '/receive'; static const newSubaddress = '/new_subaddress'; + static const walletEdit = '/walletEdit'; static const disclaimer = '/disclaimer'; static const readDisclaimer = '/read_disclaimer'; static const seedLanguage = '/seed_language'; diff --git a/lib/src/screens/nodes/node_create_or_edit_page.dart b/lib/src/screens/nodes/node_create_or_edit_page.dart index ddce52774..216238b54 100644 --- a/lib/src/screens/nodes/node_create_or_edit_page.dart +++ b/lib/src/screens/nodes/node_create_or_edit_page.dart @@ -1,6 +1,8 @@ import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cw_core/node.dart'; import 'package:flutter/material.dart'; @@ -122,17 +124,35 @@ class NodeCreateOrEditPage extends BasePage { padding: EdgeInsets.only(right: 8.0), child: LoadingPrimaryButton( onPressed: () async { - if (_formKey.currentState != null && !_formKey.currentState!.validate()) { - return; - } + final confirmed = 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)); + }) ?? + false; - await nodeCreateOrEditViewModel.connect(); + if (confirmed) { + await editingNode!.delete(); + Navigator.of(context).pop(); + } }, - isLoading: nodeCreateOrEditViewModel - .connectionState is IsExecutingState, - text: S.of(context).node_test, - isDisabled: !nodeCreateOrEditViewModel.isReady, - color: Colors.orange, + text: S.of(context).delete, + isDisabled: !nodeCreateOrEditViewModel.isReady || + (isSelected ?? false), + color: Palette.red, textColor: Colors.white), )), Flexible( diff --git a/lib/src/screens/nodes/widgets/node_indicator.dart b/lib/src/screens/nodes/widgets/node_indicator.dart index 089950bed..47a89f551 100644 --- a/lib/src/screens/nodes/widgets/node_indicator.dart +++ b/lib/src/screens/nodes/widgets/node_indicator.dart @@ -9,8 +9,8 @@ class NodeIndicator extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - width: 8.0, - height: 8.0, + width: 12.0, + height: 12.0, decoration: BoxDecoration( shape: BoxShape.circle, color: isLive ? Palette.green : Palette.red), ); diff --git a/lib/src/screens/nodes/widgets/node_list_row.dart b/lib/src/screens/nodes/widgets/node_list_row.dart index 6169c6df4..89ec3bcc7 100644 --- a/lib/src/screens/nodes/widgets/node_list_row.dart +++ b/lib/src/screens/nodes/widgets/node_list_row.dart @@ -1,21 +1,23 @@ +import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; +import 'package:cw_core/node.dart'; import 'package:flutter/material.dart'; class NodeListRow extends StandardListRow { NodeListRow( {required String title, + required this.node, required void Function(BuildContext context) onTap, - required bool isSelected, - required this.isAlive}) + required bool isSelected}) : super(title: title, onTap: onTap, isSelected: isSelected); - final Future isAlive; + final Node node; @override - Widget buildTrailing(BuildContext context) { + Widget buildLeading(BuildContext context) { return FutureBuilder( - future: isAlive, + future: node.requestNode(), builder: (context, snapshot) { switch (snapshot.connectionState) { case ConnectionState.done: @@ -25,6 +27,24 @@ class NodeListRow extends StandardListRow { } }); } + + @override + Widget buildTrailing(BuildContext context) { + return GestureDetector( + onTap: () => Navigator.of(context).pushNamed(Routes.newNode, + arguments: {'editingNode': node, 'isSelected': isSelected}), + child: Container( + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context) + .textTheme + .headlineMedium! + .decorationColor!), + child: Icon(Icons.edit, + size: 14, + color: Theme.of(context).textTheme.headlineMedium!.color!))); + } } class NodeHeaderListRow extends StandardListRow { diff --git a/lib/src/screens/settings/connection_sync_page.dart b/lib/src/screens/settings/connection_sync_page.dart index edcb57945..4a60ab136 100644 --- a/lib/src/screens/settings/connection_sync_page.dart +++ b/lib/src/screens/settings/connection_sync_page.dart @@ -12,7 +12,6 @@ import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart'; -import 'package:flutter_slidable/flutter_slidable.dart'; class ConnectionSyncPage extends BasePage { ConnectionSyncPage(this.nodeListViewModel, this.dashboardViewModel); @@ -64,49 +63,37 @@ class ConnectionSyncPage extends BasePage { itemBuilder: (_, sectionIndex, index) { final node = nodeListViewModel.nodes[index]; final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex; - final nodeListRow = Semantics( - label: 'Slidable', - selected: isSelected, - enabled: !isSelected, - child: NodeListRow( - title: node.uriRaw, - isSelected: isSelected, - isAlive: node.requestNode(), - onTap: (_) async { - if (isSelected) { - return; - } + final nodeListRow = NodeListRow( + title: node.uriRaw, + node: node, + isSelected: isSelected, + onTap: (_) async { + if (isSelected) { + return; + } - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: - S.of(context).change_current_node_title, - alertContent: nodeListViewModel - .getAlertContent(node.uriRaw), - 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(); - }, - ); - }); - }, - ), + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: + S.of(context).change_current_node_title, + alertContent: nodeListViewModel + .getAlertContent(node.uriRaw), + 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 = Slidable( - key: Key('${node.keyIndex}'), - startActionPane: _actionPane(context, node, isSelected), - endActionPane: _actionPane(context, node, isSelected), - child: nodeListRow, - ); - - return dismissibleRow; + return nodeListRow; }, ), ); @@ -134,44 +121,4 @@ class ConnectionSyncPage extends BasePage { }, ); } - - ActionPane _actionPane(BuildContext context, Node node, bool isSelected) => ActionPane( - motion: const ScrollMotion(), - extentRatio: isSelected ? 0.3 : 0.6, - children: [ - if (!isSelected) - SlidableAction( - onPressed: (context) async { - final confirmed = 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)); - }) ?? - false; - - if (confirmed) { - await nodeListViewModel.delete(node); - } - }, - backgroundColor: Colors.red, - foregroundColor: Colors.white, - icon: CupertinoIcons.delete, - label: S.of(context).delete, - ), - SlidableAction( - onPressed: (_) => Navigator.of(context).pushNamed(Routes.newNode, - arguments: {'editingNode': node, 'isSelected': isSelected}), - backgroundColor: Colors.blue, - foregroundColor: Colors.white, - icon: Icons.edit, - label: S.of(context).edit, - ), - ], - ); } diff --git a/lib/src/screens/wallet/wallet_edit_page.dart b/lib/src/screens/wallet/wallet_edit_page.dart new file mode 100644 index 000000000..3ff27f02f --- /dev/null +++ b/lib/src/screens/wallet/wallet_edit_page.dart @@ -0,0 +1,175 @@ +import 'package:another_flushbar/flushbar.dart'; +import 'package:cake_wallet/core/auth_service.dart'; +import 'package:cake_wallet/core/wallet_name_validator.dart'; +import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/utils/show_bar.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; +import 'package:cake_wallet/view_model/wallet_new_vm.dart'; +import 'package:flutter/material.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; + +class WalletEditPage extends BasePage { + WalletEditPage( + {required this.walletEditViewModel, + required this.editingWallet, + required this.walletNewVM, + required this.authService}) + : _formKey = GlobalKey(), + _labelController = TextEditingController(), + super() { + _labelController.text = editingWallet.name; + _labelController.addListener(() => walletEditViewModel.newName = _labelController.text); + } + + final GlobalKey _formKey; + final TextEditingController _labelController; + + final WalletEditViewModel walletEditViewModel; + final WalletNewVM walletNewVM; + final WalletListItem editingWallet; + final AuthService authService; + + @override + String get title => S.current.wallet_list_edit_wallet; + + Flushbar? _progressBar; + + @override + Widget body(BuildContext context) { + return Form( + key: _formKey, + child: Container( + padding: EdgeInsets.all(24.0), + child: Column( + children: [ + Expanded( + child: Center( + child: BaseTextFormField( + controller: _labelController, + hintText: S.of(context).wallet_list_wallet_name, + validator: WalletNameValidator()))), + Observer( + builder: (_) { + final isLoading = walletEditViewModel.state is WalletEditRenamePending || + walletEditViewModel.state is WalletEditDeletePending; + + return Row( + children: [ + Flexible( + child: Container( + padding: EdgeInsets.only(right: 8.0), + child: LoadingPrimaryButton( + isDisabled: isLoading, + onPressed: () => _removeWallet(context), + text: S.of(context).delete, + color: Palette.red, + textColor: Colors.white), + ), + ), + Flexible( + child: Container( + padding: EdgeInsets.only(left: 8.0), + child: LoadingPrimaryButton( + onPressed: () async { + if (_formKey.currentState?.validate() ?? false) { + if (walletNewVM.nameExists(walletEditViewModel.newName)) { + showPopUp( + context: context, + builder: (_) { + return AlertWithOneAction( + alertTitle: '', + alertContent: S.of(context).wallet_name_exists, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop(), + ); + }, + ); + } else { + try { + await walletEditViewModel.changeName(editingWallet); + Navigator.of(context).pop(); + walletEditViewModel.resetState(); + } catch (e) {} + } + } + }, + text: S.of(context).save, + color: Theme.of(context).accentTextTheme.bodyLarge!.color!, + textColor: Colors.white, + isDisabled: walletEditViewModel.newName.isEmpty || isLoading, + ), + ), + ) + ], + ); + }, + ) + ], + ), + ), + ); + } + + Future _removeWallet(BuildContext context) async { + authService.authenticateAction(context, onAuthSuccess: (isAuthenticatedSuccessfully) async { + if (!isAuthenticatedSuccessfully) { + return; + } + + _onSuccessfulAuth(context); + }); + } + + void _onSuccessfulAuth(BuildContext context) async { + bool confirmed = false; + + await showPopUp( + context: context, + builder: (BuildContext dialogContext) { + return AlertWithTwoActions( + alertTitle: S.of(context).delete_wallet, + alertContent: S.of(context).delete_wallet_confirm_message(editingWallet.name), + leftButtonText: S.of(context).cancel, + rightButtonText: S.of(context).delete, + actionLeftButton: () => Navigator.of(dialogContext).pop(), + actionRightButton: () { + confirmed = true; + Navigator.of(dialogContext).pop(); + }); + }); + + if (confirmed) { + Navigator.of(context).pop(); + + try { + changeProcessText(context, S.of(context).wallet_list_removing_wallet(editingWallet.name)); + await walletEditViewModel.remove(editingWallet); + hideProgressText(); + } catch (e) { + changeProcessText( + context, + S.of(context).wallet_list_failed_to_remove(editingWallet.name, e.toString()), + ); + } + } + } + + void changeProcessText(BuildContext context, String text) { + _progressBar = createBar(text, duration: null)..show(context); + } + + Future hideProgressText() async { + await Future.delayed(Duration(milliseconds: 50), () { + _progressBar?.dismiss(); + _progressBar = null; + }); + } +} diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 1a5433a2b..4c903abf1 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -6,7 +6,6 @@ import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:another_flushbar/flushbar.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/generated/i18n.dart'; @@ -15,7 +14,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; 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:flutter_slidable/flutter_slidable.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; class WalletListPage extends BasePage { @@ -77,31 +75,7 @@ class WalletListBodyState extends State { ? Theme.of(context).accentTextTheme!.titleSmall!.decorationColor! : Theme.of(context).colorScheme.background; final row = GestureDetector( - onTap: () async { - if (wallet.isCurrent || !wallet.isEnabled) { - return; - } - - final confirmed = await showPopUp( - context: context, - builder: (dialogContext) { - return AlertWithTwoActions( - alertTitle: S.of(context).change_wallet_alert_title, - alertContent: - S.of(context).change_wallet_alert_content(wallet.name), - leftButtonText: S.of(context).cancel, - rightButtonText: S.of(context).change, - actionLeftButton: () => - Navigator.of(dialogContext).pop(false), - actionRightButton: () => - Navigator.of(dialogContext).pop(true)); - }) ?? - false; - - if (confirmed) { - await _loadWallet(wallet); - } - }, + onTap: () => wallet.isCurrent ? null : _loadWallet(wallet), child: Container( height: tileHeight, width: double.infinity, @@ -129,16 +103,21 @@ class WalletListBodyState extends State { ? _imageFor(type: wallet.type) : nonWalletTypeIcon, SizedBox(width: 10), - Text( - wallet.name, - style: TextStyle( + Flexible( + child: Text( + wallet.name, + maxLines: null, + softWrap: true, + style: TextStyle( fontSize: 22, fontWeight: FontWeight.w500, color: Theme.of(context) .primaryTextTheme .titleLarge! - .color!), - ) + .color!, + ), + ), + ), ], ), ), @@ -149,12 +128,38 @@ class WalletListBodyState extends State { return wallet.isCurrent ? row - : Slidable( - key: Key('${wallet.key}'), - startActionPane: _actionPane(wallet), - endActionPane: _actionPane(wallet), - child: row, - ); + : Row(children: [ + Expanded(child: row), + GestureDetector( + onTap: () => Navigator.of(context).pushNamed( + Routes.walletEdit, + arguments: [widget.walletListViewModel, wallet]), + child: Container( + padding: EdgeInsets.only(right: 20), + child: Center( + child: Container( + height: 40, + width: 44, + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context) + .textTheme + .headlineMedium! + .decorationColor!), + child: Icon( + Icons.edit, + size: 14, + color: Theme.of(context) + .textTheme + .headlineMedium! + .color!, + ), + ), + ), + ), + ) + ]); }), ), ), @@ -226,47 +231,6 @@ class WalletListBodyState extends State { }); } - Future _removeWallet(WalletListItem wallet) async { - widget.authService.authenticateAction(context, - onAuthSuccess: (isAuthenticatedSuccessfully) async { - if (!isAuthenticatedSuccessfully) { - return; - } - _onSuccessfulAuth(wallet); - }); - } - - void _onSuccessfulAuth(WalletListItem wallet) async { - bool confirmed = false; - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context).delete_wallet, - alertContent: S.of(context).delete_wallet_confirm_message(wallet.name), - leftButtonText: S.of(context).cancel, - rightButtonText: S.of(context).delete, - actionLeftButton: () => Navigator.of(context).pop(), - actionRightButton: () { - confirmed = true; - Navigator.of(context).pop(); - }, - ); - }); - - if (confirmed) { - try { - changeProcessText(S.of(context).wallet_list_removing_wallet(wallet.name)); - await widget.walletListViewModel.remove(wallet); - hideProgressText(); - } catch (e) { - changeProcessText( - S.of(context).wallet_list_failed_to_remove(wallet.name, e.toString()), - ); - } - } - } - void changeProcessText(String text) { _progressBar = createBar(text, duration: null)..show(context); } @@ -277,18 +241,4 @@ class WalletListBodyState extends State { _progressBar = null; }); } - - ActionPane _actionPane(WalletListItem wallet) => ActionPane( - motion: const ScrollMotion(), - extentRatio: 0.3, - children: [ - SlidableAction( - onPressed: (_) => _removeWallet(wallet), - backgroundColor: Colors.red, - foregroundColor: Colors.white, - icon: CupertinoIcons.delete, - label: S.of(context).delete, - ), - ], - ); } diff --git a/lib/src/widgets/standard_list.dart b/lib/src/widgets/standard_list.dart index 0e371ac8a..7b2a0c004 100644 --- a/lib/src/widgets/standard_list.dart +++ b/lib/src/widgets/standard_list.dart @@ -231,7 +231,7 @@ class SectionStandardList extends StatelessWidget { return Container(); } - return StandardListSeparator(padding: EdgeInsets.only(left: 24)); + return StandardListSeparator(padding: dividerPadding); }, itemCount: totalRows.length, itemBuilder: (_, index) => totalRows[index]); diff --git a/lib/view_model/wallet_list/wallet_edit_view_model.dart b/lib/view_model/wallet_list/wallet_edit_view_model.dart new file mode 100644 index 000000000..0582c3f87 --- /dev/null +++ b/lib/view_model/wallet_list/wallet_edit_view_model.dart @@ -0,0 +1,55 @@ +import 'package:cake_wallet/core/wallet_loading_service.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cw_core/wallet_service.dart'; +import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; + +part 'wallet_edit_view_model.g.dart'; + +class WalletEditViewModel = WalletEditViewModelBase with _$WalletEditViewModel; + +abstract class WalletEditViewModelState {} + +class WalletEditViewModelInitialState extends WalletEditViewModelState {} + +class WalletEditRenamePending extends WalletEditViewModelState {} + +class WalletEditDeletePending extends WalletEditViewModelState {} + +abstract class WalletEditViewModelBase with Store { + WalletEditViewModelBase(this._walletListViewModel, this._walletLoadingService) + : state = WalletEditViewModelInitialState(), + newName = ''; + + @observable + WalletEditViewModelState state; + + @observable + String newName; + + final WalletListViewModel _walletListViewModel; + final WalletLoadingService _walletLoadingService; + + @action + Future changeName(WalletListItem walletItem) async { + state = WalletEditRenamePending(); + await _walletLoadingService.renameWallet( + walletItem.type, walletItem.name, newName); + _walletListViewModel.updateList(); + } + + @action + Future remove(WalletListItem wallet) async { + state = WalletEditDeletePending(); + final walletService = getIt.get(param1: wallet.type); + await walletService.remove(wallet.name); + resetState(); + _walletListViewModel.updateList(); + } + + @action + void resetState() { + state = WalletEditViewModelInitialState(); + } +} 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 be8d928aa..b12fe0c90 100644 --- a/lib/view_model/wallet_list/wallet_list_view_model.dart +++ b/lib/view_model/wallet_list/wallet_list_view_model.dart @@ -1,10 +1,9 @@ import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/wallet_loading_service.dart'; +import 'package:cw_core/wallet_base.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/store/app_store.dart'; -import 'package:cw_core/wallet_service.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; @@ -21,8 +20,8 @@ abstract class WalletListViewModelBase with Store { this._walletLoadingService, this._authService, ) : wallets = ObservableList() { - _updateList(); - reaction((_) => _appStore.wallet, (_) => _updateList()); + updateList(); + reaction((_) => _appStore.wallet, (_) => updateList()); } @observable @@ -37,20 +36,14 @@ abstract class WalletListViewModelBase with Store { @action Future loadWallet(WalletListItem walletItem) async { - final wallet = await _walletLoadingService.load(walletItem.type, walletItem.name); + final wallet = + await _walletLoadingService.load(walletItem.type, walletItem.name); + _appStore.changeCurrentWallet(wallet); - _updateList(); } @action - Future remove(WalletListItem wallet) async { - final walletService = getIt.get(param1: wallet.type); - await walletService.remove(wallet.name); - await _walletInfoSource.delete(wallet.key); - _updateList(); - } - - void _updateList() { + void updateList() { wallets.clear(); wallets.addAll( _walletInfoSource.values.map( @@ -58,7 +51,8 @@ abstract class WalletListViewModelBase with Store { name: info.name, type: info.type, key: info.key, - isCurrent: info.name == _appStore.wallet!.name && info.type == _appStore.wallet!.type, + isCurrent: info.name == _appStore.wallet!.name && + info.type == _appStore.wallet!.type, isEnabled: availableWalletTypes.contains(info.type), ), ), diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 9d508b810..478138159 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "عناوين المستلم", "wallet_list_title": "محفظة Monero", "wallet_list_create_new_wallet": "إنشاء محفظة جديدة", + "wallet_list_edit_wallet" : "تحرير المحفظة", + "wallet_list_wallet_name" : "اسم المحفظة", "wallet_list_restore_wallet": "استعادة المحفظة", "wallet_list_load_wallet": "تحميل المحفظة", "wallet_list_loading_wallet": "جار تحميل محفظة ${wallet_name}", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 362575d1d..05c65e305 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Адрес на получател", "wallet_list_title": "Monero портфейл", "wallet_list_create_new_wallet": "Създаване на нов портфейл", + "wallet_list_edit_wallet" : "Редактиране на портфейла", + "wallet_list_wallet_name" : "Име на портфейла", "wallet_list_restore_wallet": "Възстановяване на портфейл", "wallet_list_load_wallet": "Зареждане на портфейл", "wallet_list_loading_wallet": "Зареждане на портфейл ${wallet_name}", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index f8775901b..e33a456a0 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Adresa příjemce", "wallet_list_title": "Monero Wallet", "wallet_list_create_new_wallet": "Vytvořit novou peněženku", + "wallet_list_edit_wallet" : "Upravit peněženku", + "wallet_list_wallet_name" : "Název peněženky", "wallet_list_restore_wallet": "Obnovit peněženku", "wallet_list_load_wallet": "Načíst peněženku", "wallet_list_loading_wallet": "Načítám ${wallet_name} peněženku", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 8a761de27..79e0f3b4f 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Empfängeradressen", "wallet_list_title": "Monero-Wallet", "wallet_list_create_new_wallet": "Neue Wallet erstellen", + "wallet_list_edit_wallet" : "Wallet bearbeiten", + "wallet_list_wallet_name" : "Wallet namen", "wallet_list_restore_wallet": "Wallet wiederherstellen", "wallet_list_load_wallet": "Wallet laden", "wallet_list_loading_wallet": "Wallet ${wallet_name} wird geladen", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 60671ad15..6d23bf3ad 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Recipient addresses", "wallet_list_title": "Monero Wallet", "wallet_list_create_new_wallet": "Create New Wallet", + "wallet_list_edit_wallet" : "Edit wallet", + "wallet_list_wallet_name" : "Wallet name", "wallet_list_restore_wallet": "Restore Wallet", "wallet_list_load_wallet": "Load wallet", "wallet_list_loading_wallet": "Loading ${wallet_name} wallet", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index fc3c506d4..a4dd802f7 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Direcciones de destinatarios", "wallet_list_title": "Monedero Monero", "wallet_list_create_new_wallet": "Crear nueva billetera", + "wallet_list_edit_wallet" : "Editar billetera", + "wallet_list_wallet_name" : "Nombre de la billetera", "wallet_list_restore_wallet": "Restaurar billetera", "wallet_list_load_wallet": "Billetera de carga", "wallet_list_loading_wallet": "Billetera ${wallet_name} de carga", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 8bd2ecea4..0464ec1ed 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Adresse du bénéficiaire", "wallet_list_title": "Portefeuille (Wallet) Monero", "wallet_list_create_new_wallet": "Créer un Nouveau Portefeuille (Wallet)", + "wallet_list_edit_wallet" : "Modifier le portefeuille", + "wallet_list_wallet_name" : "Nom du portefeuille", "wallet_list_restore_wallet": "Restaurer un Portefeuille (Wallet)", "wallet_list_load_wallet": "Charger un Portefeuille (Wallet)", "wallet_list_loading_wallet": "Chargement du portefeuille (wallet) ${wallet_name}", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index a3d334b75..9c6dabbb6 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Adireshin masu amfani", "wallet_list_title": "Monero walat", "wallet_list_create_new_wallet": "Ƙirƙiri Sabon Wallet", + "wallet_list_edit_wallet" : "Gyara walat", + "wallet_list_wallet_name" : "Sunan walat", "wallet_list_restore_wallet": "Maida Wallet", "wallet_list_load_wallet": "Ana loda wallet na Monero", "wallet_list_loading_wallet": "Ana loda ${wallet_name} walat", @@ -617,3 +619,4 @@ "share": "Raba", "slidable": "Mai iya zamewa" } + diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index c4e0db57f..1f6ddeec5 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "प्राप्तकर्ता के पते", "wallet_list_title": "Monero बटुआ", "wallet_list_create_new_wallet": "नया बटुआ बनाएँ", + "wallet_list_edit_wallet" : "बटुआ संपादित करें", + "wallet_list_wallet_name" : "बटुआ नाम", "wallet_list_restore_wallet": "वॉलेट को पुनर्स्थापित करें", "wallet_list_load_wallet": "वॉलेट लोड करें", "wallet_list_loading_wallet": "लोड हो रहा है ${wallet_name} बटुआ", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 468d08ef7..d402c435f 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Adrese primatelja", "wallet_list_title": "Monero novčanik", "wallet_list_create_new_wallet": "Izradi novi novčanik", + "wallet_list_edit_wallet" : "Uredi novčanik", + "wallet_list_wallet_name" : "Naziv novčanika", "wallet_list_restore_wallet": "Oporavi novčanik", "wallet_list_load_wallet": "Učitaj novčanik", "wallet_list_loading_wallet": "Učitavanje novčanika ${wallet_name}", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 6858f22b1..cb03cd361 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Alamat Penerima", "wallet_list_title": "Dompet Monero", "wallet_list_create_new_wallet": "Buat Dompet Baru", + "wallet_list_edit_wallet" : "Edit dompet", + "wallet_list_wallet_name" : "Nama dompet", "wallet_list_restore_wallet": "Pulihkan Dompet", "wallet_list_load_wallet": "Muat dompet", "wallet_list_loading_wallet": "Memuat ${wallet_name} dompet", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 08d41fcfc..4052edca0 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Indirizzi dei destinatari", "wallet_list_title": "Portafoglio Monero", "wallet_list_create_new_wallet": "Crea Nuovo Portafoglio", + "wallet_list_edit_wallet" : "Modifica portafoglio", + "wallet_list_wallet_name" : "Nome del portafoglio", "wallet_list_restore_wallet": "Recupera Portafoglio", "wallet_list_load_wallet": "Caricamento Portafoglio", "wallet_list_loading_wallet": "Caricamento portafoglio ${wallet_name}", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 82e493077..c02f26339 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "受信者のアドレス", "wallet_list_title": "Monero 財布", "wallet_list_create_new_wallet": "新しいウォレットを作成", + "wallet_list_edit_wallet" : "ウォレットを編集する", + "wallet_list_wallet_name" : "ウォレット名", "wallet_list_restore_wallet": "ウォレットを復元", "wallet_list_load_wallet": "ウォレットをロード", "wallet_list_loading_wallet": "読み込み中 ${wallet_name} 財布", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 0461406d5..3be8d9f00 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "받는 사람 주소", "wallet_list_title": "모네로 월렛", "wallet_list_create_new_wallet": "새 월렛 만들기", + "wallet_list_edit_wallet" : "지갑 수정", + "wallet_list_wallet_name" : "지갑 이름", "wallet_list_restore_wallet": "월렛 복원", "wallet_list_load_wallet": "지갑로드", "wallet_list_loading_wallet": "로딩 ${wallet_name} 지갑", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 76b28f23d..8e60f4b38 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "လက်ခံသူလိပ်စာများ", "wallet_list_title": "Monero ပိုက်ဆံအိတ်", "wallet_list_create_new_wallet": "Wallet အသစ်ဖန်တီးပါ။", + "wallet_list_edit_wallet" : "ပိုက်ဆံအိတ်ကို တည်းဖြတ်ပါ။", + "wallet_list_wallet_name" : "ပိုက်ဆံအိတ်နာမည်", "wallet_list_restore_wallet": "ပိုက်ဆံအိတ်ကို ပြန်ယူပါ။", "wallet_list_load_wallet": "ပိုက်ဆံအိတ်ကို တင်ပါ။", "wallet_list_loading_wallet": "${wallet_name} ပိုက်ဆံအိတ်ကို ဖွင့်နေသည်။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index ddc2aab73..861591ef5 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Adressen van ontvangers", "wallet_list_title": "Monero portemonnee", "wallet_list_create_new_wallet": "Maak een nieuwe portemonnee", + "wallet_list_edit_wallet" : "Portemonnee bewerken", + "wallet_list_wallet_name" : "Portemonnee naam", "wallet_list_restore_wallet": "Portemonnee herstellen", "wallet_list_load_wallet": "Portemonnee laden", "wallet_list_loading_wallet": "Bezig met laden ${wallet_name} portemonnee", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index be1529891..48921ea29 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Adres odbiorcy", "wallet_list_title": "Portfel Monero", "wallet_list_create_new_wallet": "Utwórz nowy portfel", + "wallet_list_edit_wallet" : "Edytuj portfel", + "wallet_list_wallet_name" : "Nazwa portfela", "wallet_list_restore_wallet": "Przywróć portfel", "wallet_list_load_wallet": "Załaduj portfel", "wallet_list_loading_wallet": "Ładuję ${wallet_name} portfel", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 2a9be7887..38aa03478 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Endereços de destinatários", "wallet_list_title": "Carteira Monero", "wallet_list_create_new_wallet": "Criar nova carteira", + "wallet_list_edit_wallet" : "Editar carteira", + "wallet_list_wallet_name" : "Nome da carteira", "wallet_list_restore_wallet": "Restaurar carteira", "wallet_list_load_wallet": "Abrir carteira", "wallet_list_loading_wallet": "Abrindo a carteira ${wallet_name}", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 62d6f0a49..b4a926b64 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Адреса получателей", "wallet_list_title": "Monero Кошелёк", "wallet_list_create_new_wallet": "Создать новый кошелёк", + "wallet_list_edit_wallet" : "Изменить кошелек", + "wallet_list_wallet_name" : "Имя кошелька", "wallet_list_restore_wallet": "Восстановить кошелёк", "wallet_list_load_wallet": "Загрузка кошелька", "wallet_list_loading_wallet": "Загрузка ${wallet_name} кошелька", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index db9777add..9a73425d1 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "ที่อยู่ผู้รับ", "wallet_list_title": "กระเป๋า Monero", "wallet_list_create_new_wallet": "สร้างกระเป๋าใหม่", + "wallet_list_edit_wallet" : "แก้ไขกระเป๋าสตางค์", + "wallet_list_wallet_name" : "ชื่อกระเป๋าสตางค์", "wallet_list_restore_wallet": "กู้กระเป๋า", "wallet_list_load_wallet": "โหลดกระเป๋า", "wallet_list_loading_wallet": "กำลังโหลดกระเป๋า ${wallet_name}", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 3003d1f81..0517ebd50 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Alıcı adres", "wallet_list_title": "Monero Cüzdanı", "wallet_list_create_new_wallet": "Yeni Cüzdan Oluştur", + "wallet_list_edit_wallet" : "Cüzdanı düzenle", + "wallet_list_wallet_name" : "Cüzdan adı", "wallet_list_restore_wallet": "Cüzdanı Geri Yükle", "wallet_list_load_wallet": "Cüzdanı yükle", "wallet_list_loading_wallet": "${wallet_name} cüzdanı yükleniyor", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 8934e160e..c50ca30c8 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Адреси одержувачів", "wallet_list_title": "Monero Гаманець", "wallet_list_create_new_wallet": "Створити новий гаманець", + "wallet_list_edit_wallet" : "Редагувати гаманець", + "wallet_list_wallet_name" : "Назва гаманця", "wallet_list_restore_wallet": "Відновити гаманець", "wallet_list_load_wallet": "Завантаження гаманця", "wallet_list_loading_wallet": "Завантаження ${wallet_name} гаманця", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index b8cda7c69..41a50018b 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "وصول کنندگان کے پتے", "wallet_list_title": "Monero والیٹ", "wallet_list_create_new_wallet": "نیا والیٹ بنائیں", + "wallet_list_edit_wallet" : "بٹوے میں ترمیم کریں۔", + "wallet_list_wallet_name" : "بٹوے کا نام", "wallet_list_restore_wallet": "والیٹ کو بحال کریں۔", "wallet_list_load_wallet": "پرس لوڈ کریں۔", "wallet_list_loading_wallet": "${wallet_name} والیٹ لوڈ ہو رہا ہے۔", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 2d3869a81..a875b220d 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "Àwọn àdírẹ́sì olùgbà", "wallet_list_title": "Àpamọ́wọ́ Monero", "wallet_list_create_new_wallet": "Ṣe àpamọ́wọ́ títun", + "wallet_list_edit_wallet" : "Ṣatunkọ apamọwọ", + "wallet_list_wallet_name" : "Orukọ apamọwọ", "wallet_list_restore_wallet": "Restore àpamọ́wọ́", "wallet_list_load_wallet": "Load àpamọ́wọ́", "wallet_list_loading_wallet": "Ń ṣí àpamọ́wọ́ ${wallet_name}", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 526caeb14..2d1506589 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -250,6 +250,8 @@ "transaction_details_recipient_address": "收件人地址", "wallet_list_title": "Monero 钱包", "wallet_list_create_new_wallet": "创建新钱包", + "wallet_list_edit_wallet" : "编辑钱包", + "wallet_list_wallet_name" : "钱包名称", "wallet_list_restore_wallet": "恢复钱包", "wallet_list_load_wallet": "加载钱包", "wallet_list_loading_wallet": "载入中 ${wallet_name} 钱包",