Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-328-Restore-wallet-from-QRCode-and-sweep-all-funds-in-a-new-wallet

This commit is contained in:
Blazebrain 2023-07-14 10:04:25 +01:00
commit 3a37aab595
125 changed files with 1619 additions and 978 deletions

View file

@ -22,7 +22,6 @@
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait"
android:exported="true"> android:exported="true">
<meta-data <meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable" android:name="io.flutter.embedding.android.SplashScreenDrawable"

BIN
assets/images/aave_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
assets/images/arb_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

BIN
assets/images/bat_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

BIN
assets/images/cake_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 B

BIN
assets/images/comp_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
assets/images/cro_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
assets/images/dydx_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
assets/images/ens_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

BIN
assets/images/frax_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
assets/images/ftm_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 576 B

BIN
assets/images/grt_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
assets/images/gtc_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
assets/images/gusd_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 570 B

BIN
assets/images/ldo_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

BIN
assets/images/nexo_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
assets/images/pepe_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 773 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

BIN
assets/images/shib_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
assets/images/tusd_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

BIN
assets/images/wbtc_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
assets/images/weth_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6 KiB

BIN
assets/images/zrx_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View file

@ -53,9 +53,32 @@ class BitcoinWalletService extends WalletService<
} }
@override @override
Future<void> remove(String wallet) async => Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: WalletType.bitcoin)) File(await pathForWalletDir(name: wallet, type: getType()))
.delete(recursive: true); .delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> 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 @override
Future<BitcoinWallet> restoreFromKeys( Future<BitcoinWallet> restoreFromKeys(

View file

@ -66,54 +66,68 @@ class ElectrumClient {
socket!.listen((Uint8List event) { socket!.listen((Uint8List event) {
try { try {
final msg = utf8.decode(event.toList()); final msg = utf8.decode(event.toList());
final response = final messagesList = msg.split("\n");
json.decode(msg) as Map<String, dynamic>; for (var message in messagesList) {
_handleResponse(response); if (message.isEmpty) {
} on FormatException catch (e) { continue;
final msg = e.message.toLowerCase(); }
_parseResponse(message);
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<String, dynamic>;
_handleResponse(response);
unterminatedString = '';
}
} on TypeError catch (e) {
if (!e.toString().contains('Map<String, Object>') && !e.toString().contains('Map<String, dynamic>')) {
return;
}
final source = utf8.decode(event.toList());
unterminatedString += source;
if (isJSONStringCorrect(unterminatedString)) {
final response =
json.decode(unterminatedString) as Map<String, dynamic>;
_handleResponse(response);
// unterminatedString = null;
unterminatedString = '';
} }
} catch (e) { } catch (e) {
print(e.toString()); print(e.toString());
} }
}, onError: (Object error) { }, onError: (Object error) {
print(error.toString()); print(error.toString());
unterminatedString = '';
_setIsConnected(false); _setIsConnected(false);
}, onDone: () { }, onDone: () {
unterminatedString = '';
_setIsConnected(false); _setIsConnected(false);
}); });
keepAlive(); keepAlive();
} }
void _parseResponse(String message) {
try {
final response = json.decode(message) as Map<String, dynamic>;
_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<String, dynamic>;
_handleResponse(response);
unterminatedString = '';
}
} on TypeError catch (e) {
if (!e.toString().contains('Map<String, Object>') && !e.toString().contains('Map<String, dynamic>')) {
return;
}
unterminatedString += message;
if (isJSONStringCorrect(unterminatedString)) {
final response =
json.decode(unterminatedString) as Map<String, dynamic>;
_handleResponse(response);
// unterminatedString = null;
unterminatedString = '';
}
} catch (e) {
print(e.toString());
}
}
void keepAlive() { void keepAlive() {
_aliveTimer?.cancel(); _aliveTimer?.cancel();
_aliveTimer = Timer.periodic(aliveTimerDuration, (_) async => ping()); _aliveTimer = Timer.periodic(aliveTimerDuration, (_) async => ping());
@ -217,7 +231,7 @@ class ElectrumClient {
Future<Map<String, dynamic>> getTransactionRaw( Future<Map<String, dynamic>> getTransactionRaw(
{required String hash}) async => {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) { .then((dynamic result) {
if (result is Map<String, dynamic>) { if (result is Map<String, dynamic>) {
return result; return result;
@ -228,7 +242,7 @@ class ElectrumClient {
Future<String> getTransactionHex( Future<String> getTransactionHex(
{required String hash}) async => {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) { .then((dynamic result) {
if (result is String) { if (result is String) {
return result; return result;

View file

@ -9,7 +9,7 @@ import 'package:cw_bitcoin/electrum_transaction_info.dart';
part 'electrum_transaction_history.g.dart'; part 'electrum_transaction_history.g.dart';
const _transactionsHistoryFileName = 'transactions.json'; const transactionsHistoryFileName = 'transactions.json';
class ElectrumTransactionHistory = ElectrumTransactionHistoryBase class ElectrumTransactionHistory = ElectrumTransactionHistoryBase
with _$ElectrumTransactionHistory; with _$ElectrumTransactionHistory;
@ -42,7 +42,7 @@ abstract class ElectrumTransactionHistoryBase
try { try {
final dirPath = final dirPath =
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$_transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final data = final data =
json.encode({'height': _height, 'transactions': transactions}); json.encode({'height': _height, 'transactions': transactions});
await writeData(path: path, password: _password, data: data); await writeData(path: path, password: _password, data: data);
@ -59,7 +59,7 @@ abstract class ElectrumTransactionHistoryBase
Future<Map<String, dynamic>> _read() async { Future<Map<String, dynamic>> _read() async {
final dirPath = final dirPath =
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type); await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);
final path = '$dirPath/$_transactionsHistoryFileName'; final path = '$dirPath/$transactionsHistoryFileName';
final content = await read(path: path, password: _password); final content = await read(path: path, password: _password);
return json.decode(content) as Map<String, dynamic>; return json.decode(content) as Map<String, dynamic>;
} }
@ -86,4 +86,5 @@ abstract class ElectrumTransactionHistoryBase
void _update(ElectrumTransactionInfo transaction) => void _update(ElectrumTransactionInfo transaction) =>
transactions[transaction.id] = transaction; transactions[transaction.id] = transaction;
} }

View file

@ -1,5 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/pending_transaction.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
@ -437,6 +438,28 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
await transactionHistory.save(); await transactionHistory.save();
} }
Future<void> 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 @override
Future<void> changePassword(String password) async { Future<void> changePassword(String password) async {
_password = password; _password = password;

View file

@ -54,9 +54,32 @@ class LitecoinWalletService extends WalletService<
} }
@override @override
Future<void> remove(String wallet) async => Future<void> remove(String wallet) async {
File(await pathForWalletDir(name: wallet, type: getType())) File(await pathForWalletDir(name: wallet, type: getType()))
.delete(recursive: true); .delete(recursive: true);
final walletInfo = walletInfoSource.values.firstWhereOrNull(
(info) => info.id == WalletBase.idFor(wallet, getType()))!;
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> 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 @override
Future<LitecoinWallet> restoreFromKeys( Future<LitecoinWallet> restoreFromKeys(

View file

@ -67,6 +67,29 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
CryptoCurrency.uni, CryptoCurrency.uni,
CryptoCurrency.stx, CryptoCurrency.stx,
CryptoCurrency.btcln, 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 = [ static const havenCurrencies = [
@ -121,7 +144,7 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const ape = CryptoCurrency(title: 'APE', tag: 'ETH', fullName: 'ApeCoin', raw: 30, name: 'ape', iconPath: 'assets/images/ape_icon.png'); 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 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 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 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 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'); static const usdttrc20 = CryptoCurrency(title: 'USDT', tag: 'TRX', fullName: 'USDT Tether', raw: 36, name: 'usdttrc20', iconPath: 'assets/images/usdttrc20_icon.png');
@ -129,9 +152,9 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const sc = CryptoCurrency(title: 'SC', fullName: 'Siacoin', raw: 38, name: 'sc', iconPath: 'assets/images/sc_icon.png'); 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 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 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 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', iconPath: 'assets/images/zaddr_icon.png', raw: 42, name: 'zaddr'); 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', iconPath: 'assets/images/zec_icon.png', raw: 43, name: 'zec'); 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 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'); static const xvg = CryptoCurrency(title: 'XVG', fullName: 'Verge', raw: 45, name: 'xvg', iconPath: 'assets/images/xvg_icon.png');
@ -152,6 +175,29 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> implemen
static const uni = CryptoCurrency(title: 'UNI', tag: 'ETH', fullName: 'Uniswap', raw: 60, name: 'uni', iconPath: 'assets/images/uni_icon.png'); 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 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 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 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<int, CryptoCurrency> _rawCurrencyMap = static final Map<int, CryptoCurrency> _rawCurrencyMap =

View file

@ -14,6 +14,8 @@ abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
void addMany(Map<String, TransactionType> transactions); void addMany(Map<String, TransactionType> transactions);
void clear() => transactions.clear();
// bool _isUpdating; // bool _isUpdating;
// @action // @action

View file

@ -18,4 +18,6 @@ abstract class WalletService<N extends WalletCredentials,
Future<bool> isWalletExit(String name); Future<bool> isWalletExit(String name);
Future<void> remove(String wallet); Future<void> remove(String wallet);
Future<void> rename(String name, String password, String newName);
} }

View file

@ -1,5 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_haven/haven_transaction_creation_credentials.dart'; import 'package:cw_haven/haven_transaction_creation_credentials.dart';
import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_core/monero_amount_format.dart';
@ -257,6 +259,29 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
await haven_wallet.store(); await haven_wallet.store();
} }
Future<void> 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 @override
Future<void> changePassword(String password) async { Future<void> changePassword(String password) async {
haven_wallet.setPasswordSync(password); haven_wallet.setPasswordSync(password);

View file

@ -150,6 +150,26 @@ class HavenWalletService extends WalletService<
if (isExist) { if (isExist) {
await file.delete(recursive: true); await file.delete(recursive: true);
} }
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> 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 @override

View file

@ -24,4 +24,5 @@ abstract class MoneroTransactionHistoryBase
@override @override
void addMany(Map<String, MoneroTransactionInfo> transactions) => void addMany(Map<String, MoneroTransactionInfo> transactions) =>
this.transactions.addAll(transactions); this.transactions.addAll(transactions);
} }

View file

@ -1,4 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
import 'package:cw_core/monero_amount_format.dart'; import 'package:cw_core/monero_amount_format.dart';
import 'package:cw_monero/monero_transaction_creation_exception.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_monero/monero_wallet_addresses.dart';
import 'package:cw_core/monero_wallet_utils.dart'; import 'package:cw_core/monero_wallet_utils.dart';
import 'package:cw_monero/api/structs/pending_transaction.dart'; import 'package:cw_monero/api/structs/pending_transaction.dart';
import 'package:flutter/foundation.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cw_monero/api/transaction_history.dart' import 'package:cw_monero/api/transaction_history.dart'
as monero_transaction_history; as monero_transaction_history;
@ -309,6 +310,29 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
await monero_wallet.store(); await monero_wallet.store();
} }
Future<void> 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 @override
Future<void> changePassword(String password) async { Future<void> changePassword(String password) async {
monero_wallet.setPasswordSync(password); monero_wallet.setPasswordSync(password);

View file

@ -138,6 +138,26 @@ class MoneroWalletService extends WalletService<MoneroNewWalletCredentials,
if (isExist) { if (isExist) {
await file.delete(recursive: true); await file.delete(recursive: true);
} }
final walletInfo = walletInfoSource.values
.firstWhere((info) => info.id == WalletBase.idFor(wallet, getType()));
await walletInfoSource.delete(walletInfo.key);
}
@override
Future<void> 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 @override

View file

@ -36,6 +36,26 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.oxt: case CryptoCurrency.oxt:
case CryptoCurrency.paxg: case CryptoCurrency.paxg:
case CryptoCurrency.uni: 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:
return '0x[0-9a-zA-Z]'; return '0x[0-9a-zA-Z]';
case CryptoCurrency.xrp: case CryptoCurrency.xrp:
return '^[0-9a-zA-Z]{34}\$|^X[0-9a-zA-Z]{46}\$'; return '^[0-9a-zA-Z]{34}\$|^X[0-9a-zA-Z]{46}\$';
@ -101,8 +121,6 @@ class AddressValidator extends TextValidator {
return null; return null;
case CryptoCurrency.ada: case CryptoCurrency.ada:
return null; return null;
case CryptoCurrency.ape:
return [42];
case CryptoCurrency.avaxc: case CryptoCurrency.avaxc:
return [42]; return [42];
case CryptoCurrency.bch: case CryptoCurrency.bch:
@ -111,13 +129,43 @@ class AddressValidator extends TextValidator {
return [42]; return [42];
case CryptoCurrency.btc: case CryptoCurrency.btc:
return null; return null;
case CryptoCurrency.dai:
return [42];
case CryptoCurrency.dash: case CryptoCurrency.dash:
return [34]; return [34];
case CryptoCurrency.eos: case CryptoCurrency.eos:
return [42]; return [42];
case CryptoCurrency.eth: 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:
return [42]; return [42];
case CryptoCurrency.ltc: case CryptoCurrency.ltc:
return [34, 43, 63]; return [34, 43, 63];
@ -129,14 +177,10 @@ class AddressValidator extends TextValidator {
return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]; return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44];
case CryptoCurrency.trx: case CryptoCurrency.trx:
return [34]; return [34];
case CryptoCurrency.usdc:
return [42];
case CryptoCurrency.usdcsol: case CryptoCurrency.usdcsol:
return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]; return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44];
case CryptoCurrency.usdt: case CryptoCurrency.usdt:
return [34]; return [34];
case CryptoCurrency.usdterc20:
return [42];
case CryptoCurrency.usdttrc20: case CryptoCurrency.usdttrc20:
return [34]; return [34];
case CryptoCurrency.xlm: case CryptoCurrency.xlm:
@ -184,15 +228,6 @@ class AddressValidator extends TextValidator {
return [35]; return [35];
case CryptoCurrency.stx: case CryptoCurrency.stx:
return [40, 41, 42]; 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: case CryptoCurrency.rune:
return [43]; return [43];
case CryptoCurrency.scrt: case CryptoCurrency.scrt:

View file

@ -21,4 +21,11 @@ class KeyService {
await _secureStorage.write(key: key, value: encodedPassword); await _secureStorage.write(key: key, value: encodedPassword);
} }
Future<void> deleteWalletPassword({required String walletName}) async {
final key = generateStoreKeyFor(
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
await _secureStorage.delete(key: key);
}
} }

View file

@ -7,43 +7,58 @@ import 'package:cw_core/wallet_type.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class WalletLoadingService { class WalletLoadingService {
WalletLoadingService( WalletLoadingService(
this.sharedPreferences, this.sharedPreferences, this.keyService, this.walletServiceFactory);
this.keyService,
this.walletServiceFactory);
final SharedPreferences sharedPreferences; final SharedPreferences sharedPreferences;
final KeyService keyService; final KeyService keyService;
final WalletService Function(WalletType type) walletServiceFactory; final WalletService Function(WalletType type) walletServiceFactory;
Future<WalletBase> load(WalletType type, String name) async { Future<void> renameWallet(
final walletService = walletServiceFactory.call(type); WalletType type, String name, String newName) async {
final password = await keyService.getWalletPassword(walletName: name); final walletService = walletServiceFactory.call(type);
final wallet = await walletService.openWallet(name, password); final password = await keyService.getWalletPassword(walletName: name);
if (type == WalletType.monero) { // Save the current wallet's password to the new wallet name's key
await updateMoneroWalletPassword(wallet); 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);
return wallet; await walletService.rename(name, password, newName);
} }
Future<void> updateMoneroWalletPassword(WalletBase wallet) async { Future<WalletBase> load(WalletType type, String name) async {
final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name); final walletService = walletServiceFactory.call(type);
var isPasswordUpdated = sharedPreferences.getBool(key) ?? false; final password = await keyService.getWalletPassword(walletName: name);
final wallet = await walletService.openWallet(name, password);
if (isPasswordUpdated) { if (type == WalletType.monero) {
return; await updateMoneroWalletPassword(wallet);
} }
final password = generateWalletPassword(); return wallet;
// 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__#'; Future<void> updateMoneroWalletPassword(WalletBase wallet) async {
await keyService.saveWalletPassword(walletName: bakWalletName, password: password); final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name);
await wallet.changePassword(password); var isPasswordUpdated = sharedPreferences.getBool(key) ?? false;
await keyService.saveWalletPassword(walletName: wallet.name, password: password);
isPasswordUpdated = true; if (isPasswordUpdated) {
await sharedPreferences.setBool(key, 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);
}
} }

View file

@ -32,6 +32,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_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.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/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/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
@ -69,6 +70,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/settings/security_settings_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_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_address_list/wallet_address_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/unspent_coins_info.dart';
import 'package:cake_wallet/core/backup_service.dart'; import 'package:cake_wallet/core/backup_service.dart';
import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_service.dart';
@ -585,6 +588,21 @@ Future setup({
authService: getIt.get<AuthService>(), authService: getIt.get<AuthService>(),
)); ));
getIt.registerFactoryParam<WalletEditViewModel, WalletListViewModel, void>(
(WalletListViewModel walletListViewModel, _) => WalletEditViewModel(
walletListViewModel, getIt.get<WalletLoadingService>()));
getIt.registerFactoryParam<WalletEditPage, List<dynamic>, void>((args, _) {
final walletListViewModel = args.first as WalletListViewModel;
final editingWallet = args.last as WalletListItem;
return WalletEditPage(
walletEditViewModel: getIt.get<WalletEditViewModel>(param1: walletListViewModel),
authService: getIt.get<AuthService>(),
walletNewVM: getIt.get<WalletNewVM>(param1: editingWallet.type),
editingWallet: editingWallet);
});
getIt.registerFactory(() { getIt.registerFactory(() {
final wallet = getIt.get<AppStore>().wallet!; final wallet = getIt.get<AppStore>().wallet!;

View file

@ -202,12 +202,7 @@ class App extends StatefulWidget {
} }
class AppState extends State<App> with SingleTickerProviderStateMixin { class AppState extends State<App> with SingleTickerProviderStateMixin {
AppState() : yatStore = getIt.get<YatStore>() { AppState() : yatStore = getIt.get<YatStore>();
SystemChrome.setPreferredOrientations(
// ResponsiveLayoutUtil.instance.isIpad ?
// [DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight] :
[DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
}
YatStore yatStore; YatStore yatStore;
StreamSubscription? stream; StreamSubscription? stream;
@ -302,7 +297,43 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
locale: Locale(settingsStore.languageCode), locale: Locale(settingsStore.languageCode),
onGenerateRoute: (settings) => Router.createRoute(settings), onGenerateRoute: (settings) => Router.createRoute(settings),
initialRoute: initialRoute, 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();
}
}

View file

@ -45,6 +45,7 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:cake_wallet/view_model/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/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/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:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -63,6 +64,7 @@ import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
import 'package:cake_wallet/src/screens/receive/receive_page.dart'; import 'package:cake_wallet/src/screens/receive/receive_page.dart';
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_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_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/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/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart'; import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
@ -263,6 +265,12 @@ Route<dynamic> createRoute(RouteSettings settings) {
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true, builder: (_) => getIt.get<WalletListPage>()); fullscreenDialog: true, builder: (_) => getIt.get<WalletListPage>());
case Routes.walletEdit:
return MaterialPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<WalletEditPage>(
param1: settings.arguments as List<dynamic>));
case Routes.auth: case Routes.auth:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
fullscreenDialog: true, fullscreenDialog: true,

View file

@ -11,6 +11,7 @@ class Routes {
static const transactionDetails = '/transaction_info'; static const transactionDetails = '/transaction_info';
static const receive = '/receive'; static const receive = '/receive';
static const newSubaddress = '/new_subaddress'; static const newSubaddress = '/new_subaddress';
static const walletEdit = '/walletEdit';
static const disclaimer = '/disclaimer'; static const disclaimer = '/disclaimer';
static const readDisclaimer = '/read_disclaimer'; static const readDisclaimer = '/read_disclaimer';
static const seedLanguage = '/seed_language'; static const seedLanguage = '/seed_language';

View file

@ -5,6 +5,7 @@ import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/src/widgets/nav_bar.dart'; import 'package:cake_wallet/src/widgets/nav_bar.dart';
import 'package:cake_wallet/generated/i18n.dart';
enum AppBarStyle { regular, withShadow, transparent } enum AppBarStyle { regular, withShadow, transparent }
@ -58,7 +59,7 @@ abstract class BasePage extends StatelessWidget {
child: ButtonTheme( child: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: Semantics( child: Semantics(
label: 'Back', label: S.of(context).seed_alert_back,
child: TextButton( child: TextButton(
style: ButtonStyle( style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith( overlayColor: MaterialStateColor.resolveWith(

View file

@ -24,35 +24,40 @@ class ContactListPage extends BasePage {
@override @override
Widget? trailing(BuildContext context) { Widget? trailing(BuildContext context) {
return Container( return MergeSemantics(
width: 32.0, child: Container(
height: 32.0, width: 32.0,
decoration: BoxDecoration( height: 32.0,
shape: BoxShape.circle, decoration: BoxDecoration(
color: Theme.of(context) shape: BoxShape.circle,
color: Theme.of(context)
.accentTextTheme! .accentTextTheme!
.bodySmall! .bodySmall!
.color!), .color!),
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: <Widget>[ children: <Widget>[
Icon(Icons.add, Icon(Icons.add,
color: Theme.of(context).primaryTextTheme!.titleLarge!.color!, color: Theme.of(context).primaryTextTheme!.titleLarge!.color!,
size: 22.0), size: 22.0),
ButtonTheme( ButtonTheme(
minWidth: 32.0, minWidth: 32.0,
height: 32.0, height: 32.0,
child: TextButton( child: Semantics(
// FIX-ME: Style label: S.of(context).add,
//shape: CircleBorder(), child: TextButton(
onPressed: () async { // FIX-ME: Style
await Navigator.of(context) //shape: CircleBorder(),
.pushNamed(Routes.addressBookAddContact); onPressed: () async {
}, await Navigator.of(context)
child: Offstage()), .pushNamed(Routes.addressBookAddContact);
) },
], child: Offstage()),
)); ),
)
],
)),
);
} }
@override @override

View file

@ -4,6 +4,7 @@ import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/main_actions.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/desktop_widgets/desktop_sidebar_wrapper.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/market_place_page.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/utils/version_comparator.dart';
import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/market_place_view_model.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
@ -42,15 +43,30 @@ class DashboardPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(body: LayoutBuilder(
body: ResponsiveLayoutUtil.instance.isMobile builder: (context, constraints) {
? _DashboardPageView( if (DeviceInfo.instance.isDesktop) {
if (constraints.maxWidth > ResponsiveLayoutUtil.kDesktopMaxDashBoardWidthConstraint) {
return getIt.get<DesktopSidebarWrapper>();
} else {
return _DashboardPageView(
balancePage: balancePage, balancePage: balancePage,
dashboardViewModel: dashboardViewModel, dashboardViewModel: dashboardViewModel,
addressListViewModel: addressListViewModel, addressListViewModel: addressListViewModel,
) );
: getIt.get<DesktopSidebarWrapper>(), }
); } else if (ResponsiveLayoutUtil.instance.shouldRenderMobileUI()) {
return _DashboardPageView(
balancePage: balancePage,
dashboardViewModel: dashboardViewModel,
addressListViewModel: addressListViewModel,
);
} else {
return getIt.get<DesktopSidebarWrapper>();
}
},
));
} }
} }
@ -111,7 +127,8 @@ class _DashboardPageView extends BasePage {
//splashColor: Colors.transparent, //splashColor: Colors.transparent,
//padding: EdgeInsets.all(0), //padding: EdgeInsets.all(0),
onPressed: () => onOpenEndDrawer(), onPressed: () => onOpenEndDrawer(),
child: Semantics(label: 'Menu', child: menuButton))); child: Semantics(
label: S.of(context).wallet_menu, child: menuButton)));
} }
final DashboardViewModel dashboardViewModel; final DashboardViewModel dashboardViewModel;
@ -248,7 +265,7 @@ class _DashboardPageView extends BasePage {
} }
if (dashboardViewModel.shouldShowMarketPlaceInDashboard) { if (dashboardViewModel.shouldShowMarketPlaceInDashboard) {
pages.add(Semantics( pages.add(Semantics(
label: 'Marketplace Page', label: S.of(context).market_place,
child: MarketPlacePage( child: MarketPlacePage(
dashboardViewModel: dashboardViewModel, dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(), marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
@ -256,9 +273,9 @@ class _DashboardPageView extends BasePage {
), ),
); );
} }
pages.add(Semantics(label: 'Balance Page', child: balancePage)); pages.add(Semantics(label: S.of(context).balance_page, child: balancePage));
pages.add(Semantics( pages.add(Semantics(
label: 'Transactions Page', label: S.of(context).settings_transactions,
child: TransactionsPage(dashboardViewModel: dashboardViewModel))); child: TransactionsPage(dashboardViewModel: dashboardViewModel)));
_isEffectsInstalled = true; _isEffectsInstalled = true;

View file

@ -14,72 +14,75 @@ class DesktopDashboardActions extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Observer( return Container(
builder: (_) { color: Theme.of(context).colorScheme.background,
return Column( child: Observer(
children: [ builder: (_) {
const SizedBox(height: 16), return Column(
DesktopActionButton( children: [
title: MainActions.exchangeAction.name(context), const SizedBox(height: 16),
image: MainActions.exchangeAction.image, DesktopActionButton(
canShow: MainActions.exchangeAction.canShow?.call(dashboardViewModel), title: MainActions.exchangeAction.name(context),
isEnabled: MainActions.exchangeAction.isEnabled?.call(dashboardViewModel), image: MainActions.exchangeAction.image,
onTap: () async => await MainActions.exchangeAction.onTap(context, dashboardViewModel), canShow: MainActions.exchangeAction.canShow?.call(dashboardViewModel),
), isEnabled: MainActions.exchangeAction.isEnabled?.call(dashboardViewModel),
Row( onTap: () async => await MainActions.exchangeAction.onTap(context, dashboardViewModel),
children: [
Expanded(
child: DesktopActionButton(
title: MainActions.receiveAction.name(context),
image: MainActions.receiveAction.image,
canShow: MainActions.receiveAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.receiveAction.isEnabled?.call(dashboardViewModel),
onTap: () async =>
await MainActions.receiveAction.onTap(context, dashboardViewModel),
),
),
Expanded(
child: DesktopActionButton(
title: MainActions.sendAction.name(context),
image: MainActions.sendAction.image,
canShow: MainActions.sendAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.sendAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.sendAction.onTap(context, dashboardViewModel),
),
),
],
),
Row(
children: [
Expanded(
child: DesktopActionButton(
title: MainActions.buyAction.name(context),
image: MainActions.buyAction.image,
canShow: MainActions.buyAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.buyAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.buyAction.onTap(context, dashboardViewModel),
),
),
Expanded(
child: DesktopActionButton(
title: MainActions.sellAction.name(context),
image: MainActions.sellAction.image,
canShow: MainActions.sellAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.sellAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.sellAction.onTap(context, dashboardViewModel),
),
),
],
),
Expanded(
child: MarketPlacePage(
dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
), ),
), Row(
], children: [
); Expanded(
} child: DesktopActionButton(
title: MainActions.receiveAction.name(context),
image: MainActions.receiveAction.image,
canShow: MainActions.receiveAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.receiveAction.isEnabled?.call(dashboardViewModel),
onTap: () async =>
await MainActions.receiveAction.onTap(context, dashboardViewModel),
),
),
Expanded(
child: DesktopActionButton(
title: MainActions.sendAction.name(context),
image: MainActions.sendAction.image,
canShow: MainActions.sendAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.sendAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.sendAction.onTap(context, dashboardViewModel),
),
),
],
),
Row(
children: [
Expanded(
child: DesktopActionButton(
title: MainActions.buyAction.name(context),
image: MainActions.buyAction.image,
canShow: MainActions.buyAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.buyAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.buyAction.onTap(context, dashboardViewModel),
),
),
Expanded(
child: DesktopActionButton(
title: MainActions.sellAction.name(context),
image: MainActions.sellAction.image,
canShow: MainActions.sellAction.canShow?.call(dashboardViewModel),
isEnabled: MainActions.sellAction.isEnabled?.call(dashboardViewModel),
onTap: () async => await MainActions.sellAction.onTap(context, dashboardViewModel),
),
),
],
),
Expanded(
child: MarketPlacePage(
dashboardViewModel: dashboardViewModel,
marketPlaceViewModel: getIt.get<MarketPlaceViewModel>(),
),
),
],
);
}
),
); );
} }
} }

View file

@ -77,27 +77,21 @@ class DesktopSidebarWrapper extends BasePage {
SideMenuItem( SideMenuItem(
imagePath: 'assets/images/wallet_outline.png', imagePath: 'assets/images/wallet_outline.png',
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.dashboard, isSelected: desktopSidebarViewModel.currentPage == SidebarItem.dashboard,
onTap: () => desktopSidebarViewModel.onPageChange(SidebarItem.dashboard), onTap: () {
desktopSidebarViewModel.onPageChange(SidebarItem.dashboard);
desktopNavigatorKey.currentState
?.pushNamedAndRemoveUntil(Routes.desktop_actions, (route) => false);
},
), ),
SideMenuItem( SideMenuItem(
onTap: () { onTap: () {
String? currentPath; if (desktopSidebarViewModel.currentPage == SidebarItem.transactions) {
desktopNavigatorKey.currentState
desktopNavigatorKey.currentState?.popUntil((route) { ?.pushNamedAndRemoveUntil(Routes.desktop_actions, (route) => false);
currentPath = route.settings.name; desktopSidebarViewModel.resetSidebar();
return true; } else {
}); desktopSidebarViewModel.onPageChange(SidebarItem.transactions);
desktopNavigatorKey.currentState?.pushNamed(Routes.transactionsPage);
switch (currentPath) {
case Routes.transactionsPage:
desktopSidebarViewModel.resetSidebar();
break;
default:
desktopSidebarViewModel.resetSidebar();
Future.delayed(Duration(milliseconds: 10), () {
desktopSidebarViewModel.onPageChange(SidebarItem.transactions);
desktopNavigatorKey.currentState?.pushNamed(Routes.transactionsPage);
});
} }
}, },
isSelected: desktopSidebarViewModel.currentPage == SidebarItem.transactions, isSelected: desktopSidebarViewModel.currentPage == SidebarItem.transactions,
@ -156,20 +150,11 @@ class DesktopSidebarWrapper extends BasePage {
void _setEffects() async { void _setEffects() async {
reaction<SidebarItem>((_) => desktopSidebarViewModel.currentPage, (page) { reaction<SidebarItem>((_) => desktopSidebarViewModel.currentPage, (page) {
String? currentPath; if (page == SidebarItem.dashboard) {
pageController.jumpToPage(0);
desktopNavigatorKey.currentState?.popUntil((route) {
currentPath = route.settings.name;
return true;
});
if (page == SidebarItem.transactions) {
return; return;
} }
pageController.jumpToPage(page.index - 1);
if (currentPath == Routes.transactionsPage) {
Navigator.of(desktopNavigatorKey.currentContext!).pop();
}
pageController.jumpToPage(page.index);
}); });
} }
} }

View file

@ -82,7 +82,9 @@ class AddressPage extends BasePage {
child: ButtonTheme( child: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: Semantics( child: Semantics(
label: !isMobileView ? 'Close' : 'Back', label: !isMobileView
? S.of(context).close
: S.of(context).seed_alert_back,
child: TextButton( child: TextButton(
style: ButtonStyle( style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith( overlayColor: MaterialStateColor.resolveWith(

View file

@ -1,6 +1,7 @@
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart'; import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
import 'package:cake_wallet/utils/feature_flag.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -16,9 +17,6 @@ class BalancePage extends StatelessWidget {
final DashboardViewModel dashboardViewModel; final DashboardViewModel dashboardViewModel;
final SettingsStore settingsStore; final SettingsStore settingsStore;
Color get backgroundLightColor =>
settingsStore.currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return GestureDetector( return GestureDetector(
@ -46,7 +44,7 @@ class BalancePage extends StatelessWidget {
textAlign: TextAlign.center); textAlign: TextAlign.center);
})), })),
Observer(builder: (_) { Observer(builder: (_) {
if (dashboardViewModel.balanceViewModel.isShowCard) { if (dashboardViewModel.balanceViewModel.isShowCard && FeatureFlag.isCakePayEnabled) {
return IntroducingCard( return IntroducingCard(
title: S.of(context).introducing_cake_pay, title: S.of(context).introducing_cake_pay,
subTitle: S.of(context).cake_pay_learn_more, subTitle: S.of(context).cake_pay_learn_more,

View file

@ -52,7 +52,10 @@ class MarketPlacePage extends StatelessWidget {
children: <Widget>[ children: <Widget>[
SizedBox(height: 20), SizedBox(height: 20),
DashBoardRoundedCardWidget( DashBoardRoundedCardWidget(
onTap: () => _navigatorToGiftCardsPage(context), onTap: () => launchUrl(
Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"),
mode: LaunchMode.externalApplication,
),
title: S.of(context).cake_pay_title, title: S.of(context).cake_pay_title,
subTitle: S.of(context).cake_pay_subtitle, subTitle: S.of(context).cake_pay_subtitle,
), ),
@ -75,6 +78,7 @@ class MarketPlacePage extends StatelessWidget {
); );
} }
// TODO: Remove ionia flow/files if we will discard it
void _navigatorToGiftCardsPage(BuildContext context) { void _navigatorToGiftCardsPage(BuildContext context) {
final walletType = dashboardViewModel.type; final walletType = dashboardViewModel.type;

View file

@ -138,7 +138,7 @@ class PresentReceiveOptionPicker extends StatelessWidget {
Container( Container(
margin: EdgeInsets.only(bottom: 40), margin: EdgeInsets.only(bottom: 40),
child: InkWell( child: InkWell(
onTap: () => Navigator.pop(context), onTap: () => Navigator.pop(popUpContext),
child: CircleAvatar( child: CircleAvatar(
child: Icon( child: Icon(
Icons.close, Icons.close,

View file

@ -126,7 +126,9 @@ class ExchangePage extends BasePage {
child: ButtonTheme( child: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: Semantics( child: Semantics(
label: !isMobileView ? 'Close' : 'Back', label: !isMobileView
? S.of(context).close
: S.of(context).seed_alert_back,
child: TextButton( child: TextButton(
style: ButtonStyle( style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith( overlayColor: MaterialStateColor.resolveWith(

View file

@ -66,40 +66,6 @@ class IoniaManageCardsPage extends BasePage {
@override @override
Widget get endDrawer => CardMenu(); Widget get endDrawer => CardMenu();
@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(
// FIX-ME: Style
//highlightColor: Colors.transparent,
//splashColor: Colors.transparent,
//padding: EdgeInsets.all(0),
onPressed: (){
if (searchFocusNode.hasFocus) {
searchFocusNode.unfocus();
return;
}
Navigator.of(context).pop();
},
child: _backButton),
),
);
}
@override @override
Widget middle(BuildContext context) { Widget middle(BuildContext context) {
return Text( return Text(
@ -123,26 +89,28 @@ class IoniaManageCardsPage extends BasePage {
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
final filterButton = InkWell( final filterButton = Semantics(
onTap: () async { label: S.of(context).filter_by,
await showCategoryFilter(context); child: InkWell(
_cardsListViewModel.getMerchants(); onTap: () async {
}, await showCategoryFilter(context);
child: Container( _cardsListViewModel.getMerchants();
width: 32, },
padding: EdgeInsets.all(8), child: Container(
decoration: BoxDecoration( width: 32,
color: Theme.of(context).textTheme!.titleLarge!.backgroundColor!, padding: EdgeInsets.all(8),
border: Border.all( decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2), color: Theme.of(context).textTheme!.titleLarge!.backgroundColor!,
border: Border.all(
color: Colors.white.withOpacity(0.2),
),
borderRadius: BorderRadius.circular(10),
), ),
borderRadius: BorderRadius.circular(10), child: Image.asset(
), 'assets/images/filter.png',
child: Image.asset( color: Theme.of(context).textTheme!.bodySmall!.decorationColor!,
'assets/images/filter.png', ),
color: Theme.of(context).textTheme!.bodySmall!.decorationColor!, )),
),
)
); );
return Padding( return Padding(
@ -281,11 +249,13 @@ class _SearchWidget extends StatelessWidget {
final FocusNode focusNode; final FocusNode focusNode;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final searchIcon = Padding( final searchIcon = ExcludeSemantics(
padding: EdgeInsets.all(8), child: Padding(
child: Image.asset( padding: EdgeInsets.all(8),
'assets/images/mini_search_icon.png', child: Image.asset(
color: Theme.of(context).textTheme!.bodySmall!.decorationColor!, 'assets/images/mini_search_icon.png',
color: Theme.of(context).textTheme!.bodySmall!.decorationColor!,
),
), ),
); );
@ -335,18 +305,22 @@ class _TrailingIcon extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Material( return Semantics(
color: Colors.transparent, label: S.of(context).profile,
child: IconButton( child: Material(
padding: EdgeInsets.zero, color: Colors.transparent,
constraints: BoxConstraints(), child: IconButton(
highlightColor: Colors.transparent, padding: EdgeInsets.zero,
splashColor: Colors.transparent, constraints: BoxConstraints(),
iconSize: 25, highlightColor: Colors.transparent,
onPressed: onPressed, splashColor: Colors.transparent,
icon: Image.asset( iconSize: 25,
asset, onPressed: onPressed,
color: Theme.of(context).accentTextTheme!.displayMedium!.backgroundColor!, icon: Image.asset(
asset,
color:
Theme.of(context).accentTextTheme!.displayMedium!.backgroundColor!,
),
), ),
), ),
); );

View file

@ -118,19 +118,21 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
textColor: Colors.white, textColor: Colors.white,
), ),
const SizedBox(height: 25), const SizedBox(height: 25),
Padding( LayoutBuilder(
padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.15), builder: (_, constraints) => SizedBox(
child: Text( width: constraints.maxWidth * 0.8,
S.of(context).settings_can_be_changed_later, child: Text(
textAlign: TextAlign.center, S.of(context).settings_can_be_changed_later,
style: TextStyle( textAlign: TextAlign.center,
color: Theme.of(context) style: TextStyle(
color: Theme.of(context)
.accentTextTheme! .accentTextTheme!
.displayMedium! .displayMedium!
.color, .color,
),
), ),
), ),
), )
], ],
), ),
), ),

View file

@ -146,7 +146,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
width: 1.0), width: 1.0),
), ),
suffixIcon: Semantics( suffixIcon: Semantics(
label: 'Generate Name', label: S.of(context).generate_name,
child: IconButton( child: IconButton(
onPressed: () async { onPressed: () async {
final rName = await generateName(); final rName = await generateName();

View file

@ -1,6 +1,8 @@
import 'package:cake_wallet/core/execution_state.dart'; 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/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_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:cake_wallet/utils/show_pop_up.dart';
import 'package:cw_core/node.dart'; import 'package:cw_core/node.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -115,23 +117,42 @@ class NodeCreateOrEditPage extends BasePage {
bottomSectionPadding: EdgeInsets.only(bottom: 24), bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer( bottomSection: Observer(
builder: (_) => Row( builder: (_) => Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Flexible( Flexible(
child: Container( child: Container(
padding: EdgeInsets.only(right: 8.0), padding: EdgeInsets.only(right: 8.0),
child: LoadingPrimaryButton( child: LoadingPrimaryButton(
onPressed: () async { onPressed: () async {
if (_formKey.currentState != null && !_formKey.currentState!.validate()) { final confirmed = await showPopUp<bool>(
return; 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 text: S.of(context).delete,
.connectionState is IsExecutingState, isDisabled: !nodeCreateOrEditViewModel.isReady ||
text: S.of(context).node_test, (isSelected ?? false),
isDisabled: !nodeCreateOrEditViewModel.isReady, color: Palette.red,
color: Colors.orange,
textColor: Colors.white), textColor: Colors.white),
)), )),
Flexible( Flexible(

View file

@ -9,8 +9,8 @@ class NodeIndicator extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
width: 8.0, width: 12.0,
height: 8.0, height: 12.0,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, color: isLive ? Palette.green : Palette.red), shape: BoxShape.circle, color: isLive ? Palette.green : Palette.red),
); );

View file

@ -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/screens/nodes/widgets/node_indicator.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cw_core/node.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class NodeListRow extends StandardListRow { class NodeListRow extends StandardListRow {
NodeListRow( NodeListRow(
{required String title, {required String title,
required this.node,
required void Function(BuildContext context) onTap, required void Function(BuildContext context) onTap,
required bool isSelected, required bool isSelected})
required this.isAlive})
: super(title: title, onTap: onTap, isSelected: isSelected); : super(title: title, onTap: onTap, isSelected: isSelected);
final Future<bool> isAlive; final Node node;
@override @override
Widget buildTrailing(BuildContext context) { Widget buildLeading(BuildContext context) {
return FutureBuilder( return FutureBuilder(
future: isAlive, future: node.requestNode(),
builder: (context, snapshot) { builder: (context, snapshot) {
switch (snapshot.connectionState) { switch (snapshot.connectionState) {
case ConnectionState.done: 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 { class NodeHeaderListRow extends StandardListRow {

View file

@ -226,7 +226,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
child: Container( child: Container(
margin: EdgeInsets.only(left: marginLeft, right: marginRight), margin: EdgeInsets.only(left: marginLeft, right: marginRight),
child: Semantics( child: Semantics(
label: 'Delete', label: S.of(context).delete,
button: true, button: true,
onTap: () => _pop(), onTap: () => _pop(),
child: TextButton( child: TextButton(

View file

@ -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/screens/receive/widgets/anonpay_input_form.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.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/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/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/anon_invoice_page_view_model.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'; 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; AppBarStyle get appBarStyle => AppBarStyle.transparent;
@override @override
void onClose(BuildContext context) { void onClose(BuildContext context) => Navigator.popUntil(context, (route) => route.isFirst);
Navigator.popUntil(context, ModalRoute.withName(Routes.dashboard));
}
@override @override
Widget middle(BuildContext context) => Widget middle(BuildContext context) =>

View file

@ -32,28 +32,7 @@ class AnonPayReceivePage extends BasePage {
bool get resizeToAvoidBottomInset => false; bool get resizeToAvoidBottomInset => false;
@override @override
Widget leading(BuildContext context) { void onClose(BuildContext context) => Navigator.popUntil(context, (route) => route.isFirst);
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),
),
);
}
@override @override
Widget middle(BuildContext context) { Widget middle(BuildContext context) {

View file

@ -87,7 +87,7 @@ class ReceivePage extends BasePage {
return Material( return Material(
color: Colors.transparent, color: Colors.transparent,
child: Semantics( child: Semantics(
label: 'Share', label: S.of(context).share,
child: IconButton( child: IconButton(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
constraints: BoxConstraints(), constraints: BoxConstraints(),

View file

@ -71,7 +71,7 @@ class AddressCell extends StatelessWidget {
), ),
)); ));
return Semantics( return Semantics(
label: 'Slidable', label: S.of(context).slidable,
selected: isCurrent, selected: isCurrent,
enabled: !isCurrent, enabled: !isCurrent,
child: Slidable( child: Slidable(

View file

@ -2,7 +2,6 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart';
@ -37,46 +36,33 @@ class PreSeedPage extends BasePage {
alignment: Alignment.center, alignment: Alignment.center,
padding: EdgeInsets.all(24), padding: EdgeInsets.all(24),
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
child: Column( child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Flexible( children: <Widget>[
flex: 2, ConstrainedBox(
child: AspectRatio( constraints: BoxConstraints(
aspectRatio: 1, maxHeight: MediaQuery.of(context).size.height * 0.3
child: FittedBox(child: image, fit: BoxFit.contain))), ),
Flexible( child: AspectRatio(aspectRatio: 1, child: image),
flex: 3, ),
child: Column( Padding(
mainAxisAlignment: MainAxisAlignment.spaceBetween, padding: EdgeInsets.all(10),
children: [ child: Text(
Padding( S.of(context).pre_seed_description(wordsCount.toString()),
padding: EdgeInsets.only(top: 70, left: 16, right: 16), textAlign: TextAlign.center,
child: Text( style: TextStyle(
S fontSize: 14,
.of(context) fontWeight: FontWeight.normal,
.pre_seed_description(wordsCount.toString()), color: Theme.of(context).primaryTextTheme.bodySmall!.color!),
textAlign: TextAlign.center, ),
style: TextStyle( ),
fontSize: 14, PrimaryButton(
fontWeight: FontWeight.normal, onPressed: () =>
color: Theme.of(context) Navigator.of(context).popAndPushNamed(Routes.seed, arguments: true),
.primaryTextTheme! text: S.of(context).pre_seed_button_text,
.bodySmall! color: Theme.of(context).accentTextTheme!.bodyLarge!.color!,
.color!), textColor: Colors.white)
),
),
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)
],
))
], ],
), ),
), ),

View file

@ -52,8 +52,7 @@ class WalletSeedPage extends BasePage {
} }
@override @override
Widget? leading(BuildContext context) => Widget? leading(BuildContext context) => isNewWalletCreated ? null : super.leading(context);
isNewWalletCreated ? null: super.leading(context);
@override @override
Widget trailing(BuildContext context) { Widget trailing(BuildContext context) {
@ -67,16 +66,11 @@ class WalletSeedPage extends BasePage {
margin: EdgeInsets.only(left: 10), margin: EdgeInsets.only(left: 10),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)), borderRadius: BorderRadius.all(Radius.circular(16)),
color: Theme.of(context) color: Theme.of(context).accentTextTheme.bodySmall!.color!),
.accentTextTheme!
.bodySmall!
.color!),
child: Text( child: Text(
S.of(context).seed_language_next, S.of(context).seed_language_next,
style: TextStyle( style: TextStyle(
fontSize: 14, fontSize: 14, fontWeight: FontWeight.w600, color: Palette.blueCraiola),
fontWeight: FontWeight.w600,
color: Palette.blueCraiola),
), ),
), ),
) )
@ -87,121 +81,99 @@ class WalletSeedPage extends BasePage {
Widget body(BuildContext context) { Widget body(BuildContext context) {
final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight; final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight;
return WillPopScope(onWillPop: () async => false, child: Container( return WillPopScope(
padding: EdgeInsets.all(24), onWillPop: () async => false,
alignment: Alignment.center, child: Container(
child: ConstrainedBox( padding: EdgeInsets.all(24),
constraints: alignment: Alignment.center,
BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), child: ConstrainedBox(
child: Column( constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
children: <Widget>[ child: Column(
Flexible( mainAxisAlignment: MainAxisAlignment.spaceBetween,
flex: 2, children: <Widget>[
child: AspectRatio( ConstrainedBox(
aspectRatio: 1, constraints:
child: FittedBox(child: image, fit: BoxFit.fill))), BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.3),
Flexible( child: AspectRatio(aspectRatio: 1, child: image),
flex: 3, ),
child: Column( Observer(builder: (_) {
mainAxisAlignment: MainAxisAlignment.spaceBetween, return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Padding( Text(
padding: EdgeInsets.only(top: 33), walletSeedViewModel.name,
child: Observer(builder: (_) { style: TextStyle(
return Column( fontSize: 20,
crossAxisAlignment: CrossAxisAlignment.center, fontWeight: FontWeight.w600,
children: <Widget>[ color: Theme.of(context).primaryTextTheme.titleLarge!.color!),
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!),
),
)
],
);
}),
), ),
Column( Padding(
children: <Widget>[ padding: EdgeInsets.only(top: 20, left: 16, right: 16),
isNewWalletCreated child: Text(
? Padding( walletSeedViewModel.seed,
padding: EdgeInsets.only( textAlign: TextAlign.center,
bottom: 52, left: 43, right: 43), style: TextStyle(
child: Text( fontSize: 14,
S.of(context).seed_reminder, fontWeight: FontWeight.normal,
textAlign: TextAlign.center, color: Theme.of(context).primaryTextTheme.bodySmall!.color!),
style: TextStyle( ),
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.primaryTextTheme!
.labelSmall!
.color!),
),
)
: Offstage(),
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
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<void>(context,
S.of(context).copied_to_clipboard);
},
text: S.of(context).copy,
color: Theme.of(context)
.accentTextTheme!
.bodyMedium!
.color!,
textColor: Colors.white)),
))
],
)
],
) )
], ],
)) );
], }),
Column(
children: <Widget>[
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: <Widget>[
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<void>(context, S.of(context).copied_to_clipboard);
},
text: S.of(context).copy,
color: Theme.of(context).accentTextTheme.bodyMedium!.color!,
textColor: Colors.white)),
))
],
)
],
)
],
),
), ),
))); ));
} }
} }

View file

@ -4,16 +4,16 @@ import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
List<Image> flagImages = [ List<Image> flagImages = [
Image.asset('assets/images/usa.png'), Image.asset('assets/images/flags/usa.png'),
Image.asset('assets/images/china.png'), Image.asset('assets/images/flags/chn.png'),
Image.asset('assets/images/holland.png'), Image.asset('assets/images/flags/nld.png'),
Image.asset('assets/images/germany.png'), Image.asset('assets/images/flags/deu.png'),
Image.asset('assets/images/japan.png'), Image.asset('assets/images/flags/jpn.png'),
Image.asset('assets/images/portugal.png'), Image.asset('assets/images/flags/prt.png'),
Image.asset('assets/images/russia.png'), Image.asset('assets/images/flags/rus.png'),
Image.asset('assets/images/spain.png'), Image.asset('assets/images/flags/esp.png'),
Image.asset('assets/images/france.png'), Image.asset('assets/images/flags/fra.png'),
Image.asset('assets/images/italy.png'), Image.asset('assets/images/flags/ita.png'),
]; ];
const List<String> languageCodes = [ const List<String> languageCodes = [

View file

@ -71,7 +71,9 @@ class SendPage extends BasePage {
child: ButtonTheme( child: ButtonTheme(
minWidth: double.minPositive, minWidth: double.minPositive,
child: Semantics( child: Semantics(
label: !isMobileView ? 'Close' : 'Back', label: !isMobileView
? S.of(context).close
: S.of(context).seed_alert_back,
child: TextButton( child: TextButton(
style: ButtonStyle( style: ButtonStyle(
overlayColor: MaterialStateColor.resolveWith( overlayColor: MaterialStateColor.resolveWith(

View file

@ -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/standard_list.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.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:cake_wallet/view_model/node_list/node_list_view_model.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
class ConnectionSyncPage extends BasePage { class ConnectionSyncPage extends BasePage {
ConnectionSyncPage(this.nodeListViewModel, this.dashboardViewModel); ConnectionSyncPage(this.nodeListViewModel, this.dashboardViewModel);
@ -64,49 +63,37 @@ class ConnectionSyncPage extends BasePage {
itemBuilder: (_, sectionIndex, index) { itemBuilder: (_, sectionIndex, index) {
final node = nodeListViewModel.nodes[index]; final node = nodeListViewModel.nodes[index];
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex; final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
final nodeListRow = Semantics( final nodeListRow = NodeListRow(
label: 'Slidable', title: node.uriRaw,
selected: isSelected, node: node,
enabled: !isSelected, isSelected: isSelected,
child: NodeListRow( onTap: (_) async {
title: node.uriRaw, if (isSelected) {
isSelected: isSelected, return;
isAlive: node.requestNode(), }
onTap: (_) async {
if (isSelected) {
return;
}
await showPopUp<void>( await showPopUp<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertWithTwoActions( return AlertWithTwoActions(
alertTitle: alertTitle:
S.of(context).change_current_node_title, S.of(context).change_current_node_title,
alertContent: nodeListViewModel alertContent: nodeListViewModel
.getAlertContent(node.uriRaw), .getAlertContent(node.uriRaw),
leftButtonText: S.of(context).cancel, leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change, rightButtonText: S.of(context).change,
actionLeftButton: () => actionLeftButton: () =>
Navigator.of(context).pop(), Navigator.of(context).pop(),
actionRightButton: () async { actionRightButton: () async {
await nodeListViewModel.setAsCurrent(node); await nodeListViewModel.setAsCurrent(node);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
); );
}); });
}, },
),
); );
final dismissibleRow = Slidable( return nodeListRow;
key: Key('${node.keyIndex}'),
startActionPane: _actionPane(context, node, isSelected),
endActionPane: _actionPane(context, node, isSelected),
child: nodeListRow,
);
return dismissibleRow;
}, },
), ),
); );
@ -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<bool>(
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,
),
],
);
} }

View file

@ -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<FormState>(),
_labelController = TextEditingController(),
super() {
_labelController.text = editingWallet.name;
_labelController.addListener(() => walletEditViewModel.newName = _labelController.text);
}
final GlobalKey<FormState> _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<void>? _progressBar;
@override
Widget body(BuildContext context) {
return Form(
key: _formKey,
child: Container(
padding: EdgeInsets.all(24.0),
child: Column(
children: <Widget>[
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: <Widget>[
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<void>(
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<void> _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<void>(
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<void>(text, duration: null)..show(context);
}
Future<void> hideProgressText() async {
await Future.delayed(Duration(milliseconds: 50), () {
_progressBar?.dismiss();
_progressBar = null;
});
}
}

View file

@ -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:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:another_flushbar/flushbar.dart'; import 'package:another_flushbar/flushbar.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.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/widgets/primary_button.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.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'; import 'package:cake_wallet/wallet_type_utils.dart';
class WalletListPage extends BasePage { class WalletListPage extends BasePage {
@ -77,31 +75,7 @@ class WalletListBodyState extends State<WalletListBody> {
? Theme.of(context).accentTextTheme!.titleSmall!.decorationColor! ? Theme.of(context).accentTextTheme!.titleSmall!.decorationColor!
: Theme.of(context).colorScheme.background; : Theme.of(context).colorScheme.background;
final row = GestureDetector( final row = GestureDetector(
onTap: () async { onTap: () => wallet.isCurrent ? null : _loadWallet(wallet),
if (wallet.isCurrent || !wallet.isEnabled) {
return;
}
final confirmed = await showPopUp<bool>(
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);
}
},
child: Container( child: Container(
height: tileHeight, height: tileHeight,
width: double.infinity, width: double.infinity,
@ -129,16 +103,21 @@ class WalletListBodyState extends State<WalletListBody> {
? _imageFor(type: wallet.type) ? _imageFor(type: wallet.type)
: nonWalletTypeIcon, : nonWalletTypeIcon,
SizedBox(width: 10), SizedBox(width: 10),
Text( Flexible(
wallet.name, child: Text(
style: TextStyle( wallet.name,
maxLines: null,
softWrap: true,
style: TextStyle(
fontSize: 22, fontSize: 22,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme .primaryTextTheme
.titleLarge! .titleLarge!
.color!), .color!,
) ),
),
),
], ],
), ),
), ),
@ -149,12 +128,38 @@ class WalletListBodyState extends State<WalletListBody> {
return wallet.isCurrent return wallet.isCurrent
? row ? row
: Slidable( : Row(children: [
key: Key('${wallet.key}'), Expanded(child: row),
startActionPane: _actionPane(wallet), GestureDetector(
endActionPane: _actionPane(wallet), onTap: () => Navigator.of(context).pushNamed(
child: row, 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!,
),
),
),
),
)
]);
}), }),
), ),
), ),
@ -215,7 +220,7 @@ class WalletListBodyState extends State<WalletListBody> {
await hideProgressText(); await hideProgressText();
// only pop the wallets route in mobile as it will go back to dashboard page // only pop the wallets route in mobile as it will go back to dashboard page
// in desktop platforms the navigation tree is different // in desktop platforms the navigation tree is different
if (ResponsiveLayoutUtil.instance.isMobile) { if (ResponsiveLayoutUtil.instance.shouldRenderMobileUI()) {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.of(context).pop(); Navigator.of(context).pop();
}); });
@ -226,47 +231,6 @@ class WalletListBodyState extends State<WalletListBody> {
}); });
} }
Future<void> _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<void>(
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) { void changeProcessText(String text) {
_progressBar = createBar<void>(text, duration: null)..show(context); _progressBar = createBar<void>(text, duration: null)..show(context);
} }
@ -277,18 +241,4 @@ class WalletListBodyState extends State<WalletListBody> {
_progressBar = null; _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,
),
],
);
} }

View file

@ -46,145 +46,133 @@ class WelcomePage extends BasePage {
@override @override
Widget body(BuildContext context) { 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', final newWalletImage = Image.asset('assets/images/new_wallet.png',
height: 12, height: 12,
width: 12, width: 12,
color: Theme.of(context) color: Theme.of(context)
.accentTextTheme! .accentTextTheme.headlineSmall!
.headlineSmall!
.decorationColor!); .decorationColor!);
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png', final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
height: 12, height: 12,
width: 12, width: 12,
color: Theme.of(context).primaryTextTheme!.titleLarge!.color!);
color: Theme.of(context).primaryTextTheme.titleLarge!.color!);
return WillPopScope( return WillPopScope(
onWillPop: () async => false, onWillPop: () async => false,
child: Container( child: Container(
alignment: Alignment.center,
padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24), padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24),
child: Center( child: ConstrainedBox(
child: ConstrainedBox( constraints: BoxConstraints(
constraints: maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint),
BoxConstraints(maxWidth: ResponsiveLayoutUtil.kDesktopMaxWidthConstraint), child: Column(
child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween, children: <Widget>[
children: <Widget>[ Column(
Flexible( children: <Widget>[
flex: 2, AspectRatio(
child: AspectRatio( aspectRatio: aspectRatioImage,
aspectRatio: aspectRatioImage, child: FittedBox(
child: FittedBox(child: welcomeImage, fit: BoxFit.fill))), child: welcomeImage, fit: BoxFit.contain),
Flexible( ),
flex: 3, Padding(
child: Column( padding: EdgeInsets.only(top: 24),
mainAxisAlignment: MainAxisAlignment.spaceBetween, child: Text(
children: <Widget>[ S.of(context).welcome,
Column( style: TextStyle(
children: <Widget>[ fontSize: 18,
Padding( fontWeight: FontWeight.w500,
padding: EdgeInsets.only(top: 24), color: Theme.of(context)
child: Text( .accentTextTheme.displayMedium!
S.of(context).welcome, .color!,
style: TextStyle( ),
fontSize: 18, textAlign: TextAlign.center,
fontWeight: FontWeight.w500, ),
color: Theme.of(context) ),
.accentTextTheme! Padding(
.displayMedium! padding: EdgeInsets.only(top: 5),
.color, child: Text(
), appTitle(context),
textAlign: TextAlign.center, style: TextStyle(
), fontSize: 36,
), fontWeight: FontWeight.bold,
Padding( color: Theme.of(context)
padding: EdgeInsets.only(top: 5), .primaryTextTheme.titleLarge!
child: Text( .color!,
appTitle(context), ),
style: TextStyle( textAlign: TextAlign.center,
fontSize: 36, ),
fontWeight: FontWeight.bold, ),
color: Theme.of(context) Padding(
.primaryTextTheme! padding: EdgeInsets.only(top: 5),
.titleLarge! child: Text(
.color!, appDescription(context),
), style: TextStyle(
textAlign: TextAlign.center, fontSize: 16,
), fontWeight: FontWeight.w500,
), color: Theme.of(context)
Padding( .accentTextTheme.displayMedium!
padding: EdgeInsets.only(top: 5), .color!,
child: Text( ),
appDescription(context), textAlign: TextAlign.center,
style: TextStyle( ),
fontSize: 16, ),
fontWeight: FontWeight.w500, ],
color: Theme.of(context) ),
.accentTextTheme! Column(
.displayMedium! children: <Widget>[
.color, Text(
), S.of(context).please_make_selection,
textAlign: TextAlign.center, style: TextStyle(
), fontSize: 12,
), fontWeight: FontWeight.normal,
], color: Theme.of(context)
), .accentTextTheme.displayMedium!
Column( .color!,
children: <Widget>[ ),
Text( textAlign: TextAlign.center,
S.of(context).please_make_selection, ),
style: TextStyle( Padding(
fontSize: 12, padding: EdgeInsets.only(top: 24),
fontWeight: FontWeight.normal, child: PrimaryImageButton(
color: Theme.of(context) onPressed: () => Navigator.pushNamed(
.accentTextTheme! context, Routes.newWalletFromWelcome),
.displayMedium! image: newWalletImage,
.color, text: S.of(context).create_new,
), color: Theme.of(context)
textAlign: TextAlign.center, .accentTextTheme.titleSmall!
), .decorationColor!,
Padding( textColor: Theme.of(context)
padding: EdgeInsets.only(top: 24), .accentTextTheme.headlineSmall!
child: PrimaryImageButton( .decorationColor!,
onPressed: () => ),
Navigator.pushNamed(context, Routes.newWalletFromWelcome), ),
image: newWalletImage, Padding(
text: S.of(context).create_new, padding: EdgeInsets.only(top: 10),
color: Theme.of(context) child: PrimaryImageButton(
.accentTextTheme! onPressed: () {
.titleSmall! Navigator.pushNamed(
.decorationColor!, context, Routes.restoreOptions,
textColor: Theme.of(context) arguments: true);
.accentTextTheme! },
.headlineSmall! image: restoreWalletImage,
.decorationColor!, text: S.of(context).restore_wallet,
), color: Theme.of(context)
), .accentTextTheme.bodySmall!
Padding( .color!,
padding: EdgeInsets.only(top: 10), textColor: Theme.of(context)
child: PrimaryImageButton( .primaryTextTheme.titleLarge!
onPressed: () { .color!),
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!),
)
],
)
],
))
],
),
), ),
))); )));
} }

View file

@ -114,55 +114,66 @@ class AddressTextField extends StatelessWidget {
width: prefixIconWidth, width: prefixIconWidth,
height: prefixIconHeight, height: prefixIconHeight,
padding: EdgeInsets.only(top: 0), padding: EdgeInsets.only(top: 0),
child: InkWell( child: Semantics(
onTap: () async => _pasteAddress(context), label: S.of(context).paste,
child: Container( child: InkWell(
padding: EdgeInsets.all(8), onTap: () async => _pasteAddress(context),
decoration: BoxDecoration( child: Container(
color: buttonColor ?? padding: EdgeInsets.all(8),
Theme.of(context) decoration: BoxDecoration(
.accentTextTheme! color: buttonColor ??
Theme.of(context)
.accentTextTheme
!
.titleLarge! .titleLarge!
.color!, .color!,
borderRadius: borderRadius:
BorderRadius.all(Radius.circular(6))), BorderRadius.all(Radius.circular(6))),
child: Image.asset( child: Image.asset(
'assets/images/paste_ios.png', 'assets/images/paste_ios.png',
color: iconColor ?? color: iconColor ??
Theme.of(context) Theme.of(context)
.primaryTextTheme! .primaryTextTheme
!
.headlineMedium! .headlineMedium!
.decorationColor!, .decorationColor!,
)), )),
),
)), )),
], ],
if (this.options.contains(AddressTextFieldOption.qrCode) && DeviceInfo.instance.isMobile) if (this.options.contains(AddressTextFieldOption.qrCode) &&
...[ DeviceInfo.instance.isMobile) ...[
Container( Container(
width: prefixIconWidth, width: prefixIconWidth,
height: prefixIconHeight, height: prefixIconHeight,
padding: EdgeInsets.only(top: 0), padding: EdgeInsets.only(top: 0),
child: InkWell( child: Semantics(
onTap: () async => _presentQRScanner(context), label: S.of(context).scan_qr_code,
child: Container( child: InkWell(
padding: EdgeInsets.all(8), onTap: () async => _presentQRScanner(context),
decoration: BoxDecoration( child: Container(
color: buttonColor ?? padding: EdgeInsets.all(8),
Theme.of(context) decoration: BoxDecoration(
.accentTextTheme color: buttonColor ??
Theme.of(context)
.accentTextTheme
.titleLarge! .titleLarge!
.color!, .color!,
borderRadius: borderRadius:
BorderRadius.all(Radius.circular(6))), BorderRadius.all(Radius.circular(6))),
child: Image.asset( child: Image.asset(
'assets/images/qr_code_icon.png', 'assets/images/qr_code_icon.png',
color: iconColor ?? color: iconColor ??
Theme.of(context) Theme.of(context)
.primaryTextTheme!.headlineMedium! .primaryTextTheme
.decorationColor!, !.headlineMedium!
)), .decorationColor!,
)),
),
)) ))
] else SizedBox(width: 5), ] else
SizedBox(width: 5),
if (this if (this
.options .options
.contains(AddressTextFieldOption.addressBook)) ...[ .contains(AddressTextFieldOption.addressBook)) ...[
@ -170,26 +181,32 @@ class AddressTextField extends StatelessWidget {
width: prefixIconWidth, width: prefixIconWidth,
height: prefixIconHeight, height: prefixIconHeight,
padding: EdgeInsets.only(top: 0), padding: EdgeInsets.only(top: 0),
child: InkWell( child: Semantics(
onTap: () async => _presetAddressBookPicker(context), label: S.of(context).address_book,
child: Container( child: InkWell(
padding: EdgeInsets.all(8), onTap: () async =>
decoration: BoxDecoration( _presetAddressBookPicker(context),
color: buttonColor ?? child: Container(
Theme.of(context) padding: EdgeInsets.all(8),
.accentTextTheme! decoration: BoxDecoration(
color: buttonColor ??
Theme.of(context)
.accentTextTheme
!
.titleLarge! .titleLarge!
.color!, .color!,
borderRadius: borderRadius:
BorderRadius.all(Radius.circular(6))), BorderRadius.all(Radius.circular(6))),
child: Image.asset( child: Image.asset(
'assets/images/open_book.png', 'assets/images/open_book.png',
color: iconColor ?? color: iconColor ??
Theme.of(context) Theme.of(context)
.primaryTextTheme! .primaryTextTheme
!
.headlineMedium! .headlineMedium!
.decorationColor!, .decorationColor!,
)), )),
),
)) ))
] ]
], ],

View file

@ -1,6 +1,7 @@
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/generated/i18n.dart';
class IntroducingCard extends StatelessWidget { class IntroducingCard extends StatelessWidget {
IntroducingCard( IntroducingCard(
@ -70,7 +71,7 @@ class IntroducingCard extends StatelessWidget {
Padding( Padding(
padding: const EdgeInsets.fromLTRB(0,16,16,0), padding: const EdgeInsets.fromLTRB(0,16,16,0),
child: Semantics( child: Semantics(
label: 'Close', label: S.of(context).close,
child: GestureDetector( child: GestureDetector(
onTap: closeCard, onTap: closeCard,
child: Container( child: Container(

View file

@ -231,7 +231,7 @@ class SectionStandardList extends StatelessWidget {
return Container(); return Container();
} }
return StandardListSeparator(padding: EdgeInsets.only(left: 24)); return StandardListSeparator(padding: dividerPadding);
}, },
itemCount: totalRows.length, itemCount: totalRows.length,
itemBuilder: (_, index) => totalRows[index]); itemBuilder: (_, index) => totalRows[index]);

View file

@ -145,15 +145,13 @@ abstract class SettingsStoreBase with Store {
}); });
} }
reaction( reaction((_) => disableBuy,
(_) => disableBuy, (bool disableBuy) => sharedPreferences.setBool(PreferencesKey.disableBuyKey, disableBuy));
(bool disableBuy) => sharedPreferences.setBool(
PreferencesKey.disableBuyKey, disableBuy));
reaction( reaction(
(_) => disableSell, (_) => disableSell,
(bool disableSell) => sharedPreferences.setBool( (bool disableSell) =>
PreferencesKey.disableSellKey, disableSell)); sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell));
reaction( reaction(
(_) => fiatApiMode, (_) => fiatApiMode,
@ -350,12 +348,9 @@ abstract class SettingsStoreBase with Store {
// FIX-ME: Check for which default value we should have here // FIX-ME: Check for which default value we should have here
final shouldSaveRecipientAddress = final shouldSaveRecipientAddress =
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false; sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false;
final isAppSecure = final isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false;
sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false; final disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? false;
final disableBuy = final disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false;
sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? false;
final disableSell =
sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false;
final currentFiatApiMode = FiatApiMode.deserialize( final currentFiatApiMode = FiatApiMode.deserialize(
raw: sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey) ?? raw: sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey) ??
FiatApiMode.enabled.raw); FiatApiMode.enabled.raw);
@ -487,16 +482,14 @@ abstract class SettingsStoreBase with Store {
useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? useTOTP2FA; useTOTP2FA = sharedPreferences.getBool(PreferencesKey.useTOTP2FA) ?? useTOTP2FA;
numberOfFailedTokenTrials = numberOfFailedTokenTrials =
sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials; sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials;
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? shouldSaveRecipientAddress; sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ??
isAppSecure = shouldSaveRecipientAddress;
sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure; isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure;
disableBuy = disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy;
sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy; disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell;
disableSell = allowBiometricalAuthentication =
sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell; sharedPreferences.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
allowBiometricalAuthentication = sharedPreferences allowBiometricalAuthentication;
.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
allowBiometricalAuthentication;
shouldShowMarketPlaceInDashboard = shouldShowMarketPlaceInDashboard =
sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ?? sharedPreferences.getBool(PreferencesKey.shouldShowMarketPlaceInDashboard) ??
shouldShowMarketPlaceInDashboard; shouldShowMarketPlaceInDashboard;

View file

@ -0,0 +1,3 @@
class FeatureFlag {
static const bool isCakePayEnabled = false;
}

View file

@ -1,22 +1,32 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class ResponsiveLayoutUtil { class ResponsiveLayoutUtil {
static const double _kMobileThreshold = 768; static const double _kMobileThreshold = 550;
static const double kDesktopMaxWidthConstraint = 400; static const double kDesktopMaxWidthConstraint = 400;
static const double kDesktopMaxDashBoardWidthConstraint = 900;
static const double kPopupWidth = 400; static const double kPopupWidth = 400;
static const double kPopupSpaceHeight = 100; static const double kPopupSpaceHeight = 100;
static const _kIpadMaxWidth = 2560.0;
const ResponsiveLayoutUtil._(); const ResponsiveLayoutUtil._();
static final instance = ResponsiveLayoutUtil._(); static final instance = ResponsiveLayoutUtil._();
bool get isMobile => bool get isMobile =>
MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width < _kMobileThreshold; MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.shortestSide <=
_kMobileThreshold;
bool get isIpad { bool shouldRenderMobileUI() {
final width = MediaQueryData.fromWindow(WidgetsBinding.instance.window).size.width; final mediaQuery = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
return width >= _kMobileThreshold && !(width > _kIpadMaxWidth); 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. /// Returns dynamic size.

View file

@ -4,9 +4,9 @@ part 'desktop_sidebar_view_model.g.dart';
enum SidebarItem { enum SidebarItem {
dashboard, dashboard,
transactions,
support, support,
settings, settings,
transactions;
} }
class DesktopSidebarViewModel = DesktopSidebarViewModelBase with _$DesktopSidebarViewModel; class DesktopSidebarViewModel = DesktopSidebarViewModelBase with _$DesktopSidebarViewModel;

View file

@ -24,6 +24,7 @@ abstract class RescanViewModelBase with Store {
Future<void> rescanCurrentWallet({required int restoreHeight}) async { Future<void> rescanCurrentWallet({required int restoreHeight}) async {
state = RescanWalletState.rescaning; state = RescanWalletState.rescaning;
await _wallet.rescan(height: restoreHeight); await _wallet.rescan(height: restoreHeight);
_wallet.transactionHistory.clear();
state = RescanWalletState.none; state = RescanWalletState.none;
} }
} }

View file

@ -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<void> changeName(WalletListItem walletItem) async {
state = WalletEditRenamePending();
await _walletLoadingService.renameWallet(
walletItem.type, walletItem.name, newName);
_walletListViewModel.updateList();
}
@action
Future<void> remove(WalletListItem wallet) async {
state = WalletEditDeletePending();
final walletService = getIt.get<WalletService>(param1: wallet.type);
await walletService.remove(wallet.name);
resetState();
_walletListViewModel.updateList();
}
@action
void resetState() {
state = WalletEditViewModelInitialState();
}
}

View file

@ -1,10 +1,9 @@
import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/wallet_loading_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:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/store/app_store.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:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
@ -21,8 +20,8 @@ abstract class WalletListViewModelBase with Store {
this._walletLoadingService, this._walletLoadingService,
this._authService, this._authService,
) : wallets = ObservableList<WalletListItem>() { ) : wallets = ObservableList<WalletListItem>() {
_updateList(); updateList();
reaction((_) => _appStore.wallet, (_) => _updateList()); reaction((_) => _appStore.wallet, (_) => updateList());
} }
@observable @observable
@ -37,20 +36,14 @@ abstract class WalletListViewModelBase with Store {
@action @action
Future<void> loadWallet(WalletListItem walletItem) async { Future<void> 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); _appStore.changeCurrentWallet(wallet);
_updateList();
} }
@action @action
Future<void> remove(WalletListItem wallet) async { void updateList() {
final walletService = getIt.get<WalletService>(param1: wallet.type);
await walletService.remove(wallet.name);
await _walletInfoSource.delete(wallet.key);
_updateList();
}
void _updateList() {
wallets.clear(); wallets.clear();
wallets.addAll( wallets.addAll(
_walletInfoSource.values.map( _walletInfoSource.values.map(
@ -58,7 +51,8 @@ abstract class WalletListViewModelBase with Store {
name: info.name, name: info.name,
type: info.type, type: info.type,
key: info.key, 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), isEnabled: availableWalletTypes.contains(info.type),
), ),
), ),

View file

@ -115,10 +115,10 @@ SPEC CHECKSUMS:
platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94 platform_device_id_macos: f763bb55f088be804d61b96eb4710b8ab6598e94
ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4 share_plus_macos: 853ee48e7dce06b633998ca0735d482dd671ade4
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9 wakelock_macos: bc3f2a9bd8d2e6c89fee1e1822e7ddac3bd004a9
PODFILE CHECKSUM: 5107934592df7813b33d744aebc8ddc6b5a5445f PODFILE CHECKSUM: 5107934592df7813b33d744aebc8ddc6b5a5445f
COCOAPODS: 1.11.3 COCOAPODS: 1.12.1

Some files were not shown because too many files have changed in this diff Show more