General changes. CAKE-1.
2
assets/electrum_server_list.yml
Normal file
|
@ -0,0 +1,2 @@
|
|||
-
|
||||
uri: electrum2.hodlister.co:50002
|
BIN
assets/fonts/Montserrat-Bold.ttf
Normal file
BIN
assets/fonts/Montserrat-Regular.ttf
Normal file
BIN
assets/fonts/Montserrat-SemiBold.ttf
Normal file
BIN
assets/images/2.0x/crypto_lock_light.png
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
assets/images/2.0x/wallet_name_light.png
Normal file
After Width: | Height: | Size: 151 KiB |
BIN
assets/images/2.0x/wallet_type_light.png
Normal file
After Width: | Height: | Size: 150 KiB |
BIN
assets/images/2.0x/welcome_light.png
Normal file
After Width: | Height: | Size: 315 KiB |
BIN
assets/images/3.0x/crypto_lock_light.png
Normal file
After Width: | Height: | Size: 191 KiB |
BIN
assets/images/3.0x/wallet_name_light.png
Normal file
After Width: | Height: | Size: 295 KiB |
BIN
assets/images/3.0x/wallet_type_light.png
Normal file
After Width: | Height: | Size: 294 KiB |
BIN
assets/images/3.0x/welcome_light.png
Normal file
After Width: | Height: | Size: 640 KiB |
BIN
assets/images/crypto_lock_light.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
assets/images/wallet_name_light.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
assets/images/wallet_type_light.png
Normal file
After Width: | Height: | Size: 50 KiB |
BIN
assets/images/welcome_light.png
Normal file
After Width: | Height: | Size: 97 KiB |
|
@ -182,7 +182,7 @@ Future<void> _openWallet(Map<String, String> args) async =>
|
|||
bool _isWalletExist(String path) => isWalletExistSync(path: path);
|
||||
|
||||
void openWallet({String path, String password, int nettype = 0}) async =>
|
||||
loadWallet(path: path, password: password);
|
||||
loadWallet(path: path, password: password, nettype: nettype);
|
||||
|
||||
Future<void> openWalletAsync(Map<String, String> args) async =>
|
||||
compute(_openWallet, args);
|
||||
|
|
|
@ -36,6 +36,8 @@ PODS:
|
|||
- Flutter
|
||||
- path_provider (0.0.1):
|
||||
- Flutter
|
||||
- path_provider_linux (0.0.1):
|
||||
- Flutter
|
||||
- path_provider_macos (0.0.1):
|
||||
- Flutter
|
||||
- share (0.0.1):
|
||||
|
@ -65,6 +67,7 @@ DEPENDENCIES:
|
|||
- local_auth (from `.symlinks/plugins/local_auth/ios`)
|
||||
- package_info (from `.symlinks/plugins/package_info/ios`)
|
||||
- path_provider (from `.symlinks/plugins/path_provider/ios`)
|
||||
- path_provider_linux (from `.symlinks/plugins/path_provider_linux/ios`)
|
||||
- path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`)
|
||||
- share (from `.symlinks/plugins/share/ios`)
|
||||
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
|
||||
|
@ -100,6 +103,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/package_info/ios"
|
||||
path_provider:
|
||||
:path: ".symlinks/plugins/path_provider/ios"
|
||||
path_provider_linux:
|
||||
:path: ".symlinks/plugins/path_provider_linux/ios"
|
||||
path_provider_macos:
|
||||
:path: ".symlinks/plugins/path_provider_macos/ios"
|
||||
share:
|
||||
|
@ -129,6 +134,7 @@ SPEC CHECKSUMS:
|
|||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||
package_info: 873975fc26034f0b863a300ad47e7f1ac6c7ec62
|
||||
path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c
|
||||
path_provider_linux: 4d630dc393e1f20364f3e3b4a2ff41d9674a84e4
|
||||
path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0
|
||||
share: 0b2c3e82132f5888bccca3351c504d0003b3b410
|
||||
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
|
||||
|
@ -141,4 +147,4 @@ SPEC CHECKSUMS:
|
|||
|
||||
PODFILE CHECKSUM: f1916a43bb28badbd408be80e8e4b8652a74e93e
|
||||
|
||||
COCOAPODS: 1.9.1
|
||||
COCOAPODS: 1.9.3
|
||||
|
|
|
@ -388,7 +388,7 @@
|
|||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.28;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cakewallet.cakewallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
@ -524,7 +524,7 @@
|
|||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.28;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cakewallet.cakewallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
|
@ -555,7 +555,7 @@
|
|||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.28;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cakewallet.cakewallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
|
6
lib/bitcoin/bitcoin_transaction_credentials.dart
Normal file
|
@ -0,0 +1,6 @@
|
|||
class BitcoinTransactionCredentials {
|
||||
const BitcoinTransactionCredentials(this.address, this.amount);
|
||||
|
||||
final String address;
|
||||
final double amount;
|
||||
}
|
|
@ -26,26 +26,18 @@ abstract class BitcoinTransactionHistoryBase
|
|||
_password = password,
|
||||
_height = 0;
|
||||
|
||||
BitcoinWallet wallet;
|
||||
BitcoinWalletBase wallet;
|
||||
final ElectrumClient eclient;
|
||||
final String path;
|
||||
final String _password;
|
||||
int _height;
|
||||
|
||||
Future<void> init() async {
|
||||
// TODO: throw exeption if wallet is null;
|
||||
final info = await _read();
|
||||
_height = (info['height'] as int) ?? _height;
|
||||
// FIXME: remove hardcoded value
|
||||
transactions = ObservableList.of([
|
||||
BitcoinTransactionInfo(
|
||||
id: 'test',
|
||||
height: 12,
|
||||
amount: 12,
|
||||
direction: TransactionDirection.incoming,
|
||||
date: DateTime.now(),
|
||||
isPending: false)
|
||||
]);
|
||||
_height = info['height'] as int ?? _height;
|
||||
transactions = ObservableList.of(
|
||||
info['transactions'] as List<BitcoinTransactionInfo> ??
|
||||
<BitcoinTransactionInfo>[]);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -119,7 +111,7 @@ abstract class BitcoinTransactionHistoryBase
|
|||
|
||||
return {'transactions': transactions, 'height': height};
|
||||
} catch (_) {
|
||||
return {'transactions': <TransactionInfo>[], 'height': 0};
|
||||
return {'transactions': <BitcoinTransactionInfo>[], 'height': 0};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_credentials.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_keys.dart';
|
||||
import 'package:cake_wallet/src/domain/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
@ -13,13 +19,10 @@ import 'package:cake_wallet/bitcoin/electrum.dart';
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_balance.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
part 'bitcoin_wallet.g.dart';
|
||||
|
||||
/* TODO: Save balance to a wallet file.
|
||||
Load balance from the wallet file in `init` method.
|
||||
*/
|
||||
|
||||
class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
|
||||
|
||||
abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||
|
@ -31,7 +34,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
final data = json.decode(jsonSource) as Map;
|
||||
final mnemonic = data['mnemonic'] as String;
|
||||
final accountIndex =
|
||||
(data['account_index'] == "null" || data['account_index'] == null)
|
||||
(data['account_index'] == 'null' || data['account_index'] == null)
|
||||
? 0
|
||||
: int.parse(data['account_index'] as String);
|
||||
final _addresses = data['addresses'] as List;
|
||||
|
@ -90,18 +93,15 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
this.transactionHistory,
|
||||
this.mnemonic,
|
||||
BitcoinBalance initialBalance}) {
|
||||
type = WalletType.bitcoin;
|
||||
currency = CryptoCurrency.btc;
|
||||
balance = initialBalance ?? BitcoinBalance(confirmed: 0, unconfirmed: 0);
|
||||
hd = bitcoin.HDWallet.fromSeed(bip39.mnemonicToSeed(mnemonic),
|
||||
network: bitcoin.bitcoin);
|
||||
addresses = initialAddresses != null
|
||||
? ObservableList<BitcoinAddressRecord>.of(initialAddresses)
|
||||
: ObservableList<BitcoinAddressRecord>();
|
||||
|
||||
if (addresses.isEmpty) {
|
||||
addresses.add(BitcoinAddressRecord(hd.address));
|
||||
}
|
||||
|
||||
address = addresses.first.address;
|
||||
syncStatus = NotConnectedSyncStatus();
|
||||
|
||||
_password = password;
|
||||
_accountIndex = accountIndex;
|
||||
|
@ -113,8 +113,6 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
bitcoin.HDWallet hd;
|
||||
final ElectrumClient eclient;
|
||||
final String mnemonic;
|
||||
int _accountIndex;
|
||||
String _password;
|
||||
|
||||
@override
|
||||
String name;
|
||||
|
@ -128,13 +126,31 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
BitcoinBalance balance;
|
||||
|
||||
@override
|
||||
final type = WalletType.bitcoin;
|
||||
@observable
|
||||
SyncStatus syncStatus;
|
||||
|
||||
ObservableList<BitcoinAddressRecord> addresses;
|
||||
|
||||
String get xpub => hd.base58;
|
||||
|
||||
@override
|
||||
String get seed => mnemonic;
|
||||
|
||||
@override
|
||||
BitcoinWalletKeys get keys => BitcoinWalletKeys(
|
||||
wif: hd.wif, privateKey: hd.privKey, publicKey: hd.pubKey);
|
||||
|
||||
int _accountIndex;
|
||||
String _password;
|
||||
BehaviorSubject<Object> _addressUpdateSubject;
|
||||
|
||||
Future<void> init() async {
|
||||
if (addresses.isEmpty) {
|
||||
addresses.add(BitcoinAddressRecord(_getAddress(hd: hd, index: 0)));
|
||||
}
|
||||
|
||||
address = addresses.first.address;
|
||||
transactionHistory.wallet = this;
|
||||
await transactionHistory.init();
|
||||
}
|
||||
|
||||
|
@ -160,14 +176,55 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
}
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> startSync() async {}
|
||||
Future<void> startSync() async {
|
||||
try {
|
||||
syncStatus = StartingSyncStatus();
|
||||
await _addressUpdateSubject?.close();
|
||||
_addressUpdateSubject = eclient.addressUpdate(address: address);
|
||||
await transactionHistory.update();
|
||||
await _updateBalance();
|
||||
syncStatus = SyncedSyncStatus();
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
syncStatus = FailedSyncStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
@override
|
||||
Future<void> connectToNode({@required Node node}) async {
|
||||
try {
|
||||
syncStatus = ConnectingSyncStatus();
|
||||
await eclient.connect(host: 'electrum2.hodlister.co', port: 50002);
|
||||
syncStatus = ConnectedSyncStatus();
|
||||
} catch (e) {
|
||||
print(e.toString);
|
||||
syncStatus = FailedSyncStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> connectToNode({@required Node node}) async {}
|
||||
Future<void> createTransaction(Object credentials) async {
|
||||
final transactionCredentials = credentials as BitcoinTransactionCredentials;
|
||||
|
||||
@override
|
||||
Future<void> createTransaction(Object credentials) async {}
|
||||
final txb = bitcoin.TransactionBuilder(network: bitcoin.bitcoin);
|
||||
final keyPair = bitcoin.ECPair.fromWIF(hd.wif);
|
||||
final transactions = transactionHistory.transactions;
|
||||
transactions.sort((q, w) => q.height.compareTo(w.height));
|
||||
final prevTx = transactions.first;
|
||||
|
||||
txb.setVersion(1);
|
||||
txb.addInput(prevTx, 0);
|
||||
txb.addOutput(transactionCredentials.address,
|
||||
doubleToBitcoinAmount(transactionCredentials.amount));
|
||||
txb.sign(vin: 0, keyPair: keyPair);
|
||||
final encoded = txb.build().toHex();
|
||||
|
||||
print('Enoded transaction $encoded');
|
||||
await eclient.broadcastTransaction(transactionRaw: encoded);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> save() async =>
|
||||
|
@ -181,24 +238,27 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
});
|
||||
|
||||
String _getAddress({bitcoin.HDWallet hd, int index}) => bitcoin
|
||||
.P2PKH(
|
||||
.P2WPKH(
|
||||
data: PaymentData(
|
||||
pubkey: Uint8List.fromList(hd.derive(index).pubKey.codeUnits)))
|
||||
.data
|
||||
.address;
|
||||
|
||||
Future<Map<String, int>> _fetchBalances() async {
|
||||
Future<BitcoinBalance> _fetchBalances() async {
|
||||
final balances = await Future.wait(
|
||||
addresses.map((record) => eclient.getBalance(address: record.address)));
|
||||
final balance = balances.fold(<String, int>{}, (Map<String, int> acc, val) {
|
||||
acc['confirmed'] =
|
||||
(val['confirmed'] as int ?? 0) + (acc['confirmed'] ?? 0);
|
||||
acc['unconfirmed'] =
|
||||
(val['unconfirmed'] as int ?? 0) + (acc['unconfirmed'] ?? 0);
|
||||
|
||||
return acc;
|
||||
});
|
||||
final balance = balances.fold(
|
||||
BitcoinBalance(confirmed: 0, unconfirmed: 0),
|
||||
(BitcoinBalance acc, val) => BitcoinBalance(
|
||||
confirmed: (val['confirmed'] as int ?? 0) + (acc.confirmed ?? 0),
|
||||
unconfirmed:
|
||||
(val['unconfirmed'] as int ?? 0) + (acc.unconfirmed ?? 0)));
|
||||
|
||||
return balance;
|
||||
}
|
||||
|
||||
Future<void> _updateBalance() async {
|
||||
balance = await _fetchBalances();
|
||||
await save();
|
||||
}
|
||||
}
|
||||
|
|
9
lib/bitcoin/bitcoin_wallet_keys.dart
Normal file
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class BitcoinWalletKeys {
|
||||
const BitcoinWalletKeys({@required this.wif, @required this.privateKey, @required this.publicKey});
|
||||
|
||||
final String wif;
|
||||
final String privateKey;
|
||||
final String publicKey;
|
||||
}
|
|
@ -34,6 +34,7 @@ class ElectrumClient {
|
|||
int _id;
|
||||
final Map<String, SocketTask> _tasks;
|
||||
bool _isConnected;
|
||||
Timer _aliveTimer;
|
||||
|
||||
Future<void> connect({@required String host, @required int port}) async {
|
||||
if (socket != null) {
|
||||
|
@ -48,8 +49,7 @@ class ElectrumClient {
|
|||
|
||||
socket.listen((List<int> event) {
|
||||
try {
|
||||
final Map<String, Object> jsoned =
|
||||
json.decode(utf8.decode(event)) as Map<String, Object>;
|
||||
final jsoned = json.decode(utf8.decode(event)) as Map<String, Object>;
|
||||
final method = jsoned['method'];
|
||||
|
||||
if (method is String) {
|
||||
|
@ -73,6 +73,13 @@ class ElectrumClient {
|
|||
});
|
||||
|
||||
print('Connected to ${socket.remoteAddress}');
|
||||
keepAlive();
|
||||
}
|
||||
|
||||
void keepAlive() {
|
||||
_aliveTimer?.cancel();
|
||||
// FIXME: Unnamed constant.
|
||||
_aliveTimer = Timer.periodic(Duration(seconds: 30), (_) async => ping());
|
||||
}
|
||||
|
||||
Future<void> ping() => call(method: 'server.ping');
|
||||
|
@ -122,6 +129,16 @@ class ElectrumClient {
|
|||
return '';
|
||||
});
|
||||
|
||||
Future<String> broadcastTransaction({@required String transactionRaw}) async =>
|
||||
call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
|
||||
.then((dynamic result) {
|
||||
if (result is String) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return '';
|
||||
});
|
||||
|
||||
Future<Map<String, dynamic>> getMerkle(
|
||||
{@required String hash, @required int height}) async =>
|
||||
await call(
|
||||
|
|
82
lib/core/address_validator.dart
Normal file
|
@ -0,0 +1,82 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
|
||||
class AddressValidator extends TextValidator {
|
||||
AddressValidator({@required CryptoCurrency type})
|
||||
: super(
|
||||
errorMessage: S.current.error_text_address,
|
||||
pattern: getPattern(type),
|
||||
length: getLength(type));
|
||||
|
||||
static String getPattern(CryptoCurrency type) {
|
||||
switch (type) {
|
||||
case CryptoCurrency.xmr:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.ada:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.bch:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.bnb:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.btc:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.dash:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.eos:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.eth:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.ltc:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.nano:
|
||||
return '[0-9a-zA-Z_]';
|
||||
case CryptoCurrency.trx:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.usdt:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.xlm:
|
||||
return '[0-9a-zA-Z]';
|
||||
case CryptoCurrency.xrp:
|
||||
return '[0-9a-zA-Z]';
|
||||
default:
|
||||
return '[0-9a-zA-Z]';
|
||||
}
|
||||
}
|
||||
|
||||
static List<int> getLength(CryptoCurrency type) {
|
||||
switch (type) {
|
||||
case CryptoCurrency.xmr:
|
||||
return [95, 106];
|
||||
case CryptoCurrency.ada:
|
||||
return [59, 92, 105];
|
||||
case CryptoCurrency.bch:
|
||||
return [42];
|
||||
case CryptoCurrency.bnb:
|
||||
return [42];
|
||||
case CryptoCurrency.btc:
|
||||
return [34, 42];
|
||||
case CryptoCurrency.dash:
|
||||
return [34];
|
||||
case CryptoCurrency.eos:
|
||||
return [42];
|
||||
case CryptoCurrency.eth:
|
||||
return [42];
|
||||
case CryptoCurrency.ltc:
|
||||
return [34];
|
||||
case CryptoCurrency.nano:
|
||||
return [64, 65];
|
||||
case CryptoCurrency.trx:
|
||||
return [34];
|
||||
case CryptoCurrency.usdt:
|
||||
return [42];
|
||||
case CryptoCurrency.xlm:
|
||||
return [56];
|
||||
case CryptoCurrency.xrp:
|
||||
return [34];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
11
lib/core/contact_name_validator.dart
Normal file
|
@ -0,0 +1,11 @@
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
|
||||
class ContactNameValidator extends TextValidator {
|
||||
ContactNameValidator()
|
||||
: super(
|
||||
errorMessage: S.current.error_text_contact_name,
|
||||
pattern: '''[^`,'"]''',
|
||||
minLength: 1,
|
||||
maxLength: 32);
|
||||
}
|
38
lib/core/contact_service.dart
Normal file
|
@ -0,0 +1,38 @@
|
|||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/store/contact_list_store.dart';
|
||||
import 'package:cake_wallet/src/domain/common/contact.dart';
|
||||
|
||||
class ContactService {
|
||||
ContactService(this.contactSource, this.contactListStore) {
|
||||
_forceUpdateContactListStore();
|
||||
}
|
||||
|
||||
final Box<Contact> contactSource;
|
||||
final ContactListStore contactListStore;
|
||||
|
||||
Future add(Contact contact) async {
|
||||
await contactSource.add(contact);
|
||||
contactListStore.contacts.add(contact);
|
||||
}
|
||||
|
||||
Future update(Contact contact) async {
|
||||
await contact.save();
|
||||
final index = contactListStore.contacts.indexOf(contact) ?? -1;
|
||||
|
||||
if (index >= 0) {
|
||||
_forceUpdateContactListStore();
|
||||
} else {
|
||||
contactListStore.contacts.add(contact);
|
||||
}
|
||||
}
|
||||
|
||||
Future delete(Contact contact) async {
|
||||
await contact.delete();
|
||||
contactListStore.contacts.remove(contact);
|
||||
}
|
||||
|
||||
void _forceUpdateContactListStore() {
|
||||
contactListStore.contacts.clear();
|
||||
contactListStore.contacts.addAll(contactSource.values);
|
||||
}
|
||||
}
|
25
lib/core/key_service.dart
Normal file
|
@ -0,0 +1,25 @@
|
|||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:cake_wallet/src/domain/common/secret_store_key.dart';
|
||||
import 'package:cake_wallet/src/domain/common/encrypt.dart';
|
||||
|
||||
class KeyService {
|
||||
KeyService(this._secureStorage);
|
||||
|
||||
final FlutterSecureStorage _secureStorage;
|
||||
|
||||
Future<String> getWalletPassword({String walletName}) async {
|
||||
final key = generateStoreKeyFor(
|
||||
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
|
||||
final encodedPassword = await _secureStorage.read(key: key);
|
||||
|
||||
return decodeWalletPassword(password: encodedPassword);
|
||||
}
|
||||
|
||||
Future<void> saveWalletPassword({String walletName, String password}) async {
|
||||
final key = generateStoreKeyFor(
|
||||
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
|
||||
final encodedPassword = encodeWalletPassword(password: password);
|
||||
|
||||
await _secureStorage.write(key: key, value: encodedPassword);
|
||||
}
|
||||
}
|
13
lib/core/monero_account_label_validator.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
|
||||
class MoneroLabelValidator extends TextValidator {
|
||||
MoneroLabelValidator({@required CryptoCurrency type})
|
||||
: super(
|
||||
errorMessage: S.current.error_text_account_name,
|
||||
pattern: '^[a-zA-Z0-9_]{1,15}\$',
|
||||
minLength: 1,
|
||||
maxLength: 15);
|
||||
}
|
10
lib/core/node_address_validator.dart
Normal file
|
@ -0,0 +1,10 @@
|
|||
import 'package:cake_wallet/core/validator.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class NodeAddressValidator extends TextValidator {
|
||||
NodeAddressValidator()
|
||||
: super(
|
||||
errorMessage: S.current.error_text_node_address,
|
||||
pattern:
|
||||
'^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\$|^[0-9a-zA-Z.]+\$');
|
||||
}
|
12
lib/core/node_port_validator.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/validator.dart';
|
||||
|
||||
class NodePortValidator extends TextValidator {
|
||||
NodePortValidator()
|
||||
: super(
|
||||
errorMessage: S.current.error_text_node_port,
|
||||
minLength: 1,
|
||||
maxLength: 5,
|
||||
pattern: '^[0-9]');
|
||||
}
|
|
@ -12,11 +12,16 @@ abstract class Validator<T> {
|
|||
|
||||
class TextValidator extends Validator<String> {
|
||||
TextValidator(
|
||||
{this.minLength, this.maxLength, this.pattern, String errorMessage})
|
||||
{this.minLength,
|
||||
this.maxLength,
|
||||
this.pattern,
|
||||
this.length,
|
||||
String errorMessage})
|
||||
: super(errorMessage: errorMessage);
|
||||
|
||||
final int minLength;
|
||||
final int maxLength;
|
||||
final List<int> length;
|
||||
String pattern;
|
||||
|
||||
@override
|
||||
|
@ -25,8 +30,9 @@ class TextValidator extends Validator<String> {
|
|||
return true;
|
||||
}
|
||||
|
||||
return value.length > minLength &&
|
||||
(maxLength > 0 ? (value.length <= maxLength) : true) &&
|
||||
return value.length > (minLength ?? 0) &&
|
||||
(length?.contains(value.length) ?? true) &&
|
||||
((maxLength ?? 0) > 0 ? (value.length <= maxLength) : true) &&
|
||||
(pattern != null ? match(value) : true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,31 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/core/transaction_history.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
|
||||
abstract class WalletBase<BalaceType> {
|
||||
WalletType type;
|
||||
|
||||
CryptoCurrency currency;
|
||||
|
||||
String get name;
|
||||
|
||||
String address;
|
||||
|
||||
BalaceType balance;
|
||||
|
||||
SyncStatus syncStatus;
|
||||
|
||||
String get seed;
|
||||
|
||||
Object get keys;
|
||||
|
||||
TransactionHistoryBase transactionHistory;
|
||||
|
||||
String get id => walletTypeToString(type).toLowerCase() + '_' + name;
|
||||
|
||||
Future<void> connectToNode({@required Node node});
|
||||
|
||||
Future<void> startSync();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/core/generate_wallet_password.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/core/wallet_credentials.dart';
|
||||
|
@ -8,14 +9,13 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
|
|||
import 'package:cake_wallet/monero/monero_wallet_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_service.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/common/secret_store_key.dart';
|
||||
import 'package:cake_wallet/src/domain/common/encrypt.dart';
|
||||
|
||||
class WalletCreationService {
|
||||
WalletCreationService(
|
||||
{WalletType initialType,
|
||||
this.appStore,
|
||||
this.secureStorage,
|
||||
this.keyService,
|
||||
this.sharedPreferences})
|
||||
: type = initialType {
|
||||
if (type != null) {
|
||||
|
@ -27,10 +27,7 @@ class WalletCreationService {
|
|||
final AppStore appStore;
|
||||
final FlutterSecureStorage secureStorage;
|
||||
final SharedPreferences sharedPreferences;
|
||||
|
||||
// final WalletService walletService;
|
||||
// final Box<WalletInfo> walletInfoSource;
|
||||
|
||||
final KeyService keyService;
|
||||
WalletService _service;
|
||||
|
||||
void changeWalletType({@required WalletType type}) {
|
||||
|
@ -51,7 +48,8 @@ class WalletCreationService {
|
|||
Future<void> create(WalletCredentials credentials) async {
|
||||
final password = generateWalletPassword(type);
|
||||
credentials.password = password;
|
||||
await saveWalletPassword(password: password, walletName: credentials.name);
|
||||
await keyService.saveWalletPassword(
|
||||
password: password, walletName: credentials.name);
|
||||
final wallet = await _service.create(credentials);
|
||||
appStore.wallet = wallet;
|
||||
appStore.authenticationStore.allowed();
|
||||
|
@ -60,7 +58,8 @@ class WalletCreationService {
|
|||
Future<void> restoreFromKeys(WalletCredentials credentials) async {
|
||||
final password = generateWalletPassword(type);
|
||||
credentials.password = password;
|
||||
await saveWalletPassword(password: password, walletName: credentials.name);
|
||||
await keyService.saveWalletPassword(
|
||||
password: password, walletName: credentials.name);
|
||||
final wallet = await _service.restoreFromKeys(credentials);
|
||||
appStore.wallet = wallet;
|
||||
appStore.authenticationStore.allowed();
|
||||
|
@ -69,25 +68,10 @@ class WalletCreationService {
|
|||
Future<void> restoreFromSeed(WalletCredentials credentials) async {
|
||||
final password = generateWalletPassword(type);
|
||||
credentials.password = password;
|
||||
await saveWalletPassword(password: password, walletName: credentials.name);
|
||||
await keyService.saveWalletPassword(
|
||||
password: password, walletName: credentials.name);
|
||||
final wallet = await _service.restoreFromSeed(credentials);
|
||||
appStore.wallet = wallet;
|
||||
appStore.authenticationStore.allowed();
|
||||
}
|
||||
|
||||
Future<String> getWalletPassword({String walletName}) async {
|
||||
final key = generateStoreKeyFor(
|
||||
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
|
||||
final encodedPassword = await secureStorage.read(key: key);
|
||||
|
||||
return decodeWalletPassword(password: encodedPassword);
|
||||
}
|
||||
|
||||
Future<void> saveWalletPassword({String walletName, String password}) async {
|
||||
final key = generateStoreKeyFor(
|
||||
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
|
||||
final encodedPassword = encodeWalletPassword(password: password);
|
||||
|
||||
await secureStorage.write(key: key, value: encodedPassword);
|
||||
}
|
||||
}
|
||||
|
|
234
lib/di.dart
|
@ -1,19 +1,52 @@
|
|||
import 'package:cake_wallet/core/contact_service.dart';
|
||||
import 'package:cake_wallet/src/domain/common/contact.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/nodes_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/settings.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
||||
import 'package:cake_wallet/store/contact_list_store.dart';
|
||||
import 'package:cake_wallet/store/node_list_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/monero/monero_wallet.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_info.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/dashboard_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/receive_page.dart';
|
||||
import 'package:cake_wallet/src/screens/send/send_page.dart';
|
||||
import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
|
||||
import 'package:cake_wallet/store/wallet_list_store.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/auth_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/core/wallet_creation_service.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
|
@ -22,24 +55,63 @@ import 'package:cake_wallet/store/authentication_store.dart';
|
|||
|
||||
final getIt = GetIt.instance;
|
||||
|
||||
ReactionDisposer _onCurrentWalletChangeReaction;
|
||||
// FIXME: Move me.
|
||||
|
||||
void setup() {
|
||||
getIt.registerSingleton(AuthenticationStore());
|
||||
getIt.registerSingleton<AppStore>(
|
||||
AppStore(authenticationStore: getIt.get<AuthenticationStore>()));
|
||||
getIt.registerSingleton<FlutterSecureStorage>(FlutterSecureStorage());
|
||||
Stream<BoxEvent> _onNodesSourceChange;
|
||||
NodeListStore _nodeListStore;
|
||||
|
||||
NodeListStore setupNodeListStore(Box<Node> nodeSource) {
|
||||
if (_nodeListStore != null) {
|
||||
return _nodeListStore;
|
||||
}
|
||||
|
||||
_nodeListStore = NodeListStore();
|
||||
_nodeListStore.replaceValues(nodeSource.values);
|
||||
_onNodesSourceChange = nodeSource.watch();
|
||||
_onNodesSourceChange
|
||||
.listen((_) => _nodeListStore.replaceValues(nodeSource.values));
|
||||
|
||||
return _nodeListStore;
|
||||
}
|
||||
|
||||
Future setup(
|
||||
{Box<WalletInfo> walletInfoSource,
|
||||
Box<Node> nodeSource,
|
||||
Box<Contact> contactSource}) async {
|
||||
getIt.registerSingletonAsync<SharedPreferences>(
|
||||
() => SharedPreferences.getInstance());
|
||||
|
||||
final settingsStore = await SettingsStoreBase.load(nodeSource: nodeSource);
|
||||
|
||||
getIt.registerSingleton<FlutterSecureStorage>(FlutterSecureStorage());
|
||||
getIt.registerSingleton(AuthenticationStore());
|
||||
getIt.registerSingleton<WalletListStore>(WalletListStore());
|
||||
getIt.registerSingleton(ContactListStore());
|
||||
getIt.registerSingleton(setupNodeListStore(nodeSource));
|
||||
getIt.registerSingleton<SettingsStore>(settingsStore);
|
||||
getIt.registerSingleton<AppStore>(AppStore(
|
||||
authenticationStore: getIt.get<AuthenticationStore>(),
|
||||
walletList: getIt.get<WalletListStore>(),
|
||||
settingsStore: getIt.get<SettingsStore>(),
|
||||
contactListStore: getIt.get<ContactListStore>(),
|
||||
nodeListStore: getIt.get<NodeListStore>()));
|
||||
getIt.registerSingleton<ContactService>(
|
||||
ContactService(contactSource, getIt.get<AppStore>().contactListStore));
|
||||
getIt.registerFactory<KeyService>(
|
||||
() => KeyService(getIt.get<FlutterSecureStorage>()));
|
||||
|
||||
getIt.registerFactoryParam<WalletCreationService, WalletType, void>(
|
||||
(type, _) => WalletCreationService(
|
||||
initialType: type,
|
||||
appStore: getIt.get<AppStore>(),
|
||||
keyService: getIt.get<KeyService>(),
|
||||
secureStorage: getIt.get<FlutterSecureStorage>(),
|
||||
sharedPreferences: getIt.get<SharedPreferences>()));
|
||||
|
||||
getIt.registerFactoryParam<WalletNewVM, WalletType, void>((type, _) =>
|
||||
WalletNewVM(getIt.get<WalletCreationService>(param1: type), type: type));
|
||||
WalletNewVM(
|
||||
getIt.get<WalletCreationService>(param1: type), walletInfoSource,
|
||||
type: type));
|
||||
|
||||
getIt
|
||||
.registerFactoryParam<WalletRestorationFromSeedVM, List, void>((args, _) {
|
||||
|
@ -48,14 +120,12 @@ void setup() {
|
|||
final mnemonic = args[2] as String;
|
||||
|
||||
return WalletRestorationFromSeedVM(
|
||||
getIt.get<WalletCreationService>(param1: type),
|
||||
type: type,
|
||||
language: language,
|
||||
seed: mnemonic);
|
||||
getIt.get<WalletCreationService>(param1: type), walletInfoSource,
|
||||
type: type, language: language, seed: mnemonic);
|
||||
});
|
||||
|
||||
getIt.registerFactory<AddressListViewModel>(
|
||||
() => AddressListViewModel(wallet: getIt.get<AppStore>().wallet));
|
||||
getIt.registerFactory<WalletAddressListViewModel>(
|
||||
() => WalletAddressListViewModel(wallet: getIt.get<AppStore>().wallet));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => DashboardViewModel(appStore: getIt.get<AppStore>()));
|
||||
|
@ -68,38 +138,122 @@ void setup() {
|
|||
authService: getIt.get<AuthService>(),
|
||||
sharedPreferences: getIt.get<SharedPreferences>()));
|
||||
|
||||
getIt.registerFactory<AuthPage>(() => AuthPage(
|
||||
authViewModel: getIt.get<AuthViewModel>(),
|
||||
onAuthenticationFinished: (isAuthenticated, __) {
|
||||
if (isAuthenticated) {
|
||||
getIt.get<AuthenticationStore>().allowed();
|
||||
}
|
||||
},
|
||||
closable: false));
|
||||
getIt.registerFactory<AuthPage>(
|
||||
() => AuthPage(
|
||||
authViewModel: getIt.get<AuthViewModel>(),
|
||||
onAuthenticationFinished: (isAuthenticated, __) {
|
||||
if (isAuthenticated) {
|
||||
getIt.get<AuthenticationStore>().allowed();
|
||||
}
|
||||
},
|
||||
closable: false),
|
||||
instanceName: 'login');
|
||||
|
||||
getIt.registerFactory<DashboardPage>(() => DashboardPage(
|
||||
walletViewModel: getIt.get<DashboardViewModel>(),
|
||||
));
|
||||
getIt
|
||||
.registerFactoryParam<AuthPage, void Function(bool, AuthPageState), void>(
|
||||
(onAuthFinished, _) => AuthPage(
|
||||
authViewModel: getIt.get<AuthViewModel>(),
|
||||
onAuthenticationFinished: onAuthFinished,
|
||||
closable: false));
|
||||
|
||||
getIt.registerFactory<ReceivePage>(() =>
|
||||
ReceivePage(addressListViewModel: getIt.get<AddressListViewModel>()));
|
||||
getIt.registerFactory<DashboardPage>(
|
||||
() => DashboardPage(walletViewModel: getIt.get<DashboardViewModel>()));
|
||||
|
||||
getIt.registerFactoryParam<AddressEditOrCreateViewModel, dynamic, void>(
|
||||
(dynamic item, _) => AddressEditOrCreateViewModel(
|
||||
getIt.registerFactory<ReceivePage>(() => ReceivePage(
|
||||
addressListViewModel: getIt.get<WalletAddressListViewModel>()));
|
||||
|
||||
getIt.registerFactoryParam<WalletAddressEditOrCreateViewModel, dynamic, void>(
|
||||
(dynamic item, _) => WalletAddressEditOrCreateViewModel(
|
||||
wallet: getIt.get<AppStore>().wallet, item: item));
|
||||
|
||||
getIt.registerFactoryParam<AddressEditOrCreatePage, dynamic, void>(
|
||||
(dynamic item, _) => AddressEditOrCreatePage(
|
||||
addressEditOrCreateViewModel:
|
||||
getIt.get<AddressEditOrCreateViewModel>(param1: item)));
|
||||
getIt.get<WalletAddressEditOrCreateViewModel>(param1: item)));
|
||||
|
||||
final appStore = getIt.get<AppStore>();
|
||||
getIt.registerFactory<SendViewModel>(() => SendViewModel(
|
||||
getIt.get<AppStore>().wallet, getIt.get<AppStore>().settingsStore));
|
||||
|
||||
_onCurrentWalletChangeReaction ??=
|
||||
reaction((_) => appStore.wallet, (WalletBase wallet) async {
|
||||
print('Wallet name ${wallet.name}');
|
||||
await getIt
|
||||
.get<SharedPreferences>()
|
||||
.setString('current_wallet_name', wallet.name);
|
||||
getIt.registerFactory(
|
||||
() => SendPage(sendViewModel: getIt.get<SendViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => WalletListViewModel(
|
||||
walletInfoSource, getIt.get<AppStore>(), getIt.get<KeyService>()));
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
WalletListPage(walletListViewModel: getIt.get<WalletListViewModel>()));
|
||||
|
||||
getIt.registerFactory(() {
|
||||
final wallet = getIt.get<AppStore>().wallet;
|
||||
|
||||
if (wallet is MoneroWallet) {
|
||||
return MoneroAccountListViewModel(wallet);
|
||||
}
|
||||
|
||||
// FIXME: throw exception.
|
||||
return null;
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => MoneroAccountListPage(
|
||||
accountListViewModel: getIt.get<MoneroAccountListViewModel>()));
|
||||
|
||||
getIt.registerFactory(() {
|
||||
final wallet = getIt.get<AppStore>().wallet;
|
||||
|
||||
if (wallet is MoneroWallet) {
|
||||
return MoneroAccountEditOrCreateViewModel(wallet.accountList);
|
||||
}
|
||||
|
||||
// FIXME: throw exception.
|
||||
return null;
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => MoneroAccountEditOrCreatePage(
|
||||
moneroAccountCreationViewModel:
|
||||
getIt.get<MoneroAccountEditOrCreateViewModel>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => SettingsViewModel(getIt.get<AppStore>().settingsStore));
|
||||
|
||||
getIt.registerFactory(() => SettingsPage(getIt.get<SettingsViewModel>()));
|
||||
|
||||
getIt
|
||||
.registerFactory(() => WalletSeedViewModel(getIt.get<AppStore>().wallet));
|
||||
|
||||
getIt.registerFactoryParam<WalletSeedPage, VoidCallback, void>(
|
||||
(VoidCallback callback, _) => WalletSeedPage(
|
||||
getIt.get<WalletSeedViewModel>(),
|
||||
onCloseCallback: callback));
|
||||
|
||||
getIt
|
||||
.registerFactory(() => WalletKeysViewModel(getIt.get<AppStore>().wallet));
|
||||
|
||||
getIt.registerFactory(() => WalletKeysPage(getIt.get<WalletKeysViewModel>()));
|
||||
|
||||
getIt.registerFactoryParam<ContactViewModel, Contact, void>(
|
||||
(Contact contact, _) => ContactViewModel(
|
||||
getIt.get<ContactService>(), getIt.get<AppStore>().wallet,
|
||||
contact: contact));
|
||||
|
||||
getIt.registerFactory(() => ContactListViewModel(
|
||||
getIt.get<AppStore>().contactListStore, getIt.get<ContactService>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => ContactListPage(getIt.get<ContactListViewModel>()));
|
||||
|
||||
getIt.registerFactoryParam<ContactPage, Contact, void>((Contact contact, _) =>
|
||||
ContactPage(getIt.get<ContactViewModel>(param1: contact)));
|
||||
|
||||
getIt.registerFactory(() => NodeListViewModel(
|
||||
getIt.get<AppStore>().nodeListStore,
|
||||
nodeSource,
|
||||
getIt.get<AppStore>().wallet));
|
||||
|
||||
getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
NodeCreateOrEditViewModel(nodeSource, getIt.get<AppStore>().wallet));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>()));
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ class S implements WidgetsLocalizations {
|
|||
String get account => "Account";
|
||||
String get accounts => "Accounts";
|
||||
String get accounts_subaddresses => "Accounts and subaddresses";
|
||||
String get addresses => "Addresses";
|
||||
String get add => "Add";
|
||||
String get add_new_node => "Add new node";
|
||||
String get add_new_word => "Add new word";
|
||||
|
@ -121,6 +122,8 @@ class S implements WidgetsLocalizations {
|
|||
String get payment_id => "Payment ID: ";
|
||||
String get pending => " (pending)";
|
||||
String get pin_is_incorrect => "PIN is incorrect";
|
||||
String get placeholder_contacts => "Your contacts will be displayed here";
|
||||
String get placeholder_transactions => "Your transactions will be displayed here";
|
||||
String get please_make_selection => "Please make selection below to create or recover your wallet.";
|
||||
String get please_select => "Please select:";
|
||||
String get please_try_to_connect_to_another_node => "Please try to connect to another node";
|
||||
|
@ -392,6 +395,8 @@ class $de extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "Neue Wallet erstellen";
|
||||
@override
|
||||
String get placeholder_contacts => "Ihre Kontakte werden hier angezeigt";
|
||||
@override
|
||||
String get card_address => "Adresse:";
|
||||
@override
|
||||
String get seed_language_portuguese => "Portugiesisch";
|
||||
|
@ -462,6 +467,8 @@ class $de extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "Kontostand anzeigen als";
|
||||
@override
|
||||
String get placeholder_transactions => "Ihre Transaktionen werden hier angezeigt";
|
||||
@override
|
||||
String get trade_details_provider => "Anbieter";
|
||||
@override
|
||||
String get seed_language_japanese => "Japanisch";
|
||||
|
@ -636,6 +643,8 @@ class $de extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "Konten und Unteradressen";
|
||||
@override
|
||||
String get addresses => "Addressen";
|
||||
@override
|
||||
String get wallet_name => "Walletname";
|
||||
@override
|
||||
String get error_text_payment_id => "Die Zahlungs-ID kann nur 16 bis 64 hexadezimale Zeichen enthalten";
|
||||
|
@ -1008,6 +1017,8 @@ class $hi extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "नया बटुआ बनाएँ";
|
||||
@override
|
||||
String get placeholder_contacts => "आपके संपर्क यहां प्रदर्शित होंगे";
|
||||
@override
|
||||
String get card_address => "पता:";
|
||||
@override
|
||||
String get seed_language_portuguese => "पुर्तगाली";
|
||||
|
@ -1078,6 +1089,8 @@ class $hi extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "के रूप में संतुलन प्रदर्शित करें";
|
||||
@override
|
||||
String get placeholder_transactions => "आपके लेनदेन यहां प्रदर्शित होंगे";
|
||||
@override
|
||||
String get trade_details_provider => "प्रदाता";
|
||||
@override
|
||||
String get seed_language_japanese => "जापानी";
|
||||
|
@ -1252,6 +1265,8 @@ class $hi extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "लेखा और उपदेस";
|
||||
@override
|
||||
String get addresses => "पतों";
|
||||
@override
|
||||
String get wallet_name => "बटुए का नाम";
|
||||
@override
|
||||
String get error_text_payment_id => "पेमेंट आईडी केवल हेक्स में 16 से 64 चार्ट तक हो सकती है";
|
||||
|
@ -1624,6 +1639,8 @@ class $ru extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "Создать новый кошелёк";
|
||||
@override
|
||||
String get placeholder_contacts => "Ваши контакты будут отображаться здесь";
|
||||
@override
|
||||
String get card_address => "Адрес:";
|
||||
@override
|
||||
String get seed_language_portuguese => "Португальский";
|
||||
|
@ -1694,6 +1711,8 @@ class $ru extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "Отображать баланс как";
|
||||
@override
|
||||
String get placeholder_transactions => "Ваши транзакции будут отображаться здесь";
|
||||
@override
|
||||
String get trade_details_provider => "Провайдер";
|
||||
@override
|
||||
String get seed_language_japanese => "Японский";
|
||||
|
@ -1868,6 +1887,8 @@ class $ru extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "Аккаунты и субадреса";
|
||||
@override
|
||||
String get addresses => "Адреса";
|
||||
@override
|
||||
String get wallet_name => "Имя кошелька";
|
||||
@override
|
||||
String get error_text_payment_id => "Идентификатор платежа может содержать от 16 до 64 символов в hex";
|
||||
|
@ -2240,6 +2261,8 @@ class $ko extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "새 월렛 만들기";
|
||||
@override
|
||||
String get placeholder_contacts => "연락처가 여기에 표시됩니다";
|
||||
@override
|
||||
String get card_address => "주소:";
|
||||
@override
|
||||
String get seed_language_portuguese => "포르투갈 인";
|
||||
|
@ -2310,6 +2333,8 @@ class $ko extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "잔액 표시";
|
||||
@override
|
||||
String get placeholder_transactions => "거래가 여기에 표시됩니다";
|
||||
@override
|
||||
String get trade_details_provider => "공급자";
|
||||
@override
|
||||
String get seed_language_japanese => "일본어";
|
||||
|
@ -2484,6 +2509,8 @@ class $ko extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "계정 및 하위 주소";
|
||||
@override
|
||||
String get addresses => "구애";
|
||||
@override
|
||||
String get wallet_name => "지갑 이름";
|
||||
@override
|
||||
String get error_text_payment_id => "지불 ID는 16 ~ 64 자의 16 진 문자 만 포함 할 수 있습니다";
|
||||
|
@ -2856,6 +2883,8 @@ class $pt extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "Criar nova carteira";
|
||||
@override
|
||||
String get placeholder_contacts => "Seus contatos serão exibidos aqui";
|
||||
@override
|
||||
String get card_address => "Endereço:";
|
||||
@override
|
||||
String get seed_language_portuguese => "Português";
|
||||
|
@ -2926,6 +2955,8 @@ class $pt extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "Saldo a exibir";
|
||||
@override
|
||||
String get placeholder_transactions => "Suas transações serão exibidas aqui";
|
||||
@override
|
||||
String get trade_details_provider => "Provedor";
|
||||
@override
|
||||
String get seed_language_japanese => "Japonês";
|
||||
|
@ -3100,6 +3131,8 @@ class $pt extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "Contas e sub-endereços";
|
||||
@override
|
||||
String get addresses => "Endereços";
|
||||
@override
|
||||
String get wallet_name => "Nome da carteira";
|
||||
@override
|
||||
String get error_text_payment_id => "O ID de pagamento pode conter apenas de 16 a 64 caracteres em hexadecimal";
|
||||
|
@ -3472,6 +3505,8 @@ class $uk extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "Створити новий гаманець";
|
||||
@override
|
||||
String get placeholder_contacts => "Тут будуть показані ваші контакти";
|
||||
@override
|
||||
String get card_address => "Адреса:";
|
||||
@override
|
||||
String get seed_language_portuguese => "Португальська";
|
||||
|
@ -3542,6 +3577,8 @@ class $uk extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "Відображати баланс як";
|
||||
@override
|
||||
String get placeholder_transactions => "Тут відображатимуться ваші транзакції";
|
||||
@override
|
||||
String get trade_details_provider => "Провайдер";
|
||||
@override
|
||||
String get seed_language_japanese => "Японська";
|
||||
|
@ -3716,6 +3753,8 @@ class $uk extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "Акаунти та субадреси";
|
||||
@override
|
||||
String get addresses => "Адреси";
|
||||
@override
|
||||
String get wallet_name => "Ім'я гаманця";
|
||||
@override
|
||||
String get error_text_payment_id => "Ідентифікатор платежу може містити від 16 до 64 символів в hex";
|
||||
|
@ -4088,6 +4127,8 @@ class $ja extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "新しいウォレットを作成";
|
||||
@override
|
||||
String get placeholder_contacts => "連絡先はここに表示されます";
|
||||
@override
|
||||
String get card_address => "住所:";
|
||||
@override
|
||||
String get seed_language_portuguese => "ポルトガル語";
|
||||
|
@ -4158,6 +4199,8 @@ class $ja extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "残高を表示";
|
||||
@override
|
||||
String get placeholder_transactions => "あなたの取引はここに表示されます";
|
||||
@override
|
||||
String get trade_details_provider => "プロバイダー";
|
||||
@override
|
||||
String get seed_language_japanese => "日本語";
|
||||
|
@ -4332,6 +4375,8 @@ class $ja extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "アカウントとサブアドレス";
|
||||
@override
|
||||
String get addresses => "アドレス";
|
||||
@override
|
||||
String get wallet_name => "ウォレット名";
|
||||
@override
|
||||
String get error_text_payment_id => "支払いIDには、16進数で16〜64文字しか含めることができません";
|
||||
|
@ -4708,6 +4753,8 @@ class $pl extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "Utwórz nowy portfel";
|
||||
@override
|
||||
String get placeholder_contacts => "Twoje kontakty zostaną wyświetlone tutaj";
|
||||
@override
|
||||
String get card_address => "Adres:";
|
||||
@override
|
||||
String get seed_language_portuguese => "Portugalski";
|
||||
|
@ -4778,6 +4825,8 @@ class $pl extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "Wyświetl saldo jako";
|
||||
@override
|
||||
String get placeholder_transactions => "Twoje transakcje zostaną wyświetlone tutaj";
|
||||
@override
|
||||
String get trade_details_provider => "Dostawca";
|
||||
@override
|
||||
String get seed_language_japanese => "Japoński";
|
||||
|
@ -4952,6 +5001,8 @@ class $pl extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "Konta i podadresy";
|
||||
@override
|
||||
String get addresses => "Adresy";
|
||||
@override
|
||||
String get wallet_name => "Nazwa portfela";
|
||||
@override
|
||||
String get error_text_payment_id => "ID może zawierać od 16 do 64 znaków w formacie szesnastkowym";
|
||||
|
@ -5324,6 +5375,8 @@ class $es extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "Crear nueva billetera";
|
||||
@override
|
||||
String get placeholder_contacts => "Tus contactos se mostrarán aquí";
|
||||
@override
|
||||
String get card_address => "Dirección:";
|
||||
@override
|
||||
String get seed_language_portuguese => "Portugués";
|
||||
|
@ -5394,6 +5447,8 @@ class $es extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "Mostrar saldo como";
|
||||
@override
|
||||
String get placeholder_transactions => "Sus transacciones se mostrarán aquí";
|
||||
@override
|
||||
String get trade_details_provider => "Proveedor";
|
||||
@override
|
||||
String get seed_language_japanese => "Japonés";
|
||||
|
@ -5568,6 +5623,8 @@ class $es extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "Cuentas y subdirecciones";
|
||||
@override
|
||||
String get addresses => "Direcciones";
|
||||
@override
|
||||
String get wallet_name => "Nombre de la billetera";
|
||||
@override
|
||||
String get error_text_payment_id => "La ID de pago solo puede contener de 16 a 64 caracteres en hexadecimal";
|
||||
|
@ -5940,6 +5997,8 @@ class $nl extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "Maak een nieuwe portemonnee";
|
||||
@override
|
||||
String get placeholder_contacts => "Je contacten worden hier weergegeven";
|
||||
@override
|
||||
String get card_address => "Adres:";
|
||||
@override
|
||||
String get seed_language_portuguese => "Portugees";
|
||||
|
@ -6010,6 +6069,8 @@ class $nl extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "Toon saldo als";
|
||||
@override
|
||||
String get placeholder_transactions => "Uw transacties worden hier weergegeven";
|
||||
@override
|
||||
String get trade_details_provider => "Leverancier";
|
||||
@override
|
||||
String get seed_language_japanese => "Japans";
|
||||
|
@ -6184,6 +6245,8 @@ class $nl extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "Accounts en subadressen";
|
||||
@override
|
||||
String get addresses => "Adressen";
|
||||
@override
|
||||
String get wallet_name => "Portemonnee naam";
|
||||
@override
|
||||
String get error_text_payment_id => "Betalings-ID kan alleen 16 tot 64 tekens bevatten in hexadecimale volgorde";
|
||||
|
@ -6556,6 +6619,8 @@ class $zh extends S {
|
|||
@override
|
||||
String get wallet_list_create_new_wallet => "创建新钱包";
|
||||
@override
|
||||
String get placeholder_contacts => "您的聯繫人將顯示在這裡";
|
||||
@override
|
||||
String get card_address => "地址:";
|
||||
@override
|
||||
String get seed_language_portuguese => "葡萄牙語";
|
||||
|
@ -6626,6 +6691,8 @@ class $zh extends S {
|
|||
@override
|
||||
String get settings_display_balance_as => "将余额显示为";
|
||||
@override
|
||||
String get placeholder_transactions => "您的交易將顯示在這裡";
|
||||
@override
|
||||
String get trade_details_provider => "提供者";
|
||||
@override
|
||||
String get seed_language_japanese => "日本";
|
||||
|
@ -6800,6 +6867,8 @@ class $zh extends S {
|
|||
@override
|
||||
String get accounts_subaddresses => "帳戶和子地址";
|
||||
@override
|
||||
String get addresses => "地址";
|
||||
@override
|
||||
String get wallet_name => "钱包名称";
|
||||
@override
|
||||
String get error_text_payment_id => "付款ID只能包含16到64个字符(十六进制)";
|
||||
|
|
|
@ -54,8 +54,6 @@ import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
|
|||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setup();
|
||||
|
||||
final appDir = await getApplicationDocumentsDirectory();
|
||||
Hive.init(appDir.path);
|
||||
Hive.registerAdapter(ContactAdapter());
|
||||
|
@ -87,6 +85,13 @@ void main() async {
|
|||
final exchangeTemplates =
|
||||
await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
|
||||
|
||||
await initialSetup(
|
||||
sharedPreferences: await SharedPreferences.getInstance(),
|
||||
nodes: nodes,
|
||||
walletInfoSource: walletInfoSource,
|
||||
contactSource: contacts,
|
||||
initialMigrationVersion: 3);
|
||||
|
||||
final sharedPreferences = await SharedPreferences.getInstance();
|
||||
final walletService = WalletService();
|
||||
final walletListService = WalletListService(
|
||||
|
@ -96,15 +101,6 @@ void main() async {
|
|||
sharedPreferences: sharedPreferences);
|
||||
final userService = UserService(
|
||||
sharedPreferences: sharedPreferences, secureStorage: secureStorage);
|
||||
// final authenticationStore = AuthenticationStore(userService: userService);
|
||||
|
||||
await initialSetup(
|
||||
sharedPreferences: sharedPreferences,
|
||||
walletListService: walletListService,
|
||||
nodes: nodes,
|
||||
// authStore: authenticationStore,
|
||||
initialMigrationVersion: 2);
|
||||
|
||||
final settingsStore = await SettingsStoreBase.load(
|
||||
nodes: nodes,
|
||||
sharedPreferences: sharedPreferences,
|
||||
|
@ -129,7 +125,6 @@ void main() async {
|
|||
final walletCreationService = WalletCreationService();
|
||||
final authService = AuthService();
|
||||
|
||||
|
||||
setReactions(
|
||||
settingsStore: settingsStore,
|
||||
priceStore: priceStore,
|
||||
|
@ -164,23 +159,20 @@ void main() async {
|
|||
}
|
||||
|
||||
Future<void> initialSetup(
|
||||
{WalletListService walletListService,
|
||||
SharedPreferences sharedPreferences,
|
||||
Box<Node> nodes,
|
||||
// AuthenticationStore authStore,
|
||||
int initialMigrationVersion = 1,
|
||||
WalletType initialWalletType = WalletType.bitcoin}) async {
|
||||
await walletListService.changeWalletManger(walletType: initialWalletType);
|
||||
{@required SharedPreferences sharedPreferences,
|
||||
@required Box<Node> nodes,
|
||||
@required Box<WalletInfo> walletInfoSource,
|
||||
@required Box<Contact> contactSource,
|
||||
int initialMigrationVersion = 3}) async {
|
||||
await defaultSettingsMigration(
|
||||
version: initialMigrationVersion,
|
||||
sharedPreferences: sharedPreferences,
|
||||
nodes: nodes);
|
||||
// await authStore.started();
|
||||
await setup(
|
||||
walletInfoSource: walletInfoSource,
|
||||
nodeSource: nodes,
|
||||
contactSource: contactSource);
|
||||
await bootstrap();
|
||||
// final authenticationStore = getIt.get<AuthenticationStore>();
|
||||
// FIXME
|
||||
// authenticationStore.state = AuthenticationState.denied;
|
||||
|
||||
monero_wallet.onStartup();
|
||||
}
|
||||
|
||||
|
@ -216,8 +208,6 @@ class MaterialAppWithTheme extends StatelessWidget {
|
|||
final syncStore = Provider.of<SyncStore>(context);
|
||||
final balanceStore = Provider.of<BalanceStore>(context);
|
||||
final theme = Provider.of<ThemeChanger>(context);
|
||||
final statusBarColor =
|
||||
settingsStore.isDarkTheme ? Colors.black : Colors.white;
|
||||
final currentLanguage = Provider.of<Language>(context);
|
||||
final contacts = Provider.of<Box<Contact>>(context);
|
||||
final nodes = Provider.of<Box<Node>>(context);
|
||||
|
@ -225,8 +215,17 @@ class MaterialAppWithTheme extends StatelessWidget {
|
|||
final transactionDescriptions =
|
||||
Provider.of<Box<TransactionDescription>>(context);
|
||||
|
||||
SystemChrome.setSystemUIOverlayStyle(
|
||||
SystemUiOverlayStyle(statusBarColor: statusBarColor));
|
||||
final statusBarColor =
|
||||
settingsStore.isDarkTheme ? Colors.black : Colors.white;
|
||||
final statusBarBrightness =
|
||||
settingsStore.isDarkTheme ? Brightness.light : Brightness.dark;
|
||||
final statusBarIconBrightness =
|
||||
settingsStore.isDarkTheme ? Brightness.light : Brightness.dark;
|
||||
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||
statusBarColor: statusBarColor,
|
||||
statusBarBrightness: statusBarBrightness,
|
||||
statusBarIconBrightness: statusBarIconBrightness));
|
||||
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
|
@ -258,4 +257,4 @@ class MaterialAppWithTheme extends StatelessWidget {
|
|||
authenticationStore: getIt.get<AuthenticationStore>(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
76
lib/monero/monero_account_list.dart
Normal file
|
@ -0,0 +1,76 @@
|
|||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/account.dart';
|
||||
import 'package:cw_monero/account_list.dart' as account_list;
|
||||
|
||||
part 'monero_account_list.g.dart';
|
||||
|
||||
class MoneroAccountList = MoneroAccountListBase with _$MoneroAccountList;
|
||||
|
||||
abstract class MoneroAccountListBase with Store {
|
||||
MoneroAccountListBase()
|
||||
: accounts = ObservableList<Account>(),
|
||||
_isRefreshing = false,
|
||||
_isUpdating = false {
|
||||
refresh();
|
||||
print(account_list.accountSizeNative());
|
||||
}
|
||||
|
||||
@observable
|
||||
ObservableList<Account> accounts;
|
||||
bool _isRefreshing;
|
||||
bool _isUpdating;
|
||||
|
||||
Future update() async {
|
||||
if (_isUpdating) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isUpdating = true;
|
||||
refresh();
|
||||
final accounts = getAll();
|
||||
|
||||
if (accounts.isNotEmpty) {
|
||||
this.accounts.clear();
|
||||
this.accounts.addAll(accounts);
|
||||
}
|
||||
|
||||
_isUpdating = false;
|
||||
} catch (e) {
|
||||
_isUpdating = false;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
List<Account> getAll() => account_list
|
||||
.getAllAccount()
|
||||
.map((accountRow) => Account.fromRow(accountRow))
|
||||
.toList();
|
||||
|
||||
Future addAccount({String label}) async {
|
||||
await account_list.addAccount(label: label);
|
||||
await update();
|
||||
}
|
||||
|
||||
Future setLabelAccount({int accountIndex, String label}) async {
|
||||
await account_list.setLabelForAccount(
|
||||
accountIndex: accountIndex, label: label);
|
||||
await update();
|
||||
}
|
||||
|
||||
void refresh() {
|
||||
if (_isRefreshing) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isRefreshing = true;
|
||||
account_list.refreshAccounts();
|
||||
_isRefreshing = false;
|
||||
} catch (e) {
|
||||
_isRefreshing = false;
|
||||
print(e);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +1,21 @@
|
|||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_monero/wallet.dart';
|
||||
import 'package:cw_monero/wallet.dart' as monero_wallet;
|
||||
import 'package:cake_wallet/monero/monero_wallet_keys.dart';
|
||||
import 'package:cake_wallet/monero/monero_balance.dart';
|
||||
import 'package:cake_wallet/monero/monero_transaction_history.dart';
|
||||
import 'package:cake_wallet/monero/monero_subaddress_list.dart';
|
||||
import 'package:cake_wallet/monero/monero_account_list.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/core/transaction_history.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/account.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/account_list.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/subaddress.dart';
|
||||
import 'package:cw_monero/wallet.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cw_monero/wallet.dart' as monero_wallet;
|
||||
|
||||
part 'monero_wallet.g.dart';
|
||||
|
||||
|
@ -20,15 +23,26 @@ class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
|
|||
|
||||
abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
||||
MoneroWalletBase({String filename, this.isRecovery = false})
|
||||
: transactionHistory = MoneroTransactionHistory() {
|
||||
: transactionHistory = MoneroTransactionHistory(),
|
||||
accountList = MoneroAccountList(),
|
||||
subaddressList = MoneroSubaddressList() {
|
||||
_filename = filename;
|
||||
accountList = AccountList();
|
||||
subaddressList = MoneroSubaddressList();
|
||||
balance = MoneroBalance(
|
||||
fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
|
||||
unlockedBalance: monero_wallet.getFullBalance(accountIndex: 0));
|
||||
currency = CryptoCurrency.xmr;
|
||||
type = WalletType.monero;
|
||||
_rct = reaction(
|
||||
(_) => syncStatus, (SyncStatus status) => print(status.toString()));
|
||||
_onAccountChangeReaction = reaction((_) => account, (Account account) {
|
||||
subaddressList.update(accountIndex: account.id);
|
||||
subaddress = subaddressList.subaddresses.first;
|
||||
address = subaddress.address;
|
||||
});
|
||||
}
|
||||
|
||||
ReactionDisposer _rct;
|
||||
|
||||
@override
|
||||
final MoneroTransactionHistory transactionHistory;
|
||||
|
||||
|
@ -44,26 +58,33 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
|||
@override
|
||||
String get name => _filename.split('/').last;
|
||||
|
||||
@override
|
||||
final type = WalletType.monero;
|
||||
|
||||
@override
|
||||
@observable
|
||||
String address;
|
||||
|
||||
@override
|
||||
String get seed => monero_wallet.getSeed();
|
||||
|
||||
@override
|
||||
MoneroWalletKeys get keys => MoneroWalletKeys(
|
||||
privateSpendKey: monero_wallet.getSecretSpendKey(),
|
||||
privateViewKey: monero_wallet.getSecretViewKey(),
|
||||
publicSpendKey: monero_wallet.getPublicSpendKey(),
|
||||
publicViewKey: monero_wallet.getPublicViewKey());
|
||||
|
||||
final MoneroSubaddressList subaddressList;
|
||||
|
||||
final MoneroAccountList accountList;
|
||||
|
||||
bool isRecovery;
|
||||
|
||||
MoneroSubaddressList subaddressList;
|
||||
|
||||
AccountList accountList;
|
||||
|
||||
String _filename;
|
||||
|
||||
SyncListner _listener;
|
||||
ReactionDisposer _onAccountChangeReaction;
|
||||
|
||||
Future<void> init() async {
|
||||
await accountList.update();
|
||||
account = accountList.getAll().first;
|
||||
account = accountList.accounts.first;
|
||||
subaddressList.update(accountIndex: account.id ?? 0);
|
||||
subaddress = subaddressList.getAll().first;
|
||||
balance = MoneroBalance(
|
||||
|
@ -76,10 +97,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
|||
|
||||
void close() {
|
||||
_listener?.stop();
|
||||
_onAccountChangeReaction?.reaction?.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> connectToNode({@required Node node}) async {
|
||||
final node = Node(uri: 'xmr-node-uk.cakewallet.com:18081');
|
||||
|
||||
try {
|
||||
syncStatus = ConnectingSyncStatus();
|
||||
await monero_wallet.setupNode(
|
||||
|
@ -147,6 +171,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
|||
_listener?.stop();
|
||||
_listener = monero_wallet.setListeners(
|
||||
_onNewBlock, _onNeedToRefresh, _onNewTransaction);
|
||||
_listener.start();
|
||||
}
|
||||
|
||||
void _askForUpdateBalance() {
|
||||
|
|
12
lib/monero/monero_wallet_keys.dart
Normal file
|
@ -0,0 +1,12 @@
|
|||
class MoneroWalletKeys {
|
||||
const MoneroWalletKeys(
|
||||
{this.privateSpendKey,
|
||||
this.privateViewKey,
|
||||
this.publicSpendKey,
|
||||
this.publicViewKey});
|
||||
|
||||
final String publicViewKey;
|
||||
final String privateViewKey;
|
||||
final String publicSpendKey;
|
||||
final String privateSpendKey;
|
||||
}
|
|
@ -12,7 +12,9 @@ class Palette {
|
|||
static const Color blue = Color.fromRGBO(88, 143, 252, 1.0);
|
||||
static const Color darkLavender = Color.fromRGBO(225, 238, 250, 1.0);
|
||||
static const Color nightBlue = Color.fromRGBO(46, 57, 96, 1.0);
|
||||
// FIXME: Rename.
|
||||
static const Color eee = Color.fromRGBO(236, 239, 245, 1.0);
|
||||
static const Color xxx = Color.fromRGBO(72, 89, 109, 1);
|
||||
}
|
||||
|
||||
class PaletteDark {
|
||||
|
@ -28,4 +30,7 @@ class PaletteDark {
|
|||
static const Color headerNightBlue = Color.fromRGBO(41, 52, 84, 1.0); // menuHeader
|
||||
static const Color lightNightBlue = Color.fromRGBO(48, 59, 95, 1.0); // menuList
|
||||
static const Color moderatePurpleBlue = Color.fromRGBO(57, 74, 95, 1.0); // selectButtonText
|
||||
// FIXME: Rename.
|
||||
static const Color eee = Color.fromRGBO(236, 239, 245, 1.0);
|
||||
static const Color xxx = Color.fromRGBO(72, 89, 109, 1);
|
||||
}
|
|
@ -1,9 +1,13 @@
|
|||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
|
||||
import 'package:cake_wallet/monero/monero_wallet_service.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/core/wallet_service.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/authentication_store.dart';
|
||||
|
@ -11,22 +15,15 @@ import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
|||
import 'package:cake_wallet/src/domain/common/secret_store_key.dart';
|
||||
import 'package:cake_wallet/src/domain/common/encrypt.dart';
|
||||
|
||||
// FIXME: move me
|
||||
Future<String> getWalletPassword({String walletName}) async {
|
||||
final secureStorage = getIt.get<FlutterSecureStorage>();
|
||||
final key = generateStoreKeyFor(
|
||||
key: SecretStoreKey.moneroWalletPassword, walletName: walletName);
|
||||
final encodedPassword = await secureStorage.read(key: key);
|
||||
|
||||
return decodeWalletPassword(password: encodedPassword);
|
||||
}
|
||||
|
||||
// FIXME: move me
|
||||
Future<void> loadCurrentWallet() async {
|
||||
final appStore = getIt.get<AppStore>();
|
||||
final name = getIt.get<SharedPreferences>().getString('current_wallet_name');
|
||||
final type = WalletType.monero; // FIXME
|
||||
final password = await getWalletPassword(walletName: name);
|
||||
final typeRaw =
|
||||
getIt.get<SharedPreferences>().getInt('current_wallet_type') ?? 0;
|
||||
final type = deserializeFromInt(typeRaw);
|
||||
final password =
|
||||
await getIt.get<KeyService>().getWalletPassword(walletName: name);
|
||||
|
||||
WalletService _service;
|
||||
switch (type) {
|
||||
|
@ -45,6 +42,8 @@ Future<void> loadCurrentWallet() async {
|
|||
}
|
||||
|
||||
ReactionDisposer _initialAuthReaction;
|
||||
ReactionDisposer _onCurrentWalletChangeReaction;
|
||||
ReactionDisposer _onWalletSyncStatusChangeReaction;
|
||||
|
||||
Future<void> bootstrap() async {
|
||||
final authenticationStore = getIt.get<AuthenticationStore>();
|
||||
|
@ -63,4 +62,24 @@ Future<void> bootstrap() async {
|
|||
await loadCurrentWallet();
|
||||
}
|
||||
});
|
||||
|
||||
_onCurrentWalletChangeReaction ??=
|
||||
reaction((_) => getIt.get<AppStore>().wallet, (WalletBase wallet) async {
|
||||
print('Wallet name ${wallet.name}');
|
||||
|
||||
_onWalletSyncStatusChangeReaction?.reaction?.dispose();
|
||||
_onWalletSyncStatusChangeReaction = when(
|
||||
(_) => wallet.syncStatus is ConnectedSyncStatus,
|
||||
() async => await wallet.startSync());
|
||||
|
||||
await getIt
|
||||
.get<SharedPreferences>()
|
||||
.setString('current_wallet_name', wallet.name);
|
||||
|
||||
await getIt
|
||||
.get<SharedPreferences>()
|
||||
.setInt('current_wallet_type', serializeToInt(wallet.type));
|
||||
|
||||
await wallet.connectToNode(node: null);
|
||||
});
|
||||
}
|
||||
|
|
135
lib/router.dart
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
@ -55,7 +56,7 @@ import 'package:cake_wallet/src/stores/price/price_store.dart';
|
|||
// MARK: Import screens
|
||||
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/new_node_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/nodes_list_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';
|
||||
|
@ -70,10 +71,10 @@ import 'package:cake_wallet/src/screens/send/send_page.dart';
|
|||
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
|
||||
import 'package:cake_wallet/src/screens/seed_language/seed_language_page.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
|
||||
import 'package:cake_wallet/src/screens/accounts/account_page.dart';
|
||||
import 'package:cake_wallet/src/screens/address_book/address_book_page.dart';
|
||||
import 'package:cake_wallet/src/screens/address_book/contact_page.dart';
|
||||
import 'package:cake_wallet/src/screens/show_keys/show_keys_page.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
|
||||
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
|
||||
import 'package:cake_wallet/src/screens/subaddress/subaddress_list_page.dart';
|
||||
|
@ -86,7 +87,6 @@ import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
|||
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/create_unlock_page.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/create_login_page.dart';
|
||||
import 'package:cake_wallet/src/screens/seed/create_seed_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/create_dashboard_page.dart';
|
||||
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
|
||||
|
@ -214,10 +214,8 @@ class Router {
|
|||
|
||||
case Routes.seed:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => createSeedPage(
|
||||
settingsStore: settingsStore,
|
||||
walletService: walletService,
|
||||
callback: settings.arguments as void Function()));
|
||||
builder: (_) => getIt.get<WalletSeedPage>(
|
||||
param1: settings.arguments as VoidCallback));
|
||||
|
||||
case Routes.restoreWalletFromSeed:
|
||||
final args = settings.arguments as List<dynamic>;
|
||||
|
@ -228,13 +226,7 @@ class Router {
|
|||
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) =>
|
||||
ProxyProvider<AuthenticationStore, WalletRestorationStore>(
|
||||
update: (_, authStore, __) => WalletRestorationStore(
|
||||
authStore: authStore,
|
||||
sharedPreferences: sharedPreferences,
|
||||
walletListService: walletListService),
|
||||
child: RestoreWalletFromSeedPage(
|
||||
type: type, language: language)));
|
||||
RestoreWalletFromSeedPage(type: type, language: language));
|
||||
|
||||
case Routes.restoreWalletFromKeys:
|
||||
final args = settings.arguments as List<dynamic>;
|
||||
|
@ -267,23 +259,7 @@ class Router {
|
|||
|
||||
case Routes.send:
|
||||
return CupertinoPageRoute<void>(
|
||||
fullscreenDialog: true,
|
||||
builder: (_) => MultiProvider(providers: [
|
||||
ProxyProvider<SettingsStore, BalanceStore>(
|
||||
update: (_, settingsStore, __) => BalanceStore(
|
||||
walletService: walletService,
|
||||
settingsStore: settingsStore,
|
||||
priceStore: priceStore),
|
||||
),
|
||||
Provider(
|
||||
create: (_) => SyncStore(walletService: walletService),
|
||||
),
|
||||
Provider(
|
||||
create: (_) => SendStore(
|
||||
walletService: walletService,
|
||||
priceStore: priceStore,
|
||||
transactionDescriptions: transactionDescriptions)),
|
||||
], child: SendPage()));
|
||||
fullscreenDialog: true, builder: (_) => getIt.get<SendPage>());
|
||||
|
||||
case Routes.sendTemplate:
|
||||
return CupertinoPageRoute<void>(
|
||||
|
@ -330,25 +306,13 @@ class Router {
|
|||
case Routes.walletList:
|
||||
return MaterialPageRoute<void>(
|
||||
fullscreenDialog: true,
|
||||
builder: (_) => Provider(
|
||||
create: (_) => WalletListStore(
|
||||
walletListService: walletListService,
|
||||
walletService: walletService),
|
||||
child: WalletListPage()));
|
||||
builder: (_) => getIt.get<WalletListPage>());
|
||||
|
||||
case Routes.auth:
|
||||
return null;
|
||||
// return MaterialPageRoute<void>(
|
||||
// fullscreenDialog: true,
|
||||
// builder: (_) => Provider(
|
||||
// create: (_) => AuthStore(
|
||||
// sharedPreferences: sharedPreferences,
|
||||
// userService: userService,
|
||||
// walletService: walletService),
|
||||
// child: AuthPage(
|
||||
// onAuthenticationFinished:
|
||||
// settings.arguments as OnAuthenticationFinished),
|
||||
// ));
|
||||
return MaterialPageRoute<void>(
|
||||
fullscreenDialog: true,
|
||||
builder: (_) => getIt.get<AuthPage>(
|
||||
param1: settings.arguments as OnAuthenticationFinished));
|
||||
|
||||
case Routes.unlock:
|
||||
return MaterialPageRoute<void>(
|
||||
|
@ -361,17 +325,12 @@ class Router {
|
|||
settings.arguments as OnAuthenticationFinished));
|
||||
|
||||
case Routes.nodeList:
|
||||
return CupertinoPageRoute<void>(builder: (context) {
|
||||
return Provider(
|
||||
create: (_) => NodeListStore(nodesSource: nodes),
|
||||
child: NodeListPage());
|
||||
});
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => getIt.get<NodeListPage>());
|
||||
|
||||
case Routes.newNode:
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => Provider<NodeListStore>(
|
||||
create: (_) => NodeListStore(nodesSource: nodes),
|
||||
child: NewNodePage()));
|
||||
builder: (_) => getIt.get<NodeCreateOrEditPage>());
|
||||
|
||||
case Routes.login:
|
||||
return CupertinoPageRoute<void>(builder: (context) {
|
||||
|
@ -386,59 +345,25 @@ class Router {
|
|||
});
|
||||
|
||||
case Routes.accountCreation:
|
||||
return CupertinoPageRoute<String>(builder: (context) {
|
||||
return Provider(
|
||||
create: (_) => AccountListStore(walletService: walletService),
|
||||
child: AccountPage(account: settings.arguments as Account));
|
||||
});
|
||||
return CupertinoPageRoute<String>(
|
||||
builder: (_) => getIt.get<MoneroAccountEditOrCreatePage>());
|
||||
|
||||
case Routes.addressBook:
|
||||
return MaterialPageRoute<void>(builder: (context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
Provider(
|
||||
create: (_) =>
|
||||
AccountListStore(walletService: walletService)),
|
||||
Provider(create: (_) => AddressBookStore(contacts: contacts))
|
||||
],
|
||||
child: AddressBookPage(),
|
||||
);
|
||||
});
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => getIt.get<ContactListPage>());
|
||||
|
||||
case Routes.pickerAddressBook:
|
||||
return MaterialPageRoute<void>(builder: (context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
Provider(
|
||||
create: (_) =>
|
||||
AccountListStore(walletService: walletService)),
|
||||
Provider(create: (_) => AddressBookStore(contacts: contacts))
|
||||
],
|
||||
child: AddressBookPage(isEditable: false),
|
||||
);
|
||||
});
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => getIt.get<ContactListPage>());
|
||||
|
||||
case Routes.addressBookAddContact:
|
||||
return CupertinoPageRoute<void>(builder: (context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
Provider(
|
||||
create: (_) =>
|
||||
AccountListStore(walletService: walletService)),
|
||||
Provider(create: (_) => AddressBookStore(contacts: contacts))
|
||||
],
|
||||
child: ContactPage(contact: settings.arguments as Contact),
|
||||
);
|
||||
});
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) =>
|
||||
getIt.get<ContactPage>(param1: settings.arguments as Contact));
|
||||
|
||||
case Routes.showKeys:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (context) {
|
||||
return Provider(
|
||||
create: (_) => WalletKeysStore(walletService: walletService),
|
||||
child: ShowKeysPage(),
|
||||
);
|
||||
},
|
||||
builder: (_) => getIt.get<WalletKeysPage>(),
|
||||
fullscreenDialog: true);
|
||||
|
||||
case Routes.exchangeTrade:
|
||||
|
@ -538,9 +463,7 @@ class Router {
|
|||
|
||||
case Routes.settings:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => Provider(
|
||||
create: (_) => NodeListStore(nodesSource: nodes),
|
||||
child: SettingsPage()));
|
||||
builder: (_) => getIt.get<SettingsPage>());
|
||||
|
||||
case Routes.rescan:
|
||||
return MaterialPageRoute<void>(
|
||||
|
|
|
@ -19,7 +19,7 @@ class Routes {
|
|||
static const disclaimer = '/disclaimer';
|
||||
static const readDisclaimer = '/read_disclaimer';
|
||||
static const seedLanguage = '/seed_language';
|
||||
static const walletList = '/wallet_list';
|
||||
static const walletList = '/view_model.wallet_list';
|
||||
static const auth = '/auth';
|
||||
static const nodeList = '/node_list';
|
||||
static const newNode = '/new_node_list';
|
||||
|
|
|
@ -3,4 +3,7 @@ import 'package:cake_wallet/src/domain/common/crypto_amount_format.dart';
|
|||
const bitcoinAmountDivider = 100000000;
|
||||
|
||||
double bitcoinAmountToDouble({int amount}) =>
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
|
||||
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
|
||||
|
||||
int doubleToBitcoinAmount(double amount) =>
|
||||
(amount * bitcoinAmountDivider).toInt();
|
||||
|
|
|
@ -22,6 +22,12 @@ class Contact extends HiveObject {
|
|||
|
||||
CryptoCurrency get type => CryptoCurrency.deserialize(raw: raw);
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) => o is Contact && o.key == key;
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode;
|
||||
|
||||
void updateCryptoCurrency({@required CryptoCurrency currency}) =>
|
||||
raw = currency.raw;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
@ -14,7 +16,6 @@ Future defaultSettingsMigration(
|
|||
final currentVersion =
|
||||
sharedPreferences.getInt('current_default_settings_migration_version') ??
|
||||
0;
|
||||
|
||||
if (currentVersion >= version) {
|
||||
return;
|
||||
}
|
||||
|
@ -28,10 +29,11 @@ Future defaultSettingsMigration(
|
|||
switch (version) {
|
||||
case 1:
|
||||
await sharedPreferences.setString(
|
||||
'current_fiat_currency', FiatCurrency.usd.toString());
|
||||
SettingsStoreBase.currentFiatCurrencyKey, FiatCurrency.usd.toString());
|
||||
await sharedPreferences.setInt(
|
||||
'current_fee_priority', TransactionPriority.standart.raw);
|
||||
await sharedPreferences.setInt('current_balance_display_mode',
|
||||
SettingsStoreBase.currentTransactionPriorityKey, TransactionPriority.standart.raw);
|
||||
await sharedPreferences.setInt(
|
||||
SettingsStoreBase.currentBalanceDisplayModeKey,
|
||||
BalanceDisplayMode.availableBalance.raw);
|
||||
await sharedPreferences.setBool('save_recipient_address', true);
|
||||
await resetToDefault(nodes);
|
||||
|
@ -44,6 +46,10 @@ Future defaultSettingsMigration(
|
|||
await replaceDefaultNode(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
|
||||
break;
|
||||
case 3:
|
||||
await updateNodeTypes(nodes: nodes);
|
||||
await addBitcoinElectrumServerList(nodes: nodes);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -87,9 +93,11 @@ Future<void> changeCurrentNodeToDefault(
|
|||
final timeZone = DateTime.now().timeZoneOffset.inHours;
|
||||
String nodeUri = '';
|
||||
|
||||
if (timeZone >= 1) { // Eurasia
|
||||
if (timeZone >= 1) {
|
||||
// Eurasia
|
||||
nodeUri = 'xmr-node-eu.cakewallet.com:18081';
|
||||
} else if (timeZone <= -4) { // America
|
||||
} else if (timeZone <= -4) {
|
||||
// America
|
||||
nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
|
||||
}
|
||||
|
||||
|
@ -121,3 +129,17 @@ Future<void> replaceDefaultNode(
|
|||
await changeCurrentNodeToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
}
|
||||
|
||||
Future<void> updateNodeTypes({@required Box<Node> nodes}) async {
|
||||
nodes.values.forEach((node) async {
|
||||
if (node.type == null) {
|
||||
node.type = WalletType.monero;
|
||||
await node.save();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> addBitcoinElectrumServerList({@required Box<Node> nodes}) async {
|
||||
final serverList = await loadElectrumServerList();
|
||||
await nodes.addAll(serverList);
|
||||
}
|
||||
|
|
|
@ -2,18 +2,22 @@ import 'package:flutter/foundation.dart';
|
|||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/domain/common/digest_request.dart';
|
||||
|
||||
part 'node.g.dart';
|
||||
|
||||
@HiveType(typeId: 1)
|
||||
class Node extends HiveObject {
|
||||
Node({@required this.uri, this.login, this.password});
|
||||
Node({@required this.uri, @required WalletType type, this.login, this.password}) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
Node.fromMap(Map map)
|
||||
: uri = (map['uri'] ?? '') as String,
|
||||
login = map['login'] as String,
|
||||
password = map['password'] as String;
|
||||
password = map['password'] as String,
|
||||
typeRaw = map['typeRaw'] as int;
|
||||
|
||||
static const boxName = 'Nodes';
|
||||
|
||||
|
@ -26,7 +30,29 @@ class Node extends HiveObject {
|
|||
@HiveField(2)
|
||||
String password;
|
||||
|
||||
Future<bool> requestNode(String uri, {String login, String password}) async {
|
||||
@HiveField(3)
|
||||
int typeRaw;
|
||||
|
||||
WalletType get type => deserializeFromInt(typeRaw);
|
||||
|
||||
set type(WalletType type) => typeRaw = serializeToInt(type);
|
||||
|
||||
Future<bool> requestNode() async {
|
||||
try {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return requestMoneroNode();
|
||||
case WalletType.bitcoin:
|
||||
return requestBitcoinElectrumServer();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> requestMoneroNode() async {
|
||||
Map<String, dynamic> resBody;
|
||||
|
||||
if (login != null && password != null) {
|
||||
|
@ -38,12 +64,17 @@ class Node extends HiveObject {
|
|||
final url = Uri.http(uri, '/json_rpc');
|
||||
final headers = {'Content-type': 'application/json'};
|
||||
final body =
|
||||
json.encode({"jsonrpc": "2.0", "id": "0", "method": "get_info"});
|
||||
json.encode({'jsonrpc': '2.0', 'id': '0', 'method': 'get_info'});
|
||||
final response =
|
||||
await http.post(url.toString(), headers: headers, body: body);
|
||||
resBody = json.decode(response.body) as Map<String, dynamic>;
|
||||
}
|
||||
|
||||
return !(resBody["result"]["offline"] as bool);
|
||||
return !(resBody['result']['offline'] as bool);
|
||||
}
|
||||
|
||||
Future<bool> requestBitcoinElectrumServer() async {
|
||||
// FIXME: IMPLEMENT ME
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:hive/hive.dart';
|
||||
import "package:yaml/yaml.dart";
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
|
||||
Future<List<Node>> loadDefaultNodes() async {
|
||||
final nodesRaw = await rootBundle.loadString('assets/node_list.yml');
|
||||
|
@ -16,15 +17,34 @@ Future<List<Node>> loadDefaultNodes() async {
|
|||
}).toList();
|
||||
}
|
||||
|
||||
Future<List<Node>> loadElectrumServerList() async {
|
||||
final serverListRaw =
|
||||
await rootBundle.loadString('assets/electrum_server_list.yml');
|
||||
final serverList = loadYaml(serverListRaw) as YamlList;
|
||||
|
||||
return serverList.map((dynamic raw) {
|
||||
if (raw is Map) {
|
||||
final node = Node.fromMap(raw);
|
||||
node?.type = WalletType.bitcoin;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future resetToDefault(Box<Node> nodeSource) async {
|
||||
final nodes = await loadDefaultNodes();
|
||||
final enteties = Map<int, Node>();
|
||||
final moneroNodes = await loadDefaultNodes();
|
||||
final bitcoinElectrumServerList = await loadElectrumServerList();
|
||||
final nodes = moneroNodes + bitcoinElectrumServerList;
|
||||
final entities = <int, Node>{};
|
||||
|
||||
await nodeSource.clear();
|
||||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
enteties[i] = nodes[i];
|
||||
entities[i] = nodes[i];
|
||||
}
|
||||
|
||||
await nodeSource.putAll(enteties);
|
||||
await nodeSource.putAll(entities);
|
||||
}
|
||||
|
|
|
@ -20,15 +20,19 @@ int serializeToInt(WalletType type) {
|
|||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return 0;
|
||||
case WalletType.bitcoin:
|
||||
return 1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
WalletType deserializeToInt(int raw) {
|
||||
WalletType deserializeFromInt(int raw) {
|
||||
switch (raw) {
|
||||
case 0:
|
||||
return WalletType.monero;
|
||||
case 1:
|
||||
return WalletType.bitcoin;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ void setReactions(
|
|||
@required SyncStore syncStore,
|
||||
@required WalletStore walletStore,
|
||||
@required WalletService walletService,
|
||||
@required AuthenticationStore authenticationStore,
|
||||
// @required AuthenticationStore authenticationStore,
|
||||
@required LoginStore loginStore}) {
|
||||
connectToNode(settingsStore: settingsStore, walletStore: walletStore);
|
||||
onSyncStatusChange(
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/account_list/account_list_store.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/account.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
|
||||
class AccountPage extends BasePage {
|
||||
AccountPage({this.account});
|
||||
|
||||
final Account account;
|
||||
|
||||
@override
|
||||
String get title => S.current.account;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => AccountForm(account);
|
||||
}
|
||||
|
||||
class AccountForm extends StatefulWidget {
|
||||
AccountForm(this.account);
|
||||
|
||||
final Account account;
|
||||
|
||||
@override
|
||||
AccountFormState createState() => AccountFormState();
|
||||
}
|
||||
|
||||
class AccountFormState extends State<AccountForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _textController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
if (widget.account != null) _textController.text = widget.account.label;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_textController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final accountListStore = Provider.of<AccountListStore>(context);
|
||||
|
||||
_textController.addListener(() {
|
||||
if (_textController.text.isNotEmpty) {
|
||||
accountListStore.setDisabledStatus(false);
|
||||
} else {
|
||||
accountListStore.setDisabledStatus(true);
|
||||
}
|
||||
});
|
||||
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: BaseTextFormField(
|
||||
controller: _textController,
|
||||
hintText: S.of(context).account,
|
||||
validator: (value) {
|
||||
accountListStore.validateAccountName(value);
|
||||
return accountListStore.errorMessage;
|
||||
},
|
||||
)
|
||||
)),
|
||||
Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (widget.account != null) {
|
||||
await accountListStore.renameAccount(
|
||||
index: widget.account.id,
|
||||
label: _textController.text);
|
||||
} else {
|
||||
await accountListStore.addAccount(
|
||||
label: _textController.text);
|
||||
}
|
||||
Navigator.of(context).pop(_textController.text);
|
||||
},
|
||||
text:
|
||||
widget.account != null ? S.of(context).rename : S.of(context).add,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isLoading: accountListStore.isAccountCreating,
|
||||
isDisabled: accountListStore.isDisabledStatus,
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,293 +0,0 @@
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/stores/address_book/address_book_store.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
|
||||
class AddressBookPage extends BasePage {
|
||||
AddressBookPage({this.isEditable = true});
|
||||
|
||||
final bool isEditable;
|
||||
|
||||
@override
|
||||
String get title => S.current.address_book;
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
if (!isEditable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final addressBookStore = Provider.of<AddressBookStore>(context);
|
||||
|
||||
return Container(
|
||||
width: 32.0,
|
||||
height: 32.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor
|
||||
),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
Icon(Icons.add,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
size: 22.0),
|
||||
ButtonTheme(
|
||||
minWidth: 32.0,
|
||||
height: 32.0,
|
||||
child: FlatButton(
|
||||
shape: CircleBorder(),
|
||||
onPressed: () async {
|
||||
await Navigator.of(context)
|
||||
.pushNamed(Routes.addressBookAddContact);
|
||||
await addressBookStore.updateContactList();
|
||||
},
|
||||
child: Offstage()),
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final addressBookStore = Provider.of<AddressBookStore>(context);
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||
child: Observer(
|
||||
builder: (_) => ListView.separated(
|
||||
separatorBuilder: (_, __) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: addressBookStore.contactList == null
|
||||
? 0
|
||||
: addressBookStore.contactList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final contact = addressBookStore.contactList[index];
|
||||
final image = _getCurrencyImage(contact.type);
|
||||
|
||||
final isDrawTop = index == 0 ? true : false;
|
||||
final isDrawBottom = index == addressBookStore.contactList.length - 1 ? true : false;
|
||||
|
||||
final content = GestureDetector(
|
||||
onTap: () async {
|
||||
if (!isEditable) {
|
||||
Navigator.of(context).pop(contact);
|
||||
return;
|
||||
}
|
||||
|
||||
final isCopied = await showNameAndAddressDialog(
|
||||
context, contact.name, contact.address);
|
||||
|
||||
if (isCopied != null && isCopied) {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: contact.address));
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard,
|
||||
style: TextStyle(
|
||||
color: Colors.white
|
||||
),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
isDrawTop
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image != null
|
||||
? image
|
||||
: Offstage(),
|
||||
Padding(
|
||||
padding: image != null
|
||||
? EdgeInsets.only(left: 12)
|
||||
: EdgeInsets.only(left: 0),
|
||||
child: Text(
|
||||
contact.name,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
),
|
||||
isDrawBottom
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return !isEditable
|
||||
? content
|
||||
: Slidable(
|
||||
key: Key('${contact.key}'),
|
||||
actionPane: SlidableDrawerActionPane(),
|
||||
child: content,
|
||||
secondaryActions: <Widget>[
|
||||
IconSlideAction(
|
||||
caption: S.of(context).edit,
|
||||
color: Colors.blue,
|
||||
icon: Icons.edit,
|
||||
onTap: () async {
|
||||
await Navigator.of(context).pushNamed(
|
||||
Routes.addressBookAddContact,
|
||||
arguments: contact);
|
||||
await addressBookStore.updateContactList();
|
||||
},
|
||||
),
|
||||
IconSlideAction(
|
||||
caption: S.of(context).delete,
|
||||
color: Colors.red,
|
||||
icon: CupertinoIcons.delete,
|
||||
onTap: () async {
|
||||
await showAlertDialog(context)
|
||||
.then((isDelete) async {
|
||||
if (isDelete != null && isDelete) {
|
||||
await addressBookStore.delete(
|
||||
contact: contact);
|
||||
await addressBookStore.updateContactList();
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
dismissal: SlidableDismissal(
|
||||
child: SlidableDrawerDismissal(),
|
||||
onDismissed: (actionType) async {
|
||||
await addressBookStore.delete(contact: contact);
|
||||
await addressBookStore.updateContactList();
|
||||
},
|
||||
onWillDismiss: (actionType) async {
|
||||
return await showAlertDialog(context);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
Image _getCurrencyImage(CryptoCurrency currency) {
|
||||
Image image;
|
||||
switch (currency) {
|
||||
case CryptoCurrency.xmr:
|
||||
image = Image.asset('assets/images/monero.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.ada:
|
||||
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.bch:
|
||||
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.bnb:
|
||||
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.btc:
|
||||
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.dash:
|
||||
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.eos:
|
||||
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.eth:
|
||||
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.ltc:
|
||||
image = Image.asset('assets/images/litecoin.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.nano:
|
||||
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.trx:
|
||||
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.usdt:
|
||||
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xlm:
|
||||
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xrp:
|
||||
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
|
||||
break;
|
||||
default:
|
||||
image = null;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
Future<bool> showAlertDialog(BuildContext context) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).address_remove_contact,
|
||||
alertContent: S.of(context).address_remove_content,
|
||||
leftButtonText: S.of(context).remove,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () => Navigator.of(context).pop(true),
|
||||
actionRightButton: () => Navigator.of(context).pop(false)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> showNameAndAddressDialog(
|
||||
BuildContext context, String name, String address) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: name,
|
||||
alertContent: address,
|
||||
leftButtonText: S.of(context).copy,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () => Navigator.of(context).pop(true),
|
||||
actionRightButton: () => Navigator.of(context).pop(false)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,234 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/contact.dart';
|
||||
import 'package:cake_wallet/src/stores/address_book/address_book_store.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
|
||||
class ContactPage extends BasePage {
|
||||
ContactPage({this.contact});
|
||||
|
||||
final Contact contact;
|
||||
|
||||
@override
|
||||
String get title => S.current.contact;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => ContactForm(contact);
|
||||
}
|
||||
|
||||
class ContactForm extends StatefulWidget {
|
||||
ContactForm(this.contact);
|
||||
|
||||
final Contact contact;
|
||||
|
||||
@override
|
||||
State<ContactForm> createState() => ContactFormState();
|
||||
}
|
||||
|
||||
class ContactFormState extends State<ContactForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _contactNameController = TextEditingController();
|
||||
final _currencyTypeController = TextEditingController();
|
||||
final _addressController = TextEditingController();
|
||||
final currencies = CryptoCurrency.all;
|
||||
|
||||
CryptoCurrency _selectectCrypto;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.contact != null) {
|
||||
_selectectCrypto = widget.contact.type;
|
||||
_contactNameController.text = widget.contact.name;
|
||||
_currencyTypeController.text = _selectectCrypto.toString();
|
||||
_addressController.text = widget.contact.address;
|
||||
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
|
||||
}
|
||||
}
|
||||
|
||||
void afterLayout(dynamic _) {
|
||||
final addressBookStore = Provider.of<AddressBookStore>(context);
|
||||
addressBookStore.setDisabledStatus(false);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_contactNameController.dispose();
|
||||
_currencyTypeController.dispose();
|
||||
_addressController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void onHandleControllers(AddressBookStore addressBookStore) {
|
||||
if (_contactNameController.text.isNotEmpty &&
|
||||
_addressController.text.isNotEmpty &&
|
||||
_currencyTypeController.text.isNotEmpty) {
|
||||
addressBookStore.setDisabledStatus(false);
|
||||
} else {
|
||||
addressBookStore.setDisabledStatus(true);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final addressBookStore = Provider.of<AddressBookStore>(context);
|
||||
final downArrow = Image.asset(
|
||||
'assets/images/arrow_bottom_purple_icon.png',
|
||||
color: Theme.of(context).dividerColor,
|
||||
height: 8);
|
||||
|
||||
_contactNameController.addListener(() {onHandleControllers(addressBookStore);});
|
||||
_currencyTypeController.addListener(() {onHandleControllers(addressBookStore);});
|
||||
_addressController.addListener(() {onHandleControllers(addressBookStore);});
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.all(24),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
BaseTextFormField(
|
||||
controller: _contactNameController,
|
||||
hintText: S.of(context).contact_name,
|
||||
validator: (value) {
|
||||
addressBookStore.validateContactName(value);
|
||||
return addressBookStore.errorMessage;
|
||||
}
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Container(
|
||||
child: InkWell(
|
||||
onTap: () => _presentPicker(context),
|
||||
child: IgnorePointer(
|
||||
child: BaseTextFormField(
|
||||
controller: _currencyTypeController,
|
||||
hintText: S.of(context).settings_currency,
|
||||
suffixIcon: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
downArrow
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: AddressTextField(
|
||||
controller: _addressController,
|
||||
options: [AddressTextFieldOption.qrCode],
|
||||
validator: (value) {
|
||||
addressBookStore.validateAddress(value,
|
||||
cryptoCurrency: _selectectCrypto);
|
||||
return addressBookStore.errorMessage;
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
bottom: 24
|
||||
),
|
||||
bottomSection: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_selectectCrypto = null;
|
||||
_contactNameController.text = '';
|
||||
_currencyTypeController.text = '';
|
||||
_addressController.text = '';
|
||||
});
|
||||
},
|
||||
text: S.of(context).reset,
|
||||
color: Colors.red,
|
||||
textColor: Colors.white
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (_) => PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (widget.contact == null) {
|
||||
final newContact = Contact(
|
||||
name: _contactNameController.text,
|
||||
address: _addressController.text,
|
||||
type: _selectectCrypto);
|
||||
|
||||
await addressBookStore.add(contact: newContact);
|
||||
} else {
|
||||
widget.contact.name = _contactNameController.text;
|
||||
widget.contact.address = _addressController.text;
|
||||
widget.contact
|
||||
.updateCryptoCurrency(currency: _selectectCrypto);
|
||||
|
||||
await addressBookStore.update(
|
||||
contact: widget.contact);
|
||||
}
|
||||
Navigator.pop(context);
|
||||
} catch (e) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.current.contact,
|
||||
alertContent: e.toString(),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop()
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isDisabled: addressBookStore.isDisabledStatus,
|
||||
)
|
||||
)
|
||||
)
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
void _presentPicker(BuildContext context) {
|
||||
showDialog<void>(
|
||||
builder: (_) => CurrencyPicker(
|
||||
selectedAtIndex: currencies.indexOf(_selectectCrypto),
|
||||
items: currencies,
|
||||
title: S.of(context).please_select,
|
||||
onItemSelected: (CryptoCurrency item) {
|
||||
_selectectCrypto = item;
|
||||
_currencyTypeController.text = _selectectCrypto.toString();
|
||||
}
|
||||
),
|
||||
context: context);
|
||||
}
|
||||
}
|
|
@ -125,7 +125,7 @@ abstract class BasePage extends StatelessWidget {
|
|||
_isDarkTheme ? backgroundDarkColor : backgroundLightColor,
|
||||
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
|
||||
appBar: appBar(context),
|
||||
body: SafeArea(child: body(context)),
|
||||
body: body(context), //SafeArea(child: ),
|
||||
floatingActionButton: floatingActionButton(context));
|
||||
|
||||
return rootWrapper?.call(context, root) ?? root;
|
||||
|
|
280
lib/src/screens/contact/contact_list_page.dart
Normal file
|
@ -0,0 +1,280 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
|
||||
class ContactListPage extends BasePage {
|
||||
ContactListPage(this.contactListViewModel, {this.isEditable = true});
|
||||
|
||||
final ContactListViewModel contactListViewModel;
|
||||
final bool isEditable;
|
||||
|
||||
@override
|
||||
String get title => S.current.address_book;
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
if (!isEditable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Container(
|
||||
width: 32.0,
|
||||
height: 32.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
Icon(Icons.add,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
size: 22.0),
|
||||
ButtonTheme(
|
||||
minWidth: 32.0,
|
||||
height: 32.0,
|
||||
child: FlatButton(
|
||||
shape: CircleBorder(),
|
||||
onPressed: () async {
|
||||
await Navigator.of(context)
|
||||
.pushNamed(Routes.addressBookAddContact);
|
||||
},
|
||||
child: Offstage()),
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
return contactListViewModel.contacts.isNotEmpty
|
||||
? ListView.separated(
|
||||
separatorBuilder: (_, __) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: contactListViewModel.contacts.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final contact = contactListViewModel.contacts[index];
|
||||
final image = _getCurrencyImage(contact.type);
|
||||
final content = GestureDetector(
|
||||
onTap: () async {
|
||||
if (!isEditable) {
|
||||
Navigator.of(context).pop(contact);
|
||||
return;
|
||||
}
|
||||
|
||||
final isCopied = await showNameAndAddressDialog(
|
||||
context, contact.name, contact.address);
|
||||
|
||||
if (isCopied != null && isCopied) {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: contact.address));
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: double.infinity,
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.backgroundColor,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 24, top: 16, bottom: 16, right: 24),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image ?? Offstage(),
|
||||
Padding(
|
||||
padding: image != null
|
||||
? EdgeInsets.only(left: 12)
|
||||
: EdgeInsets.only(left: 0),
|
||||
child: Text(
|
||||
contact.name,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
),
|
||||
)
|
||||
],
|
||||
)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
return !isEditable
|
||||
? content
|
||||
: Slidable(
|
||||
key: Key('${contact.key}'),
|
||||
actionPane: SlidableDrawerActionPane(),
|
||||
child: content,
|
||||
secondaryActions: <Widget>[
|
||||
IconSlideAction(
|
||||
caption: S.of(context).edit,
|
||||
color: Colors.blue,
|
||||
icon: Icons.edit,
|
||||
onTap: () async => await Navigator.of(context)
|
||||
.pushNamed(Routes.addressBookAddContact,
|
||||
arguments: contact),
|
||||
),
|
||||
IconSlideAction(
|
||||
caption: S.of(context).delete,
|
||||
color: Colors.red,
|
||||
icon: CupertinoIcons.delete,
|
||||
onTap: () async {
|
||||
final isDelete =
|
||||
await showAlertDialog(context) ?? false;
|
||||
|
||||
if (isDelete) {
|
||||
await contactListViewModel
|
||||
.delete(contact);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
dismissal: SlidableDismissal(
|
||||
child: SlidableDrawerDismissal(),
|
||||
onDismissed: (actionType) async =>
|
||||
await contactListViewModel.delete(contact),
|
||||
onWillDismiss: (actionType) async =>
|
||||
showAlertDialog(context),
|
||||
),
|
||||
);
|
||||
})
|
||||
: Center(
|
||||
child: Text(
|
||||
S.of(context).placeholder_contacts,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color
|
||||
.withOpacity(0.5),
|
||||
fontSize: 14),
|
||||
),
|
||||
);
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
Image _getCurrencyImage(CryptoCurrency currency) {
|
||||
Image image;
|
||||
switch (currency) {
|
||||
case CryptoCurrency.xmr:
|
||||
image = Image.asset('assets/images/monero.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.ada:
|
||||
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.bch:
|
||||
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.bnb:
|
||||
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.btc:
|
||||
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.dash:
|
||||
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.eos:
|
||||
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.eth:
|
||||
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.ltc:
|
||||
image =
|
||||
Image.asset('assets/images/litecoin.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.nano:
|
||||
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.trx:
|
||||
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.usdt:
|
||||
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xlm:
|
||||
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xrp:
|
||||
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
|
||||
break;
|
||||
default:
|
||||
image = null;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
Future<bool> showAlertDialog(BuildContext context) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).address_remove_contact,
|
||||
alertContent: S.of(context).address_remove_content,
|
||||
leftButtonText: S.of(context).remove,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () => Navigator.of(context).pop(true),
|
||||
actionRightButton: () => Navigator.of(context).pop(false));
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> showNameAndAddressDialog(
|
||||
BuildContext context, String name, String address) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: name,
|
||||
alertContent: address,
|
||||
leftButtonText: S.of(context).copy,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () => Navigator.of(context).pop(true),
|
||||
actionRightButton: () => Navigator.of(context).pop(false));
|
||||
});
|
||||
}
|
||||
}
|
161
lib/src/screens/contact/contact_page.dart
Normal file
|
@ -0,0 +1,161 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/core/contact_name_validator.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_view_model_state.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
|
||||
|
||||
class ContactPage extends BasePage {
|
||||
ContactPage(this.contactViewModel)
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_nameController = TextEditingController(),
|
||||
_addressController = TextEditingController(),
|
||||
_currencyTypeController = TextEditingController() {
|
||||
_nameController.text = contactViewModel.name;
|
||||
_addressController.text = contactViewModel.address;
|
||||
_nameController
|
||||
.addListener(() => contactViewModel.name = _nameController.text);
|
||||
_addressController
|
||||
.addListener(() => contactViewModel.address = _addressController.text);
|
||||
|
||||
autorun((_) =>
|
||||
_currencyTypeController.text = contactViewModel.currency.toString());
|
||||
}
|
||||
|
||||
@override
|
||||
String get title => S.current.contact;
|
||||
|
||||
final ContactViewModel contactViewModel;
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _nameController;
|
||||
final TextEditingController _currencyTypeController;
|
||||
final TextEditingController _addressController;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final downArrow = Image.asset('assets/images/arrow_bottom_purple_icon.png',
|
||||
color: Theme.of(context).dividerColor, height: 8);
|
||||
|
||||
reaction((_) => contactViewModel.state, (ContactViewModelState state) {
|
||||
if (state is ContactCreationFailure) {
|
||||
_onContactSavingFailure(context, state.error);
|
||||
}
|
||||
|
||||
if (state is ContactSavingSuccessfully) {
|
||||
_onContactSavedSuccessfully(context);
|
||||
}
|
||||
});
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.all(24),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
BaseTextFormField(
|
||||
controller: _nameController,
|
||||
hintText: S.of(context).contact_name,
|
||||
validator: ContactNameValidator()),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Container(
|
||||
child: InkWell(
|
||||
onTap: () => _presentCurrencyPicker(context),
|
||||
child: IgnorePointer(
|
||||
child: BaseTextFormField(
|
||||
controller: _currencyTypeController,
|
||||
hintText: S.of(context).settings_currency,
|
||||
suffixIcon: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[downArrow],
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Observer(
|
||||
builder: (_) => AddressTextField(
|
||||
controller: _addressController,
|
||||
options: [AddressTextFieldOption.qrCode],
|
||||
validator: AddressValidator(
|
||||
type: contactViewModel.currency),
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomSectionPadding:
|
||||
EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
onPressed: () => contactViewModel.reset(),
|
||||
text: S.of(context).reset,
|
||||
color: Colors.red,
|
||||
textColor: Colors.white),
|
||||
),
|
||||
SizedBox(width: 20),
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (_) => PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await contactViewModel.save();
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isDisabled: !contactViewModel.isReady)))
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
void _presentCurrencyPicker(BuildContext context) {
|
||||
showDialog<void>(
|
||||
builder: (_) => CurrencyPicker(
|
||||
selectedAtIndex:
|
||||
contactViewModel.currencies.indexOf(contactViewModel.currency),
|
||||
items: contactViewModel.currencies,
|
||||
title: S.of(context).please_select,
|
||||
onItemSelected: (CryptoCurrency item) =>
|
||||
contactViewModel.currency = item),
|
||||
context: context);
|
||||
}
|
||||
|
||||
void _onContactSavingFailure(BuildContext context, String error) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.current.contact,
|
||||
alertContent: error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
}
|
||||
|
||||
void _onContactSavedSuccessfully(BuildContext context) =>
|
||||
Navigator.of(context).pop();
|
||||
}
|
|
@ -1,78 +1,150 @@
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/wallet_card.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/trade_history_panel.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/menu_widget.dart';
|
||||
|
||||
class DashboardPage extends StatelessWidget {
|
||||
class DashboardPage extends BasePage {
|
||||
DashboardPage({@required this.walletViewModel});
|
||||
|
||||
final DashboardViewModel walletViewModel;
|
||||
final _bodyKey = GlobalKey();
|
||||
@override
|
||||
Color get backgroundLightColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
DashboardPageBody(key: _bodyKey, walletViewModel: walletViewModel);
|
||||
}
|
||||
|
||||
class DashboardPageBody extends StatefulWidget {
|
||||
DashboardPageBody({Key key, @required this.walletViewModel})
|
||||
: super(key: key);
|
||||
|
||||
final DashboardViewModel walletViewModel;
|
||||
Color get backgroundDarkColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
DashboardPageBodyState createState() => DashboardPageBodyState();
|
||||
}
|
||||
|
||||
class DashboardPageBodyState extends State<DashboardPageBody> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final menuButton = Image.asset(
|
||||
'assets/images/header.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
);
|
||||
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
body: Container(
|
||||
Widget Function(BuildContext, Widget) get rootWrapper =>
|
||||
(BuildContext context, Widget scaffold) => Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(colors: [
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
Theme.of(context).primaryColor
|
||||
], begin: Alignment.centerLeft, end: Alignment.centerRight)),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 10, right: 10),
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
height: 44,
|
||||
width: 44,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
onPressed: () async {
|
||||
await showDialog<void>(
|
||||
builder: (_) => MenuWidget(), context: context);
|
||||
},
|
||||
child: menuButton),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 20, top: 20),
|
||||
child: WalletCard(walletVM: widget.walletViewModel)),
|
||||
SizedBox(height: 28),
|
||||
Expanded(child: TradeHistoryPanel(dashboardViewModel: widget.walletViewModel))
|
||||
],
|
||||
),
|
||||
),
|
||||
], begin: Alignment.topLeft, end: Alignment.bottomRight)),
|
||||
child: scaffold);
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
final menuButton = Image.asset('assets/images/header.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color);
|
||||
|
||||
return Container(
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
width: 24,
|
||||
child: FlatButton(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
onPressed: () async {
|
||||
await showDialog<void>(
|
||||
builder: (_) => MenuWidget(
|
||||
name: walletViewModel.name,
|
||||
subname: walletViewModel.subname,
|
||||
type: walletViewModel.type),
|
||||
context: context);
|
||||
},
|
||||
child: menuButton),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final DashboardViewModel walletViewModel;
|
||||
final sendImage = Image.asset('assets/images/send.png');
|
||||
final exchangeImage = Image.asset('assets/images/exchange.png');
|
||||
final buyImage = Image.asset('assets/images/coins.png');
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return LayoutBuilder(builder: (context, constraints) {
|
||||
final transactionListMinHeight =
|
||||
constraints.heightConstraints().maxHeight - 345 - 32;
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(children: [
|
||||
Container(
|
||||
height: 345,
|
||||
child: Column(children: [
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24, top: 10),
|
||||
child: WalletCard(walletVM: walletViewModel)),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 44, right: 44, top: 32),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: actionButton(
|
||||
context: context,
|
||||
image: sendImage,
|
||||
title: S.of(context).send,
|
||||
route: Routes.send)),
|
||||
Flexible(
|
||||
child: actionButton(
|
||||
context: context,
|
||||
image: exchangeImage,
|
||||
title: S.of(context).exchange,
|
||||
route: Routes.exchange)),
|
||||
],
|
||||
),
|
||||
)
|
||||
])),
|
||||
SizedBox(height: 32),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(minHeight: transactionListMinHeight),
|
||||
child: TradeHistoryPanel(dashboardViewModel: walletViewModel)),
|
||||
// Column(children: [
|
||||
// Text('1'),
|
||||
// Text('2')
|
||||
// ])
|
||||
]));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget actionButton(
|
||||
{BuildContext context,
|
||||
@required Image image,
|
||||
@required String title,
|
||||
@required String route}) {
|
||||
return Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (route.isNotEmpty) {
|
||||
Navigator.of(context, rootNavigator: true).pushNamed(route);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
height: 48,
|
||||
width: 48,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryTextTheme.subhead.color,
|
||||
shape: BoxShape.circle),
|
||||
child: image,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color.fromRGBO(140, 153, 201,
|
||||
0.8) // Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -16,10 +16,8 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
|||
final buyImage = Image.asset('assets/images/coins.png');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
// final actionListStore = Provider.of<ActionListStore>(context);
|
||||
final historyPanelWidth = MediaQuery.of(context).size.width;
|
||||
|
||||
Widget build(
|
||||
BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
final _themeChanger = Provider.of<ThemeChanger>(context);
|
||||
Image filterButton;
|
||||
|
||||
|
@ -29,74 +27,39 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
|||
filterButton = Image.asset('assets/images/filter_light_button.png');
|
||||
}
|
||||
|
||||
double buttonsOpacity = 1 - shrinkOffset / (maxExtent - minExtent);
|
||||
double buttonsHeight = maxExtent - minExtent - shrinkOffset;
|
||||
|
||||
buttonsOpacity = buttonsOpacity >= 0 ? buttonsOpacity : 0;
|
||||
buttonsHeight = buttonsHeight >= 0 ? buttonsHeight : 0;
|
||||
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
overflow: Overflow.visible,
|
||||
children: <Widget>[
|
||||
Opacity(
|
||||
opacity: buttonsOpacity,
|
||||
child: Container(
|
||||
height: buttonsHeight,
|
||||
padding: EdgeInsets.only(left: 44, right: 44),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: actionButton(
|
||||
context: context,
|
||||
image: sendImage,
|
||||
title: S.of(context).send,
|
||||
route: Routes.send
|
||||
)
|
||||
),
|
||||
Flexible(
|
||||
child: actionButton(
|
||||
context: context,
|
||||
image: exchangeImage,
|
||||
title: S.of(context).exchange,
|
||||
route: Routes.exchange
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
top: buttonsHeight,
|
||||
left: 0,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(20), topRight: Radius.circular(20)),
|
||||
child: Container(
|
||||
width: historyPanelWidth,
|
||||
height: 66,
|
||||
padding: EdgeInsets.only(top: 20, left: 20, right: 20, bottom: 10),
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 0,
|
||||
child: PopupMenuButton<int>(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color))),
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(24), topRight: Radius.circular(24)),
|
||||
child: Container(
|
||||
color: Colors.red,
|
||||
// height: 75,
|
||||
padding: EdgeInsets.only(top: 26, left: 20, right: 20, bottom: 10),
|
||||
// color: Theme.of(context).backgroundColor,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Text(
|
||||
S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
)),
|
||||
Positioned(
|
||||
right: 0,
|
||||
height: 36,
|
||||
child: PopupMenuButton<int>(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color))),
|
||||
// PopupMenuItem(
|
||||
// value: 0,
|
||||
// child: Observer(
|
||||
|
@ -135,27 +98,28 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
|||
// .toggleOutgoing(),
|
||||
// )
|
||||
// ]))),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child:
|
||||
Text(S.of(context).transactions_by_date)),
|
||||
PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).trades,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color))),
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text('XMR.TO'),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child: Text(S.of(context).transactions_by_date)),
|
||||
PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).trades,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color))),
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('XMR.TO'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
|
@ -167,16 +131,15 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
|||
// ExchangeProviderDescription
|
||||
// .xmrto),
|
||||
// )
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 4,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text('Change.NOW'),
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 4,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Change.NOW'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
|
@ -188,16 +151,15 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
|||
// ExchangeProviderDescription
|
||||
// .changeNow),
|
||||
// )
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 5,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text('MorphToken'),
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 5,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('MorphToken'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
|
@ -209,42 +171,37 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
|||
// ExchangeProviderDescription
|
||||
// .morphToken),
|
||||
// )
|
||||
])))
|
||||
],
|
||||
child: filterButton,
|
||||
onSelected: (item) async {
|
||||
if (item == 2) {
|
||||
final List<DateTime> picked =
|
||||
await date_rage_picker.showDatePicker(
|
||||
context: context,
|
||||
initialFirstDate: DateTime.now()
|
||||
.subtract(Duration(days: 1)),
|
||||
initialLastDate: (DateTime.now()),
|
||||
firstDate: DateTime(2015),
|
||||
lastDate: DateTime.now()
|
||||
.add(Duration(days: 1)));
|
||||
])))
|
||||
],
|
||||
child: filterButton,
|
||||
onSelected: (item) async {
|
||||
if (item == 2) {
|
||||
final List<DateTime> picked =
|
||||
await date_rage_picker.showDatePicker(
|
||||
context: context,
|
||||
initialFirstDate:
|
||||
DateTime.now().subtract(Duration(days: 1)),
|
||||
initialLastDate: (DateTime.now()),
|
||||
firstDate: DateTime(2015),
|
||||
lastDate: DateTime.now().add(Duration(days: 1)));
|
||||
|
||||
if (picked != null && picked.length == 2) {
|
||||
if (picked != null && picked.length == 2) {
|
||||
// actionListStore.transactionFilterStore
|
||||
// .changeStartDate(picked.first);
|
||||
// actionListStore.transactionFilterStore
|
||||
// .changeEndDate(picked.last);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
}
|
||||
}
|
||||
},
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
double get maxExtent => 174;
|
||||
double get maxExtent => 164;
|
||||
|
||||
@override
|
||||
double get minExtent => 66;
|
||||
|
@ -252,12 +209,11 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
|||
@override
|
||||
bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) => true;
|
||||
|
||||
Widget actionButton({
|
||||
BuildContext context,
|
||||
@required Image image,
|
||||
@required String title,
|
||||
@required String route}) {
|
||||
|
||||
Widget actionButton(
|
||||
{BuildContext context,
|
||||
@required Image image,
|
||||
@required String title,
|
||||
@required String route}) {
|
||||
return Container(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
child: Column(
|
||||
|
@ -276,23 +232,24 @@ class ButtonHeader extends SliverPersistentHeaderDelegate {
|
|||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).primaryTextTheme.subhead.color,
|
||||
shape: BoxShape.circle
|
||||
),
|
||||
shape: BoxShape.circle),
|
||||
child: image,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color.fromRGBO(140, 153, 201,
|
||||
0.8) // Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,28 @@
|
|||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class MenuWidget extends StatefulWidget {
|
||||
MenuWidget({this.type, this.name, this.subname});
|
||||
|
||||
final WalletType type;
|
||||
final String name;
|
||||
final String subname;
|
||||
|
||||
@override
|
||||
MenuWidgetState createState() => MenuWidgetState();
|
||||
}
|
||||
|
||||
class MenuWidgetState extends State<MenuWidget> {
|
||||
final moneroIcon = Image.asset('assets/images/monero.png');
|
||||
final bitcoinIcon = Image.asset('assets/images/bitcoin.png');
|
||||
final largeScreen = 731;
|
||||
|
||||
double menuWidth;
|
||||
double screenWidth;
|
||||
double screenHeight;
|
||||
double opacity;
|
||||
bool isDraw;
|
||||
|
||||
double headerHeight;
|
||||
double tileHeight;
|
||||
|
@ -32,7 +35,6 @@ class MenuWidgetState extends State<MenuWidget> {
|
|||
screenWidth = 0;
|
||||
screenHeight = 0;
|
||||
opacity = 0;
|
||||
isDraw = false;
|
||||
|
||||
headerHeight = 120;
|
||||
tileHeight = 75;
|
||||
|
@ -59,206 +61,205 @@ class MenuWidgetState extends State<MenuWidget> {
|
|||
fromBottomEdge *= scale;
|
||||
}
|
||||
});
|
||||
|
||||
Timer(Duration(milliseconds: 350), () =>
|
||||
setState(() => isDraw = true)
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final walletMenu = WalletMenu(context);
|
||||
final walletStore = Provider.of<WalletStore>(context);
|
||||
// final walletStore = Provider.of<WalletStore>(context);
|
||||
final itemCount = walletMenu.items.length;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: Container(
|
||||
height: 60,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(2)),
|
||||
color: Theme.of(context).hintColor),
|
||||
)),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => null,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: isDraw
|
||||
? Container(
|
||||
height: 60,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(2)),
|
||||
color: Theme.of(context).hintColor //
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
height: 60,
|
||||
width: 4,
|
||||
)
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => null,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
alignment: Alignment.centerRight,
|
||||
child: AnimatedContainer(
|
||||
alignment: Alignment.centerLeft,
|
||||
width: menuWidth,
|
||||
height: double.infinity,
|
||||
duration: Duration(milliseconds: 500),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(24), bottomLeft: Radius.circular(24)),
|
||||
color: Theme.of(context).primaryTextTheme.display1.color.withOpacity(opacity)
|
||||
),
|
||||
child: isDraw
|
||||
? ListView.separated(
|
||||
itemBuilder: (_, index) {
|
||||
|
||||
if (index == 0) {
|
||||
return Container(
|
||||
height: headerHeight,
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
top: fromTopEdge,
|
||||
right: 24,
|
||||
bottom: fromBottomEdge),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(24)),
|
||||
color: Theme.of(context).primaryTextTheme.display2.color
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
moneroIcon,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 40,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
walletStore.name,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
fontFamily: 'Lato',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
),
|
||||
Text(
|
||||
walletStore.account.label,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
decoration: TextDecoration.none,
|
||||
fontFamily: 'Lato',
|
||||
fontSize: 12
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
index -= 1;
|
||||
final item = walletMenu.items[index];
|
||||
final image = walletMenu.images[index] ?? Offstage();
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
walletMenu.action(index);
|
||||
},
|
||||
child: index == itemCount - 1
|
||||
? Container(
|
||||
height: headerHeight,
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: fromBottomEdge,
|
||||
bottom: fromTopEdge),
|
||||
alignment: Alignment.topLeft,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24)),
|
||||
color: Theme.of(context).primaryTextTheme.display1.color,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
fontFamily: 'Lato',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
height: tileHeight,
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
color: Theme.of(context).primaryTextTheme.display1.color,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
fontFamily: 'Lato',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
width: menuWidth,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(24),
|
||||
bottomLeft: Radius.circular(24)),
|
||||
color: Theme.of(context).primaryTextTheme.display1.color),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(24),
|
||||
bottomLeft: Radius.circular(24)),
|
||||
child: ListView.separated(
|
||||
itemBuilder: (_, index) {
|
||||
if (index == 0) {
|
||||
return Container(
|
||||
height: headerHeight,
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
top: fromTopEdge,
|
||||
right: 24,
|
||||
bottom: fromBottomEdge),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius:
|
||||
BorderRadius.only(topLeft: Radius.circular(24)),
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.display2
|
||||
.color),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
_iconFor(type: widget.type),
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 40,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: widget.subname != null
|
||||
? MainAxisAlignment.spaceBetween
|
||||
: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
widget.name,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
decoration: TextDecoration.none,
|
||||
fontFamily: 'Avenir Next',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, index) =>
|
||||
Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
itemCount: itemCount + 1)
|
||||
: Offstage()
|
||||
if (widget.subname != null)
|
||||
Text(
|
||||
widget.subname,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color,
|
||||
decoration: TextDecoration.none,
|
||||
fontFamily: 'Avenir Next',
|
||||
fontSize: 12),
|
||||
)
|
||||
],
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
index -= 1;
|
||||
final item = walletMenu.items[index];
|
||||
final image = walletMenu.images[index] ?? Offstage();
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
walletMenu.action(index);
|
||||
},
|
||||
child: index == itemCount - 1
|
||||
? Container(
|
||||
height: headerHeight,
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: fromBottomEdge,
|
||||
bottom: fromTopEdge),
|
||||
alignment: Alignment.topLeft,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(24)),
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.display1
|
||||
.color,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
fontFamily: 'Avenir Next',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold),
|
||||
))
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(
|
||||
height: tileHeight,
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.display1
|
||||
.color,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item,
|
||||
style: TextStyle(
|
||||
decoration: TextDecoration.none,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
fontFamily: 'Avenir Next',
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
itemCount: itemCount + 1),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
))
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Image _iconFor({@required WalletType type}) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return moneroIcon;
|
||||
case WalletType.bitcoin:
|
||||
return bitcoinIcon;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/theme_changer.dart';
|
||||
import 'package:cake_wallet/themes.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
@ -15,11 +18,12 @@ import 'date_section_raw.dart';
|
|||
import 'trade_row.dart';
|
||||
import 'transaction_raw.dart';
|
||||
import 'button_header.dart';
|
||||
import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
|
||||
|
||||
class TradeHistoryPanel extends StatefulWidget {
|
||||
TradeHistoryPanel({this.dashboardViewModel});
|
||||
|
||||
DashboardViewModel dashboardViewModel;
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
|
||||
@override
|
||||
TradeHistoryPanelState createState() => TradeHistoryPanelState();
|
||||
|
@ -49,127 +53,270 @@ class TradeHistoryPanelState extends State<TradeHistoryPanel> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// final actionListStore = Provider.of<ActionListStore>(context);
|
||||
// final settingsStore = Provider.of<SettingsStore>(context);
|
||||
// AnimatedContainer(
|
||||
// width: MediaQuery.of(context).size.width,
|
||||
// height: panelHeight,
|
||||
// duration: Duration(milliseconds: 1000),
|
||||
// curve: Curves.fastOutSlowIn,
|
||||
// child: )
|
||||
|
||||
final transactionDateFormat = DateFormat('HH:mm');
|
||||
final _themeChanger = Provider.of<ThemeChanger>(context);
|
||||
final filterButton = Image.asset(
|
||||
_themeChanger.getTheme() == Themes.darkTheme
|
||||
? 'assets/images/filter_button.png'
|
||||
: 'assets/images/filter_light_button.png',
|
||||
height: 36);
|
||||
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: AnimatedContainer(
|
||||
width: MediaQuery.of(context).size.width,
|
||||
height: panelHeight,
|
||||
duration: Duration(milliseconds: 1000),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20),
|
||||
topRight: Radius.circular(20)),
|
||||
child: CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverPersistentHeader(
|
||||
delegate: ButtonHeader(),
|
||||
pinned: true,
|
||||
floating: false,
|
||||
),
|
||||
Observer(
|
||||
key: _listObserverKey,
|
||||
builder: (_) {
|
||||
// final items = actionListStore.items == null
|
||||
// ? <String>[]
|
||||
// : actionListStore.items;
|
||||
final items = widget.dashboardViewModel.transactions;
|
||||
final itemsCount = items.length + 1;
|
||||
final symbol =
|
||||
'\$'; // settingsStore.fiatCurrency.toString();
|
||||
var freeSpaceHeight =
|
||||
MediaQuery.of(context).size.height - 496;
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(20), topRight: Radius.circular(20)),
|
||||
child: Container(
|
||||
color: Colors.white,
|
||||
child: Column(children: [
|
||||
Container(
|
||||
padding:
|
||||
EdgeInsets.only(top: 32, left: 20, right: 20, bottom: 20),
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
SizedBox(height: 37), // Force stack height
|
||||
Center(
|
||||
child: Text(S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color))),
|
||||
Positioned(
|
||||
right: 0,
|
||||
child: PopupMenuButton<int>(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).transactions,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color))),
|
||||
// PopupMenuItem(
|
||||
// value: 0,
|
||||
// child: Observer(
|
||||
// builder: (_) => Row(
|
||||
// mainAxisAlignment:
|
||||
// MainAxisAlignment
|
||||
// .spaceBetween,
|
||||
// children: [
|
||||
// Text(S.of(context).incoming),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .transactionFilterStore
|
||||
// .displayIncoming,
|
||||
// onChanged: (value) =>
|
||||
// actionListStore
|
||||
// .transactionFilterStore
|
||||
// .toggleIncoming(),
|
||||
// )
|
||||
// ]))),
|
||||
// PopupMenuItem(
|
||||
// value: 1,
|
||||
// child: Observer(
|
||||
// builder: (_) => Row(
|
||||
// mainAxisAlignment:
|
||||
// MainAxisAlignment
|
||||
// .spaceBetween,
|
||||
// children: [
|
||||
// Text(S.of(context).outgoing),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .transactionFilterStore
|
||||
// .displayOutgoing,
|
||||
// onChanged: (value) =>
|
||||
// actionListStore
|
||||
// .transactionFilterStore
|
||||
// .toggleOutgoing(),
|
||||
// )
|
||||
// ]))),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child:
|
||||
Text(S.of(context).transactions_by_date)),
|
||||
PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).trades,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color))),
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('XMR.TO'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
// .displayXMRTO,
|
||||
// onChanged: (value) =>
|
||||
// actionListStore
|
||||
// .tradeFilterStore
|
||||
// .toggleDisplayExchange(
|
||||
// ExchangeProviderDescription
|
||||
// .xmrto),
|
||||
// )
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 4,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('Change.NOW'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
// .displayChangeNow,
|
||||
// onChanged: (value) =>
|
||||
// actionListStore
|
||||
// .tradeFilterStore
|
||||
// .toggleDisplayExchange(
|
||||
// ExchangeProviderDescription
|
||||
// .changeNow),
|
||||
// )
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 5,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text('MorphToken'),
|
||||
// Checkbox(
|
||||
// value: actionListStore
|
||||
// .tradeFilterStore
|
||||
// .displayMorphToken,
|
||||
// onChanged: (value) =>
|
||||
// actionListStore
|
||||
// .tradeFilterStore
|
||||
// .toggleDisplayExchange(
|
||||
// ExchangeProviderDescription
|
||||
// .morphToken),
|
||||
// )
|
||||
])))
|
||||
],
|
||||
child: filterButton,
|
||||
onSelected: (item) async {
|
||||
if (item == 2) {
|
||||
final picked =
|
||||
await date_rage_picker.showDatePicker(
|
||||
context: context,
|
||||
initialFirstDate: DateTime.now()
|
||||
.subtract(Duration(days: 1)),
|
||||
initialLastDate: (DateTime.now()),
|
||||
firstDate: DateTime(2015),
|
||||
lastDate: DateTime.now()
|
||||
.add(Duration(days: 1)));
|
||||
|
||||
return SliverList(
|
||||
key: _listKey,
|
||||
delegate:
|
||||
SliverChildBuilderDelegate((context, index) {
|
||||
if (index == itemsCount - 1) {
|
||||
freeSpaceHeight = freeSpaceHeight >= 0
|
||||
? freeSpaceHeight
|
||||
: 0;
|
||||
|
||||
return Container(
|
||||
height: freeSpaceHeight,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
color: Theme.of(context).backgroundColor);
|
||||
}
|
||||
|
||||
final item = items[index];
|
||||
|
||||
if (item is DateSectionItem) {
|
||||
freeSpaceHeight -= 38;
|
||||
return DateSectionRaw(date: item.date);
|
||||
}
|
||||
|
||||
if (item is TransactionListItem) {
|
||||
freeSpaceHeight -= 62;
|
||||
final transaction = item.transaction;
|
||||
final savedDisplayMode =
|
||||
BalanceDisplayMode.all;
|
||||
//settingsStore
|
||||
// .balanceDisplayMode;
|
||||
final formattedAmount = savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: transaction.amountFormatted();
|
||||
final formattedFiatAmount =
|
||||
savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: transaction
|
||||
.fiatAmount(); // symbol ???
|
||||
|
||||
return TransactionRow(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.transactionDetails,
|
||||
arguments: transaction),
|
||||
direction: transaction.direction,
|
||||
formattedDate: transactionDateFormat
|
||||
.format(transaction.date),
|
||||
formattedAmount: formattedAmount,
|
||||
formattedFiatAmount: formattedFiatAmount,
|
||||
isPending: transaction.isPending);
|
||||
}
|
||||
|
||||
if (item is TradeListItem) {
|
||||
freeSpaceHeight -= 62;
|
||||
final trade = item.trade;
|
||||
final savedDisplayMode =
|
||||
BalanceDisplayMode.all;
|
||||
//settingsStore
|
||||
// .balanceDisplayMode;
|
||||
final formattedAmount = trade.amount != null
|
||||
? savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: trade.amountFormatted()
|
||||
: trade.amount;
|
||||
|
||||
return TradeRow(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.tradeDetails,
|
||||
arguments: trade),
|
||||
provider: trade.provider,
|
||||
from: trade.from,
|
||||
to: trade.to,
|
||||
createdAtFormattedDate:
|
||||
transactionDateFormat
|
||||
.format(trade.createdAt),
|
||||
formattedAmount: formattedAmount);
|
||||
}
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor);
|
||||
}, childCount: itemsCount));
|
||||
})
|
||||
if (picked != null && picked.length == 2) {
|
||||
// actionListStore.transactionFilterStore
|
||||
// .changeStartDate(picked.first);
|
||||
// actionListStore.transactionFilterStore
|
||||
// .changeEndDate(picked.last);
|
||||
}
|
||||
}
|
||||
},
|
||||
)),
|
||||
],
|
||||
)))); //,
|
||||
),
|
||||
),
|
||||
widget.dashboardViewModel.transactions?.isNotEmpty ?? false
|
||||
? ListView.separated(
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.dashboardViewModel.transactions.length,
|
||||
itemBuilder: (_, index) {
|
||||
final item =
|
||||
widget.dashboardViewModel.transactions[index];
|
||||
|
||||
if (item is DateSectionItem) {
|
||||
return DateSectionRaw(date: item.date);
|
||||
}
|
||||
|
||||
if (item is TransactionListItem) {
|
||||
final transaction = item.transaction;
|
||||
final savedDisplayMode = BalanceDisplayMode.all;
|
||||
//settingsStore
|
||||
// .balanceDisplayMode;
|
||||
final formattedAmount = savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: transaction.amountFormatted();
|
||||
final formattedFiatAmount = savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: transaction.fiatAmount(); // symbol ???
|
||||
|
||||
return TransactionRow(
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.transactionDetails,
|
||||
arguments: transaction),
|
||||
direction: transaction.direction,
|
||||
formattedDate: transactionDateFormat
|
||||
.format(transaction.date),
|
||||
formattedAmount: formattedAmount,
|
||||
formattedFiatAmount: formattedFiatAmount,
|
||||
isPending: transaction.isPending);
|
||||
}
|
||||
|
||||
if (item is TradeListItem) {
|
||||
final trade = item.trade;
|
||||
final savedDisplayMode = BalanceDisplayMode.all;
|
||||
//settingsStore
|
||||
// .balanceDisplayMode;
|
||||
final formattedAmount = trade.amount != null
|
||||
? savedDisplayMode ==
|
||||
BalanceDisplayMode.hiddenBalance
|
||||
? '---'
|
||||
: trade.amountFormatted()
|
||||
: trade.amount;
|
||||
|
||||
return TradeRow(
|
||||
onTap: () => Navigator.of(context).pushNamed(
|
||||
Routes.tradeDetails,
|
||||
arguments: trade),
|
||||
provider: trade.provider,
|
||||
from: trade.from,
|
||||
to: trade.to,
|
||||
createdAtFormattedDate:
|
||||
transactionDateFormat.format(trade.createdAt),
|
||||
formattedAmount: formattedAmount);
|
||||
}
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
height: 1);
|
||||
},
|
||||
separatorBuilder: (_, __) =>
|
||||
Container(height: 14, color: Colors.white),
|
||||
)
|
||||
: Padding(
|
||||
padding: EdgeInsets.all(20),
|
||||
child: Text('Your transactions will be displayed here!',
|
||||
style: TextStyle(color: Colors.grey)))
|
||||
]))); //,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,27 +24,19 @@ class TransactionRow extends StatelessWidget {
|
|||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Theme.of(context).backgroundColor
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.only(top: 5, bottom: 5, left: 20, right: 20),
|
||||
height: 41,
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.only(left: 20, right: 20),
|
||||
child: Row(children: <Widget>[
|
||||
Container(
|
||||
height: 36,
|
||||
width: 36,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).primaryTextTheme.display3.color
|
||||
),
|
||||
child: Image.asset(
|
||||
direction == TransactionDirection.incoming
|
||||
? 'assets/images/down_arrow.png'
|
||||
: 'assets/images/up_arrow.png'),
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).primaryTextTheme.display3.color),
|
||||
child: Image.asset(direction == TransactionDirection.incoming
|
||||
? 'assets/images/down_arrow.png'
|
||||
: 'assets/images/up_arrow.png'),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
|
@ -62,28 +54,43 @@ class TransactionRow extends StatelessWidget {
|
|||
(isPending ? S.of(context).pending : ''),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
)),
|
||||
Text(direction == TransactionDirection.incoming
|
||||
? formattedAmount
|
||||
: '- ' + formattedAmount,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color)),
|
||||
Text(
|
||||
direction == TransactionDirection.incoming
|
||||
? formattedAmount
|
||||
: '- ' + formattedAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
))
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color))
|
||||
]),
|
||||
SizedBox(height: 5,),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(formattedDate,
|
||||
style: TextStyle(
|
||||
fontSize: 14, color: Theme.of(context).primaryTextTheme.headline.color)),
|
||||
Text(direction == TransactionDirection.incoming
|
||||
? formattedFiatAmount
|
||||
: '- ' + formattedFiatAmount,
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.headline
|
||||
.color)),
|
||||
Text(
|
||||
direction == TransactionDirection.incoming
|
||||
? formattedFiatAmount
|
||||
: '- ' + formattedFiatAmount,
|
||||
style: TextStyle(
|
||||
fontSize: 14, color: Theme.of(context).primaryTextTheme.headline.color))
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.headline
|
||||
.color))
|
||||
]),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
import 'dart:async';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/src/stores/balance/balance_store.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/stores/sync/sync_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||
|
@ -20,7 +16,7 @@ import 'package:cake_wallet/view_model/dashboard_view_model.dart';
|
|||
class WalletCard extends StatefulWidget {
|
||||
WalletCard({this.walletVM});
|
||||
|
||||
DashboardViewModel walletVM;
|
||||
final DashboardViewModel walletVM;
|
||||
|
||||
@override
|
||||
WalletCardState createState() => WalletCardState();
|
||||
|
@ -70,24 +66,20 @@ class WalletCardState extends State<WalletCard> {
|
|||
width: double.infinity,
|
||||
height: cardHeight,
|
||||
alignment: Alignment.centerRight,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(14), bottomLeft: Radius.circular(14))),
|
||||
child: AnimatedContainer(
|
||||
alignment: Alignment.centerLeft,
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
duration: Duration(milliseconds: 500),
|
||||
curve: Curves.fastOutSlowIn,
|
||||
padding: EdgeInsets.only(top: 1, left: 1, bottom: 1),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
bottomLeft: Radius.circular(10)),
|
||||
color: Theme.of(context).focusColor,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: PaletteDark.darkNightBlue.withOpacity(0.5),
|
||||
blurRadius: 8,
|
||||
offset: Offset(5, 5))
|
||||
]),
|
||||
topLeft: Radius.circular(14),
|
||||
bottomLeft: Radius.circular(14)),
|
||||
color: Theme.of(context).focusColor),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
|
||||
|
@ -95,21 +87,18 @@ class WalletCardState extends State<WalletCard> {
|
|||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
color: Theme.of(context).cardColor,
|
||||
child: InkWell(
|
||||
onTap: () => setState(() => isFrontSide = !isFrontSide),
|
||||
child: isFrontSide
|
||||
? frontSide(colorsSync)
|
||||
: backSide(colorsSync)),
|
||||
child: isFrontSide
|
||||
? frontSide(colorsSync)
|
||||
: InkWell(
|
||||
onTap: () => setState(() => isFrontSide = true),
|
||||
child: backSide(colorsSync)),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
Widget frontSide(List<Color> colorsSync) {
|
||||
// final syncStore = Provider.of<SyncStore>(context);
|
||||
// final walletStore = Provider.of<WalletStore>(context);
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
// final balanceStore = Provider.of<BalanceStore>(context);
|
||||
final triangleButton = Image.asset(
|
||||
'assets/images/triangle.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
|
@ -121,9 +110,9 @@ class WalletCardState extends State<WalletCard> {
|
|||
final status = widget.walletVM.status;
|
||||
final statusText = status.title();
|
||||
final progress = status.progress();
|
||||
final indicatorWidth = progress * cardWidth;
|
||||
final shortAddress = widget.walletVM.address
|
||||
.replaceRange(4, widget.walletVM.address.length - 4, '...');
|
||||
final indicatorOffset = progress * cardWidth;
|
||||
final indicatorWidth =
|
||||
progress <= 1 ? cardWidth - indicatorOffset : 0.0;
|
||||
var descriptionText = '';
|
||||
|
||||
if (status is SyncingSyncStatus) {
|
||||
|
@ -137,37 +126,26 @@ class WalletCardState extends State<WalletCard> {
|
|||
return Container(
|
||||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
color: Colors.white,
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: cardHeight,
|
||||
width: indicatorWidth,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
bottomLeft: Radius.circular(10)),
|
||||
gradient: LinearGradient(
|
||||
colors: colorsSync,
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter)),
|
||||
),
|
||||
progress != 1
|
||||
progress <= 1
|
||||
? Positioned(
|
||||
left: indicatorWidth,
|
||||
left: indicatorOffset,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
width: 1,
|
||||
width: indicatorWidth,
|
||||
height: cardHeight,
|
||||
color: Theme.of(context).focusColor,
|
||||
color: Color.fromRGBO(227, 238, 249, 1),
|
||||
))
|
||||
: Offstage(),
|
||||
isDraw
|
||||
? Positioned(
|
||||
left: 20,
|
||||
right: 20,
|
||||
top: 30,
|
||||
bottom: 30,
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: 32,
|
||||
bottom: 24,
|
||||
child: Container(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
@ -180,7 +158,8 @@ class WalletCardState extends State<WalletCard> {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
InkWell(
|
||||
onTap: () {},
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.walletList),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
|
@ -199,11 +178,13 @@ class WalletCardState extends State<WalletCard> {
|
|||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
if (widget.walletVM.subname?.isNotEmpty ?? false)
|
||||
if (widget.walletVM.subname?.isNotEmpty ??
|
||||
false)
|
||||
Text(
|
||||
widget.walletVM.subname,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
|
@ -211,26 +192,34 @@ class WalletCardState extends State<WalletCard> {
|
|||
)
|
||||
],
|
||||
),
|
||||
Container(
|
||||
width: 98,
|
||||
height: 32,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.subtitle
|
||||
.backgroundColor,
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(16))),
|
||||
child: Text(
|
||||
shortAddress,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color),
|
||||
),
|
||||
InkWell(
|
||||
onTap: () =>
|
||||
setState(() => isFrontSide = false),
|
||||
child: Container(
|
||||
width: 98,
|
||||
height: 32,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.subtitle
|
||||
.backgroundColor,
|
||||
border: Border.all(
|
||||
color: Color.fromRGBO(
|
||||
219, 231, 237, 1)),
|
||||
// FIXME
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(16))),
|
||||
child: Text(
|
||||
'Receive',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -239,7 +228,7 @@ class WalletCardState extends State<WalletCard> {
|
|||
key: _balanceObserverKey,
|
||||
builder: (_) {
|
||||
final balanceDisplayMode =
|
||||
BalanceDisplayMode.fullBalance;
|
||||
BalanceDisplayMode.availableBalance;
|
||||
// settingsStore.balanceDisplayMode;
|
||||
final symbol =
|
||||
settingsStore.fiatCurrency.toString();
|
||||
|
@ -251,7 +240,7 @@ class WalletCardState extends State<WalletCard> {
|
|||
balance = widget.walletVM.balance
|
||||
.unlockedBalance ??
|
||||
'0.0';
|
||||
fiatBalance = '\$ 123.43';
|
||||
fiatBalance = '\$ 0.00';
|
||||
// '$symbol ${balanceStore.fiatUnlockedBalance}';
|
||||
}
|
||||
|
||||
|
@ -260,7 +249,7 @@ class WalletCardState extends State<WalletCard> {
|
|||
balance = widget.walletVM.balance
|
||||
.totalBalance ??
|
||||
'0.0';
|
||||
fiatBalance = '\$ 123.43';
|
||||
fiatBalance = '\$ 0.00';
|
||||
// '$symbol ${balanceStore.fiatFullBalance}';
|
||||
}
|
||||
|
||||
|
@ -271,6 +260,8 @@ class WalletCardState extends State<WalletCard> {
|
|||
MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
|
@ -284,25 +275,33 @@ class WalletCardState extends State<WalletCard> {
|
|||
.color),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
balance,
|
||||
style: TextStyle(
|
||||
fontSize: 28,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
)
|
||||
Container(
|
||||
height: 36,
|
||||
child: Text(
|
||||
balance,
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
fontWeight:
|
||||
FontWeight.bold),
|
||||
))
|
||||
],
|
||||
),
|
||||
Text(
|
||||
fiatBalance,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
fontWeight: FontWeight.w600,
|
||||
// FIXME
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .title
|
||||
// .color,
|
||||
color: Color.fromRGBO(
|
||||
72, 89, 109, 1)),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
@ -320,6 +319,7 @@ class WalletCardState extends State<WalletCard> {
|
|||
statusText,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
|
@ -330,6 +330,7 @@ class WalletCardState extends State<WalletCard> {
|
|||
descriptionText,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
|
@ -351,10 +352,8 @@ class WalletCardState extends State<WalletCard> {
|
|||
}
|
||||
|
||||
Widget backSide(List<Color> colorsSync) {
|
||||
final rightArrow = Image.asset(
|
||||
'assets/images/right_arrow.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
);
|
||||
final rightArrow = Image.asset('assets/images/right_arrow.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color);
|
||||
var messageBoxHeight = 0.0;
|
||||
var messageBoxWidth = cardWidth - 10;
|
||||
|
||||
|
@ -372,7 +371,7 @@ class WalletCardState extends State<WalletCard> {
|
|||
width: cardWidth,
|
||||
height: cardHeight,
|
||||
padding:
|
||||
EdgeInsets.only(left: 20, right: 20, top: 30, bottom: 30),
|
||||
EdgeInsets.only(left: 24, right: 24, top: 32, bottom: 32),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(10),
|
||||
|
@ -385,10 +384,10 @@ class WalletCardState extends State<WalletCard> {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 90,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
|
@ -426,7 +425,8 @@ class WalletCardState extends State<WalletCard> {
|
|||
child: Text(
|
||||
widget.walletVM.address,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
|
@ -468,9 +468,10 @@ class WalletCardState extends State<WalletCard> {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).accounts_subaddresses,
|
||||
S.of(context).addresses,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'package:cake_wallet/src/screens/base_page.dart';
|
|||
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
|
||||
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||
|
||||
class ExchangePage extends BasePage {
|
||||
@override
|
||||
|
@ -32,20 +33,9 @@ class ExchangePage extends BasePage {
|
|||
Widget trailing(BuildContext context) {
|
||||
final exchangeStore = Provider.of<ExchangeStore>(context);
|
||||
|
||||
return ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
child: FlatButton(
|
||||
padding: EdgeInsets.all(0),
|
||||
child: Text(
|
||||
S.of(context).clear,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14),
|
||||
),
|
||||
onPressed: () => exchangeStore.reset()),
|
||||
return TrailButton(
|
||||
caption: S.of(context).reset,
|
||||
onPressed: () => exchangeStore.reset()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/monero_account_label_validator.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_state.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
|
||||
class MoneroAccountEditOrCreatePage extends BasePage {
|
||||
MoneroAccountEditOrCreatePage({@required this.moneroAccountCreationViewModel})
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_textController = TextEditingController() {
|
||||
_textController.addListener(
|
||||
() => moneroAccountCreationViewModel.label = _textController.text);
|
||||
_textController.text = moneroAccountCreationViewModel.label;
|
||||
}
|
||||
|
||||
final MoneroAccountEditOrCreateViewModel moneroAccountCreationViewModel;
|
||||
|
||||
@override
|
||||
String get title => S.current.account;
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _textController;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) =>
|
||||
Form(
|
||||
key: _formKey,
|
||||
child: Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.all(24.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: BaseTextFormField(
|
||||
controller: _textController,
|
||||
hintText: S.of(context).account,
|
||||
validator: MoneroLabelValidator(),
|
||||
))),
|
||||
Observer(
|
||||
builder: (_) =>
|
||||
LoadingPrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await moneroAccountCreationViewModel.save();
|
||||
|
||||
Navigator.of(context).pop(_textController.text);
|
||||
},
|
||||
text: moneroAccountCreationViewModel.isEdit
|
||||
? S.of(context).rename
|
||||
: S.of(context).add,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isLoading: moneroAccountCreationViewModel.state
|
||||
is AccountIsCreating,
|
||||
isDisabled:
|
||||
moneroAccountCreationViewModel.label?.isEmpty ?? true,
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
|
@ -1,33 +1,30 @@
|
|||
import 'dart:ui';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/account_list/account_list_store.dart';
|
||||
import 'package:cake_wallet/src/screens/accounts/widgets/account_tile.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/widgets/account_tile.dart';
|
||||
|
||||
class AccountListPage extends StatefulWidget {
|
||||
AccountListPage({@required this.accountListStore});
|
||||
class MoneroAccountListPage extends StatefulWidget {
|
||||
MoneroAccountListPage({@required this.accountListViewModel});
|
||||
|
||||
final AccountListStore accountListStore;
|
||||
final MoneroAccountListViewModel accountListViewModel;
|
||||
|
||||
@override
|
||||
AccountListPageForm createState() => AccountListPageForm(accountListStore);
|
||||
MoneroAccountListPageForm createState() =>
|
||||
MoneroAccountListPageForm(accountListViewModel);
|
||||
}
|
||||
|
||||
class AccountListPageForm extends State<AccountListPage> {
|
||||
AccountListPageForm(this.accountListStore);
|
||||
class MoneroAccountListPageForm extends State<MoneroAccountListPage> {
|
||||
MoneroAccountListPageForm(this.accountListViewModel);
|
||||
|
||||
final AccountListStore accountListStore;
|
||||
final MoneroAccountListViewModel accountListViewModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final walletStore = Provider.of<WalletStore>(context);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
|
@ -35,7 +32,8 @@ class AccountListPageForm extends State<AccountListPage> {
|
|||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
|
||||
decoration: BoxDecoration(
|
||||
color: PaletteDark.darkNightBlue.withOpacity(0.75)),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -46,11 +44,10 @@ class AccountListPageForm extends State<AccountListPage> {
|
|||
S.of(context).choose_account,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.none,
|
||||
color: Colors.white
|
||||
),
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.none,
|
||||
color: Colors.white),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
|
@ -61,52 +58,42 @@ class AccountListPageForm extends State<AccountListPage> {
|
|||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
child: Container(
|
||||
height: 296,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.backgroundColor,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final accounts = accountListStore.accounts;
|
||||
Expanded(child: Observer(builder: (_) {
|
||||
final accounts =
|
||||
widget.accountListViewModel.accounts;
|
||||
|
||||
return ListView.separated(
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
color: Theme.of(context).dividerColor,
|
||||
height: 1,
|
||||
),
|
||||
itemCount: accounts == null ? 0 : accounts.length,
|
||||
itemBuilder: (context, index) {
|
||||
final account = accounts[index];
|
||||
return ListView.separated(
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
color: Theme.of(context).dividerColor,
|
||||
height: 1),
|
||||
itemCount: accounts.length ?? 0,
|
||||
itemBuilder: (context, index) {
|
||||
final account = accounts[index];
|
||||
|
||||
return Observer(
|
||||
builder: (_) {
|
||||
final isCurrent = walletStore.account.id == account.id;
|
||||
|
||||
return AccountTile(
|
||||
isCurrent: isCurrent,
|
||||
accountName: account.label,
|
||||
onTap: () {
|
||||
if (isCurrent) {
|
||||
return;
|
||||
}
|
||||
|
||||
walletStore.setAccount(account);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
);
|
||||
return AccountTile(
|
||||
isCurrent: account.isSelected,
|
||||
accountName: account.label,
|
||||
onTap: () {
|
||||
if (account.isSelected) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
)
|
||||
),
|
||||
|
||||
widget.accountListViewModel
|
||||
.select(account);
|
||||
Navigator.of(context).pop();
|
||||
});
|
||||
},
|
||||
);
|
||||
})),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
await Navigator.of(context)
|
||||
.pushNamed(Routes.accountCreation);
|
||||
accountListStore.updateAccountList();
|
||||
},
|
||||
onTap: () async => await Navigator.of(context)
|
||||
.pushNamed(Routes.accountCreation),
|
||||
child: Container(
|
||||
height: 62,
|
||||
color: Colors.white,
|
|
@ -1,241 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/node_list/node_list_store.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
|
||||
class NewNodePage extends BasePage {
|
||||
@override
|
||||
String get title => S.current.node_new;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => NewNodePageForm();
|
||||
}
|
||||
|
||||
class NewNodePageForm extends StatefulWidget {
|
||||
@override
|
||||
NewNodeFormState createState() => NewNodeFormState();
|
||||
}
|
||||
|
||||
class NewNodeFormState extends State<NewNodePageForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _nodeAddressController = TextEditingController();
|
||||
final _nodePortController = TextEditingController();
|
||||
final _loginController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nodeAddressController.dispose();
|
||||
_nodePortController.dispose();
|
||||
_loginController.dispose();
|
||||
_passwordController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void onHandleControllers(NodeListStore nodeListStore) {
|
||||
if (_nodeAddressController.text.isNotEmpty &&
|
||||
_nodePortController.text.isNotEmpty) {
|
||||
nodeListStore.setDisabledState(false);
|
||||
} else {
|
||||
nodeListStore.setDisabledState(true);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final nodeList = Provider.of<NodeListStore>(context);
|
||||
|
||||
_nodeAddressController.addListener(() {onHandleControllers(nodeList);});
|
||||
_nodePortController.addListener(() {onHandleControllers(nodeList);});
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24.0),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintStyle:
|
||||
TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontSize: 16
|
||||
),
|
||||
hintText: S.of(context).node_address,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _nodeAddressController,
|
||||
validator: (value) {
|
||||
nodeList.validateNodeAddress(value);
|
||||
return nodeList.errorMessage;
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: false),
|
||||
decoration: InputDecoration(
|
||||
hintStyle:
|
||||
TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontSize: 16
|
||||
),
|
||||
hintText: S.of(context).node_port,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _nodePortController,
|
||||
validator: (value) {
|
||||
nodeList.validateNodePort(value);
|
||||
return nodeList.errorMessage;
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintStyle:
|
||||
TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontSize: 16
|
||||
),
|
||||
hintText: S.of(context).login,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _loginController,
|
||||
validator: (value) => null,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
hintStyle:
|
||||
TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.caption.color,
|
||||
fontSize: 16
|
||||
),
|
||||
hintText: S.of(context).password,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _passwordController,
|
||||
validator: (value) => null,
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
_nodeAddressController.text = '';
|
||||
_nodePortController.text = '';
|
||||
_loginController.text = '';
|
||||
_passwordController.text = '';
|
||||
},
|
||||
text: S.of(context).reset,
|
||||
color: Colors.red,
|
||||
textColor: Colors.white),
|
||||
)),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nodeList.addNode(
|
||||
address: _nodeAddressController.text,
|
||||
port: _nodePortController.text,
|
||||
login: _loginController.text,
|
||||
password: _passwordController.text);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isDisabled: nodeList.disabledState,
|
||||
),
|
||||
)),
|
||||
],
|
||||
)
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
249
lib/src/screens/nodes/node_create_or_edit_page.dart
Normal file
|
@ -0,0 +1,249 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/node_address_validator.dart';
|
||||
import 'package:cake_wallet/core/node_port_validator.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
|
||||
class NodeCreateOrEditPage extends BasePage {
|
||||
NodeCreateOrEditPage(this.nodeCreateOrEditViewModel)
|
||||
: _formKey = GlobalKey<FormState>(),
|
||||
_addressController = TextEditingController(),
|
||||
_portController = TextEditingController(),
|
||||
_loginController = TextEditingController(),
|
||||
_passwordController = TextEditingController() {
|
||||
reaction((_) => nodeCreateOrEditViewModel.address, (String address) {
|
||||
if (address != _addressController.text) {
|
||||
_addressController.text = address;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => nodeCreateOrEditViewModel.port, (String port) {
|
||||
if (port != _portController.text) {
|
||||
_portController.text = port;
|
||||
}
|
||||
});
|
||||
|
||||
if (nodeCreateOrEditViewModel.hasAuthCredentials) {
|
||||
reaction((_) => nodeCreateOrEditViewModel.login, (String login) {
|
||||
if (login != _loginController.text) {
|
||||
_loginController.text = login;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => nodeCreateOrEditViewModel.password, (String password) {
|
||||
if (password != _passwordController.text) {
|
||||
_passwordController.text = password;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_addressController.addListener(
|
||||
() => nodeCreateOrEditViewModel.address = _addressController.text);
|
||||
_portController.addListener(
|
||||
() => nodeCreateOrEditViewModel.port = _portController.text);
|
||||
_loginController.addListener(
|
||||
() => nodeCreateOrEditViewModel.login = _loginController.text);
|
||||
_passwordController.addListener(
|
||||
() => nodeCreateOrEditViewModel.password = _passwordController.text);
|
||||
}
|
||||
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _addressController;
|
||||
final TextEditingController _portController;
|
||||
final TextEditingController _loginController;
|
||||
final TextEditingController _passwordController;
|
||||
|
||||
@override
|
||||
String get title => S.current.node_new;
|
||||
|
||||
final NodeCreateOrEditViewModel nodeCreateOrEditViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24.0),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color,
|
||||
fontSize: 16),
|
||||
hintText: S.of(context).node_address,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _addressController,
|
||||
validator: NodeAddressValidator(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: false),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color,
|
||||
fontSize: 16),
|
||||
hintText: S.of(context).node_port,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _portController,
|
||||
validator: NodePortValidator(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
if (nodeCreateOrEditViewModel.hasAuthCredentials) ...[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color,
|
||||
fontSize: 16),
|
||||
hintText: S.of(context).login,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _loginController,
|
||||
validator: (value) => null,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
decoration: InputDecoration(
|
||||
hintStyle: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color,
|
||||
fontSize: 16),
|
||||
hintText: S.of(context).password,
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
controller: _passwordController,
|
||||
validator: (value) => null,
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
]
|
||||
],
|
||||
)),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () => nodeCreateOrEditViewModel.reset(),
|
||||
text: S.of(context).reset,
|
||||
color: Colors.red,
|
||||
textColor: Colors.white),
|
||||
)),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () async {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nodeCreateOrEditViewModel.save();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isDisabled: !nodeCreateOrEditViewModel.isReady,
|
||||
),
|
||||
)),
|
||||
],
|
||||
)),
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,35 +1,30 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart';
|
||||
import 'package:cake_wallet/src/stores/node_list/node_list_store.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
|
||||
|
||||
class NodeListPage extends BasePage {
|
||||
NodeListPage();
|
||||
NodeListPage(this.nodeListViewModel);
|
||||
|
||||
@override
|
||||
String get title => S.current.nodes;
|
||||
|
||||
final NodeListViewModel nodeListViewModel;
|
||||
|
||||
@override
|
||||
Widget trailing(context) {
|
||||
final nodeList = Provider.of<NodeListStore>(context);
|
||||
final settings = Provider.of<SettingsStore>(context);
|
||||
|
||||
return Container(
|
||||
height: 32,
|
||||
width: 72,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor),
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
|
@ -38,24 +33,24 @@ class NodeListPage extends BasePage {
|
|||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).node_reset_settings_title,
|
||||
alertContent: S.of(context).nodes_list_reset_to_default_message,
|
||||
leftButtonText: S.of(context).reset,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () async {
|
||||
Navigator.of(context).pop();
|
||||
await nodeList.reset();
|
||||
await settings.setCurrentNodeToDefault();
|
||||
},
|
||||
actionRightButton: () => Navigator.of(context).pop()
|
||||
);
|
||||
alertTitle: S.of(context).node_reset_settings_title,
|
||||
alertContent:
|
||||
S.of(context).nodes_list_reset_to_default_message,
|
||||
leftButtonText: S.of(context).reset,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () async {
|
||||
Navigator.of(context).pop();
|
||||
await nodeListViewModel.reset();
|
||||
},
|
||||
actionRightButton: () => Navigator.of(context).pop());
|
||||
});
|
||||
},
|
||||
child: Text(
|
||||
S.of(context).reset,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 10.0,
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blue),
|
||||
)),
|
||||
),
|
||||
|
@ -63,157 +58,77 @@ class NodeListPage extends BasePage {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget body(context) => NodeListPageBody();
|
||||
}
|
||||
|
||||
class NodeListPageBody extends StatefulWidget {
|
||||
@override
|
||||
NodeListPageBodyState createState() => NodeListPageBodyState();
|
||||
}
|
||||
|
||||
class NodeListPageBodyState extends State<NodeListPageBody> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final nodeList = Provider.of<NodeListStore>(context);
|
||||
final settings = Provider.of<SettingsStore>(context);
|
||||
|
||||
final trashImage = Image.asset('assets/images/trash.png', height: 32, width: 32, color: Colors.white);
|
||||
|
||||
final currentColor = Theme.of(context).accentTextTheme.subtitle.decorationColor;
|
||||
final notCurrentColor = Theme.of(context).accentTextTheme.title.backgroundColor;
|
||||
|
||||
final currentTextColor = Colors.blue;
|
||||
final notCurrentTextColor = Theme.of(context).primaryTextTheme.title.color;
|
||||
|
||||
Widget body(context) {
|
||||
return Container(
|
||||
height: double.infinity,
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
NodeListRow(
|
||||
title: S.of(context).add_new_node,
|
||||
trailing: Icon(Icons.add,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
size: 24.0),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color,
|
||||
onTap: () async =>
|
||||
await Navigator.of(context).pushNamed(Routes.newNode),
|
||||
isDrawTop: true,
|
||||
isDrawBottom: true),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 32),
|
||||
child: Observer(
|
||||
builder: (_) => ListView.separated(
|
||||
separatorBuilder: (_, __) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: nodeList.nodes.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final node = nodeList.nodes[index];
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Observer(
|
||||
builder: (_) => SectionStandardList(
|
||||
sectionCount: 2,
|
||||
context: context,
|
||||
itemBuilder: (_, sectionIndex, index) {
|
||||
if (sectionIndex == 0) {
|
||||
return NodeHeaderListRow(
|
||||
title: S.of(context).add_new_node,
|
||||
onTap: (_) async =>
|
||||
await Navigator.of(context).pushNamed(Routes.newNode));
|
||||
}
|
||||
|
||||
final isDrawTop = index == 0 ? true : false;
|
||||
final isDrawBottom = index == nodeList.nodes.length - 1 ? true : false;
|
||||
final node = nodeListViewModel.nodes[index];
|
||||
final isSelected = index == 1; // FIXME: hardcoded value.
|
||||
final nodeListRow = NodeListRow(
|
||||
title: node.uri,
|
||||
isSelected: isSelected,
|
||||
isAlive: node.requestNode(),
|
||||
onTap: (_) {});
|
||||
|
||||
return Observer(
|
||||
builder: (_) {
|
||||
final isCurrent = settings.node == null
|
||||
? false
|
||||
: node.key == settings.node.key;
|
||||
final dismissibleRow = Dismissible(
|
||||
key: Key('${node.key}'),
|
||||
confirmDismiss: (direction) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).remove_node,
|
||||
alertContent: S.of(context).remove_node_message,
|
||||
leftButtonText: S.of(context).remove,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () =>
|
||||
Navigator.pop(context, true),
|
||||
actionRightButton: () =>
|
||||
Navigator.pop(context, false));
|
||||
});
|
||||
},
|
||||
onDismissed: (direction) async =>
|
||||
nodeListViewModel.delete(node),
|
||||
direction: DismissDirection.endToStart,
|
||||
background: Container(
|
||||
padding: EdgeInsets.only(right: 10.0),
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
color: Palette.red,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
CupertinoIcons.delete,
|
||||
color: Colors.white,
|
||||
),
|
||||
Text(
|
||||
S.of(context).delete,
|
||||
style: TextStyle(color: Colors.white),
|
||||
)
|
||||
],
|
||||
)),
|
||||
child: nodeListRow);
|
||||
|
||||
final content = NodeListRow(
|
||||
title: node.uri,
|
||||
trailing: FutureBuilder(
|
||||
future: nodeList.isNodeOnline(node),
|
||||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.done:
|
||||
return NodeIndicator(
|
||||
color: snapshot.data as bool
|
||||
? Palette.green
|
||||
: Palette.red);
|
||||
default:
|
||||
return NodeIndicator();
|
||||
}
|
||||
}),
|
||||
color: isCurrent ? currentColor : notCurrentColor,
|
||||
textColor: isCurrent ? currentTextColor : notCurrentTextColor,
|
||||
onTap: () async {
|
||||
if (!isCurrent) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.current.nodes,
|
||||
alertContent: S.of(context)
|
||||
.change_current_node(node.uri),
|
||||
leftButtonText: S.of(context).change,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () async {
|
||||
Navigator.of(context).pop();
|
||||
await settings.setCurrentNode(
|
||||
node: node);
|
||||
},
|
||||
actionRightButton: () => Navigator.of(context).pop()
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
isDrawTop: isDrawTop,
|
||||
isDrawBottom: isDrawBottom);
|
||||
return isSelected ? nodeListRow : dismissibleRow;
|
||||
},
|
||||
itemCounter: (int sectionIndex) {
|
||||
if (sectionIndex == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return isCurrent
|
||||
? content
|
||||
: Dismissible(
|
||||
key: Key('${node.key}'),
|
||||
confirmDismiss: (direction) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).remove_node,
|
||||
alertContent: S.of(context).remove_node_message,
|
||||
leftButtonText: S.of(context).remove,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () =>
|
||||
Navigator.pop(context, true),
|
||||
actionRightButton: () =>
|
||||
Navigator.pop(context, false)
|
||||
);
|
||||
});
|
||||
},
|
||||
onDismissed: (direction) async =>
|
||||
await nodeList.remove(node: node),
|
||||
direction: DismissDirection.endToStart,
|
||||
background: Container(
|
||||
padding: EdgeInsets.only(right: 10.0, top: 2),
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
color: Palette.red,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
trashImage,
|
||||
Text(
|
||||
S.of(context).delete,
|
||||
style: TextStyle(color: Colors.white),
|
||||
)
|
||||
],
|
||||
)),
|
||||
child: content);
|
||||
},
|
||||
);
|
||||
})
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
return nodeListViewModel.nodes.length;
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,16 +2,17 @@ import 'package:flutter/material.dart';
|
|||
import 'package:cake_wallet/palette.dart';
|
||||
|
||||
class NodeIndicator extends StatelessWidget {
|
||||
NodeIndicator({this.color = Palette.red});
|
||||
NodeIndicator({this.isLive = false});
|
||||
|
||||
final Color color;
|
||||
final bool isLive;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 8.0,
|
||||
height: 8.0,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle, color: color),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle, color: isLive ? Palette.green : Palette.red),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,62 +1,43 @@
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/widgets/node_indicator.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class NodeListRow extends StatelessWidget {
|
||||
NodeListRow({
|
||||
@required this.title,
|
||||
@required this.trailing,
|
||||
@required this.color,
|
||||
@required this.textColor,
|
||||
@required this.onTap,
|
||||
@required this.isDrawTop,
|
||||
@required this.isDrawBottom});
|
||||
class NodeListRow extends StandardListRow {
|
||||
NodeListRow(
|
||||
{@required String title,
|
||||
@required void Function(BuildContext context) onTap,
|
||||
@required bool isSelected,
|
||||
@required this.isAlive})
|
||||
: super(title: title, onTap: onTap, isSelected: isSelected);
|
||||
|
||||
final String title;
|
||||
final Widget trailing;
|
||||
final Color color;
|
||||
final Color textColor;
|
||||
final VoidCallback onTap;
|
||||
final bool isDrawTop;
|
||||
final bool isDrawBottom;
|
||||
final Future<bool> isAlive;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
isDrawTop
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
color: color,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
),
|
||||
title: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: textColor
|
||||
),
|
||||
textAlign: TextAlign.left),
|
||||
trailing: trailing,
|
||||
onTap: onTap,
|
||||
)
|
||||
),
|
||||
isDrawBottom
|
||||
? Container(
|
||||
width: double.infinity,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
)
|
||||
: Offstage(),
|
||||
],
|
||||
);
|
||||
Widget buildTrailing(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: isAlive,
|
||||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.done:
|
||||
return NodeIndicator(isLive: snapshot.data as bool);
|
||||
default:
|
||||
return NodeIndicator();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class NodeHeaderListRow extends StandardListRow {
|
||||
NodeHeaderListRow({@required String title, @required void Function(BuildContext context) onTap})
|
||||
: super(title: title, onTap: onTap, isSelected: false);
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) {
|
||||
return Icon(Icons.add,
|
||||
color: Theme.of(context).primaryTextTheme.title.color, size: 24.0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/biometric_auth.dart';
|
||||
|
||||
abstract class PinCodeWidget extends StatefulWidget {
|
||||
PinCodeWidget({Key key, this.onPinCodeEntered, this.hasLengthSwitcher})
|
||||
|
@ -29,6 +30,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
|||
static const sixPinLength = 6;
|
||||
static const fourPinLength = 4;
|
||||
final _gridViewKey = GlobalKey();
|
||||
final _key = GlobalKey<ScaffoldState>();
|
||||
|
||||
int pinLength = defaultPinLength;
|
||||
List<int> pin = List<int>.filled(defaultPinLength, null);
|
||||
|
@ -83,9 +85,12 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
|||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Scaffold(body: body(context));
|
||||
Widget build(BuildContext context) =>
|
||||
Scaffold(key: _key, body: body(context));
|
||||
|
||||
Widget body(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
final deleteIconImage = Image.asset(
|
||||
'assets/images/delete_icon.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
|
@ -95,8 +100,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
|||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
);
|
||||
|
||||
return SafeArea(
|
||||
child: Container(
|
||||
return Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
padding: EdgeInsets.only(left: 40.0, right: 40.0, bottom: 40.0),
|
||||
child: Column(children: <Widget>[
|
||||
|
@ -105,8 +109,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
|||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
)),
|
||||
color: Theme.of(context).primaryTextTheme.title.color)),
|
||||
Spacer(flex: 3),
|
||||
Container(
|
||||
width: 180,
|
||||
|
@ -138,7 +141,9 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
|||
},
|
||||
child: Text(
|
||||
_changePinLengthText(),
|
||||
style: TextStyle(fontSize: 14.0, color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
))
|
||||
],
|
||||
Spacer(flex: 1),
|
||||
|
@ -161,10 +166,38 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
|||
margin: EdgeInsets.only(
|
||||
left: marginLeft, right: marginRight),
|
||||
child: FlatButton(
|
||||
onPressed: () {},
|
||||
onPressed: (widget.hasLengthSwitcher ||
|
||||
!settingsStore
|
||||
.allowBiometricalAuthentication)
|
||||
? null
|
||||
: () {
|
||||
// FIXME
|
||||
// if (authStore != null) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// final biometricAuth = BiometricAuth();
|
||||
// biometricAuth.isAuthenticated().then(
|
||||
// (isAuth) {
|
||||
// if (isAuth) {
|
||||
// authStore.biometricAuth();
|
||||
// _key.currentState.showSnackBar(
|
||||
// SnackBar(
|
||||
// content: Text(S.of(context).authenticated),
|
||||
// backgroundColor: Colors.green,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
},
|
||||
color: Theme.of(context).backgroundColor,
|
||||
shape: CircleBorder(),
|
||||
child: faceImage,
|
||||
child: (widget.hasLengthSwitcher ||
|
||||
!settingsStore
|
||||
.allowBiometricalAuthentication)
|
||||
? Offstage()
|
||||
: faceImage,
|
||||
),
|
||||
);
|
||||
} else if (index == 10) {
|
||||
|
@ -195,14 +228,17 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
|
|||
style: TextStyle(
|
||||
fontSize: 30.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color)),
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color)),
|
||||
),
|
||||
);
|
||||
}),
|
||||
)
|
||||
: null))
|
||||
]),
|
||||
));
|
||||
);
|
||||
}
|
||||
|
||||
void _push(int num) {
|
||||
|
|
|
@ -1,29 +1,22 @@
|
|||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/subaddress_list/subaddress_list_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||
import 'package:cake_wallet/src/screens/accounts/account_list_page.dart';
|
||||
import 'package:cake_wallet/src/stores/account_list/account_list_store.dart';
|
||||
import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/themes.dart';
|
||||
import 'package:cake_wallet/theme_changer.dart';
|
||||
import 'package:cake_wallet/core/amount_validator.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/widgets/address_cell.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/account_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_list_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.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_view_model.dart';
|
||||
|
||||
class ReceivePage extends BasePage {
|
||||
ReceivePage({this.addressListViewModel})
|
||||
|
@ -33,7 +26,7 @@ class ReceivePage extends BasePage {
|
|||
_formKey.currentState.validate() ? amountController.text : '');
|
||||
}
|
||||
|
||||
final AddressListViewModel addressListViewModel;
|
||||
final WalletAddressListViewModel addressListViewModel;
|
||||
final TextEditingController amountController;
|
||||
final GlobalKey<FormState> _formKey;
|
||||
|
||||
|
@ -83,11 +76,6 @@ class ReceivePage extends BasePage {
|
|||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return super.build(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final copyImage = Image.asset('assets/images/copy_content.png',
|
||||
|
@ -186,7 +174,7 @@ class ReceivePage extends BasePage {
|
|||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
|
@ -213,17 +201,11 @@ class ReceivePage extends BasePage {
|
|||
final item = addressListViewModel.items[index];
|
||||
Widget cell = Container();
|
||||
|
||||
if (item is AccountListHeader) {
|
||||
if (item is WalletAccountListHeader) {
|
||||
cell = HeaderTile(
|
||||
onTap: () async {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
// return AccountListPage(
|
||||
// accountListStore:
|
||||
// accountListStore);
|
||||
});
|
||||
},
|
||||
onTap: () async => await showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => getIt.get<MoneroAccountListPage>()),
|
||||
title: addressListViewModel.accountLabel,
|
||||
icon: Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
|
@ -233,11 +215,11 @@ class ReceivePage extends BasePage {
|
|||
));
|
||||
}
|
||||
|
||||
if (item is AddressListHeader) {
|
||||
if (item is WalletAddressListHeader) {
|
||||
cell = HeaderTile(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.newSubaddress),
|
||||
title: S.of(context).subaddresses,
|
||||
title: S.of(context).addresses,
|
||||
icon: Icon(
|
||||
Icons.add,
|
||||
size: 20,
|
||||
|
@ -246,15 +228,15 @@ class ReceivePage extends BasePage {
|
|||
));
|
||||
}
|
||||
|
||||
if (item is AddressListItem) {
|
||||
if (item is WalletAddressListItem) {
|
||||
cell = Observer(
|
||||
builder: (_) => AddressCell.fromItem(item,
|
||||
isCurrent: item.address ==
|
||||
addressListViewModel.address.address,
|
||||
onTap: (_) =>
|
||||
addressListViewModel.address = item,
|
||||
onEdit: () => Navigator.of(context)
|
||||
.pushNamed(Routes.newSubaddress, arguments: item)));
|
||||
onTap: (_) => addressListViewModel.address = item,
|
||||
onEdit: () => Navigator.of(context).pushNamed(
|
||||
Routes.newSubaddress,
|
||||
arguments: item)));
|
||||
}
|
||||
|
||||
return index != 0
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
|
||||
|
||||
class AddressCell extends StatelessWidget {
|
||||
factory AddressCell.fromItem(AddressListItem item,
|
||||
factory AddressCell.fromItem(WalletAddressListItem item,
|
||||
{@required bool isCurrent,
|
||||
Function(String) onTap,
|
||||
Function() onEdit}) =>
|
||||
|
@ -47,7 +47,7 @@ class AddressCell extends StatelessWidget {
|
|||
name ?? address,
|
||||
style: TextStyle(
|
||||
fontSize: name?.isNotEmpty ?? false ? 18 : 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isCurrent ? currentTextColor : notCurrentTextColor,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -32,7 +32,7 @@ class HeaderTile extends StatelessWidget {
|
|||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
|
|
|
@ -20,7 +20,6 @@ import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
|||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/transaction_description.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/create_login_page.dart';
|
||||
import 'package:cake_wallet/src/screens/seed/create_seed_page.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/create_dashboard_page.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart';
|
||||
|
@ -104,13 +103,13 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
|
||||
return Observer(builder: (_) {
|
||||
final state = widget.authenticationStore.state;
|
||||
print(state);
|
||||
|
||||
if (state == AuthenticationState.denied) {
|
||||
return createWelcomePage();
|
||||
}
|
||||
|
||||
if (state == AuthenticationState.installed) {
|
||||
return getIt.get<AuthPage>();
|
||||
return getIt.get<AuthPage>(instanceName: 'login');
|
||||
}
|
||||
|
||||
if (state == AuthenticationState.allowed) {
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/src/domain/services/wallet_service.dart';
|
||||
import 'package:cake_wallet/src/screens/seed/seed_page.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet_seed/wallet_seed_store.dart';
|
||||
|
||||
Widget createSeedPage(
|
||||
{@required SettingsStore settingsStore,
|
||||
@required WalletService walletService,
|
||||
@required void Function() callback}) =>
|
||||
Provider(
|
||||
create: (_) => WalletSeedStore(walletService: walletService),
|
||||
child: SeedPage(onCloseCallback: callback));
|
|
@ -1,161 +0,0 @@
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet_seed/wallet_seed_store.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
|
||||
class SeedPage extends BasePage {
|
||||
SeedPage({this.onCloseCallback});
|
||||
|
||||
static final image = Image.asset('assets/images/crypto_lock.png');
|
||||
|
||||
@override
|
||||
String get title => S.current.seed_title;
|
||||
|
||||
final VoidCallback onCloseCallback;
|
||||
|
||||
@override
|
||||
void onClose(BuildContext context) =>
|
||||
onCloseCallback != null ? onCloseCallback() : Navigator.of(context).pop();
|
||||
|
||||
@override
|
||||
Widget leading(BuildContext context) {
|
||||
return onCloseCallback != null ? Offstage() : super.leading(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
return onCloseCallback != null
|
||||
? GestureDetector(
|
||||
onTap: () => onClose(context),
|
||||
child: Container(
|
||||
width: 70,
|
||||
height: 32,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.color
|
||||
),
|
||||
child: Text(
|
||||
S.of(context).seed_language_next,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blue
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Offstage();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final walletSeedStore = Provider.of<WalletSeedStore>(context);
|
||||
String _seed;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(left: 40, right: 40, bottom: 20),
|
||||
content: Column(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 33),
|
||||
child: image,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 33),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
_seed = walletSeedStore.seed;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
walletSeedStore.name,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Text(
|
||||
_seed,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
bottom: 52
|
||||
),
|
||||
bottomSection: Container(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () => Share.text(
|
||||
S.of(context).seed_share,
|
||||
_seed,
|
||||
'text/plain'),
|
||||
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: _seed));
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(S
|
||||
.of(context)
|
||||
.copied_to_clipboard),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
),
|
||||
);
|
||||
},
|
||||
text: S.of(context).copy,
|
||||
color: Colors.blue,
|
||||
textColor: Colors.white)
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
154
lib/src/screens/seed/wallet_seed_page.dart
Normal file
|
@ -0,0 +1,154 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/themes.dart';
|
||||
import 'package:cake_wallet/theme_changer.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
|
||||
|
||||
class WalletSeedPage extends BasePage {
|
||||
WalletSeedPage(this.walletSeedViewModel, {this.onCloseCallback});
|
||||
|
||||
static final imageLight = Image.asset('assets/images/crypto_lock_light.png');
|
||||
static final imageDark = Image.asset('assets/images/crypto_lock.png');
|
||||
|
||||
@override
|
||||
String get title => S.current.seed_title;
|
||||
|
||||
final VoidCallback onCloseCallback;
|
||||
final WalletSeedViewModel walletSeedViewModel;
|
||||
|
||||
@override
|
||||
void onClose(BuildContext context) =>
|
||||
onCloseCallback != null ? onCloseCallback() : Navigator.of(context).pop();
|
||||
|
||||
@override
|
||||
Widget leading(BuildContext context) =>
|
||||
onCloseCallback != null ? Offstage() : super.leading(context);
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
return onCloseCallback != null
|
||||
? GestureDetector(
|
||||
onTap: () => onClose(context),
|
||||
child: Container(
|
||||
width: 100,
|
||||
height: 42,
|
||||
alignment: Alignment.center,
|
||||
margin: EdgeInsets.only(left: 10),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.color),
|
||||
child: Text(
|
||||
S.of(context).seed_language_next,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blue),
|
||||
),
|
||||
),
|
||||
)
|
||||
: Offstage();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final _themeChanger = Provider.of<ThemeChanger>(context);
|
||||
final image =
|
||||
_themeChanger.getTheme() == Themes.darkTheme ? imageDark : imageLight;
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
flex: 2,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: FittedBox(child: image, fit: BoxFit.fill))),
|
||||
Flexible(
|
||||
flex: 3,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 33),
|
||||
child: Observer(builder: (_) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
walletSeedViewModel.name,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Text(
|
||||
walletSeedViewModel.seed,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 8.0),
|
||||
child: PrimaryButton(
|
||||
onPressed: () => Share.text(
|
||||
S.of(context).seed_share,
|
||||
walletSeedViewModel.seed,
|
||||
'text/plain'),
|
||||
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));
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
),
|
||||
);
|
||||
},
|
||||
text: S.of(context).copy,
|
||||
color: Colors.blue,
|
||||
textColor: Colors.white)),
|
||||
))
|
||||
],
|
||||
)
|
||||
],
|
||||
))
|
||||
],
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
import 'package:cake_wallet/core/address_validator.dart';
|
||||
import 'package:cake_wallet/core/amount_validator.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/view_model/send_view_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -14,7 +17,8 @@ import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
|||
import 'package:cake_wallet/src/stores/balance/balance_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/src/stores/send/send_store.dart';
|
||||
import 'package:cake_wallet/src/stores/send/sending_state.dart';
|
||||
|
||||
//import 'package:cake_wallet/src/stores/send/sending_state.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/calculate_estimated_fee.dart';
|
||||
|
@ -29,8 +33,13 @@ import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart'
|
|||
import 'package:cake_wallet/src/screens/send/widgets/sending_alert.dart';
|
||||
import 'package:cake_wallet/src/widgets/template_tile.dart';
|
||||
import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
|
||||
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||
|
||||
class SendPage extends BasePage {
|
||||
SendPage({@required this.sendViewModel});
|
||||
|
||||
final SendViewModel sendViewModel;
|
||||
|
||||
@override
|
||||
String get title => S.current.send_title;
|
||||
|
||||
|
@ -45,35 +54,20 @@ class SendPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget trailing(context) {
|
||||
final sendStore = Provider.of<SendStore>(context);
|
||||
// final sendStore = Provider.of<SendStore>(context);
|
||||
|
||||
return Container(
|
||||
height: 32,
|
||||
width: 82,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.color
|
||||
),
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
onPressed: () => sendStore.clear(),
|
||||
child: Text(
|
||||
S.of(context).clear,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 10.0,
|
||||
color: Colors.blue),
|
||||
)),
|
||||
),
|
||||
);
|
||||
return TrailButton(caption: S.of(context).clear, onPressed: () => null);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => SendForm();
|
||||
Widget body(BuildContext context) => SendForm(sendViewModel: sendViewModel);
|
||||
}
|
||||
|
||||
class SendForm extends StatefulWidget {
|
||||
SendForm({this.sendViewModel});
|
||||
|
||||
final SendViewModel sendViewModel;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => SendFormState();
|
||||
}
|
||||
|
@ -92,7 +86,7 @@ class SendFormState extends State<SendForm> {
|
|||
@override
|
||||
void initState() {
|
||||
_focusNode.addListener(() {
|
||||
if (!_focusNode.hasFocus &&_addressController.text.isNotEmpty) {
|
||||
if (!_focusNode.hasFocus && _addressController.text.isNotEmpty) {
|
||||
getOpenaliasRecord(context);
|
||||
}
|
||||
});
|
||||
|
@ -102,7 +96,8 @@ class SendFormState extends State<SendForm> {
|
|||
|
||||
Future<void> getOpenaliasRecord(BuildContext context) async {
|
||||
final sendStore = Provider.of<SendStore>(context);
|
||||
final isOpenalias = await sendStore.isOpenaliasRecord(_addressController.text);
|
||||
final isOpenalias =
|
||||
await sendStore.isOpenaliasRecord(_addressController.text);
|
||||
|
||||
if (isOpenalias) {
|
||||
_addressController.text = sendStore.recordAddress;
|
||||
|
@ -111,24 +106,24 @@ class SendFormState extends State<SendForm> {
|
|||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).openalias_alert_title,
|
||||
alertContent: S.of(context).openalias_alert_content(sendStore.recordName),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop()
|
||||
);
|
||||
alertTitle: S.of(context).openalias_alert_title,
|
||||
alertContent:
|
||||
S.of(context).openalias_alert_content(sendStore.recordName),
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final sendStore = Provider.of<SendStore>(context);
|
||||
sendStore.settingsStore = settingsStore;
|
||||
final balanceStore = Provider.of<BalanceStore>(context);
|
||||
final walletStore = Provider.of<WalletStore>(context);
|
||||
final syncStore = Provider.of<SyncStore>(context);
|
||||
final sendTemplateStore = Provider.of<SendTemplateStore>(context);
|
||||
// final settingsStore = Provider.of<SettingsStore>(context);
|
||||
// final sendStore = Provider.of<SendStore>(context);
|
||||
// sendStore.settingsStore = settingsStore;
|
||||
// final balanceStore = Provider.of<BalanceStore>(context);
|
||||
// final walletStore = Provider.of<WalletStore>(context);
|
||||
// final syncStore = Provider.of<SyncStore>(context);
|
||||
// final sendTemplateStore = Provider.of<SendTemplateStore>(context);
|
||||
|
||||
_setEffects(context);
|
||||
|
||||
|
@ -166,110 +161,116 @@ class SendFormState extends State<SendForm> {
|
|||
AddressTextFieldOption.addressBook
|
||||
],
|
||||
buttonColor: Theme.of(context).accentTextTheme.title.color,
|
||||
validator: (value) {
|
||||
sendStore.validateAddress(value,
|
||||
cryptoCurrency: CryptoCurrency.xmr);
|
||||
return sendStore.errorMessage;
|
||||
},
|
||||
validator: widget.sendViewModel.addressValidator,
|
||||
),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
controller: _cryptoAmountController,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: true),
|
||||
inputFormatters: [
|
||||
BlacklistingTextInputFormatter(
|
||||
RegExp('[\\-|\\ |\\,]'))
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text('XMR:',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
)),
|
||||
),
|
||||
suffixIcon: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: 5
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: MediaQuery.of(context).size.width/2,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
' / ' + balanceStore.unlockedBalance,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
)
|
||||
Observer(builder: (_) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
controller: _cryptoAmountController,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: true),
|
||||
inputFormatters: [
|
||||
BlacklistingTextInputFormatter(
|
||||
RegExp('[\\-|\\ |\\,]'))
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text('XMR:',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
)),
|
||||
),
|
||||
suffixIcon: Padding(
|
||||
padding: EdgeInsets.only(bottom: 5),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width:
|
||||
MediaQuery.of(context).size.width / 2,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
' / ' + widget.sendViewModel.balance,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color)),
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
width: 32,
|
||||
margin: EdgeInsets.only(
|
||||
left: 12, bottom: 7, top: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.color,
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(6))),
|
||||
child: InkWell(
|
||||
onTap: () => null,
|
||||
// widget.sendViewModel,
|
||||
child: Center(
|
||||
child: Text(S.of(context).all,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color)),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 32,
|
||||
width: 32,
|
||||
margin: EdgeInsets.only(left: 12, bottom: 7, top: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).accentTextTheme.title.color,
|
||||
borderRadius: BorderRadius.all(Radius.circular(6))
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () => sendStore.setSendAll(),
|
||||
child: Center(
|
||||
child: Text(S.of(context).all,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 9,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
hintText: '0.0000',
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
validator: (value) {
|
||||
sendStore.validateXMR(
|
||||
value, balanceStore.unlockedBalance);
|
||||
return sendStore.errorMessage;
|
||||
}),
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
hintText: '0.0000',
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0)),
|
||||
enabledBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Theme.of(context).dividerColor,
|
||||
width: 1.0))),
|
||||
validator: widget.sendViewModel.amountValidator),
|
||||
);
|
||||
}),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
color:
|
||||
Theme.of(context).primaryTextTheme.title.color),
|
||||
controller: _fiatAmountController,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: true),
|
||||
|
@ -281,16 +282,22 @@ class SendFormState extends State<SendForm> {
|
|||
prefixIcon: Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: Text(
|
||||
'${settingsStore.fiatCurrency.toString()}:',
|
||||
'${widget.sendViewModel.fiat.toString()}:',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
)),
|
||||
),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.color),
|
||||
hintText: '0.00',
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
|
@ -310,14 +317,20 @@ class SendFormState extends State<SendForm> {
|
|||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
)),
|
||||
Text(
|
||||
'${calculateEstimatedFee(priority: settingsStore.transactionPriority)} XMR',
|
||||
'${widget.sendViewModel.estimatedFee} ${widget.sendViewModel.currency.toString()}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color,
|
||||
))
|
||||
],
|
||||
),
|
||||
|
@ -325,143 +338,140 @@ class SendFormState extends State<SendForm> {
|
|||
]),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 32,
|
||||
left: 24,
|
||||
bottom: 24
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).send_templates,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final itemCount = sendTemplateStore.templates.length + 1;
|
||||
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
|
||||
if (index == 0) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.sendTemplate),
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 10),
|
||||
child: DottedBorder(
|
||||
borderType: BorderType.RRect,
|
||||
dashPattern: [8, 4],
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
strokeWidth: 2,
|
||||
radius: Radius.circular(20),
|
||||
child: Container(
|
||||
height: 40,
|
||||
width: 75,
|
||||
padding: EdgeInsets.only(left: 10, right: 10),
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(20)),
|
||||
color: Colors.transparent,
|
||||
),
|
||||
child: Text(
|
||||
S.of(context).send_new,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
index -= 1;
|
||||
|
||||
final template = sendTemplateStore.templates[index];
|
||||
|
||||
return TemplateTile(
|
||||
to: template.name,
|
||||
amount: template.amount,
|
||||
from: template.cryptoCurrency,
|
||||
onTap: () {
|
||||
_addressController.text = template.address;
|
||||
_cryptoAmountController.text = template.amount;
|
||||
getOpenaliasRecord(context);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
),
|
||||
)
|
||||
// Padding(
|
||||
// padding: EdgeInsets.only(top: 32, left: 24, bottom: 24),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.start,
|
||||
// children: <Widget>[
|
||||
// Text(
|
||||
// S.of(context).send_templates,
|
||||
// style: TextStyle(
|
||||
// fontSize: 18,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color:
|
||||
// Theme.of(context).primaryTextTheme.caption.color),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// Container(
|
||||
// height: 40,
|
||||
// width: double.infinity,
|
||||
// padding: EdgeInsets.only(left: 24),
|
||||
// child: Observer(builder: (_) {
|
||||
// final itemCount = sendTemplateStore.templates.length + 1;
|
||||
//
|
||||
// return ListView.builder(
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// itemCount: itemCount,
|
||||
// itemBuilder: (context, index) {
|
||||
// if (index == 0) {
|
||||
// return GestureDetector(
|
||||
// onTap: () => Navigator.of(context)
|
||||
// .pushNamed(Routes.sendTemplate),
|
||||
// child: Container(
|
||||
// padding: EdgeInsets.only(right: 10),
|
||||
// child: DottedBorder(
|
||||
// borderType: BorderType.RRect,
|
||||
// dashPattern: [8, 4],
|
||||
// color: Theme.of(context)
|
||||
// .accentTextTheme
|
||||
// .title
|
||||
// .backgroundColor,
|
||||
// strokeWidth: 2,
|
||||
// radius: Radius.circular(20),
|
||||
// child: Container(
|
||||
// height: 40,
|
||||
// width: 75,
|
||||
// padding: EdgeInsets.only(left: 10, right: 10),
|
||||
// alignment: Alignment.center,
|
||||
// decoration: BoxDecoration(
|
||||
// borderRadius:
|
||||
// BorderRadius.all(Radius.circular(20)),
|
||||
// color: Colors.transparent,
|
||||
// ),
|
||||
// child: Text(
|
||||
// S.of(context).send_new,
|
||||
// style: TextStyle(
|
||||
// fontSize: 14,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .caption
|
||||
// .color),
|
||||
// ),
|
||||
// )),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// index -= 1;
|
||||
//
|
||||
// final template = sendTemplateStore.templates[index];
|
||||
//
|
||||
// return TemplateTile(
|
||||
// to: template.name,
|
||||
// amount: template.amount,
|
||||
// from: template.cryptoCurrency,
|
||||
// onTap: () {
|
||||
// _addressController.text = template.address;
|
||||
// _cryptoAmountController.text = template.amount;
|
||||
// getOpenaliasRecord(context);
|
||||
// });
|
||||
// });
|
||||
// }),
|
||||
// )
|
||||
],
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Observer(builder: (_) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: syncStore.status is SyncedSyncStatus
|
||||
? () async {
|
||||
// Hack. Don't ask me.
|
||||
FocusScope.of(context).requestFocus(FocusNode());
|
||||
|
||||
if (_formKey.currentState.validate()) {
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).send_creating_transaction,
|
||||
alertContent: S.of(context).confirm_sending,
|
||||
leftButtonText: S.of(context).send,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () async {
|
||||
await Navigator.of(dialogContext)
|
||||
.popAndPushNamed(Routes.auth,
|
||||
arguments: (bool
|
||||
isAuthenticatedSuccessfully,
|
||||
AuthPageState auth) {
|
||||
if (!isAuthenticatedSuccessfully) {
|
||||
return;
|
||||
}
|
||||
|
||||
Navigator.of(auth.context).pop();
|
||||
|
||||
sendStore.createTransaction(
|
||||
address: _addressController.text,
|
||||
paymentId: '');
|
||||
});
|
||||
},
|
||||
actionRightButton: () =>
|
||||
Navigator.of(context).pop()
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
: null,
|
||||
onPressed: () => null,
|
||||
// syncStore.status is SyncedSyncStatus
|
||||
// ? () async {
|
||||
// // Hack. Don't ask me.
|
||||
// FocusScope.of(context).requestFocus(FocusNode());
|
||||
//
|
||||
// if (_formKey.currentState.validate()) {
|
||||
// await showDialog<void>(
|
||||
// context: context,
|
||||
// builder: (dialogContext) {
|
||||
// return AlertWithTwoActions(
|
||||
// alertTitle:
|
||||
// S.of(context).send_creating_transaction,
|
||||
// alertContent: S.of(context).confirm_sending,
|
||||
// leftButtonText: S.of(context).send,
|
||||
// rightButtonText: S.of(context).cancel,
|
||||
// actionLeftButton: () async {
|
||||
// await Navigator.of(dialogContext)
|
||||
// .popAndPushNamed(Routes.auth, arguments:
|
||||
// (bool isAuthenticatedSuccessfully,
|
||||
// AuthPageState auth) {
|
||||
// if (!isAuthenticatedSuccessfully) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// Navigator.of(auth.context).pop();
|
||||
//
|
||||
// sendStore.createTransaction(
|
||||
// address: _addressController.text,
|
||||
// paymentId: '');
|
||||
// });
|
||||
// },
|
||||
// actionRightButton: () =>
|
||||
// Navigator.of(context).pop());
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// : null,
|
||||
text: S.of(context).send,
|
||||
color: Colors.blue,
|
||||
textColor: Colors.white,
|
||||
isLoading: sendStore.state is CreatingTransaction ||
|
||||
sendStore.state is TransactionCommiting,
|
||||
isDisabled: !(syncStore.status is SyncedSyncStatus),
|
||||
);
|
||||
isLoading: widget.sendViewModel.state is TransactionIsCreating ||
|
||||
widget.sendViewModel.state is TransactionCommitting,
|
||||
isDisabled:
|
||||
false // FIXME !(syncStore.status is SyncedSyncStatus),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
@ -472,93 +482,88 @@ class SendFormState extends State<SendForm> {
|
|||
return;
|
||||
}
|
||||
|
||||
final sendStore = Provider.of<SendStore>(context);
|
||||
// reaction((_) => widget.sendViewModel.fiatAmount, (String amount) {
|
||||
// if (amount != _fiatAmountController.text) {
|
||||
// _fiatAmountController.text = amount;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// reaction((_) => widget.sendViewModel.cryptoAmount, (String amount) {
|
||||
// if (amount != _cryptoAmountController.text) {
|
||||
// _cryptoAmountController.text = amount;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// reaction((_) => widget.sendViewModel.address, (String address) {
|
||||
// if (address != _addressController.text) {
|
||||
// _addressController.text = address;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// _addressController.addListener(() {
|
||||
// final address = _addressController.text;
|
||||
//
|
||||
// if (widget.sendViewModel.address != address) {
|
||||
// widget.sendViewModel.changeAddress(address);
|
||||
// }
|
||||
// });
|
||||
|
||||
reaction((_) => sendStore.fiatAmount, (String amount) {
|
||||
if (amount != _fiatAmountController.text) {
|
||||
_fiatAmountController.text = amount;
|
||||
}
|
||||
});
|
||||
// _fiatAmountController.addListener(() {
|
||||
// final fiatAmount = _fiatAmountController.text;
|
||||
//
|
||||
// if (sendStore.fiatAmount != fiatAmount) {
|
||||
// sendStore.changeFiatAmount(fiatAmount);
|
||||
// }
|
||||
// });
|
||||
|
||||
reaction((_) => sendStore.cryptoAmount, (String amount) {
|
||||
if (amount != _cryptoAmountController.text) {
|
||||
_cryptoAmountController.text = amount;
|
||||
}
|
||||
});
|
||||
// _cryptoAmountController.addListener(() {
|
||||
// final cryptoAmount = _cryptoAmountController.text;
|
||||
//
|
||||
// if (sendStore.cryptoAmount != cryptoAmount) {
|
||||
// sendStore.changeCryptoAmount(cryptoAmount);
|
||||
// }
|
||||
// });
|
||||
|
||||
reaction((_) => sendStore.address, (String address) {
|
||||
if (address != _addressController.text) {
|
||||
_addressController.text = address;
|
||||
}
|
||||
});
|
||||
|
||||
_addressController.addListener(() {
|
||||
final address = _addressController.text;
|
||||
|
||||
if (sendStore.address != address) {
|
||||
sendStore.changeAddress(address);
|
||||
}
|
||||
});
|
||||
|
||||
_fiatAmountController.addListener(() {
|
||||
final fiatAmount = _fiatAmountController.text;
|
||||
|
||||
if (sendStore.fiatAmount != fiatAmount) {
|
||||
sendStore.changeFiatAmount(fiatAmount);
|
||||
}
|
||||
});
|
||||
|
||||
_cryptoAmountController.addListener(() {
|
||||
final cryptoAmount = _cryptoAmountController.text;
|
||||
|
||||
if (sendStore.cryptoAmount != cryptoAmount) {
|
||||
sendStore.changeCryptoAmount(cryptoAmount);
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => sendStore.state, (SendingState state) {
|
||||
reaction((_) => widget.sendViewModel.state, (SendViewModelState state) {
|
||||
if (state is SendingFailed) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop()
|
||||
);
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (state is TransactionCreatedSuccessfully) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return ConfirmSendingAlert(
|
||||
alertTitle: S.of(context).confirm_sending,
|
||||
amount: S.of(context).send_amount,
|
||||
amountValue: sendStore.pendingTransaction.amount,
|
||||
fee: S.of(context).send_fee,
|
||||
feeValue: sendStore.pendingTransaction.fee,
|
||||
leftButtonText: S.of(context).ok,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () {
|
||||
Navigator.of(context).pop();
|
||||
sendStore.commitTransaction();
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return SendingAlert(sendStore: sendStore);
|
||||
}
|
||||
);
|
||||
},
|
||||
actionRightButton: () => Navigator.of(context).pop()
|
||||
);
|
||||
});
|
||||
});
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// showDialog<void>(
|
||||
// context: context,
|
||||
// builder: (BuildContext context) {
|
||||
// return ConfirmSendingAlert(
|
||||
// alertTitle: S.of(context).confirm_sending,
|
||||
// amount: S.of(context).send_amount,
|
||||
// amountValue: sendStore.pendingTransaction.amount,
|
||||
// fee: S.of(context).send_fee,
|
||||
// feeValue: sendStore.pendingTransaction.fee,
|
||||
// leftButtonText: S.of(context).ok,
|
||||
// rightButtonText: S.of(context).cancel,
|
||||
// actionLeftButton: () {
|
||||
// Navigator.of(context).pop();
|
||||
// sendStore.commitTransaction();
|
||||
// showDialog<void>(
|
||||
// context: context,
|
||||
// builder: (BuildContext context) {
|
||||
// return SendingAlert(sendStore: sendStore);
|
||||
// });
|
||||
// },
|
||||
// actionRightButton: () => Navigator.of(context).pop());
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
if (state is TransactionCommitted) {
|
||||
|
|
|
@ -55,7 +55,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
|
@ -64,7 +64,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)
|
||||
|
@ -79,7 +79,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
|
@ -88,7 +88,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
|||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)
|
||||
|
|
|
@ -1,438 +1,72 @@
|
|||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/balance_display_mode.dart';
|
||||
import 'package:cake_wallet/src/domain/common/fiat_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/stores/action_list/action_list_display_mode.dart';
|
||||
import 'package:cake_wallet/view_model/settings/settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/link_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/settings/picker_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/settings/regular_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/settings/switcher_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/attributes.dart';
|
||||
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/items/settings_item.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/items/item_headers.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
// Settings widgets
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_arrow_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_header_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switch_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_text_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_raw_widget_list_row.dart';
|
||||
|
||||
class SettingsPage extends BasePage {
|
||||
SettingsPage(this.settingsViewModel);
|
||||
|
||||
final SettingsViewModel settingsViewModel;
|
||||
|
||||
@override
|
||||
String get title => S.current.settings_title;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return SettingsForm();
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsForm extends StatefulWidget {
|
||||
@override
|
||||
SettingsFormState createState() => SettingsFormState();
|
||||
}
|
||||
|
||||
class SettingsFormState extends State<SettingsForm> {
|
||||
final _telegramImage = Image.asset('assets/images/Telegram.png');
|
||||
final _twitterImage = Image.asset('assets/images/Twitter.png');
|
||||
final _changeNowImage = Image.asset('assets/images/change_now.png');
|
||||
final _xmrBtcImage = Image.asset('assets/images/xmr_btc.png');
|
||||
final _morphImage = Image.asset('assets/images/morph_icon.png');
|
||||
|
||||
final _emailUrl = 'mailto:support@cakewallet.com';
|
||||
final _telegramUrl = 'https:t.me/cakewallet_bot';
|
||||
final _twitterUrl = 'https:twitter.com/CakewalletXMR';
|
||||
final _changeNowUrl = 'mailto:support@changenow.io';
|
||||
final _xmrToUrl = 'mailto:support@xmr.to';
|
||||
final _morphUrl = 'mailto:support@morphtoken.com';
|
||||
|
||||
final _items = List<SettingsItem>();
|
||||
|
||||
void _launchUrl(String url) async {
|
||||
if (await canLaunch(url)) await launch(url);
|
||||
}
|
||||
|
||||
void _setSettingsList() {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
settingsStore.setItemHeaders();
|
||||
|
||||
_items.addAll([
|
||||
SettingsItem(
|
||||
onTaped: () => _setBalance(context),
|
||||
title: ItemHeaders.displayBalanceAs,
|
||||
widget: Observer(
|
||||
builder: (_) => Text(
|
||||
settingsStore.balanceDisplayMode.toString(),
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
)),
|
||||
attribute: Attributes.widget),
|
||||
SettingsItem(
|
||||
onTaped: () => _setCurrency(context),
|
||||
title: ItemHeaders.currency,
|
||||
widget: Observer(
|
||||
builder: (_) => Text(
|
||||
settingsStore.fiatCurrency.toString(),
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
)),
|
||||
attribute: Attributes.widget),
|
||||
SettingsItem(
|
||||
onTaped: () => _setTransactionPriority(context),
|
||||
title: ItemHeaders.feePriority,
|
||||
widget: Observer(
|
||||
builder: (_) => Text(
|
||||
settingsStore.transactionPriority.toString(),
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
)),
|
||||
attribute: Attributes.widget),
|
||||
SettingsItem(
|
||||
title: ItemHeaders.saveRecipientAddress,
|
||||
attribute: Attributes.switcher),
|
||||
SettingsItem(title: '', attribute: Attributes.header),
|
||||
SettingsItem(
|
||||
onTaped: () {
|
||||
Navigator.of(context).pushNamed(Routes.auth,
|
||||
arguments: (bool isAuthenticatedSuccessfully,
|
||||
AuthPageState auth) =>
|
||||
isAuthenticatedSuccessfully
|
||||
? Navigator.of(context).popAndPushNamed(Routes.setupPin,
|
||||
arguments:
|
||||
(BuildContext setupPinContext, String _) =>
|
||||
Navigator.of(context).pop())
|
||||
: null);
|
||||
},
|
||||
title: ItemHeaders.changePIN,
|
||||
attribute: Attributes.arrow),
|
||||
SettingsItem(
|
||||
onTaped: () => Navigator.pushNamed(context, Routes.changeLanguage),
|
||||
title: ItemHeaders.changeLanguage,
|
||||
attribute: Attributes.arrow),
|
||||
SettingsItem(
|
||||
title: ItemHeaders.allowBiometricalAuthentication,
|
||||
attribute: Attributes.switcher),
|
||||
SettingsItem(title: ItemHeaders.darkMode, attribute: Attributes.switcher),
|
||||
SettingsItem(
|
||||
widgetBuilder: (context) {
|
||||
return PopupMenuButton<ActionListDisplayMode>(
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
value: ActionListDisplayMode.transactions,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(S
|
||||
.of(context)
|
||||
.settings_transactions,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: settingsStore
|
||||
.actionlistDisplayMode
|
||||
.contains(ActionListDisplayMode
|
||||
.transactions),
|
||||
onChanged: (value) => settingsStore
|
||||
.toggleTransactionsDisplay(),
|
||||
)
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: ActionListDisplayMode.trades,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
S.of(context).settings_trades,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: settingsStore
|
||||
.actionlistDisplayMode
|
||||
.contains(
|
||||
ActionListDisplayMode.trades),
|
||||
onChanged: (value) => settingsStore
|
||||
.toggleTradesDisplay(),
|
||||
)
|
||||
])))
|
||||
],
|
||||
child: Container(
|
||||
height: 56,
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(S.of(context).settings_display_on_dashboard_list,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).primaryTextTheme.title.color)),
|
||||
Observer(builder: (_) {
|
||||
var title = '';
|
||||
|
||||
if (settingsStore.actionlistDisplayMode.length ==
|
||||
ActionListDisplayMode.values.length) {
|
||||
title = S.of(context).settings_all;
|
||||
}
|
||||
|
||||
if (title.isEmpty &&
|
||||
settingsStore.actionlistDisplayMode
|
||||
.contains(ActionListDisplayMode.trades)) {
|
||||
title = S.of(context).settings_only_trades;
|
||||
}
|
||||
|
||||
if (title.isEmpty &&
|
||||
settingsStore.actionlistDisplayMode.contains(
|
||||
ActionListDisplayMode.transactions)) {
|
||||
title = S.of(context).settings_only_transactions;
|
||||
}
|
||||
|
||||
if (title.isEmpty) {
|
||||
title = S.of(context).settings_none;
|
||||
}
|
||||
|
||||
return Text(title,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color));
|
||||
})
|
||||
]),
|
||||
));
|
||||
},
|
||||
attribute: Attributes.rawWidget),
|
||||
SettingsItem(title: '', attribute: Attributes.header),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_emailUrl),
|
||||
title: 'Email',
|
||||
link: 'support@cakewallet.com',
|
||||
image: null,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_telegramUrl),
|
||||
title: 'Telegram',
|
||||
link: 'Cake_Wallet',
|
||||
image: _telegramImage,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_twitterUrl),
|
||||
title: 'Twitter',
|
||||
link: '@CakeWalletXMR',
|
||||
image: _twitterImage,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_changeNowUrl),
|
||||
title: 'ChangeNow',
|
||||
link: 'support@changenow.io',
|
||||
image: _changeNowImage,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_morphUrl),
|
||||
title: 'Morph',
|
||||
link: 'support@morphtoken.com',
|
||||
image: _morphImage,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () => _launchUrl(_xmrToUrl),
|
||||
title: 'XMR.to',
|
||||
link: 'support@xmr.to',
|
||||
image: _xmrBtcImage,
|
||||
attribute: Attributes.link),
|
||||
SettingsItem(
|
||||
onTaped: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
CupertinoPageRoute<void>(
|
||||
builder: (BuildContext context) => DisclaimerPage()));
|
||||
},
|
||||
title: ItemHeaders.termsAndConditions,
|
||||
attribute: Attributes.arrow),
|
||||
SettingsItem(
|
||||
onTaped: () => Navigator.pushNamed(context, Routes.faq),
|
||||
title: ItemHeaders.faq,
|
||||
attribute: Attributes.arrow)
|
||||
]);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void _afterLayout(dynamic _) => _setSettingsList();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
|
||||
}
|
||||
|
||||
Widget _getWidget(SettingsItem item) {
|
||||
switch (item.attribute) {
|
||||
case Attributes.arrow:
|
||||
return SettingsArrowListRow(
|
||||
onTaped: item.onTaped,
|
||||
title: item.title,
|
||||
);
|
||||
case Attributes.header:
|
||||
return SettingsHeaderListRow(
|
||||
title: item.title,
|
||||
);
|
||||
case Attributes.link:
|
||||
return SettingsLinktListRow(
|
||||
onTaped: item.onTaped,
|
||||
title: item.title,
|
||||
link: item.link,
|
||||
image: item.image,
|
||||
);
|
||||
case Attributes.switcher:
|
||||
return SettingsSwitchListRow(
|
||||
title: item.title,
|
||||
);
|
||||
case Attributes.widget:
|
||||
return SettingsTextListRow(
|
||||
onTaped: item.onTaped,
|
||||
title: item.title,
|
||||
widget: item.widget,
|
||||
);
|
||||
case Attributes.rawWidget:
|
||||
return SettingRawWidgetListRow(widgetBuilder: item.widgetBuilder);
|
||||
default:
|
||||
return Offstage();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
settingsStore.setItemHeaders();
|
||||
|
||||
final shortDivider = Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
);
|
||||
|
||||
final longDivider = Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
);
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
longDivider,
|
||||
ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: _items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _items[index];
|
||||
|
||||
Widget divider;
|
||||
|
||||
if (item.attribute == Attributes.header || item == _items.last) {
|
||||
divider = longDivider;
|
||||
} else if (_items[index + 1].attribute == Attributes.header){
|
||||
divider = longDivider;
|
||||
} else {
|
||||
divider = shortDivider;
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
_getWidget(item),
|
||||
divider
|
||||
],
|
||||
);
|
||||
}),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
child: ListTile(
|
||||
title: Center(
|
||||
child: Text(
|
||||
settingsStore.itemHeaders[ItemHeaders.version],
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color)
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _setBalance(BuildContext context) async {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final items = BalanceDisplayMode.all;
|
||||
final selectedItem = items.indexOf(settingsStore.balanceDisplayMode);
|
||||
|
||||
await showDialog<void>(
|
||||
builder: (_) => Picker(
|
||||
items: items,
|
||||
selectedAtIndex: selectedItem,
|
||||
title: S.of(context).please_select,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
onItemSelected: (BalanceDisplayMode mode) async =>
|
||||
await settingsStore.setCurrentBalanceDisplayMode(
|
||||
balanceDisplayMode: mode)),
|
||||
context: context);
|
||||
}
|
||||
|
||||
Future<void> _setCurrency(BuildContext context) async {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final items = FiatCurrency.all;
|
||||
final selectedItem = items.indexOf(settingsStore.fiatCurrency);
|
||||
|
||||
await showDialog<void>(
|
||||
builder: (_) => Picker(
|
||||
items: items,
|
||||
selectedAtIndex: selectedItem,
|
||||
title: S.of(context).please_select,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
onItemSelected: (FiatCurrency currency) async =>
|
||||
await settingsStore.setCurrentFiatCurrency(currency: currency)),
|
||||
context: context);
|
||||
}
|
||||
|
||||
Future<void> _setTransactionPriority(BuildContext context) async {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final items = TransactionPriority.all;
|
||||
final selectedItem = items.indexOf(settingsStore.transactionPriority);
|
||||
|
||||
await showDialog<void>(
|
||||
builder: (_) => Picker(
|
||||
items: items,
|
||||
selectedAtIndex: selectedItem,
|
||||
title: S.of(context).please_select,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
onItemSelected: (TransactionPriority priority) async =>
|
||||
await settingsStore.setCurrentTransactionPriority(
|
||||
priority: priority)),
|
||||
context: context);
|
||||
return SectionStandardList(
|
||||
sectionCount: settingsViewModel.sections.length,
|
||||
itemCounter: (int sectionIndex) {
|
||||
if (sectionIndex < settingsViewModel.sections.length) {
|
||||
return settingsViewModel.sections[sectionIndex].length;
|
||||
}
|
||||
|
||||
return 0;
|
||||
},
|
||||
itemBuilder: (_, sectionIndex, itemIndex) {
|
||||
final item = settingsViewModel.sections[sectionIndex][itemIndex];
|
||||
|
||||
if (item is PickerListItem) {
|
||||
return Observer(builder: (_) {
|
||||
return SettingsPickerCell<dynamic>(
|
||||
title: item.title,
|
||||
selectedItem: item.selectedItem(),
|
||||
items: item.items);
|
||||
});
|
||||
}
|
||||
|
||||
if (item is SwitcherListItem) {
|
||||
return Observer(builder: (_) {
|
||||
return SettingsSwitcherCell(
|
||||
title: item.title,
|
||||
value: item.value(),
|
||||
onValueChange: item.onValueChange);
|
||||
});
|
||||
}
|
||||
|
||||
if (item is RegularListItem) {
|
||||
return SettingsCellWithArrow(title: item.title);
|
||||
}
|
||||
|
||||
if (item is LinkListItem) {
|
||||
return SettingsLinkProviderCell(
|
||||
title: item.title,
|
||||
icon: item.icon,
|
||||
link: item.link,
|
||||
linkTitle: item.linkTitle);
|
||||
}
|
||||
|
||||
return Container();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
|
||||
class SettingsArrowListRow extends StatelessWidget {
|
||||
SettingsArrowListRow({@required this.onTaped, this.title});
|
||||
|
||||
final VoidCallback onTaped;
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final _cakeArrowImage = Image.asset('assets/images/select_arrow.png',
|
||||
color: Theme.of(context).primaryTextTheme.caption.color);
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
title: Observer(
|
||||
builder: (_) => Text(
|
||||
settingsStore.itemHeaders[title],
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
)),
|
||||
trailing: _cakeArrowImage,
|
||||
onTap: onTaped),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
|
||||
class SettingsCellWithArrow extends StandardListRow {
|
||||
SettingsCellWithArrow({@required String title})
|
||||
: super(title: title, isSelected: false);
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) =>
|
||||
Image.asset('assets/images/select_arrow.png',
|
||||
color: Theme.of(context).primaryTextTheme.caption.color);
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class SettingsHeaderListRow extends StatelessWidget {
|
||||
SettingsHeaderListRow({this.title});
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
SizedBox(
|
||||
height: 20.0,
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 20.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Observer(
|
||||
builder: (_) => Text(
|
||||
title.isNotEmpty
|
||||
? settingsStore.itemHeaders[title]
|
||||
: '',
|
||||
style: TextStyle(
|
||||
fontSize: 15.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 14.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingsLinktListRow extends StatelessWidget {
|
||||
SettingsLinktListRow(
|
||||
{@required this.onTaped, this.title, this.link, this.image});
|
||||
|
||||
final VoidCallback onTaped;
|
||||
final String title;
|
||||
final String link;
|
||||
final Image image;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
image != null ? image : Offstage(),
|
||||
Container(
|
||||
padding: image != null ? EdgeInsets.only(left: 10) : null,
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
trailing: Text(
|
||||
link,
|
||||
style: TextStyle(fontSize: 14.0, color: Colors.blue),
|
||||
),
|
||||
onTap: onTaped,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
|
||||
class SettingsLinkProviderCell extends StandardListRow {
|
||||
SettingsLinkProviderCell(
|
||||
{@required String title,
|
||||
@required this.icon,
|
||||
@required this.link,
|
||||
@required this.linkTitle})
|
||||
: super(title: title, isSelected: false);
|
||||
|
||||
final String icon;
|
||||
final String link;
|
||||
final String linkTitle;
|
||||
|
||||
@override
|
||||
Widget buildLeading(BuildContext context) =>
|
||||
icon != null ? Image.asset(icon) : null;
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) => Text(linkTitle,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0, fontWeight: FontWeight.w500, color: Colors.blue));
|
||||
}
|
38
lib/src/screens/settings/widgets/settings_picker_cell.dart
Normal file
|
@ -0,0 +1,38 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
class SettingsPickerCell<ItemType> extends StandardListRow {
|
||||
SettingsPickerCell({@required String title, this.selectedItem, this.items})
|
||||
: super(
|
||||
title: title,
|
||||
isSelected: false,
|
||||
onTap: (BuildContext context) async {
|
||||
final selectedAtIndex = items.indexOf(selectedItem);
|
||||
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => Picker(
|
||||
items: items,
|
||||
selectedAtIndex: selectedAtIndex,
|
||||
title: S.current.please_select,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
onItemSelected: (Object _) {}));
|
||||
});
|
||||
|
||||
final ItemType selectedItem;
|
||||
final List<ItemType> items;
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) {
|
||||
return Text(
|
||||
selectedItem.toString(),
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class SettingsPickerRaw extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement build
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class SettingRawWidgetListRow extends StatelessWidget {
|
||||
SettingRawWidgetListRow({@required this.widgetBuilder});
|
||||
|
||||
final WidgetBuilder widgetBuilder;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: widgetBuilder(context) ?? Container(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/theme_changer.dart';
|
||||
import 'package:cake_wallet/themes.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_switch.dart';
|
||||
|
||||
class SettingsSwitchListRow extends StatelessWidget {
|
||||
SettingsSwitchListRow({@required this.title});
|
||||
|
||||
final String title;
|
||||
|
||||
Widget _getSwitch(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final _themeChanger = Provider.of<ThemeChanger>(context);
|
||||
|
||||
if (settingsStore.itemHeaders[title] ==
|
||||
S.of(context).settings_save_recipient_address) {
|
||||
return Observer(
|
||||
builder: (_) => StandartSwitch(
|
||||
value: settingsStore.shouldSaveRecipientAddress,
|
||||
onTaped: () {
|
||||
final _currentValue = !settingsStore.shouldSaveRecipientAddress;
|
||||
settingsStore.setSaveRecipientAddress(
|
||||
shouldSaveRecipientAddress: _currentValue);
|
||||
}));
|
||||
}
|
||||
|
||||
if (settingsStore.itemHeaders[title] ==
|
||||
S.of(context).settings_allow_biometrical_authentication) {
|
||||
return Observer(
|
||||
builder: (_) => StandartSwitch(
|
||||
value: settingsStore.allowBiometricalAuthentication,
|
||||
onTaped: () {
|
||||
final _currentValue =
|
||||
!settingsStore.allowBiometricalAuthentication;
|
||||
settingsStore.setAllowBiometricalAuthentication(
|
||||
allowBiometricalAuthentication: _currentValue);
|
||||
}));
|
||||
}
|
||||
|
||||
if (settingsStore.itemHeaders[title] == S.of(context).settings_dark_mode) {
|
||||
return Observer(
|
||||
builder: (_) => StandartSwitch(
|
||||
value: settingsStore.isDarkTheme,
|
||||
onTaped: () {
|
||||
final _currentValue = !settingsStore.isDarkTheme;
|
||||
settingsStore.saveDarkTheme(isDarkTheme: _currentValue);
|
||||
_themeChanger.setTheme(
|
||||
_currentValue ? Themes.darkTheme : Themes.lightTheme);
|
||||
}));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
title: Observer(
|
||||
builder: (_) => Text(settingsStore.itemHeaders[title],
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Theme.of(context).primaryTextTheme.title.color)),
|
||||
),
|
||||
trailing: _getSwitch(context)),
|
||||
);
|
||||
}
|
||||
}
|
16
lib/src/screens/settings/widgets/settings_switcher_cell.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_switch.dart';
|
||||
|
||||
class SettingsSwitcherCell extends StandardListRow {
|
||||
SettingsSwitcherCell(
|
||||
{@required String title, @required this.value, this.onValueChange})
|
||||
: super(title: title, isSelected: false);
|
||||
|
||||
final bool value;
|
||||
final void Function(bool value) onValueChange;
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) =>
|
||||
StandartSwitch(value: value, onTaped: () => onValueChange(!value));
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
|
||||
class SettingsTextListRow extends StatelessWidget {
|
||||
SettingsTextListRow({@required this.onTaped, this.title, this.widget});
|
||||
|
||||
final VoidCallback onTaped;
|
||||
final String title;
|
||||
final Widget widget;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: ListTile(
|
||||
contentPadding: EdgeInsets.only(left: 24.0, right: 24.0),
|
||||
title: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Observer(
|
||||
builder: (_) => Text(
|
||||
settingsStore.itemHeaders[title],
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
)),
|
||||
),
|
||||
Flexible(
|
||||
child: widget
|
||||
)
|
||||
],
|
||||
),
|
||||
onTap: onTaped,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_keys_store.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||
|
||||
class ShowKeysPage extends BasePage {
|
||||
@override
|
||||
String get title => S.current.wallet_keys;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final walletKeysStore = Provider.of<WalletKeysStore>(context);
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
final keysMap = {
|
||||
S.of(context).view_key_public: walletKeysStore.publicViewKey,
|
||||
S.of(context).spend_key_private: walletKeysStore.privateSpendKey
|
||||
};
|
||||
|
||||
if (walletKeysStore.privateViewKey.isNotEmpty) {
|
||||
keysMap[S.of(context).view_key_private] =
|
||||
walletKeysStore.privateViewKey;
|
||||
}
|
||||
|
||||
if (walletKeysStore.publicSpendKey.isNotEmpty) {
|
||||
keysMap[S.of(context).spend_key_public] =
|
||||
walletKeysStore.publicSpendKey;
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: keysMap.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final key = keysMap.keys.elementAt(index);
|
||||
final value = keysMap.values.elementAt(index);
|
||||
|
||||
final isDrawTop = index == 0 ? true : false;
|
||||
final isDrawBottom = index == keysMap.length - 1 ? true : false;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: value));
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_key_to_clipboard(key),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(seconds: 1),
|
||||
));
|
||||
},
|
||||
child: StandartListRow(
|
||||
title: key + ':',
|
||||
value: value,
|
||||
isDrawTop: isDrawTop,
|
||||
isDrawBottom: isDrawBottom,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
|
@ -3,8 +3,8 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/view_model/address_list/address_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/core/AddressLabelValidator.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
|
||||
import 'package:cake_wallet/core/address_label_validator.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';
|
||||
|
@ -21,7 +21,7 @@ class AddressEditOrCreatePage extends BasePage {
|
|||
print(addressEditOrCreateViewModel.label);
|
||||
}
|
||||
|
||||
final AddressEditOrCreateViewModel addressEditOrCreateViewModel;
|
||||
final WalletAddressEditOrCreateViewModel addressEditOrCreateViewModel;
|
||||
final GlobalKey<FormState> _formKey;
|
||||
final TextEditingController _labelController;
|
||||
|
||||
|
|
|
@ -1,52 +1,21 @@
|
|||
import 'package:cake_wallet/src/domain/monero/monero_transaction_info.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
|
||||
import 'package:cake_wallet/src/domain/monero/monero_transaction_info.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
|
||||
import 'package:cake_wallet/src/stores/settings/settings_store.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
|
||||
class TransactionDetailsPage extends BasePage {
|
||||
TransactionDetailsPage({this.transactionInfo});
|
||||
|
||||
final TransactionInfo transactionInfo;
|
||||
|
||||
@override
|
||||
String get title => S.current.transaction_details_title;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
|
||||
return TransactionDetailsForm(
|
||||
transactionInfo: transactionInfo, settingsStore: settingsStore);
|
||||
}
|
||||
}
|
||||
|
||||
class TransactionDetailsForm extends StatefulWidget {
|
||||
TransactionDetailsForm(
|
||||
{@required this.transactionInfo, @required this.settingsStore});
|
||||
|
||||
final TransactionInfo transactionInfo;
|
||||
final SettingsStore settingsStore;
|
||||
|
||||
@override
|
||||
TransactionDetailsFormState createState() => TransactionDetailsFormState();
|
||||
}
|
||||
|
||||
class TransactionDetailsFormState extends State<TransactionDetailsForm> {
|
||||
final _items = List<StandartListItem>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
final _dateFormat = widget.settingsStore.getCurrentDateFormat(
|
||||
formatUSA: "yyyy.MM.dd, HH:mm", formatDefault: "dd.MM.yyyy, HH:mm");
|
||||
final tx = widget.transactionInfo;
|
||||
TransactionDetailsPage({this.transactionInfo}) : _items = [] {
|
||||
// FIXME
|
||||
// final _dateFormat = widget.settingsStore.getCurrentDateFormat(
|
||||
// formatUSA: "yyyy.MM.dd, HH:mm", formatDefault: "dd.MM.yyyy, HH:mm");
|
||||
final dateFormat = DateFormat('dd.MM.yyyy, HH:mm');
|
||||
final tx = transactionInfo;
|
||||
|
||||
if (tx is MoneroTransactionInfo) {
|
||||
final items = [
|
||||
|
@ -54,7 +23,31 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
|
|||
title: S.current.transaction_details_transaction_id, value: tx.id),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_date,
|
||||
value: _dateFormat.format(tx.date)),
|
||||
value: dateFormat.format(tx.date)),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_height, value: '${tx.height}'),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_amount,
|
||||
value: tx.amountFormatted())
|
||||
];
|
||||
// FIXME
|
||||
// if (widget.settingsStore.shouldSaveRecipientAddress &&
|
||||
// tx.recipientAddress != null) {
|
||||
// items.add(StandartListItem(
|
||||
// title: S.current.transaction_details_recipient_address,
|
||||
// value: tx.recipientAddress));
|
||||
// }
|
||||
|
||||
_items.addAll(items);
|
||||
}
|
||||
|
||||
if (tx is BitcoinTransactionInfo) {
|
||||
final items = [
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_transaction_id, value: tx.id),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_date,
|
||||
value: dateFormat.format(tx.date)),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_height, value: '${tx.height}'),
|
||||
StandartListItem(
|
||||
|
@ -62,33 +55,31 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
|
|||
value: tx.amountFormatted())
|
||||
];
|
||||
|
||||
if (widget.settingsStore.shouldSaveRecipientAddress &&
|
||||
tx.recipientAddress != null) {
|
||||
items.add(StandartListItem(
|
||||
title: S.current.transaction_details_recipient_address,
|
||||
value: tx.recipientAddress));
|
||||
}
|
||||
|
||||
_items.addAll(items);
|
||||
}
|
||||
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String get title => S.current.transaction_details_title;
|
||||
|
||||
final TransactionInfo transactionInfo;
|
||||
|
||||
final List<StandartListItem> _items;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 20),
|
||||
child: ListView.separated(
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: _items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _items[index];
|
||||
|
@ -108,8 +99,7 @@ class TransactionDetailsFormState extends State<TransactionDetailsForm> {
|
|||
),
|
||||
);
|
||||
},
|
||||
child:
|
||||
StandartListRow(
|
||||
child: StandartListRow(
|
||||
title: '${item.title}:',
|
||||
value: item.value,
|
||||
isDrawTop: isDrawTop,
|
||||
|
|
63
lib/src/screens/wallet_keys/wallet_keys_page.dart
Normal file
|
@ -0,0 +1,63 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
|
||||
|
||||
class WalletKeysPage extends BasePage {
|
||||
WalletKeysPage(this.walletKeysViewModel);
|
||||
|
||||
@override
|
||||
String get title => S.current.wallet_keys;
|
||||
|
||||
final WalletKeysViewModel walletKeysViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
return ListView.separated(
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.title
|
||||
.backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
),
|
||||
itemCount: walletKeysViewModel.items.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
final item = walletKeysViewModel.items[index];
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: item.value));
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_key_to_clipboard(item.title),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(seconds: 1),
|
||||
));
|
||||
},
|
||||
child: StandartListRow(
|
||||
title: item.title + ':',
|
||||
value: item.value,
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
|
@ -1,177 +1,188 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet_list/wallet_list_store.dart';
|
||||
import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_list/wallet_menu.dart';
|
||||
import 'package:cake_wallet/src/screens/wallet_list/widgets/wallet_tile.dart';
|
||||
|
||||
class WalletListPage extends BasePage {
|
||||
WalletListPage({this.walletListViewModel});
|
||||
|
||||
final WalletListViewModel walletListViewModel;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => WalletListBody();
|
||||
Widget body(BuildContext context) =>
|
||||
WalletListBody(walletListViewModel: walletListViewModel);
|
||||
}
|
||||
|
||||
class WalletListBody extends StatefulWidget {
|
||||
WalletListBody({this.walletListViewModel});
|
||||
|
||||
final WalletListViewModel walletListViewModel;
|
||||
|
||||
@override
|
||||
WalletListBodyState createState() => WalletListBodyState();
|
||||
}
|
||||
|
||||
class WalletListBodyState extends State<WalletListBody> {
|
||||
final moneroIcon = Image.asset('assets/images/monero.png', height: 24, width: 24);
|
||||
WalletListStore _walletListStore;
|
||||
ScrollController scrollController = ScrollController();
|
||||
final moneroIcon =
|
||||
Image.asset('assets/images/monero.png', height: 24, width: 24);
|
||||
final bitcoinIcon =
|
||||
Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
||||
final scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final walletStore = Provider.of<WalletStore>(context);
|
||||
_walletListStore = Provider.of<WalletListStore>(context);
|
||||
|
||||
final newWalletImage = Image.asset('assets/images/new_wallet.png',
|
||||
height: 12,
|
||||
width: 12,
|
||||
color: Palette.oceanBlue);
|
||||
height: 12, width: 12, color: Palette.oceanBlue);
|
||||
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
|
||||
height: 12,
|
||||
width: 12,
|
||||
color: Theme.of(context).primaryTextTheme.title.color);
|
||||
|
||||
return SafeArea(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 20),
|
||||
content: Container(
|
||||
child: Observer(
|
||||
builder: (_) => ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (_, index) => Divider(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
height: 16),
|
||||
itemCount: _walletListStore.wallets.length,
|
||||
itemBuilder: (__, index) {
|
||||
final wallet = _walletListStore.wallets[index];
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 20),
|
||||
content: Container(
|
||||
child: Observer(
|
||||
builder: (_) => ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (_, index) => Divider(
|
||||
color: Theme.of(context).backgroundColor, height: 16),
|
||||
itemCount: widget.walletListViewModel.wallets.length,
|
||||
itemBuilder: (__, index) {
|
||||
final wallet = widget.walletListViewModel.wallets[index];
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
// String shortAddress = '';
|
||||
|
||||
final isCurrentWallet =
|
||||
_walletListStore.isCurrentWallet(wallet);
|
||||
// if (wallet.isCurrent) {
|
||||
// shortAddress = wallet.address;
|
||||
// shortAddress = shortAddress.replaceRange(
|
||||
// 4, shortAddress.length - 4, '...');
|
||||
// }
|
||||
|
||||
String shortAddress = '';
|
||||
final walletMenu = WalletMenu(context, widget.walletListViewModel);
|
||||
final items =
|
||||
walletMenu.generateItemsForWalletMenu(wallet.isCurrent);
|
||||
final colors = walletMenu
|
||||
.generateColorsForWalletMenu(wallet.isCurrent);
|
||||
final images = walletMenu
|
||||
.generateImagesForWalletMenu(wallet.isCurrent);
|
||||
|
||||
if (isCurrentWallet) {
|
||||
shortAddress = walletStore.subaddress.address;
|
||||
shortAddress = shortAddress.replaceRange(4, shortAddress.length - 4, '...');
|
||||
}
|
||||
return Container(
|
||||
height: 108,
|
||||
width: double.infinity,
|
||||
child: CustomScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
controller: scrollController,
|
||||
slivers: <Widget>[
|
||||
SliverPersistentHeader(
|
||||
pinned: false,
|
||||
floating: true,
|
||||
delegate: WalletTile(
|
||||
min: screenWidth - 228,
|
||||
max: screenWidth,
|
||||
image: _imageFor(type: wallet.type),
|
||||
walletName: wallet.name,
|
||||
walletAddress: '', //shortAddress,
|
||||
isCurrent: wallet.isCurrent),
|
||||
),
|
||||
SliverList(
|
||||
delegate:
|
||||
SliverChildBuilderDelegate((context, index) {
|
||||
final item = items[index];
|
||||
final color = colors[index];
|
||||
final image = images[index];
|
||||
|
||||
final walletMenu = WalletMenu(context);
|
||||
final items = walletMenu.generateItemsForWalletMenu(isCurrentWallet);
|
||||
final colors = walletMenu.generateColorsForWalletMenu(isCurrentWallet);
|
||||
final images = walletMenu.generateImagesForWalletMenu(isCurrentWallet);
|
||||
final radius = index == 0 ? 12.0 : 0.0;
|
||||
|
||||
return Container(
|
||||
height: 108,
|
||||
width: double.infinity,
|
||||
child: CustomScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
controller: scrollController,
|
||||
slivers: <Widget>[
|
||||
SliverPersistentHeader(
|
||||
pinned: false,
|
||||
floating: true,
|
||||
delegate: WalletTile(
|
||||
min: screenWidth - 228,
|
||||
max: screenWidth,
|
||||
image: moneroIcon,
|
||||
walletName: wallet.name,
|
||||
walletAddress: shortAddress,
|
||||
isCurrent: isCurrentWallet
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
scrollController.animateTo(0.0,
|
||||
duration: Duration(milliseconds: 500),
|
||||
curve: Curves.fastOutSlowIn);
|
||||
walletMenu.action(
|
||||
walletMenu.listItems.indexOf(item),
|
||||
wallet,
|
||||
wallet.isCurrent);
|
||||
},
|
||||
child: Container(
|
||||
height: 108,
|
||||
width: 108,
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 5, right: 5),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(radius),
|
||||
bottomLeft: Radius.circular(radius)),
|
||||
color: color),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
item,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
|
||||
final item = items[index];
|
||||
final color = colors[index];
|
||||
final image = images[index];
|
||||
|
||||
final radius = index == 0 ? 12.0 : 0.0;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
scrollController.animateTo(0.0, duration: Duration(milliseconds: 500), curve: Curves.fastOutSlowIn);
|
||||
walletMenu.action(
|
||||
walletMenu.listItems.indexOf(item), wallet, isCurrentWallet);
|
||||
},
|
||||
child: Container(
|
||||
height: 108,
|
||||
width: 108,
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 5, right: 5),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(radius),
|
||||
bottomLeft: Radius.circular(radius)
|
||||
),
|
||||
color: color
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(height: 5),
|
||||
Text(
|
||||
item,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: items.length
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}, childCount: items.length))
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
bottomSection: Column(children: <Widget>[
|
||||
PrimaryImageButton(
|
||||
onPressed: () => Navigator.of(context).pushNamed(Routes.newWalletType),
|
||||
image: newWalletImage,
|
||||
text: S.of(context).wallet_list_create_new_wallet,
|
||||
color: Colors.white,
|
||||
textColor: Palette.oceanBlue,
|
||||
borderColor: Palette.oceanBlue,
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
PrimaryImageButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pushNamed(Routes.restoreWalletOptions),
|
||||
),
|
||||
bottomSection: Column(children: <Widget>[
|
||||
PrimaryImageButton(
|
||||
onPressed: () =>
|
||||
Navigator.of(context).pushNamed(Routes.newWalletType),
|
||||
image: newWalletImage,
|
||||
text: S.of(context).wallet_list_create_new_wallet,
|
||||
color: Colors.white,
|
||||
textColor: Palette.oceanBlue,
|
||||
borderColor: Palette.oceanBlue,
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
PrimaryImageButton(
|
||||
onPressed: () => Navigator.of(context)
|
||||
.pushNamed(Routes.restoreWalletType),
|
||||
image: restoreWalletImage,
|
||||
text: S.of(context).wallet_list_restore_wallet,
|
||||
color: Theme.of(context).primaryTextTheme.overline.color,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color)
|
||||
])),
|
||||
)
|
||||
);
|
||||
])),
|
||||
));
|
||||
}
|
||||
|
||||
Image _imageFor({WalletType type}) {
|
||||
switch (type) {
|
||||
case WalletType.bitcoin:
|
||||
return bitcoinIcon;
|
||||
case WalletType.monero:
|
||||
return moneroIcon;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|