diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml index 64adea1e7..bc189f92c 100644 --- a/android/app/src/main/AndroidManifestBase.xml +++ b/android/app/src/main/AndroidManifestBase.xml @@ -22,7 +22,6 @@ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:windowSoftInputMode="adjustResize" - android:screenOrientation="portrait" android:exported="true"> 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 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.dart b/cw_bitcoin/lib/electrum.dart index 70b072f7b..a05c251fe 100644 --- a/cw_bitcoin/lib/electrum.dart +++ b/cw_bitcoin/lib/electrum.dart @@ -66,54 +66,68 @@ class ElectrumClient { socket!.listen((Uint8List event) { try { final msg = utf8.decode(event.toList()); - final response = - json.decode(msg) as Map; - _handleResponse(response); - } on FormatException catch (e) { - final msg = e.message.toLowerCase(); - - if (e.source is String) { - unterminatedString += e.source as String; - } - - if (msg.contains("not a subtype of type")) { - unterminatedString += e.source as String; - return; - } - - if (isJSONStringCorrect(unterminatedString)) { - final response = - json.decode(unterminatedString) as Map; - _handleResponse(response); - unterminatedString = ''; - } - } on TypeError catch (e) { - if (!e.toString().contains('Map') && !e.toString().contains('Map')) { - return; - } - - final source = utf8.decode(event.toList()); - unterminatedString += source; - - if (isJSONStringCorrect(unterminatedString)) { - final response = - json.decode(unterminatedString) as Map; - _handleResponse(response); - // unterminatedString = null; - unterminatedString = ''; + final messagesList = msg.split("\n"); + for (var message in messagesList) { + if (message.isEmpty) { + continue; + } + _parseResponse(message); } } catch (e) { print(e.toString()); } }, onError: (Object error) { print(error.toString()); + unterminatedString = ''; _setIsConnected(false); }, onDone: () { + unterminatedString = ''; _setIsConnected(false); }); keepAlive(); } + void _parseResponse(String message) { + try { + final response = json.decode(message) as Map; + _handleResponse(response); + } on FormatException catch (e) { + final msg = e.message.toLowerCase(); + + if (e.source is String) { + unterminatedString += e.source as String; + } + + if (msg.contains("not a subtype of type")) { + unterminatedString += e.source as String; + return; + } + + if (isJSONStringCorrect(unterminatedString)) { + final response = + json.decode(unterminatedString) as Map; + _handleResponse(response); + unterminatedString = ''; + } + } on TypeError catch (e) { + if (!e.toString().contains('Map') && !e.toString().contains('Map')) { + return; + } + + unterminatedString += message; + + if (isJSONStringCorrect(unterminatedString)) { + final response = + json.decode(unterminatedString) as Map; + _handleResponse(response); + // unterminatedString = null; + unterminatedString = ''; + } + } catch (e) { + print(e.toString()); + } + } + void keepAlive() { _aliveTimer?.cancel(); _aliveTimer = Timer.periodic(aliveTimerDuration, (_) async => ping()); @@ -217,7 +231,7 @@ class ElectrumClient { Future> getTransactionRaw( {required String hash}) async => - call(method: 'blockchain.transaction.get', params: [hash, true]) + callWithTimeout(method: 'blockchain.transaction.get', params: [hash, true], timeout: 10000) .then((dynamic result) { if (result is Map) { return result; @@ -228,7 +242,7 @@ class ElectrumClient { Future getTransactionHex( {required String hash}) async => - call(method: 'blockchain.transaction.get', params: [hash, false]) + callWithTimeout(method: 'blockchain.transaction.get', params: [hash, false], timeout: 10000) .then((dynamic result) { if (result is String) { return result; 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/crypto_currency.dart b/cw_core/lib/crypto_currency.dart index 698631af4..86ea3f214 100644 --- a/cw_core/lib/crypto_currency.dart +++ b/cw_core/lib/crypto_currency.dart @@ -68,6 +68,28 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen CryptoCurrency.stx, CryptoCurrency.btcln, CryptoCurrency.shib, + CryptoCurrency.aave, + CryptoCurrency.arb, + CryptoCurrency.bat, + CryptoCurrency.comp, + CryptoCurrency.cro, + CryptoCurrency.ens, + CryptoCurrency.ftm, + CryptoCurrency.frax, + CryptoCurrency.gusd, + CryptoCurrency.gtc, + CryptoCurrency.grt, + CryptoCurrency.ldo, + CryptoCurrency.nexo, + CryptoCurrency.cake, + CryptoCurrency.pepe, + CryptoCurrency.storj, + CryptoCurrency.tusd, + CryptoCurrency.wbtc, + CryptoCurrency.weth, + CryptoCurrency.zrx, + CryptoCurrency.dydx, + CryptoCurrency.steth, ]; static const havenCurrencies = [ @@ -122,7 +144,7 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen static const ape = CryptoCurrency(title: 'APE', tag: 'ETH', fullName: 'ApeCoin', raw: 30, name: 'ape', iconPath: 'assets/images/ape_icon.png'); static const avaxc = CryptoCurrency(title: 'AVAX', tag: 'AVAXC', raw: 31, name: 'avaxc', iconPath: 'assets/images/avaxc_icon.png'); static const btt = CryptoCurrency(title: 'BTT', tag: 'ETH', fullName: 'BitTorrent', raw: 32, name: 'btt', iconPath: 'assets/images/btt_icon.png'); - static const bttc = CryptoCurrency(title: 'BTTC', tag: 'TRX', fullName: 'BitTorrent-NEW', raw: 33, name: 'bttc', iconPath: 'assets/images/bttbsc_icon.png'); + static const bttc = CryptoCurrency(title: 'BTTC', tag: 'TRX', fullName: 'BitTorrent-NEW', raw: 33, name: 'bttc', iconPath: 'assets/images/btt_icon.png'); static const doge = CryptoCurrency(title: 'DOGE', fullName: 'Dogecoin', raw: 34, name: 'doge', iconPath: 'assets/images/doge_icon.png'); static const firo = CryptoCurrency(title: 'FIRO', raw: 35, name: 'firo', iconPath: 'assets/images/firo_icon.png'); static const usdttrc20 = CryptoCurrency(title: 'USDT', tag: 'TRX', fullName: 'USDT Tether', raw: 36, name: 'usdttrc20', iconPath: 'assets/images/usdttrc20_icon.png'); @@ -130,9 +152,9 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen static const sc = CryptoCurrency(title: 'SC', fullName: 'Siacoin', raw: 38, name: 'sc', iconPath: 'assets/images/sc_icon.png'); static const sol = CryptoCurrency(title: 'SOL', fullName: 'Solana', raw: 39, name: 'sol', iconPath: 'assets/images/sol_icon.png'); static const usdc = CryptoCurrency(title: 'USDC', tag: 'ETH', fullName: 'USD Coin', raw: 40, name: 'usdc', iconPath: 'assets/images/usdc_icon.png'); - static const usdcsol = CryptoCurrency(title: 'USDC', tag: 'SOL', fullName: 'USDC Coin', raw: 41, name: 'usdcsol', iconPath: 'assets/images/usdcsol_icon.png'); - static const zaddr = CryptoCurrency(title: 'ZZEC', tag: 'ZEC', fullName: 'Shielded Zcash', iconPath: 'assets/images/zaddr_icon.png', raw: 42, name: 'zaddr'); - static const zec = CryptoCurrency(title: 'TZEC', tag: 'ZEC', fullName: 'Transparent Zcash', iconPath: 'assets/images/zec_icon.png', raw: 43, name: 'zec'); + static const usdcsol = CryptoCurrency(title: 'USDC', tag: 'SOL', fullName: 'USDC Coin', raw: 41, name: 'usdcsol', iconPath: 'assets/images/usdc_icon.png'); + static const zaddr = CryptoCurrency(title: 'ZZEC', tag: 'ZEC', fullName: 'Shielded Zcash', raw: 42, name: 'zaddr', iconPath: 'assets/images/zec_icon.png'); + static const zec = CryptoCurrency(title: 'TZEC', tag: 'ZEC', fullName: 'Transparent Zcash', raw: 43, name: 'zec', iconPath: 'assets/images/zec_icon.png'); static const zen = CryptoCurrency(title: 'ZEN', fullName: 'Horizen', raw: 44, name: 'zen', iconPath: 'assets/images/zen_icon.png'); static const xvg = CryptoCurrency(title: 'XVG', fullName: 'Verge', raw: 45, name: 'xvg', iconPath: 'assets/images/xvg_icon.png'); @@ -153,7 +175,29 @@ class CryptoCurrency extends EnumerableItem with Serializable implemen static const uni = CryptoCurrency(title: 'UNI', tag: 'ETH', fullName: 'Uniswap', raw: 60, name: 'uni', iconPath: 'assets/images/uni_icon.png'); static const stx = CryptoCurrency(title: 'STX', fullName: 'Stacks', raw: 61, name: 'stx', iconPath: 'assets/images/stx_icon.png'); static const btcln = CryptoCurrency(title: 'BTC', tag: 'LN', fullName: 'Bitcoin Lightning Network', raw: 62, name: 'btcln', iconPath: 'assets/images/btc.png'); - static const shib = CryptoCurrency(title: 'SHIB', tag: 'ETH', fullName: 'SHIBA INU', raw: 63, name: 'shib', iconPath: 'assets/images/shib_icon.png'); + static const shib = CryptoCurrency(title: 'SHIB', tag: 'ETH', fullName: 'Shiba Inu', raw: 63, name: 'shib', iconPath: 'assets/images/shib_icon.png'); + static const aave = CryptoCurrency(title: 'AAVE', tag: 'ETH', fullName: 'Aave', raw: 64, name: 'aave', iconPath: 'assets/images/aave_icon.png'); + static const arb = CryptoCurrency(title: 'ARB', fullName: 'Arbitrum', raw: 65, name: 'arb', iconPath: 'assets/images/arb_icon.png'); + static const bat = CryptoCurrency(title: 'BAT', tag: 'ETH', fullName: 'Basic Attention Token', raw: 66, name: 'bat', iconPath: 'assets/images/bat_icon.png'); + static const comp = CryptoCurrency(title: 'COMP', tag: 'ETH', fullName: 'Compound', raw: 67, name: 'comp', iconPath: 'assets/images/comp_icon.png'); + static const cro = CryptoCurrency(title: 'CRO', tag: 'ETH', fullName: 'Crypto.com Cronos', raw: 68, name: 'cro', iconPath: 'assets/images/cro_icon.png'); + static const ens = CryptoCurrency(title: 'ENS', tag: 'ETH', fullName: 'Ethereum Name Service', raw: 69, name: 'ens', iconPath: 'assets/images/ens_icon.png'); + static const ftm = CryptoCurrency(title: 'FTM', tag: 'ETH', fullName: 'Fantom', raw: 70, name: 'ftm', iconPath: 'assets/images/ftm_icon.png'); + static const frax = CryptoCurrency(title: 'FRAX', tag: 'ETH', fullName: 'Frax', raw: 71, name: 'frax', iconPath: 'assets/images/frax_icon.png'); + static const gusd = CryptoCurrency(title: 'GUSD', tag: 'ETH', fullName: 'Gemini USD', raw: 72, name: 'gusd', iconPath: 'assets/images/gusd_icon.png'); + static const gtc = CryptoCurrency(title: 'GTC', tag: 'ETH', fullName: 'Gitcoin', raw: 73, name: 'gtc', iconPath: 'assets/images/gtc_icon.png'); + static const grt = CryptoCurrency(title: 'GRT', tag: 'ETH', fullName: 'The Graph', raw: 74, name: 'grt', iconPath: 'assets/images/grt_icon.png'); + static const ldo = CryptoCurrency(title: 'LDO', tag: 'ETH', fullName: 'Lido DAO', raw: 75, name: 'ldo', iconPath: 'assets/images/ldo_icon.png'); + static const nexo = CryptoCurrency(title: 'NEXO', tag: 'ETH', fullName: 'Nexo', raw: 76, name: 'nexo', iconPath: 'assets/images/nexo_icon.png'); + static const cake = CryptoCurrency(title: 'CAKE', tag: 'BSC', fullName: 'PancakeSwap', raw: 77, name: 'cake', iconPath: 'assets/images/cake_icon.png'); + static const pepe = CryptoCurrency(title: 'PEPE', tag: 'ETH', fullName: 'Pepe', raw: 78, name: 'pepe', iconPath: 'assets/images/pepe_icon.png'); + static const storj = CryptoCurrency(title: 'STORJ', tag: 'ETH', fullName: 'Storj', raw: 79, name: 'storj', iconPath: 'assets/images/storj_icon.png'); + static const tusd = CryptoCurrency(title: 'TUSD', tag: 'ETH', fullName: 'TrueUSD', raw: 80, name: 'tusd', iconPath: 'assets/images/tusd_icon.png'); + static const wbtc = CryptoCurrency(title: 'WBTC', tag: 'ETH', fullName: 'Wrapped Bitcoin', raw: 81, name: 'wbtc', iconPath: 'assets/images/wbtc_icon.png'); + static const weth = CryptoCurrency(title: 'WETH', tag: 'ETH', fullName: 'Wrapped Ethereum', raw: 82, name: 'weth', iconPath: 'assets/images/weth_icon.png'); + static const zrx = CryptoCurrency(title: 'ZRX', tag: 'ETH', fullName: '0x Protocol', raw: 83, name: 'zrx', iconPath: 'assets/images/zrx_icon.png'); + static const dydx = CryptoCurrency(title: 'DYDX', tag: 'ETH', fullName: 'dYdX', raw: 84, name: 'dydx', iconPath: 'assets/images/dydx_icon.png'); + static const steth = CryptoCurrency(title: 'STETH', tag: 'ETH', fullName: 'Lido Staked Ethereum', raw: 85, name: 'steth', iconPath: 'assets/images/steth_icon.png'); static final Map _rawCurrencyMap = 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..7592001b8 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,62 @@ abstract class MoneroWalletBase extends WalletBase renameWalletFiles(String newWalletName) async { + final currentWalletDirPath = await pathForWalletDir(name: name, type: type); + + try { + // -- rename the waller folder -- + final currentWalletDir = + Directory(await pathForWalletDir(name: name, type: type)); + final newWalletDirPath = + await pathForWalletDir(name: newWalletName, type: type); + await currentWalletDir.rename(newWalletDirPath); + + // -- use new waller folder to rename files with old names still -- + final renamedWalletPath = newWalletDirPath + '/$name'; + + final currentCacheFile = File(renamedWalletPath); + final currentKeysFile = File('$renamedWalletPath.keys'); + final currentAddressListFile = File('$renamedWalletPath.address.txt'); + + final newWalletPath = + await pathForWallet(name: newWalletName, type: type); + + if (currentCacheFile.existsSync()) { + await currentCacheFile.rename(newWalletPath); + } + if (currentKeysFile.existsSync()) { + await currentKeysFile.rename('$newWalletPath.keys'); + } + if (currentAddressListFile.existsSync()) { + await currentAddressListFile.rename('$newWalletPath.address.txt'); + } + } catch (e) { + 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(currentWalletDirPath).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/ios/Podfile.lock b/ios/Podfile.lock index 3a3e25dda..5c6ed30a6 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -286,4 +286,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 09df1114e7c360f55770d35a79356bf5446e0100 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 6ddb2b139..6522b2756 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -36,6 +36,26 @@ class AddressValidator extends TextValidator { case CryptoCurrency.oxt: case CryptoCurrency.paxg: case CryptoCurrency.uni: + case CryptoCurrency.aave: + case CryptoCurrency.bat: + case CryptoCurrency.comp: + case CryptoCurrency.cro: + case CryptoCurrency.ens: + case CryptoCurrency.ftm: + case CryptoCurrency.frax: + case CryptoCurrency.gusd: + case CryptoCurrency.gtc: + case CryptoCurrency.grt: + case CryptoCurrency.ldo: + case CryptoCurrency.nexo: + case CryptoCurrency.pepe: + case CryptoCurrency.storj: + case CryptoCurrency.tusd: + case CryptoCurrency.wbtc: + case CryptoCurrency.weth: + case CryptoCurrency.zrx: + case CryptoCurrency.dydx: + case CryptoCurrency.steth: case CryptoCurrency.shib: return '0x[0-9a-zA-Z]'; case CryptoCurrency.xrp: @@ -102,8 +122,6 @@ class AddressValidator extends TextValidator { return null; case CryptoCurrency.ada: return null; - case CryptoCurrency.ape: - return [42]; case CryptoCurrency.avaxc: return [42]; case CryptoCurrency.bch: @@ -112,13 +130,43 @@ class AddressValidator extends TextValidator { return [42]; case CryptoCurrency.btc: return null; - case CryptoCurrency.dai: - return [42]; case CryptoCurrency.dash: return [34]; case CryptoCurrency.eos: return [42]; case CryptoCurrency.eth: + case CryptoCurrency.usdcpoly: + case CryptoCurrency.mana: + case CryptoCurrency.matic: + case CryptoCurrency.maticpoly: + case CryptoCurrency.mkr: + case CryptoCurrency.oxt: + case CryptoCurrency.paxg: + case CryptoCurrency.uni: + case CryptoCurrency.dai: + case CryptoCurrency.ape: + case CryptoCurrency.usdc: + case CryptoCurrency.usdterc20: + case CryptoCurrency.aave: + case CryptoCurrency.bat: + case CryptoCurrency.comp: + case CryptoCurrency.cro: + case CryptoCurrency.ens: + case CryptoCurrency.ftm: + case CryptoCurrency.frax: + case CryptoCurrency.gusd: + case CryptoCurrency.gtc: + case CryptoCurrency.grt: + case CryptoCurrency.ldo: + case CryptoCurrency.nexo: + case CryptoCurrency.pepe: + case CryptoCurrency.storj: + case CryptoCurrency.tusd: + case CryptoCurrency.wbtc: + case CryptoCurrency.weth: + case CryptoCurrency.zrx: + case CryptoCurrency.dydx: + case CryptoCurrency.steth: case CryptoCurrency.shib: return [42]; case CryptoCurrency.ltc: @@ -131,14 +179,10 @@ class AddressValidator extends TextValidator { return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]; case CryptoCurrency.trx: return [34]; - case CryptoCurrency.usdc: - return [42]; case CryptoCurrency.usdcsol: return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]; case CryptoCurrency.usdt: return [34]; - case CryptoCurrency.usdterc20: - return [42]; case CryptoCurrency.usdttrc20: return [34]; case CryptoCurrency.xlm: @@ -186,15 +230,6 @@ class AddressValidator extends TextValidator { return [35]; case CryptoCurrency.stx: return [40, 41, 42]; - case CryptoCurrency.usdcpoly: - case CryptoCurrency.mana: - case CryptoCurrency.matic: - case CryptoCurrency.maticpoly: - case CryptoCurrency.mkr: - case CryptoCurrency.oxt: - case CryptoCurrency.paxg: - case CryptoCurrency.uni: - return [42]; case CryptoCurrency.rune: return [43]; case CryptoCurrency.scrt: 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..3323e7831 100644 --- a/lib/core/wallet_loading_service.dart +++ b/lib/core/wallet_loading_service.dart @@ -7,43 +7,66 @@ 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; - } + // set shared preferences flag based on previous wallet name + if (type == WalletType.monero) { + final oldNameKey = PreferencesKey.moneroWalletUpdateV1Key(name); + final isPasswordUpdated = sharedPreferences.getBool(oldNameKey) ?? false; + final newNameKey = PreferencesKey.moneroWalletUpdateV1Key(newName); + await sharedPreferences.setBool(newNameKey, isPasswordUpdated); + } + } - 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 + 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); + + 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); + } +} diff --git a/lib/di.dart b/lib/di.dart index 06b5328dc..281b671c5 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -34,6 +34,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'; @@ -71,6 +72,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/erc20_token.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cake_wallet/core/backup_service.dart'; @@ -573,6 +576,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/main.dart b/lib/main.dart index 6ddc1c086..0860a167f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -193,12 +193,7 @@ class App extends StatefulWidget { } class AppState extends State with SingleTickerProviderStateMixin { - AppState() : yatStore = getIt.get() { - SystemChrome.setPreferredOrientations( - ResponsiveLayoutUtil.instance.isIpad ? - [DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight] : - [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); - } + AppState() : yatStore = getIt.get(); YatStore yatStore; StreamSubscription? stream; @@ -290,7 +285,43 @@ class AppState extends State with SingleTickerProviderStateMixin { locale: Locale(settingsStore.languageCode), onGenerateRoute: (settings) => Router.createRoute(settings), initialRoute: initialRoute, + home: _Home(), )); }); } } + +class _Home extends StatefulWidget { + const _Home(); + + @override + State<_Home> createState() => _HomeState(); +} + +class _HomeState extends State<_Home> { + @override + void didChangeDependencies() { + if(!ResponsiveLayoutUtil.instance.isMobile){ + _setOrientation(context); + } + super.didChangeDependencies(); + } + + + void _setOrientation(BuildContext context){ + final orientation = MediaQuery.of(context).orientation; + final width = MediaQuery.of(context).size.width; + final height = MediaQuery.of(context).size.height; + if (orientation == Orientation.portrait && width < height) { + SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); + } else if (orientation == Orientation.landscape && width > height) { + SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]); + } + + } + + @override + Widget build(BuildContext context) { + return const SizedBox.shrink(); + } +} diff --git a/lib/router.dart b/lib/router.dart index 1ceaf3e37..8a42e7b04 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -47,6 +47,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'; @@ -65,6 +66,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'; @@ -262,6 +264,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 c9d8cfc61..c6489d08c 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/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index 71dfda356..0c46aaf25 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/main_actions.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/version_comparator.dart'; import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -42,15 +43,30 @@ class DashboardPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Scaffold( - body: ResponsiveLayoutUtil.instance.isMobile - ? _DashboardPageView( + return Scaffold(body: LayoutBuilder( + builder: (context, constraints) { + if (DeviceInfo.instance.isDesktop) { + if (constraints.maxWidth > ResponsiveLayoutUtil.kDesktopMaxDashBoardWidthConstraint) { + return getIt.get(); + } else { + return _DashboardPageView( balancePage: balancePage, dashboardViewModel: dashboardViewModel, addressListViewModel: addressListViewModel, - ) - : getIt.get(), - ); + ); + } + } else if (ResponsiveLayoutUtil.instance.shouldRenderMobileUI()) { + return _DashboardPageView( + balancePage: balancePage, + dashboardViewModel: dashboardViewModel, + addressListViewModel: addressListViewModel, + ); + } else { + return getIt.get(); + } + + }, + )); } } @@ -251,7 +267,7 @@ class _DashboardPageView extends BasePage { pages.add(Semantics( label: S.of(context).market_place, child: MarketPlacePage( - dashboardViewModel: dashboardViewModel, + dashboardViewModel: dashboardViewModel, marketPlaceViewModel: getIt.get(), ), ), diff --git a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart index c29442450..cf2ae7e3d 100644 --- a/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart +++ b/lib/src/screens/dashboard/widgets/present_receive_option_picker.dart @@ -138,7 +138,7 @@ class PresentReceiveOptionPicker extends StatelessWidget { Container( margin: EdgeInsets.only(bottom: 40), child: InkWell( - onTap: () => Navigator.pop(context), + onTap: () => Navigator.pop(popUpContext), child: CircleAvatar( child: Icon( Icons.close, 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/receive/anonpay_invoice_page.dart b/lib/src/screens/receive/anonpay_invoice_page.dart index f8df3eeb3..88547e483 100644 --- a/lib/src/screens/receive/anonpay_invoice_page.dart +++ b/lib/src/screens/receive/anonpay_invoice_page.dart @@ -8,7 +8,6 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option import 'package:cake_wallet/src/screens/receive/widgets/anonpay_input_form.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; -import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/view_model/anon_invoice_page_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; @@ -54,9 +53,7 @@ class AnonPayInvoicePage extends BasePage { AppBarStyle get appBarStyle => AppBarStyle.transparent; @override - void onClose(BuildContext context) { - Navigator.popUntil(context, ModalRoute.withName(Routes.dashboard)); - } + void onClose(BuildContext context) => Navigator.popUntil(context, (route) => route.isFirst); @override Widget middle(BuildContext context) => diff --git a/lib/src/screens/receive/anonpay_receive_page.dart b/lib/src/screens/receive/anonpay_receive_page.dart index 0c175eb19..4deae7972 100644 --- a/lib/src/screens/receive/anonpay_receive_page.dart +++ b/lib/src/screens/receive/anonpay_receive_page.dart @@ -32,28 +32,7 @@ class AnonPayReceivePage extends BasePage { bool get resizeToAvoidBottomInset => false; @override - Widget leading(BuildContext context) { - final _backButton = Icon( - Icons.arrow_back_ios, - color: Theme.of(context) - .accentTextTheme! - .displayMedium! - .backgroundColor!, - size: 16, - ); - - return SizedBox( - height: 37, - width: 37, - child: ButtonTheme( - minWidth: double.minPositive, - child: TextButton( - onPressed: () => - Navigator.pushNamedAndRemoveUntil(context, Routes.dashboard, (route) => false), - child: _backButton), - ), - ); - } + void onClose(BuildContext context) => Navigator.popUntil(context, (route) => route.isFirst); @override Widget middle(BuildContext context) { diff --git a/lib/src/screens/seed/pre_seed_page.dart b/lib/src/screens/seed/pre_seed_page.dart index 5d0b0b804..6a8b6aa82 100644 --- a/lib/src/screens/seed/pre_seed_page.dart +++ b/lib/src/screens/seed/pre_seed_page.dart @@ -2,7 +2,6 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/themes/theme_base.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; @@ -37,46 +36,33 @@ class PreSeedPage extends BasePage { alignment: Alignment.center, padding: EdgeInsets.all(24), child: ConstrainedBox( - constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), child: Column( - children: [ - Flexible( - flex: 2, - child: AspectRatio( - aspectRatio: 1, - child: FittedBox(child: image, fit: BoxFit.contain))), - Flexible( - flex: 3, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: EdgeInsets.only(top: 70, left: 16, right: 16), - child: Text( - S - .of(context) - .pre_seed_description(wordsCount.toString()), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.normal, - color: Theme.of(context) - .primaryTextTheme! - .bodySmall! - .color!), - ), - ), - PrimaryButton( - onPressed: () => Navigator.of(context) - .popAndPushNamed(Routes.seed, arguments: true), - text: S.of(context).pre_seed_button_text, - color: Theme.of(context) - .accentTextTheme! - .bodyLarge! - .color!, - textColor: Colors.white) - ], - )) + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.3 + ), + child: AspectRatio(aspectRatio: 1, child: image), + ), + Padding( + padding: EdgeInsets.all(10), + child: Text( + S.of(context).pre_seed_description(wordsCount.toString()), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + color: Theme.of(context).primaryTextTheme.bodySmall!.color!), + ), + ), + PrimaryButton( + onPressed: () => + Navigator.of(context).popAndPushNamed(Routes.seed, arguments: true), + text: S.of(context).pre_seed_button_text, + color: Theme.of(context).accentTextTheme!.bodyLarge!.color!, + textColor: Colors.white) ], ), ), diff --git a/lib/src/screens/seed/wallet_seed_page.dart b/lib/src/screens/seed/wallet_seed_page.dart index d0687aa74..f37a24dcd 100644 --- a/lib/src/screens/seed/wallet_seed_page.dart +++ b/lib/src/screens/seed/wallet_seed_page.dart @@ -52,8 +52,7 @@ class WalletSeedPage extends BasePage { } @override - Widget? leading(BuildContext context) => - isNewWalletCreated ? null: super.leading(context); + Widget? leading(BuildContext context) => isNewWalletCreated ? null : super.leading(context); @override Widget trailing(BuildContext context) { @@ -67,16 +66,11 @@ class WalletSeedPage extends BasePage { margin: EdgeInsets.only(left: 10), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(16)), - color: Theme.of(context) - .accentTextTheme! - .bodySmall! - .color!), + color: Theme.of(context).accentTextTheme.bodySmall!.color!), child: Text( S.of(context).seed_language_next, style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w600, - color: Palette.blueCraiola), + fontSize: 14, fontWeight: FontWeight.w600, color: Palette.blueCraiola), ), ), ) @@ -87,121 +81,99 @@ class WalletSeedPage extends BasePage { Widget body(BuildContext context) { final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight; - return WillPopScope(onWillPop: () async => false, child: Container( - padding: EdgeInsets.all(24), - alignment: Alignment.center, - child: ConstrainedBox( - constraints: - BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), - child: Column( - children: [ - Flexible( - flex: 2, - child: AspectRatio( - aspectRatio: 1, - child: FittedBox(child: image, fit: BoxFit.fill))), - Flexible( - flex: 3, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return WillPopScope( + onWillPop: () async => false, + child: Container( + padding: EdgeInsets.all(24), + alignment: Alignment.center, + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + ConstrainedBox( + constraints: + BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.3), + child: AspectRatio(aspectRatio: 1, child: image), + ), + Observer(builder: (_) { + return Column( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - Padding( - padding: EdgeInsets.only(top: 33), - child: Observer(builder: (_) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - walletSeedViewModel.name, - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.w600, - color: Theme.of(context) - .primaryTextTheme! - .titleLarge! - .color!), - ), - Padding( - padding: - EdgeInsets.only(top: 20, left: 16, right: 16), - child: Text( - walletSeedViewModel.seed, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.normal, - color: Theme.of(context) - .primaryTextTheme! - .bodySmall! - .color!), - ), - ) - ], - ); - }), + Text( + walletSeedViewModel.name, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w600, + color: Theme.of(context).primaryTextTheme.titleLarge!.color!), ), - Column( - children: [ - isNewWalletCreated - ? Padding( - padding: EdgeInsets.only( - bottom: 52, left: 43, right: 43), - child: Text( - S.of(context).seed_reminder, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.normal, - color: Theme.of(context) - .primaryTextTheme! - .labelSmall! - .color!), - ), - ) - : Offstage(), - Row( - mainAxisSize: MainAxisSize.max, - children: [ - Flexible( - child: Container( - padding: EdgeInsets.only(right: 8.0), - child: PrimaryButton( - onPressed: () { - ShareUtil.share( - text: walletSeedViewModel.seed, - context: context, - ); - }, - text: S.of(context).save, - color: Colors.green, - textColor: Colors.white), - )), - Flexible( - child: Container( - padding: EdgeInsets.only(left: 8.0), - child: Builder( - builder: (context) => PrimaryButton( - onPressed: () { - Clipboard.setData(ClipboardData( - text: walletSeedViewModel.seed)); - showBar(context, - S.of(context).copied_to_clipboard); - }, - text: S.of(context).copy, - color: Theme.of(context) - .accentTextTheme! - .bodyMedium! - .color!, - textColor: Colors.white)), - )) - ], - ) - ], + Padding( + padding: EdgeInsets.only(top: 20, left: 16, right: 16), + child: Text( + walletSeedViewModel.seed, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + color: Theme.of(context).primaryTextTheme.bodySmall!.color!), + ), ) ], - )) - ], + ); + }), + Column( + children: [ + isNewWalletCreated + ? Padding( + padding: EdgeInsets.only(bottom: 43, left: 43, right: 43), + child: Text( + S.of(context).seed_reminder, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: Theme.of(context).primaryTextTheme.labelSmall!.color!), + ), + ) + : Offstage(), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + Flexible( + child: Container( + padding: EdgeInsets.only(right: 8.0), + child: PrimaryButton( + onPressed: () { + ShareUtil.share( + text: walletSeedViewModel.seed, + context: context, + ); + }, + text: S.of(context).save, + color: Colors.green, + textColor: Colors.white), + )), + Flexible( + child: Container( + padding: EdgeInsets.only(left: 8.0), + child: Builder( + builder: (context) => PrimaryButton( + onPressed: () { + Clipboard.setData( + ClipboardData(text: walletSeedViewModel.seed)); + showBar(context, S.of(context).copied_to_clipboard); + }, + text: S.of(context).copy, + color: Theme.of(context).accentTextTheme.bodyMedium!.color!, + textColor: Colors.white)), + )) + ], + ) + ], + ) + ], + ), ), - ))); + )); } } diff --git a/lib/src/screens/seed_language/widgets/seed_language_picker.dart b/lib/src/screens/seed_language/widgets/seed_language_picker.dart index 0aa22088f..742a31e61 100644 --- a/lib/src/screens/seed_language/widgets/seed_language_picker.dart +++ b/lib/src/screens/seed_language/widgets/seed_language_picker.dart @@ -4,16 +4,16 @@ import 'package:flutter/cupertino.dart'; import 'package:cake_wallet/generated/i18n.dart'; List flagImages = [ - Image.asset('assets/images/usa.png'), - Image.asset('assets/images/china.png'), - Image.asset('assets/images/holland.png'), - Image.asset('assets/images/germany.png'), - Image.asset('assets/images/japan.png'), - Image.asset('assets/images/portugal.png'), - Image.asset('assets/images/russia.png'), - Image.asset('assets/images/spain.png'), - Image.asset('assets/images/france.png'), - Image.asset('assets/images/italy.png'), + Image.asset('assets/images/flags/usa.png'), + Image.asset('assets/images/flags/chn.png'), + Image.asset('assets/images/flags/nld.png'), + Image.asset('assets/images/flags/deu.png'), + Image.asset('assets/images/flags/jpn.png'), + Image.asset('assets/images/flags/prt.png'), + Image.asset('assets/images/flags/rus.png'), + Image.asset('assets/images/flags/esp.png'), + Image.asset('assets/images/flags/fra.png'), + Image.asset('assets/images/flags/ita.png'), ]; const List languageCodes = [ 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 35151e528..ce4b0e25d 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 { @@ -78,31 +76,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, @@ -130,16 +104,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!, + ), + ), + ), ], ), ), @@ -150,12 +129,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!, + ), + ), + ), + ), + ) + ]); }), ), ), @@ -218,7 +223,7 @@ class WalletListBodyState extends State { await hideProgressText(); // only pop the wallets route in mobile as it will go back to dashboard page // in desktop platforms the navigation tree is different - if (ResponsiveLayoutUtil.instance.isMobile) { + if (ResponsiveLayoutUtil.instance.shouldRenderMobileUI()) { WidgetsBinding.instance.addPostFrameCallback((_) { Navigator.of(context).pop(); }); @@ -229,47 +234,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); } @@ -280,18 +244,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/screens/welcome/welcome_page.dart b/lib/src/screens/welcome/welcome_page.dart index 3d3e4c2e4..35537f063 100644 --- a/lib/src/screens/welcome/welcome_page.dart +++ b/lib/src/screens/welcome/welcome_page.dart @@ -46,145 +46,133 @@ class WelcomePage extends BasePage { @override Widget body(BuildContext context) { - final welcomeImage = currentTheme.type == ThemeType.dark ? welcomeImageDark : welcomeImageLight; + final welcomeImage = currentTheme.type == ThemeType.dark + ? welcomeImageDark + : welcomeImageLight; final newWalletImage = Image.asset('assets/images/new_wallet.png', height: 12, width: 12, color: Theme.of(context) - .accentTextTheme! - .headlineSmall! + .accentTextTheme.headlineSmall! .decorationColor!); final restoreWalletImage = Image.asset('assets/images/restore_wallet.png', height: 12, + width: 12, - color: Theme.of(context).primaryTextTheme!.titleLarge!.color!); + + color: Theme.of(context).primaryTextTheme.titleLarge!.color!); return WillPopScope( onWillPop: () async => false, child: Container( + alignment: Alignment.center, padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24), - child: Center( - child: ConstrainedBox( - constraints: - BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - flex: 2, - child: AspectRatio( - aspectRatio: aspectRatioImage, - child: FittedBox(child: welcomeImage, fit: BoxFit.fill))), - Flexible( - flex: 3, - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - children: [ - Padding( - padding: EdgeInsets.only(top: 24), - child: Text( - S.of(context).welcome, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.w500, - color: Theme.of(context) - .accentTextTheme! - .displayMedium! - .color, - ), - textAlign: TextAlign.center, - ), - ), - Padding( - padding: EdgeInsets.only(top: 5), - child: Text( - appTitle(context), - style: TextStyle( - fontSize: 36, - fontWeight: FontWeight.bold, - color: Theme.of(context) - .primaryTextTheme! - .titleLarge! - .color!, - ), - textAlign: TextAlign.center, - ), - ), - Padding( - padding: EdgeInsets.only(top: 5), - child: Text( - appDescription(context), - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context) - .accentTextTheme! - .displayMedium! - .color, - ), - textAlign: TextAlign.center, - ), - ), - ], - ), - Column( - children: [ - Text( - S.of(context).please_make_selection, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.normal, - color: Theme.of(context) - .accentTextTheme! - .displayMedium! - .color, - ), - textAlign: TextAlign.center, - ), - Padding( - padding: EdgeInsets.only(top: 24), - child: PrimaryImageButton( - onPressed: () => - Navigator.pushNamed(context, Routes.newWalletFromWelcome), - image: newWalletImage, - text: S.of(context).create_new, - color: Theme.of(context) - .accentTextTheme! - .titleSmall! - .decorationColor!, - textColor: Theme.of(context) - .accentTextTheme! - .headlineSmall! - .decorationColor!, - ), - ), - Padding( - padding: EdgeInsets.only(top: 10), - child: PrimaryImageButton( - onPressed: () { - Navigator.pushNamed(context, Routes.restoreOptions, - arguments: true); - }, - image: restoreWalletImage, - text: S.of(context).restore_wallet, - color: Theme.of(context) - .accentTextTheme! - .bodySmall! - .color!, - textColor: Theme.of(context) - .primaryTextTheme! - .titleLarge! - .color!), - ) - ], - ) - ], - )) - ], - ), + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + AspectRatio( + aspectRatio: aspectRatioImage, + child: FittedBox( + child: welcomeImage, fit: BoxFit.contain), + ), + Padding( + padding: EdgeInsets.only(top: 24), + child: Text( + S.of(context).welcome, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .accentTextTheme.displayMedium! + .color!, + ), + textAlign: TextAlign.center, + ), + ), + Padding( + padding: EdgeInsets.only(top: 5), + child: Text( + appTitle(context), + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.bold, + color: Theme.of(context) + .primaryTextTheme.titleLarge! + .color!, + ), + textAlign: TextAlign.center, + ), + ), + Padding( + padding: EdgeInsets.only(top: 5), + child: Text( + appDescription(context), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .accentTextTheme.displayMedium! + .color!, + ), + textAlign: TextAlign.center, + ), + ), + ], + ), + Column( + children: [ + Text( + S.of(context).please_make_selection, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.normal, + color: Theme.of(context) + .accentTextTheme.displayMedium! + .color!, + ), + textAlign: TextAlign.center, + ), + Padding( + padding: EdgeInsets.only(top: 24), + child: PrimaryImageButton( + onPressed: () => Navigator.pushNamed( + context, Routes.newWalletFromWelcome), + image: newWalletImage, + text: S.of(context).create_new, + color: Theme.of(context) + .accentTextTheme.titleSmall! + .decorationColor!, + textColor: Theme.of(context) + .accentTextTheme.headlineSmall! + .decorationColor!, + ), + ), + Padding( + padding: EdgeInsets.only(top: 10), + child: PrimaryImageButton( + onPressed: () { + Navigator.pushNamed( + context, Routes.restoreOptions, + arguments: true); + }, + image: restoreWalletImage, + text: S.of(context).restore_wallet, + color: Theme.of(context) + .accentTextTheme.bodySmall! + .color!, + textColor: Theme.of(context) + .primaryTextTheme.titleLarge! + .color!), + ) + ], + ) + ], ), ))); } diff --git a/lib/src/widgets/standard_list.dart b/lib/src/widgets/standard_list.dart index e16f5a38a..23e3914cf 100644 --- a/lib/src/widgets/standard_list.dart +++ b/lib/src/widgets/standard_list.dart @@ -234,7 +234,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/utils/responsive_layout_util.dart b/lib/utils/responsive_layout_util.dart index 99c722887..4c06b7a3c 100644 --- a/lib/utils/responsive_layout_util.dart +++ b/lib/utils/responsive_layout_util.dart @@ -1,22 +1,32 @@ import 'package:flutter/material.dart'; class ResponsiveLayoutUtil { - static const double _kMobileThreshold = 768; + static const double _kMobileThreshold = 550; static const double kDesktopMaxWidthConstraint = 400; + static const double kDesktopMaxDashBoardWidthConstraint = 900; static const double kPopupWidth = 400; static const double kPopupSpaceHeight = 100; - static const _kIpadMaxWidth = 2560.0; const ResponsiveLayoutUtil._(); static final instance = ResponsiveLayoutUtil._(); bool get isMobile => - MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width < _kMobileThreshold; + MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.shortestSide <= + _kMobileThreshold; - bool get isIpad { - final width = MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width; - return width >= _kMobileThreshold && !(width > _kIpadMaxWidth); + bool shouldRenderMobileUI() { + final mediaQuery = MediaQueryData.fromWindow(WidgetsBinding.instance.window); + final orientation = mediaQuery.orientation; + final width = mediaQuery.size.width; + final height = mediaQuery.size.height; + if (isMobile || + (orientation == Orientation.portrait && width < height) || + (orientation == Orientation.landscape && width < height)) { + return true; + } else { + return false; + } } /// Returns dynamic size. 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 5802b325d..66d2e7afd 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 06248b733..f86e0d00a 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 1a3ca4d85..36b9154b6 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 ea994d5bd..9d4350617 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 ac9f988f8..497b2cda5 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 5ff9cf3d4..23e00d8a8 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 60f9392cd..cd679b75f 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 d6fef5384..3525f533b 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", @@ -634,3 +636,4 @@ "share": "Raba", "slidable": "Mai iya zamewa" } + diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 7e782aefd..ee3c362bc 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 89e2526f8..324f9e09a 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 d084d6d82..0bcab42f1 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 4973ed215..a13101dee 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 8d582f529..36d2327d9 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 97f541648..195329cb3 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 4e751316c..bdd5dc2e4 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 25c759eea..bf0f38041 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 4f8645c9f..9864961f2 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 5179a182e..9e065d8a6 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 fecb57454..ceb2ba1e3 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 80590b2f6..9d9048fd4 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 03b425e63..7d5b1937a 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 0151cc6bd..0e3dd0101 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 fd65dc138..9e87549cf 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 d13cefc6a..3d7ec62e9 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 0f5a5ef7d..8039b88b1 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} 钱包", diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 8e4a338ab..e90d26406 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -14,14 +14,14 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.3.8" -MONERO_COM_BUILD_NUMBER=51 +MONERO_COM_VERSION="1.4.0" +MONERO_COM_BUILD_NUMBER=52 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.6.7" -CAKEWALLET_BUILD_NUMBER=161 +CAKEWALLET_VERSION="4.7.0" +CAKEWALLET_BUILD_NUMBER=162 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 114c9191f..52cb07183 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.3.8" -MONERO_COM_BUILD_NUMBER=49 +MONERO_COM_VERSION="1.4.0" +MONERO_COM_BUILD_NUMBER=50 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.6.7" -CAKEWALLET_BUILD_NUMBER=159 +CAKEWALLET_VERSION="4.7.0" +CAKEWALLET_BUILD_NUMBER=165 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index e05172bad..b31a5a973 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -15,8 +15,8 @@ if [ -n "$1" ]; then fi CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.0.6" -CAKEWALLET_BUILD_NUMBER=25 +CAKEWALLET_VERSION="1.0.7" +CAKEWALLET_BUILD_NUMBER=26 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then