mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-04-27 17:24:44 +00:00
commit
e8db93a079
121 changed files with 5077 additions and 2677 deletions
.gitignore
assets
electrum_server_list.yml
images
ios
lib
bitcoin
bitcoin_address_record.dartbitcoin_transaction_credentials.dartbitcoin_transaction_history.dartbitcoin_transaction_info.dartbitcoin_transaction_no_inputs_exception.dartbitcoin_transaction_wrong_balance_exception.dartbitcoin_unspent.dartbitcoin_wallet.dartbitcoin_wallet_service.dartelectrum.dartpending_bitcoin_transaction.dartscript_hash.dartutils.dart
core
di.dartgenerated
main.dartmonero
palette.dartreactions
router.dartsrc
domain/common
screens
contact
dashboard
exchange
exchange_trade
exchange_confirm_page.dartexchange_trade_item.dartexchange_trade_page.dartinformation_page.dart
widgets
monero_accounts
nodes
send
settings
transaction_details
wallet_list
stores/settings
widgets
store
themes.dartutils
view_model
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -89,4 +89,6 @@ android/key.properties
|
|||
**/tool/.secrets-prod.json
|
||||
**/lib/.secrets.g.dart
|
||||
|
||||
vendor/
|
||||
vendor/
|
||||
|
||||
android/app/.cxx/**
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
-
|
||||
uri: electrum2.hodlister.co:50002
|
||||
uri: electrum2.hodlister.co:50002
|
||||
-
|
||||
uri: bitcoin.electrumx.multicoin.co:50002
|
BIN
assets/images/2.0x/back_vector.png
Normal file
BIN
assets/images/2.0x/back_vector.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 337 B |
BIN
assets/images/2.0x/question_mark.png
Normal file
BIN
assets/images/2.0x/question_mark.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.2 KiB |
BIN
assets/images/3.0x/back_vector.png
Normal file
BIN
assets/images/3.0x/back_vector.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 459 B |
BIN
assets/images/3.0x/question_mark.png
Normal file
BIN
assets/images/3.0x/question_mark.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 1.6 KiB |
BIN
assets/images/back_vector.png
Normal file
BIN
assets/images/back_vector.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 251 B |
BIN
assets/images/question_mark.png
Normal file
BIN
assets/images/question_mark.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 713 B |
1
ios/Flutter/.last_build_id
Normal file
1
ios/Flutter/.last_build_id
Normal file
|
@ -0,0 +1 @@
|
|||
a2dce69f54a78f5b00e19850e4b2d402
|
|
@ -44,6 +44,8 @@ PODS:
|
|||
- Flutter
|
||||
- shared_preferences (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_linux (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_macos (0.0.1):
|
||||
- Flutter
|
||||
- shared_preferences_web (0.0.1):
|
||||
|
@ -51,6 +53,8 @@ PODS:
|
|||
- SwiftProtobuf (1.8.0)
|
||||
- url_launcher (0.0.1):
|
||||
- Flutter
|
||||
- url_launcher_linux (0.0.1):
|
||||
- Flutter
|
||||
- url_launcher_macos (0.0.1):
|
||||
- Flutter
|
||||
- url_launcher_web (0.0.1):
|
||||
|
@ -71,9 +75,11 @@ DEPENDENCIES:
|
|||
- path_provider_macos (from `.symlinks/plugins/path_provider_macos/ios`)
|
||||
- share (from `.symlinks/plugins/share/ios`)
|
||||
- shared_preferences (from `.symlinks/plugins/shared_preferences/ios`)
|
||||
- shared_preferences_linux (from `.symlinks/plugins/shared_preferences_linux/ios`)
|
||||
- shared_preferences_macos (from `.symlinks/plugins/shared_preferences_macos/ios`)
|
||||
- shared_preferences_web (from `.symlinks/plugins/shared_preferences_web/ios`)
|
||||
- url_launcher (from `.symlinks/plugins/url_launcher/ios`)
|
||||
- url_launcher_linux (from `.symlinks/plugins/url_launcher_linux/ios`)
|
||||
- url_launcher_macos (from `.symlinks/plugins/url_launcher_macos/ios`)
|
||||
- url_launcher_web (from `.symlinks/plugins/url_launcher_web/ios`)
|
||||
|
||||
|
@ -111,12 +117,16 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/share/ios"
|
||||
shared_preferences:
|
||||
:path: ".symlinks/plugins/shared_preferences/ios"
|
||||
shared_preferences_linux:
|
||||
:path: ".symlinks/plugins/shared_preferences_linux/ios"
|
||||
shared_preferences_macos:
|
||||
:path: ".symlinks/plugins/shared_preferences_macos/ios"
|
||||
shared_preferences_web:
|
||||
:path: ".symlinks/plugins/shared_preferences_web/ios"
|
||||
url_launcher:
|
||||
:path: ".symlinks/plugins/url_launcher/ios"
|
||||
url_launcher_linux:
|
||||
:path: ".symlinks/plugins/url_launcher_linux/ios"
|
||||
url_launcher_macos:
|
||||
:path: ".symlinks/plugins/url_launcher_macos/ios"
|
||||
url_launcher_web:
|
||||
|
@ -138,10 +148,12 @@ SPEC CHECKSUMS:
|
|||
path_provider_macos: f760a3c5b04357c380e2fddb6f9db6f3015897e0
|
||||
share: 0b2c3e82132f5888bccca3351c504d0003b3b410
|
||||
shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d
|
||||
shared_preferences_linux: afefbfe8d921e207f01ede8b60373d9e3b566b78
|
||||
shared_preferences_macos: f3f29b71ccbb56bf40c9dd6396c9acf15e214087
|
||||
shared_preferences_web: 141cce0c3ed1a1c5bf2a0e44f52d31eeb66e5ea9
|
||||
SwiftProtobuf: 2cbd9409689b7df170d82a92a33443c8e3e14a70
|
||||
url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef
|
||||
url_launcher_linux: ac237cb7a8058736e4aae38bdbcc748a4b394cc0
|
||||
url_launcher_macos: fd7894421cd39320dce5f292fc99ea9270b2a313
|
||||
url_launcher_web: e5527357f037c87560776e36436bf2b0288b965c
|
||||
|
||||
|
|
|
@ -319,7 +319,6 @@
|
|||
/* Begin XCBuildConfiguration section */
|
||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
|
@ -373,7 +372,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
CURRENT_PROJECT_VERSION = 12;
|
||||
DEVELOPMENT_TEAM = 32J6BB6VUS;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -387,7 +386,7 @@
|
|||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.28;
|
||||
MARKETING_VERSION = 3.2.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cakewallet.cakewallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
|
@ -399,7 +398,6 @@
|
|||
};
|
||||
97C147031CF9000F007C117D /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
|
@ -455,7 +453,6 @@
|
|||
};
|
||||
97C147041CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
|
@ -509,7 +506,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
CURRENT_PROJECT_VERSION = 12;
|
||||
DEVELOPMENT_TEAM = 32J6BB6VUS;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -523,7 +520,7 @@
|
|||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.28;
|
||||
MARKETING_VERSION = 3.2.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cakewallet.cakewallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
|
@ -540,7 +537,7 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
CURRENT_PROJECT_VERSION = 12;
|
||||
DEVELOPMENT_TEAM = 32J6BB6VUS;
|
||||
ENABLE_BITCODE = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
@ -554,7 +551,7 @@
|
|||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Flutter",
|
||||
);
|
||||
MARKETING_VERSION = 3.1.28;
|
||||
MARKETING_VERSION = 3.2.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cakewallet.cakewallet;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Cake Wallet requires access to your phone’s camera.</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import 'dart:convert';
|
||||
|
||||
class BitcoinAddressRecord {
|
||||
BitcoinAddressRecord(this.address, {this.label});
|
||||
BitcoinAddressRecord(this.address, {this.label, this.index});
|
||||
|
||||
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
||||
return BitcoinAddressRecord(decoded['address'] as String,
|
||||
label: decoded['label'] as String);
|
||||
label: decoded['label'] as String, index: decoded['index'] as int);
|
||||
}
|
||||
|
||||
final String address;
|
||||
int index;
|
||||
String label;
|
||||
|
||||
String toJSON() => json.encode({'label': label, 'address': address});
|
||||
String toJSON() =>
|
||||
json.encode({'label': label, 'address': address, 'index': index});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
|
||||
|
||||
class BitcoinTransactionCredentials {
|
||||
const BitcoinTransactionCredentials(this.address, this.amount);
|
||||
BitcoinTransactionCredentials(this.address, this.amount, this.priority);
|
||||
|
||||
final String address;
|
||||
final double amount;
|
||||
TransactionPriority priority;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,6 @@ import 'package:cake_wallet/bitcoin/file.dart';
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
|
||||
|
||||
part 'bitcoin_transaction_history.g.dart';
|
||||
|
||||
|
@ -24,100 +22,176 @@ abstract class BitcoinTransactionHistoryBase
|
|||
{this.eclient, String dirPath, @required String password})
|
||||
: path = '$dirPath/$_transactionsHistoryFileName',
|
||||
_password = password,
|
||||
_height = 0;
|
||||
_height = 0,
|
||||
_isUpdating = false {
|
||||
transactions = ObservableMap<String, BitcoinTransactionInfo>();
|
||||
}
|
||||
|
||||
BitcoinWalletBase wallet;
|
||||
final ElectrumClient eclient;
|
||||
final String path;
|
||||
final String _password;
|
||||
int _height;
|
||||
bool _isUpdating;
|
||||
|
||||
Future<void> init() async {
|
||||
final info = await _read();
|
||||
_height = info['height'] as int ?? _height;
|
||||
transactions = ObservableList.of(
|
||||
info['transactions'] as List<BitcoinTransactionInfo> ??
|
||||
<BitcoinTransactionInfo>[]);
|
||||
await _load();
|
||||
}
|
||||
|
||||
@override
|
||||
Future update() async {
|
||||
await super.update();
|
||||
_updateHeight();
|
||||
if (_isUpdating) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_isUpdating = true;
|
||||
final txs = await fetchTransactions();
|
||||
await add(txs);
|
||||
_isUpdating = false;
|
||||
} catch (_) {
|
||||
_isUpdating = false;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<BitcoinTransactionInfo>> fetchTransactions() async {
|
||||
final addresses = wallet.addresses;
|
||||
Future<Map<String, BitcoinTransactionInfo>> fetchTransactions() async {
|
||||
final histories =
|
||||
addresses.map((record) => eclient.getHistory(address: record.address));
|
||||
wallet.scriptHashes.map((scriptHash) => eclient.getHistory(scriptHash));
|
||||
final _historiesWithDetails = await Future.wait(histories)
|
||||
.then((histories) => histories
|
||||
.map((h) => h.where((tx) => (tx['height'] as int) > _height))
|
||||
// .map((h) => h.where((tx) {
|
||||
// final height = tx['height'] as int ?? 0;
|
||||
// // FIXME: Filter only needed transactions
|
||||
// final _tx = get(tx['tx_hash'] as String);
|
||||
//
|
||||
// return height == 0 || height > _height;
|
||||
// }))
|
||||
.expand((i) => i)
|
||||
.toList())
|
||||
.then((histories) => histories.map((tx) => fetchTransactionInfo(
|
||||
hash: tx['tx_hash'] as String, height: tx['height'] as int)));
|
||||
final historiesWithDetails = await Future.wait(_historiesWithDetails);
|
||||
|
||||
return historiesWithDetails
|
||||
.map((info) => BitcoinTransactionInfo.fromHexAndHeader(
|
||||
info['raw'] as String, info['header'] as Map<String, Object>,
|
||||
addresses: addresses.map((record) => record.address).toList()))
|
||||
.toList();
|
||||
return historiesWithDetails.fold<Map<String, BitcoinTransactionInfo>>(
|
||||
<String, BitcoinTransactionInfo>{}, (acc, tx) {
|
||||
acc[tx.id] = tx;
|
||||
return acc;
|
||||
});
|
||||
}
|
||||
|
||||
Future<Map<String, Object>> fetchTransactionInfo(
|
||||
Future<BitcoinTransactionInfo> fetchTransactionInfo(
|
||||
{@required String hash, @required int height}) async {
|
||||
final rawFetching = eclient.getTransactionRaw(hash: hash);
|
||||
final headerFetching = eclient.getHeader(height: height);
|
||||
final result = await Future.wait([rawFetching, headerFetching]);
|
||||
final raw = result.first as String;
|
||||
final header = result[1] as Map<String, Object>;
|
||||
|
||||
return {'raw': raw, 'header': header};
|
||||
final tx = await eclient.getTransactionExpanded(hash: hash);
|
||||
return BitcoinTransactionInfo.fromElectrumVerbose(tx,
|
||||
height: height, addresses: wallet.addresses);
|
||||
}
|
||||
|
||||
Future<void> add(List<BitcoinTransactionInfo> transactions) async {
|
||||
this.transactions.addAll(transactions);
|
||||
Future<void> add(Map<String, BitcoinTransactionInfo> transactionsList) async {
|
||||
transactionsList.entries.forEach((entry) {
|
||||
_updateOrInsert(entry.value);
|
||||
|
||||
if (entry.value.height > _height) {
|
||||
_height = entry.value.height;
|
||||
}
|
||||
});
|
||||
|
||||
await save();
|
||||
}
|
||||
|
||||
Future<void> addOne(BitcoinTransactionInfo tx) async {
|
||||
transactions.add(tx);
|
||||
_updateOrInsert(tx);
|
||||
|
||||
if (tx.height > _height) {
|
||||
_height = tx.height;
|
||||
}
|
||||
|
||||
await save();
|
||||
}
|
||||
|
||||
Future<void> save() async => writeData(
|
||||
path: path,
|
||||
password: _password,
|
||||
data: json.encode({'height': _height, 'transactions': transactions}));
|
||||
BitcoinTransactionInfo get(String id) => transactions[id];
|
||||
|
||||
Future<void> save() async {
|
||||
final data = json.encode({'height': _height, 'transactions': transactions});
|
||||
|
||||
print('data');
|
||||
print(data);
|
||||
|
||||
await writeData(path: path, password: _password, data: data);
|
||||
}
|
||||
|
||||
@override
|
||||
void updateAsync({void Function() onFinished}) {
|
||||
fetchTransactionsAsync((transaction) => _updateOrInsert(transaction),
|
||||
onFinished: onFinished);
|
||||
}
|
||||
|
||||
@override
|
||||
void fetchTransactionsAsync(
|
||||
void Function(BitcoinTransactionInfo transaction) onTransactionLoaded,
|
||||
{void Function() onFinished}) async {
|
||||
final histories = await Future.wait(wallet.scriptHashes
|
||||
.map((scriptHash) async => await eclient.getHistory(scriptHash)));
|
||||
final transactionsCount =
|
||||
histories.fold<int>(0, (acc, m) => acc + m.length);
|
||||
var counter = 0;
|
||||
|
||||
final batches = histories.map((metaList) =>
|
||||
_fetchBatchOfTransactions(metaList, onTransactionLoaded: (transaction) {
|
||||
onTransactionLoaded(transaction);
|
||||
counter += 1;
|
||||
|
||||
if (counter == transactionsCount) {
|
||||
onFinished?.call();
|
||||
}
|
||||
}));
|
||||
|
||||
await Future.wait(batches);
|
||||
}
|
||||
|
||||
Future<void> _fetchBatchOfTransactions(
|
||||
Iterable<Map<String, dynamic>> metaList,
|
||||
{void Function(BitcoinTransactionInfo tranasaction)
|
||||
onTransactionLoaded}) async =>
|
||||
metaList.forEach((txMeta) => fetchTransactionInfo(
|
||||
hash: txMeta['tx_hash'] as String,
|
||||
height: txMeta['height'] as int)
|
||||
.then((transaction) => onTransactionLoaded(transaction)));
|
||||
|
||||
Future<Map<String, Object>> _read() async {
|
||||
final content = await read(path: path, password: _password);
|
||||
return json.decode(content) as Map<String, Object>;
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
try {
|
||||
final content = await read(path: path, password: _password);
|
||||
final jsoned = json.decode(content) as Map<String, Object>;
|
||||
final height = jsoned['height'] as int;
|
||||
final transactions = (jsoned['transactions'] as List<dynamic>)
|
||||
.map((dynamic row) {
|
||||
if (row is Map<String, Object>) {
|
||||
return BitcoinTransactionInfo.fromJson(row);
|
||||
}
|
||||
final content = await _read();
|
||||
final txs = content['transactions'] as Map<String, Object> ?? {};
|
||||
|
||||
return null;
|
||||
})
|
||||
.where((el) => el != null)
|
||||
.toList();
|
||||
txs.entries.forEach((entry) {
|
||||
final val = entry.value;
|
||||
|
||||
return {'transactions': transactions, 'height': height};
|
||||
} catch (_) {
|
||||
return {'transactions': <BitcoinTransactionInfo>[], 'height': 0};
|
||||
if (val is Map<String, Object>) {
|
||||
final tx = BitcoinTransactionInfo.fromJson(val);
|
||||
_updateOrInsert(tx);
|
||||
}
|
||||
});
|
||||
|
||||
_height = content['height'] as int;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
void _updateOrInsert(BitcoinTransactionInfo transaction) {
|
||||
if (transactions[transaction.id] == null) {
|
||||
transactions[transaction.id] = transaction;
|
||||
} else {
|
||||
final originalTx = transactions[transaction.id];
|
||||
originalTx.confirmations = transaction.confirmations;
|
||||
originalTx.amount = transaction.amount;
|
||||
originalTx.height = transaction.height;
|
||||
originalTx.date ??= transaction.date;
|
||||
originalTx.isPending = transaction.isPending;
|
||||
}
|
||||
}
|
||||
|
||||
void _updateHeight() {
|
||||
final newHeight = transactions.fold(
|
||||
0, (int acc, val) => val.height > acc ? val.height : acc);
|
||||
_height = newHeight > _height ? newHeight : _height;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/src/domain/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
|
||||
import 'package:cake_wallet/src/domain/common/format_amount.dart';
|
||||
|
@ -13,42 +15,97 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
|||
@required int amount,
|
||||
@required TransactionDirection direction,
|
||||
@required bool isPending,
|
||||
@required DateTime date}) {
|
||||
@required DateTime date,
|
||||
@required int confirmations}) {
|
||||
this.height = height;
|
||||
this.amount = amount;
|
||||
this.direction = direction;
|
||||
this.date = date;
|
||||
this.isPending = isPending;
|
||||
this.confirmations = confirmations;
|
||||
}
|
||||
|
||||
factory BitcoinTransactionInfo.fromHexAndHeader(
|
||||
String hex, Map<String, Object> header,
|
||||
{List<String> addresses}) {
|
||||
factory BitcoinTransactionInfo.fromElectrumVerbose(Map<String, Object> obj,
|
||||
{@required List<BitcoinAddressRecord> addresses, @required int height}) {
|
||||
final addressesSet = addresses.map((addr) => addr.address).toSet();
|
||||
final id = obj['txid'] as String;
|
||||
final vins = obj['vin'] as List<Object> ?? [];
|
||||
final vout = (obj['vout'] as List<Object> ?? []);
|
||||
final date = obj['time'] is int
|
||||
? DateTime.fromMillisecondsSinceEpoch((obj['time'] as int) * 1000)
|
||||
: DateTime.now();
|
||||
final confirmations = obj['confirmations'] as int ?? 0;
|
||||
var direction = TransactionDirection.incoming;
|
||||
|
||||
for (dynamic vin in vins) {
|
||||
final vout = vin['vout'] as int;
|
||||
final out = vin['tx']['vout'][vout] as Map;
|
||||
final outAddresses =
|
||||
(out['scriptPubKey']['addresses'] as List<Object>)?.toSet();
|
||||
|
||||
if (outAddresses?.intersection(addressesSet)?.isNotEmpty ?? false) {
|
||||
direction = TransactionDirection.outgoing;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
final amount = vout.fold(0, (int acc, dynamic out) {
|
||||
final outAddresses =
|
||||
out['scriptPubKey']['addresses'] as List<Object> ?? [];
|
||||
final ntrs = outAddresses.toSet().intersection(addressesSet);
|
||||
var amount = acc;
|
||||
|
||||
if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) ||
|
||||
(direction == TransactionDirection.outgoing && ntrs.isEmpty)) {
|
||||
amount += doubleToBitcoinAmount(out['value'] as double ?? 0.0);
|
||||
}
|
||||
|
||||
return amount;
|
||||
});
|
||||
|
||||
return BitcoinTransactionInfo(
|
||||
id: id,
|
||||
height: height,
|
||||
isPending: false,
|
||||
direction: direction,
|
||||
amount: amount,
|
||||
date: date,
|
||||
confirmations: confirmations);
|
||||
}
|
||||
|
||||
factory BitcoinTransactionInfo.fromHexAndHeader(String hex,
|
||||
{List<String> addresses, int height, int timestamp, int confirmations}) {
|
||||
final tx = bitcoin.Transaction.fromHex(hex);
|
||||
var exist = false;
|
||||
var amount = 0;
|
||||
|
||||
tx.outs.forEach((out) {
|
||||
try {
|
||||
final p2pkh = bitcoin.P2PKH(
|
||||
data: PaymentData(output: out.script), network: bitcoin.bitcoin);
|
||||
exist = addresses.contains(p2pkh.data.address);
|
||||
if (addresses != null) {
|
||||
tx.outs.forEach((out) {
|
||||
try {
|
||||
final p2pkh = bitcoin.P2PKH(
|
||||
data: PaymentData(output: out.script), network: bitcoin.bitcoin);
|
||||
exist = addresses.contains(p2pkh.data.address);
|
||||
|
||||
if (exist) {
|
||||
amount += out.value;
|
||||
}
|
||||
} catch (_) {}
|
||||
});
|
||||
if (exist) {
|
||||
amount += out.value;
|
||||
}
|
||||
} catch (_) {}
|
||||
});
|
||||
}
|
||||
|
||||
final date = timestamp != null
|
||||
? DateTime.fromMillisecondsSinceEpoch(timestamp * 1000)
|
||||
: DateTime.now();
|
||||
|
||||
// FIXME: Get transaction is pending
|
||||
return BitcoinTransactionInfo(
|
||||
id: tx.getId(),
|
||||
height: header['block_height'] as int,
|
||||
height: height,
|
||||
isPending: false,
|
||||
direction: TransactionDirection.incoming,
|
||||
amount: amount,
|
||||
date: DateTime.fromMillisecondsSinceEpoch(
|
||||
(header['timestamp'] as int) * 1000));
|
||||
date: date,
|
||||
confirmations: confirmations);
|
||||
}
|
||||
|
||||
factory BitcoinTransactionInfo.fromJson(Map<String, dynamic> data) {
|
||||
|
@ -58,7 +115,8 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
|||
amount: data['amount'] as int,
|
||||
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||
isPending: data['isPending'] as bool);
|
||||
isPending: data['isPending'] as bool,
|
||||
confirmations: data['confirmations'] as int);
|
||||
}
|
||||
|
||||
final String id;
|
||||
|
@ -66,7 +124,8 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
|||
String _fiatAmount;
|
||||
|
||||
@override
|
||||
String amountFormatted() => '${formatAmount(bitcoinAmountToString(amount: amount))} BTC';
|
||||
String amountFormatted() =>
|
||||
'${formatAmount(bitcoinAmountToString(amount: amount))} BTC';
|
||||
|
||||
@override
|
||||
String fiatAmount() => _fiatAmount ?? '';
|
||||
|
@ -75,13 +134,14 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
|||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final m = Map<String, dynamic>();
|
||||
final m = <String, dynamic>{};
|
||||
m['id'] = id;
|
||||
m['height'] = height;
|
||||
m['amount'] = amount;
|
||||
m['direction'] = direction.index;
|
||||
m['date'] = date.millisecondsSinceEpoch;
|
||||
m['isPending'] = isPending;
|
||||
m['confirmations'] = confirmations;
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
|
4
lib/bitcoin/bitcoin_transaction_no_inputs_exception.dart
Normal file
4
lib/bitcoin/bitcoin_transaction_no_inputs_exception.dart
Normal file
|
@ -0,0 +1,4 @@
|
|||
class BitcoinTransactionNoInputsException implements Exception {
|
||||
@override
|
||||
String toString() => 'No inputs for the transaction.';
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
class BitcoinTransactionWrongBalanceException implements Exception {
|
||||
@override
|
||||
String toString() => 'Wrong balance. Not enough BTC on your balance.';
|
||||
}
|
17
lib/bitcoin/bitcoin_unspent.dart
Normal file
17
lib/bitcoin/bitcoin_unspent.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||
|
||||
class BitcoinUnspent {
|
||||
BitcoinUnspent(this.address, this.hash, this.value, this.vout);
|
||||
|
||||
factory BitcoinUnspent.fromJSON(
|
||||
BitcoinAddressRecord address, Map<String, dynamic> json) =>
|
||||
BitcoinUnspent(address, json['tx_hash'] as String, json['value'] as int,
|
||||
json['tx_pos'] as int);
|
||||
|
||||
final BitcoinAddressRecord address;
|
||||
final String hash;
|
||||
final int value;
|
||||
final int vout;
|
||||
|
||||
bool get isP2wpkh => address.address.startsWith('bc1');
|
||||
}
|
|
@ -1,10 +1,21 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_credentials.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_no_inputs_exception.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_wrong_balance_exception.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_unspent.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_keys.dart';
|
||||
import 'package:cake_wallet/bitcoin/pending_bitcoin_transaction.dart';
|
||||
import 'package:cake_wallet/bitcoin/script_hash.dart';
|
||||
import 'package:cake_wallet/bitcoin/utils.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:cake_wallet/src/domain/common/transaction_direction.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
|
||||
import 'package:cw_monero/transaction_history.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
|
@ -20,12 +31,41 @@ 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';
|
||||
import 'package:hex/hex.dart';
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
part 'bitcoin_wallet.g.dart';
|
||||
|
||||
class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet;
|
||||
|
||||
abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||
BitcoinWalletBase._internal(
|
||||
{@required this.eclient,
|
||||
@required this.path,
|
||||
@required String password,
|
||||
@required this.name,
|
||||
List<BitcoinAddressRecord> initialAddresses,
|
||||
int accountIndex = 0,
|
||||
this.transactionHistory,
|
||||
this.mnemonic,
|
||||
BitcoinBalance initialBalance})
|
||||
: 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>(),
|
||||
syncStatus = NotConnectedSyncStatus(),
|
||||
_password = password,
|
||||
_accountIndex = accountIndex,
|
||||
_addressesKeys = {} {
|
||||
type = WalletType.bitcoin;
|
||||
currency = CryptoCurrency.btc;
|
||||
_scripthashesUpdateSubject = {};
|
||||
}
|
||||
|
||||
static BitcoinWallet fromJSON(
|
||||
{@required String password,
|
||||
@required String name,
|
||||
|
@ -37,12 +77,12 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
(data['account_index'] == 'null' || data['account_index'] == null)
|
||||
? 0
|
||||
: int.parse(data['account_index'] as String);
|
||||
final _addresses = data['addresses'] as List;
|
||||
final _addresses = data['addresses'] as List ?? <Object>[];
|
||||
final addresses = <BitcoinAddressRecord>[];
|
||||
final balance = BitcoinBalance.fromJSON(data['balance'] as String) ??
|
||||
BitcoinBalance(confirmed: 0, unconfirmed: 0);
|
||||
|
||||
_addresses?.forEach((Object el) {
|
||||
_addresses.forEach((Object el) {
|
||||
if (el is String) {
|
||||
addresses.add(BitcoinAddressRecord.fromJSON(el));
|
||||
}
|
||||
|
@ -83,34 +123,10 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
transactionHistory: history);
|
||||
}
|
||||
|
||||
BitcoinWalletBase._internal(
|
||||
{@required this.eclient,
|
||||
@required this.path,
|
||||
@required String password,
|
||||
@required this.name,
|
||||
List<BitcoinAddressRecord> initialAddresses,
|
||||
int accountIndex = 0,
|
||||
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>();
|
||||
syncStatus = NotConnectedSyncStatus();
|
||||
|
||||
_password = password;
|
||||
_accountIndex = accountIndex;
|
||||
}
|
||||
|
||||
@override
|
||||
final BitcoinTransactionHistory transactionHistory;
|
||||
final String path;
|
||||
bitcoin.HDWallet hd;
|
||||
final bitcoin.HDWallet hd;
|
||||
final ElectrumClient eclient;
|
||||
final String mnemonic;
|
||||
|
||||
|
@ -131,6 +147,11 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
|
||||
ObservableList<BitcoinAddressRecord> addresses;
|
||||
|
||||
Map<String, bitcoin.ECPair> _addressesKeys;
|
||||
|
||||
List<String> get scriptHashes =>
|
||||
addresses.map((addr) => scriptHash(addr.address)).toList();
|
||||
|
||||
String get xpub => hd.base58;
|
||||
|
||||
@override
|
||||
|
@ -142,11 +163,13 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
|
||||
int _accountIndex;
|
||||
String _password;
|
||||
BehaviorSubject<Object> _addressUpdateSubject;
|
||||
Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject;
|
||||
|
||||
Future<void> init() async {
|
||||
if (addresses.isEmpty) {
|
||||
addresses.add(BitcoinAddressRecord(_getAddress(hd: hd, index: 0)));
|
||||
final index = 0;
|
||||
addresses
|
||||
.add(BitcoinAddressRecord(_getAddress(index: index), index: index));
|
||||
}
|
||||
|
||||
address = addresses.first.address;
|
||||
|
@ -156,9 +179,8 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
|
||||
Future<BitcoinAddressRecord> generateNewAddress({String label}) async {
|
||||
_accountIndex += 1;
|
||||
final address = BitcoinAddressRecord(
|
||||
_getAddress(hd: hd, index: _accountIndex),
|
||||
label: label);
|
||||
final address = BitcoinAddressRecord(_getAddress(index: _accountIndex),
|
||||
index: _accountIndex, label: label);
|
||||
addresses.add(address);
|
||||
|
||||
await save();
|
||||
|
@ -181,9 +203,9 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
Future<void> startSync() async {
|
||||
try {
|
||||
syncStatus = StartingSyncStatus();
|
||||
await _addressUpdateSubject?.close();
|
||||
_addressUpdateSubject = eclient.addressUpdate(address: address);
|
||||
await transactionHistory.update();
|
||||
transactionHistory.updateAsync(
|
||||
onFinished: () => print('transactionHistory update finished!'));
|
||||
_subscribeForUpdates();
|
||||
await _updateBalance();
|
||||
syncStatus = SyncedSyncStatus();
|
||||
} catch (e) {
|
||||
|
@ -197,39 +219,103 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
Future<void> connectToNode({@required Node node}) async {
|
||||
try {
|
||||
syncStatus = ConnectingSyncStatus();
|
||||
await eclient.connect(host: 'electrum2.hodlister.co', port: 50002);
|
||||
await eclient.connectToUri(node.uri);
|
||||
eclient.onConnectionStatusChange = (bool isConnected) {
|
||||
if (!isConnected) {
|
||||
syncStatus = LostConnectionSyncStatus();
|
||||
}
|
||||
};
|
||||
syncStatus = ConnectedSyncStatus();
|
||||
} catch (e) {
|
||||
print(e.toString);
|
||||
print(e.toString());
|
||||
syncStatus = FailedSyncStatus();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> createTransaction(Object credentials) async {
|
||||
Future<PendingBitcoinTransaction> createTransaction(
|
||||
Object credentials) async {
|
||||
final transactionCredentials = credentials as BitcoinTransactionCredentials;
|
||||
|
||||
final inputs = <BitcoinUnspent>[];
|
||||
final fee = _feeMultiplier(transactionCredentials.priority);
|
||||
final amount = transactionCredentials.amount != null
|
||||
? doubleToBitcoinAmount(transactionCredentials.amount)
|
||||
: balance.total - fee;
|
||||
final totalAmount = amount + fee;
|
||||
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;
|
||||
var leftAmount = totalAmount;
|
||||
final changeAddress = address;
|
||||
var totalInputAmount = 0;
|
||||
|
||||
final unspent = addresses.map((address) => eclient
|
||||
.getListUnspentWithAddress(address.address)
|
||||
.then((unspent) => unspent
|
||||
.map((unspent) => BitcoinUnspent.fromJSON(address, unspent))));
|
||||
|
||||
for (final unptsFutures in unspent) {
|
||||
final utxs = await unptsFutures;
|
||||
|
||||
for (final utx in utxs) {
|
||||
final inAmount = utx.value > totalAmount ? totalAmount : utx.value;
|
||||
leftAmount = leftAmount - inAmount;
|
||||
totalInputAmount += inAmount;
|
||||
inputs.add(utx);
|
||||
|
||||
if (leftAmount <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (leftAmount <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs.isEmpty) {
|
||||
throw BitcoinTransactionNoInputsException();
|
||||
}
|
||||
|
||||
if (amount <= 0 || totalInputAmount < amount) {
|
||||
throw BitcoinTransactionWrongBalanceException();
|
||||
}
|
||||
|
||||
final changeValue = totalInputAmount - amount - fee;
|
||||
|
||||
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);
|
||||
inputs.forEach((input) {
|
||||
if (input.isP2wpkh) {
|
||||
final p2wpkh = bitcoin
|
||||
.P2WPKH(
|
||||
data: generatePaymentData(hd: hd, index: input.address.index),
|
||||
network: bitcoin.bitcoin)
|
||||
.data;
|
||||
|
||||
txb.addInput(input.hash, input.vout, null, p2wpkh.output);
|
||||
} else {
|
||||
txb.addInput(input.hash, input.vout);
|
||||
}
|
||||
});
|
||||
|
||||
txb.addOutput(transactionCredentials.address, amount);
|
||||
|
||||
if (changeValue > 0) {
|
||||
txb.addOutput(changeAddress, changeValue);
|
||||
}
|
||||
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
final input = inputs[i];
|
||||
final keyPair = generateKeyPair(hd: hd, index: input.address.index);
|
||||
final witnessValue = input.isP2wpkh ? input.value : null;
|
||||
|
||||
txb.sign(vin: i, keyPair: keyPair, witnessValue: witnessValue);
|
||||
}
|
||||
|
||||
return PendingBitcoinTransaction(txb.build(),
|
||||
eclient: eclient, amount: amount, fee: fee)
|
||||
..addListener((transaction) => transactionHistory.addOne(transaction));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> save() async =>
|
||||
await write(path: path, password: _password, data: toJSON());
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'mnemonic': mnemonic,
|
||||
'account_index': _accountIndex.toString(),
|
||||
|
@ -237,16 +323,31 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
'balance': balance?.toJSON()
|
||||
});
|
||||
|
||||
String _getAddress({bitcoin.HDWallet hd, int index}) => bitcoin
|
||||
.P2WPKH(
|
||||
data: PaymentData(
|
||||
pubkey: Uint8List.fromList(hd.derive(index).pubKey.codeUnits)))
|
||||
.data
|
||||
.address;
|
||||
@override
|
||||
double calculateEstimatedFee(TransactionPriority priority) =>
|
||||
bitcoinAmountToDouble(amount: _feeMultiplier(priority));
|
||||
|
||||
@override
|
||||
Future<void> save() async =>
|
||||
await write(path: path, password: _password, data: toJSON());
|
||||
|
||||
bitcoin.ECPair keyPairFor({@required int index}) =>
|
||||
generateKeyPair(hd: hd, index: index);
|
||||
|
||||
void _subscribeForUpdates() {
|
||||
scriptHashes.forEach((sh) async {
|
||||
await _scripthashesUpdateSubject[sh]?.close();
|
||||
_scripthashesUpdateSubject[sh] = eclient.scripthashUpdate(sh);
|
||||
_scripthashesUpdateSubject[sh].listen((event) async {
|
||||
transactionHistory.updateAsync();
|
||||
await _updateBalance();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<BitcoinBalance> _fetchBalances() async {
|
||||
final balances = await Future.wait(
|
||||
addresses.map((record) => eclient.getBalance(address: record.address)));
|
||||
scriptHashes.map((sHash) => eclient.getBalance(sHash)));
|
||||
final balance = balances.fold(
|
||||
BitcoinBalance(confirmed: 0, unconfirmed: 0),
|
||||
(BitcoinBalance acc, val) => BitcoinBalance(
|
||||
|
@ -261,4 +362,20 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
|||
balance = await _fetchBalances();
|
||||
await save();
|
||||
}
|
||||
|
||||
String _getAddress({@required int index}) =>
|
||||
generateAddress(hd: hd, index: index);
|
||||
|
||||
int _feeMultiplier(TransactionPriority priority) {
|
||||
switch (priority) {
|
||||
case TransactionPriority.slow:
|
||||
return 6000;
|
||||
case TransactionPriority.regular:
|
||||
return 9000;
|
||||
case TransactionPriority.fast:
|
||||
return 15000;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:bip39/bip39.dart' as bip39;
|
||||
import 'package:cake_wallet/bitcoin/file.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart';
|
||||
|
@ -49,10 +48,9 @@ class BitcoinWalletService extends WalletService<
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> remove(String wallet) {
|
||||
// TODO: implement remove
|
||||
throw UnimplementedError();
|
||||
}
|
||||
Future<void> remove(String wallet) async =>
|
||||
File(await pathForWalletDir(name: wallet, type: WalletType.bitcoin))
|
||||
.delete(recursive: true);
|
||||
|
||||
@override
|
||||
Future<BitcoinWallet> restoreFromKeys(
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:cake_wallet/bitcoin/script_hash.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:rxdart/rxdart.dart';
|
||||
|
||||
String jsonrpcparams(List<Object> params) {
|
||||
final _params = params?.map((val) => '"${val.toString()}"')?.join(',');
|
||||
return "[$_params]";
|
||||
return '[$_params]';
|
||||
}
|
||||
|
||||
String jsonrpc(
|
||||
{String method, List<Object> params, int id, double version = 2.0}) =>
|
||||
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${jsonrpcparams(params)}}\n';
|
||||
'{"jsonrpc": "$version", "method": "$method", "id": "$id", "params": ${json.encode(params)}}\n';
|
||||
|
||||
class SocketTask {
|
||||
SocketTask({this.completer, this.isSubscription, this.subject});
|
||||
|
@ -31,58 +33,66 @@ class ElectrumClient {
|
|||
|
||||
bool get isConnected => _isConnected;
|
||||
Socket socket;
|
||||
void Function(bool) onConnectionStatusChange;
|
||||
int _id;
|
||||
final Map<String, SocketTask> _tasks;
|
||||
bool _isConnected;
|
||||
Timer _aliveTimer;
|
||||
|
||||
Future<void> connect({@required String host, @required int port}) async {
|
||||
if (socket != null) {
|
||||
await socket.close();
|
||||
}
|
||||
Future<void> connectToUri(String uri) async {
|
||||
final _uri = Uri.parse(uri);
|
||||
final host = _uri.scheme;
|
||||
final port = int.parse(_uri.path);
|
||||
await connect(host: host, port: port);
|
||||
}
|
||||
|
||||
final start = DateTime.now();
|
||||
Future<void> connect({@required String host, @required int port}) async {
|
||||
try {
|
||||
await socket?.close();
|
||||
} catch (_) {}
|
||||
|
||||
socket = await SecureSocket.connect(host, port, timeout: connectionTimeout);
|
||||
_setIsConnected(true);
|
||||
|
||||
_isConnected = true;
|
||||
|
||||
socket.listen((List<int> event) {
|
||||
socket.listen((Uint8List event) {
|
||||
try {
|
||||
final jsoned = json.decode(utf8.decode(event)) as Map<String, Object>;
|
||||
final jsoned =
|
||||
json.decode(utf8.decode(event.toList())) as Map<String, Object>;
|
||||
print(jsoned);
|
||||
final method = jsoned['method'];
|
||||
final id = jsoned['id'] as String;
|
||||
final params = jsoned['result'];
|
||||
|
||||
if (method is String) {
|
||||
_methodHandler(method: method, request: jsoned);
|
||||
return;
|
||||
}
|
||||
|
||||
final id = jsoned['id'] as String;
|
||||
final params = jsoned['result'];
|
||||
|
||||
_finish(id, params);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
}
|
||||
}, onError: (Object error) {
|
||||
print('ElectrumClient error: ${error.toString()}');
|
||||
}, onDone: () {
|
||||
final end = DateTime.now();
|
||||
final diff = end.millisecondsSinceEpoch - start.millisecondsSinceEpoch;
|
||||
print('On done: $diff');
|
||||
});
|
||||
|
||||
print('Connected to ${socket.remoteAddress}');
|
||||
print(error.toString());
|
||||
_setIsConnected(false);
|
||||
}, onDone: () => _setIsConnected(false));
|
||||
keepAlive();
|
||||
}
|
||||
|
||||
void keepAlive() {
|
||||
_aliveTimer?.cancel();
|
||||
// FIXME: Unnamed constant.
|
||||
_aliveTimer = Timer.periodic(Duration(seconds: 30), (_) async => ping());
|
||||
_aliveTimer = Timer.periodic(Duration(seconds: 2), (_) async => ping());
|
||||
}
|
||||
|
||||
Future<void> ping() => call(method: 'server.ping');
|
||||
Future<void> ping() async {
|
||||
try {
|
||||
await callWithTimeout(method: 'server.ping');
|
||||
_setIsConnected(true);
|
||||
} on RequestFailedTimeoutException catch (_) {
|
||||
_setIsConnected(false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<String>> version() =>
|
||||
call(method: 'server.version').then((dynamic result) {
|
||||
|
@ -93,18 +103,18 @@ class ElectrumClient {
|
|||
return [];
|
||||
});
|
||||
|
||||
Future<Map<String, Object>> getBalance({String address}) =>
|
||||
call(method: 'blockchain.address.get_balance', params: [address])
|
||||
Future<Map<String, Object>> getBalance(String scriptHash) =>
|
||||
call(method: 'blockchain.scripthash.get_balance', params: [scriptHash])
|
||||
.then((dynamic result) {
|
||||
if (result is Map<String, Object>) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return Map<String, Object>();
|
||||
return <String, Object>{};
|
||||
});
|
||||
|
||||
Future<List<Map<String, dynamic>>> getHistory({String address}) =>
|
||||
call(method: 'blockchain.address.get_history', params: [address])
|
||||
Future<List<Map<String, dynamic>>> getHistory(String scriptHash) =>
|
||||
call(method: 'blockchain.scripthash.get_history', params: [scriptHash])
|
||||
.then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) {
|
||||
|
@ -112,24 +122,91 @@ class ElectrumClient {
|
|||
return val;
|
||||
}
|
||||
|
||||
return Map<String, Object>();
|
||||
return <String, Object>{};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<String> getTransactionRaw({@required String hash}) async =>
|
||||
call(method: 'blockchain.transaction.get', params: [hash])
|
||||
Future<List<Map<String, dynamic>>> getListUnspentWithAddress(
|
||||
String address) =>
|
||||
call(
|
||||
method: 'blockchain.scripthash.listunspent',
|
||||
params: [scriptHash(address)]).then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) {
|
||||
if (val is Map<String, Object>) {
|
||||
val['address'] = address;
|
||||
return val;
|
||||
}
|
||||
|
||||
return <String, Object>{};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<List<Map<String, dynamic>>> getListUnspent(String scriptHash) =>
|
||||
call(method: 'blockchain.scripthash.listunspent', params: [scriptHash])
|
||||
.then((dynamic result) {
|
||||
if (result is String) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) {
|
||||
if (val is Map<String, Object>) {
|
||||
return val;
|
||||
}
|
||||
|
||||
return <String, Object>{};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<List<Map<String, dynamic>>> getMempool(String scriptHash) =>
|
||||
call(method: 'blockchain.scripthash.get_mempool', params: [scriptHash])
|
||||
.then((dynamic result) {
|
||||
if (result is List) {
|
||||
return result.map((dynamic val) {
|
||||
if (val is Map<String, Object>) {
|
||||
return val;
|
||||
}
|
||||
|
||||
return <String, Object>{};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
Future<Map<String, Object>> getTransactionRaw(
|
||||
{@required String hash}) async =>
|
||||
call(method: 'blockchain.transaction.get', params: [hash, true])
|
||||
.then((dynamic result) {
|
||||
if (result is Map<String, Object>) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return '';
|
||||
return <String, Object>{};
|
||||
});
|
||||
|
||||
Future<String> broadcastTransaction({@required String transactionRaw}) async =>
|
||||
Future<Map<String, Object>> getTransactionExpanded(
|
||||
{@required String hash}) async {
|
||||
final originalTx = await getTransactionRaw(hash: hash);
|
||||
final vins = originalTx['vin'] as List<Object>;
|
||||
|
||||
for (dynamic vin in vins) {
|
||||
if (vin is Map<String, Object>) {
|
||||
vin['tx'] = await getTransactionRaw(hash: vin['txid'] as String);
|
||||
}
|
||||
}
|
||||
|
||||
return originalTx;
|
||||
}
|
||||
|
||||
Future<String> broadcastTransaction(
|
||||
{@required String transactionRaw}) async =>
|
||||
call(method: 'blockchain.transaction.broadcast', params: [transactionRaw])
|
||||
.then((dynamic result) {
|
||||
if (result is String) {
|
||||
|
@ -163,11 +240,11 @@ class ElectrumClient {
|
|||
return 0;
|
||||
});
|
||||
|
||||
BehaviorSubject<Object> addressUpdate({@required String address}) =>
|
||||
BehaviorSubject<Object> scripthashUpdate(String scripthash) =>
|
||||
subscribe<Object>(
|
||||
id: 'blockchain.address.subscribe:$address',
|
||||
method: 'blockchain.address.subscribe',
|
||||
params: [address]);
|
||||
id: 'blockchain.scripthash.subscribe:$scripthash',
|
||||
method: 'blockchain.scripthash.subscribe',
|
||||
params: [scripthash]);
|
||||
|
||||
BehaviorSubject<T> subscribe<T>(
|
||||
{@required String id,
|
||||
|
@ -190,6 +267,25 @@ class ElectrumClient {
|
|||
return completer.future;
|
||||
}
|
||||
|
||||
Future<dynamic> callWithTimeout(
|
||||
{String method,
|
||||
List<Object> params = const [],
|
||||
int timeout = 2000}) async {
|
||||
final completer = Completer<dynamic>();
|
||||
_id += 1;
|
||||
final id = _id;
|
||||
_regisryTask(id, completer);
|
||||
socket.write(jsonrpc(method: method, id: _id, params: params));
|
||||
|
||||
Timer(Duration(milliseconds: timeout), () {
|
||||
if (!completer.isCompleted) {
|
||||
completer.completeError(RequestFailedTimeoutException(method, _id));
|
||||
}
|
||||
});
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
void request({String method, List<Object> params = const []}) {
|
||||
_id += 1;
|
||||
socket.write(jsonrpc(method: method, id: _id, params: params));
|
||||
|
@ -206,7 +302,9 @@ class ElectrumClient {
|
|||
return;
|
||||
}
|
||||
|
||||
_tasks[id]?.completer?.complete(data);
|
||||
if (!(_tasks[id]?.completer?.isCompleted ?? false)) {
|
||||
_tasks[id]?.completer?.complete(data);
|
||||
}
|
||||
|
||||
if (!(_tasks[id]?.isSubscription ?? false)) {
|
||||
_tasks[id] = null;
|
||||
|
@ -218,18 +316,30 @@ class ElectrumClient {
|
|||
void _methodHandler(
|
||||
{@required String method, @required Map<String, Object> request}) {
|
||||
switch (method) {
|
||||
case 'blockchain.address.subscribe':
|
||||
case 'blockchain.scripthash.subscribe':
|
||||
final params = request['params'] as List<dynamic>;
|
||||
final address = params.first as String;
|
||||
final id = 'blockchain.address.subscribe:$address';
|
||||
|
||||
if (_tasks[id] != null) {
|
||||
_tasks[id].subject.add(params.last);
|
||||
}
|
||||
final scripthash = params.first as String;
|
||||
final id = 'blockchain.scripthash.subscribe:$scripthash';
|
||||
|
||||
_tasks[id]?.subject?.add(params.last);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _setIsConnected(bool isConnected) {
|
||||
if (_isConnected != isConnected) {
|
||||
onConnectionStatusChange?.call(isConnected);
|
||||
}
|
||||
|
||||
_isConnected = isConnected;
|
||||
}
|
||||
}
|
||||
|
||||
class RequestFailedTimeoutException implements Exception {
|
||||
RequestFailedTimeoutException(this.method, this.id);
|
||||
|
||||
final String method;
|
||||
final int id;
|
||||
}
|
||||
|
|
47
lib/bitcoin/pending_bitcoin_transaction.dart
Normal file
47
lib/bitcoin/pending_bitcoin_transaction.dart
Normal file
|
@ -0,0 +1,47 @@
|
|||
import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:cake_wallet/core/pending_transaction.dart';
|
||||
import 'package:cake_wallet/bitcoin/electrum.dart';
|
||||
|
||||
class PendingBitcoinTransaction with PendingTransaction {
|
||||
PendingBitcoinTransaction(this._tx,
|
||||
{@required this.eclient, @required this.amount, @required this.fee})
|
||||
: _listeners = <void Function(BitcoinTransactionInfo transaction)>[];
|
||||
|
||||
final bitcoin.Transaction _tx;
|
||||
final ElectrumClient eclient;
|
||||
final int amount;
|
||||
final int fee;
|
||||
|
||||
String get id => _tx.getId();
|
||||
|
||||
@override
|
||||
String get amountFormatted => bitcoinAmountToString(amount: amount);
|
||||
|
||||
@override
|
||||
String get feeFormatted => bitcoinAmountToString(amount: fee);
|
||||
|
||||
final List<void Function(BitcoinTransactionInfo transaction)> _listeners;
|
||||
|
||||
@override
|
||||
Future<void> commit() async {
|
||||
await eclient.broadcastTransaction(transactionRaw: _tx.toHex());
|
||||
_listeners?.forEach((listener) => listener(transactionInfo()));
|
||||
}
|
||||
|
||||
void addListener(
|
||||
void Function(BitcoinTransactionInfo transaction) listener) =>
|
||||
_listeners.add(listener);
|
||||
|
||||
BitcoinTransactionInfo transactionInfo() => BitcoinTransactionInfo(
|
||||
id: id,
|
||||
height: 0,
|
||||
amount: amount,
|
||||
direction: TransactionDirection.outgoing,
|
||||
date: DateTime.now(),
|
||||
isPending: true,
|
||||
confirmations: 0);
|
||||
}
|
18
lib/bitcoin/script_hash.dart
Normal file
18
lib/bitcoin/script_hash.dart
Normal file
|
@ -0,0 +1,18 @@
|
|||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
String scriptHash(String address) {
|
||||
final outputScript = bitcoin.Address.addressToOutputScript(address);
|
||||
final splitted = sha256.convert(outputScript).toString().split('');
|
||||
var res = '';
|
||||
|
||||
for (var i = splitted.length - 1; i >= 0; i--) {
|
||||
final char = splitted[i];
|
||||
i--;
|
||||
final nextChar = splitted[i];
|
||||
res += nextChar;
|
||||
res += char;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
26
lib/bitcoin/utils.dart
Normal file
26
lib/bitcoin/utils.dart
Normal file
|
@ -0,0 +1,26 @@
|
|||
import 'dart:typed_data';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||
import 'package:bitcoin_flutter/src/payments/index.dart' show PaymentData;
|
||||
import 'package:hex/hex.dart';
|
||||
|
||||
bitcoin.PaymentData generatePaymentData(
|
||||
{@required bitcoin.HDWallet hd, @required int index}) =>
|
||||
PaymentData(
|
||||
pubkey: Uint8List.fromList(HEX.decode(hd.derive(index).pubKey)));
|
||||
|
||||
bitcoin.ECPair generateKeyPair(
|
||||
{@required bitcoin.HDWallet hd,
|
||||
@required int index,
|
||||
bitcoin.NetworkType network}) =>
|
||||
bitcoin.ECPair.fromWIF(hd.derive(index).wif,
|
||||
network: network ?? bitcoin.bitcoin);
|
||||
|
||||
String generateAddress({@required bitcoin.HDWallet hd, @required int index}) =>
|
||||
bitcoin
|
||||
.P2WPKH(
|
||||
data: PaymentData(
|
||||
pubkey:
|
||||
Uint8List.fromList(HEX.decode(hd.derive(index).pubKey))))
|
||||
.data
|
||||
.address;
|
|
@ -14,10 +14,10 @@ class AmountValidator extends TextValidator {
|
|||
static String _pattern(WalletType type) {
|
||||
switch (type) {
|
||||
case WalletType.monero:
|
||||
return '^([0-9]+([.][0-9]{0,12})?|[.][0-9]{1,12}|ALL)\$';
|
||||
return '^([0-9]+([.\,][0-9]{0,12})?|[.\,][0-9]{1,12})\$';
|
||||
case WalletType.bitcoin:
|
||||
// FIXME: Incorrect pattern for bitcoin
|
||||
return '^([0-9]+([.][0-9]{0,12})?|[.][0-9]{1,12}|ALL)\$';
|
||||
return '^([0-9]+([.\,][0-9]{0,12})?|[.\,][0-9]{1,12})\$';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
|
|
6
lib/core/pending_transaction.dart
Normal file
6
lib/core/pending_transaction.dart
Normal file
|
@ -0,0 +1,6 @@
|
|||
mixin PendingTransaction {
|
||||
String get amountFormatted;
|
||||
String get feeFormatted;
|
||||
|
||||
Future<void> commit();
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_info.dart';
|
||||
|
||||
|
@ -5,7 +6,7 @@ abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
|
|||
TransactionHistoryBase() : _isUpdating = false;
|
||||
|
||||
@observable
|
||||
ObservableList<TransactionType> transactions;
|
||||
ObservableMap<String, TransactionType> transactions;
|
||||
|
||||
bool _isUpdating;
|
||||
|
||||
|
@ -24,5 +25,15 @@ abstract class TransactionHistoryBase<TransactionType extends TransactionInfo> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<List<TransactionType>> fetchTransactions();
|
||||
}
|
||||
void updateAsync({void Function() onFinished}) {
|
||||
fetchTransactionsAsync(
|
||||
(transaction) => transactions[transaction.id] = transaction,
|
||||
onFinished: onFinished);
|
||||
}
|
||||
|
||||
void fetchTransactionsAsync(
|
||||
void Function(TransactionType transaction) onTransactionLoaded,
|
||||
{void Function() onFinished});
|
||||
|
||||
Future<Map<String, TransactionType>> fetchTransactions();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/core/pending_transaction.dart';
|
||||
import 'package:cake_wallet/core/transaction_history.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_priority.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';
|
||||
|
@ -30,7 +32,9 @@ abstract class WalletBase<BalaceType> {
|
|||
|
||||
Future<void> startSync();
|
||||
|
||||
Future<void> createTransaction(Object credentials);
|
||||
Future<PendingTransaction> createTransaction(Object credentials);
|
||||
|
||||
double calculateEstimatedFee(TransactionPriority priority);
|
||||
|
||||
Future<void> save();
|
||||
}
|
||||
|
|
117
lib/di.dart
117
lib/di.dart
|
@ -4,6 +4,8 @@ import 'package:cake_wallet/src/domain/common/node.dart';
|
|||
import 'package:cake_wallet/src/domain/exchange/trade.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/exchange_trade/exchange_confirm_page.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_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';
|
||||
|
@ -27,9 +29,13 @@ 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/src/screens/wallet_list/wallet_list_page.dart';
|
||||
import 'package:cake_wallet/store/theme_changer_store.dart';
|
||||
import 'package:cake_wallet/store/wallet_list_store.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:cake_wallet/theme_changer.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/exchange/exchange_trade_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';
|
||||
|
@ -39,7 +45,7 @@ import 'package:cake_wallet/view_model/dashboard/balance_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/send/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';
|
||||
|
@ -81,8 +87,20 @@ NodeListStore setupNodeListStore(Box<Node> nodeSource) {
|
|||
_nodeListStore = NodeListStore();
|
||||
_nodeListStore.replaceValues(nodeSource.values);
|
||||
_onNodesSourceChange = nodeSource.watch();
|
||||
_onNodesSourceChange
|
||||
.listen((_) => _nodeListStore.replaceValues(nodeSource.values));
|
||||
_onNodesSourceChange.listen((event) {
|
||||
// print(event);
|
||||
|
||||
if (event.deleted) {
|
||||
_nodeListStore.nodes.removeWhere((n) {
|
||||
return n.key != null ? n.key == event.key : true;
|
||||
});
|
||||
}
|
||||
|
||||
if (event.value is Node) {
|
||||
final val = event.value as Node;
|
||||
_nodeListStore.nodes.add(val);
|
||||
}
|
||||
});
|
||||
|
||||
return _nodeListStore;
|
||||
}
|
||||
|
@ -114,10 +132,8 @@ Future setup(
|
|||
getIt.registerSingleton<ContactService>(
|
||||
ContactService(contactSource, getIt.get<AppStore>().contactListStore));
|
||||
getIt.registerSingleton<TradesStore>(TradesStore(
|
||||
tradesSource: tradesSource,
|
||||
settingsStore: getIt.get<SettingsStore>()));
|
||||
getIt.registerSingleton<TradeFilterStore>(
|
||||
TradeFilterStore(wallet: getIt.get<AppStore>().wallet));
|
||||
tradesSource: tradesSource, settingsStore: getIt.get<SettingsStore>()));
|
||||
getIt.registerSingleton<TradeFilterStore>(TradeFilterStore());
|
||||
getIt.registerSingleton<TransactionFilterStore>(TransactionFilterStore());
|
||||
getIt.registerSingleton<FiatConvertationStore>(FiatConvertationStore());
|
||||
getIt.registerSingleton<SendTemplateStore>(
|
||||
|
@ -155,20 +171,17 @@ Future setup(
|
|||
getIt.registerFactory<WalletAddressListViewModel>(
|
||||
() => WalletAddressListViewModel(wallet: getIt.get<AppStore>().wallet));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => BalanceViewModel(
|
||||
wallet: getIt.get<AppStore>().wallet,
|
||||
settingsStore: getIt.get<SettingsStore>(),
|
||||
fiatConvertationStore: getIt.get<FiatConvertationStore>()));
|
||||
getIt.registerFactory(() => BalanceViewModel(
|
||||
wallet: getIt.get<AppStore>().wallet,
|
||||
settingsStore: getIt.get<SettingsStore>(),
|
||||
fiatConvertationStore: getIt.get<FiatConvertationStore>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => DashboardViewModel(
|
||||
balanceViewModel: getIt.get<BalanceViewModel>(),
|
||||
appStore: getIt.get<AppStore>(),
|
||||
tradesStore: getIt.get<TradesStore>(),
|
||||
tradeFilterStore: getIt.get<TradeFilterStore>(),
|
||||
transactionFilterStore: getIt.get<TransactionFilterStore>()
|
||||
));
|
||||
getIt.registerFactory(() => DashboardViewModel(
|
||||
balanceViewModel: getIt.get<BalanceViewModel>(),
|
||||
appStore: getIt.get<AppStore>(),
|
||||
tradesStore: getIt.get<TradesStore>(),
|
||||
tradeFilterStore: getIt.get<TradeFilterStore>(),
|
||||
transactionFilterStore: getIt.get<TransactionFilterStore>()));
|
||||
|
||||
getIt.registerFactory<AuthService>(() => AuthService(
|
||||
secureStorage: getIt.get<FlutterSecureStorage>(),
|
||||
|
@ -196,10 +209,9 @@ Future setup(
|
|||
onAuthenticationFinished: onAuthFinished,
|
||||
closable: false));
|
||||
|
||||
getIt.registerFactory<DashboardPage>(
|
||||
() => DashboardPage(
|
||||
walletViewModel: getIt.get<DashboardViewModel>(),
|
||||
addressListViewModel: getIt.get<WalletAddressListViewModel>()));
|
||||
getIt.registerFactory<DashboardPage>(() => DashboardPage(
|
||||
walletViewModel: getIt.get<DashboardViewModel>(),
|
||||
addressListViewModel: getIt.get<WalletAddressListViewModel>()));
|
||||
|
||||
getIt.registerFactory<ReceivePage>(() => ReceivePage(
|
||||
addressListViewModel: getIt.get<WalletAddressListViewModel>()));
|
||||
|
@ -213,17 +225,17 @@ Future setup(
|
|||
addressEditOrCreateViewModel:
|
||||
getIt.get<WalletAddressEditOrCreateViewModel>(param1: item)));
|
||||
|
||||
// getIt.get<SendTemplateStore>()
|
||||
getIt.registerFactory<SendViewModel>(() => SendViewModel(
|
||||
getIt.get<AppStore>().wallet,
|
||||
getIt.get<AppStore>().settingsStore,
|
||||
getIt.get<FiatConvertationStore>(),
|
||||
getIt.get<SendTemplateStore>()));
|
||||
getIt.get<FiatConvertationStore>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => SendPage(sendViewModel: getIt.get<SendViewModel>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => SendTemplatePage(sendViewModel: getIt.get<SendViewModel>()));
|
||||
// getIt.registerFactory(
|
||||
// () => SendTemplatePage(sendViewModel: getIt.get<SendViewModel>()));
|
||||
|
||||
getIt.registerFactory(() => WalletListViewModel(
|
||||
walletInfoSource, getIt.get<AppStore>(), getIt.get<KeyService>()));
|
||||
|
@ -260,8 +272,10 @@ Future setup(
|
|||
moneroAccountCreationViewModel:
|
||||
getIt.get<MoneroAccountEditOrCreateViewModel>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => SettingsViewModel(getIt.get<AppStore>().settingsStore));
|
||||
getIt.registerFactory(() {
|
||||
final appStore = getIt.get<AppStore>();
|
||||
return SettingsViewModel(appStore.settingsStore, appStore.wallet);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => SettingsPage(getIt.get<SettingsViewModel>()));
|
||||
|
||||
|
@ -292,10 +306,11 @@ Future setup(
|
|||
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(() {
|
||||
final appStore = getIt.get<AppStore>();
|
||||
return NodeListViewModel(appStore.nodeListStore, nodeSource,
|
||||
appStore.wallet, appStore.settingsStore);
|
||||
});
|
||||
|
||||
getIt.registerFactory(() => NodeListPage(getIt.get<NodeListViewModel>()));
|
||||
|
||||
|
@ -305,16 +320,30 @@ Future setup(
|
|||
getIt.registerFactory(
|
||||
() => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>()));
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
ExchangeViewModel(
|
||||
wallet: getIt.get<AppStore>().wallet,
|
||||
exchangeTemplateStore: getIt.get<ExchangeTemplateStore>(),
|
||||
trades: tradesSource
|
||||
));
|
||||
getIt.registerFactory(() => ExchangeViewModel(
|
||||
wallet: getIt.get<AppStore>().wallet,
|
||||
exchangeTemplateStore: getIt.get<ExchangeTemplateStore>(),
|
||||
trades: tradesSource,
|
||||
tradesStore: getIt.get<TradesStore>()));
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
ExchangePage(getIt.get<ExchangeViewModel>()));
|
||||
getIt.registerFactory(() => ExchangeTradeViewModel(
|
||||
wallet: getIt.get<AppStore>().wallet,
|
||||
trades: tradesSource,
|
||||
tradesStore: getIt.get<TradesStore>()));
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
ExchangeTemplatePage(getIt.get<ExchangeViewModel>()));
|
||||
getIt.registerFactory(() => ExchangePage(getIt.get<ExchangeViewModel>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => ExchangeConfirmPage(tradesStore: getIt.get<TradesStore>()));
|
||||
|
||||
getIt.registerFactory(() => ExchangeTradePage(
|
||||
exchangeTradeViewModel: getIt.get<ExchangeTradeViewModel>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => ExchangeTemplatePage(getIt.get<ExchangeViewModel>()));
|
||||
}
|
||||
|
||||
void setupThemeChangerStore(ThemeChanger themeChanger) {
|
||||
getIt.registerSingleton<ThemeChangerStore>(
|
||||
ThemeChangerStore(themeChanger: themeChanger));
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ class S implements WidgetsLocalizations {
|
|||
String get expired => "Expired";
|
||||
String get faq => "FAQ";
|
||||
String get fetching => "Fetching";
|
||||
String get filters => "Filters";
|
||||
String get filters => "Filter";
|
||||
String get first_wallet_text => "Awesome wallet for Monero";
|
||||
String get full_balance => "Full Balance";
|
||||
String get hidden_balance => "Hidden Balance";
|
||||
|
@ -312,8 +312,8 @@ class S implements WidgetsLocalizations {
|
|||
String error_text_limits_loading_failed(String provider) => "Trade for ${provider} is not created. Limits loading failed";
|
||||
String error_text_maximum_limit(String provider, String max, String currency) => "Trade for ${provider} is not created. Amount is more then maximum: ${max} ${currency}";
|
||||
String error_text_minimal_limit(String provider, String min, String currency) => "Trade for ${provider} is not created. Amount is less then minimal: ${min} ${currency}";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "By pressing confirm, you will be sending ${fetchingLabel} ${from} from your wallet called ${walletName} to the address shown above. Or you can send from your external wallet to the above address/QR code.\n\nPlease press confirm to continue or go back to change the amounts.\n\n";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Please send ${fetchingLabel} ${from} to the address shown above.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "By pressing confirm, you will be sending ${fetchingLabel} ${from} from your wallet called ${walletName} to the address shown above. Or you can send from your external wallet to the above address/QR code.\n\nPlease press confirm to continue or go back to change the amounts.";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Please send ${fetchingLabel} ${from} to the address shown above.";
|
||||
String failed_authentication(String state_error) => "Failed authentication. ${state_error}";
|
||||
String max_value(String value, String currency) => "Max: ${value} ${currency}";
|
||||
String min_value(String value, String currency) => "Min: ${value} ${currency}";
|
||||
|
@ -948,11 +948,11 @@ class $de extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} Verbleibende Blöcke";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Durch Drücken von Bestätigen wird gesendet ${fetchingLabel} ${from} von Ihrer Brieftasche aus angerufen ${walletName} an die oben angegebene Adresse. Oder Sie können von Ihrem externen Portemonnaie an die oben angegebene Adresse / QR-Code senden.\n\nBitte bestätigen Sie, um fortzufahren, oder gehen Sie zurück, um die Beträge zu änderns.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Durch Drücken von Bestätigen wird gesendet ${fetchingLabel} ${from} von Ihrer Brieftasche aus angerufen ${walletName} an die oben angegebene Adresse. Oder Sie können von Ihrem externen Portemonnaie an die oben angegebene Adresse / QR-Code senden.\n\nBitte bestätigen Sie, um fortzufahren, oder gehen Sie zurück, um die Beträge zu änderns.";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "Handel für ${provider} wird nicht erstellt. Das Laden der Limits ist fehlgeschlagen";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Bitte senden ${fetchingLabel} ${from} an die oben angegebene Adresse.\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Bitte senden ${fetchingLabel} ${from} an die oben angegebene Adresse.'";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "Transaktion festschreiben\nMenge: ${amount}\nGebühr: ${fee}";
|
||||
@override
|
||||
|
@ -1216,7 +1216,7 @@ class $hi extends S {
|
|||
@override
|
||||
String get estimated => "अनुमानित";
|
||||
@override
|
||||
String get filters => "फिल्टर";
|
||||
String get filters => "फ़िल्टर";
|
||||
@override
|
||||
String get settings_current_node => "वर्तमान नोड";
|
||||
@override
|
||||
|
@ -1580,11 +1580,11 @@ class $hi extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} शेष रहते हैं";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "पुष्टि दबाकर, आप भेज रहे होंगे ${fetchingLabel} ${from} अपने बटुए से ${walletName} ऊपर दिखाए गए पते पर। या आप अपने बाहरी वॉलेट से उपरोक्त पते / क्यूआर कोड पर भेज सकते हैं।\n\nकृपया जारी रखने या राशि बदलने के लिए वापस जाने के लिए पुष्टि करें दबाएं.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "पुष्टि दबाकर, आप भेज रहे होंगे ${fetchingLabel} ${from} अपने बटुए से ${walletName} ऊपर दिखाए गए पते पर। या आप अपने बाहरी वॉलेट से उपरोक्त पते / क्यूआर कोड पर भेज सकते हैं।\n\nकृपया जारी रखने या राशि बदलने के लिए वापस जाने के लिए पुष्टि करें दबाएं.";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "व्यापार ${provider} के लिए नहीं बनाया गया है। लोडिंग की सीमाएं विफल रहीं";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "कृपया भेजें ${fetchingLabel} ${from} ऊपर दिखाए गए पते पर\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "कृपया भेजें ${fetchingLabel} ${from} ऊपर दिखाए गए पते पर";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "लेन-देन करें\nरकम: ${amount}\nशुल्क: ${fee}";
|
||||
@override
|
||||
|
@ -1848,7 +1848,7 @@ class $ru extends S {
|
|||
@override
|
||||
String get estimated => "Примерно";
|
||||
@override
|
||||
String get filters => "Фильтры";
|
||||
String get filters => "Фильтр";
|
||||
@override
|
||||
String get settings_current_node => "Текущая нода";
|
||||
@override
|
||||
|
@ -2212,11 +2212,11 @@ class $ru extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} Осталось блоков";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Нажимая подтвердить, вы отправите ${fetchingLabel} ${from} с вашего кошелька ${walletName} на адрес указанный выше. Или вы можете отправить со своего внешнего кошелька на вышеуказанный адрес/QR-код.\n\nПожалуйста, нажмите подтвердить для продолжения, или вернитесь назад для изменения суммы.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Нажимая подтвердить, вы отправите ${fetchingLabel} ${from} с вашего кошелька ${walletName} на адрес указанный выше. Или вы можете отправить со своего внешнего кошелька на вышеуказанный адрес/QR-код.\n\nПожалуйста, нажмите подтвердить для продолжения, или вернитесь назад для изменения суммы.";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "Сделка для ${provider} не создана. Ошибка загрузки лимитов";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Пожалуйста отправьте ${fetchingLabel} ${from} на адрес, указанный выше.\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Пожалуйста отправьте ${fetchingLabel} ${from} на адрес, указанный выше.";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "Подтвердить транзакцию \nСумма: ${amount}\nКомиссия: ${fee}";
|
||||
@override
|
||||
|
@ -2844,11 +2844,11 @@ class $ko extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} 남은 블록";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "확인을 누르면 전송됩니다 ${fetchingLabel} ${from} 지갑에서 ${walletName} 위에 표시된 주소로. 또는 외부 지갑에서 위의 주소 / QR 코드로 보낼 수 있습니다.\n\n확인을 눌러 계속하거나 금액을 변경하려면 돌아가십시오.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "확인을 누르면 전송됩니다 ${fetchingLabel} ${from} 지갑에서 ${walletName} 위에 표시된 주소로. 또는 외부 지갑에서 위의 주소 / QR 코드로 보낼 수 있습니다.\n\n확인을 눌러 계속하거나 금액을 변경하려면 돌아가십시오.";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "거래 ${provider} 가 생성되지 않습니다. 로딩 실패";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "보내주세요 ${fetchingLabel} ${from} 위에 표시된 주소로.\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "보내주세요 ${fetchingLabel} ${from} 위에 표시된 주소로.";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "커밋 거래\n양: ${amount}\n보수: ${fee}";
|
||||
@override
|
||||
|
@ -3112,7 +3112,7 @@ class $pt extends S {
|
|||
@override
|
||||
String get estimated => "Estimado";
|
||||
@override
|
||||
String get filters => "Filtros";
|
||||
String get filters => "Filtro";
|
||||
@override
|
||||
String get settings_current_node => "Nó atual";
|
||||
@override
|
||||
|
@ -3476,11 +3476,11 @@ class $pt extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} blocos restantes";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Ao confirmar, você enviará ${fetchingLabel} ${from} da sua carteira ${walletName} para o endereço exibido acima. Você também pode enviar com uma carteira externa para o endereço/código QR acima.\n\nPressione Confirmar para continuar ou volte para alterar os valores.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Ao confirmar, você enviará ${fetchingLabel} ${from} da sua carteira ${walletName} para o endereço exibido acima. Você também pode enviar com uma carteira externa para o endereço/código QR acima.\n\nPressione Confirmar para continuar ou volte para alterar os valores.";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "A troca por ${provider} não é criada. Falha no carregamento dos limites";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Por favor, envie ${fetchingLabel} ${from} para o endereço mostrado acima.\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Por favor, envie ${fetchingLabel} ${from} para o endereço mostrado acima.";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "Confirmar transação\nQuantia: ${amount}\nTaxa: ${fee}";
|
||||
@override
|
||||
|
@ -3744,7 +3744,7 @@ class $uk extends S {
|
|||
@override
|
||||
String get estimated => "Приблизно ";
|
||||
@override
|
||||
String get filters => "Фільтри";
|
||||
String get filters => "Фільтр";
|
||||
@override
|
||||
String get settings_current_node => "Поточний вузол";
|
||||
@override
|
||||
|
@ -4108,11 +4108,11 @@ class $uk extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} Залишилось блоків";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Натиснувши підтвердити, ви відправите ${fetchingLabel} ${from} з вашого гаманця ${walletName} на адресу вказану вище. Або ви можете відправити зі свого зовнішнього гаманця на вищевказану адресу/QR-код.\n\nБудь ласка, натисніть підтвердити для продовження або поверніться назад щоб змінити суму.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Натиснувши підтвердити, ви відправите ${fetchingLabel} ${from} з вашого гаманця ${walletName} на адресу вказану вище. Або ви можете відправити зі свого зовнішнього гаманця на вищевказану адресу/QR-код.\n\nБудь ласка, натисніть підтвердити для продовження або поверніться назад щоб змінити суму.";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "Операція для ${provider} не створена. Помилка завантаження лімітів";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Будь ласка, відправте ${fetchingLabel} ${from} на адресу, вказану вище.\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Будь ласка, відправте ${fetchingLabel} ${from} на адресу, вказану вище.";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "Підтвердити транзакцію \nСума: ${amount}\nКомісія: ${fee}";
|
||||
@override
|
||||
|
@ -4376,7 +4376,7 @@ class $ja extends S {
|
|||
@override
|
||||
String get estimated => "推定";
|
||||
@override
|
||||
String get filters => "フィルター";
|
||||
String get filters => "フィルタ";
|
||||
@override
|
||||
String get settings_current_node => "現在のノード";
|
||||
@override
|
||||
|
@ -4740,11 +4740,11 @@ class $ja extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} 残りのブロック";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "確認を押すと、送信されます ${fetchingLabel} ${from} と呼ばれるあなたの財布から ${walletName} 上記のアドレスへ. または、外部ウォレットから上記のアドレス/ QRコードに送信できます.\n\n確認を押して続行するか、戻って金額を変更してください.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "確認を押すと、送信されます ${fetchingLabel} ${from} と呼ばれるあなたの財布から ${walletName} 上記のアドレスへ. または、外部ウォレットから上記のアドレス/ QRコードに送信できます.\n\n確認を押して続行するか、戻って金額を変更してください.";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "${provider} の取引は作成されません。 制限の読み込みに失敗しました";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "送信してください ${fetchingLabel} ${from} 上記のアドレスへ.\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "送信してください ${fetchingLabel} ${from} 上記のアドレスへ.";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "トランザクションをコミット\n量: ${amount}\n費用: ${fee}";
|
||||
@override
|
||||
|
@ -5012,7 +5012,7 @@ class $pl extends S {
|
|||
@override
|
||||
String get estimated => "Oszacowano";
|
||||
@override
|
||||
String get filters => "Filtry";
|
||||
String get filters => "Filtr";
|
||||
@override
|
||||
String get settings_current_node => "Bieżący węzeł";
|
||||
@override
|
||||
|
@ -5376,11 +5376,11 @@ class $pl extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} Bloki pozostałe";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Naciskając Potwierdź, wyślesz ${fetchingLabel} ${from} z twojego portfela ${walletName} z twojego portfela. Lub możesz wysłać z zewnętrznego portfela na powyższy adres / kod QR.\n\nNaciśnij Potwierdź, aby kontynuować lub wróć, aby zmienić kwoty.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Naciskając Potwierdź, wyślesz ${fetchingLabel} ${from} z twojego portfela ${walletName} z twojego portfela. Lub możesz wysłać z zewnętrznego portfela na powyższy adres / kod QR.\n\nNaciśnij Potwierdź, aby kontynuować lub wróć, aby zmienić kwoty.";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "Wymiana dla ${provider} nie została utworzona. Ładowanie limitów nie powiodło się";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Proszę wyślij ${fetchingLabel} ${from} na adres podany powyżej.\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Proszę wyślij ${fetchingLabel} ${from} na adres podany powyżej.";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "Zatwierdź transakcję\nIlość: ${amount}\nOpłata: ${fee}";
|
||||
@override
|
||||
|
@ -5644,7 +5644,7 @@ class $es extends S {
|
|||
@override
|
||||
String get estimated => "Estimado";
|
||||
@override
|
||||
String get filters => "Filtros";
|
||||
String get filters => "Filtrar";
|
||||
@override
|
||||
String get settings_current_node => "Nodo actual";
|
||||
@override
|
||||
|
@ -6008,11 +6008,11 @@ class $es extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} Bloques restantes";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Al presionar confirmar, enviará ${fetchingLabel} ${from} desde su billetera llamada ${walletName} a la dirección que se muestra arriba. O puede enviar desde su billetera externa a la dirección / código QR anterior.\n\nPresione confirmar para continuar o regrese para cambiar los montos.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Al presionar confirmar, enviará ${fetchingLabel} ${from} desde su billetera llamada ${walletName} a la dirección que se muestra arriba. O puede enviar desde su billetera externa a la dirección / código QR anterior.\n\nPresione confirmar para continuar o regrese para cambiar los montos.";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "El comercio por ${provider} no se crea. Límites de carga fallidos";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Envíe ${fetchingLabel} ${from} a la dirección que se muestra arriba.\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Envíe ${fetchingLabel} ${from} a la dirección que se muestra arriba.";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "Confirmar transacción\nCantidad: ${amount}\nCuota: ${fee}";
|
||||
@override
|
||||
|
@ -6276,7 +6276,7 @@ class $nl extends S {
|
|||
@override
|
||||
String get estimated => "Geschatte";
|
||||
@override
|
||||
String get filters => "Filters";
|
||||
String get filters => "Filter";
|
||||
@override
|
||||
String get settings_current_node => "Huidige knooppunt";
|
||||
@override
|
||||
|
@ -6640,11 +6640,11 @@ class $nl extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} Resterende blokken";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Door op bevestigen te drukken, wordt u verzonden ${fetchingLabel} ${from} uit je portemonnee genoemd ${walletName} naar bovenstaand adres. Of u kunt uw externe portemonnee naar bovenstaand adres / QR-code sturen.\n\nDruk op bevestigen om door te gaan of terug te gaan om de bedragen te wijzigen.\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "Door op bevestigen te drukken, wordt u verzonden ${fetchingLabel} ${from} uit je portemonnee genoemd ${walletName} naar bovenstaand adres. Of u kunt uw externe portemonnee naar bovenstaand adres / QR-code sturen.\n\nDruk op bevestigen om door te gaan of terug te gaan om de bedragen te wijzigen.";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "Ruil voor ${provider} is niet gemaakt. Beperkingen laden mislukt";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Zend alstublieft ${fetchingLabel} ${from} naar bovenstaand adres.\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "Zend alstublieft ${fetchingLabel} ${from} naar bovenstaand adres.";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "Verricht transactie\nBedrag: ${amount}\nhonorarium: ${fee}";
|
||||
@override
|
||||
|
@ -6908,7 +6908,7 @@ class $zh extends S {
|
|||
@override
|
||||
String get estimated => "估计的";
|
||||
@override
|
||||
String get filters => "筛选器";
|
||||
String get filters => "過濾";
|
||||
@override
|
||||
String get settings_current_node => "当前节点";
|
||||
@override
|
||||
|
@ -7272,11 +7272,11 @@ class $zh extends S {
|
|||
@override
|
||||
String Blocks_remaining(String status) => "${status} 剩余的块";
|
||||
@override
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "点击确认 您将发送 ${fetchingLabel} ${from} 从你的钱包里 ${walletName} 到上面显示的地址. 或者,您也可以从外部钱包发送上述地址/ QR码。\n\n请按确认继续或返回以更改金额\n\n";
|
||||
String exchange_result_confirm(String fetchingLabel, String from, String walletName) => "点击确认 您将发送 ${fetchingLabel} ${from} 从你的钱包里 ${walletName} 到上面显示的地址. 或者,您也可以从外部钱包发送上述地址/ QR码。\n\n请按确认继续或返回以更改金额";
|
||||
@override
|
||||
String error_text_limits_loading_failed(String provider) => "未創建 ${provider} 交易。 限制加載失敗";
|
||||
@override
|
||||
String exchange_result_description(String fetchingLabel, String from) => "请发送 ${fetchingLabel} ${from} 到上面显示的地址.\n\n'";
|
||||
String exchange_result_description(String fetchingLabel, String from) => "请发送 ${fetchingLabel} ${from} 到上面显示的地址.";
|
||||
@override
|
||||
String commit_transaction_amount_fee(String amount, String fee) => "提交交易\n量: ${amount}\nFee: ${fee}";
|
||||
@override
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:cake_wallet/reactions/bootstrap.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/store/authentication_store.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart';
|
||||
|
@ -52,6 +53,8 @@ import 'package:cake_wallet/generated/i18n.dart';
|
|||
import 'package:cake_wallet/src/domain/common/language.dart';
|
||||
import 'package:cake_wallet/src/stores/seed_language/seed_language_store.dart';
|
||||
|
||||
bool isThemeChangerRegistered = false;
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
|
@ -129,7 +132,7 @@ void main() async {
|
|||
fiatConvertationService: fiatConvertationService,
|
||||
templates: templates,
|
||||
exchangeTemplates: exchangeTemplates,
|
||||
initialMigrationVersion: 3);
|
||||
initialMigrationVersion: 4);
|
||||
|
||||
setReactions(
|
||||
settingsStore: settingsStore,
|
||||
|
@ -169,11 +172,11 @@ Future<void> initialSetup(
|
|||
@required Box<Node> nodes,
|
||||
@required Box<WalletInfo> walletInfoSource,
|
||||
@required Box<Contact> contactSource,
|
||||
@required Box<Trade> tradesSource,
|
||||
@required Box<Trade> tradesSource,
|
||||
@required FiatConvertationService fiatConvertationService,
|
||||
@required Box<Template> templates,
|
||||
@required Box<ExchangeTemplate> exchangeTemplates,
|
||||
int initialMigrationVersion = 3}) async {
|
||||
int initialMigrationVersion = 4}) async {
|
||||
await defaultSettingsMigration(
|
||||
version: initialMigrationVersion,
|
||||
sharedPreferences: sharedPreferences,
|
||||
|
@ -197,7 +200,8 @@ class CakeWalletApp extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final settingsStore = Provider.of<SettingsStore>(context);
|
||||
//final settingsStore = Provider.of<SettingsStore>(context);
|
||||
final settingsStore = getIt.get<AppStore>().settingsStore;
|
||||
|
||||
return ChangeNotifierProvider<ThemeChanger>(
|
||||
create: (_) => ThemeChanger(
|
||||
|
@ -228,12 +232,20 @@ class MaterialAppWithTheme extends StatelessWidget {
|
|||
final transactionDescriptions =
|
||||
Provider.of<Box<TransactionDescription>>(context);
|
||||
|
||||
final statusBarColor =
|
||||
settingsStore.isDarkTheme ? Colors.black : Colors.white;
|
||||
if (!isThemeChangerRegistered) {
|
||||
setupThemeChangerStore(theme);
|
||||
isThemeChangerRegistered = true;
|
||||
}
|
||||
|
||||
/*final statusBarColor =
|
||||
settingsStore.isDarkTheme ? Colors.black : Colors.white;*/
|
||||
final _settingsStore = getIt.get<AppStore>().settingsStore;
|
||||
|
||||
final statusBarColor = Colors.transparent;
|
||||
final statusBarBrightness =
|
||||
settingsStore.isDarkTheme ? Brightness.light : Brightness.dark;
|
||||
_settingsStore.isDarkTheme ? Brightness.light : Brightness.dark;
|
||||
final statusBarIconBrightness =
|
||||
settingsStore.isDarkTheme ? Brightness.light : Brightness.dark;
|
||||
_settingsStore.isDarkTheme ? Brightness.light : Brightness.dark;
|
||||
|
||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||
statusBarColor: statusBarColor,
|
||||
|
@ -270,4 +282,4 @@ class MaterialAppWithTheme extends StatelessWidget {
|
|||
authenticationStore: getIt.get<AuthenticationStore>(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,12 +20,29 @@ class MoneroTransactionHistory = MoneroTransactionHistoryBase
|
|||
abstract class MoneroTransactionHistoryBase
|
||||
extends TransactionHistoryBase<MoneroTransactionInfo> with Store {
|
||||
MoneroTransactionHistoryBase() {
|
||||
transactions = ObservableList<MoneroTransactionInfo>();
|
||||
transactions = ObservableMap<String, MoneroTransactionInfo>();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<MoneroTransactionInfo>> fetchTransactions() async {
|
||||
Future<Map<String, MoneroTransactionInfo>> fetchTransactions() async {
|
||||
monero_transaction_history.refreshTransactions();
|
||||
return _getAllTransactions(null);
|
||||
return _getAllTransactions(null).fold<Map<String, MoneroTransactionInfo>>(
|
||||
<String, MoneroTransactionInfo>{},
|
||||
(Map<String, MoneroTransactionInfo> acc, MoneroTransactionInfo tx) {
|
||||
acc[tx.id] = tx;
|
||||
return acc;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void updateAsync({void Function() onFinished}) {
|
||||
fetchTransactionsAsync(
|
||||
(transaction) => transactions[transaction.id] = transaction,
|
||||
onFinished: onFinished);
|
||||
}
|
||||
|
||||
@override
|
||||
void fetchTransactionsAsync(
|
||||
void Function(MoneroTransactionInfo transaction) onTransactionLoaded,
|
||||
{void Function() onFinished}) {}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@ 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:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/core/pending_transaction.dart';
|
||||
import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
|
||||
import 'package:cake_wallet/src/domain/common/calculate_fiat_amount.dart' as cfa;
|
||||
|
||||
part 'monero_wallet.g.dart';
|
||||
|
||||
|
@ -133,7 +136,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<void> createTransaction(Object credentials) async {
|
||||
Future<PendingTransaction> createTransaction(Object credentials) async {
|
||||
// final _credentials = credentials as MoneroTransactionCreationCredentials;
|
||||
// final transactionDescription = await transaction_history.createTransaction(
|
||||
// address: _credentials.address,
|
||||
|
@ -146,6 +149,33 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
|||
// transactionDescription);
|
||||
}
|
||||
|
||||
@override
|
||||
double calculateEstimatedFee(TransactionPriority priority) {
|
||||
// FIXME: hardcoded value;
|
||||
|
||||
if (priority == TransactionPriority.slow) {
|
||||
return 0.00002459;
|
||||
}
|
||||
|
||||
if (priority == TransactionPriority.regular) {
|
||||
return 0.00012305;
|
||||
}
|
||||
|
||||
if (priority == TransactionPriority.medium) {
|
||||
return 0.00024503;
|
||||
}
|
||||
|
||||
if (priority == TransactionPriority.fast) {
|
||||
return 0.00061453;
|
||||
}
|
||||
|
||||
if (priority == TransactionPriority.fastest) {
|
||||
return 0.0260216;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> save() async {
|
||||
// if (_isSaving) {
|
||||
|
|
|
@ -9,11 +9,20 @@ class Palette {
|
|||
static const Color lavender = Color.fromRGBO(237, 245, 252, 1.0);
|
||||
static const Color oceanBlue = Color.fromRGBO(30, 52, 78, 1.0);
|
||||
static const Color lightBlueGrey = Color.fromRGBO(118, 131, 169, 1.0);
|
||||
static const Color periwinkle = Color.fromRGBO(197, 208, 230, 1.0);
|
||||
static const Color periwinkle = Color.fromRGBO(195, 210, 227, 1.0);
|
||||
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 darkLavender = Color.fromRGBO(229, 238, 250, 1.0);
|
||||
static const Color nightBlue = Color.fromRGBO(46, 57, 96, 1.0);
|
||||
|
||||
static const Color moderateOrangeYellow = Color.fromRGBO(245, 134, 82, 1.0);
|
||||
static const Color moderateOrange = Color.fromRGBO(235, 117, 63, 1.0);
|
||||
static const Color shineGreen = Color.fromRGBO(76, 189, 87, 1.0);
|
||||
static const Color moderateGreen = Color.fromRGBO(45, 158, 56, 1.0);
|
||||
static const Color cornflower = Color.fromRGBO(85, 147, 240, 1.0);
|
||||
static const Color royalBlue = Color.fromRGBO(43, 114, 221, 1.0);
|
||||
static const Color lightRed = Color.fromRGBO(227, 87, 87, 1.0);
|
||||
static const Color persianRed = Color.fromRGBO(206, 55, 55, 1.0);
|
||||
|
||||
// NEW DESIGN
|
||||
static const Color blueCraiola = Color.fromRGBO(69, 110, 255, 1.0);
|
||||
static const Color darkBlueCraiola = Color.fromRGBO(53, 86, 136, 1.0);
|
||||
|
@ -29,6 +38,9 @@ class Palette {
|
|||
static const Color gray = Color.fromRGBO(112, 147, 186, 1.0);
|
||||
static const Color wildPeriwinkle = Color.fromRGBO(219, 227, 243, 1.0);
|
||||
static const Color darkGray = Color.fromRGBO(122, 147, 186, 1.0);
|
||||
static const Color shadowWhite = Color.fromRGBO(242, 245, 255, 1.0);
|
||||
static const Color niagara = Color.fromRGBO(152, 172, 201, 1.0);
|
||||
static const Color alizarinRed = Color.fromRGBO(233, 45, 45, 1.0);
|
||||
|
||||
// FIXME: Rename.
|
||||
static const Color eee = Color.fromRGBO(236, 239, 245, 1.0);
|
||||
|
@ -76,6 +88,8 @@ class PaletteDark {
|
|||
static const Color dividerColor = Color.fromRGBO(48, 59, 95, 1.0);
|
||||
static const Color violetBlue = Color.fromRGBO(59, 72, 119, 1.0);
|
||||
static const Color distantBlue = Color.fromRGBO(72, 85, 131, 1.0);
|
||||
static const Color moderateVioletBlue = Color.fromRGBO(62, 73, 113, 1.0);
|
||||
static const Color deepVioletBlue = Color.fromRGBO(52, 66, 104, 1.0);
|
||||
|
||||
// FIXME: Rename.
|
||||
static const Color eee = Color.fromRGBO(236, 239, 245, 1.0);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cake_wallet/src/domain/common/sync_status.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -49,8 +51,10 @@ ReactionDisposer _initialAuthReaction;
|
|||
ReactionDisposer _onCurrentWalletChangeReaction;
|
||||
ReactionDisposer _onWalletSyncStatusChangeReaction;
|
||||
ReactionDisposer _onCurrentFiatCurrencyChangeDisposer;
|
||||
Timer _reconnectionTimer;
|
||||
|
||||
Future<void> bootstrap({FiatConvertationService fiatConvertationService}) async {
|
||||
Future<void> bootstrap(
|
||||
{FiatConvertationService fiatConvertationService}) async {
|
||||
final authenticationStore = getIt.get<AuthenticationStore>();
|
||||
final settingsStore = getIt.get<SettingsStore>();
|
||||
final fiatConvertationStore = getIt.get<FiatConvertationStore>();
|
||||
|
@ -72,12 +76,21 @@ Future<void> bootstrap({FiatConvertationService fiatConvertationService}) async
|
|||
|
||||
_onCurrentWalletChangeReaction ??=
|
||||
reaction((_) => getIt.get<AppStore>().wallet, (WalletBase wallet) async {
|
||||
print('Wallet name ${wallet.name}');
|
||||
|
||||
_onWalletSyncStatusChangeReaction?.reaction?.dispose();
|
||||
_onWalletSyncStatusChangeReaction = when(
|
||||
_reconnectionTimer?.cancel();
|
||||
_onWalletSyncStatusChangeReaction = reaction(
|
||||
(_) => wallet.syncStatus is ConnectedSyncStatus,
|
||||
() async => await wallet.startSync());
|
||||
(Object _) async => await wallet.startSync());
|
||||
|
||||
_reconnectionTimer = Timer.periodic(Duration(seconds: 5), (_) async {
|
||||
if (wallet.syncStatus is LostConnectionSyncStatus ||
|
||||
wallet.syncStatus is FailedSyncStatus) {
|
||||
try {
|
||||
await wallet.connectToNode(
|
||||
node: settingsStore.getCurrentNode(wallet.type));
|
||||
} catch (_) {}
|
||||
}
|
||||
});
|
||||
|
||||
await getIt
|
||||
.get<SharedPreferences>()
|
||||
|
@ -87,30 +100,24 @@ Future<void> bootstrap({FiatConvertationService fiatConvertationService}) async
|
|||
.get<SharedPreferences>()
|
||||
.setInt('current_wallet_type', serializeToInt(wallet.type));
|
||||
|
||||
await wallet.connectToNode(node: null);
|
||||
|
||||
final node = settingsStore.getCurrentNode(wallet.type);
|
||||
final cryptoCurrency = wallet.currency;
|
||||
final fiatCurrency = settingsStore.fiatCurrency;
|
||||
|
||||
await wallet.connectToNode(node: node);
|
||||
|
||||
final price = await fiatConvertationService.getPrice(
|
||||
crypto: cryptoCurrency,
|
||||
fiat: fiatCurrency
|
||||
);
|
||||
crypto: cryptoCurrency, fiat: fiatCurrency);
|
||||
|
||||
fiatConvertationStore.setPrice(price);
|
||||
});
|
||||
|
||||
//
|
||||
|
||||
_onCurrentFiatCurrencyChangeDisposer ??=
|
||||
reaction((_) => settingsStore.fiatCurrency,
|
||||
(FiatCurrency fiatCurrency) async {
|
||||
_onCurrentFiatCurrencyChangeDisposer ??= reaction(
|
||||
(_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async {
|
||||
final cryptoCurrency = getIt.get<AppStore>().wallet.currency;
|
||||
|
||||
final price = await fiatConvertationService.getPrice(
|
||||
crypto: cryptoCurrency,
|
||||
fiat: fiatCurrency
|
||||
);
|
||||
crypto: cryptoCurrency, fiat: fiatCurrency);
|
||||
|
||||
fiatConvertationStore.setPrice(price);
|
||||
});
|
||||
|
|
|
@ -358,7 +358,9 @@ class Router {
|
|||
|
||||
case Routes.exchangeTrade:
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => MultiProvider(
|
||||
builder: (_) => getIt.get<ExchangeTradePage>());
|
||||
|
||||
/*MultiProvider(
|
||||
providers: [
|
||||
ProxyProvider<SettingsStore, ExchangeTradeStore>(
|
||||
update: (_, settingsStore, __) => ExchangeTradeStore(
|
||||
|
@ -374,12 +376,13 @@ class Router {
|
|||
priceStore: priceStore)),
|
||||
],
|
||||
child: ExchangeTradePage(),
|
||||
));
|
||||
));*/
|
||||
|
||||
case Routes.exchangeConfirm:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) =>
|
||||
ExchangeConfirmPage(trade: settings.arguments as Trade));
|
||||
builder: (_) => getIt.get<ExchangeConfirmPage>());
|
||||
|
||||
//ExchangeConfirmPage(trade: settings.arguments as Trade));
|
||||
|
||||
case Routes.tradeDetails:
|
||||
return MaterialPageRoute<void>(builder: (context) {
|
||||
|
|
|
@ -29,15 +29,19 @@ Future defaultSettingsMigration(
|
|||
switch (version) {
|
||||
case 1:
|
||||
await sharedPreferences.setString(
|
||||
SettingsStoreBase.currentFiatCurrencyKey, FiatCurrency.usd.toString());
|
||||
SettingsStoreBase.currentFiatCurrencyKey,
|
||||
FiatCurrency.usd.toString());
|
||||
await sharedPreferences.setInt(
|
||||
SettingsStoreBase.currentTransactionPriorityKey, TransactionPriority.standart.raw);
|
||||
SettingsStoreBase.currentTransactionPriorityKey,
|
||||
TransactionPriority.standart.raw);
|
||||
await sharedPreferences.setInt(
|
||||
SettingsStoreBase.currentBalanceDisplayModeKey,
|
||||
BalanceDisplayMode.availableBalance.raw);
|
||||
await sharedPreferences.setBool('save_recipient_address', true);
|
||||
await resetToDefault(nodes);
|
||||
await changeCurrentNodeToDefault(
|
||||
await changeMoneroCurrentNodeToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
await changeBitcoinCurrentElectrumServerToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
|
||||
break;
|
||||
|
@ -50,6 +54,11 @@ Future defaultSettingsMigration(
|
|||
case 3:
|
||||
await updateNodeTypes(nodes: nodes);
|
||||
await addBitcoinElectrumServerList(nodes: nodes);
|
||||
|
||||
break;
|
||||
case 4:
|
||||
await changeBitcoinCurrentElectrumServerToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -69,10 +78,11 @@ Future defaultSettingsMigration(
|
|||
Future<void> replaceNodesMigration({@required Box<Node> nodes}) async {
|
||||
final replaceNodes = <String, Node>{
|
||||
'eu-node.cakewallet.io:18081':
|
||||
Node(uri: 'xmr-node-eu.cakewallet.com:18081'),
|
||||
'node.cakewallet.io:18081':
|
||||
Node(uri: 'xmr-node-usa-east.cakewallet.com:18081'),
|
||||
'node.xmr.ru:13666': Node(uri: 'node.monero.net:18081')
|
||||
Node(uri: 'xmr-node-eu.cakewallet.com:18081', type: WalletType.monero),
|
||||
'node.cakewallet.io:18081': Node(
|
||||
uri: 'xmr-node-usa-east.cakewallet.com:18081', type: WalletType.monero),
|
||||
'node.xmr.ru:13666':
|
||||
Node(uri: 'node.monero.net:18081', type: WalletType.monero)
|
||||
};
|
||||
|
||||
nodes.values.forEach((Node node) async {
|
||||
|
@ -87,11 +97,27 @@ Future<void> replaceNodesMigration({@required Box<Node> nodes}) async {
|
|||
});
|
||||
}
|
||||
|
||||
Future<void> changeCurrentNodeToDefault(
|
||||
Future<void> changeMoneroCurrentNodeToDefault(
|
||||
{@required SharedPreferences sharedPreferences,
|
||||
@required Box<Node> nodes}) async {
|
||||
final node = getMoneroDefaultNode(nodes: nodes);
|
||||
final nodeId = node?.key as int ?? 0; // 0 - England
|
||||
|
||||
await sharedPreferences.setInt('current_node_id', nodeId);
|
||||
}
|
||||
|
||||
Node getBitcoinDefaultElectrumServer({@required Box<Node> nodes}) {
|
||||
final uri = 'bitcoin.electrumx.multicoin.co:50002';
|
||||
|
||||
return nodes.values
|
||||
.firstWhere((Node node) => node.uri == uri, orElse: () => null) ??
|
||||
nodes.values.firstWhere((node) => node.type == WalletType.bitcoin,
|
||||
orElse: () => null);
|
||||
}
|
||||
|
||||
Node getMoneroDefaultNode({@required Box<Node> nodes}) {
|
||||
final timeZone = DateTime.now().timeZoneOffset.inHours;
|
||||
String nodeUri = '';
|
||||
var nodeUri = '';
|
||||
|
||||
if (timeZone >= 1) {
|
||||
// Eurasia
|
||||
|
@ -101,11 +127,18 @@ Future<void> changeCurrentNodeToDefault(
|
|||
nodeUri = 'xmr-node-usa-east.cakewallet.com:18081';
|
||||
}
|
||||
|
||||
final node = nodes.values.firstWhere((Node node) => node.uri == nodeUri) ??
|
||||
return nodes.values
|
||||
.firstWhere((Node node) => node.uri == nodeUri, orElse: () => null) ??
|
||||
nodes.values.first;
|
||||
final nodeId = node != null ? node.key as int : 0; // 0 - England
|
||||
}
|
||||
|
||||
await sharedPreferences.setInt('current_node_id', nodeId);
|
||||
Future<void> changeBitcoinCurrentElectrumServerToDefault(
|
||||
{@required SharedPreferences sharedPreferences,
|
||||
@required Box<Node> nodes}) async {
|
||||
final server = getBitcoinDefaultElectrumServer(nodes: nodes);
|
||||
final serverId = server?.key as int ?? 0;
|
||||
|
||||
await sharedPreferences.setInt('current_node_id_btc', serverId);
|
||||
}
|
||||
|
||||
Future<void> replaceDefaultNode(
|
||||
|
@ -126,7 +159,7 @@ Future<void> replaceDefaultNode(
|
|||
return;
|
||||
}
|
||||
|
||||
await changeCurrentNodeToDefault(
|
||||
await changeMoneroCurrentNodeToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,12 +9,16 @@ part 'node.g.dart';
|
|||
|
||||
@HiveType(typeId: 1)
|
||||
class Node extends HiveObject {
|
||||
Node({@required this.uri, @required WalletType type, 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,
|
||||
: uri = map['uri'] as String ?? '',
|
||||
login = map['login'] as String,
|
||||
password = map['password'] as String,
|
||||
typeRaw = map['typeRaw'] as int;
|
||||
|
|
|
@ -10,7 +10,10 @@ Future<List<Node>> loadDefaultNodes() async {
|
|||
|
||||
return nodes.map((dynamic raw) {
|
||||
if (raw is Map) {
|
||||
return Node.fromMap(raw);
|
||||
final node = Node.fromMap(raw);
|
||||
node?.type = WalletType.monero;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -38,13 +41,7 @@ Future resetToDefault(Box<Node> nodeSource) async {
|
|||
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++) {
|
||||
entities[i] = nodes[i];
|
||||
}
|
||||
|
||||
await nodeSource.putAll(entities);
|
||||
await nodeSource.addAll(nodes);
|
||||
}
|
||||
|
|
|
@ -73,3 +73,11 @@ class ConnectedSyncStatus extends SyncStatus {
|
|||
@override
|
||||
String title() => S.current.sync_status_connected;
|
||||
}
|
||||
|
||||
class LostConnectionSyncStatus extends SyncStatus {
|
||||
@override
|
||||
double progress() => 1.0;
|
||||
|
||||
@override
|
||||
String title() => S.current.sync_status_failed_connect;
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
import 'package:cake_wallet/src/domain/common/transaction_direction.dart';
|
||||
|
||||
abstract class TransactionInfo extends Object {
|
||||
String id;
|
||||
int amount;
|
||||
TransactionDirection direction;
|
||||
bool isPending;
|
||||
DateTime date;
|
||||
int height;
|
||||
int confirmations;
|
||||
String amountFormatted();
|
||||
String fiatAmount();
|
||||
void changeFiatAmount(String amount);
|
||||
|
|
|
@ -9,6 +9,7 @@ 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';
|
||||
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||
|
||||
class ContactListPage extends BasePage {
|
||||
ContactListPage(this.contactListViewModel, {this.isEditable = true});
|
||||
|
@ -30,7 +31,7 @@ class ContactListPage extends BasePage {
|
|||
height: 32.0,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor),
|
||||
color: Theme.of(context).accentTextTheme.caption.color),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
|
@ -54,30 +55,30 @@ class ContactListPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final shortDivider = Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).primaryTextTheme.title.backgroundColor,
|
||||
),
|
||||
);
|
||||
|
||||
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(
|
||||
? SectionStandardList(
|
||||
sectionCount: 1,
|
||||
context: context,
|
||||
itemCounter: (int sectionIndex) => contactListViewModel.contacts.length,
|
||||
itemBuilder: (_, sectionIndex, index) {
|
||||
final contact = contactListViewModel.contacts[index];
|
||||
final image = _getCurrencyImage(contact.type);
|
||||
final content = Builder(
|
||||
builder: (context) => GestureDetector(
|
||||
onTap: () async {
|
||||
if (!isEditable) {
|
||||
Navigator.of(context).pop(contact);
|
||||
|
@ -106,10 +107,6 @@ class ContactListPage extends BasePage {
|
|||
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),
|
||||
|
@ -117,7 +114,7 @@ class ContactListPage extends BasePage {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image ?? Offstage(),
|
||||
Padding(
|
||||
|
@ -128,6 +125,7 @@ class ContactListPage extends BasePage {
|
|||
contact.name,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
|
@ -139,57 +137,55 @@ class ContactListPage extends BasePage {
|
|||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
)
|
||||
);
|
||||
|
||||
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;
|
||||
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),
|
||||
),
|
||||
);
|
||||
})
|
||||
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),
|
||||
color: Colors.grey,
|
||||
fontSize: 14),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
@ -30,7 +31,7 @@ class ContactPage extends BasePage {
|
|||
.addListener(() => contactViewModel.address = _addressController.text);
|
||||
|
||||
autorun((_) =>
|
||||
_currencyTypeController.text = contactViewModel.currency.toString());
|
||||
_currencyTypeController.text = contactViewModel.currency?.toString()??'');
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -45,7 +46,7 @@ class ContactPage extends BasePage {
|
|||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final downArrow = Image.asset('assets/images/arrow_bottom_purple_icon.png',
|
||||
color: Theme.of(context).dividerColor, height: 8);
|
||||
color: Theme.of(context).primaryTextTheme.overline.color, height: 8);
|
||||
|
||||
reaction((_) => contactViewModel.state, (ContactViewModelState state) {
|
||||
if (state is ContactCreationFailure) {
|
||||
|
@ -57,79 +58,83 @@ class ContactPage extends BasePage {
|
|||
}
|
||||
});
|
||||
|
||||
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],
|
||||
),
|
||||
)),
|
||||
),
|
||||
return 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)))
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Observer(
|
||||
builder: (_) => AddressTextField(
|
||||
controller: _addressController,
|
||||
options: [AddressTextFieldOption.qrCode],
|
||||
buttonColor: Theme.of(context).accentTextTheme.display2.color,
|
||||
iconColor: PaletteDark.gray,
|
||||
borderColor: Theme.of(context).primaryTextTheme.title.backgroundColor,
|
||||
validator: AddressValidator(
|
||||
type: contactViewModel.currency),
|
||||
)),
|
||||
)
|
||||
],
|
||||
)),
|
||||
);
|
||||
),
|
||||
),
|
||||
bottomSectionPadding:
|
||||
EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: PrimaryButton(
|
||||
onPressed: () {
|
||||
contactViewModel.reset();
|
||||
_nameController.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;
|
||||
}
|
||||
|
||||
await contactViewModel.save();
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Palette.blueCraiola,
|
||||
textColor: Colors.white,
|
||||
isDisabled: !contactViewModel.isReady)))
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
void _presentCurrencyPicker(BuildContext context) {
|
||||
|
|
|
@ -27,25 +27,20 @@ class DashboardPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget Function(BuildContext, Widget) get rootWrapper =>
|
||||
(BuildContext context, Widget scaffold) => Container(
|
||||
(BuildContext context, Widget scaffold) => Container(
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(colors: [
|
||||
Theme.of(context).accentColor,
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
Theme.of(context).primaryColor,
|
||||
],
|
||||
begin: Alignment.topRight,
|
||||
end: Alignment.bottomLeft)),
|
||||
Theme.of(context).accentColor,
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
Theme.of(context).primaryColor,
|
||||
], begin: Alignment.topRight, end: Alignment.bottomLeft)),
|
||||
child: scaffold);
|
||||
|
||||
@override
|
||||
bool get resizeToAvoidBottomPadding => false;
|
||||
|
||||
@override
|
||||
Widget get endDrawer => MenuWidget(
|
||||
name: walletViewModel.name,
|
||||
subname: walletViewModel.subname,
|
||||
type: walletViewModel.type);
|
||||
Widget get endDrawer => MenuWidget(walletViewModel);
|
||||
|
||||
@override
|
||||
Widget middle(BuildContext context) {
|
||||
|
@ -54,20 +49,18 @@ class DashboardPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget trailing(BuildContext context) {
|
||||
final menuButton = Image.asset('assets/images/menu.png',
|
||||
color: Colors.white);
|
||||
final menuButton =
|
||||
Image.asset('assets/images/menu.png', color: Colors.white);
|
||||
|
||||
return Container(
|
||||
alignment: Alignment.centerRight,
|
||||
width: 40,
|
||||
child: FlatButton(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
onPressed: () => onOpenEndDrawer(),
|
||||
child: menuButton
|
||||
)
|
||||
);
|
||||
alignment: Alignment.centerRight,
|
||||
width: 40,
|
||||
child: FlatButton(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
onPressed: () => onOpenEndDrawer(),
|
||||
child: menuButton));
|
||||
}
|
||||
|
||||
final DashboardViewModel walletViewModel;
|
||||
|
@ -85,77 +78,64 @@ class DashboardPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
|
||||
_setEffects();
|
||||
|
||||
return SafeArea(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: PageView.builder(
|
||||
controller: controller,
|
||||
itemCount: pages.length,
|
||||
itemBuilder: (context, index) {
|
||||
return pages[index];
|
||||
}
|
||||
)
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: 24
|
||||
),
|
||||
controller: controller,
|
||||
itemCount: pages.length,
|
||||
itemBuilder: (context, index) {
|
||||
return pages[index];
|
||||
})),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 24),
|
||||
child: SmoothPageIndicator(
|
||||
controller: controller,
|
||||
count: pages.length,
|
||||
count: pages.length,
|
||||
effect: ColorTransitionEffect(
|
||||
spacing: 6.0,
|
||||
radius: 6.0,
|
||||
dotWidth: 6.0,
|
||||
dotHeight: 6.0,
|
||||
dotColor: Theme.of(context).indicatorColor,
|
||||
activeDotColor: Colors.white
|
||||
),
|
||||
)
|
||||
),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(
|
||||
left: 45,
|
||||
right: 45,
|
||||
bottom: 24
|
||||
),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: ActionButton(
|
||||
image: sendImage,
|
||||
title: S.of(context).send,
|
||||
route: Routes.send,
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
spacing: 6.0,
|
||||
radius: 6.0,
|
||||
dotWidth: 6.0,
|
||||
dotHeight: 6.0,
|
||||
dotColor: Theme.of(context).indicatorColor,
|
||||
activeDotColor: Colors.white),
|
||||
)),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(left: 45, right: 45, bottom: 24),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: ActionButton(
|
||||
image: sendImage,
|
||||
title: S.of(context).send,
|
||||
route: Routes.send,
|
||||
alignment: Alignment.centerLeft,
|
||||
),
|
||||
Flexible(
|
||||
child: ActionButton(
|
||||
),
|
||||
Flexible(
|
||||
child: ActionButton(
|
||||
image: exchangeImage,
|
||||
title: S.of(context).exchange,
|
||||
route: Routes.exchange
|
||||
),
|
||||
route: Routes.exchange),
|
||||
),
|
||||
Flexible(
|
||||
child: ActionButton(
|
||||
image: receiveImage,
|
||||
title: S.of(context).receive,
|
||||
route: Routes.receive,
|
||||
alignment: Alignment.centerRight,
|
||||
),
|
||||
Flexible(
|
||||
child: ActionButton(
|
||||
image: receiveImage,
|
||||
title: S.of(context).receive,
|
||||
route: Routes.receive,
|
||||
alignment: Alignment.centerRight,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
void _setEffects() {
|
||||
|
|
|
@ -6,8 +6,10 @@ import 'package:cake_wallet/src/stores/wallet/wallet_store.dart';
|
|||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
|
||||
// FIXME: terrible design
|
||||
|
||||
class WalletMenu {
|
||||
WalletMenu(this.context);
|
||||
WalletMenu(this.context, this.reconnect);
|
||||
|
||||
final List<String> items = [
|
||||
S.current.reconnect,
|
||||
|
@ -30,6 +32,7 @@ class WalletMenu {
|
|||
];
|
||||
|
||||
final BuildContext context;
|
||||
final Future<void> Function() reconnect;
|
||||
|
||||
void action(int index) {
|
||||
switch (index) {
|
||||
|
@ -70,8 +73,6 @@ class WalletMenu {
|
|||
}
|
||||
|
||||
Future<void> _presentReconnectAlert(BuildContext context) async {
|
||||
final walletStore = Provider.of<WalletStore>(context);
|
||||
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
|
@ -80,12 +81,11 @@ class WalletMenu {
|
|||
alertContent: S.of(context).reconnect_alert_text,
|
||||
leftButtonText: S.of(context).ok,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () {
|
||||
walletStore.reconnect();
|
||||
actionLeftButton: () async {
|
||||
await reconnect?.call();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
actionRightButton: () => Navigator.of(context).pop()
|
||||
);
|
||||
actionRightButton: () => Navigator.of(context).pop());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
21
lib/src/screens/dashboard/widgets/filter_tile.dart
Normal file
21
lib/src/screens/dashboard/widgets/filter_tile.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class FilterTile extends StatelessWidget {
|
||||
FilterTile({@required this.child});
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(
|
||||
top: 18,
|
||||
bottom: 18,
|
||||
left: 24,
|
||||
right: 24
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
155
lib/src/screens/dashboard/widgets/filter_widget.dart
Normal file
155
lib/src/screens/dashboard/widgets/filter_widget.dart
Normal file
|
@ -0,0 +1,155 @@
|
|||
import 'dart:ui';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_tile.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_background.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/checkbox_widget.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
|
||||
|
||||
class FilterWidget extends StatelessWidget {
|
||||
FilterWidget({@required this.dashboardViewModel});
|
||||
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
final backVector = Image.asset('assets/images/back_vector.png',
|
||||
color: Palette.darkBlueCraiola
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertBackground(
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).filters,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'Poppins',
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: 24
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
child: Container(
|
||||
color: Theme.of(context).textTheme.body2.decorationColor,
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: dashboardViewModel.filterItems.length,
|
||||
separatorBuilder: (context, _) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).accentTextTheme.subhead.backgroundColor,
|
||||
),
|
||||
itemBuilder: (_, index1) {
|
||||
final title = dashboardViewModel.filterItems.keys.elementAt(index1);
|
||||
final section = dashboardViewModel.filterItems.values.elementAt(index1);
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 20,
|
||||
left: 24,
|
||||
right: 24
|
||||
),
|
||||
child: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).accentTextTheme.subhead.color,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontFamily: 'Poppins',
|
||||
decoration: TextDecoration.none
|
||||
),
|
||||
),
|
||||
),
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: section.length,
|
||||
separatorBuilder: (context, _) => Container(
|
||||
height: 1,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
color: Theme.of(context).textTheme.body2.decorationColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).accentTextTheme.subhead.backgroundColor,
|
||||
),
|
||||
),
|
||||
itemBuilder: (_, index2) {
|
||||
|
||||
final item = section[index2];
|
||||
final content = item.onChanged != null
|
||||
? CheckboxWidget(
|
||||
value: item.value,
|
||||
caption: item.caption,
|
||||
onChanged: item.onChanged
|
||||
)
|
||||
: GestureDetector(
|
||||
onTap: () async {
|
||||
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) {
|
||||
dashboardViewModel.transactionFilterStore
|
||||
.changeStartDate(picked.first);
|
||||
dashboardViewModel.transactionFilterStore
|
||||
.changeEndDate(picked.last);
|
||||
}
|
||||
},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 32),
|
||||
child: Text(
|
||||
item.caption,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
fontSize: 18,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.w500,
|
||||
decoration: TextDecoration.none
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return FilterTile(child: content);
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
AlertCloseButton(image: backVector)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,9 +1,7 @@
|
|||
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
||||
class HeaderRow extends StatelessWidget {
|
||||
HeaderRow({this.dashboardViewModel});
|
||||
|
@ -31,148 +29,13 @@ class HeaderRow extends StatelessWidget {
|
|||
color: Colors.white
|
||||
),
|
||||
),
|
||||
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.title.color))),
|
||||
PopupMenuItem(
|
||||
value: 0,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(S.of(context).incoming,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
Checkbox(
|
||||
value: dashboardViewModel
|
||||
.transactionFilterStore
|
||||
.displayIncoming,
|
||||
onChanged: (value) => dashboardViewModel
|
||||
.transactionFilterStore
|
||||
.toggleIncoming()
|
||||
)
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 1,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(S.of(context).outgoing,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
)
|
||||
),
|
||||
Checkbox(
|
||||
value: dashboardViewModel
|
||||
.transactionFilterStore
|
||||
.displayOutgoing,
|
||||
onChanged: (value) => dashboardViewModel
|
||||
.transactionFilterStore
|
||||
.toggleOutgoing(),
|
||||
)
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 2,
|
||||
child:
|
||||
Text(S.of(context).transactions_by_date,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
)
|
||||
)),
|
||||
PopupMenuDivider(),
|
||||
PopupMenuItem(
|
||||
enabled: false,
|
||||
value: -1,
|
||||
child: Text(S.of(context).trades,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color))),
|
||||
PopupMenuItem(
|
||||
value: 3,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text('XMR.TO',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
)
|
||||
),
|
||||
Checkbox(
|
||||
value: dashboardViewModel
|
||||
.tradeFilterStore
|
||||
.displayXMRTO,
|
||||
onChanged: (value) => dashboardViewModel
|
||||
.tradeFilterStore
|
||||
.toggleDisplayExchange(
|
||||
ExchangeProviderDescription
|
||||
.xmrto),
|
||||
)
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 4,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text('Change.NOW',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
)
|
||||
),
|
||||
Checkbox(
|
||||
value: dashboardViewModel
|
||||
.tradeFilterStore
|
||||
.displayChangeNow,
|
||||
onChanged: (value) => dashboardViewModel
|
||||
.tradeFilterStore
|
||||
.toggleDisplayExchange(
|
||||
ExchangeProviderDescription
|
||||
.changeNow),
|
||||
)
|
||||
]))),
|
||||
PopupMenuItem(
|
||||
value: 5,
|
||||
child: Observer(
|
||||
builder: (_) => Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text('MorphToken',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
)
|
||||
),
|
||||
Checkbox(
|
||||
value: dashboardViewModel
|
||||
.tradeFilterStore
|
||||
.displayMorphToken,
|
||||
onChanged: (value) => dashboardViewModel
|
||||
.tradeFilterStore
|
||||
.toggleDisplayExchange(
|
||||
ExchangeProviderDescription
|
||||
.morphToken),
|
||||
)
|
||||
])))
|
||||
],
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) => FilterWidget(dashboardViewModel: dashboardViewModel)
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
height: 36,
|
||||
width: 36,
|
||||
|
@ -182,27 +45,7 @@ class HeaderRow extends StatelessWidget {
|
|||
),
|
||||
child: filterIcon,
|
||||
),
|
||||
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)));
|
||||
|
||||
if (picked != null && picked.length == 2) {
|
||||
dashboardViewModel.transactionFilterStore
|
||||
.changeStartDate(picked.first);
|
||||
dashboardViewModel.transactionFilterStore
|
||||
.changeEndDate(picked.last);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,29 +1,30 @@
|
|||
import 'dart:ui';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/src/screens/dashboard/wallet_menu.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
// FIXME: terrible design.
|
||||
|
||||
class MenuWidget extends StatefulWidget {
|
||||
MenuWidget({this.type, this.name, this.subname});
|
||||
MenuWidget(this.dashboardViewModel);
|
||||
|
||||
final WalletType type;
|
||||
final String name;
|
||||
final String subname;
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
|
||||
@override
|
||||
MenuWidgetState createState() => MenuWidgetState();
|
||||
}
|
||||
|
||||
class MenuWidgetState extends State<MenuWidget> {
|
||||
final moneroIcon = Image.asset('assets/images/monero_menu.png');
|
||||
final bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png');
|
||||
Image moneroIcon;
|
||||
Image bitcoinIcon;
|
||||
final largeScreen = 731;
|
||||
|
||||
double menuWidth;
|
||||
double screenWidth;
|
||||
double screenHeight;
|
||||
double opacity;
|
||||
|
||||
double headerHeight;
|
||||
double tileHeight;
|
||||
|
@ -35,12 +36,11 @@ class MenuWidgetState extends State<MenuWidget> {
|
|||
menuWidth = 0;
|
||||
screenWidth = 0;
|
||||
screenHeight = 0;
|
||||
opacity = 0;
|
||||
|
||||
headerHeight = 125;
|
||||
headerHeight = 137;
|
||||
tileHeight = 75;
|
||||
fromTopEdge = 50;
|
||||
fromBottomEdge = 21;
|
||||
fromBottomEdge = 30;
|
||||
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
|
||||
|
@ -52,7 +52,6 @@ class MenuWidgetState extends State<MenuWidget> {
|
|||
|
||||
setState(() {
|
||||
menuWidth = screenWidth;
|
||||
opacity = 1;
|
||||
|
||||
if (screenHeight > largeScreen) {
|
||||
final scale = screenHeight / largeScreen;
|
||||
|
@ -66,145 +65,163 @@ class MenuWidgetState extends State<MenuWidget> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final walletMenu = WalletMenu(context);
|
||||
final walletMenu =
|
||||
WalletMenu(context, () async => widget.dashboardViewModel.reconnect());
|
||||
final itemCount = walletMenu.items.length;
|
||||
|
||||
return SafeArea(
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
moneroIcon = Image.asset('assets/images/monero_menu.png',
|
||||
color: Theme.of(context).accentTextTheme.overline.decorationColor);
|
||||
bitcoinIcon = Image.asset('assets/images/bitcoin_menu.png',
|
||||
color: Theme.of(context).accentTextTheme.overline.decorationColor);
|
||||
|
||||
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: PaletteDark.gray),
|
||||
borderRadius: BorderRadius.all(Radius.circular(2)),
|
||||
color: PaletteDark.gray),
|
||||
)),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(24),
|
||||
bottomLeft: Radius.circular(24)),
|
||||
topLeft: Radius.circular(24),
|
||||
bottomLeft: Radius.circular(24)),
|
||||
child: Container(
|
||||
width: menuWidth,
|
||||
height: double.infinity,
|
||||
color: Theme.of(context).textTheme.body2.decorationColor,
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: headerHeight,
|
||||
color: Theme.of(context).textTheme.body2.color,
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
top: fromTopEdge,
|
||||
right: 24,
|
||||
bottom: fromBottomEdge),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
_iconFor(type: widget.type),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 42,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: widget.subname != null
|
||||
? MainAxisAlignment.spaceBetween
|
||||
: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
widget.name,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).textTheme
|
||||
.display2.color,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
if (widget.subname != null)
|
||||
Text(
|
||||
widget.subname,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption.color,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12),
|
||||
)
|
||||
],
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).primaryTextTheme.caption.decorationColor,
|
||||
),
|
||||
ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder: (_, index) {
|
||||
|
||||
final item = walletMenu.items[index];
|
||||
final image = walletMenu.images[index] ?? Offstage();
|
||||
final isLastTile = index == itemCount - 1;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
walletMenu.action(index);
|
||||
},
|
||||
child: Container(
|
||||
height: isLastTile
|
||||
? headerHeight
|
||||
: tileHeight,
|
||||
padding: isLastTile
|
||||
? EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: fromBottomEdge,
|
||||
bottom: fromTopEdge)
|
||||
: EdgeInsets.only(left: 24, right: 24),
|
||||
alignment: isLastTile ? Alignment.topLeft : null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
child: ListView.separated(
|
||||
padding: EdgeInsets.only(top: 0),
|
||||
itemBuilder: (_, index) {
|
||||
if (index == 0) {
|
||||
return Container(
|
||||
height: headerHeight,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context)
|
||||
.accentTextTheme
|
||||
.display1
|
||||
.color,
|
||||
Theme.of(context)
|
||||
.accentTextTheme
|
||||
.display1
|
||||
.decorationColor,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight),
|
||||
),
|
||||
padding: EdgeInsets.only(
|
||||
left: 24,
|
||||
top: fromTopEdge,
|
||||
right: 24,
|
||||
bottom: fromBottomEdge),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
_iconFor(type: widget.dashboardViewModel.type),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Container(
|
||||
height: 42,
|
||||
child: Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
mainAxisAlignment:
|
||||
widget.dashboardViewModel.subname !=
|
||||
null
|
||||
? MainAxisAlignment.spaceBetween
|
||||
: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).textTheme
|
||||
.display2.color,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold),
|
||||
))
|
||||
Text(
|
||||
widget.dashboardViewModel.name,
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold),
|
||||
),
|
||||
if (widget.dashboardViewModel.subname !=
|
||||
null)
|
||||
Text(
|
||||
widget.dashboardViewModel.subname,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.accentTextTheme
|
||||
.overline
|
||||
.decorationColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).primaryTextTheme.caption.decorationColor,
|
||||
))
|
||||
],
|
||||
),
|
||||
itemCount: itemCount)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
index--;
|
||||
|
||||
final item = walletMenu.items[index];
|
||||
final image = walletMenu.images[index] ?? Offstage();
|
||||
final isLastTile = index == itemCount - 1;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
walletMenu.action(index);
|
||||
},
|
||||
child: Container(
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.body2
|
||||
.decorationColor,
|
||||
height: isLastTile ? headerHeight : tileHeight,
|
||||
padding: isLastTile
|
||||
? EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24,
|
||||
top: fromBottomEdge,
|
||||
//bottom: fromTopEdge
|
||||
)
|
||||
: EdgeInsets.only(left: 24, right: 24),
|
||||
alignment: isLastTile ? Alignment.topLeft : null,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: Text(
|
||||
item,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.display2
|
||||
.color,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold),
|
||||
))
|
||||
],
|
||||
),
|
||||
));
|
||||
},
|
||||
separatorBuilder: (_, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.caption
|
||||
.decorationColor,
|
||||
),
|
||||
itemCount: itemCount + 1),
|
||||
)))
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,10 +18,13 @@ class ExchangePage extends BasePage {
|
|||
String get title => S.current.exchange;
|
||||
|
||||
@override
|
||||
Color get backgroundLightColor => PaletteDark.wildVioletBlue;
|
||||
Color get titleColor => Colors.white;
|
||||
|
||||
@override
|
||||
Color get backgroundDarkColor => PaletteDark.wildVioletBlue;
|
||||
Color get backgroundLightColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
Color get backgroundDarkColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
Widget middle(BuildContext context) =>
|
||||
|
@ -36,5 +39,21 @@ class ExchangePage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) =>
|
||||
BaseExchangeWidget(exchangeViewModel: exchangeViewModel);
|
||||
BaseExchangeWidget(
|
||||
exchangeViewModel: exchangeViewModel,
|
||||
leading: leading(context),
|
||||
middle: middle(context),
|
||||
trailing: trailing(context),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
|
||||
body: Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: body(context)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:ui';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.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';
|
||||
|
@ -17,10 +16,13 @@ class ExchangeTemplatePage extends BasePage {
|
|||
String get title => S.current.exchange_new_template;
|
||||
|
||||
@override
|
||||
Color get backgroundLightColor => PaletteDark.wildVioletBlue;
|
||||
Color get titleColor => Colors.white;
|
||||
|
||||
@override
|
||||
Color get backgroundDarkColor => PaletteDark.wildVioletBlue;
|
||||
Color get backgroundLightColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
Color get backgroundDarkColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
Widget trailing(BuildContext context) =>
|
||||
|
@ -28,5 +30,22 @@ class ExchangeTemplatePage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) =>
|
||||
BaseExchangeWidget(exchangeViewModel: exchangeViewModel, isTemplate: true);
|
||||
BaseExchangeWidget(
|
||||
exchangeViewModel: exchangeViewModel,
|
||||
leading: leading(context),
|
||||
middle: middle(context),
|
||||
trailing: trailing(context),
|
||||
isTemplate: true
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
|
||||
body: Container(
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: body(context)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -25,29 +25,43 @@ import 'package:cake_wallet/core/amount_validator.dart';
|
|||
|
||||
class BaseExchangeWidget extends StatefulWidget {
|
||||
BaseExchangeWidget({
|
||||
@ required this.exchangeViewModel,
|
||||
@required this.exchangeViewModel,
|
||||
this.leading,
|
||||
this.middle,
|
||||
this.trailing,
|
||||
this.isTemplate = false,
|
||||
});
|
||||
|
||||
final ExchangeViewModel exchangeViewModel;
|
||||
final Widget leading;
|
||||
final Widget middle;
|
||||
final Widget trailing;
|
||||
final bool isTemplate;
|
||||
|
||||
@override
|
||||
BaseExchangeWidgetState createState() =>
|
||||
BaseExchangeWidgetState(
|
||||
exchangeViewModel: exchangeViewModel,
|
||||
isTemplate: isTemplate
|
||||
);
|
||||
BaseExchangeWidgetState createState() => BaseExchangeWidgetState(
|
||||
exchangeViewModel: exchangeViewModel,
|
||||
leading: leading,
|
||||
middle: middle,
|
||||
trailing: trailing,
|
||||
isTemplate: isTemplate);
|
||||
}
|
||||
|
||||
class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
|
||||
BaseExchangeWidgetState({
|
||||
@ required this.exchangeViewModel,
|
||||
@ required this.isTemplate,
|
||||
@required this.exchangeViewModel,
|
||||
@required this.leading,
|
||||
@required this.middle,
|
||||
@required this.trailing,
|
||||
@required this.isTemplate,
|
||||
});
|
||||
|
||||
final ExchangeViewModel exchangeViewModel;
|
||||
final Widget leading;
|
||||
final Widget middle;
|
||||
final Widget trailing;
|
||||
final bool isTemplate;
|
||||
final double topPanelHeight = 290;
|
||||
|
||||
final depositKey = GlobalKey<ExchangeCardState>();
|
||||
final receiveKey = GlobalKey<ExchangeCardState>();
|
||||
|
@ -68,264 +82,329 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
|
|||
);
|
||||
|
||||
final depositWalletName =
|
||||
exchangeViewModel.depositCurrency == CryptoCurrency.xmr
|
||||
? exchangeViewModel.wallet.name
|
||||
: null;
|
||||
exchangeViewModel.depositCurrency == CryptoCurrency.xmr
|
||||
? exchangeViewModel.wallet.name
|
||||
: null;
|
||||
final receiveWalletName =
|
||||
exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
|
||||
? exchangeViewModel.wallet.name
|
||||
: null;
|
||||
exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
|
||||
? exchangeViewModel.wallet.name
|
||||
: null;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => _setReactions(context, exchangeViewModel));
|
||||
WidgetsBinding.instance
|
||||
.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
|
||||
|
||||
return Container(
|
||||
color: PaletteDark.backgroundColor,
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24),
|
||||
content: Column(
|
||||
children: <Widget>[
|
||||
TopPanel(
|
||||
color: PaletteDark.darkNightBlue,
|
||||
edgeInsets: EdgeInsets.only(bottom: 32),
|
||||
widget: Column(
|
||||
children: <Widget>[
|
||||
TopPanel(
|
||||
edgeInsets: EdgeInsets.fromLTRB(24, 29, 24, 32),
|
||||
color: PaletteDark.wildVioletBlue,
|
||||
widget: Observer(
|
||||
builder: (_) => ExchangeCard(
|
||||
key: depositKey,
|
||||
title: S.of(context).you_will_send,
|
||||
initialCurrency: exchangeViewModel.depositCurrency,
|
||||
initialWalletName: depositWalletName,
|
||||
initialAddress:
|
||||
exchangeViewModel.depositCurrency == exchangeViewModel.wallet.currency
|
||||
? exchangeViewModel.wallet.address
|
||||
: exchangeViewModel.depositAddress,
|
||||
initialIsAmountEditable: true,
|
||||
initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled,
|
||||
isAmountEstimated: false,
|
||||
currencies: CryptoCurrency.all,
|
||||
onCurrencySelected: (currency) =>
|
||||
exchangeViewModel.changeDepositCurrency(currency: currency),
|
||||
imageArrow: arrowBottomPurple,
|
||||
currencyButtonColor: PaletteDark.wildVioletBlue,
|
||||
addressButtonsColor: PaletteDark.moderateBlue,
|
||||
currencyValueValidator: AmountValidator(
|
||||
type: exchangeViewModel.wallet.type),
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type: exchangeViewModel.depositCurrency),
|
||||
),
|
||||
)
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 29, left: 24, right: 24),
|
||||
child: Observer(
|
||||
builder: (_) => ExchangeCard(
|
||||
key: receiveKey,
|
||||
title: S.of(context).you_will_get,
|
||||
initialCurrency: exchangeViewModel.receiveCurrency,
|
||||
initialWalletName: receiveWalletName,
|
||||
initialAddress:
|
||||
exchangeViewModel.receiveCurrency == exchangeViewModel.wallet.currency
|
||||
? exchangeViewModel.wallet.address
|
||||
: exchangeViewModel.receiveAddress,
|
||||
initialIsAmountEditable: false,
|
||||
initialIsAddressEditable: exchangeViewModel.isReceiveAddressEnabled,
|
||||
isAmountEstimated: true,
|
||||
currencies: CryptoCurrency.all,
|
||||
onCurrencySelected: (currency) => exchangeViewModel
|
||||
.changeReceiveCurrency(currency: currency),
|
||||
imageArrow: arrowBottomCakeGreen,
|
||||
currencyButtonColor: PaletteDark.darkNightBlue,
|
||||
addressButtonsColor: PaletteDark.moderateBlue,
|
||||
currencyValueValidator: AmountValidator(
|
||||
type: exchangeViewModel.wallet.type),
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type: exchangeViewModel.receiveCurrency),
|
||||
)),
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
isTemplate
|
||||
? Offstage()
|
||||
: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 30,
|
||||
left: 24,
|
||||
bottom: 24
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
return Form(
|
||||
key: _formKey,
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24),
|
||||
content: Column(
|
||||
children: <Widget>[
|
||||
TopPanel(
|
||||
gradient: LinearGradient(colors: [
|
||||
Theme.of(context).primaryTextTheme.body1.color,
|
||||
Theme.of(context).primaryTextTheme.body1.decorationColor,
|
||||
], stops: [
|
||||
0.35,
|
||||
1.0
|
||||
], begin: Alignment.topLeft, end: Alignment.bottomRight),
|
||||
edgeInsets: EdgeInsets.only(bottom: 32),
|
||||
widget: Column(
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).send_templates,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: PaletteDark.darkCyanBlue
|
||||
),
|
||||
TopPanel(
|
||||
edgeInsets: EdgeInsets.all(0),
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.subtitle
|
||||
.color,
|
||||
Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.subtitle
|
||||
.decorationColor,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight),
|
||||
widget: Column(
|
||||
children: <Widget>[
|
||||
CupertinoNavigationBar(
|
||||
leading: leading,
|
||||
middle: middle,
|
||||
trailing: trailing,
|
||||
backgroundColor: Colors.transparent,
|
||||
border: null,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(24, 29, 24, 32),
|
||||
child: Observer(
|
||||
builder: (_) => ExchangeCard(
|
||||
key: depositKey,
|
||||
title: S.of(context).you_will_send,
|
||||
initialCurrency:
|
||||
exchangeViewModel.depositCurrency,
|
||||
initialWalletName: depositWalletName,
|
||||
initialAddress: exchangeViewModel
|
||||
.depositCurrency ==
|
||||
exchangeViewModel.wallet.currency
|
||||
? exchangeViewModel.wallet.address
|
||||
: exchangeViewModel.depositAddress,
|
||||
initialIsAmountEditable: true,
|
||||
initialIsAddressEditable: exchangeViewModel
|
||||
.isDepositAddressEnabled,
|
||||
isAmountEstimated: false,
|
||||
currencies: CryptoCurrency.all,
|
||||
onCurrencySelected: (currency) =>
|
||||
exchangeViewModel.changeDepositCurrency(
|
||||
currency: currency),
|
||||
imageArrow: arrowBottomPurple,
|
||||
currencyButtonColor: Colors.transparent,
|
||||
addressButtonsColor:
|
||||
Theme.of(context).focusColor,
|
||||
borderColor: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.body2
|
||||
.color,
|
||||
currencyValueValidator: AmountValidator(
|
||||
type: exchangeViewModel.wallet.type),
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type:
|
||||
exchangeViewModel.depositCurrency),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
)),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 29, left: 24, right: 24),
|
||||
child: Observer(
|
||||
builder: (_) => ExchangeCard(
|
||||
key: receiveKey,
|
||||
title: S.of(context).you_will_get,
|
||||
initialCurrency:
|
||||
exchangeViewModel.receiveCurrency,
|
||||
initialWalletName: receiveWalletName,
|
||||
initialAddress:
|
||||
exchangeViewModel.receiveCurrency ==
|
||||
exchangeViewModel.wallet.currency
|
||||
? exchangeViewModel.wallet.address
|
||||
: exchangeViewModel.receiveAddress,
|
||||
initialIsAmountEditable: false,
|
||||
initialIsAddressEditable:
|
||||
exchangeViewModel.isReceiveAddressEnabled,
|
||||
isAmountEstimated: true,
|
||||
currencies: CryptoCurrency.all,
|
||||
onCurrencySelected: (currency) =>
|
||||
exchangeViewModel.changeReceiveCurrency(
|
||||
currency: currency),
|
||||
imageArrow: arrowBottomCakeGreen,
|
||||
currencyButtonColor: Colors.transparent,
|
||||
addressButtonsColor:
|
||||
Theme.of(context).focusColor,
|
||||
borderColor: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.body2
|
||||
.decorationColor,
|
||||
currencyValueValidator: AmountValidator(
|
||||
type: exchangeViewModel.wallet.type),
|
||||
addressTextFieldValidator: AddressValidator(
|
||||
type: exchangeViewModel.receiveCurrency),
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
isTemplate
|
||||
? Offstage()
|
||||
: Container(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.exchangeTemplate),
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 1, right: 10),
|
||||
child: DottedBorder(
|
||||
borderType: BorderType.RRect,
|
||||
dashPattern: [6, 4],
|
||||
color: PaletteDark.darkCyanBlue,
|
||||
strokeWidth: 2,
|
||||
radius: Radius.circular(20),
|
||||
child: Container(
|
||||
height: 34,
|
||||
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: PaletteDark.darkCyanBlue
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
final templates = exchangeViewModel.templates;
|
||||
final itemCount = exchangeViewModel.templates.length;
|
||||
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
final template = templates[index];
|
||||
|
||||
return TemplateTile(
|
||||
key: UniqueKey(),
|
||||
amount: template.amount,
|
||||
from: template.depositCurrency,
|
||||
to: template.receiveCurrency,
|
||||
onTap: () {
|
||||
applyTemplate(exchangeViewModel, template);
|
||||
},
|
||||
onRemove: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.of(context).template,
|
||||
alertContent: S.of(context).confirm_delete_template,
|
||||
leftButtonText: S.of(context).delete,
|
||||
rightButtonText: S.of(context).cancel,
|
||||
actionLeftButton: () {
|
||||
Navigator.of(dialogContext).pop();
|
||||
exchangeViewModel.exchangeTemplateStore.remove(template: template);
|
||||
exchangeViewModel.exchangeTemplateStore.update();
|
||||
},
|
||||
actionRightButton: () => Navigator.of(dialogContext).pop()
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Column(children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 15),
|
||||
child: Observer(builder: (_) {
|
||||
final description =
|
||||
exchangeViewModel.provider is XMRTOExchangeProvider
|
||||
? S.of(context).amount_is_guaranteed
|
||||
: S.of(context).amount_is_estimate;
|
||||
return Center(
|
||||
child: Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
color: PaletteDark.darkCyanBlue,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12
|
||||
)),
|
||||
isTemplate
|
||||
? Offstage()
|
||||
: Padding(
|
||||
padding: EdgeInsets.only(top: 30, 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
|
||||
.display4
|
||||
.color),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
isTemplate
|
||||
? PrimaryButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState.validate()) {
|
||||
exchangeViewModel.exchangeTemplateStore.addTemplate(
|
||||
amount: exchangeViewModel.depositAmount,
|
||||
depositCurrency: exchangeViewModel.depositCurrency.toString(),
|
||||
receiveCurrency: exchangeViewModel.receiveCurrency.toString(),
|
||||
provider: exchangeViewModel.provider.toString(),
|
||||
depositAddress: exchangeViewModel.depositAddress,
|
||||
receiveAddress: exchangeViewModel.receiveAddress
|
||||
);
|
||||
exchangeViewModel.exchangeTemplateStore.update();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white
|
||||
)
|
||||
: Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
text: S.of(context).exchange,
|
||||
? Offstage()
|
||||
: Container(
|
||||
height: 40,
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.exchangeTemplate),
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 1, right: 10),
|
||||
child: DottedBorder(
|
||||
borderType: BorderType.RRect,
|
||||
dashPattern: [6, 4],
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.display2
|
||||
.decorationColor,
|
||||
strokeWidth: 2,
|
||||
radius: Radius.circular(20),
|
||||
child: Container(
|
||||
height: 34,
|
||||
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
|
||||
.display3
|
||||
.color),
|
||||
),
|
||||
)),
|
||||
),
|
||||
),
|
||||
Observer(builder: (_) {
|
||||
final templates = exchangeViewModel.templates;
|
||||
final itemCount =
|
||||
exchangeViewModel.templates.length;
|
||||
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (context, index) {
|
||||
final template = templates[index];
|
||||
|
||||
return TemplateTile(
|
||||
key: UniqueKey(),
|
||||
amount: template.amount,
|
||||
from: template.depositCurrency,
|
||||
to: template.receiveCurrency,
|
||||
onTap: () {
|
||||
applyTemplate(
|
||||
exchangeViewModel, template);
|
||||
},
|
||||
onRemove: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (dialogContext) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle:
|
||||
S.of(context).template,
|
||||
alertContent: S
|
||||
.of(context)
|
||||
.confirm_delete_template,
|
||||
leftButtonText:
|
||||
S.of(context).delete,
|
||||
rightButtonText:
|
||||
S.of(context).cancel,
|
||||
actionLeftButton: () {
|
||||
Navigator.of(
|
||||
dialogContext)
|
||||
.pop();
|
||||
exchangeViewModel
|
||||
.exchangeTemplateStore
|
||||
.remove(
|
||||
template:
|
||||
template);
|
||||
exchangeViewModel
|
||||
.exchangeTemplateStore
|
||||
.update();
|
||||
},
|
||||
actionRightButton: () =>
|
||||
Navigator.of(
|
||||
dialogContext)
|
||||
.pop());
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
}),
|
||||
],
|
||||
)))
|
||||
],
|
||||
),
|
||||
bottomSectionPadding:
|
||||
EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Column(children: <Widget>[
|
||||
Padding(
|
||||
padding: EdgeInsets.only(bottom: 15),
|
||||
child: Observer(builder: (_) {
|
||||
final description =
|
||||
exchangeViewModel.provider is XMRTOExchangeProvider
|
||||
? S.of(context).amount_is_guaranteed
|
||||
: S.of(context).amount_is_estimate;
|
||||
return Center(
|
||||
child: Text(
|
||||
description,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.display4
|
||||
.decorationColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 12),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
isTemplate
|
||||
? PrimaryButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState.validate()) {
|
||||
exchangeViewModel.createTrade();
|
||||
exchangeViewModel.exchangeTemplateStore.addTemplate(
|
||||
amount: exchangeViewModel.depositAmount,
|
||||
depositCurrency:
|
||||
exchangeViewModel.depositCurrency.toString(),
|
||||
receiveCurrency:
|
||||
exchangeViewModel.receiveCurrency.toString(),
|
||||
provider: exchangeViewModel.provider.toString(),
|
||||
depositAddress: exchangeViewModel.depositAddress,
|
||||
receiveAddress: exchangeViewModel.receiveAddress);
|
||||
exchangeViewModel.exchangeTemplateStore.update();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
color: Colors.blue,
|
||||
textColor: Colors.white,
|
||||
isLoading: exchangeViewModel.tradeState is TradeIsCreating,
|
||||
)),
|
||||
]),
|
||||
)),
|
||||
);
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white)
|
||||
: Observer(
|
||||
builder: (_) => LoadingPrimaryButton(
|
||||
text: S.of(context).exchange,
|
||||
onPressed: () {
|
||||
if (_formKey.currentState.validate()) {
|
||||
exchangeViewModel.createTrade();
|
||||
}
|
||||
},
|
||||
color: Palette.blueCraiola,
|
||||
textColor: Colors.white,
|
||||
isLoading:
|
||||
exchangeViewModel.tradeState is TradeIsCreating,
|
||||
)),
|
||||
]),
|
||||
));
|
||||
}
|
||||
|
||||
void applyTemplate(ExchangeViewModel exchangeViewModel,
|
||||
ExchangeTemplate template) {
|
||||
void applyTemplate(
|
||||
ExchangeViewModel exchangeViewModel, ExchangeTemplate template) {
|
||||
exchangeViewModel.changeDepositCurrency(
|
||||
currency: CryptoCurrency.fromString(template.depositCurrency));
|
||||
exchangeViewModel.changeReceiveCurrency(
|
||||
|
@ -374,27 +453,29 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
|
|||
key.currentState.changeLimits(min: min, max: max);
|
||||
}
|
||||
|
||||
_onCurrencyChange(exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
|
||||
_onCurrencyChange(exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
|
||||
_onCurrencyChange(
|
||||
exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
|
||||
_onCurrencyChange(
|
||||
exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
|
||||
|
||||
reaction(
|
||||
(_) => exchangeViewModel.wallet.name,
|
||||
(String _) => _onWalletNameChange(
|
||||
(_) => exchangeViewModel.wallet.name,
|
||||
(String _) => _onWalletNameChange(
|
||||
exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
|
||||
|
||||
reaction(
|
||||
(_) => exchangeViewModel.wallet.name,
|
||||
(String _) => _onWalletNameChange(
|
||||
exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
|
||||
(_) => exchangeViewModel.wallet.name,
|
||||
(String _) => _onWalletNameChange(
|
||||
exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
|
||||
|
||||
reaction(
|
||||
(_) => exchangeViewModel.receiveCurrency,
|
||||
(CryptoCurrency currency) =>
|
||||
(_) => exchangeViewModel.receiveCurrency,
|
||||
(CryptoCurrency currency) =>
|
||||
_onCurrencyChange(currency, exchangeViewModel, receiveKey));
|
||||
|
||||
reaction(
|
||||
(_) => exchangeViewModel.depositCurrency,
|
||||
(CryptoCurrency currency) =>
|
||||
(_) => exchangeViewModel.depositCurrency,
|
||||
(CryptoCurrency currency) =>
|
||||
_onCurrencyChange(currency, exchangeViewModel, depositKey));
|
||||
|
||||
reaction((_) => exchangeViewModel.depositAmount, (String amount) {
|
||||
|
@ -409,7 +490,8 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
|
|||
}
|
||||
});
|
||||
|
||||
reaction((_) => exchangeViewModel.isDepositAddressEnabled, (bool isEnabled) {
|
||||
reaction((_) => exchangeViewModel.isDepositAddressEnabled,
|
||||
(bool isEnabled) {
|
||||
depositKey.currentState.isAddressEditable(isEditable: isEnabled);
|
||||
});
|
||||
|
||||
|
@ -425,7 +507,8 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
|
|||
}
|
||||
});
|
||||
|
||||
reaction((_) => exchangeViewModel.isReceiveAddressEnabled, (bool isEnabled) {
|
||||
reaction((_) => exchangeViewModel.isReceiveAddressEnabled,
|
||||
(bool isEnabled) {
|
||||
receiveKey.currentState.isAddressEditable(isEditable: isEnabled);
|
||||
});
|
||||
|
||||
|
@ -439,14 +522,12 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
|
|||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop()
|
||||
);
|
||||
buttonAction: () => Navigator.of(context).pop());
|
||||
});
|
||||
});
|
||||
}
|
||||
if (state is TradeIsCreatedSuccessfully) {
|
||||
Navigator.of(context)
|
||||
.pushNamed(Routes.exchangeConfirm, arguments: state.trade);
|
||||
Navigator.of(context).pushNamed(Routes.exchangeConfirm);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -474,20 +555,22 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
|
|||
});
|
||||
|
||||
depositAddressController.addListener(
|
||||
() => exchangeViewModel.depositAddress = depositAddressController.text);
|
||||
() => exchangeViewModel.depositAddress = depositAddressController.text);
|
||||
|
||||
depositAmountController.addListener(() {
|
||||
if (depositAmountController.text != exchangeViewModel.depositAmount) {
|
||||
exchangeViewModel.changeDepositAmount(amount: depositAmountController.text);
|
||||
exchangeViewModel.changeDepositAmount(
|
||||
amount: depositAmountController.text);
|
||||
}
|
||||
});
|
||||
|
||||
receiveAddressController.addListener(
|
||||
() => exchangeViewModel.receiveAddress = receiveAddressController.text);
|
||||
() => exchangeViewModel.receiveAddress = receiveAddressController.text);
|
||||
|
||||
receiveAmountController.addListener(() {
|
||||
if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
|
||||
exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text);
|
||||
exchangeViewModel.changeReceiveAmount(
|
||||
amount: receiveAmountController.text);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -505,34 +588,31 @@ class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
|
|||
}
|
||||
|
||||
void _onCurrencyChange(CryptoCurrency currency,
|
||||
ExchangeViewModel exchangeViewModel,
|
||||
GlobalKey<ExchangeCardState> key) {
|
||||
ExchangeViewModel exchangeViewModel, GlobalKey<ExchangeCardState> key) {
|
||||
final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
|
||||
|
||||
key.currentState.changeSelectedCurrency(currency);
|
||||
key.currentState
|
||||
.changeWalletName(isCurrentTypeWallet
|
||||
? exchangeViewModel.wallet.name : null);
|
||||
key.currentState.changeWalletName(
|
||||
isCurrentTypeWallet ? exchangeViewModel.wallet.name : null);
|
||||
|
||||
key.currentState
|
||||
.changeAddress(address: isCurrentTypeWallet
|
||||
? exchangeViewModel.wallet.address : '');
|
||||
key.currentState.changeAddress(
|
||||
address: isCurrentTypeWallet ? exchangeViewModel.wallet.address : '');
|
||||
|
||||
key.currentState.changeAmount(amount: '');
|
||||
}
|
||||
|
||||
void _onWalletNameChange(ExchangeViewModel exchangeViewModel,
|
||||
CryptoCurrency currency,
|
||||
GlobalKey<ExchangeCardState> key) {
|
||||
CryptoCurrency currency, GlobalKey<ExchangeCardState> key) {
|
||||
final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
|
||||
|
||||
if (isCurrentTypeWallet) {
|
||||
key.currentState.changeWalletName(exchangeViewModel.wallet.name);
|
||||
key.currentState.addressController.text = exchangeViewModel.wallet.address;
|
||||
key.currentState.addressController.text =
|
||||
exchangeViewModel.wallet.address;
|
||||
} else if (key.currentState.addressController.text ==
|
||||
exchangeViewModel.wallet.address) {
|
||||
key.currentState.changeWalletName(null);
|
||||
key.currentState.addressController.text = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import 'dart:ui';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_background.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
|
||||
|
||||
class CurrencyPicker extends StatelessWidget {
|
||||
CurrencyPicker({
|
||||
|
@ -16,104 +18,103 @@ class CurrencyPicker extends StatelessWidget {
|
|||
final List<CryptoCurrency> items;
|
||||
final String title;
|
||||
final Function(CryptoCurrency) onItemSelected;
|
||||
final closeButton = Image.asset('assets/images/close.png',
|
||||
color: Palette.darkBlueCraiola,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.none,
|
||||
color: Colors.white
|
||||
),
|
||||
return AlertBackground(
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.none,
|
||||
color: Colors.white
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: GestureDetector(
|
||||
onTap: () => null,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
child: Container(
|
||||
height: 400,
|
||||
width: 300,
|
||||
color: Theme.of(context).dividerColor,
|
||||
child: GridView.count(
|
||||
shrinkWrap: true,
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: 1.25,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisSpacing: 1,
|
||||
mainAxisSpacing: 1,
|
||||
children: List.generate(15, (index) {
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: GestureDetector(
|
||||
onTap: () => null,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
child: Container(
|
||||
height: 400,
|
||||
width: 300,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: GridView.count(
|
||||
shrinkWrap: true,
|
||||
crossAxisCount: 3,
|
||||
childAspectRatio: 1.25,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
crossAxisSpacing: 1,
|
||||
mainAxisSpacing: 1,
|
||||
children: List.generate(15, (index) {
|
||||
|
||||
if (index == 14) {
|
||||
return Container(
|
||||
color: Theme.of(context).primaryTextTheme.display1.color,
|
||||
);
|
||||
}
|
||||
if (index == 14) {
|
||||
return Container(
|
||||
color: Theme.of(context).accentTextTheme.title.color,
|
||||
);
|
||||
}
|
||||
|
||||
final item = items[index];
|
||||
final isItemSelected = index == selectedAtIndex;
|
||||
final item = items[index];
|
||||
final isItemSelected = index == selectedAtIndex;
|
||||
|
||||
final color = isItemSelected
|
||||
? Theme.of(context).accentTextTheme.subtitle.decorationColor
|
||||
: Theme.of(context).primaryTextTheme.display1.color;
|
||||
final textColor = isItemSelected
|
||||
? Colors.blue
|
||||
: Theme.of(context).primaryTextTheme.title.color;
|
||||
final color = isItemSelected
|
||||
? Theme.of(context).textTheme.body2.color
|
||||
: Theme.of(context).accentTextTheme.title.color;
|
||||
final textColor = isItemSelected
|
||||
? Palette.blueCraiola
|
||||
: Theme.of(context).primaryTextTheme.title.color;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (onItemSelected == null) {
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
onItemSelected(item);
|
||||
},
|
||||
child: Container(
|
||||
color: color,
|
||||
child: Center(
|
||||
child: Text(
|
||||
item.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.none,
|
||||
color: textColor
|
||||
),
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (onItemSelected == null) {
|
||||
return;
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
onItemSelected(item);
|
||||
},
|
||||
child: Container(
|
||||
color: color,
|
||||
child: Center(
|
||||
child: Text(
|
||||
item.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.w600,
|
||||
decoration: TextDecoration.none,
|
||||
color: textColor
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
})
|
||||
),
|
||||
),
|
||||
);
|
||||
})
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
AlertCloseButton(image: closeButton)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
|||
import 'package:cake_wallet/src/widgets/address_text_field.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/palette.dart';
|
||||
|
||||
class ExchangeCard extends StatefulWidget {
|
||||
ExchangeCard(
|
||||
|
@ -22,6 +21,7 @@ class ExchangeCard extends StatefulWidget {
|
|||
this.imageArrow,
|
||||
this.currencyButtonColor = Colors.transparent,
|
||||
this.addressButtonsColor = Colors.transparent,
|
||||
this.borderColor = Colors.transparent,
|
||||
this.currencyValueValidator,
|
||||
this.addressTextFieldValidator})
|
||||
: super(key: key);
|
||||
|
@ -38,6 +38,7 @@ class ExchangeCard extends StatefulWidget {
|
|||
final Image imageArrow;
|
||||
final Color currencyButtonColor;
|
||||
final Color addressButtonsColor;
|
||||
final Color borderColor;
|
||||
final FormFieldValidator<String> currencyValueValidator;
|
||||
final FormFieldValidator<String> addressTextFieldValidator;
|
||||
|
||||
|
@ -58,10 +59,6 @@ class ExchangeCardState extends State<ExchangeCard> {
|
|||
bool _isAddressEditable;
|
||||
bool _isAmountEstimated;
|
||||
|
||||
final copyImage = Image.asset('assets/images/copy_content.png',
|
||||
height: 16, width: 16,
|
||||
color: Colors.white);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_title = widget.title;
|
||||
|
@ -115,6 +112,10 @@ class ExchangeCardState extends State<ExchangeCard> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final copyImage = Image.asset('assets/images/copy_content.png',
|
||||
height: 16, width: 16,
|
||||
color: Theme.of(context).primaryTextTheme.display2.color);
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
color: Colors.transparent,
|
||||
|
@ -129,7 +130,7 @@ class ExchangeCardState extends State<ExchangeCard> {
|
|||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: PaletteDark.lightBlueGrey
|
||||
color: Theme.of(context).textTheme.headline.color
|
||||
),
|
||||
)
|
||||
],
|
||||
|
@ -145,11 +146,12 @@ class ExchangeCardState extends State<ExchangeCard> {
|
|||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: true),
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(15),
|
||||
BlacklistingTextInputFormatter(
|
||||
RegExp('[\\-|\\ |\\,]'))
|
||||
],
|
||||
hintText: '0.0000',
|
||||
borderColor: PaletteDark.blueGrey,
|
||||
borderColor: widget.borderColor,
|
||||
textStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
|
@ -158,7 +160,7 @@ class ExchangeCardState extends State<ExchangeCard> {
|
|||
placeholderTextStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: PaletteDark.lightBlueGrey
|
||||
color: Theme.of(context).textTheme.subhead.decorationColor
|
||||
),
|
||||
validator: _isAmountEditable
|
||||
? widget.currencyValueValidator
|
||||
|
@ -206,7 +208,7 @@ class ExchangeCardState extends State<ExchangeCard> {
|
|||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
height: 1.2,
|
||||
color: PaletteDark.lightBlueGrey),
|
||||
color: Theme.of(context).textTheme.subhead.decorationColor),
|
||||
)
|
||||
: Offstage(),
|
||||
_min != null ? SizedBox(width: 10) : Offstage(),
|
||||
|
@ -217,39 +219,45 @@ class ExchangeCardState extends State<ExchangeCard> {
|
|||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
height: 1.2,
|
||||
color: PaletteDark.lightBlueGrey))
|
||||
color: Theme.of(context).textTheme.subhead.decorationColor))
|
||||
: Offstage(),
|
||||
]),
|
||||
),
|
||||
Padding(
|
||||
_isAddressEditable
|
||||
? Offstage()
|
||||
: Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Text(
|
||||
_isAddressEditable
|
||||
? S.of(context).widgets_address
|
||||
: S.of(context).refund_address,
|
||||
S.of(context).refund_address,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: PaletteDark.lightBlueGrey
|
||||
color: Theme.of(context).textTheme.subhead.decorationColor
|
||||
),
|
||||
)
|
||||
),
|
||||
_isAddressEditable
|
||||
? AddressTextField(
|
||||
? Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: AddressTextField(
|
||||
controller: addressController,
|
||||
options: [
|
||||
AddressTextFieldOption.paste,
|
||||
AddressTextFieldOption.qrCode,
|
||||
AddressTextFieldOption.addressBook,
|
||||
],
|
||||
placeholder: '',
|
||||
isBorderExist: false,
|
||||
textStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white),
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).textTheme.subhead.decorationColor),
|
||||
buttonColor: widget.addressButtonsColor,
|
||||
validator: widget.addressTextFieldValidator,
|
||||
),
|
||||
)
|
||||
: Padding(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
|
|
|
@ -5,7 +5,6 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/picker.dart';
|
||||
import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
|
||||
class PresentProviderPicker extends StatelessWidget {
|
||||
PresentProviderPicker({@required this.exchangeViewModel});
|
||||
|
@ -41,7 +40,7 @@ class PresentProviderPicker extends StatelessWidget {
|
|||
style: TextStyle(
|
||||
fontSize: 10.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: PaletteDark.lightBlueGrey)))
|
||||
color: Theme.of(context).textTheme.headline.color)))
|
||||
],
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -6,10 +7,12 @@ 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/src/domain/exchange/trade.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
|
||||
class ExchangeConfirmPage extends BasePage {
|
||||
ExchangeConfirmPage({@required this.trade});
|
||||
ExchangeConfirmPage({@required this.tradesStore}) : trade = tradesStore.trade;
|
||||
|
||||
final TradesStore tradesStore;
|
||||
final Trade trade;
|
||||
|
||||
@override
|
||||
|
@ -17,93 +20,102 @@ class ExchangeConfirmPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
final copyImage = Image.asset('assets/images/copy_content.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color);
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(24),
|
||||
padding: EdgeInsets.fromLTRB(24, 0, 24, 24),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).exchange_result_write_down_trade_id,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 60),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Center(
|
||||
child: Text(
|
||||
S.of(context).trade_id,
|
||||
S.of(context).exchange_result_write_down_trade_id,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
),
|
||||
)
|
||||
),
|
||||
Container(
|
||||
height: 178,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Theme.of(context).accentTextTheme.caption.color
|
||||
),
|
||||
color: Theme.of(context).accentTextTheme.title.color
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: Builder(
|
||||
builder: (context) => GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: trade.id));
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
));
|
||||
},
|
||||
child: Container(
|
||||
height: 60,
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(
|
||||
trade.id,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
Text(
|
||||
S.of(context).trade_id,
|
||||
style: TextStyle(
|
||||
fontSize: 12.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).primaryTextTheme.overline.color
|
||||
),
|
||||
),
|
||||
Text(
|
||||
trade.id,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 12),
|
||||
child: copyImage,
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
|
||||
child: Builder(
|
||||
builder: (context) => PrimaryButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: trade.id));
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
));
|
||||
},
|
||||
text: S.of(context).copy_id,
|
||||
color: Theme.of(context).accentTextTheme.caption.backgroundColor,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
),
|
||||
Flexible(
|
||||
child: Offstage()
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
PrimaryButton(
|
||||
onPressed: () => Navigator.of(context)
|
||||
.pushReplacementNamed(Routes.exchangeTrade, arguments: trade),
|
||||
.pushReplacementNamed(Routes.exchangeTrade),
|
||||
text: S.of(context).saved_the_trade_id,
|
||||
color: Colors.green,
|
||||
color: Palette.blueCraiola,
|
||||
textColor: Colors.white)
|
||||
],
|
||||
),
|
||||
|
|
13
lib/src/screens/exchange_trade/exchange_trade_item.dart
Normal file
13
lib/src/screens/exchange_trade/exchange_trade_item.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
class ExchangeTradeItem {
|
||||
ExchangeTradeItem({
|
||||
@required this.title,
|
||||
@required this.data,
|
||||
@required this.isCopied,
|
||||
});
|
||||
|
||||
String title;
|
||||
String data;
|
||||
bool isCopied;
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||
import 'package:cake_wallet/view_model/exchange/exchange_trade_view_model.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
@ -19,15 +24,62 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
|||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
|
||||
void showInformation(ExchangeTradeViewModel exchangeTradeViewModel, BuildContext context) {
|
||||
final fetchingLabel = S.current.fetching;
|
||||
final trade = exchangeTradeViewModel.trade;
|
||||
final walletName = exchangeTradeViewModel.wallet.name;
|
||||
|
||||
final information = exchangeTradeViewModel.isSendable
|
||||
? S.current.exchange_result_confirm(
|
||||
trade.amount ?? fetchingLabel,
|
||||
trade.from.toString(),
|
||||
walletName)
|
||||
: S.current.exchange_result_description(
|
||||
trade.amount ?? fetchingLabel, trade.from.toString());
|
||||
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => InformationPage(information: information)
|
||||
);
|
||||
}
|
||||
|
||||
class ExchangeTradePage extends BasePage {
|
||||
ExchangeTradePage({@required this.exchangeTradeViewModel});
|
||||
|
||||
final ExchangeTradeViewModel exchangeTradeViewModel;
|
||||
|
||||
@override
|
||||
String get title => S.current.exchange;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => ExchangeTradeForm();
|
||||
Widget trailing(BuildContext context) {
|
||||
final questionImage = Image.asset('assets/images/question_mark.png',
|
||||
color: Theme.of(context).primaryTextTheme.title.color);
|
||||
|
||||
return SizedBox(
|
||||
height: 20.0,
|
||||
width: 20.0,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
padding: EdgeInsets.all(0),
|
||||
onPressed: () => showInformation(exchangeTradeViewModel, context),
|
||||
child: questionImage),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => ExchangeTradeForm(exchangeTradeViewModel);
|
||||
}
|
||||
|
||||
class ExchangeTradeForm extends StatefulWidget {
|
||||
ExchangeTradeForm(this.exchangeTradeViewModel);
|
||||
|
||||
final ExchangeTradeViewModel exchangeTradeViewModel;
|
||||
|
||||
@override
|
||||
ExchangeTradeState createState() => ExchangeTradeState();
|
||||
}
|
||||
|
@ -39,263 +91,139 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
bool _effectsInstalled = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tradeStore = Provider.of<ExchangeTradeStore>(context);
|
||||
final sendStore = Provider.of<SendStore>(context);
|
||||
final walletStore = Provider.of<WalletStore>(context);
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
|
||||
}
|
||||
|
||||
_setEffects(context);
|
||||
void afterLayout(dynamic _) {
|
||||
showInformation(widget.exchangeTradeViewModel, context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final copyImage = Image.asset('assets/images/copy_content.png',
|
||||
height: 16, width: 16,
|
||||
color: Theme.of(context).primaryTextTheme.overline.color);
|
||||
|
||||
//_setEffects(context);
|
||||
|
||||
return Container(
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(left: 24, right: 24, top: 24),
|
||||
contentPadding: EdgeInsets.only(top: 10, bottom: 16),
|
||||
content: Observer(builder: (_) {
|
||||
final trade = tradeStore.trade;
|
||||
final walletName = walletStore.name;
|
||||
final trade = widget.exchangeTradeViewModel.trade;
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).id,
|
||||
style: TextStyle(
|
||||
height: 2,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
),
|
||||
Text(
|
||||
'${trade.id ?? fetchingLabel}',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
height: 2,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).amount,
|
||||
style: TextStyle(
|
||||
height: 2,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
),
|
||||
Text(
|
||||
'${trade.amount ?? fetchingLabel}',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
height: 2,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
)
|
||||
],
|
||||
),
|
||||
trade.extraId != null
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).payment_id,
|
||||
style: TextStyle(
|
||||
height: 2,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
),
|
||||
Text(
|
||||
'${trade.extraId ?? fetchingLabel}',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
height: 2,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
)
|
||||
],
|
||||
)
|
||||
: Container(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).status,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
height: 2),
|
||||
),
|
||||
Text(
|
||||
'${trade.state ?? fetchingLabel}',
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
height: 2,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
)
|
||||
],
|
||||
),
|
||||
trade.expiredAt != null
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).offer_expires_in,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
),
|
||||
TimerWidget(trade.expiredAt,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color)
|
||||
],
|
||||
)
|
||||
: Container(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
trade.expiredAt != null
|
||||
? Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
S.of(context).offer_expires_in,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context).primaryTextTheme.overline.color),
|
||||
),
|
||||
TimerWidget(trade.expiredAt,
|
||||
color: Theme.of(context).primaryTextTheme.title.color)
|
||||
])
|
||||
: Offstage(),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 32),
|
||||
child: Row(children: <Widget>[
|
||||
Spacer(flex: 3),
|
||||
Flexible(
|
||||
flex: 4,
|
||||
child: Center(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: QrImage(
|
||||
data: trade.inputAddress ?? fetchingLabel,
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Theme.of(context)
|
||||
.accentTextTheme.subtitle.color,
|
||||
)))),
|
||||
Spacer(flex: 3)
|
||||
]),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Spacer(
|
||||
flex: 1,
|
||||
),
|
||||
Flexible(
|
||||
flex: 1,
|
||||
child: Center(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1.0,
|
||||
child: QrImage(
|
||||
data: trade.inputAddress ?? fetchingLabel,
|
||||
backgroundColor: Colors.transparent,
|
||||
foregroundColor: Theme.of(context).primaryTextTheme.display4.color,
|
||||
),
|
||||
),
|
||||
)),
|
||||
Spacer(
|
||||
flex: 1,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: 20.0,
|
||||
),
|
||||
Center(
|
||||
child: Text(
|
||||
S.of(context).trade_is_powered_by(trade.provider != null
|
||||
? trade.provider.title
|
||||
: fetchingLabel),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20, bottom: 20),
|
||||
child: Center(
|
||||
child: Text(
|
||||
trade.inputAddress ?? fetchingLabel,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: widget.exchangeTradeViewModel.items.length,
|
||||
separatorBuilder: (context, index) => Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).accentTextTheme.subtitle.backgroundColor,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
final item = widget.exchangeTradeViewModel.items[index];
|
||||
String value;
|
||||
|
||||
final content = Observer(
|
||||
builder: (_) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
value = '${widget.exchangeTradeViewModel.trade.id ?? fetchingLabel}';
|
||||
break;
|
||||
case 1:
|
||||
value = '${widget.exchangeTradeViewModel.trade.amount ?? fetchingLabel}';
|
||||
break;
|
||||
case 2:
|
||||
value = '${widget.exchangeTradeViewModel.trade.state ?? fetchingLabel}';
|
||||
break;
|
||||
case 3:
|
||||
value = widget.exchangeTradeViewModel.trade.inputAddress ?? fetchingLabel;
|
||||
break;
|
||||
}
|
||||
|
||||
return StandartListRow(
|
||||
title: item.title,
|
||||
value: value,
|
||||
valueFontSize: 14,
|
||||
image: item.isCopied ? copyImage : null,
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return item.isCopied
|
||||
? Builder(
|
||||
builder: (context) =>
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Clipboard.setData(ClipboardData(text: value));
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
));
|
||||
},
|
||||
child: content,
|
||||
)
|
||||
)
|
||||
: content;
|
||||
},
|
||||
),
|
||||
),
|
||||
Container(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(right: 5.0),
|
||||
child: Builder(
|
||||
builder: (context) => PrimaryButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: trade.inputAddress));
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
));
|
||||
},
|
||||
text: S.of(context).copy_address,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color)
|
||||
),
|
||||
)),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 5.0),
|
||||
child: Builder(
|
||||
builder: (context) => PrimaryButton(
|
||||
onPressed: () {
|
||||
Clipboard.setData(ClipboardData(text: trade.id));
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
S.of(context).copied_to_clipboard,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
backgroundColor: Colors.green,
|
||||
duration: Duration(milliseconds: 1500),
|
||||
));
|
||||
},
|
||||
text: S.of(context).copy_id,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color)
|
||||
),
|
||||
))
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Text(
|
||||
tradeStore.isSendable
|
||||
? S.of(context).exchange_result_confirm(
|
||||
trade.amount ?? fetchingLabel,
|
||||
trade.from.toString(),
|
||||
walletName)
|
||||
: S.of(context).exchange_result_description(
|
||||
trade.amount ?? fetchingLabel, trade.from.toString()),
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
fontSize: 13.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
S.of(context).exchange_result_write_down_ID,
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
fontSize: 13.0,
|
||||
color: Theme.of(context).primaryTextTheme.title.color),
|
||||
)
|
||||
],
|
||||
);
|
||||
}),
|
||||
bottomSectionPadding: EdgeInsets.all(24),
|
||||
bottomSection: Observer(
|
||||
bottomSectionPadding: EdgeInsets.fromLTRB(24, 0, 24, 24),
|
||||
bottomSection: PrimaryButton(
|
||||
onPressed: () {},
|
||||
text: S.of(context).confirm,
|
||||
color: Palette.blueCraiola,
|
||||
textColor: Colors.white
|
||||
)
|
||||
/*Observer(
|
||||
builder: (_) => tradeStore.trade.from == CryptoCurrency.xmr &&
|
||||
!(sendStore.state is TransactionCommitted)
|
||||
? LoadingPrimaryButton(
|
||||
|
@ -312,7 +240,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
: S.of(context).send_xmr,
|
||||
color: Colors.blue,
|
||||
textColor: Colors.white)
|
||||
: Offstage()),
|
||||
: Offstage()),*/
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -322,7 +250,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
return;
|
||||
}
|
||||
|
||||
final sendStore = Provider.of<SendStore>(context);
|
||||
/*final sendStore = Provider.of<SendStore>(context);
|
||||
|
||||
reaction((_) => sendStore.state, (SendingState state) {
|
||||
if (state is SendingFailed) {
|
||||
|
@ -376,7 +304,7 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
|||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});*/
|
||||
|
||||
_effectsInstalled = true;
|
||||
}
|
||||
|
|
57
lib/src/screens/exchange_trade/information_page.dart
Normal file
57
lib/src/screens/exchange_trade/information_page.dart
Normal file
|
@ -0,0 +1,57 @@
|
|||
import 'dart:ui';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_background.dart';
|
||||
|
||||
class InformationPage extends StatelessWidget {
|
||||
InformationPage({@required this.information});
|
||||
|
||||
final String information;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertBackground(
|
||||
child: Center(
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(
|
||||
left: 24,
|
||||
right: 24
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
color: Theme.of(context).textTheme.body2.decorationColor
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.fromLTRB(24, 28, 24, 24),
|
||||
child: Text(
|
||||
information,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
fontFamily: 'Poppins',
|
||||
decoration: TextDecoration.none,
|
||||
color: Theme.of(context).accentTextTheme.caption.decorationColor
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
|
||||
child: PrimaryButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
text: S.of(context).send_got_it,
|
||||
color: Theme.of(context).accentTextTheme.caption.backgroundColor,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -53,10 +53,16 @@ class TimerWidgetState extends State<TimerWidget> {
|
|||
Widget build(BuildContext context) {
|
||||
return _isExpired
|
||||
? Text(S.of(context).expired,
|
||||
style: TextStyle(fontSize: 14.0, color: Colors.red))
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.red))
|
||||
: Text(
|
||||
S.of(context).time(_minutes.toString(), _seconds.toString()),
|
||||
style: TextStyle(fontSize: 14.0, color: widget.color),
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: widget.color),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,9 @@ class MoneroAccountListPage extends StatelessWidget {
|
|||
}
|
||||
|
||||
final MoneroAccountListViewModel accountListViewModel;
|
||||
final closeIcon = Image.asset('assets/images/close.png');
|
||||
final closeIcon = Image.asset('assets/images/close.png',
|
||||
color: Palette.darkBlueCraiola,
|
||||
);
|
||||
|
||||
ScrollController controller;
|
||||
double backgroundHeight;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
|
@ -6,6 +7,7 @@ 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/widgets/base_text_form_field.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';
|
||||
|
@ -77,32 +79,11 @@ class NodeCreateOrEditPage extends BasePage {
|
|||
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))),
|
||||
child: BaseTextFormField(
|
||||
controller: _addressController,
|
||||
hintText: S.of(context).node_address,
|
||||
validator: NodeAddressValidator(),
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -110,34 +91,13 @@ class NodeCreateOrEditPage extends BasePage {
|
|||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color),
|
||||
child: BaseTextFormField(
|
||||
controller: _portController,
|
||||
hintText: S.of(context).node_port,
|
||||
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(),
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -146,32 +106,10 @@ class NodeCreateOrEditPage extends BasePage {
|
|||
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))),
|
||||
child: BaseTextFormField(
|
||||
controller: _loginController,
|
||||
validator: (value) => null,
|
||||
),
|
||||
hintText: S.of(context).login,
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -179,32 +117,10 @@ class NodeCreateOrEditPage extends BasePage {
|
|||
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))),
|
||||
child: BaseTextFormField(
|
||||
controller: _passwordController,
|
||||
validator: (value) => null,
|
||||
),
|
||||
hintText: S.of(context).password,
|
||||
)
|
||||
)
|
||||
],
|
||||
)
|
||||
|
@ -237,7 +153,7 @@ class NodeCreateOrEditPage extends BasePage {
|
|||
Navigator.of(context).pop();
|
||||
},
|
||||
text: S.of(context).save,
|
||||
color: Colors.green,
|
||||
color: Palette.blueCraiola,
|
||||
textColor: Colors.white,
|
||||
isDisabled: !nodeCreateOrEditViewModel.isReady,
|
||||
),
|
||||
|
|
|
@ -24,7 +24,7 @@ class NodeListPage extends BasePage {
|
|||
height: 32,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor),
|
||||
color: Theme.of(context).accentTextTheme.caption.color),
|
||||
child: ButtonTheme(
|
||||
minWidth: double.minPositive,
|
||||
child: FlatButton(
|
||||
|
@ -51,7 +51,7 @@ class NodeListPage extends BasePage {
|
|||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blue),
|
||||
color: Palette.blueCraiola),
|
||||
)),
|
||||
),
|
||||
);
|
||||
|
@ -74,15 +74,41 @@ class NodeListPage extends BasePage {
|
|||
}
|
||||
|
||||
final node = nodeListViewModel.nodes[index];
|
||||
final isSelected = index == 1; // FIXME: hardcoded value.
|
||||
final nodeListRow = NodeListRow(
|
||||
title: node.uri,
|
||||
isSelected: isSelected,
|
||||
isAlive: node.requestNode(),
|
||||
onTap: (_) {});
|
||||
title: node.value.uri,
|
||||
isSelected: node.isSelected,
|
||||
isAlive: node.value.requestNode(),
|
||||
onTap: (_) async {
|
||||
if (node.isSelected) {
|
||||
return;
|
||||
}
|
||||
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
content: Text(
|
||||
S.of(context).change_current_node(node.value.uri),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(S.of(context).cancel)),
|
||||
FlatButton(
|
||||
onPressed: () async {
|
||||
Navigator.of(context).pop();
|
||||
await nodeListViewModel
|
||||
.setAsCurrent(node.value);
|
||||
},
|
||||
child: Text(S.of(context).change)),
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
final dismissibleRow = Dismissible(
|
||||
key: Key('${node.key}'),
|
||||
key: Key('${node.value.key}'),
|
||||
confirmDismiss: (direction) async {
|
||||
return await showDialog(
|
||||
context: context,
|
||||
|
@ -99,7 +125,7 @@ class NodeListPage extends BasePage {
|
|||
});
|
||||
},
|
||||
onDismissed: (direction) async =>
|
||||
nodeListViewModel.delete(node),
|
||||
nodeListViewModel.delete(node.value),
|
||||
direction: DismissDirection.endToStart,
|
||||
background: Container(
|
||||
padding: EdgeInsets.only(right: 10.0),
|
||||
|
@ -120,7 +146,7 @@ class NodeListPage extends BasePage {
|
|||
)),
|
||||
child: nodeListRow);
|
||||
|
||||
return isSelected ? nodeListRow : dismissibleRow;
|
||||
return node.isSelected ? nodeListRow : dismissibleRow;
|
||||
},
|
||||
itemCounter: (int sectionIndex) {
|
||||
if (sectionIndex == 0) {
|
||||
|
|
|
@ -23,9 +23,9 @@ class NodeListRow extends StandardListRow {
|
|||
builder: (context, snapshot) {
|
||||
switch (snapshot.connectionState) {
|
||||
case ConnectionState.done:
|
||||
return NodeIndicator(isLive: snapshot.data as bool);
|
||||
return NodeIndicator(isLive: (snapshot.data as bool)??false);
|
||||
default:
|
||||
return NodeIndicator();
|
||||
return NodeIndicator(isLive: false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -38,6 +38,6 @@ class NodeHeaderListRow extends StandardListRow {
|
|||
@override
|
||||
Widget buildTrailing(BuildContext context) {
|
||||
return Icon(Icons.add,
|
||||
color: Theme.of(context).primaryTextTheme.title.color, size: 24.0);
|
||||
color: Theme.of(context).accentTextTheme.subhead.color, size: 24.0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,28 +1,643 @@
|
|||
import 'package:cake_wallet/view_model/send_view_model.dart';
|
||||
import 'dart:ui';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/top_panel.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
|
||||
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/base_send_widget.dart';
|
||||
|
||||
// FIXME: Refactor this screen.
|
||||
|
||||
class SendPage extends BasePage {
|
||||
SendPage({@required this.sendViewModel});
|
||||
|
||||
final SendViewModel sendViewModel;
|
||||
|
||||
// ???
|
||||
@override
|
||||
String get title => sendViewModel.pageTitle;
|
||||
String get title => 'SEND';
|
||||
|
||||
@override
|
||||
Color get backgroundLightColor => PaletteDark.nightBlue;
|
||||
Color get titleColor => Colors.white;
|
||||
|
||||
@override
|
||||
Color get backgroundDarkColor => PaletteDark.nightBlue;
|
||||
Color get backgroundLightColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
bool get resizeToAvoidBottomPadding => false;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) =>
|
||||
BaseSendWidget(sendViewModel: sendViewModel);
|
||||
Widget trailing(context) => TrailButton(
|
||||
caption: S.of(context).clear, onPressed: () => sendViewModel.reset());
|
||||
Color get backgroundDarkColor => Colors.transparent;
|
||||
|
||||
// @override
|
||||
// State<StatefulWidget> createState() => SendFormState();
|
||||
|
||||
final _addressController = TextEditingController();
|
||||
final _cryptoAmountController = TextEditingController();
|
||||
final _fiatAmountController = TextEditingController();
|
||||
|
||||
final _focusNode = FocusNode();
|
||||
|
||||
bool _effectsInstalled = false;
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
// @override
|
||||
// void initState() {
|
||||
// _focusNode.addListener(() {
|
||||
// if (!_focusNode.hasFocus && _addressController.text.isNotEmpty) {
|
||||
// getOpenaliasRecord(context);
|
||||
// }
|
||||
// });
|
||||
|
||||
// super.initState();
|
||||
// }
|
||||
|
||||
Future<void> getOpenaliasRecord(BuildContext context) async {
|
||||
// final sendStore = Provider.of<SendStore>(context);
|
||||
// final isOpenalias =
|
||||
// await sendStore.isOpenaliasRecord(_addressController.text);
|
||||
//
|
||||
// if (isOpenalias) {
|
||||
// _addressController.text = sendStore.recordAddress;
|
||||
//
|
||||
// await showDialog<void>(
|
||||
// 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());
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
// @override
|
||||
// Widget body(BuildContext context) {
|
||||
// return super.build(context);
|
||||
// }
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) => BaseSendWidget(
|
||||
sendViewModel: sendViewModel,
|
||||
leading: leading(context),
|
||||
middle: middle(context),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
|
||||
body: Container(
|
||||
color: Theme.of(context).backgroundColor, child: body(context)));
|
||||
}
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// _setEffects(context);
|
||||
|
||||
// return Container(
|
||||
// color: Theme.of(context).backgroundColor,
|
||||
// child: ScrollableWithBottomSection(
|
||||
// contentPadding: EdgeInsets.only(bottom: 24),
|
||||
// content: Column(
|
||||
// children: <Widget>[
|
||||
// TopPanel(
|
||||
// color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
// widget: Form(
|
||||
// key: _formKey,
|
||||
// child: Column(children: <Widget>[
|
||||
// AddressTextField(
|
||||
// controller: _addressController,
|
||||
// placeholder: 'Address',
|
||||
// //S.of(context).send_monero_address, FIXME: placeholder for btc and xmr address text field.
|
||||
// focusNode: _focusNode,
|
||||
// onURIScanned: (uri) {
|
||||
// var address = '';
|
||||
// var amount = '';
|
||||
|
||||
// if (uri != null) {
|
||||
// address = uri.path;
|
||||
// amount = uri.queryParameters['tx_amount'];
|
||||
// } else {
|
||||
// address = uri.toString();
|
||||
// }
|
||||
|
||||
// _addressController.text = address;
|
||||
// _cryptoAmountController.text = amount;
|
||||
// },
|
||||
// options: [
|
||||
// AddressTextFieldOption.qrCode,
|
||||
// AddressTextFieldOption.addressBook
|
||||
// ],
|
||||
// buttonColor: Theme.of(context).accentTextTheme.title.color,
|
||||
// validator: widget.sendViewModel.addressValidator,
|
||||
// ),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(top: 20),
|
||||
// child: TextFormField(
|
||||
// onChanged: (value) =>
|
||||
// widget.sendViewModel.setCryptoAmount(value),
|
||||
// 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(
|
||||
// '${widget.sendViewModel.currency.toString()}:',
|
||||
// style: TextStyle(
|
||||
// fontSize: 16,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .title
|
||||
// .color,
|
||||
// )),
|
||||
// ),
|
||||
// suffixIcon: Padding(
|
||||
// padding: EdgeInsets.only(bottom: 5),
|
||||
// child: 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: () => widget.sendViewModel.setAll(),
|
||||
// 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: (String value) {
|
||||
// if (widget.sendViewModel.all) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// return widget.sendViewModel.amountValidator
|
||||
// .call(value);
|
||||
// }),
|
||||
// ),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(top: 20),
|
||||
// child: TextFormField(
|
||||
// onChanged: (value) =>
|
||||
// widget.sendViewModel.setFiatAmount(value),
|
||||
// style: TextStyle(
|
||||
// fontSize: 16.0,
|
||||
// color:
|
||||
// Theme.of(context).primaryTextTheme.title.color),
|
||||
// controller: _fiatAmountController,
|
||||
// keyboardType: TextInputType.numberWithOptions(
|
||||
// signed: false, decimal: true),
|
||||
// // inputFormatters: [
|
||||
// // BlacklistingTextInputFormatter(
|
||||
// // RegExp('[\\-|\\ |\\,]'))
|
||||
// // ],
|
||||
// decoration: InputDecoration(
|
||||
// prefixIcon: Padding(
|
||||
// padding: EdgeInsets.only(top: 12),
|
||||
// child: Text(
|
||||
// '${widget.sendViewModel.fiat.toString()}:',
|
||||
// style: TextStyle(
|
||||
// fontSize: 16,
|
||||
// fontWeight: FontWeight.w500,
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .title
|
||||
// .color,
|
||||
// )),
|
||||
// ),
|
||||
// hintStyle: TextStyle(
|
||||
// fontSize: 16.0,
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .caption
|
||||
// .color),
|
||||
// hintText: '0.00',
|
||||
// focusedBorder: UnderlineInputBorder(
|
||||
// borderSide: BorderSide(
|
||||
// color: Theme.of(context).dividerColor,
|
||||
// width: 1.0)),
|
||||
// enabledBorder: UnderlineInputBorder(
|
||||
// borderSide: BorderSide(
|
||||
// color: Theme.of(context).dividerColor,
|
||||
// width: 1.0)))),
|
||||
// ),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(top: 20),
|
||||
// child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: <Widget>[
|
||||
// Text(S.of(context).send_estimated_fee,
|
||||
// style: TextStyle(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .title
|
||||
// .color,
|
||||
// )),
|
||||
// Text(
|
||||
// '${widget.sendViewModel.estimatedFee} ${widget.sendViewModel.currency.toString()}',
|
||||
// style: TextStyle(
|
||||
// fontSize: 12,
|
||||
// fontWeight: FontWeight.w600,
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .title
|
||||
// .color,
|
||||
// ))
|
||||
// ],
|
||||
// ),
|
||||
// )
|
||||
// ]),
|
||||
// ),
|
||||
// ),
|
||||
// // 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: () async {
|
||||
// FocusScope.of(context).requestFocus(FocusNode());
|
||||
|
||||
// if (!_formKey.currentState.validate()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 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();
|
||||
// widget.sendViewModel.createTransaction();
|
||||
// });
|
||||
// },
|
||||
// actionRightButton: () => Navigator.of(context).pop());
|
||||
// });
|
||||
// },
|
||||
// text: S.of(context).send,
|
||||
// color: Colors.blue,
|
||||
// textColor: Colors.white,
|
||||
// isLoading: widget.sendViewModel.state is TransactionIsCreating ||
|
||||
// widget.sendViewModel.state is TransactionCommitting,
|
||||
// isDisabled: !widget.sendViewModel.isReadyForSend);
|
||||
// }),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
// void _setEffects(BuildContext context) {
|
||||
// if (_effectsInstalled) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// reaction((_) => widget.sendViewModel.all, (bool all) {
|
||||
// if (all) {
|
||||
// _cryptoAmountController.text = S.current.all;
|
||||
// _fiatAmountController.text = null;
|
||||
// }
|
||||
// });
|
||||
|
||||
// reaction((_) => widget.sendViewModel.fiatAmount, (String amount) {
|
||||
// if (amount != _fiatAmountController.text) {
|
||||
// _fiatAmountController.text = amount;
|
||||
// }
|
||||
// });
|
||||
|
||||
// reaction((_) => widget.sendViewModel.cryptoAmount, (String amount) {
|
||||
// if (widget.sendViewModel.all && amount != S.current.all) {
|
||||
// widget.sendViewModel.all = false;
|
||||
// }
|
||||
|
||||
// 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.address = address;
|
||||
// }
|
||||
// });
|
||||
|
||||
// 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());
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// 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:
|
||||
// widget.sendViewModel.pendingTransaction.amountFormatted,
|
||||
// fee: S.of(context).send_fee,
|
||||
// feeValue:
|
||||
// widget.sendViewModel.pendingTransaction.feeFormatted,
|
||||
// leftButtonText: S.of(context).ok,
|
||||
// rightButtonText: S.of(context).cancel,
|
||||
// actionLeftButton: () {
|
||||
// Navigator.of(context).pop();
|
||||
// widget.sendViewModel.commitTransaction();
|
||||
// showDialog<void>(
|
||||
// context: context,
|
||||
// builder: (BuildContext context) {
|
||||
// return Observer(builder: (_) {
|
||||
// final state = widget.sendViewModel.state;
|
||||
|
||||
// if (state is TransactionCommitted) {
|
||||
// return Stack(
|
||||
// children: <Widget>[
|
||||
// Container(
|
||||
// color: Theme.of(context).backgroundColor,
|
||||
// child: Center(
|
||||
// child: Image.asset(
|
||||
// 'assets/images/birthday_cake.png'),
|
||||
// ),
|
||||
// ),
|
||||
// Center(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(
|
||||
// top: 220, left: 24, right: 24),
|
||||
// child: Text(
|
||||
// S.of(context).send_success,
|
||||
// textAlign: TextAlign.center,
|
||||
// style: TextStyle(
|
||||
// fontSize: 22,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .title
|
||||
// .color,
|
||||
// decoration: TextDecoration.none,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// Positioned(
|
||||
// left: 24,
|
||||
// right: 24,
|
||||
// bottom: 24,
|
||||
// child: PrimaryButton(
|
||||
// onPressed: () =>
|
||||
// Navigator.of(context).pop(),
|
||||
// text: S.of(context).send_got_it,
|
||||
// color: Colors.blue,
|
||||
// textColor: Colors.white))
|
||||
// ],
|
||||
// );
|
||||
// }
|
||||
|
||||
// return Stack(
|
||||
// children: <Widget>[
|
||||
// Container(
|
||||
// color: Theme.of(context).backgroundColor,
|
||||
// child: Center(
|
||||
// child: Image.asset(
|
||||
// 'assets/images/birthday_cake.png'),
|
||||
// ),
|
||||
// ),
|
||||
// BackdropFilter(
|
||||
// filter: ImageFilter.blur(
|
||||
// sigmaX: 3.0, sigmaY: 3.0),
|
||||
// child: Container(
|
||||
// decoration: BoxDecoration(
|
||||
// color: Theme.of(context)
|
||||
// .backgroundColor
|
||||
// .withOpacity(0.25)),
|
||||
// child: Center(
|
||||
// child: Padding(
|
||||
// padding: EdgeInsets.only(top: 220),
|
||||
// child: Text(
|
||||
// S.of(context).send_sending,
|
||||
// textAlign: TextAlign.center,
|
||||
// style: TextStyle(
|
||||
// fontSize: 22,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Theme.of(context)
|
||||
// .primaryTextTheme
|
||||
// .title
|
||||
// .color,
|
||||
// decoration: TextDecoration.none,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// ],
|
||||
// );
|
||||
// });
|
||||
// });
|
||||
// },
|
||||
// actionRightButton: () => Navigator.of(context).pop());
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// if (state is TransactionCommitted) {
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// _addressController.text = '';
|
||||
// _cryptoAmountController.text = '';
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
// _effectsInstalled = true;
|
||||
// }
|
||||
|
||||
// Widget body(BuildContext context) => BaseSendWidget(
|
||||
// sendViewModel: sendViewModel,
|
||||
// leading: leading(context),
|
||||
// middle: middle(context),
|
||||
// );
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// return Scaffold(
|
||||
// resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
|
||||
// body: Container(
|
||||
// color: Theme.of(context).backgroundColor, child: body(context)));
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/view_model/send_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/send/send_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/send/widgets/base_send_widget.dart';
|
||||
|
||||
class SendTemplatePage extends BasePage {
|
||||
|
@ -15,15 +14,29 @@ class SendTemplatePage extends BasePage {
|
|||
String get title => S.current.exchange_new_template;
|
||||
|
||||
@override
|
||||
Color get backgroundLightColor => PaletteDark.nightBlue;
|
||||
Color get titleColor => Colors.white;
|
||||
|
||||
@override
|
||||
Color get backgroundDarkColor => PaletteDark.nightBlue;
|
||||
Color get backgroundLightColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
Color get backgroundDarkColor => Colors.transparent;
|
||||
|
||||
@override
|
||||
bool get resizeToAvoidBottomPadding => false;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) =>
|
||||
BaseSendWidget(sendViewModel: sendViewModel, isTemplate: true);
|
||||
}
|
||||
Widget body(BuildContext context) => BaseSendWidget(
|
||||
sendViewModel: sendViewModel,
|
||||
leading: leading(context),
|
||||
middle: middle(context),
|
||||
isTemplate: true);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
|
||||
body: Container(
|
||||
color: Theme.of(context).backgroundColor, child: body(context)));
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
|||
import 'dart:ui';
|
||||
import 'package:cake_wallet/src/stores/send/sending_state.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/stores/send/sending_state.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/stores/send/send_store.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
|
|
@ -39,9 +39,11 @@ class SettingsPage extends BasePage {
|
|||
if (item is PickerListItem) {
|
||||
return Observer(builder: (_) {
|
||||
return SettingsPickerCell<dynamic>(
|
||||
title: item.title,
|
||||
selectedItem: item.selectedItem(),
|
||||
items: item.items);
|
||||
title: item.title,
|
||||
selectedItem: item.selectedItem(),
|
||||
items: item.items,
|
||||
onItemSelected: (dynamic value) => item.onItemSelected(value),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,25 +4,30 @@ 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})
|
||||
SettingsPickerCell(
|
||||
{@required String title,
|
||||
this.selectedItem,
|
||||
this.items,
|
||||
this.onItemSelected})
|
||||
: super(
|
||||
title: title,
|
||||
isSelected: false,
|
||||
onTap: (BuildContext context) async {
|
||||
final selectedAtIndex = items.indexOf(selectedItem);
|
||||
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 _) {}));
|
||||
});
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => Picker(
|
||||
items: items,
|
||||
selectedAtIndex: selectedAtIndex,
|
||||
title: S.current.please_select,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
onItemSelected: (ItemType item) => onItemSelected?.call(item)));
|
||||
});
|
||||
|
||||
final ItemType selectedItem;
|
||||
final List<ItemType> items;
|
||||
final void Function(ItemType item) onItemSelected;
|
||||
|
||||
@override
|
||||
Widget buildTrailing(BuildContext context) {
|
||||
|
@ -35,4 +40,4 @@ class SettingsPickerCell<ItemType> extends StandardListRow {
|
|||
color: Theme.of(context).primaryTextTheme.caption.color),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
@ -48,6 +49,9 @@ class TransactionDetailsPage extends BasePage {
|
|||
StandartListItem(
|
||||
title: S.current.transaction_details_date,
|
||||
value: dateFormat.format(tx.date)),
|
||||
StandartListItem(
|
||||
title: 'Confirmations',
|
||||
value: tx.confirmations?.toString()),
|
||||
StandartListItem(
|
||||
title: S.current.transaction_details_height, value: '${tx.height}'),
|
||||
StandartListItem(
|
||||
|
|
|
@ -37,11 +37,14 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
final bitcoinIcon =
|
||||
Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
||||
final scrollController = ScrollController();
|
||||
final double tileHeight = 60;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final newWalletImage = Image.asset('assets/images/new_wallet.png',
|
||||
height: 12, width: 12, color: Palette.oceanBlue);
|
||||
height: 12,
|
||||
width: 12,
|
||||
color: Theme.of(context).accentTextTheme.headline.decorationColor);
|
||||
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
|
||||
height: 12,
|
||||
width: 12,
|
||||
|
@ -58,19 +61,11 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
separatorBuilder: (_, index) => Divider(
|
||||
color: Theme.of(context).backgroundColor, height: 16),
|
||||
color: Theme.of(context).backgroundColor, height: 32),
|
||||
itemCount: widget.walletListViewModel.wallets.length,
|
||||
itemBuilder: (__, index) {
|
||||
final wallet = widget.walletListViewModel.wallets[index];
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
// String shortAddress = '';
|
||||
|
||||
// if (wallet.isCurrent) {
|
||||
// shortAddress = wallet.address;
|
||||
// shortAddress = shortAddress.replaceRange(
|
||||
// 4, shortAddress.length - 4, '...');
|
||||
// }
|
||||
|
||||
final walletMenu = WalletMenu(context, widget.walletListViewModel);
|
||||
final items =
|
||||
walletMenu.generateItemsForWalletMenu(wallet.isCurrent);
|
||||
|
@ -80,7 +75,7 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
.generateImagesForWalletMenu(wallet.isCurrent);
|
||||
|
||||
return Container(
|
||||
height: 108,
|
||||
height: tileHeight,
|
||||
width: double.infinity,
|
||||
child: CustomScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
|
@ -90,7 +85,7 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
pinned: false,
|
||||
floating: true,
|
||||
delegate: WalletTile(
|
||||
min: screenWidth - 228,
|
||||
min: screenWidth - 170,
|
||||
max: screenWidth,
|
||||
image: _imageFor(type: wallet.type),
|
||||
walletName: wallet.name,
|
||||
|
@ -101,10 +96,11 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
delegate:
|
||||
SliverChildBuilderDelegate((context, index) {
|
||||
final item = items[index];
|
||||
final color = colors[index];
|
||||
final image = images[index];
|
||||
final firstColor = colors[index*2];
|
||||
final secondColor = colors[index*2 + 1];
|
||||
|
||||
final radius = index == 0 ? 12.0 : 0.0;
|
||||
final radius = index == 0 ? 10.0 : 0.0;
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
|
@ -117,8 +113,8 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
wallet.isCurrent);
|
||||
},
|
||||
child: Container(
|
||||
height: 108,
|
||||
width: 108,
|
||||
height: tileHeight,
|
||||
width: 80,
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 5, right: 5),
|
||||
|
@ -126,18 +122,27 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(radius),
|
||||
bottomLeft: Radius.circular(radius)),
|
||||
color: color),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
firstColor,
|
||||
secondColor
|
||||
]
|
||||
)
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(height: 5),
|
||||
SizedBox(height: 2),
|
||||
Text(
|
||||
item,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontSize: 7,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.white),
|
||||
)
|
||||
],
|
||||
|
@ -159,9 +164,8 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
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,
|
||||
color: Theme.of(context).accentTextTheme.subtitle.decorationColor,
|
||||
textColor: Theme.of(context).accentTextTheme.headline.decorationColor,
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
PrimaryImageButton(
|
||||
|
@ -169,7 +173,7 @@ class WalletListBodyState extends State<WalletListBody> {
|
|||
.pushNamed(Routes.restoreWalletType),
|
||||
image: restoreWalletImage,
|
||||
text: S.of(context).wallet_list_restore_wallet,
|
||||
color: Theme.of(context).primaryTextTheme.overline.color,
|
||||
color: Theme.of(context).accentTextTheme.caption.color,
|
||||
textColor: Theme.of(context).primaryTextTheme.title.color)
|
||||
])),
|
||||
));
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:cake_wallet/generated/i18n.dart';
|
|||
import 'package:cake_wallet/src/stores/wallet_list/wallet_list_store.dart';
|
||||
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
|
||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
|
||||
class WalletMenu {
|
||||
WalletMenu(this.context, this.walletListViewModel);
|
||||
|
@ -20,18 +21,25 @@ class WalletMenu {
|
|||
S.current.rescan
|
||||
];
|
||||
|
||||
final List<Color> listColors = [
|
||||
Colors.blue,
|
||||
Colors.orange,
|
||||
Colors.red,
|
||||
Colors.green
|
||||
final List<Color> firstColors = [
|
||||
Palette.cornflower,
|
||||
Palette.moderateOrangeYellow,
|
||||
Palette.lightRed,
|
||||
Palette.shineGreen
|
||||
];
|
||||
|
||||
final List<Color> secondColors = [
|
||||
Palette.royalBlue,
|
||||
Palette.moderateOrange,
|
||||
Palette.persianRed,
|
||||
Palette.moderateGreen
|
||||
];
|
||||
|
||||
final List<Image> listImages = [
|
||||
Image.asset('assets/images/load.png', height: 32, width: 32, color: Colors.white),
|
||||
Image.asset('assets/images/eye_action.png', height: 32, width: 32, color: Colors.white),
|
||||
Image.asset('assets/images/trash.png', height: 32, width: 32, color: Colors.white),
|
||||
Image.asset('assets/images/scanner.png', height: 32, width: 32, color: Colors.white)
|
||||
Image.asset('assets/images/load.png', height: 24, width: 24, color: Colors.white),
|
||||
Image.asset('assets/images/eye_action.png', height: 24, width: 24, color: Colors.white),
|
||||
Image.asset('assets/images/trash.png', height: 24, width: 24, color: Colors.white),
|
||||
Image.asset('assets/images/scanner.png', height: 24, width: 24, color: Colors.white)
|
||||
];
|
||||
|
||||
List<String> generateItemsForWalletMenu(bool isCurrentWallet) {
|
||||
|
@ -46,18 +54,30 @@ class WalletMenu {
|
|||
}
|
||||
|
||||
List<Color> generateColorsForWalletMenu(bool isCurrentWallet) {
|
||||
final colors = List<Color>();
|
||||
final colors = <Color>[];
|
||||
|
||||
if (!isCurrentWallet) colors.add(listColors[0]);
|
||||
if (isCurrentWallet) colors.add(listColors[1]);
|
||||
if (!isCurrentWallet) colors.add(listColors[2]);
|
||||
if (isCurrentWallet) colors.add(listColors[3]);
|
||||
if (!isCurrentWallet) {
|
||||
colors.add(firstColors[0]);
|
||||
colors.add(secondColors[0]);
|
||||
}
|
||||
if (isCurrentWallet) {
|
||||
colors.add(firstColors[1]);
|
||||
colors.add(secondColors[1]);
|
||||
}
|
||||
if (!isCurrentWallet) {
|
||||
colors.add(firstColors[2]);
|
||||
colors.add(secondColors[2]);
|
||||
}
|
||||
if (isCurrentWallet) {
|
||||
colors.add(firstColors[3]);
|
||||
colors.add(secondColors[3]);
|
||||
}
|
||||
|
||||
return colors;
|
||||
}
|
||||
|
||||
List<Image> generateImagesForWalletMenu(bool isCurrentWallet) {
|
||||
final images = List<Image>();
|
||||
final images = <Image>[];
|
||||
|
||||
if (!isCurrentWallet) images.add(listImages[0]);
|
||||
if (isCurrentWallet) images.add(listImages[1]);
|
||||
|
@ -109,7 +129,7 @@ class WalletMenu {
|
|||
try {
|
||||
auth.changeProcessText(
|
||||
S.of(context).wallet_list_removing_wallet(wallet.name));
|
||||
// await _walletListStore.remove(wallet);
|
||||
await walletListViewModel.remove(wallet);
|
||||
auth.close();
|
||||
} catch (e) {
|
||||
auth.changeProcessText(S
|
||||
|
|
|
@ -17,17 +17,18 @@ class WalletTile extends SliverPersistentHeaderDelegate {
|
|||
final String walletName;
|
||||
final String walletAddress;
|
||||
final bool isCurrent;
|
||||
final double tileHeight = 60;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
|
||||
var opacity = 1 - shrinkOffset / (max - min);
|
||||
opacity = opacity >= 0 ? opacity : 0;
|
||||
|
||||
var panelWidth = 12 * opacity;
|
||||
panelWidth = panelWidth < 12 ? 0 : 12;
|
||||
var panelWidth = 10 * opacity;
|
||||
panelWidth = panelWidth < 10 ? 0 : 10;
|
||||
|
||||
final currentColor = isCurrent
|
||||
? Theme.of(context).accentTextTheme.caption.color
|
||||
? Theme.of(context).accentTextTheme.subtitle.decorationColor
|
||||
: Theme.of(context).backgroundColor;
|
||||
|
||||
return Stack(
|
||||
|
@ -38,7 +39,7 @@ class WalletTile extends SliverPersistentHeaderDelegate {
|
|||
top: 0,
|
||||
right: max - 4,
|
||||
child: Container(
|
||||
height: 108,
|
||||
height: tileHeight,
|
||||
width: 4,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(topRight: Radius.circular(4), bottomRight: Radius.circular(4)),
|
||||
|
@ -48,13 +49,30 @@ class WalletTile extends SliverPersistentHeaderDelegate {
|
|||
),
|
||||
Positioned(
|
||||
top: 0,
|
||||
right: 12,
|
||||
right: 10,
|
||||
child: Container(
|
||||
height: 108,
|
||||
width: max - 16,
|
||||
height: tileHeight,
|
||||
width: max - 14,
|
||||
padding: EdgeInsets.only(left: 20, right: 20),
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Column(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
//mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image,
|
||||
SizedBox(width: 10),
|
||||
Text(
|
||||
walletName,
|
||||
style: TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
/*Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
|
@ -92,7 +110,7 @@ class WalletTile extends SliverPersistentHeaderDelegate {
|
|||
)
|
||||
: Offstage()
|
||||
],
|
||||
),
|
||||
),*/
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
|
@ -101,29 +119,18 @@ class WalletTile extends SliverPersistentHeaderDelegate {
|
|||
child: Opacity(
|
||||
opacity: opacity,
|
||||
child: Container(
|
||||
height: 108,
|
||||
height: tileHeight,
|
||||
width: panelWidth,
|
||||
padding: EdgeInsets.only(
|
||||
top: 1,
|
||||
left: 1,
|
||||
bottom: 1
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(12), bottomLeft: Radius.circular(12)),
|
||||
color: Theme.of(context).accentTextTheme.subtitle.color
|
||||
),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(12), bottomLeft: Radius.circular(12)),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Theme.of(context).accentTextTheme.caption.backgroundColor,
|
||||
Theme.of(context).accentTextTheme.caption.decorationColor
|
||||
]
|
||||
)
|
||||
),
|
||||
borderRadius: BorderRadius.only(topLeft: Radius.circular(10), bottomLeft: Radius.circular(10)),
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Theme.of(context).accentTextTheme.headline.color,
|
||||
Theme.of(context).accentTextTheme.headline.backgroundColor
|
||||
]
|
||||
)
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -42,7 +42,7 @@ abstract class SettingsStoreBase with Store {
|
|||
_sharedPreferences = sharedPreferences;
|
||||
_nodes = nodes;
|
||||
allowBiometricalAuthentication = initialAllowBiometricalAuthentication;
|
||||
isDarkTheme = initialDarkTheme;
|
||||
isDarkTheme = true;
|
||||
defaultPinLength = initialPinLength;
|
||||
languageCode = initialLanguageCode;
|
||||
currentLocale = initialCurrentLocale;
|
||||
|
@ -143,7 +143,7 @@ abstract class SettingsStoreBase with Store {
|
|||
bool allowBiometricalAuthentication;
|
||||
|
||||
@observable
|
||||
bool isDarkTheme;
|
||||
bool isDarkTheme = true;
|
||||
|
||||
@observable
|
||||
int defaultPinLength;
|
||||
|
@ -285,7 +285,7 @@ abstract class SettingsStoreBase with Store {
|
|||
}
|
||||
|
||||
Future setCurrentNodeToDefault() async {
|
||||
await changeCurrentNodeToDefault(sharedPreferences: _sharedPreferences, nodes: _nodes);
|
||||
// await changeCurrentNodeToDefault(sharedPreferences: _sharedPreferences, nodes: _nodes);
|
||||
await loadSettings();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
@ -23,6 +22,7 @@ class AddressTextField extends StatelessWidget {
|
|||
this.isBorderExist = true,
|
||||
this.buttonColor,
|
||||
this.borderColor,
|
||||
this.iconColor,
|
||||
this.textStyle,
|
||||
this.hintStyle,
|
||||
this.validator});
|
||||
|
@ -40,6 +40,7 @@ class AddressTextField extends StatelessWidget {
|
|||
final bool isBorderExist;
|
||||
final Color buttonColor;
|
||||
final Color borderColor;
|
||||
final Color iconColor;
|
||||
final TextStyle textStyle;
|
||||
final TextStyle hintStyle;
|
||||
FocusNode focusNode;
|
||||
|
@ -55,7 +56,7 @@ class AddressTextField extends StatelessWidget {
|
|||
focusNode: focusNode,
|
||||
style: textStyle ?? TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white
|
||||
color: Theme.of(context).primaryTextTheme.title.color
|
||||
),
|
||||
decoration: InputDecoration(
|
||||
suffixIcon: SizedBox(
|
||||
|
@ -64,7 +65,7 @@ class AddressTextField extends StatelessWidget {
|
|||
),
|
||||
hintStyle: hintStyle ?? TextStyle(
|
||||
fontSize: 16,
|
||||
color: PaletteDark.darkCyanBlue
|
||||
color: Theme.of(context).hintColor
|
||||
),
|
||||
hintText: placeholder ?? S.current.widgets_address,
|
||||
focusedBorder: isBorderExist
|
||||
|
@ -112,7 +113,9 @@ class AddressTextField extends StatelessWidget {
|
|||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(6))),
|
||||
child: Image.asset(
|
||||
'assets/images/duplicate.png')),
|
||||
'assets/images/duplicate.png',
|
||||
color: iconColor ?? Theme.of(context).primaryTextTheme.display1.decorationColor,
|
||||
)),
|
||||
)),
|
||||
],
|
||||
if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
|
||||
|
@ -128,7 +131,9 @@ class AddressTextField extends StatelessWidget {
|
|||
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(6))),
|
||||
child: Image.asset('assets/images/qr_code_icon.png')),
|
||||
child: Image.asset('assets/images/qr_code_icon.png',
|
||||
color: iconColor ?? Theme.of(context).primaryTextTheme.display1.decorationColor,
|
||||
)),
|
||||
))
|
||||
],
|
||||
if (this
|
||||
|
@ -147,7 +152,9 @@ class AddressTextField extends StatelessWidget {
|
|||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(6))),
|
||||
child: Image.asset(
|
||||
'assets/images/open_book.png')),
|
||||
'assets/images/open_book.png',
|
||||
color: iconColor ?? Theme.of(context).primaryTextTheme.display1.decorationColor,
|
||||
)),
|
||||
))
|
||||
],
|
||||
if (this
|
||||
|
@ -166,7 +173,9 @@ class AddressTextField extends StatelessWidget {
|
|||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(6))),
|
||||
child: Image.asset(
|
||||
'assets/images/receive_icon_raw.png')),
|
||||
'assets/images/receive_icon_raw.png',
|
||||
color: iconColor ?? Theme.of(context).primaryTextTheme.display1.decorationColor,
|
||||
)),
|
||||
)),
|
||||
],
|
||||
],
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
|
||||
class AlertWithOneAction extends BaseAlertDialog {
|
||||
AlertWithOneAction({
|
||||
|
@ -31,13 +32,7 @@ class AlertWithOneAction extends BaseAlertDialog {
|
|||
width: 300,
|
||||
height: 52,
|
||||
padding: EdgeInsets.only(left: 12, right: 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(24),
|
||||
bottomRight: Radius.circular(24)
|
||||
),
|
||||
color: Colors.white
|
||||
),
|
||||
color: Palette.blueCraiola,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.infinity,
|
||||
child: FlatButton(
|
||||
|
@ -50,7 +45,7 @@ class AlertWithOneAction extends BaseAlertDialog {
|
|||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blue,
|
||||
color: Colors.white,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)),
|
||||
|
|
|
@ -17,6 +17,7 @@ class BaseAlertDialog extends StatelessWidget {
|
|||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
|
@ -30,7 +31,7 @@ class BaseAlertDialog extends StatelessWidget {
|
|||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFamily: 'Poppins',
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
|
@ -38,55 +39,39 @@ class BaseAlertDialog extends StatelessWidget {
|
|||
}
|
||||
|
||||
Widget actionButtons(BuildContext context) {
|
||||
return Container(
|
||||
width: 300,
|
||||
height: 52,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(24),
|
||||
bottomRight: Radius.circular(24)
|
||||
),
|
||||
color: Colors.white
|
||||
),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 12, right: 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24)),
|
||||
),
|
||||
height: 52,
|
||||
padding: EdgeInsets.only(left: 6, right: 6),
|
||||
color: Palette.blueCraiola,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.infinity,
|
||||
child: FlatButton(
|
||||
onPressed: actionLeft,
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
child: Text(
|
||||
leftActionButtonText,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.blue,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)),
|
||||
onPressed: actionLeft,
|
||||
highlightColor: Colors.transparent,
|
||||
splashColor: Colors.transparent,
|
||||
child: Text(
|
||||
leftActionButtonText,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)),
|
||||
),
|
||||
)
|
||||
),
|
||||
Container(
|
||||
height: 52,
|
||||
width: 1,
|
||||
color: Colors.grey.withOpacity(0.2),
|
||||
),
|
||||
Flexible(
|
||||
),
|
||||
Flexible(
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(left: 12, right: 12),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(bottomRight: Radius.circular(24)),
|
||||
),
|
||||
height: 52,
|
||||
padding: EdgeInsets.only(left: 6, right: 6),
|
||||
color: Palette.alizarinRed,
|
||||
child: ButtonTheme(
|
||||
minWidth: double.infinity,
|
||||
child: FlatButton(
|
||||
|
@ -98,16 +83,16 @@ class BaseAlertDialog extends StatelessWidget {
|
|||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.red,
|
||||
color: Colors.white,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
)),
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -126,44 +111,30 @@ class BaseAlertDialog extends StatelessWidget {
|
|||
child: Center(
|
||||
child: GestureDetector(
|
||||
onTap: () => null,
|
||||
child: Container(
|
||||
width: 300,
|
||||
height: 257,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.all(Radius.circular(24)),
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor
|
||||
),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 300,
|
||||
height: 77,
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(24),
|
||||
topRight: Radius.circular(24)
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(30)),
|
||||
child: Container(
|
||||
width: 300,
|
||||
color: Theme.of(context).accentTextTheme.title.decorationColor,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.fromLTRB(24, 32, 24, 32),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
title(context),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 8),
|
||||
child: content(context),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: title(context),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 300,
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor,
|
||||
),
|
||||
Container(
|
||||
width: 300,
|
||||
height: 127,
|
||||
padding: EdgeInsets.all(24),
|
||||
child: Center(
|
||||
child: content(context),
|
||||
),
|
||||
),
|
||||
actionButtons(context)
|
||||
],
|
||||
actionButtons(context)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -21,7 +21,8 @@ class BaseTextFormField extends StatelessWidget {
|
|||
this.enabled = true,
|
||||
this.validator,
|
||||
this.textStyle,
|
||||
this.placeholderTextStyle});
|
||||
this.placeholderTextStyle,
|
||||
this.maxLength});
|
||||
|
||||
final TextEditingController controller;
|
||||
final TextInputType keyboardType;
|
||||
|
@ -42,6 +43,7 @@ class BaseTextFormField extends StatelessWidget {
|
|||
final FormFieldValidator<String> validator;
|
||||
final TextStyle placeholderTextStyle;
|
||||
final TextStyle textStyle;
|
||||
final int maxLength;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -54,6 +56,7 @@ class BaseTextFormField extends StatelessWidget {
|
|||
maxLines: maxLines,
|
||||
inputFormatters: inputFormatters,
|
||||
enabled: enabled,
|
||||
maxLength: maxLength,
|
||||
style: textStyle ?? TextStyle(
|
||||
fontSize: 16.0,
|
||||
color: textColor ?? Theme.of(context).primaryTextTheme.title.color),
|
||||
|
|
81
lib/src/widgets/checkbox_widget.dart
Normal file
81
lib/src/widgets/checkbox_widget.dart
Normal file
|
@ -0,0 +1,81 @@
|
|||
import 'dart:ui';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CheckboxWidget extends StatefulWidget {
|
||||
CheckboxWidget({
|
||||
@required this.value,
|
||||
@required this.caption,
|
||||
@required this.onChanged});
|
||||
|
||||
final bool value;
|
||||
final String caption;
|
||||
final Function(bool) onChanged;
|
||||
|
||||
@override
|
||||
CheckboxWidgetState createState() => CheckboxWidgetState(value, caption, onChanged);
|
||||
}
|
||||
|
||||
class CheckboxWidgetState extends State<CheckboxWidget> {
|
||||
CheckboxWidgetState(this.value, this.caption, this.onChanged);
|
||||
|
||||
bool value;
|
||||
String caption;
|
||||
Function(bool) onChanged;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
value = !value;
|
||||
onChanged(value);
|
||||
setState(() {});
|
||||
},
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: 16,
|
||||
width: 16,
|
||||
decoration: BoxDecoration(
|
||||
color: value
|
||||
? Palette.blueCraiola
|
||||
: Theme.of(context).accentTextTheme.subhead.decorationColor,
|
||||
borderRadius: BorderRadius.all(Radius.circular(2)),
|
||||
border: Border.all(
|
||||
color: value
|
||||
? Palette.blueCraiola
|
||||
: Theme.of(context).accentTextTheme.overline.color,
|
||||
width: 1
|
||||
)
|
||||
),
|
||||
child: value
|
||||
? Center(
|
||||
child: Icon(
|
||||
Icons.done,
|
||||
color: Colors.white,
|
||||
size: 14,
|
||||
),
|
||||
)
|
||||
: Offstage(),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 16),
|
||||
child: Text(
|
||||
caption,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).primaryTextTheme.title.color,
|
||||
fontSize: 18,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.w500,
|
||||
decoration: TextDecoration.none
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,15 +1,20 @@
|
|||
import 'dart:ui';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_background.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_scrollbar.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
|
||||
class Picker<Item extends Object> extends StatelessWidget {
|
||||
class Picker<Item extends Object> extends StatefulWidget {
|
||||
Picker({
|
||||
@required this.selectedAtIndex,
|
||||
@required this.items,
|
||||
this.images,
|
||||
@required this.title,
|
||||
@required this.onItemSelected,
|
||||
this.mainAxisAlignment = MainAxisAlignment.start
|
||||
this.mainAxisAlignment = MainAxisAlignment.start,
|
||||
this.isAlwaysShowScrollThumb = false
|
||||
});
|
||||
|
||||
final int selectedAtIndex;
|
||||
|
@ -18,59 +23,87 @@ class Picker<Item extends Object> extends StatelessWidget {
|
|||
final String title;
|
||||
final Function(Item) onItemSelected;
|
||||
final MainAxisAlignment mainAxisAlignment;
|
||||
final bool isAlwaysShowScrollThumb;
|
||||
|
||||
@override
|
||||
PickerState createState() => PickerState<Item>(items, images, onItemSelected);
|
||||
}
|
||||
|
||||
class PickerState<Item> extends State<Picker> {
|
||||
PickerState(this.items, this.images, this.onItemSelected);
|
||||
|
||||
final Function(Item) onItemSelected;
|
||||
final List<Item> items;
|
||||
final List<Image> images;
|
||||
|
||||
final closeButton = Image.asset('assets/images/close.png',
|
||||
color: Palette.darkBlueCraiola,
|
||||
);
|
||||
ScrollController controller = ScrollController();
|
||||
|
||||
final double backgroundHeight = 193;
|
||||
final double thumbHeight = 72;
|
||||
double fromTop = 0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Text(
|
||||
title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.none,
|
||||
color: Colors.white
|
||||
),
|
||||
),
|
||||
controller.addListener(() {
|
||||
fromTop = controller.hasClients
|
||||
? (controller.offset / controller.position.maxScrollExtent * (backgroundHeight - thumbHeight))
|
||||
: 0;
|
||||
setState(() {});
|
||||
});
|
||||
|
||||
return AlertBackground(
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: Text(
|
||||
widget.title,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.bold,
|
||||
decoration: TextDecoration.none,
|
||||
color: Colors.white
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
|
||||
child: GestureDetector(
|
||||
onTap: () => null,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
child: Container(
|
||||
height: 233,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: ListView.separated(
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
|
||||
child: GestureDetector(
|
||||
onTap: () => null,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14)),
|
||||
child: Container(
|
||||
height: 233,
|
||||
color: Theme.of(context).accentTextTheme.title.color,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: <Widget>[
|
||||
ListView.separated(
|
||||
controller: controller,
|
||||
separatorBuilder: (context, index) => Divider(
|
||||
color: Theme.of(context).dividerColor,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
height: 1,
|
||||
),
|
||||
itemCount: items == null ? 0 : items.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = items[index];
|
||||
final image = images != null? images[index] : null;
|
||||
final isItemSelected = index == selectedAtIndex;
|
||||
final isItemSelected = index == widget.selectedAtIndex;
|
||||
|
||||
final color = isItemSelected
|
||||
? Theme.of(context).accentTextTheme.subtitle.decorationColor
|
||||
: Colors.transparent;
|
||||
? Theme.of(context).textTheme.body2.color
|
||||
: Theme.of(context).accentTextTheme.title.color;
|
||||
final textColor = isItemSelected
|
||||
? Colors.blue
|
||||
? Palette.blueCraiola
|
||||
: Theme.of(context).primaryTextTheme.title.color;
|
||||
|
||||
return GestureDetector(
|
||||
|
@ -87,21 +120,22 @@ class Picker<Item extends Object> extends StatelessWidget {
|
|||
color: color,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: mainAxisAlignment,
|
||||
mainAxisAlignment: widget.mainAxisAlignment,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
image != null
|
||||
? image
|
||||
: Offstage(),
|
||||
? image
|
||||
: Offstage(),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
left: image != null ? 12 : 0
|
||||
left: image != null ? 12 : 0
|
||||
),
|
||||
child: Text(
|
||||
item.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontFamily: 'Poppins',
|
||||
fontWeight: FontWeight.w600,
|
||||
color: textColor,
|
||||
decoration: TextDecoration.none,
|
||||
),
|
||||
|
@ -112,17 +146,25 @@ class Picker<Item extends Object> extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
widget.isAlwaysShowScrollThumb
|
||||
? CakeScrollbar(
|
||||
backgroundHeight: backgroundHeight,
|
||||
thumbHeight: thumbHeight,
|
||||
fromTop: fromTop
|
||||
)
|
||||
),
|
||||
),
|
||||
: Offstage(),
|
||||
],
|
||||
)
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
AlertCloseButton(image: closeButton)
|
||||
],
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
@ -39,7 +40,7 @@ class StandardListRow extends StatelessWidget {
|
|||
Text(title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: _titleColor(context)))
|
||||
]));
|
||||
}
|
||||
|
@ -47,21 +48,24 @@ class StandardListRow extends StatelessWidget {
|
|||
Widget buildTrailing(BuildContext context) => null;
|
||||
|
||||
Color _titleColor(BuildContext context) => isSelected
|
||||
? Color.fromRGBO(20, 200, 71, 1)
|
||||
? Palette.blueCraiola
|
||||
: Theme.of(context).primaryTextTheme.title.color;
|
||||
|
||||
Color _backgroundColor(BuildContext context) {
|
||||
// return Theme.of(context).accentTextTheme.subtitle.decorationColor;
|
||||
return Theme.of(context).accentTextTheme.title.backgroundColor;
|
||||
return Theme.of(context).backgroundColor;
|
||||
}
|
||||
}
|
||||
|
||||
class SectionHeaderListRow extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(children: [
|
||||
StandardListSeparator(),
|
||||
Container(width: double.infinity, height: 40, color: Colors.white),
|
||||
StandardListSeparator()
|
||||
StandardListSeparator(padding: EdgeInsets.only(left: 24)),
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 40,
|
||||
color: Theme.of(context).backgroundColor),
|
||||
//StandardListSeparator(padding: EdgeInsets.only(left: 24))
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -75,8 +79,10 @@ class StandardListSeparator extends StatelessWidget {
|
|||
return Container(
|
||||
height: 1,
|
||||
padding: padding,
|
||||
color: Theme.of(context).accentTextTheme.title.backgroundColor,
|
||||
child: Container(height: 1, color: Color.fromRGBO(219, 227, 243, 1)));
|
||||
color: Theme.of(context).backgroundColor,
|
||||
child: Container(
|
||||
height: 1,
|
||||
color: Theme.of(context).primaryTextTheme.title.backgroundColor));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,9 +132,9 @@ class SectionStandardList extends StatelessWidget {
|
|||
final items = <Widget>[];
|
||||
|
||||
for (var sectionIndex = 0; sectionIndex < sectionCount; sectionIndex++) {
|
||||
if (sectionIndex == 0) {
|
||||
/*if (sectionIndex == 0) {
|
||||
items.add(StandardListSeparator());
|
||||
}
|
||||
}*/
|
||||
|
||||
final itemCount = itemCounter(sectionIndex);
|
||||
|
||||
|
@ -140,7 +146,7 @@ class SectionStandardList extends StatelessWidget {
|
|||
|
||||
items.add(sectionIndex + 1 != sectionCount
|
||||
? SectionHeaderListRow()
|
||||
: StandardListSeparator());
|
||||
: StandardListSeparator(padding: EdgeInsets.only(left: 24)));
|
||||
}
|
||||
|
||||
return items;
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StandartListRow extends StatelessWidget {
|
||||
StandartListRow(
|
||||
{this.title,
|
||||
this.value,
|
||||
this.titleFontSize = 14,
|
||||
this.valueFontSize = 16,
|
||||
this.image,
|
||||
this.isDrawBottom = false});
|
||||
|
||||
final String title;
|
||||
final String value;
|
||||
final double titleFontSize;
|
||||
final double valueFontSize;
|
||||
final Image image;
|
||||
final bool isDrawBottom;
|
||||
|
||||
@override
|
||||
|
@ -19,27 +26,42 @@ class StandartListRow extends StatelessWidget {
|
|||
color: Theme.of(context).backgroundColor,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
|
||||
const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontSize: titleFontSize,
|
||||
fontWeight: FontWeight.w500,
|
||||
color:
|
||||
Theme.of(context).primaryTextTheme.overline.color),
|
||||
textAlign: TextAlign.left),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 12),
|
||||
child: Text(value,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color)),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Text(value,
|
||||
style: TextStyle(
|
||||
fontSize: valueFontSize,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Theme.of(context)
|
||||
.primaryTextTheme
|
||||
.title
|
||||
.color)),
|
||||
),
|
||||
image != null
|
||||
? Padding(
|
||||
padding: EdgeInsets.only(left: 24),
|
||||
child: image,
|
||||
)
|
||||
: Offstage()
|
||||
],
|
||||
),
|
||||
)
|
||||
]),
|
||||
),
|
||||
|
|
|
@ -47,8 +47,7 @@ class TemplateTileState extends State<TemplateTile> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
//final color = isRemovable ? Colors.white : Theme.of(context).primaryTextTheme.title.color;
|
||||
final color = Colors.white;
|
||||
final color = isRemovable ? Colors.white : Theme.of(context).primaryTextTheme.title.color;
|
||||
final toIcon = Image.asset('assets/images/to_icon.png', color: color);
|
||||
|
||||
final content = Row(
|
||||
|
@ -106,7 +105,7 @@ class TemplateTileState extends State<TemplateTile> {
|
|||
child: Container(
|
||||
height: 40,
|
||||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
color: PaletteDark.darkVioletBlue,
|
||||
color: Theme.of(context).primaryTextTheme.display3.decorationColor,
|
||||
child: content,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -2,25 +2,28 @@ import 'package:flutter/material.dart';
|
|||
|
||||
class TopPanel extends StatefulWidget {
|
||||
TopPanel({
|
||||
@required this.color,
|
||||
@required this.widget,
|
||||
this.edgeInsets = const EdgeInsets.all(24)
|
||||
this.edgeInsets = const EdgeInsets.all(24),
|
||||
this.color,
|
||||
this.gradient
|
||||
});
|
||||
|
||||
final Color color;
|
||||
final Widget widget;
|
||||
final EdgeInsets edgeInsets;
|
||||
final Gradient gradient;
|
||||
|
||||
@override
|
||||
TopPanelState createState() => TopPanelState(color, widget, edgeInsets);
|
||||
TopPanelState createState() => TopPanelState(widget, edgeInsets, color, gradient);
|
||||
}
|
||||
|
||||
class TopPanelState extends State<TopPanel> {
|
||||
TopPanelState(this._color, this._widget, this._edgeInsets);
|
||||
TopPanelState(this._widget, this._edgeInsets, this._color, this._gradient);
|
||||
|
||||
final Color _color;
|
||||
final Widget _widget;
|
||||
final EdgeInsets _edgeInsets;
|
||||
final Gradient _gradient;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -32,7 +35,8 @@ class TopPanelState extends State<TopPanel> {
|
|||
bottomLeft: Radius.circular(24),
|
||||
bottomRight: Radius.circular(24)
|
||||
),
|
||||
color: _color
|
||||
color: _color,
|
||||
gradient: _gradient
|
||||
),
|
||||
child: _widget,
|
||||
);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
|
||||
class TrailButton extends StatelessWidget {
|
||||
TrailButton({
|
||||
|
@ -22,7 +21,7 @@ class TrailButton extends StatelessWidget {
|
|||
child: Text(
|
||||
caption,
|
||||
style: TextStyle(
|
||||
color: PaletteDark.lightBlueGrey,
|
||||
color: Theme.of(context).textTheme.subhead.decorationColor,
|
||||
fontWeight: FontWeight.w500,
|
||||
fontSize: 14),
|
||||
),
|
||||
|
|
|
@ -11,8 +11,7 @@ abstract class TradeFilterStoreBase with Store {
|
|||
TradeFilterStoreBase(
|
||||
{this.displayXMRTO = true,
|
||||
this.displayChangeNow = true,
|
||||
this.displayMorphToken = true,
|
||||
this.wallet});
|
||||
this.displayMorphToken = true});
|
||||
|
||||
@observable
|
||||
bool displayXMRTO;
|
||||
|
@ -23,8 +22,6 @@ abstract class TradeFilterStoreBase with Store {
|
|||
@observable
|
||||
bool displayMorphToken;
|
||||
|
||||
WalletBase wallet;
|
||||
|
||||
@action
|
||||
void toggleDisplayExchange(ExchangeProviderDescription provider) {
|
||||
switch (provider) {
|
||||
|
@ -40,13 +37,13 @@ abstract class TradeFilterStoreBase with Store {
|
|||
}
|
||||
}
|
||||
|
||||
List<TradeListItem> filtered({List<TradeListItem> trades}) {
|
||||
List<TradeListItem> filtered({List<TradeListItem> trades, WalletBase wallet}) {
|
||||
final _trades =
|
||||
trades.where((item) => item.trade.walletId == wallet.id).toList();
|
||||
final needToFilter = !displayChangeNow || !displayXMRTO || !displayMorphToken;
|
||||
|
||||
return needToFilter
|
||||
? trades
|
||||
? _trades
|
||||
.where((item) =>
|
||||
(displayXMRTO &&
|
||||
item.trade.provider == ExchangeProviderDescription.xmrto) ||
|
||||
|
|
|
@ -27,6 +27,12 @@ abstract class TradesStoreBase with Store {
|
|||
@observable
|
||||
List<TradeListItem> trades;
|
||||
|
||||
@observable
|
||||
Trade trade;
|
||||
|
||||
@action
|
||||
void setTrade(Trade trade) => this.trade = trade;
|
||||
|
||||
@action
|
||||
Future updateTradeList() async => trades =
|
||||
tradesSource.values.map((trade) => TradeListItem(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -29,8 +30,9 @@ abstract class SettingsStoreBase with Store {
|
|||
@required int initialPinLength,
|
||||
@required String initialLanguageCode,
|
||||
@required String initialCurrentLocale,
|
||||
@required this.node,
|
||||
// @required this.node,
|
||||
@required this.appVersion,
|
||||
@required Map<WalletType, Node> nodes,
|
||||
this.actionlistDisplayMode}) {
|
||||
fiatCurrency = initialFiatCurrency;
|
||||
transactionPriority = initialTransactionPriority;
|
||||
|
@ -42,17 +44,13 @@ abstract class SettingsStoreBase with Store {
|
|||
languageCode = initialLanguageCode;
|
||||
currentLocale = initialCurrentLocale;
|
||||
itemHeaders = {};
|
||||
|
||||
// actionlistDisplayMode.observe(
|
||||
// (dynamic _) => _sharedPreferences.setInt(displayActionListModeKey,
|
||||
// serializeActionlistDisplayModes(actionlistDisplayMode)),
|
||||
// fireImmediately: false);
|
||||
|
||||
_sharedPreferences = sharedPreferences;
|
||||
_nodeSource = nodeSource;
|
||||
_nodes = nodes;
|
||||
}
|
||||
|
||||
static const currentNodeIdKey = 'current_node_id';
|
||||
static const currentBitcoinElectrumSererIdKey = 'current_node_id_btc';
|
||||
static const currentFiatCurrencyKey = 'current_fiat_currency';
|
||||
static const currentTransactionPriorityKey = 'current_fee_priority';
|
||||
static const currentBalanceDisplayModeKey = 'current_balance_display_mode';
|
||||
|
@ -64,8 +62,8 @@ abstract class SettingsStoreBase with Store {
|
|||
static const currentPinLength = 'current_pin_length';
|
||||
static const currentLanguageCode = 'language_code';
|
||||
|
||||
@observable
|
||||
Node node;
|
||||
// @observable
|
||||
// Node node;
|
||||
|
||||
@observable
|
||||
FiatCurrency fiatCurrency;
|
||||
|
@ -103,6 +101,26 @@ abstract class SettingsStoreBase with Store {
|
|||
SharedPreferences _sharedPreferences;
|
||||
Box<Node> _nodeSource;
|
||||
|
||||
Map<WalletType, Node> _nodes;
|
||||
|
||||
Node getCurrentNode(WalletType walletType) => _nodes[walletType];
|
||||
|
||||
Future<void> setCurrentNode(Node node, WalletType walletType) async {
|
||||
switch (walletType) {
|
||||
case WalletType.bitcoin:
|
||||
await _sharedPreferences.setInt(
|
||||
currentBitcoinElectrumSererIdKey, node.key as int);
|
||||
break;
|
||||
case WalletType.monero:
|
||||
await _sharedPreferences.setInt(currentNodeIdKey, node.key as int);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
_nodes[walletType] = node;
|
||||
}
|
||||
|
||||
static Future<SettingsStore> load(
|
||||
{@required Box<Node> nodeSource,
|
||||
FiatCurrency initialFiatCurrency = FiatCurrency.usd,
|
||||
|
@ -132,12 +150,18 @@ abstract class SettingsStoreBase with Store {
|
|||
await Language.localeDetection();
|
||||
final initialCurrentLocale = await Devicelocale.currentLocale;
|
||||
final nodeId = sharedPreferences.getInt(currentNodeIdKey);
|
||||
final node = nodeSource.get(nodeId);
|
||||
final bitcoinElectrumServerId =
|
||||
sharedPreferences.getInt(currentBitcoinElectrumSererIdKey);
|
||||
final moneroNode = nodeSource.get(nodeId);
|
||||
final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
|
||||
return SettingsStore(
|
||||
sharedPreferences: sharedPreferences,
|
||||
node: node,
|
||||
nodes: {
|
||||
WalletType.monero: moneroNode,
|
||||
WalletType.bitcoin: bitcoinElectrumServer
|
||||
},
|
||||
nodeSource: nodeSource,
|
||||
appVersion: packageInfo.version,
|
||||
initialFiatCurrency: currentFiatCurrency,
|
||||
|
|
13
lib/store/theme_changer_store.dart
Normal file
13
lib/store/theme_changer_store.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
import 'package:cake_wallet/theme_changer.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
part 'theme_changer_store.g.dart';
|
||||
|
||||
class ThemeChangerStore = ThemeChangerStoreBase with _$ThemeChangerStore;
|
||||
|
||||
abstract class ThemeChangerStoreBase with Store {
|
||||
ThemeChangerStoreBase({this.themeChanger});
|
||||
|
||||
@observable
|
||||
ThemeChanger themeChanger;
|
||||
}
|
194
lib/themes.dart
194
lib/themes.dart
|
@ -80,61 +80,92 @@ class Themes {
|
|||
color: Palette.darkGray, // transaction/trade details titles
|
||||
decorationColor: Colors.white.withOpacity(0.5), // placeholder
|
||||
),
|
||||
|
||||
|
||||
|
||||
|
||||
subhead: TextStyle(
|
||||
color: Colors.white.withOpacity(0.5) // send, exchange, buy buttons on dashboard page
|
||||
color: Palette.blueCraiola, // first gradient color (send page)
|
||||
decorationColor: Palette.pinkFlamingo // second gradient color (send page)
|
||||
),
|
||||
headline: TextStyle(
|
||||
color: Palette.lightBlueGrey // historyPanelText
|
||||
color: Colors.white.withOpacity(0.5), // text field border color (send page)
|
||||
decorationColor: Colors.white.withOpacity(0.5), // text field hint color (send page)
|
||||
),
|
||||
display1: TextStyle(
|
||||
color: Colors.white // menuList
|
||||
color: Colors.white.withOpacity(0.2), // text field button color (send page)
|
||||
decorationColor: Colors.white // text field button icon color (send page)
|
||||
),
|
||||
display2: TextStyle(
|
||||
color: Palette.lavender // menuHeader
|
||||
color: Colors.white.withOpacity(0.5), // estimated fee (send page)
|
||||
decorationColor: Palette.shadowWhite // template dotted border (send page)
|
||||
),
|
||||
display3: TextStyle(
|
||||
color: Palette.lavender // historyPanelButton
|
||||
color: Palette.darkBlueCraiola, // template new text (send page)
|
||||
decorationColor: Palette.shadowWhite // template background color (send page)
|
||||
),
|
||||
display4: TextStyle(
|
||||
color: Palette.oceanBlue // QR code
|
||||
color: Palette.darkBlueCraiola, // template title (send page)
|
||||
decorationColor: Palette.niagara // receive amount text (exchange page)
|
||||
),
|
||||
subtitle: TextStyle(
|
||||
color: Palette.blueCraiola, // first gradient color top panel (exchange page)
|
||||
decorationColor: Palette.pinkFlamingo // second gradient color top panel (exchange page)
|
||||
),
|
||||
body1: TextStyle(
|
||||
color: Palette.blueCraiola.withOpacity(0.7), // first gradient color bottom panel (exchange page)
|
||||
decorationColor: Palette.pinkFlamingo.withOpacity(0.7) // second gradient color bottom panel (exchange page)
|
||||
),
|
||||
body2: TextStyle(
|
||||
color: Colors.white.withOpacity(0.5), // text field border on top panel (exchange page)
|
||||
decorationColor: Colors.white.withOpacity(0.5), // text field border on bottom panel (exchange page)
|
||||
)
|
||||
),
|
||||
focusColor: Colors.white.withOpacity(0.2), // text field button (exchange page)
|
||||
accentTextTheme: TextTheme(
|
||||
title: TextStyle(
|
||||
color: Colors.white, // picker background
|
||||
backgroundColor: Palette.periwinkleCraiola, // picker divider
|
||||
decorationColor: Colors.white // dialog background
|
||||
),
|
||||
caption: TextStyle(
|
||||
color: Palette.moderateLavender, // container (confirm exchange)
|
||||
backgroundColor: Palette.moderateLavender, // button background (confirm exchange)
|
||||
decorationColor: Palette.darkBlueCraiola, // text color (information page)
|
||||
),
|
||||
subtitle: TextStyle(
|
||||
color: Palette.darkBlueCraiola, // QR code (exchange trade page)
|
||||
backgroundColor: Palette.wildPeriwinkle, // divider (exchange trade page)
|
||||
decorationColor: Palette.blueCraiola // crete new wallet button background (wallet list page)
|
||||
),
|
||||
headline: TextStyle(
|
||||
color: Palette.moderateLavender, // first gradient color of wallet action buttons (wallet list page)
|
||||
backgroundColor: Palette.moderateLavender, // second gradient color of wallet action buttons (wallet list page)
|
||||
decorationColor: Colors.white // restore wallet button text color (wallet list page)
|
||||
),
|
||||
subhead: TextStyle(
|
||||
color: Palette.darkGray, // titles color (filter widget)
|
||||
backgroundColor: Palette.periwinkle, // divider color (filter widget)
|
||||
decorationColor: Colors.white // checkbox background (filter widget)
|
||||
),
|
||||
overline: TextStyle(
|
||||
color: Palette.wildPeriwinkle, // checkbox bounds (filter widget)
|
||||
decorationColor: Colors.white, // menu subname
|
||||
),
|
||||
display1: TextStyle(
|
||||
color: Palette.blueCraiola, // first gradient color (menu header)
|
||||
decorationColor: Palette.pinkFlamingo // second gradient color(menu header)
|
||||
),
|
||||
display2: TextStyle(
|
||||
color: Palette.shadowWhite, // action button color (address text field)
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
|
||||
focusColor: Colors.white, // wallet card border
|
||||
|
||||
cardColor: Palette.blueAlice,
|
||||
cardTheme: CardTheme(
|
||||
color: Colors.white, // synced card start
|
||||
),
|
||||
|
||||
|
||||
accentTextTheme: TextTheme(
|
||||
title: TextStyle(
|
||||
color: Palette.darkLavender, // top panel
|
||||
backgroundColor: Palette.lavender, // bottom panel
|
||||
decorationColor: PaletteDark.distantBlue // select button background color
|
||||
),
|
||||
caption: TextStyle(
|
||||
color: Palette.blue, // current wallet label
|
||||
backgroundColor: Colors.white, // gradient start, wallet label
|
||||
decorationColor: Palette.lavender, // gradient end, wallet label
|
||||
),
|
||||
subtitle: TextStyle(
|
||||
color: Palette.lightBlueGrey, // border color, wallet label
|
||||
backgroundColor: Palette.lavender, // address field, wallet card
|
||||
decorationColor: Palette.darkLavender // selected item
|
||||
),
|
||||
headline: TextStyle(
|
||||
color: Palette.darkLavender, // faq background
|
||||
backgroundColor: Palette.lavender // faq extension
|
||||
)
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
|
||||
|
@ -182,7 +213,7 @@ class Themes {
|
|||
decorationColor: PaletteDark.nightBlue // background of tiles (receive page)
|
||||
),
|
||||
display3: TextStyle(
|
||||
color: Colors.blue, // text color of current tile (receive page)
|
||||
color: Palette.blueCraiola, // text color of current tile (receive page)
|
||||
decorationColor: PaletteDark.lightOceanBlue // background of current tile (receive page)
|
||||
),
|
||||
display4: TextStyle(
|
||||
|
@ -215,32 +246,85 @@ class Themes {
|
|||
color: PaletteDark.lightBlueGrey, // transaction/trade details titles
|
||||
decorationColor: Colors.grey, // placeholder
|
||||
),
|
||||
|
||||
|
||||
|
||||
subhead: TextStyle(
|
||||
color: PaletteDark.lightDistantBlue // send, exchange, buy buttons on dashboard page
|
||||
color: PaletteDark.darkNightBlue, // first gradient color (send page)
|
||||
decorationColor: PaletteDark.darkNightBlue // second gradient color (send page)
|
||||
),
|
||||
headline: TextStyle(
|
||||
color: PaletteDark.pigeonBlue // historyPanelText
|
||||
color: PaletteDark.lightVioletBlue, // text field border color (send page)
|
||||
decorationColor: PaletteDark.darkCyanBlue, // text field hint color (send page)
|
||||
),
|
||||
display1: TextStyle(
|
||||
color: PaletteDark.lightNightBlue // menuList
|
||||
color: PaletteDark.buttonNightBlue, // text field button color (send page)
|
||||
decorationColor: PaletteDark.gray // text field button icon color (send page)
|
||||
),
|
||||
display2: TextStyle(
|
||||
color: PaletteDark.headerNightBlue // menuHeader
|
||||
color: Colors.white, // estimated fee (send page)
|
||||
decorationColor: PaletteDark.darkCyanBlue // template dotted border (send page)
|
||||
),
|
||||
display3: TextStyle(
|
||||
color: PaletteDark.moderateNightBlue // historyPanelButton
|
||||
color: PaletteDark.darkCyanBlue, // template new text (send page)
|
||||
decorationColor: PaletteDark.darkVioletBlue // template background color (send page)
|
||||
),
|
||||
display4: TextStyle(
|
||||
color: PaletteDark.gray // QR code
|
||||
color: PaletteDark.cyanBlue, // template title (send page)
|
||||
decorationColor: PaletteDark.darkCyanBlue // receive amount text (exchange page)
|
||||
),
|
||||
subtitle: TextStyle(
|
||||
color: PaletteDark.wildVioletBlue, // first gradient color top panel (exchange page)
|
||||
decorationColor: PaletteDark.wildVioletBlue // second gradient color top panel (exchange page)
|
||||
),
|
||||
body1: TextStyle(
|
||||
color: PaletteDark.darkNightBlue, // first gradient color bottom panel (exchange page)
|
||||
decorationColor: PaletteDark.darkNightBlue // second gradient color bottom panel (exchange page)
|
||||
),
|
||||
body2: TextStyle(
|
||||
color: PaletteDark.blueGrey, // text field border on top panel (exchange page)
|
||||
decorationColor: PaletteDark.moderateVioletBlue, // text field border on bottom panel (exchange page)
|
||||
)
|
||||
),
|
||||
focusColor: PaletteDark.moderateBlue, // text field button (exchange page)
|
||||
accentTextTheme: TextTheme(
|
||||
title: TextStyle(
|
||||
color: PaletteDark.nightBlue, // picker background
|
||||
backgroundColor: PaletteDark.dividerColor, // picker divider
|
||||
decorationColor: PaletteDark.darkNightBlue // dialog background
|
||||
),
|
||||
caption: TextStyle(
|
||||
color: PaletteDark.nightBlue, // container (confirm exchange)
|
||||
backgroundColor: PaletteDark.deepVioletBlue, // button background (confirm exchange)
|
||||
decorationColor: Palette.darkLavender, // text color (information page)
|
||||
),
|
||||
subtitle: TextStyle(
|
||||
color: PaletteDark.lightBlueGrey, // QR code (exchange trade page)
|
||||
backgroundColor: PaletteDark.deepVioletBlue, // divider (exchange trade page)
|
||||
decorationColor: Colors.white // crete new wallet button background (wallet list page)
|
||||
),
|
||||
headline: TextStyle(
|
||||
color: PaletteDark.distantBlue, // first gradient color of wallet action buttons (wallet list page)
|
||||
backgroundColor: PaletteDark.distantNightBlue, // second gradient color of wallet action buttons (wallet list page)
|
||||
decorationColor: Palette.darkBlueCraiola // restore wallet button text color (wallet list page)
|
||||
),
|
||||
subhead: TextStyle(
|
||||
color: Colors.white, // titles color (filter widget)
|
||||
backgroundColor: PaletteDark.darkOceanBlue, // divider color (filter widget)
|
||||
decorationColor: PaletteDark.wildVioletBlue.withOpacity(0.3) // checkbox background (filter widget)
|
||||
),
|
||||
overline: TextStyle(
|
||||
color: PaletteDark.wildVioletBlue, // checkbox bounds (filter widget)
|
||||
decorationColor: PaletteDark.darkCyanBlue, // menu subname
|
||||
),
|
||||
display1: TextStyle(
|
||||
color: PaletteDark.deepPurpleBlue, // first gradient color (menu header)
|
||||
decorationColor: PaletteDark.deepPurpleBlue // second gradient color(menu header)
|
||||
),
|
||||
display2: TextStyle(
|
||||
color: PaletteDark.nightBlue, // action button color (address text field)
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
|
||||
focusColor: PaletteDark.lightDistantBlue, // wallet card border
|
||||
cardColor: PaletteDark.darkNightBlue,
|
||||
cardTheme: CardTheme(
|
||||
color: PaletteDark.moderateBlue, // synced card start
|
||||
|
@ -248,27 +332,7 @@ class Themes {
|
|||
|
||||
|
||||
|
||||
accentTextTheme: TextTheme(
|
||||
title: TextStyle(
|
||||
color: PaletteDark.moderateBlue, // top panel
|
||||
backgroundColor: PaletteDark.lightNightBlue, // bottom panel
|
||||
decorationColor: Colors.white // select button background color
|
||||
),
|
||||
caption: TextStyle(
|
||||
color: Colors.white, // current wallet label
|
||||
backgroundColor: PaletteDark.distantBlue, // gradient start, wallet label
|
||||
decorationColor: PaletteDark.nightBlue, // gradient end, wallet label
|
||||
),
|
||||
subtitle: TextStyle(
|
||||
color: PaletteDark.darkNightBlue, // border color, wallet label
|
||||
backgroundColor: PaletteDark.violetBlue, // address field, wallet card
|
||||
decorationColor: PaletteDark.headerNightBlue // selected item
|
||||
),
|
||||
headline: TextStyle(
|
||||
color: PaletteDark.lightNightBlue, // faq background
|
||||
backgroundColor: PaletteDark.headerNightBlue // faq extension
|
||||
)
|
||||
),
|
||||
|
||||
);
|
||||
|
||||
}
|
46
lib/utils/mobx.dart
Normal file
46
lib/utils/mobx.dart
Normal file
|
@ -0,0 +1,46 @@
|
|||
import 'package:mobx/mobx.dart';
|
||||
|
||||
void connectDifferent<T, Y>(
|
||||
ObservableList<T> source, ObservableList<Y> dest, Y Function(T) transform,
|
||||
{bool Function(T) filter}) {
|
||||
source.observe((ListChange<T> change) {
|
||||
// switch (change.type) {
|
||||
// case OperationType.add:
|
||||
// final _values = change.added;
|
||||
// Iterable<T> values;
|
||||
|
||||
// if (filter != null) {
|
||||
// values = _values.where(filter);
|
||||
// }
|
||||
|
||||
// dest.addAll(values.map((e) => transform(e)));
|
||||
// break;
|
||||
// case OperationType.remove:
|
||||
// change.removed.forEach((element) {
|
||||
// dest.remove(element);
|
||||
// });
|
||||
|
||||
// // dest.removeAt(change.index);
|
||||
// break;
|
||||
// case OperationType.update:
|
||||
// // change.index
|
||||
// break;
|
||||
// }
|
||||
});
|
||||
}
|
||||
|
||||
void connect<T>(ObservableList<T> source, ObservableList<T> dest) {
|
||||
source.observe((ListChange<T> change) {
|
||||
// switch (change.type) {
|
||||
// case OperationType.add:
|
||||
// dest.addAll(change.added);
|
||||
// break;
|
||||
// case OperationType.remove:
|
||||
// dest.removeAt(change.index);
|
||||
// break;
|
||||
// case OperationType.update:
|
||||
// // change.index
|
||||
// break;
|
||||
// }
|
||||
});
|
||||
}
|
|
@ -16,7 +16,7 @@ abstract class ContactViewModelBase with Store {
|
|||
_contact = contact {
|
||||
name = _contact?.name;
|
||||
address = _contact?.address;
|
||||
currency = _contact?.type ?? _wallet.currency;
|
||||
currency = _contact?.type; //_wallet.currency;
|
||||
}
|
||||
|
||||
@observable
|
||||
|
@ -33,7 +33,8 @@ abstract class ContactViewModelBase with Store {
|
|||
|
||||
@computed
|
||||
bool get isReady =>
|
||||
(name?.isNotEmpty ?? false) && (address?.isNotEmpty ?? false);
|
||||
(name?.isNotEmpty ?? false) && (currency?.toString()?.isNotEmpty ?? false)
|
||||
&& (address?.isNotEmpty ?? false);
|
||||
|
||||
final List<CryptoCurrency> currencies;
|
||||
final ContactService _contactService;
|
||||
|
@ -44,7 +45,8 @@ abstract class ContactViewModelBase with Store {
|
|||
void reset() {
|
||||
address = '';
|
||||
name = '';
|
||||
currency = _wallet.currency;
|
||||
//currency = _wallet.currency;
|
||||
currency = null;
|
||||
}
|
||||
|
||||
Future save() async {
|
||||
|
|
|
@ -35,9 +35,11 @@ abstract class BalanceViewModelBase with Store {
|
|||
|
||||
if (_wallet is BitcoinWallet) {
|
||||
return WalletBalance(
|
||||
unlockedBalance: _wallet.balance.confirmedFormatted,
|
||||
totalBalance: _wallet.balance.unconfirmedFormatted);
|
||||
unlockedBalance: _wallet.balance.totalFormatted,
|
||||
totalBalance: _wallet.balance.totalFormatted);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
String _getFiatBalance({double price, String cryptoAmount}) {
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:cake_wallet/src/domain/common/transaction_info.dart';
|
|||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||
|
@ -28,23 +29,57 @@ part 'dashboard_view_model.g.dart';
|
|||
class DashboardViewModel = DashboardViewModelBase with _$DashboardViewModel;
|
||||
|
||||
abstract class DashboardViewModelBase with Store {
|
||||
DashboardViewModelBase({
|
||||
this.balanceViewModel,
|
||||
this.appStore,
|
||||
this.tradesStore,
|
||||
this.tradeFilterStore,
|
||||
this.transactionFilterStore}) {
|
||||
DashboardViewModelBase(
|
||||
{this.balanceViewModel,
|
||||
this.appStore,
|
||||
this.tradesStore,
|
||||
this.tradeFilterStore,
|
||||
this.transactionFilterStore}) {
|
||||
filterItems = {
|
||||
S.current.transactions: [
|
||||
FilterItem(
|
||||
value: transactionFilterStore.displayIncoming,
|
||||
caption: S.current.incoming,
|
||||
onChanged: (value) => transactionFilterStore.toggleIncoming()),
|
||||
FilterItem(
|
||||
value: transactionFilterStore.displayOutgoing,
|
||||
caption: S.current.outgoing,
|
||||
onChanged: (value) => transactionFilterStore.toggleOutgoing()),
|
||||
FilterItem(
|
||||
value: false,
|
||||
caption: S.current.transactions_by_date,
|
||||
onChanged: null),
|
||||
],
|
||||
S.current.trades: [
|
||||
FilterItem(
|
||||
value: tradeFilterStore.displayXMRTO,
|
||||
caption: 'XMR.TO',
|
||||
onChanged: (value) => tradeFilterStore
|
||||
.toggleDisplayExchange(ExchangeProviderDescription.xmrto)),
|
||||
FilterItem(
|
||||
value: tradeFilterStore.displayChangeNow,
|
||||
caption: 'Change.NOW',
|
||||
onChanged: (value) => tradeFilterStore
|
||||
.toggleDisplayExchange(ExchangeProviderDescription.changeNow)),
|
||||
FilterItem(
|
||||
value: tradeFilterStore.displayMorphToken,
|
||||
caption: 'MorphToken',
|
||||
onChanged: (value) => tradeFilterStore
|
||||
.toggleDisplayExchange(ExchangeProviderDescription.morphToken)),
|
||||
]
|
||||
};
|
||||
|
||||
name = appStore.wallet?.name;
|
||||
wallet ??= appStore.wallet;
|
||||
type = wallet.type;
|
||||
|
||||
transactions = ObservableList.of(wallet.transactionHistory.transactions
|
||||
transactions = ObservableList.of(wallet
|
||||
.transactionHistory.transactions.values
|
||||
.map((transaction) => TransactionListItem(
|
||||
transaction: transaction,
|
||||
price: price,
|
||||
fiatCurrency: appStore.settingsStore.fiatCurrency,
|
||||
displayMode: balanceDisplayMode)));
|
||||
transaction: transaction,
|
||||
price: price,
|
||||
fiatCurrency: appStore.settingsStore.fiatCurrency,
|
||||
displayMode: balanceDisplayMode)));
|
||||
|
||||
_reaction = reaction((_) => appStore.wallet, _onWalletChange);
|
||||
|
||||
|
@ -78,15 +113,11 @@ abstract class DashboardViewModelBase with Store {
|
|||
var statusText = '';
|
||||
|
||||
if (status is SyncingSyncStatus) {
|
||||
statusText = S.current
|
||||
.Blocks_remaining(
|
||||
status.toString());
|
||||
statusText = S.current.Blocks_remaining(status.toString());
|
||||
}
|
||||
|
||||
if (status is FailedSyncStatus) {
|
||||
statusText = S
|
||||
.current
|
||||
.please_try_to_connect_to_another_node;
|
||||
if (status is FailedSyncStatus || status is LostConnectionSyncStatus) {
|
||||
statusText = S.current.please_try_to_connect_to_another_node;
|
||||
}
|
||||
|
||||
return statusText;
|
||||
|
@ -106,9 +137,8 @@ abstract class DashboardViewModelBase with Store {
|
|||
List<ActionListItem> get items {
|
||||
final _items = <ActionListItem>[];
|
||||
|
||||
_items
|
||||
.addAll(transactionFilterStore.filtered(transactions: transactions));
|
||||
_items.addAll(tradeFilterStore.filtered(trades: trades));
|
||||
_items.addAll(transactionFilterStore.filtered(transactions: transactions));
|
||||
_items.addAll(tradeFilterStore.filtered(trades: trades, wallet: wallet));
|
||||
|
||||
return formattedItemsList(_items);
|
||||
}
|
||||
|
@ -125,16 +155,23 @@ abstract class DashboardViewModelBase with Store {
|
|||
|
||||
TransactionFilterStore transactionFilterStore;
|
||||
|
||||
Map<String, List<FilterItem>> filterItems;
|
||||
|
||||
ReactionDisposer _reaction;
|
||||
|
||||
Future<void> reconnect() async {
|
||||
final node = appStore.settingsStore.getCurrentNode(wallet.type);
|
||||
await wallet.connectToNode(node: node);
|
||||
}
|
||||
|
||||
void _onWalletChange(WalletBase wallet) {
|
||||
name = wallet.name;
|
||||
transactions.clear();
|
||||
transactions.addAll(wallet.transactionHistory.transactions
|
||||
.map((transaction) => TransactionListItem(
|
||||
transaction: transaction,
|
||||
price: price,
|
||||
fiatCurrency: appStore.settingsStore.fiatCurrency,
|
||||
displayMode: balanceDisplayMode)));
|
||||
transactions.addAll(wallet.transactionHistory.transactions.values.map(
|
||||
(transaction) => TransactionListItem(
|
||||
transaction: transaction,
|
||||
price: price,
|
||||
fiatCurrency: appStore.settingsStore.fiatCurrency,
|
||||
displayMode: balanceDisplayMode)));
|
||||
}
|
||||
}
|
||||
|
|
7
lib/view_model/dashboard/filter_item.dart
Normal file
7
lib/view_model/dashboard/filter_item.dart
Normal file
|
@ -0,0 +1,7 @@
|
|||
class FilterItem {
|
||||
FilterItem({this.value, this.caption, this.onChanged});
|
||||
|
||||
bool value;
|
||||
String caption;
|
||||
Function(bool) onChanged;
|
||||
}
|
93
lib/view_model/exchange/exchange_trade_view_model.dart
Normal file
93
lib/view_model/exchange/exchange_trade_view_model.dart
Normal file
|
@ -0,0 +1,93 @@
|
|||
import 'dart:async';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/changenow/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/morphtoken/morphtoken_exchange_provider.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_item.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
part 'exchange_trade_view_model.g.dart';
|
||||
|
||||
class ExchangeTradeViewModel = ExchangeTradeViewModelBase with _$ExchangeTradeViewModel;
|
||||
|
||||
abstract class ExchangeTradeViewModelBase with Store {
|
||||
ExchangeTradeViewModelBase({this.wallet, this.trades, this.tradesStore}) {
|
||||
trade = tradesStore.trade;
|
||||
|
||||
isSendable = trade.from == wallet.currency ||
|
||||
trade.provider == ExchangeProviderDescription.xmrto;
|
||||
|
||||
switch (trade.provider) {
|
||||
case ExchangeProviderDescription.xmrto:
|
||||
_provider = XMRTOExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.changeNow:
|
||||
_provider = ChangeNowExchangeProvider();
|
||||
break;
|
||||
case ExchangeProviderDescription.morphToken:
|
||||
_provider = MorphTokenExchangeProvider(trades: trades);
|
||||
break;
|
||||
}
|
||||
|
||||
items = ObservableList<ExchangeTradeItem>();
|
||||
items.addAll([
|
||||
ExchangeTradeItem(
|
||||
title: S.current.id,
|
||||
data: '${trade.id}',
|
||||
isCopied: true),
|
||||
ExchangeTradeItem(
|
||||
title: S.current.amount,
|
||||
data: '${trade.amount}',
|
||||
isCopied: false),
|
||||
ExchangeTradeItem(
|
||||
title: S.current.status,
|
||||
data: '${trade.state}',
|
||||
isCopied: false),
|
||||
ExchangeTradeItem(
|
||||
title: S.current.widgets_address + ':',
|
||||
data: trade.inputAddress,
|
||||
isCopied: true),
|
||||
]);
|
||||
|
||||
_updateTrade();
|
||||
_timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade());
|
||||
}
|
||||
|
||||
final WalletBase wallet;
|
||||
final Box<Trade> trades;
|
||||
final TradesStore tradesStore;
|
||||
|
||||
@observable
|
||||
Trade trade;
|
||||
|
||||
@observable
|
||||
bool isSendable;
|
||||
|
||||
@observable
|
||||
ObservableList<ExchangeTradeItem> items;
|
||||
|
||||
ExchangeProvider _provider;
|
||||
|
||||
Timer _timer;
|
||||
|
||||
@action
|
||||
Future<void> _updateTrade() async {
|
||||
try {
|
||||
final updatedTrade = await _provider.findTradeById(id: trade.id);
|
||||
|
||||
if (updatedTrade.createdAt == null && trade.createdAt != null) {
|
||||
updatedTrade.createdAt = trade.createdAt;
|
||||
}
|
||||
|
||||
trade = updatedTrade;
|
||||
} catch (e) {
|
||||
print(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import 'package:cake_wallet/core/wallet_base.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/exchange/exchange_provider.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/limits.dart';
|
||||
import 'package:cake_wallet/src/domain/exchange/trade.dart';
|
||||
import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
@ -24,17 +26,18 @@ part 'exchange_view_model.g.dart';
|
|||
class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel;
|
||||
|
||||
abstract class ExchangeViewModelBase with Store {
|
||||
ExchangeViewModelBase({this.wallet, this.trades, this.exchangeTemplateStore}) {
|
||||
ExchangeViewModelBase(
|
||||
{this.wallet,
|
||||
this.trades,
|
||||
this.exchangeTemplateStore,
|
||||
this.tradesStore}) {
|
||||
providerList = [
|
||||
XMRTOExchangeProvider(),
|
||||
ChangeNowExchangeProvider(),
|
||||
MorphTokenExchangeProvider(trades: trades)
|
||||
];
|
||||
|
||||
provider = providerList[ 0 ];
|
||||
|
||||
depositCurrency = CryptoCurrency.xmr;
|
||||
receiveCurrency = CryptoCurrency.btc;
|
||||
_initialPairBasedOnWallet();
|
||||
isDepositAddressEnabled = !(depositCurrency == wallet.currency);
|
||||
isReceiveAddressEnabled = !(receiveCurrency == wallet.currency);
|
||||
depositAmount = '';
|
||||
|
@ -44,12 +47,14 @@ abstract class ExchangeViewModelBase with Store {
|
|||
limitsState = LimitsInitialState();
|
||||
tradeState = ExchangeTradeStateInitial();
|
||||
_cryptoNumberFormat = NumberFormat()..maximumFractionDigits = 12;
|
||||
provider = providersForCurrentPair().first;
|
||||
loadLimits();
|
||||
}
|
||||
|
||||
final WalletBase wallet;
|
||||
final Box<Trade> trades;
|
||||
final ExchangeTemplateStore exchangeTemplateStore;
|
||||
final TradesStore tradesStore;
|
||||
|
||||
@observable
|
||||
ExchangeProvider provider;
|
||||
|
@ -133,8 +138,11 @@ abstract class ExchangeViewModelBase with Store {
|
|||
|
||||
provider
|
||||
.calculateAmount(
|
||||
from: depositCurrency, to: receiveCurrency, amount: _amount)
|
||||
.then((amount) => _cryptoNumberFormat.format(amount).toString().replaceAll(RegExp("\\,"), ""))
|
||||
from: depositCurrency, to: receiveCurrency, amount: _amount)
|
||||
.then((amount) => _cryptoNumberFormat
|
||||
.format(amount)
|
||||
.toString()
|
||||
.replaceAll(RegExp('\\,'), ''))
|
||||
.then((amount) => depositAmount = amount);
|
||||
}
|
||||
|
||||
|
@ -151,8 +159,11 @@ abstract class ExchangeViewModelBase with Store {
|
|||
final _amount = double.parse(amount);
|
||||
provider
|
||||
.calculateAmount(
|
||||
from: depositCurrency, to: receiveCurrency, amount: _amount)
|
||||
.then((amount) => _cryptoNumberFormat.format(amount).toString().replaceAll(RegExp("\\,"), ""))
|
||||
from: depositCurrency, to: receiveCurrency, amount: _amount)
|
||||
.then((amount) => _cryptoNumberFormat
|
||||
.format(amount)
|
||||
.toString()
|
||||
.replaceAll(RegExp('\\,'), ''))
|
||||
.then((amount) => receiveAmount = amount);
|
||||
}
|
||||
|
||||
|
@ -210,16 +221,19 @@ abstract class ExchangeViewModelBase with Store {
|
|||
|
||||
if (limitsState is LimitsLoadedSuccessfully && amount != null) {
|
||||
if (double.parse(amount) < limits.min) {
|
||||
tradeState = TradeIsCreatedFailure(error: S.current.error_text_minimal_limit('${provider.description}',
|
||||
'${limits.min}', currency.toString()));
|
||||
tradeState = TradeIsCreatedFailure(
|
||||
error: S.current.error_text_minimal_limit('${provider.description}',
|
||||
'${limits.min}', currency.toString()));
|
||||
} else if (limits.max != null && double.parse(amount) > limits.max) {
|
||||
tradeState = TradeIsCreatedFailure(error: S.current.error_text_maximum_limit('${provider.description}',
|
||||
'${limits.max}', currency.toString()));
|
||||
tradeState = TradeIsCreatedFailure(
|
||||
error: S.current.error_text_maximum_limit('${provider.description}',
|
||||
'${limits.max}', currency.toString()));
|
||||
} else {
|
||||
try {
|
||||
tradeState = TradeIsCreating();
|
||||
final trade = await provider.createTrade(request: request);
|
||||
trade.walletId = wallet.id;
|
||||
tradesStore.setTrade(trade);
|
||||
await trades.add(trade);
|
||||
tradeState = TradeIsCreatedSuccessfully(trade: trade);
|
||||
} catch (e) {
|
||||
|
@ -227,9 +241,10 @@ abstract class ExchangeViewModelBase with Store {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
tradeState = TradeIsCreatedFailure(error: S.current.error_text_limits_loading_failed("${provider.description}"));
|
||||
tradeState = TradeIsCreatedFailure(
|
||||
error: S.current
|
||||
.error_text_limits_loading_failed('${provider.description}'));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -253,9 +268,9 @@ abstract class ExchangeViewModelBase with Store {
|
|||
{CryptoCurrency from, CryptoCurrency to}) {
|
||||
final providers = providerList
|
||||
.where((provider) => provider.pairList
|
||||
.where((pair) =>
|
||||
pair.from == depositCurrency && pair.to == receiveCurrency)
|
||||
.isNotEmpty)
|
||||
.where((pair) =>
|
||||
pair.from == depositCurrency && pair.to == receiveCurrency)
|
||||
.isNotEmpty)
|
||||
.toList();
|
||||
|
||||
return providers;
|
||||
|
@ -264,12 +279,12 @@ abstract class ExchangeViewModelBase with Store {
|
|||
void _onPairChange() {
|
||||
final isPairExist = provider.pairList
|
||||
.where((pair) =>
|
||||
pair.from == depositCurrency && pair.to == receiveCurrency)
|
||||
pair.from == depositCurrency && pair.to == receiveCurrency)
|
||||
.isNotEmpty;
|
||||
|
||||
if (!isPairExist) {
|
||||
final provider =
|
||||
_providerForPair(from: depositCurrency, to: receiveCurrency);
|
||||
_providerForPair(from: depositCurrency, to: receiveCurrency);
|
||||
|
||||
if (provider != null) {
|
||||
changeProvider(provider: provider);
|
||||
|
@ -287,4 +302,18 @@ abstract class ExchangeViewModelBase with Store {
|
|||
return providers.isNotEmpty ? providers[0] : null;
|
||||
}
|
||||
|
||||
}
|
||||
void _initialPairBasedOnWallet() {
|
||||
switch (wallet.type) {
|
||||
case WalletType.monero:
|
||||
depositCurrency = CryptoCurrency.xmr;
|
||||
receiveCurrency = CryptoCurrency.btc;
|
||||
break;
|
||||
case WalletType.bitcoin:
|
||||
depositCurrency = CryptoCurrency.btc;
|
||||
receiveCurrency = CryptoCurrency.xmr;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,92 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/core/wallet_base.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node.dart';
|
||||
import 'package:cake_wallet/src/domain/common/node_list.dart';
|
||||
import 'package:cake_wallet/store/node_list_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/src/domain/common/default_settings_migration.dart';
|
||||
import 'package:cake_wallet/src/domain/common/wallet_type.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
|
||||
part 'node_list_view_model.g.dart';
|
||||
|
||||
class NodeListViewModel = NodeListViewModelBase with _$NodeListViewModel;
|
||||
|
||||
abstract class NodeListViewModelBase with Store {
|
||||
NodeListViewModelBase(this._nodeListStore, this._nodeSource, this._wallet);
|
||||
class ItemCell<Item> {
|
||||
ItemCell(this.value, {@required this.isSelected});
|
||||
|
||||
@computed
|
||||
ObservableList<Node> get nodes => ObservableList<Node>.of(
|
||||
_nodeListStore.nodes.where((node) => node.type == _wallet.type));
|
||||
final Item value;
|
||||
final bool isSelected;
|
||||
}
|
||||
|
||||
abstract class NodeListViewModelBase with Store {
|
||||
NodeListViewModelBase(
|
||||
this._nodeListStore, this._nodeSource, this._wallet, this._settingsStore)
|
||||
: nodes = ObservableList<ItemCell<Node>>() {
|
||||
final currentNode = _settingsStore.getCurrentNode(_wallet.type);
|
||||
final values = _nodeListStore.nodes;
|
||||
nodes.clear();
|
||||
nodes.addAll(values.where((Node node) => node.type == _wallet.type).map(
|
||||
(Node val) =>
|
||||
ItemCell<Node>(val, isSelected: val.key == currentNode.key)));
|
||||
connectDifferent(
|
||||
_nodeListStore.nodes,
|
||||
nodes,
|
||||
(Node val) =>
|
||||
ItemCell<Node>(val, isSelected: val.key == currentNode.key),
|
||||
filter: (Node val) {
|
||||
return val.type == _wallet.type;
|
||||
});
|
||||
}
|
||||
|
||||
ObservableList<ItemCell<Node>> nodes;
|
||||
|
||||
final WalletBase _wallet;
|
||||
final Box<Node> _nodeSource;
|
||||
final NodeListStore _nodeListStore;
|
||||
final SettingsStore _settingsStore;
|
||||
|
||||
Future<void> reset() async => await resetToDefault(_nodeSource);
|
||||
Future<void> reset() async {
|
||||
await resetToDefault(_nodeSource);
|
||||
|
||||
Future<void> delete(Node node) async => node.delete();
|
||||
Node node;
|
||||
|
||||
switch (_wallet.type) {
|
||||
case WalletType.bitcoin:
|
||||
node = getBitcoinDefaultElectrumServer(nodes: _nodeSource);
|
||||
break;
|
||||
case WalletType.monero:
|
||||
node = getMoneroDefaultNode(
|
||||
nodes: _nodeSource,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
await _wallet.connectToNode(node: node);
|
||||
}
|
||||
|
||||
Future<void> delete(Node node) async => _nodeSource.delete(node.key);
|
||||
|
||||
Future<void> setAsCurrent(Node node) async {
|
||||
await _wallet.connectToNode(node: node);
|
||||
await _settingsStore.setCurrentNode(node, _wallet.type);
|
||||
_updateCurrentNode();
|
||||
}
|
||||
|
||||
void _updateCurrentNode() {
|
||||
final currentNode = _settingsStore.getCurrentNode(_wallet.type);
|
||||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
final item = nodes[i];
|
||||
final isSelected = item.value.key == currentNode.key;
|
||||
|
||||
if (item.isSelected != isSelected) {
|
||||
nodes[i] = ItemCell<Node>(item.value, isSelected: isSelected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue