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
|
@ -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
After Width: | Height: | Size: 13 KiB |
BIN
assets/images/arb_icon.png
Normal file
After Width: | Height: | Size: 9.7 KiB |
BIN
assets/images/bat_icon.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 10 KiB |
BIN
assets/images/cake_icon.png
Normal file
After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 582 B |
BIN
assets/images/comp_icon.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
assets/images/cro_icon.png
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/images/dydx_icon.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
assets/images/ens_icon.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 1 KiB |
BIN
assets/images/frax_icon.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/images/ftm_icon.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 576 B |
BIN
assets/images/grt_icon.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
assets/images/gtc_icon.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/images/gusd_icon.png
Normal file
After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 596 B |
Before Width: | Height: | Size: 683 B |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 570 B |
BIN
assets/images/ldo_icon.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
assets/images/nexo_icon.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
assets/images/pepe_icon.png
Normal file
After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 538 B |
Before Width: | Height: | Size: 773 B |
Before Width: | Height: | Size: 557 B |
BIN
assets/images/shib_icon.png
Normal file
After Width: | Height: | Size: 7.8 KiB |
Before Width: | Height: | Size: 680 B |
Before Width: | Height: | Size: 518 B |
BIN
assets/images/steth_icon.png
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
assets/images/storj_icon.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/images/tusd_icon.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 838 B |
Before Width: | Height: | Size: 6.8 KiB |
BIN
assets/images/wbtc_icon.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
assets/images/weth_icon.png
Normal file
After Width: | Height: | Size: 7 KiB |
Before Width: | Height: | Size: 6 KiB |
BIN
assets/images/zrx_icon.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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 =
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
18
lib/di.dart
|
@ -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!;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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>(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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!,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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) =>
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
|
||||||
],
|
|
||||||
))
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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)),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = [
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
175
lib/src/screens/wallet/wallet_edit_page.dart
Normal 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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!,
|
||||||
)),
|
)),
|
||||||
|
),
|
||||||
))
|
))
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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]);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
3
lib/utils/feature_flag.dart
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
class FeatureFlag {
|
||||||
|
static const bool isCakePayEnabled = false;
|
||||||
|
}
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
55
lib/view_model/wallet_list/wallet_edit_view_model.dart
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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
|
||||||
|
|