Merged 4.1.0
|
@ -1,2 +1,2 @@
|
||||||
-
|
-
|
||||||
uri: electrumx.cakewallet.com:50002
|
uri: electrum.cakewallet.com:50002
|
BIN
assets/images/2.0x/transfer.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/images/2.0x/upload.png
Normal file
After Width: | Height: | Size: 760 B |
BIN
assets/images/3.0x/transfer.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/images/3.0x/upload.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 408 B After Width: | Height: | Size: 629 B |
Before Width: | Height: | Size: 341 B After Width: | Height: | Size: 440 B |
|
@ -294,14 +294,26 @@ extern "C"
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_wallet(char *path, char *password, int32_t nettype)
|
bool load_wallet(char *path, char *password, int32_t nettype)
|
||||||
{
|
{
|
||||||
nice(19);
|
nice(19);
|
||||||
Monero::NetworkType networkType = static_cast<Monero::NetworkType>(nettype);
|
Monero::NetworkType networkType = static_cast<Monero::NetworkType>(nettype);
|
||||||
Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->openWallet(std::string(path), std::string(password), networkType);
|
Monero::WalletManager *walletManager = Monero::WalletManagerFactory::getWalletManager();
|
||||||
|
Monero::Wallet *wallet = walletManager->openWallet(std::string(path), std::string(password), networkType);
|
||||||
|
int status;
|
||||||
|
std::string errorString;
|
||||||
|
|
||||||
|
wallet->statusWithErrorString(status, errorString);
|
||||||
change_current_wallet(wallet);
|
change_current_wallet(wallet);
|
||||||
|
|
||||||
|
return !(status != Monero::Wallet::Status_Ok || !errorString.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *error_string() {
|
||||||
|
return strdup(get_current_wallet()->errorString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool is_wallet_exist(char *path)
|
bool is_wallet_exist(char *path)
|
||||||
{
|
{
|
||||||
return Monero::WalletManagerFactory::getWalletManager()->walletExists(std::string(path));
|
return Monero::WalletManagerFactory::getWalletManager()->walletExists(std::string(path));
|
||||||
|
|
8
cw_monero/lib/exceptions/wallet_opening_exception.dart
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
class WalletOpeningException implements Exception {
|
||||||
|
WalletOpeningException({this.message});
|
||||||
|
|
||||||
|
final String message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => message;
|
||||||
|
}
|
|
@ -14,7 +14,9 @@ typedef restore_wallet_from_keys = Int8 Function(Pointer<Utf8>, Pointer<Utf8>,
|
||||||
|
|
||||||
typedef is_wallet_exist = Int8 Function(Pointer<Utf8>);
|
typedef is_wallet_exist = Int8 Function(Pointer<Utf8>);
|
||||||
|
|
||||||
typedef load_wallet = Void Function(Pointer<Utf8>, Pointer<Utf8>, Int8);
|
typedef load_wallet = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Int8);
|
||||||
|
|
||||||
|
typedef error_string = Pointer<Utf8> Function();
|
||||||
|
|
||||||
typedef get_filename = Pointer<Utf8> Function();
|
typedef get_filename = Pointer<Utf8> Function();
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,9 @@ typedef RestoreWalletFromKeys = int Function(Pointer<Utf8>, Pointer<Utf8>,
|
||||||
|
|
||||||
typedef IsWalletExist = int Function(Pointer<Utf8>);
|
typedef IsWalletExist = int Function(Pointer<Utf8>);
|
||||||
|
|
||||||
typedef LoadWallet = void Function(Pointer<Utf8>, Pointer<Utf8>, int);
|
typedef LoadWallet = int Function(Pointer<Utf8>, Pointer<Utf8>, int);
|
||||||
|
|
||||||
|
typedef ErrorString = Pointer<Utf8> Function();
|
||||||
|
|
||||||
typedef GetFilename = Pointer<Utf8> Function();
|
typedef GetFilename = Pointer<Utf8> Function();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
|
import 'package:cw_monero/exceptions/wallet_opening_exception.dart';
|
||||||
import 'package:cw_monero/wallet.dart';
|
import 'package:cw_monero/wallet.dart';
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
@ -32,6 +33,10 @@ final loadWalletNative = moneroApi
|
||||||
.lookup<NativeFunction<load_wallet>>('load_wallet')
|
.lookup<NativeFunction<load_wallet>>('load_wallet')
|
||||||
.asFunction<LoadWallet>();
|
.asFunction<LoadWallet>();
|
||||||
|
|
||||||
|
final errorStringNative = moneroApi
|
||||||
|
.lookup<NativeFunction<error_string>>('error_string')
|
||||||
|
.asFunction<ErrorString>();
|
||||||
|
|
||||||
void createWalletSync(
|
void createWalletSync(
|
||||||
{String path, String password, String language, int nettype = 0}) {
|
{String path, String password, String language, int nettype = 0}) {
|
||||||
final pathPointer = Utf8.toUtf8(path);
|
final pathPointer = Utf8.toUtf8(path);
|
||||||
|
@ -136,10 +141,14 @@ void restoreWalletFromKeysSync(
|
||||||
void loadWallet({String path, String password, int nettype = 0}) {
|
void loadWallet({String path, String password, int nettype = 0}) {
|
||||||
final pathPointer = Utf8.toUtf8(path);
|
final pathPointer = Utf8.toUtf8(path);
|
||||||
final passwordPointer = Utf8.toUtf8(password);
|
final passwordPointer = Utf8.toUtf8(password);
|
||||||
|
final loaded = loadWalletNative(pathPointer, passwordPointer, nettype) != 0;
|
||||||
loadWalletNative(pathPointer, passwordPointer, nettype);
|
|
||||||
free(pathPointer);
|
free(pathPointer);
|
||||||
free(passwordPointer);
|
free(passwordPointer);
|
||||||
|
|
||||||
|
if (!loaded) {
|
||||||
|
throw WalletOpeningException(
|
||||||
|
message: convertUTF8ToString(pointer: errorStringNative()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _createWallet(Map<String, dynamic> args) {
|
void _createWallet(Map<String, dynamic> args) {
|
||||||
|
|
|
@ -354,7 +354,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 17;
|
||||||
DEVELOPMENT_TEAM = 32J6BB6VUS;
|
DEVELOPMENT_TEAM = 32J6BB6VUS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -371,7 +371,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 4.0.9;
|
MARKETING_VERSION = 4.1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
@ -494,7 +494,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 17;
|
||||||
DEVELOPMENT_TEAM = 32J6BB6VUS;
|
DEVELOPMENT_TEAM = 32J6BB6VUS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -511,7 +511,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 4.0.9;
|
MARKETING_VERSION = 4.1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
@ -528,7 +528,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 17;
|
||||||
DEVELOPMENT_TEAM = 32J6BB6VUS;
|
DEVELOPMENT_TEAM = 32J6BB6VUS;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
|
@ -545,7 +545,7 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/Flutter",
|
"$(PROJECT_DIR)/Flutter",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 4.0.9;
|
MARKETING_VERSION = 4.1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
PRODUCT_BUNDLE_IDENTIFIER = com.fotolockr.cakewallet;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||||
|
|
25
lib/bitcoin/address_to_output_script.dart
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import 'dart:typed_data';
|
||||||
|
import 'package:bs58check/bs58check.dart' as bs58check;
|
||||||
|
import 'package:bitcoin_flutter/src/utils/constants/op.dart';
|
||||||
|
import 'package:bitcoin_flutter/src/utils/script.dart' as bscript;
|
||||||
|
import 'package:bitcoin_flutter/src/address.dart';
|
||||||
|
|
||||||
|
|
||||||
|
Uint8List p2shAddressToOutputScript(String address) {
|
||||||
|
final decodeBase58 = bs58check.decode(address);
|
||||||
|
final hash = decodeBase58.sublist(1);
|
||||||
|
return bscript.compile(<dynamic>[OPS['OP_HASH160'], hash, OPS['OP_EQUAL']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8List addressToOutputScript(String address) {
|
||||||
|
try {
|
||||||
|
// FIXME: improve validation for p2sh addresses
|
||||||
|
if (address.startsWith('3')) {
|
||||||
|
return p2shAddressToOutputScript(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Address.addressToOutputScript(address);
|
||||||
|
} catch (_) {
|
||||||
|
return Uint8List(0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,25 @@
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:quiver/core.dart';
|
||||||
|
|
||||||
class BitcoinAddressRecord {
|
class BitcoinAddressRecord {
|
||||||
BitcoinAddressRecord(this.address, {this.label, this.index});
|
BitcoinAddressRecord(this.address, {this.index});
|
||||||
|
|
||||||
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
|
factory BitcoinAddressRecord.fromJSON(String jsonSource) {
|
||||||
final decoded = json.decode(jsonSource) as Map;
|
final decoded = json.decode(jsonSource) as Map;
|
||||||
|
|
||||||
return BitcoinAddressRecord(decoded['address'] as String,
|
return BitcoinAddressRecord(decoded['address'] as String,
|
||||||
label: decoded['label'] as String, index: decoded['index'] as int);
|
index: decoded['index'] as int);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object o) =>
|
||||||
|
o is BitcoinAddressRecord && address == o.address;
|
||||||
|
|
||||||
final String address;
|
final String address;
|
||||||
int index;
|
int index;
|
||||||
String label;
|
|
||||||
|
|
||||||
String toJSON() =>
|
@override
|
||||||
json.encode({'label': label, 'address': address, 'index': index});
|
int get hashCode => address.hashCode;
|
||||||
|
|
||||||
|
String toJSON() => json.encode({'address': address, 'index': index});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:cake_wallet/entities/crypto_amount_format.dart';
|
import 'package:cake_wallet/entities/crypto_amount_format.dart';
|
||||||
|
|
||||||
|
@ -7,10 +9,32 @@ final bitcoinAmountFormat = NumberFormat()
|
||||||
..maximumFractionDigits = bitcoinAmountLength
|
..maximumFractionDigits = bitcoinAmountLength
|
||||||
..minimumFractionDigits = 1;
|
..minimumFractionDigits = 1;
|
||||||
|
|
||||||
String bitcoinAmountToString({int amount}) =>
|
String bitcoinAmountToString({int amount}) => bitcoinAmountFormat.format(
|
||||||
bitcoinAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider));
|
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider));
|
||||||
|
|
||||||
double bitcoinAmountToDouble({int amount}) => cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
|
double bitcoinAmountToDouble({int amount}) =>
|
||||||
|
cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider);
|
||||||
|
|
||||||
int doubleToBitcoinAmount(double amount) =>
|
int stringDoubleToBitcoinAmount(String amount) {
|
||||||
(amount * bitcoinAmountDivider).toInt();
|
final splitted = amount.split('');
|
||||||
|
final dotIndex = amount.indexOf('.');
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
|
||||||
|
for (var i = 0; i < splitted.length; i++) {
|
||||||
|
try {
|
||||||
|
if (dotIndex == i) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final char = splitted[i];
|
||||||
|
final multiplier = dotIndex < i
|
||||||
|
? bitcoinAmountDivider ~/ pow(10, (i - dotIndex))
|
||||||
|
: (bitcoinAmountDivider * pow(10, (dotIndex - i -1))).toInt();
|
||||||
|
final num = int.parse(char) * multiplier;
|
||||||
|
result += num;
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,8 @@ import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart';
|
||||||
import 'package:cake_wallet/entities/balance.dart';
|
import 'package:cake_wallet/entities/balance.dart';
|
||||||
|
|
||||||
class BitcoinBalance extends Balance {
|
class BitcoinBalance extends Balance {
|
||||||
const BitcoinBalance({@required this.confirmed, @required this.unconfirmed}) : super();
|
const BitcoinBalance({@required this.confirmed, @required this.unconfirmed})
|
||||||
|
: super(confirmed, unconfirmed);
|
||||||
|
|
||||||
factory BitcoinBalance.fromJSON(String jsonSource) {
|
factory BitcoinBalance.fromJSON(String jsonSource) {
|
||||||
if (jsonSource == null) {
|
if (jsonSource == null) {
|
||||||
|
@ -22,13 +23,12 @@ class BitcoinBalance extends Balance {
|
||||||
final int confirmed;
|
final int confirmed;
|
||||||
final int unconfirmed;
|
final int unconfirmed;
|
||||||
|
|
||||||
int get total => confirmed + unconfirmed;
|
@override
|
||||||
|
String get formattedAvailableBalance => bitcoinAmountToString(amount: confirmed);
|
||||||
|
|
||||||
String get confirmedFormatted => bitcoinAmountToString(amount: confirmed);
|
@override
|
||||||
|
String get formattedAdditionalBalance =>
|
||||||
String get unconfirmedFormatted => bitcoinAmountToString(amount: unconfirmed);
|
bitcoinAmountToString(amount: unconfirmed);
|
||||||
|
|
||||||
String get totalFormatted => bitcoinAmountToString(amount: total);
|
|
||||||
|
|
||||||
String toJSON() =>
|
String toJSON() =>
|
||||||
json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed});
|
json.encode({'confirmed': confirmed, 'unconfirmed': unconfirmed});
|
||||||
|
|
2308
lib/bitcoin/bitcoin_mnemonic.dart
Normal file
5
lib/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
class BitcoinMnemonicIsIncorrectException implements Exception {
|
||||||
|
@override
|
||||||
|
String toString() =>
|
||||||
|
'Bitcoin mnemonic has incorrect format. Mnemonic should contain 12 words separated by space.';
|
||||||
|
}
|
|
@ -4,6 +4,6 @@ class BitcoinTransactionCredentials {
|
||||||
BitcoinTransactionCredentials(this.address, this.amount, this.priority);
|
BitcoinTransactionCredentials(this.address, this.amount, this.priority);
|
||||||
|
|
||||||
final String address;
|
final String address;
|
||||||
final double amount;
|
final String amount;
|
||||||
TransactionPriority priority;
|
TransactionPriority priority;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ abstract class BitcoinTransactionHistoryBase
|
||||||
|
|
||||||
return historiesWithDetails.fold<Map<String, BitcoinTransactionInfo>>(
|
return historiesWithDetails.fold<Map<String, BitcoinTransactionInfo>>(
|
||||||
<String, BitcoinTransactionInfo>{}, (acc, tx) {
|
<String, BitcoinTransactionInfo>{}, (acc, tx) {
|
||||||
acc[tx.id] = tx;
|
acc[tx.id] = acc[tx.id]?.updated(tx) ?? tx;
|
||||||
return acc;
|
return acc;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -103,10 +103,6 @@ abstract class BitcoinTransactionHistoryBase
|
||||||
|
|
||||||
Future<void> save() async {
|
Future<void> save() async {
|
||||||
final data = json.encode({'height': _height, 'transactions': transactions});
|
final data = json.encode({'height': _height, 'transactions': transactions});
|
||||||
|
|
||||||
print('data');
|
|
||||||
print(data);
|
|
||||||
|
|
||||||
await writeData(path: path, password: _password, data: data);
|
await writeData(path: path, password: _password, data: data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +164,9 @@ abstract class BitcoinTransactionHistoryBase
|
||||||
});
|
});
|
||||||
|
|
||||||
_height = content['height'] as int;
|
_height = content['height'] as int;
|
||||||
} catch (_) {}
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateOrInsert(BitcoinTransactionInfo transaction) {
|
void _updateOrInsert(BitcoinTransactionInfo transaction) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
||||||
{@required String id,
|
{@required String id,
|
||||||
@required int height,
|
@required int height,
|
||||||
@required int amount,
|
@required int amount,
|
||||||
|
@required int fee,
|
||||||
@required TransactionDirection direction,
|
@required TransactionDirection direction,
|
||||||
@required bool isPending,
|
@required bool isPending,
|
||||||
@required DateTime date,
|
@required DateTime date,
|
||||||
|
@ -19,6 +20,7 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
|
this.fee = fee;
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
this.date = date;
|
this.date = date;
|
||||||
this.isPending = isPending;
|
this.isPending = isPending;
|
||||||
|
@ -36,37 +38,42 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
||||||
: DateTime.now();
|
: DateTime.now();
|
||||||
final confirmations = obj['confirmations'] as int ?? 0;
|
final confirmations = obj['confirmations'] as int ?? 0;
|
||||||
var direction = TransactionDirection.incoming;
|
var direction = TransactionDirection.incoming;
|
||||||
|
var inputsAmount = 0;
|
||||||
|
var amount = 0;
|
||||||
|
var totalOutAmount = 0;
|
||||||
|
|
||||||
for (dynamic vin in vins) {
|
for (dynamic vin in vins) {
|
||||||
final vout = vin['vout'] as int;
|
final vout = vin['vout'] as int;
|
||||||
final out = vin['tx']['vout'][vout] as Map;
|
final out = vin['tx']['vout'][vout] as Map;
|
||||||
final outAddresses =
|
final outAddresses =
|
||||||
(out['scriptPubKey']['addresses'] as List<Object>)?.toSet();
|
(out['scriptPubKey']['addresses'] as List<Object>)?.toSet();
|
||||||
|
inputsAmount += stringDoubleToBitcoinAmount((out['value'] as double ?? 0).toString());
|
||||||
|
|
||||||
if (outAddresses?.intersection(addressesSet)?.isNotEmpty ?? false) {
|
if (outAddresses?.intersection(addressesSet)?.isNotEmpty ?? false) {
|
||||||
direction = TransactionDirection.outgoing;
|
direction = TransactionDirection.outgoing;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final amount = vout.fold(0, (int acc, dynamic out) {
|
for (dynamic out in vout) {
|
||||||
final outAddresses =
|
final outAddresses =
|
||||||
out['scriptPubKey']['addresses'] as List<Object> ?? [];
|
out['scriptPubKey']['addresses'] as List<Object> ?? [];
|
||||||
final ntrs = outAddresses.toSet().intersection(addressesSet);
|
final ntrs = outAddresses.toSet().intersection(addressesSet);
|
||||||
var amount = acc;
|
final value = stringDoubleToBitcoinAmount((out['value'] as double ?? 0.0).toString());
|
||||||
|
totalOutAmount += value;
|
||||||
|
|
||||||
if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) ||
|
if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) ||
|
||||||
(direction == TransactionDirection.outgoing && ntrs.isEmpty)) {
|
(direction == TransactionDirection.outgoing && ntrs.isEmpty)) {
|
||||||
amount += doubleToBitcoinAmount(out['value'] as double ?? 0.0);
|
amount += value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return amount;
|
final fee = inputsAmount - totalOutAmount;
|
||||||
});
|
|
||||||
|
|
||||||
return BitcoinTransactionInfo(
|
return BitcoinTransactionInfo(
|
||||||
id: id,
|
id: id,
|
||||||
height: height,
|
height: height,
|
||||||
isPending: false,
|
isPending: false,
|
||||||
|
fee: fee,
|
||||||
direction: direction,
|
direction: direction,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
date: date,
|
date: date,
|
||||||
|
@ -101,6 +108,7 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
||||||
id: tx.getId(),
|
id: tx.getId(),
|
||||||
height: height,
|
height: height,
|
||||||
isPending: false,
|
isPending: false,
|
||||||
|
fee: null,
|
||||||
direction: TransactionDirection.incoming,
|
direction: TransactionDirection.incoming,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
date: date,
|
date: date,
|
||||||
|
@ -112,6 +120,7 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
||||||
id: data['id'] as String,
|
id: data['id'] as String,
|
||||||
height: data['height'] as int,
|
height: data['height'] as int,
|
||||||
amount: data['amount'] as int,
|
amount: data['amount'] as int,
|
||||||
|
fee: data['fee'] as int,
|
||||||
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
direction: parseTransactionDirectionFromInt(data['direction'] as int),
|
||||||
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
date: DateTime.fromMillisecondsSinceEpoch(data['date'] as int),
|
||||||
isPending: data['isPending'] as bool,
|
isPending: data['isPending'] as bool,
|
||||||
|
@ -124,12 +133,29 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
||||||
String amountFormatted() =>
|
String amountFormatted() =>
|
||||||
'${formatAmount(bitcoinAmountToString(amount: amount))} BTC';
|
'${formatAmount(bitcoinAmountToString(amount: amount))} BTC';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String feeFormatted() => fee != null
|
||||||
|
? '${formatAmount(bitcoinAmountToString(amount: fee))} BTC'
|
||||||
|
: '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String fiatAmount() => _fiatAmount ?? '';
|
String fiatAmount() => _fiatAmount ?? '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
void changeFiatAmount(String amount) => _fiatAmount = formatAmount(amount);
|
||||||
|
|
||||||
|
BitcoinTransactionInfo updated(BitcoinTransactionInfo info) {
|
||||||
|
return BitcoinTransactionInfo(
|
||||||
|
id: id,
|
||||||
|
height: info.height,
|
||||||
|
amount: info.amount,
|
||||||
|
fee: info.fee,
|
||||||
|
direction: direction ?? info.direction,
|
||||||
|
date: date ?? info.date,
|
||||||
|
isPending: isPending ?? info.isPending,
|
||||||
|
confirmations: info.confirmations);
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final m = <String, dynamic>{};
|
final m = <String, dynamic>{};
|
||||||
m['id'] = id;
|
m['id'] = id;
|
||||||
|
@ -139,6 +165,7 @@ class BitcoinTransactionInfo extends TransactionInfo {
|
||||||
m['date'] = date.millisecondsSinceEpoch;
|
m['date'] = date.millisecondsSinceEpoch;
|
||||||
m['isPending'] = isPending;
|
m['isPending'] = isPending;
|
||||||
m['confirmations'] = confirmations;
|
m['confirmations'] = confirmations;
|
||||||
|
m['fee'] = fee;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'package:cake_wallet/bitcoin/address_to_output_script.dart';
|
||||||
|
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
|
||||||
|
@ -42,10 +43,11 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
BitcoinBalance initialBalance})
|
BitcoinBalance initialBalance})
|
||||||
: balance =
|
: balance =
|
||||||
initialBalance ?? BitcoinBalance(confirmed: 0, unconfirmed: 0),
|
initialBalance ?? BitcoinBalance(confirmed: 0, unconfirmed: 0),
|
||||||
hd = bitcoin.HDWallet.fromSeed(bip39.mnemonicToSeed(mnemonic),
|
hd = bitcoin.HDWallet.fromSeed(mnemonicToSeedBytes(mnemonic),
|
||||||
network: bitcoin.bitcoin),
|
network: bitcoin.bitcoin)
|
||||||
|
.derivePath("m/0'/0"),
|
||||||
addresses = initialAddresses != null
|
addresses = initialAddresses != null
|
||||||
? ObservableList<BitcoinAddressRecord>.of(initialAddresses)
|
? ObservableList<BitcoinAddressRecord>.of(initialAddresses.toSet())
|
||||||
: ObservableList<BitcoinAddressRecord>(),
|
: ObservableList<BitcoinAddressRecord>(),
|
||||||
syncStatus = NotConnectedSyncStatus(),
|
syncStatus = NotConnectedSyncStatus(),
|
||||||
_password = password,
|
_password = password,
|
||||||
|
@ -58,6 +60,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
{@required String password,
|
{@required String password,
|
||||||
@required String name,
|
@required String name,
|
||||||
@required String dirPath,
|
@required String dirPath,
|
||||||
|
@required WalletInfo walletInfo,
|
||||||
String jsonSource}) {
|
String jsonSource}) {
|
||||||
final data = json.decode(jsonSource) as Map;
|
final data = json.decode(jsonSource) as Map;
|
||||||
final mnemonic = data['mnemonic'] as String;
|
final mnemonic = data['mnemonic'] as String;
|
||||||
|
@ -83,7 +86,8 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
name: name,
|
name: name,
|
||||||
accountIndex: accountIndex,
|
accountIndex: accountIndex,
|
||||||
initialAddresses: addresses,
|
initialAddresses: addresses,
|
||||||
initialBalance: balance);
|
initialBalance: balance,
|
||||||
|
walletInfo: walletInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
static BitcoinWallet build(
|
static BitcoinWallet build(
|
||||||
|
@ -91,6 +95,7 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
@required String password,
|
@required String password,
|
||||||
@required String name,
|
@required String name,
|
||||||
@required String dirPath,
|
@required String dirPath,
|
||||||
|
@required WalletInfo walletInfo,
|
||||||
List<BitcoinAddressRecord> initialAddresses,
|
List<BitcoinAddressRecord> initialAddresses,
|
||||||
BitcoinBalance initialBalance,
|
BitcoinBalance initialBalance,
|
||||||
int accountIndex = 0}) {
|
int accountIndex = 0}) {
|
||||||
|
@ -107,7 +112,21 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
accountIndex: accountIndex,
|
accountIndex: accountIndex,
|
||||||
initialAddresses: initialAddresses,
|
initialAddresses: initialAddresses,
|
||||||
initialBalance: initialBalance,
|
initialBalance: initialBalance,
|
||||||
transactionHistory: history);
|
transactionHistory: history,
|
||||||
|
walletInfo: walletInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int feeAmountForPriority(TransactionPriority priority) {
|
||||||
|
switch (priority) {
|
||||||
|
case TransactionPriority.slow:
|
||||||
|
return 6000;
|
||||||
|
case TransactionPriority.regular:
|
||||||
|
return 22080;
|
||||||
|
case TransactionPriority.fast:
|
||||||
|
return 24000;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -148,21 +167,31 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject;
|
Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject;
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
if (addresses.isEmpty) {
|
if (addresses.isEmpty || addresses.length < 33) {
|
||||||
final index = 0;
|
final addressesCount = 33 - addresses.length;
|
||||||
addresses
|
await generateNewAddresses(addressesCount, startIndex: addresses.length);
|
||||||
.add(BitcoinAddressRecord(_getAddress(index: index), index: index));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
address = addresses.first.address;
|
address = addresses[_accountIndex].address;
|
||||||
transactionHistory.wallet = this;
|
transactionHistory.wallet = this;
|
||||||
await transactionHistory.init();
|
await transactionHistory.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<BitcoinAddressRecord> generateNewAddress({String label}) async {
|
@action
|
||||||
|
void nextAddress() {
|
||||||
|
_accountIndex += 1;
|
||||||
|
|
||||||
|
if (_accountIndex >= addresses.length) {
|
||||||
|
_accountIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
address = addresses[_accountIndex].address;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<BitcoinAddressRecord> generateNewAddress() async {
|
||||||
_accountIndex += 1;
|
_accountIndex += 1;
|
||||||
final address = BitcoinAddressRecord(_getAddress(index: _accountIndex),
|
final address = BitcoinAddressRecord(_getAddress(index: _accountIndex),
|
||||||
index: _accountIndex, label: label);
|
index: _accountIndex);
|
||||||
addresses.add(address);
|
addresses.add(address);
|
||||||
|
|
||||||
await save();
|
await save();
|
||||||
|
@ -170,10 +199,24 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateAddress(String address, {String label}) async {
|
Future<List<BitcoinAddressRecord>> generateNewAddresses(int count,
|
||||||
|
{int startIndex = 0}) async {
|
||||||
|
final list = <BitcoinAddressRecord>[];
|
||||||
|
|
||||||
|
for (var i = startIndex; i < count + startIndex; i++) {
|
||||||
|
final address = BitcoinAddressRecord(_getAddress(index: i), index: i);
|
||||||
|
list.add(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
addresses.addAll(list);
|
||||||
|
await save();
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateAddress(String address) async {
|
||||||
for (final addr in addresses) {
|
for (final addr in addresses) {
|
||||||
if (addr.address == address) {
|
if (addr.address == address) {
|
||||||
addr.label = label;
|
|
||||||
await save();
|
await save();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -185,8 +228,10 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
Future<void> startSync() async {
|
Future<void> startSync() async {
|
||||||
try {
|
try {
|
||||||
syncStatus = StartingSyncStatus();
|
syncStatus = StartingSyncStatus();
|
||||||
transactionHistory.updateAsync(
|
transactionHistory.updateAsync(onFinished: () {
|
||||||
onFinished: () => print('transactionHistory update finished!'));
|
print('transactionHistory update finished!');
|
||||||
|
transactionHistory.save();
|
||||||
|
});
|
||||||
_subscribeForUpdates();
|
_subscribeForUpdates();
|
||||||
await _updateBalance();
|
await _updateBalance();
|
||||||
syncStatus = SyncedSyncStatus();
|
syncStatus = SyncedSyncStatus();
|
||||||
|
@ -219,16 +264,20 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
Object credentials) async {
|
Object credentials) async {
|
||||||
final transactionCredentials = credentials as BitcoinTransactionCredentials;
|
final transactionCredentials = credentials as BitcoinTransactionCredentials;
|
||||||
final inputs = <BitcoinUnspent>[];
|
final inputs = <BitcoinUnspent>[];
|
||||||
final fee = _feeMultiplier(transactionCredentials.priority);
|
final fee = feeAmountForPriority(transactionCredentials.priority);
|
||||||
final amount = transactionCredentials.amount != null
|
final amount = transactionCredentials.amount != null
|
||||||
? doubleToBitcoinAmount(transactionCredentials.amount)
|
? stringDoubleToBitcoinAmount(transactionCredentials.amount)
|
||||||
: balance.total - fee;
|
: balance.confirmed - fee;
|
||||||
final totalAmount = amount + fee;
|
final totalAmount = amount + fee;
|
||||||
final txb = bitcoin.TransactionBuilder(network: bitcoin.bitcoin);
|
final txb = bitcoin.TransactionBuilder(network: bitcoin.bitcoin);
|
||||||
var leftAmount = totalAmount;
|
|
||||||
final changeAddress = address;
|
final changeAddress = address;
|
||||||
|
var leftAmount = totalAmount;
|
||||||
var totalInputAmount = 0;
|
var totalInputAmount = 0;
|
||||||
|
|
||||||
|
if (totalAmount > balance.confirmed) {
|
||||||
|
throw BitcoinTransactionWrongBalanceException();
|
||||||
|
}
|
||||||
|
|
||||||
final unspent = addresses.map((address) => eclient
|
final unspent = addresses.map((address) => eclient
|
||||||
.getListUnspentWithAddress(address.address)
|
.getListUnspentWithAddress(address.address)
|
||||||
.then((unspent) => unspent
|
.then((unspent) => unspent
|
||||||
|
@ -238,9 +287,8 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
final utxs = await unptsFutures;
|
final utxs = await unptsFutures;
|
||||||
|
|
||||||
for (final utx in utxs) {
|
for (final utx in utxs) {
|
||||||
final inAmount = utx.value > totalAmount ? totalAmount : utx.value;
|
leftAmount = leftAmount - utx.value;
|
||||||
leftAmount = leftAmount - inAmount;
|
totalInputAmount += utx.value;
|
||||||
totalInputAmount += inAmount;
|
|
||||||
inputs.add(utx);
|
inputs.add(utx);
|
||||||
|
|
||||||
if (leftAmount <= 0) {
|
if (leftAmount <= 0) {
|
||||||
|
@ -279,7 +327,8 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
txb.addOutput(transactionCredentials.address, amount);
|
txb.addOutput(
|
||||||
|
addressToOutputScript(transactionCredentials.address), amount);
|
||||||
|
|
||||||
if (changeValue > 0) {
|
if (changeValue > 0) {
|
||||||
txb.addOutput(changeAddress, changeValue);
|
txb.addOutput(changeAddress, changeValue);
|
||||||
|
@ -295,7 +344,10 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
|
|
||||||
return PendingBitcoinTransaction(txb.build(),
|
return PendingBitcoinTransaction(txb.build(),
|
||||||
eclient: eclient, amount: amount, fee: fee)
|
eclient: eclient, amount: amount, fee: fee)
|
||||||
..addListener((transaction) => transactionHistory.addOne(transaction));
|
..addListener((transaction) async {
|
||||||
|
transactionHistory.addOne(transaction);
|
||||||
|
await _updateBalance();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
String toJSON() => json.encode({
|
String toJSON() => json.encode({
|
||||||
|
@ -307,11 +359,13 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
double calculateEstimatedFee(TransactionPriority priority) =>
|
double calculateEstimatedFee(TransactionPriority priority) =>
|
||||||
bitcoinAmountToDouble(amount: _feeMultiplier(priority));
|
bitcoinAmountToDouble(amount: feeAmountForPriority(priority));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> save() async =>
|
Future<void> save() async {
|
||||||
await write(path: path, password: _password, data: toJSON());
|
await write(path: path, password: _password, data: toJSON());
|
||||||
|
await transactionHistory.save();
|
||||||
|
}
|
||||||
|
|
||||||
bitcoin.ECPair keyPairFor({@required int index}) =>
|
bitcoin.ECPair keyPairFor({@required int index}) =>
|
||||||
generateKeyPair(hd: hd, index: index);
|
generateKeyPair(hd: hd, index: index);
|
||||||
|
@ -321,13 +375,18 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
// FIXME: Unimplemented
|
// FIXME: Unimplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void close() async {
|
||||||
|
await eclient.close();
|
||||||
|
}
|
||||||
|
|
||||||
void _subscribeForUpdates() {
|
void _subscribeForUpdates() {
|
||||||
scriptHashes.forEach((sh) async {
|
scriptHashes.forEach((sh) async {
|
||||||
await _scripthashesUpdateSubject[sh]?.close();
|
await _scripthashesUpdateSubject[sh]?.close();
|
||||||
_scripthashesUpdateSubject[sh] = eclient.scripthashUpdate(sh);
|
_scripthashesUpdateSubject[sh] = eclient.scripthashUpdate(sh);
|
||||||
_scripthashesUpdateSubject[sh].listen((event) async {
|
_scripthashesUpdateSubject[sh].listen((event) async {
|
||||||
transactionHistory.updateAsync();
|
|
||||||
await _updateBalance();
|
await _updateBalance();
|
||||||
|
transactionHistory.updateAsync();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -352,17 +411,4 @@ abstract class BitcoinWalletBase extends WalletBase<BitcoinBalance> with Store {
|
||||||
|
|
||||||
String _getAddress({@required int index}) =>
|
String _getAddress({@required int index}) =>
|
||||||
generateAddress(hd: hd, index: 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,21 +1,23 @@
|
||||||
import 'package:cake_wallet/core/wallet_credentials.dart';
|
import 'package:cake_wallet/core/wallet_credentials.dart';
|
||||||
|
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||||
|
|
||||||
class BitcoinNewWalletCredentials extends WalletCredentials {
|
class BitcoinNewWalletCredentials extends WalletCredentials {
|
||||||
BitcoinNewWalletCredentials({String name}) : super(name: name);
|
BitcoinNewWalletCredentials({String name, WalletInfo walletInfo})
|
||||||
|
: super(name: name, walletInfo: walletInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
|
class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
|
||||||
BitcoinRestoreWalletFromSeedCredentials(
|
BitcoinRestoreWalletFromSeedCredentials(
|
||||||
{String name, String password, this.mnemonic})
|
{String name, String password, this.mnemonic, WalletInfo walletInfo})
|
||||||
: super(name: name, password: password);
|
: super(name: name, password: password, walletInfo: walletInfo);
|
||||||
|
|
||||||
final String mnemonic;
|
final String mnemonic;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
|
class BitcoinRestoreWalletFromWIFCredentials extends WalletCredentials {
|
||||||
BitcoinRestoreWalletFromWIFCredentials(
|
BitcoinRestoreWalletFromWIFCredentials(
|
||||||
{String name, String password, this.wif})
|
{String name, String password, this.wif, WalletInfo walletInfo})
|
||||||
: super(name: name, password: password);
|
: super(name: name, password: password, walletInfo: walletInfo);
|
||||||
|
|
||||||
final String wif;
|
final String wif;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,34 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:bip39/bip39.dart' as bip39;
|
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart';
|
||||||
|
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic_is_incorrect_exception.dart';
|
||||||
import 'package:cake_wallet/bitcoin/file.dart';
|
import 'package:cake_wallet/bitcoin/file.dart';
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin_wallet_creation_credentials.dart';
|
||||||
|
import 'package:cake_wallet/core/wallet_base.dart';
|
||||||
import 'package:cake_wallet/core/wallet_service.dart';
|
import 'package:cake_wallet/core/wallet_service.dart';
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart';
|
||||||
import 'package:cake_wallet/entities/pathForWallet.dart';
|
import 'package:cake_wallet/entities/pathForWallet.dart';
|
||||||
|
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
class BitcoinWalletService extends WalletService<
|
class BitcoinWalletService extends WalletService<
|
||||||
BitcoinNewWalletCredentials,
|
BitcoinNewWalletCredentials,
|
||||||
BitcoinRestoreWalletFromSeedCredentials,
|
BitcoinRestoreWalletFromSeedCredentials,
|
||||||
BitcoinRestoreWalletFromWIFCredentials> {
|
BitcoinRestoreWalletFromWIFCredentials> {
|
||||||
|
BitcoinWalletService(this.walletInfoSource);
|
||||||
|
|
||||||
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials) async {
|
Future<BitcoinWallet> create(BitcoinNewWalletCredentials credentials) async {
|
||||||
final dirPath = await pathForWalletDir(
|
final dirPath = await pathForWalletDir(
|
||||||
type: WalletType.bitcoin, name: credentials.name);
|
type: WalletType.bitcoin, name: credentials.name);
|
||||||
final wallet = BitcoinWalletBase.build(
|
final wallet = BitcoinWalletBase.build(
|
||||||
dirPath: dirPath,
|
dirPath: dirPath,
|
||||||
mnemonic: bip39.generateMnemonic(),
|
mnemonic: generateMnemonic(),
|
||||||
password: credentials.password,
|
password: credentials.password,
|
||||||
name: credentials.name);
|
name: credentials.name,
|
||||||
|
walletInfo: credentials.walletInfo);
|
||||||
await wallet.save();
|
await wallet.save();
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
||||||
|
@ -37,11 +46,15 @@ class BitcoinWalletService extends WalletService<
|
||||||
await pathForWalletDir(name: name, type: WalletType.bitcoin);
|
await pathForWalletDir(name: name, type: WalletType.bitcoin);
|
||||||
final walletPath = '$walletDirPath/$name';
|
final walletPath = '$walletDirPath/$name';
|
||||||
final walletJSONRaw = await read(path: walletPath, password: password);
|
final walletJSONRaw = await read(path: walletPath, password: password);
|
||||||
|
final walletInfo = walletInfoSource.values.firstWhere(
|
||||||
|
(info) => info.id == WalletBase.idFor(name, WalletType.bitcoin),
|
||||||
|
orElse: () => null);
|
||||||
final wallet = BitcoinWalletBase.fromJSON(
|
final wallet = BitcoinWalletBase.fromJSON(
|
||||||
password: password,
|
password: password,
|
||||||
name: name,
|
name: name,
|
||||||
dirPath: walletDirPath,
|
dirPath: walletDirPath,
|
||||||
jsonSource: walletJSONRaw);
|
jsonSource: walletJSONRaw,
|
||||||
|
walletInfo: walletInfo);
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
||||||
return wallet;
|
return wallet;
|
||||||
|
@ -62,13 +75,18 @@ class BitcoinWalletService extends WalletService<
|
||||||
@override
|
@override
|
||||||
Future<BitcoinWallet> restoreFromSeed(
|
Future<BitcoinWallet> restoreFromSeed(
|
||||||
BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
BitcoinRestoreWalletFromSeedCredentials credentials) async {
|
||||||
|
if (!validateMnemonic(credentials.mnemonic)) {
|
||||||
|
throw BitcoinMnemonicIsIncorrectException();
|
||||||
|
}
|
||||||
|
|
||||||
final dirPath = await pathForWalletDir(
|
final dirPath = await pathForWalletDir(
|
||||||
type: WalletType.bitcoin, name: credentials.name);
|
type: WalletType.bitcoin, name: credentials.name);
|
||||||
final wallet = BitcoinWalletBase.build(
|
final wallet = BitcoinWalletBase.build(
|
||||||
dirPath: dirPath,
|
dirPath: dirPath,
|
||||||
name: credentials.name,
|
name: credentials.name,
|
||||||
password: credentials.password,
|
password: credentials.password,
|
||||||
mnemonic: credentials.mnemonic);
|
mnemonic: credentials.mnemonic,
|
||||||
|
walletInfo: credentials.walletInfo);
|
||||||
await wallet.save();
|
await wallet.save();
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,8 @@ class ElectrumClient {
|
||||||
ElectrumClient()
|
ElectrumClient()
|
||||||
: _id = 0,
|
: _id = 0,
|
||||||
_isConnected = false,
|
_isConnected = false,
|
||||||
_tasks = {};
|
_tasks = {},
|
||||||
|
unterminatedString = '';
|
||||||
|
|
||||||
static const connectionTimeout = Duration(seconds: 5);
|
static const connectionTimeout = Duration(seconds: 5);
|
||||||
static const aliveTimerDuration = Duration(seconds: 2);
|
static const aliveTimerDuration = Duration(seconds: 2);
|
||||||
|
@ -49,6 +50,7 @@ class ElectrumClient {
|
||||||
final Map<String, SocketTask> _tasks;
|
final Map<String, SocketTask> _tasks;
|
||||||
bool _isConnected;
|
bool _isConnected;
|
||||||
Timer _aliveTimer;
|
Timer _aliveTimer;
|
||||||
|
String unterminatedString;
|
||||||
|
|
||||||
Future<void> connectToUri(String uri) async {
|
Future<void> connectToUri(String uri) async {
|
||||||
final splittedUri = uri.split(':');
|
final splittedUri = uri.split(':');
|
||||||
|
@ -73,26 +75,50 @@ class ElectrumClient {
|
||||||
|
|
||||||
socket.listen((Uint8List event) {
|
socket.listen((Uint8List event) {
|
||||||
try {
|
try {
|
||||||
final jsoned =
|
final response =
|
||||||
json.decode(utf8.decode(event.toList())) as Map<String, Object>;
|
json.decode(utf8.decode(event.toList())) as Map<String, Object>;
|
||||||
print(jsoned);
|
_handleResponse(response);
|
||||||
final method = jsoned['method'];
|
} on FormatException catch (e) {
|
||||||
final id = jsoned['id'] as String;
|
final msg = e.message.toLowerCase();
|
||||||
final params = jsoned['result'];
|
|
||||||
|
|
||||||
if (method is String) {
|
if (e.source is String) {
|
||||||
_methodHandler(method: method, request: jsoned);
|
unterminatedString += e.source as String;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.contains("not a subtype of type")) {
|
||||||
|
unterminatedString += e.source as String;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_finish(id, params);
|
if (isJSONStringCorrect(unterminatedString)) {
|
||||||
|
final response =
|
||||||
|
json.decode(unterminatedString) as Map<String, Object>;
|
||||||
|
_handleResponse(response);
|
||||||
|
unterminatedString = '';
|
||||||
|
}
|
||||||
|
} on TypeError catch (e) {
|
||||||
|
if (!e.toString().contains('Map<String, Object>')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final source = utf8.decode(event.toList());
|
||||||
|
unterminatedString += source;
|
||||||
|
|
||||||
|
if (isJSONStringCorrect(unterminatedString)) {
|
||||||
|
final response =
|
||||||
|
json.decode(unterminatedString) as Map<String, Object>;
|
||||||
|
_handleResponse(response);
|
||||||
|
unterminatedString = null;
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e.toString());
|
||||||
}
|
}
|
||||||
}, onError: (Object error) {
|
}, onError: (Object error) {
|
||||||
print(error.toString());
|
print(error.toString());
|
||||||
_setIsConnected(false);
|
_setIsConnected(false);
|
||||||
}, onDone: () => _setIsConnected(false));
|
}, onDone: () {
|
||||||
|
_setIsConnected(false);
|
||||||
|
});
|
||||||
keepAlive();
|
keepAlive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +129,7 @@ class ElectrumClient {
|
||||||
|
|
||||||
Future<void> ping() async {
|
Future<void> ping() async {
|
||||||
try {
|
try {
|
||||||
// await callWithTimeout(method: 'server.ping');
|
await callWithTimeout(method: 'server.ping');
|
||||||
_setIsConnected(true);
|
_setIsConnected(true);
|
||||||
} on RequestFailedTimeoutException catch (_) {
|
} on RequestFailedTimeoutException catch (_) {
|
||||||
_setIsConnected(false);
|
_setIsConnected(false);
|
||||||
|
@ -209,16 +235,20 @@ class ElectrumClient {
|
||||||
|
|
||||||
Future<Map<String, Object>> getTransactionExpanded(
|
Future<Map<String, Object>> getTransactionExpanded(
|
||||||
{@required String hash}) async {
|
{@required String hash}) async {
|
||||||
final originalTx = await getTransactionRaw(hash: hash);
|
try {
|
||||||
final vins = originalTx['vin'] as List<Object>;
|
final originalTx = await getTransactionRaw(hash: hash);
|
||||||
|
final vins = originalTx['vin'] as List<Object>;
|
||||||
|
|
||||||
for (dynamic vin in vins) {
|
for (dynamic vin in vins) {
|
||||||
if (vin is Map<String, Object>) {
|
if (vin is Map<String, Object>) {
|
||||||
vin['tx'] = await getTransactionRaw(hash: vin['txid'] as String);
|
vin['tx'] = await getTransactionRaw(hash: vin['txid'] as String);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return originalTx;
|
return originalTx;
|
||||||
|
} catch (_) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> broadcastTransaction(
|
Future<String> broadcastTransaction(
|
||||||
|
@ -228,7 +258,7 @@ class ElectrumClient {
|
||||||
if (result is String) {
|
if (result is String) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
print(result);
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -256,11 +286,13 @@ class ElectrumClient {
|
||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
BehaviorSubject<Object> scripthashUpdate(String scripthash) =>
|
BehaviorSubject<Object> scripthashUpdate(String scripthash) {
|
||||||
subscribe<Object>(
|
_id += 1;
|
||||||
id: 'blockchain.scripthash.subscribe:$scripthash',
|
return subscribe<Object>(
|
||||||
method: 'blockchain.scripthash.subscribe',
|
id: 'blockchain.scripthash.subscribe:$scripthash',
|
||||||
params: [scripthash]);
|
method: 'blockchain.scripthash.subscribe',
|
||||||
|
params: [scripthash]);
|
||||||
|
}
|
||||||
|
|
||||||
BehaviorSubject<T> subscribe<T>(
|
BehaviorSubject<T> subscribe<T>(
|
||||||
{@required String id,
|
{@required String id,
|
||||||
|
@ -273,7 +305,8 @@ class ElectrumClient {
|
||||||
return subscription;
|
return subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> call({String method, List<Object> params = const []}) {
|
Future<dynamic> call({String method, List<Object> params = const []}) async {
|
||||||
|
await Future<void>.delayed(Duration(milliseconds: 100));
|
||||||
final completer = Completer<dynamic>();
|
final completer = Completer<dynamic>();
|
||||||
_id += 1;
|
_id += 1;
|
||||||
final id = _id;
|
final id = _id;
|
||||||
|
@ -307,6 +340,12 @@ class ElectrumClient {
|
||||||
socket.write(jsonrpc(method: method, id: _id, params: params));
|
socket.write(jsonrpc(method: method, id: _id, params: params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> close() async {
|
||||||
|
_aliveTimer.cancel();
|
||||||
|
await socket.close();
|
||||||
|
onConnectionStatusChange = null;
|
||||||
|
}
|
||||||
|
|
||||||
void _regisryTask(int id, Completer completer) => _tasks[id.toString()] =
|
void _regisryTask(int id, Completer completer) => _tasks[id.toString()] =
|
||||||
SocketTask(completer: completer, isSubscription: false);
|
SocketTask(completer: completer, isSubscription: false);
|
||||||
|
|
||||||
|
@ -351,6 +390,29 @@ class ElectrumClient {
|
||||||
|
|
||||||
_isConnected = isConnected;
|
_isConnected = isConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _handleResponse(Map<String, Object> response) {
|
||||||
|
final method = response['method'];
|
||||||
|
final id = response['id'] as String;
|
||||||
|
final result = response['result'];
|
||||||
|
|
||||||
|
if (method is String) {
|
||||||
|
_methodHandler(method: method, request: response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_finish(id, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: move me
|
||||||
|
bool isJSONStringCorrect(String source) {
|
||||||
|
try {
|
||||||
|
json.decode(source);
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RequestFailedTimeoutException implements Exception {
|
class RequestFailedTimeoutException implements Exception {
|
||||||
|
|
|
@ -16,6 +16,7 @@ class PendingBitcoinTransaction with PendingTransaction {
|
||||||
final int amount;
|
final int amount;
|
||||||
final int fee;
|
final int fee;
|
||||||
|
|
||||||
|
@override
|
||||||
String get id => _tx.getId();
|
String get id => _tx.getId();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -205,8 +205,8 @@ class BackupService {
|
||||||
_sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey),
|
_sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey),
|
||||||
PreferencesKey.shouldSaveRecipientAddressKey: _sharedPreferences
|
PreferencesKey.shouldSaveRecipientAddressKey: _sharedPreferences
|
||||||
.getBool(PreferencesKey.shouldSaveRecipientAddressKey),
|
.getBool(PreferencesKey.shouldSaveRecipientAddressKey),
|
||||||
PreferencesKey.currentDarkTheme:
|
PreferencesKey.isDarkThemeLegacy:
|
||||||
_sharedPreferences.getBool(PreferencesKey.currentDarkTheme),
|
_sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy),
|
||||||
PreferencesKey.currentPinLength:
|
PreferencesKey.currentPinLength:
|
||||||
_sharedPreferences.getInt(PreferencesKey.currentPinLength),
|
_sharedPreferences.getInt(PreferencesKey.currentPinLength),
|
||||||
PreferencesKey.currentTransactionPriorityKey: _sharedPreferences
|
PreferencesKey.currentTransactionPriorityKey: _sharedPreferences
|
||||||
|
@ -219,7 +219,7 @@ class BackupService {
|
||||||
_sharedPreferences.getString(PreferencesKey.currentLanguageCode),
|
_sharedPreferences.getString(PreferencesKey.currentLanguageCode),
|
||||||
PreferencesKey.displayActionListModeKey:
|
PreferencesKey.displayActionListModeKey:
|
||||||
_sharedPreferences.getInt(PreferencesKey.displayActionListModeKey),
|
_sharedPreferences.getInt(PreferencesKey.displayActionListModeKey),
|
||||||
'currentTheme': _sharedPreferences.getInt('current_theme')
|
PreferencesKey.currentTheme: _sharedPreferences.getInt(PreferencesKey.currentTheme)
|
||||||
// FIX-ME: Unnamed constant.
|
// FIX-ME: Unnamed constant.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:bip39/src/wordlists/english.dart' as bitcoin_english;
|
import 'package:cake_wallet/bitcoin/bitcoin_mnemonic.dart' as bitcoin_electrum;
|
||||||
import 'package:cake_wallet/core/validator.dart';
|
import 'package:cake_wallet/core/validator.dart';
|
||||||
import 'package:cake_wallet/entities/mnemonic_item.dart';
|
import 'package:cake_wallet/entities/mnemonic_item.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
|
@ -64,7 +64,7 @@ class SeedValidator extends Validator<MnemonicItem> {
|
||||||
|
|
||||||
static List<String> getBitcoinWordList(String language) {
|
static List<String> getBitcoinWordList(String language) {
|
||||||
assert(language.toLowerCase() == LanguageList.english.toLowerCase());
|
assert(language.toLowerCase() == LanguageList.english.toLowerCase());
|
||||||
return bitcoin_english.WORDLIST;
|
return bitcoin_electrum.englishWordlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/entities/balance.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||||
import 'package:cake_wallet/core/pending_transaction.dart';
|
import 'package:cake_wallet/core/pending_transaction.dart';
|
||||||
|
@ -9,7 +10,7 @@ import 'package:cake_wallet/entities/sync_status.dart';
|
||||||
import 'package:cake_wallet/entities/node.dart';
|
import 'package:cake_wallet/entities/node.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
|
|
||||||
abstract class WalletBase<BalaceType> {
|
abstract class WalletBase<BalaceType extends Balance> {
|
||||||
WalletBase(this.walletInfo);
|
WalletBase(this.walletInfo);
|
||||||
|
|
||||||
static String idFor(String name, WalletType type) =>
|
static String idFor(String name, WalletType type) =>
|
||||||
|
@ -52,4 +53,6 @@ abstract class WalletBase<BalaceType> {
|
||||||
Future<void> save();
|
Future<void> save();
|
||||||
|
|
||||||
Future<void> rescan({int height});
|
Future<void> rescan({int height});
|
||||||
|
|
||||||
|
void close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:cake_wallet/entities/wallet_info.dart';
|
import 'package:cake_wallet/entities/wallet_info.dart';
|
||||||
|
|
||||||
abstract class WalletCredentials {
|
abstract class WalletCredentials {
|
||||||
WalletCredentials({this.name, this.password, this.height});
|
WalletCredentials({this.name, this.password, this.height, this.walletInfo});
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
final int height;
|
final int height;
|
||||||
|
|
38
lib/di.dart
|
@ -19,6 +19,7 @@ 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_confirm_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
|
import 'package:cake_wallet/src/screens/exchange_trade/exchange_trade_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
import 'package:cake_wallet/src/screens/faq/faq_page.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_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/nodes/nodes_list_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||||
|
@ -32,6 +33,7 @@ import 'package:cake_wallet/src/screens/send/send_template_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/change_language.dart';
|
import 'package:cake_wallet/src/screens/settings/change_language.dart';
|
||||||
import 'package:cake_wallet/src/screens/settings/settings.dart';
|
import 'package:cake_wallet/src/screens/settings/settings.dart';
|
||||||
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
|
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
|
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
|
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
|
||||||
|
@ -63,6 +65,8 @@ import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.
|
||||||
import 'package:cake_wallet/view_model/rescan_view_model.dart';
|
import 'package:cake_wallet/view_model/rescan_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
|
import 'package:cake_wallet/view_model/restore_from_backup_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
|
import 'package:cake_wallet/view_model/setup_pin_code_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
|
||||||
|
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_edit_or_create_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/auth_view_model.dart';
|
import 'package:cake_wallet/view_model/auth_view_model.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||||
|
@ -340,7 +344,8 @@ Future setup(
|
||||||
(ContactRecord contact, _) =>
|
(ContactRecord contact, _) =>
|
||||||
ContactViewModel(contactSource, contact: contact));
|
ContactViewModel(contactSource, contact: contact));
|
||||||
|
|
||||||
getIt.registerFactory(() => ContactListViewModel(contactSource));
|
getIt.registerFactory(
|
||||||
|
() => ContactListViewModel(contactSource, walletInfoSource));
|
||||||
|
|
||||||
getIt.registerFactoryParam<ContactListPage, bool, void>(
|
getIt.registerFactoryParam<ContactListPage, bool, void>(
|
||||||
(bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(),
|
(bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(),
|
||||||
|
@ -368,7 +373,8 @@ Future setup(
|
||||||
getIt.get<AppStore>().wallet,
|
getIt.get<AppStore>().wallet,
|
||||||
tradesSource,
|
tradesSource,
|
||||||
getIt.get<ExchangeTemplateStore>(),
|
getIt.get<ExchangeTemplateStore>(),
|
||||||
getIt.get<TradesStore>()));
|
getIt.get<TradesStore>(),
|
||||||
|
getIt.get<AppStore>().settingsStore));
|
||||||
|
|
||||||
getIt.registerFactory(() => ExchangeTradeViewModel(
|
getIt.registerFactory(() => ExchangeTradeViewModel(
|
||||||
wallet: getIt.get<AppStore>().wallet,
|
wallet: getIt.get<AppStore>().wallet,
|
||||||
|
@ -389,7 +395,7 @@ Future setup(
|
||||||
|
|
||||||
getIt.registerFactory(() => MoneroWalletService(walletInfoSource));
|
getIt.registerFactory(() => MoneroWalletService(walletInfoSource));
|
||||||
|
|
||||||
getIt.registerFactory(() => BitcoinWalletService());
|
getIt.registerFactory(() => BitcoinWalletService(walletInfoSource));
|
||||||
|
|
||||||
getIt.registerFactoryParam<WalletService, WalletType, void>(
|
getIt.registerFactoryParam<WalletService, WalletType, void>(
|
||||||
(WalletType param1, __) {
|
(WalletType param1, __) {
|
||||||
|
@ -428,13 +434,28 @@ Future setup(
|
||||||
getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) =>
|
getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) =>
|
||||||
WalletRestorePage(getIt.get<WalletRestoreViewModel>(param1: type)));
|
WalletRestorePage(getIt.get<WalletRestoreViewModel>(param1: type)));
|
||||||
|
|
||||||
|
getIt
|
||||||
|
.registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>(
|
||||||
|
(TransactionInfo transactionInfo, _) => TransactionDetailsViewModel(
|
||||||
|
transactionInfo: transactionInfo,
|
||||||
|
transactionDescriptionBox: transactionDescriptionBox,
|
||||||
|
settingsStore: getIt.get<SettingsStore>()));
|
||||||
|
|
||||||
getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>(
|
getIt.registerFactoryParam<TransactionDetailsPage, TransactionInfo, void>(
|
||||||
(TransactionInfo transactionInfo, _) => TransactionDetailsPage(
|
(TransactionInfo transactionInfo, _) => TransactionDetailsPage(
|
||||||
transactionInfo,
|
transactionDetailsViewModel:
|
||||||
getIt.get<SettingsStore>().shouldSaveRecipientAddress,
|
getIt.get<TransactionDetailsViewModel>(param1: transactionInfo)));
|
||||||
transactionDescriptionBox));
|
|
||||||
|
|
||||||
getIt.registerFactory(() => PreSeedPage());
|
getIt.registerFactoryParam<NewWalletTypePage,
|
||||||
|
void Function(BuildContext, WalletType), bool>(
|
||||||
|
(para1, param2) => NewWalletTypePage(getIt.get<WalletNewVM>(),
|
||||||
|
onTypeSelected: para1, isNewWallet: param2));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<PreSeedPage, WalletType, void>(
|
||||||
|
(WalletType type, _) => PreSeedPage(type));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<TradeDetailsViewModel, Trade, void>((trade, _) =>
|
||||||
|
TradeDetailsViewModel(tradeForDetails: trade, trades: tradesSource));
|
||||||
|
|
||||||
getIt.registerFactory(() => BackupService(
|
getIt.registerFactory(() => BackupService(
|
||||||
getIt.get<FlutterSecureStorage>(),
|
getIt.get<FlutterSecureStorage>(),
|
||||||
|
@ -463,4 +484,7 @@ Future setup(
|
||||||
|
|
||||||
getIt.registerFactory(
|
getIt.registerFactory(
|
||||||
() => RestoreFromBackupPage(getIt.get<RestoreFromBackupViewModel>()));
|
() => RestoreFromBackupPage(getIt.get<RestoreFromBackupViewModel>()));
|
||||||
|
|
||||||
|
getIt.registerFactoryParam<TradeDetailsPage, Trade, void>((Trade trade, _) =>
|
||||||
|
TradeDetailsPage(getIt.get<TradeDetailsViewModel>(param1: trade)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
abstract class Balance {
|
abstract class Balance {
|
||||||
const Balance();
|
const Balance(this.available, this.additional);
|
||||||
|
|
||||||
|
final int available;
|
||||||
|
|
||||||
|
final int additional;
|
||||||
|
|
||||||
|
String get formattedAvailableBalance;
|
||||||
|
|
||||||
|
String get formattedAdditionalBalance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,16 @@ class BalanceDisplayMode extends EnumerableItem<int> with Serializable<int> {
|
||||||
: super(title: title, raw: raw);
|
: super(title: title, raw: raw);
|
||||||
|
|
||||||
static const all = [
|
static const all = [
|
||||||
BalanceDisplayMode.fullBalance,
|
BalanceDisplayMode.hiddenBalance,
|
||||||
BalanceDisplayMode.availableBalance,
|
BalanceDisplayMode.displayableBalance,
|
||||||
BalanceDisplayMode.hiddenBalance
|
|
||||||
];
|
];
|
||||||
static const fullBalance = BalanceDisplayMode(raw: 0, title: 'Full Balance');
|
static const fullBalance = BalanceDisplayMode(raw: 0, title: 'Full Balance');
|
||||||
static const availableBalance =
|
static const availableBalance =
|
||||||
BalanceDisplayMode(raw: 1, title: 'Available Balance');
|
BalanceDisplayMode(raw: 1, title: 'Available Balance');
|
||||||
static const hiddenBalance =
|
static const hiddenBalance =
|
||||||
BalanceDisplayMode(raw: 2, title: 'Hidden Balance');
|
BalanceDisplayMode(raw: 2, title: 'Hidden Balance');
|
||||||
|
static const displayableBalance =
|
||||||
|
BalanceDisplayMode(raw: 3, title: 'Displayable Balance');
|
||||||
|
|
||||||
static BalanceDisplayMode deserialize({int raw}) {
|
static BalanceDisplayMode deserialize({int raw}) {
|
||||||
switch (raw) {
|
switch (raw) {
|
||||||
|
@ -25,6 +26,8 @@ class BalanceDisplayMode extends EnumerableItem<int> with Serializable<int> {
|
||||||
return availableBalance;
|
return availableBalance;
|
||||||
case 2:
|
case 2:
|
||||||
return hiddenBalance;
|
return hiddenBalance;
|
||||||
|
case 3:
|
||||||
|
return displayableBalance;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +42,8 @@ class BalanceDisplayMode extends EnumerableItem<int> with Serializable<int> {
|
||||||
return S.current.xmr_available_balance;
|
return S.current.xmr_available_balance;
|
||||||
case BalanceDisplayMode.hiddenBalance:
|
case BalanceDisplayMode.hiddenBalance:
|
||||||
return S.current.xmr_hidden;
|
return S.current.xmr_hidden;
|
||||||
|
case BalanceDisplayMode.displayableBalance:
|
||||||
|
return S.current.displayable;
|
||||||
default:
|
default:
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
9
lib/entities/contact_base.dart
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||||
|
|
||||||
|
abstract class ContactBase {
|
||||||
|
String name;
|
||||||
|
|
||||||
|
String address;
|
||||||
|
|
||||||
|
CryptoCurrency type;
|
||||||
|
}
|
|
@ -3,21 +3,27 @@ import 'package:mobx/mobx.dart';
|
||||||
import 'package:cake_wallet/entities/contact.dart';
|
import 'package:cake_wallet/entities/contact.dart';
|
||||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||||
import 'package:cake_wallet/entities/record.dart';
|
import 'package:cake_wallet/entities/record.dart';
|
||||||
|
import 'package:cake_wallet/entities/contact_base.dart';
|
||||||
|
|
||||||
part 'contact_record.g.dart';
|
part 'contact_record.g.dart';
|
||||||
|
|
||||||
class ContactRecord = ContactRecordBase with _$ContactRecord;
|
class ContactRecord = ContactRecordBase with _$ContactRecord;
|
||||||
|
|
||||||
abstract class ContactRecordBase extends Record<Contact> with Store {
|
abstract class ContactRecordBase extends Record<Contact>
|
||||||
|
with Store
|
||||||
|
implements ContactBase {
|
||||||
ContactRecordBase(Box<Contact> source, Contact original)
|
ContactRecordBase(Box<Contact> source, Contact original)
|
||||||
: super(source, original);
|
: super(source, original);
|
||||||
|
|
||||||
|
@override
|
||||||
@observable
|
@observable
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
|
@override
|
||||||
@observable
|
@observable
|
||||||
String address;
|
String address;
|
||||||
|
|
||||||
|
@override
|
||||||
@observable
|
@observable
|
||||||
CryptoCurrency type;
|
CryptoCurrency type;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import 'dart:io' show Platform;
|
import 'dart:io' show File, Platform;
|
||||||
|
import 'package:cake_wallet/core/key_service.dart';
|
||||||
|
import 'package:cake_wallet/di.dart';
|
||||||
|
import 'package:cake_wallet/entities/pathForWallet.dart';
|
||||||
|
import 'package:cake_wallet/monero/monero_wallet_service.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
@ -45,7 +49,7 @@ Future defaultSettingsMigration(
|
||||||
FiatCurrency.usd.toString());
|
FiatCurrency.usd.toString());
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(
|
||||||
PreferencesKey.currentTransactionPriorityKey,
|
PreferencesKey.currentTransactionPriorityKey,
|
||||||
TransactionPriority.standart.raw);
|
TransactionPriority.standard.raw);
|
||||||
await sharedPreferences.setInt(
|
await sharedPreferences.setInt(
|
||||||
PreferencesKey.currentBalanceDisplayModeKey,
|
PreferencesKey.currentBalanceDisplayModeKey,
|
||||||
BalanceDisplayMode.availableBalance.raw);
|
BalanceDisplayMode.availableBalance.raw);
|
||||||
|
@ -73,6 +77,14 @@ Future defaultSettingsMigration(
|
||||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
await addAddressesForMoneroWallets(walletInfoSource);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
await updateDisplayModes(sharedPreferences);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -120,7 +132,7 @@ Future<void> changeMoneroCurrentNodeToDefault(
|
||||||
}
|
}
|
||||||
|
|
||||||
Node getBitcoinDefaultElectrumServer({@required Box<Node> nodes}) {
|
Node getBitcoinDefaultElectrumServer({@required Box<Node> nodes}) {
|
||||||
final uri = 'electrumx.cakewallet.com:50002';
|
final uri = 'electrum.cakewallet.com:50002';
|
||||||
|
|
||||||
return nodes.values
|
return nodes.values
|
||||||
.firstWhere((Node node) => node.uri == uri, orElse: () => null) ??
|
.firstWhere((Node node) => node.uri == uri, orElse: () => null) ??
|
||||||
|
@ -189,3 +201,34 @@ Future<void> addBitcoinElectrumServerList({@required Box<Node> nodes}) async {
|
||||||
final serverList = await loadElectrumServerList();
|
final serverList = await loadElectrumServerList();
|
||||||
await nodes.addAll(serverList);
|
await nodes.addAll(serverList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> addAddressesForMoneroWallets(
|
||||||
|
Box<WalletInfo> walletInfoSource) async {
|
||||||
|
final moneroWalletsInfo =
|
||||||
|
walletInfoSource.values.where((info) => info.type == WalletType.monero);
|
||||||
|
moneroWalletsInfo.forEach((info) async {
|
||||||
|
try {
|
||||||
|
final walletPath =
|
||||||
|
await pathForWallet(name: info.name, type: WalletType.monero);
|
||||||
|
final addressFilePath = '$walletPath.address.txt';
|
||||||
|
final addressFile = File(addressFilePath);
|
||||||
|
|
||||||
|
if (!addressFile.existsSync()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final addressText = await addressFile.readAsString();
|
||||||
|
info.address = addressText;
|
||||||
|
await info.save();
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> updateDisplayModes(SharedPreferences sharedPreferences) async {
|
||||||
|
final currentBalanceDisplayMode =
|
||||||
|
sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey);
|
||||||
|
final balanceDisplayMode = currentBalanceDisplayMode < 2 ? 3 : 2;
|
||||||
|
await sharedPreferences.setInt(PreferencesKey.currentBalanceDisplayModeKey, balanceDisplayMode);
|
||||||
|
}
|
||||||
|
|
|
@ -19,5 +19,5 @@ Future<void> loadCurrentWallet() async {
|
||||||
await getIt.get<KeyService>().getWalletPassword(walletName: name);
|
await getIt.get<KeyService>().getWalletPassword(walletName: name);
|
||||||
final _service = getIt.get<WalletService>(param1: type);
|
final _service = getIt.get<WalletService>(param1: type);
|
||||||
final wallet = await _service.openWallet(name, password);
|
final wallet = await _service.openWallet(name, password);
|
||||||
appStore.wallet = wallet;
|
appStore.changeCurrentWallet(wallet);
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,9 +39,9 @@ Future<List<Node>> loadElectrumServerList() async {
|
||||||
|
|
||||||
Future resetToDefault(Box<Node> nodeSource) async {
|
Future resetToDefault(Box<Node> nodeSource) async {
|
||||||
final moneroNodes = await loadDefaultNodes();
|
final moneroNodes = await loadDefaultNodes();
|
||||||
// final bitcoinElectrumServerList = await loadElectrumServerList();
|
final bitcoinElectrumServerList = await loadElectrumServerList();
|
||||||
// final nodes = moneroNodes + bitcoinElectrumServerList;
|
final nodes = moneroNodes + bitcoinElectrumServerList;
|
||||||
|
|
||||||
await nodeSource.clear();
|
await nodeSource.clear();
|
||||||
await nodeSource.addAll(moneroNodes);
|
await nodeSource.addAll(nodes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,8 @@ class PreferencesKey {
|
||||||
static const shouldSaveRecipientAddressKey = 'save_recipient_address';
|
static const shouldSaveRecipientAddressKey = 'save_recipient_address';
|
||||||
static const allowBiometricalAuthenticationKey =
|
static const allowBiometricalAuthenticationKey =
|
||||||
'allow_biometrical_authentication';
|
'allow_biometrical_authentication';
|
||||||
static const currentDarkTheme = 'dark_theme';
|
static const currentTheme = 'current_theme';
|
||||||
|
static const isDarkThemeLegacy = 'dark_theme';
|
||||||
static const displayActionListModeKey = 'display_list_mode';
|
static const displayActionListModeKey = 'display_list_mode';
|
||||||
static const currentPinLength = 'current_pin_length';
|
static const currentPinLength = 'current_pin_length';
|
||||||
static const currentLanguageCode = 'language_code';
|
static const currentLanguageCode = 'language_code';
|
||||||
|
|
|
@ -4,7 +4,7 @@ part 'transaction_description.g.dart';
|
||||||
|
|
||||||
@HiveType(typeId: 2)
|
@HiveType(typeId: 2)
|
||||||
class TransactionDescription extends HiveObject {
|
class TransactionDescription extends HiveObject {
|
||||||
TransactionDescription({this.id, this.recipientAddress});
|
TransactionDescription({this.id, this.recipientAddress, this.transactionNote});
|
||||||
|
|
||||||
static const boxName = 'TransactionDescriptions';
|
static const boxName = 'TransactionDescriptions';
|
||||||
static const boxKey = 'transactionDescriptionsBoxKey';
|
static const boxKey = 'transactionDescriptionsBoxKey';
|
||||||
|
@ -14,4 +14,9 @@ class TransactionDescription extends HiveObject {
|
||||||
|
|
||||||
@HiveField(1)
|
@HiveField(1)
|
||||||
String recipientAddress;
|
String recipientAddress;
|
||||||
|
|
||||||
|
@HiveField(2)
|
||||||
|
String transactionNote;
|
||||||
|
|
||||||
|
String get note => transactionNote ?? '';
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:cake_wallet/utils/mobx.dart';
|
||||||
abstract class TransactionInfo extends Object with Keyable {
|
abstract class TransactionInfo extends Object with Keyable {
|
||||||
String id;
|
String id;
|
||||||
int amount;
|
int amount;
|
||||||
|
int fee;
|
||||||
TransactionDirection direction;
|
TransactionDirection direction;
|
||||||
bool isPending;
|
bool isPending;
|
||||||
DateTime date;
|
DateTime date;
|
||||||
|
@ -11,6 +12,7 @@ abstract class TransactionInfo extends Object with Keyable {
|
||||||
int confirmations;
|
int confirmations;
|
||||||
String amountFormatted();
|
String amountFormatted();
|
||||||
String fiatAmount();
|
String fiatAmount();
|
||||||
|
String feeFormatted();
|
||||||
void changeFiatAmount(String amount);
|
void changeFiatAmount(String amount);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/entities/enumerable_item.dart';
|
import 'package:cake_wallet/entities/enumerable_item.dart';
|
||||||
|
|
||||||
|
@ -17,7 +18,23 @@ class TransactionPriority extends EnumerableItem<int> with Serializable<int> {
|
||||||
static const medium = TransactionPriority(title: 'Medium', raw: 2);
|
static const medium = TransactionPriority(title: 'Medium', raw: 2);
|
||||||
static const fast = TransactionPriority(title: 'Fast', raw: 3);
|
static const fast = TransactionPriority(title: 'Fast', raw: 3);
|
||||||
static const fastest = TransactionPriority(title: 'Fastest', raw: 4);
|
static const fastest = TransactionPriority(title: 'Fastest', raw: 4);
|
||||||
static const standart = slow;
|
static const standard = slow;
|
||||||
|
|
||||||
|
|
||||||
|
static List<TransactionPriority> forWalletType(WalletType type) {
|
||||||
|
switch (type) {
|
||||||
|
case WalletType.monero:
|
||||||
|
return TransactionPriority.all;
|
||||||
|
case WalletType.bitcoin:
|
||||||
|
return [
|
||||||
|
TransactionPriority.slow,
|
||||||
|
TransactionPriority.regular,
|
||||||
|
TransactionPriority.fast
|
||||||
|
];
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static TransactionPriority deserialize({int raw}) {
|
static TransactionPriority deserialize({int raw}) {
|
||||||
switch (raw) {
|
switch (raw) {
|
||||||
|
|
15
lib/entities/wallet_contact.dart
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import 'package:cake_wallet/entities/contact_base.dart';
|
||||||
|
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||||
|
|
||||||
|
class WalletContact implements ContactBase {
|
||||||
|
WalletContact(this.address, this.name, this.type);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String address;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String name;
|
||||||
|
|
||||||
|
@override
|
||||||
|
CryptoCurrency type;
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ part 'wallet_info.g.dart';
|
||||||
@HiveType(typeId: 4)
|
@HiveType(typeId: 4)
|
||||||
class WalletInfo extends HiveObject {
|
class WalletInfo extends HiveObject {
|
||||||
WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight,
|
WalletInfo(this.id, this.name, this.type, this.isRecovery, this.restoreHeight,
|
||||||
this.timestamp, this.dirPath, this.path);
|
this.timestamp, this.dirPath, this.path, this.address);
|
||||||
|
|
||||||
factory WalletInfo.external(
|
factory WalletInfo.external(
|
||||||
{@required String id,
|
{@required String id,
|
||||||
|
@ -17,9 +17,10 @@ class WalletInfo extends HiveObject {
|
||||||
@required int restoreHeight,
|
@required int restoreHeight,
|
||||||
@required DateTime date,
|
@required DateTime date,
|
||||||
@required String dirPath,
|
@required String dirPath,
|
||||||
@required String path}) {
|
@required String path,
|
||||||
|
@required String address}) {
|
||||||
return WalletInfo(id, name, type, isRecovery, restoreHeight,
|
return WalletInfo(id, name, type, isRecovery, restoreHeight,
|
||||||
date.millisecondsSinceEpoch ?? 0, dirPath, path);
|
date.millisecondsSinceEpoch ?? 0, dirPath, path, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const boxName = 'WalletInfo';
|
static const boxName = 'WalletInfo';
|
||||||
|
@ -48,5 +49,8 @@ class WalletInfo extends HiveObject {
|
||||||
@HiveField(7)
|
@HiveField(7)
|
||||||
String path;
|
String path;
|
||||||
|
|
||||||
|
@HiveField(8)
|
||||||
|
String address;
|
||||||
|
|
||||||
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
|
DateTime get date => DateTime.fromMillisecondsSinceEpoch(timestamp);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
|
||||||
part 'wallet_type.g.dart';
|
part 'wallet_type.g.dart';
|
||||||
|
@ -48,3 +49,25 @@ String walletTypeToString(WalletType type) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String walletTypeToDisplayName(WalletType type) {
|
||||||
|
switch (type) {
|
||||||
|
case WalletType.monero:
|
||||||
|
return 'Monero';
|
||||||
|
case WalletType.bitcoin:
|
||||||
|
return 'Bitcoin (Electrum)';
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CryptoCurrency walletTypeToCryptoCurrency(WalletType type) {
|
||||||
|
switch (type) {
|
||||||
|
case WalletType.monero:
|
||||||
|
return CryptoCurrency.xmr;
|
||||||
|
case WalletType.bitcoin:
|
||||||
|
return CryptoCurrency.btc;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -27,11 +27,14 @@ class XMRTOExchangeProvider extends ExchangeProvider {
|
||||||
static const _orderParameterUriSuffix = '/order_parameter_query';
|
static const _orderParameterUriSuffix = '/order_parameter_query';
|
||||||
static const _orderStatusUriSuffix = '/order_status_query/';
|
static const _orderStatusUriSuffix = '/order_status_query/';
|
||||||
static const _orderCreateUriSuffix = '/order_create/';
|
static const _orderCreateUriSuffix = '/order_create/';
|
||||||
|
static const _headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'User-Agent': userAgent
|
||||||
|
};
|
||||||
|
|
||||||
static Future<bool> _checkIsAvailable() async {
|
static Future<bool> _checkIsAvailable() async {
|
||||||
const url = originalApiUri + _orderParameterUriSuffix;
|
const url = originalApiUri + _orderParameterUriSuffix;
|
||||||
final response =
|
final response = await get(url, headers: _headers);
|
||||||
await get(url, headers: {'Content-Type': 'application/json'});
|
|
||||||
return !(response.statusCode == 403);
|
return !(response.statusCode == 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,9 +94,8 @@ class XMRTOExchangeProvider extends ExchangeProvider {
|
||||||
Future<Trade> createTrade({TradeRequest request}) async {
|
Future<Trade> createTrade({TradeRequest request}) async {
|
||||||
final _request = request as XMRTOTradeRequest;
|
final _request = request as XMRTOTradeRequest;
|
||||||
final url = originalApiUri + _orderCreateUriSuffix;
|
final url = originalApiUri + _orderCreateUriSuffix;
|
||||||
final _amount = _request.isBTCRequest
|
final _amount =
|
||||||
? _request.receiveAmount
|
_request.isBTCRequest ? _request.receiveAmount : _request.amount;
|
||||||
: _request.amount;
|
|
||||||
|
|
||||||
final _amountCurrency = _request.isBTCRequest
|
final _amountCurrency = _request.isBTCRequest
|
||||||
? _request.to.toString()
|
? _request.to.toString()
|
||||||
|
@ -112,8 +114,8 @@ class XMRTOExchangeProvider extends ExchangeProvider {
|
||||||
'amount_currency': _amountCurrency,
|
'amount_currency': _amountCurrency,
|
||||||
'btc_dest_address': _request.address
|
'btc_dest_address': _request.address
|
||||||
};
|
};
|
||||||
final response = await post(url,
|
final response =
|
||||||
headers: {'Content-Type': 'application/json'}, body: json.encode(body));
|
await post(url, headers: _headers, body: json.encode(body));
|
||||||
|
|
||||||
if (response.statusCode != 201) {
|
if (response.statusCode != 201) {
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
|
@ -141,13 +143,10 @@ class XMRTOExchangeProvider extends ExchangeProvider {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Trade> findTradeById({@required String id}) async {
|
Future<Trade> findTradeById({@required String id}) async {
|
||||||
const headers = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'User-Agent': userAgent
|
|
||||||
};
|
|
||||||
final url = originalApiUri + _orderStatusUriSuffix;
|
final url = originalApiUri + _orderStatusUriSuffix;
|
||||||
final body = {'uuid': id};
|
final body = {'uuid': id};
|
||||||
final response = await post(url, headers: headers, body: json.encode(body));
|
final response =
|
||||||
|
await post(url, headers: _headers, body: json.encode(body));
|
||||||
|
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
if (response.statusCode == 400) {
|
if (response.statusCode == 400) {
|
||||||
|
@ -210,8 +209,7 @@ class XMRTOExchangeProvider extends ExchangeProvider {
|
||||||
Future<double> _fetchRates() async {
|
Future<double> _fetchRates() async {
|
||||||
try {
|
try {
|
||||||
final url = originalApiUri + _orderParameterUriSuffix;
|
final url = originalApiUri + _orderParameterUriSuffix;
|
||||||
final response =
|
final response = await get(url, headers: _headers);
|
||||||
await get(url, headers: {'Content-Type': 'application/json'});
|
|
||||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||||
final price = double.parse(responseJSON['price'] as String);
|
final price = double.parse(responseJSON['price'] as String);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import 'package:cake_wallet/core/backup.dart';
|
import 'package:cake_wallet/core/backup.dart';
|
||||||
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
|
||||||
|
import 'package:cake_wallet/bitcoin/bitcoin_address_record.dart';
|
||||||
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
|
@ -55,11 +57,11 @@ void main() async {
|
||||||
TransactionDescription.boxName,
|
TransactionDescription.boxName,
|
||||||
encryptionKey: transactionDescriptionsBoxKey);
|
encryptionKey: transactionDescriptionsBoxKey);
|
||||||
final trades =
|
final trades =
|
||||||
await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
|
await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
|
||||||
final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
|
final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
|
||||||
final templates = await Hive.openBox<Template>(Template.boxName);
|
final templates = await Hive.openBox<Template>(Template.boxName);
|
||||||
final exchangeTemplates =
|
final exchangeTemplates =
|
||||||
await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
|
await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
|
||||||
await initialSetup(
|
await initialSetup(
|
||||||
sharedPreferences: await SharedPreferences.getInstance(),
|
sharedPreferences: await SharedPreferences.getInstance(),
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
|
@ -70,7 +72,7 @@ void main() async {
|
||||||
templates: templates,
|
templates: templates,
|
||||||
exchangeTemplates: exchangeTemplates,
|
exchangeTemplates: exchangeTemplates,
|
||||||
transactionDescriptions: transactionDescriptions,
|
transactionDescriptions: transactionDescriptions,
|
||||||
initialMigrationVersion: 4);
|
initialMigrationVersion: 5);
|
||||||
runApp(App());
|
runApp(App());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
runApp(MaterialApp(
|
runApp(MaterialApp(
|
||||||
|
@ -78,7 +80,7 @@ void main() async {
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
body: Container(
|
body: Container(
|
||||||
margin:
|
margin:
|
||||||
EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
|
EdgeInsets.only(top: 50, left: 20, right: 20, bottom: 20),
|
||||||
child: Text(
|
child: Text(
|
||||||
'Error:\n${e.toString()}',
|
'Error:\n${e.toString()}',
|
||||||
style: TextStyle(fontSize: 22),
|
style: TextStyle(fontSize: 22),
|
||||||
|
@ -86,17 +88,16 @@ void main() async {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initialSetup(
|
Future<void> initialSetup({@required SharedPreferences sharedPreferences,
|
||||||
{@required SharedPreferences sharedPreferences,
|
@required Box<Node> nodes,
|
||||||
@required Box<Node> nodes,
|
@required Box<WalletInfo> walletInfoSource,
|
||||||
@required Box<WalletInfo> walletInfoSource,
|
@required Box<Contact> contactSource,
|
||||||
@required Box<Contact> contactSource,
|
@required Box<Trade> tradesSource,
|
||||||
@required Box<Trade> tradesSource,
|
// @required FiatConvertationService fiatConvertationService,
|
||||||
// @required FiatConvertationService fiatConvertationService,
|
@required Box<Template> templates,
|
||||||
@required Box<Template> templates,
|
@required Box<ExchangeTemplate> exchangeTemplates,
|
||||||
@required Box<ExchangeTemplate> exchangeTemplates,
|
@required Box<TransactionDescription> transactionDescriptions,
|
||||||
@required Box<TransactionDescription> transactionDescriptions,
|
int initialMigrationVersion = 6}) async {
|
||||||
int initialMigrationVersion = 5}) async {
|
|
||||||
await defaultSettingsMigration(
|
await defaultSettingsMigration(
|
||||||
version: initialMigrationVersion,
|
version: initialMigrationVersion,
|
||||||
sharedPreferences: sharedPreferences,
|
sharedPreferences: sharedPreferences,
|
||||||
|
@ -124,28 +125,28 @@ class App extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final settingsStore = getIt.get<AppStore>().settingsStore;
|
final settingsStore = getIt
|
||||||
|
.get<AppStore>()
|
||||||
if (settingsStore.theme == null) {
|
.settingsStore;
|
||||||
settingsStore.isDarkTheme = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final statusBarColor = Colors.transparent;
|
final statusBarColor = Colors.transparent;
|
||||||
final statusBarBrightness =
|
|
||||||
settingsStore.isDarkTheme ? Brightness.light : Brightness.dark;
|
|
||||||
final statusBarIconBrightness =
|
|
||||||
settingsStore.isDarkTheme ? Brightness.light : Brightness.dark;
|
|
||||||
final authenticationStore = getIt.get<AuthenticationStore>();
|
final authenticationStore = getIt.get<AuthenticationStore>();
|
||||||
final initialRoute = authenticationStore.state == AuthenticationState.denied
|
final initialRoute = authenticationStore.state == AuthenticationState.denied
|
||||||
? Routes.disclaimer
|
? Routes.disclaimer
|
||||||
: Routes.login;
|
: Routes.login;
|
||||||
|
|
||||||
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
|
||||||
statusBarColor: statusBarColor,
|
|
||||||
statusBarBrightness: statusBarBrightness,
|
|
||||||
statusBarIconBrightness: statusBarIconBrightness));
|
|
||||||
|
|
||||||
return Observer(builder: (BuildContext context) {
|
return Observer(builder: (BuildContext context) {
|
||||||
|
final currentTheme = settingsStore.currentTheme;
|
||||||
|
final statusBarBrightness = currentTheme.type == ThemeType.dark
|
||||||
|
? Brightness.light
|
||||||
|
: Brightness.dark;
|
||||||
|
final statusBarIconBrightness = currentTheme.type == ThemeType.dark
|
||||||
|
? Brightness.light
|
||||||
|
: Brightness.dark;
|
||||||
|
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
|
||||||
|
statusBarColor: statusBarColor,
|
||||||
|
statusBarBrightness: statusBarBrightness,
|
||||||
|
statusBarIconBrightness: statusBarIconBrightness));
|
||||||
|
|
||||||
return Root(
|
return Root(
|
||||||
authenticationStore: authenticationStore,
|
authenticationStore: authenticationStore,
|
||||||
navigatorKey: navigatorKey,
|
navigatorKey: navigatorKey,
|
||||||
|
|
|
@ -1,20 +1,31 @@
|
||||||
|
import 'package:cake_wallet/entities/balance.dart';
|
||||||
|
import 'package:cake_wallet/entities/balance_display_mode.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:cake_wallet/monero/monero_amount_format.dart';
|
import 'package:cake_wallet/monero/monero_amount_format.dart';
|
||||||
|
|
||||||
class MoneroBalance {
|
class MoneroBalance extends Balance {
|
||||||
MoneroBalance({@required this.fullBalance, @required this.unlockedBalance})
|
MoneroBalance({@required this.fullBalance, @required this.unlockedBalance})
|
||||||
: formattedFullBalance = moneroAmountToString(amount: fullBalance),
|
: formattedFullBalance = moneroAmountToString(amount: fullBalance),
|
||||||
formattedUnlockedBalance =
|
formattedUnlockedBalance =
|
||||||
moneroAmountToString(amount: unlockedBalance);
|
moneroAmountToString(amount: unlockedBalance),
|
||||||
|
super(unlockedBalance, fullBalance);
|
||||||
|
|
||||||
MoneroBalance.fromString(
|
MoneroBalance.fromString(
|
||||||
{@required this.formattedFullBalance,
|
{@required this.formattedFullBalance,
|
||||||
@required this.formattedUnlockedBalance})
|
@required this.formattedUnlockedBalance})
|
||||||
: fullBalance = moneroParseAmount(amount: formattedFullBalance),
|
: fullBalance = moneroParseAmount(amount: formattedFullBalance),
|
||||||
unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance);
|
unlockedBalance = moneroParseAmount(amount: formattedUnlockedBalance),
|
||||||
|
super(moneroParseAmount(amount: formattedUnlockedBalance),
|
||||||
|
moneroParseAmount(amount: formattedFullBalance));
|
||||||
|
|
||||||
final int fullBalance;
|
final int fullBalance;
|
||||||
final int unlockedBalance;
|
final int unlockedBalance;
|
||||||
final String formattedFullBalance;
|
final String formattedFullBalance;
|
||||||
final String formattedUnlockedBalance;
|
final String formattedUnlockedBalance;
|
||||||
}
|
|
||||||
|
@override
|
||||||
|
String get formattedAvailableBalance => formattedUnlockedBalance;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get formattedAdditionalBalance => formattedFullBalance;
|
||||||
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
||||||
balance = MoneroBalance(
|
balance = MoneroBalance(
|
||||||
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
|
fullBalance: monero_wallet.getFullBalance(accountIndex: account.id),
|
||||||
unlockedBalance:
|
unlockedBalance:
|
||||||
monero_wallet.getUnlockedBalance(accountIndex: account.id));
|
monero_wallet.getUnlockedBalance(accountIndex: account.id));
|
||||||
subaddressList.update(accountIndex: account.id);
|
subaddressList.update(accountIndex: account.id);
|
||||||
subaddress = subaddressList.subaddresses.first;
|
subaddress = subaddressList.subaddresses.first;
|
||||||
address = subaddress.address;
|
address = subaddress.address;
|
||||||
|
@ -120,6 +120,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
void close() {
|
void close() {
|
||||||
_listener?.stop();
|
_listener?.stop();
|
||||||
_onAccountChangeReaction?.reaction?.dispose();
|
_onAccountChangeReaction?.reaction?.dispose();
|
||||||
|
@ -315,9 +316,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _askForUpdateTransactionHistory() async {
|
Future<void> _askForUpdateTransactionHistory() async =>
|
||||||
await transactionHistory.update();
|
await transactionHistory.update();
|
||||||
}
|
|
||||||
|
|
||||||
int _getFullBalance() =>
|
int _getFullBalance() =>
|
||||||
monero_wallet.getFullBalance(accountIndex: account.id);
|
monero_wallet.getFullBalance(accountIndex: account.id);
|
||||||
|
@ -326,13 +326,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
||||||
monero_wallet.getUnlockedBalance(accountIndex: account.id);
|
monero_wallet.getUnlockedBalance(accountIndex: account.id);
|
||||||
|
|
||||||
Future<void> _afterSyncSave() async {
|
Future<void> _afterSyncSave() async {
|
||||||
if (_isSavingAfterSync) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isSavingAfterSync = true;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (_isSavingAfterSync) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isSavingAfterSync = true;
|
||||||
|
|
||||||
final nowTimestamp = DateTime.now().millisecondsSinceEpoch;
|
final nowTimestamp = DateTime.now().millisecondsSinceEpoch;
|
||||||
final sum = _lastAutosaveTimestamp + _autoAfterSyncSaveInterval;
|
final sum = _lastAutosaveTimestamp + _autoAfterSyncSaveInterval;
|
||||||
|
|
||||||
|
@ -350,13 +350,13 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _afterNewTransactionSave() async {
|
Future<void> _afterNewTransactionSave() async {
|
||||||
if (_isSavingAfterNewTransaction) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isSavingAfterNewTransaction = true;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (_isSavingAfterNewTransaction) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isSavingAfterNewTransaction = true;
|
||||||
|
|
||||||
await save();
|
await save();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
|
@ -366,30 +366,38 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance> with Store {
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onNewBlock(int height, int blocksLeft, double ptc) async {
|
void _onNewBlock(int height, int blocksLeft, double ptc) async {
|
||||||
if (walletInfo.isRecovery) {
|
try {
|
||||||
await _askForUpdateTransactionHistory();
|
|
||||||
_askForUpdateBalance();
|
|
||||||
accountList.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (blocksLeft < 100) {
|
|
||||||
await _askForUpdateTransactionHistory();
|
|
||||||
_askForUpdateBalance();
|
|
||||||
accountList.update();
|
|
||||||
syncStatus = SyncedSyncStatus();
|
|
||||||
await _afterSyncSave();
|
|
||||||
|
|
||||||
if (walletInfo.isRecovery) {
|
if (walletInfo.isRecovery) {
|
||||||
await setAsRecovered();
|
await _askForUpdateTransactionHistory();
|
||||||
|
_askForUpdateBalance();
|
||||||
|
accountList.update();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
syncStatus = SyncingSyncStatus(blocksLeft, ptc);
|
if (blocksLeft < 100) {
|
||||||
|
await _askForUpdateTransactionHistory();
|
||||||
|
_askForUpdateBalance();
|
||||||
|
accountList.update();
|
||||||
|
syncStatus = SyncedSyncStatus();
|
||||||
|
await _afterSyncSave();
|
||||||
|
|
||||||
|
if (walletInfo.isRecovery) {
|
||||||
|
await setAsRecovered();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
syncStatus = SyncingSyncStatus(blocksLeft, ptc);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onNewTransaction() {
|
void _onNewTransaction() {
|
||||||
_askForUpdateTransactionHistory();
|
try {
|
||||||
_askForUpdateBalance();
|
_askForUpdateTransactionHistory();
|
||||||
Timer(Duration(seconds: 1), () => _afterNewTransactionSave());
|
_askForUpdateBalance();
|
||||||
|
Timer(Duration(seconds: 1), () => _afterNewTransactionSave());
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:cake_wallet/core/wallet_base.dart';
|
||||||
import 'package:hive/hive.dart';
|
import 'package:hive/hive.dart';
|
||||||
import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager;
|
import 'package:cw_monero/wallet_manager.dart' as monero_wallet_manager;
|
||||||
import 'package:cw_monero/wallet.dart' as monero_wallet;
|
import 'package:cw_monero/wallet.dart' as monero_wallet;
|
||||||
|
import 'package:cw_monero/exceptions/wallet_opening_exception.dart';
|
||||||
import 'package:cake_wallet/monero/monero_wallet.dart';
|
import 'package:cake_wallet/monero/monero_wallet.dart';
|
||||||
import 'package:cake_wallet/core/wallet_credentials.dart';
|
import 'package:cake_wallet/core/wallet_credentials.dart';
|
||||||
import 'package:cake_wallet/core/wallet_service.dart';
|
import 'package:cake_wallet/core/wallet_service.dart';
|
||||||
|
@ -55,6 +56,18 @@ class MoneroWalletService extends WalletService<
|
||||||
|
|
||||||
final Box<WalletInfo> walletInfoSource;
|
final Box<WalletInfo> walletInfoSource;
|
||||||
|
|
||||||
|
static Future<void> _removeCache(String name) async {
|
||||||
|
final path = await pathForWallet(name: name, type: WalletType.monero);
|
||||||
|
final cacheFile = File(path);
|
||||||
|
|
||||||
|
if (cacheFile.existsSync()) {
|
||||||
|
cacheFile.deleteSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool walletFilesExist(String path) =>
|
||||||
|
!File(path).existsSync() && !File('$path.keys').existsSync();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MoneroWallet> create(MoneroNewWalletCredentials credentials) async {
|
Future<MoneroWallet> create(MoneroNewWalletCredentials credentials) async {
|
||||||
try {
|
try {
|
||||||
|
@ -94,7 +107,7 @@ class MoneroWalletService extends WalletService<
|
||||||
try {
|
try {
|
||||||
final path = await pathForWallet(name: name, type: WalletType.monero);
|
final path = await pathForWallet(name: name, type: WalletType.monero);
|
||||||
|
|
||||||
if (!File(path).existsSync()) {
|
if (walletFilesExist(path)) {
|
||||||
await repairOldAndroidWallet(name);
|
await repairOldAndroidWallet(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,17 +121,9 @@ class MoneroWalletService extends WalletService<
|
||||||
final isValid = wallet.validate();
|
final isValid = wallet.validate();
|
||||||
|
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
// if (wallet.seed?.isNotEmpty ?? false) {
|
await _removeCache(name);
|
||||||
// let restore from seed in this case;
|
wallet.close();
|
||||||
// final seed = wallet.seed;
|
return openWallet(name, password);
|
||||||
// final credentials = MoneroRestoreWalletFromSeedCredentials(
|
|
||||||
// name: name, password: password, mnemonic: seed, height: 2000000)
|
|
||||||
// ..walletInfo = walletInfo;
|
|
||||||
// await remove(name);
|
|
||||||
// return restoreFromSeed(credentials);
|
|
||||||
// }
|
|
||||||
|
|
||||||
throw MoneroWalletLoadingException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await wallet.init();
|
await wallet.init();
|
||||||
|
@ -126,7 +131,15 @@ class MoneroWalletService extends WalletService<
|
||||||
return wallet;
|
return wallet;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// TODO: Implement Exception for wallet list service.
|
// TODO: Implement Exception for wallet list service.
|
||||||
print('MoneroWalletsManager Error: $e');
|
|
||||||
|
if (e.toString().contains('bad_alloc') ||
|
||||||
|
(e is WalletOpeningException &&
|
||||||
|
(e.message == 'std::bad_alloc' ||
|
||||||
|
e.message.contains('bad_alloc')))) {
|
||||||
|
await _removeCache(name);
|
||||||
|
return openWallet(name, password);
|
||||||
|
}
|
||||||
|
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,7 +217,7 @@ class MoneroWalletService extends WalletService<
|
||||||
final dir = Directory(oldAndroidWalletDirPath);
|
final dir = Directory(oldAndroidWalletDirPath);
|
||||||
|
|
||||||
if (!dir.existsSync()) {
|
if (!dir.existsSync()) {
|
||||||
throw MoneroWalletLoadingException();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final newWalletDirPath =
|
final newWalletDirPath =
|
||||||
|
@ -223,7 +236,6 @@ class MoneroWalletService extends WalletService<
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
throw MoneroWalletLoadingException();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ class Palette {
|
||||||
static const Color green = Color.fromRGBO(39, 206, 80, 1.0);
|
static const Color green = Color.fromRGBO(39, 206, 80, 1.0);
|
||||||
static const Color red = Color.fromRGBO(255, 51, 51, 1.0);
|
static const Color red = Color.fromRGBO(255, 51, 51, 1.0);
|
||||||
static const Color darkRed = Color.fromRGBO(204, 38, 38, 1.0);
|
static const Color darkRed = Color.fromRGBO(204, 38, 38, 1.0);
|
||||||
static const Color blueAlice = Color.fromRGBO(231, 240, 253, 1.0);
|
static const Color blueAlice = Color.fromRGBO(229, 247, 255, 1.0);
|
||||||
static const Color lightBlue = Color.fromRGBO(172, 203, 238, 1.0);
|
static const Color lightBlue = Color.fromRGBO(172, 203, 238, 1.0);
|
||||||
static const Color lavender = Color.fromRGBO(237, 245, 252, 1.0);
|
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 oceanBlue = Color.fromRGBO(30, 52, 78, 1.0);
|
||||||
|
@ -13,7 +13,6 @@ class Palette {
|
||||||
static const Color blue = Color.fromRGBO(88, 143, 252, 1.0);
|
static const Color blue = Color.fromRGBO(88, 143, 252, 1.0);
|
||||||
static const Color darkLavender = Color.fromRGBO(229, 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 nightBlue = Color.fromRGBO(46, 57, 96, 1.0);
|
||||||
|
|
||||||
static const Color moderateOrangeYellow = Color.fromRGBO(245, 134, 82, 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 moderateOrange = Color.fromRGBO(235, 117, 63, 1.0);
|
||||||
static const Color shineGreen = Color.fromRGBO(76, 189, 87, 1.0);
|
static const Color shineGreen = Color.fromRGBO(76, 189, 87, 1.0);
|
||||||
|
@ -22,9 +21,8 @@ class Palette {
|
||||||
static const Color royalBlue = Color.fromRGBO(43, 114, 221, 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 lightRed = Color.fromRGBO(227, 87, 87, 1.0);
|
||||||
static const Color persianRed = Color.fromRGBO(206, 55, 55, 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 blueCraiola = Color.fromRGBO(69, 110, 255, 1.0);
|
||||||
|
static const Color blueGreyCraiola = Color.fromRGBO(106, 177, 207, 1.0);
|
||||||
static const Color darkBlueCraiola = Color.fromRGBO(53, 86, 136, 1.0);
|
static const Color darkBlueCraiola = Color.fromRGBO(53, 86, 136, 1.0);
|
||||||
static const Color pinkFlamingo = Color.fromRGBO(240, 60, 243, 1.0);
|
static const Color pinkFlamingo = Color.fromRGBO(240, 60, 243, 1.0);
|
||||||
static const Color redHat = Color.fromRGBO(209, 68, 37, 1.0);
|
static const Color redHat = Color.fromRGBO(209, 68, 37, 1.0);
|
||||||
|
@ -43,26 +41,17 @@ class Palette {
|
||||||
static const Color alizarinRed = Color.fromRGBO(233, 45, 45, 1.0);
|
static const Color alizarinRed = Color.fromRGBO(233, 45, 45, 1.0);
|
||||||
static const Color moderateSlateBlue = Color.fromRGBO(129, 93, 251, 1.0);
|
static const Color moderateSlateBlue = Color.fromRGBO(129, 93, 251, 1.0);
|
||||||
static const Color brightOrange = Color.fromRGBO(255, 102, 0, 1.0);
|
static const Color brightOrange = Color.fromRGBO(255, 102, 0, 1.0);
|
||||||
|
static const Color dullGray = Color.fromRGBO(98, 98, 98, 1.0);
|
||||||
// FIXME: Rename.
|
static const Color protectiveBlue = Color.fromRGBO(33, 148, 255, 1.0);
|
||||||
static const Color eee = Color.fromRGBO(236, 239, 245, 1.0);
|
|
||||||
static const Color xxx = Color.fromRGBO(72, 89, 109, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PaletteDark {
|
class PaletteDark {
|
||||||
//static const Color distantBlue = Color.fromRGBO(70, 85, 133, 1.0); // mainBackgroundColor
|
static const Color lightDistantBlue = Color.fromRGBO(81, 96, 147, 1.0);
|
||||||
static const Color lightDistantBlue = Color.fromRGBO(81, 96, 147, 1.0); // borderCardColor
|
static const Color gray = Color.fromRGBO(140, 153, 201, 1.0);
|
||||||
static const Color gray = Color.fromRGBO(140, 153, 201, 1.0); // walletCardText
|
static const Color pigeonBlue = Color.fromRGBO(91, 112, 146, 1.0);
|
||||||
//static const Color violetBlue = Color.fromRGBO(51, 63, 104, 1.0); // walletCardAddressField
|
static const Color moderateNightBlue = Color.fromRGBO(39, 53, 96, 1.0);
|
||||||
//static const Color moderateBlue = Color.fromRGBO(63, 77, 122, 1.0); // walletCardSubAddressField
|
static const Color headerNightBlue = Color.fromRGBO(41, 52, 84, 1.0);
|
||||||
//static const Color darkNightBlue = Color.fromRGBO(33, 43, 73, 1.0); // historyPanel
|
static const Color moderatePurpleBlue = Color.fromRGBO(57, 74, 95, 1.0);
|
||||||
static const Color pigeonBlue = Color.fromRGBO(91, 112, 146, 1.0); // historyPanelText
|
|
||||||
static const Color moderateNightBlue = Color.fromRGBO(39, 53, 96, 1.0); // historyPanelButton
|
|
||||||
static const Color headerNightBlue = Color.fromRGBO(41, 52, 84, 1.0); // menuHeader
|
|
||||||
//static const Color lightNightBlue = Color.fromRGBO(48, 59, 95, 1.0); // menuList
|
|
||||||
static const Color moderatePurpleBlue = Color.fromRGBO(57, 74, 95, 1.0); // selectButtonText
|
|
||||||
|
|
||||||
// NEW DESIGN
|
|
||||||
static const Color backgroundColor = Color.fromRGBO(25, 35, 60, 1.0);
|
static const Color backgroundColor = Color.fromRGBO(25, 35, 60, 1.0);
|
||||||
static const Color nightBlue = Color.fromRGBO(35, 47, 79, 1.0);
|
static const Color nightBlue = Color.fromRGBO(35, 47, 79, 1.0);
|
||||||
static const Color wildNightBlue = Color.fromRGBO(39, 53, 96, 1.0);
|
static const Color wildNightBlue = Color.fromRGBO(39, 53, 96, 1.0);
|
||||||
|
@ -94,8 +83,5 @@ class PaletteDark {
|
||||||
static const Color deepVioletBlue = Color.fromRGBO(52, 66, 104, 1.0);
|
static const Color deepVioletBlue = Color.fromRGBO(52, 66, 104, 1.0);
|
||||||
static const Color lightPurpleBlue = Color.fromRGBO(120, 133, 170, 1.0);
|
static const Color lightPurpleBlue = Color.fromRGBO(120, 133, 170, 1.0);
|
||||||
static const Color indicatorVioletBlue = Color.fromRGBO(59, 72, 119, 1.0);
|
static const Color indicatorVioletBlue = Color.fromRGBO(59, 72, 119, 1.0);
|
||||||
|
static const Color granite = Color.fromRGBO(48, 51, 60, 1.0);
|
||||||
// FIXME: Rename.
|
|
||||||
static const Color eee = Color.fromRGBO(236, 239, 245, 1.0);
|
|
||||||
static const Color xxx = Color.fromRGBO(72, 89, 109, 1);
|
|
||||||
}
|
}
|
|
@ -12,12 +12,13 @@ Future<void> startFiatRateUpdate(AppStore appStore, SettingsStore settingsStore,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fiatConversionStore.price = await FiatConversionService.fetchPrice(
|
fiatConversionStore.prices[appStore.wallet.currency] =
|
||||||
appStore.wallet.currency, settingsStore.fiatCurrency);
|
await FiatConversionService.fetchPrice(
|
||||||
|
appStore.wallet.currency, settingsStore.fiatCurrency);
|
||||||
|
|
||||||
_timer = Timer.periodic(
|
_timer = Timer.periodic(
|
||||||
Duration(seconds: 30),
|
Duration(seconds: 30),
|
||||||
(_) async => fiatConversionStore.price =
|
(_) async => fiatConversionStore.prices[appStore.wallet.currency] =
|
||||||
await FiatConversionService.fetchPrice(
|
await FiatConversionService.fetchPrice(
|
||||||
appStore.wallet.currency, settingsStore.fiatCurrency));
|
appStore.wallet.currency, settingsStore.fiatCurrency));
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,14 @@ ReactionDisposer _onAuthenticationStateChange;
|
||||||
dynamic loginError;
|
dynamic loginError;
|
||||||
|
|
||||||
void startAuthenticationStateChange(AuthenticationStore authenticationStore,
|
void startAuthenticationStateChange(AuthenticationStore authenticationStore,
|
||||||
@required GlobalKey<NavigatorState> navigatorKey) {
|
GlobalKey<NavigatorState> navigatorKey) {
|
||||||
_onAuthenticationStateChange ??= autorun((_) async {
|
_onAuthenticationStateChange ??= autorun((_) async {
|
||||||
final state = authenticationStore.state;
|
final state = authenticationStore.state;
|
||||||
|
|
||||||
if (state == AuthenticationState.installed) {
|
if (state == AuthenticationState.installed) {
|
||||||
try {
|
try {
|
||||||
await loadCurrentWallet();
|
await loadCurrentWallet();
|
||||||
} catch(e) {
|
} catch (e) {
|
||||||
loginError = e;
|
loginError = e;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,12 +7,13 @@ import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
|
|
||||||
ReactionDisposer _onCurrentFiatCurrencyChangeDisposer;
|
ReactionDisposer _onCurrentFiatCurrencyChangeDisposer;
|
||||||
|
|
||||||
void startCurrentFiatChangeReaction(AppStore appStore, SettingsStore settingsStore, FiatConversionStore fiatConversionStore) {
|
void startCurrentFiatChangeReaction(AppStore appStore,
|
||||||
|
SettingsStore settingsStore, FiatConversionStore fiatConversionStore) {
|
||||||
_onCurrentFiatCurrencyChangeDisposer?.reaction?.dispose();
|
_onCurrentFiatCurrencyChangeDisposer?.reaction?.dispose();
|
||||||
_onCurrentFiatCurrencyChangeDisposer = reaction(
|
_onCurrentFiatCurrencyChangeDisposer = reaction(
|
||||||
(_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async {
|
(_) => settingsStore.fiatCurrency, (FiatCurrency fiatCurrency) async {
|
||||||
final cryptoCurrency = appStore.wallet.currency;
|
final cryptoCurrency = appStore.wallet.currency;
|
||||||
fiatConversionStore.price = await FiatConversionService.fetchPrice(
|
fiatConversionStore.prices[appStore.wallet.currency] =
|
||||||
cryptoCurrency, fiatCurrency);
|
await FiatConversionService.fetchPrice(cryptoCurrency, fiatCurrency);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,9 @@ ReactionDisposer _onCurrentNodeChangeReaction;
|
||||||
|
|
||||||
void startOnCurrentNodeChangeReaction(AppStore appStore) {
|
void startOnCurrentNodeChangeReaction(AppStore appStore) {
|
||||||
_onCurrentNodeChangeReaction?.reaction?.dispose();
|
_onCurrentNodeChangeReaction?.reaction?.dispose();
|
||||||
_onCurrentNodeChangeReaction =
|
appStore.settingsStore.nodes.observe((change) async {
|
||||||
reaction((_) => appStore.settingsStore.currentNode, (Node node) async {
|
|
||||||
try {
|
try {
|
||||||
await appStore.wallet.connectToNode(node: node);
|
await appStore.wallet.connectToNode(node: change.newValue);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/entities/balance.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/di.dart';
|
||||||
|
@ -12,13 +13,14 @@ import 'package:cake_wallet/core/wallet_base.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
|
|
||||||
ReactionDisposer _onCurrentWalletChangeReaction;
|
ReactionDisposer _onCurrentWalletChangeReaction;
|
||||||
|
ReactionDisposer _onCurrentWalletChangeFiatRateUpdateReaction;
|
||||||
|
|
||||||
void startCurrentWalletChangeReaction(AppStore appStore,
|
void startCurrentWalletChangeReaction(AppStore appStore,
|
||||||
SettingsStore settingsStore, FiatConversionStore fiatConversionStore) {
|
SettingsStore settingsStore, FiatConversionStore fiatConversionStore) {
|
||||||
_onCurrentWalletChangeReaction?.reaction?.dispose();
|
_onCurrentWalletChangeReaction?.reaction?.dispose();
|
||||||
|
|
||||||
_onCurrentWalletChangeReaction =
|
_onCurrentWalletChangeReaction =
|
||||||
reaction((_) => appStore.wallet, (WalletBase wallet) async {
|
reaction((_) => appStore.wallet, (WalletBase<Balance> wallet) async {
|
||||||
try {
|
try {
|
||||||
final node = settingsStore.getCurrentNode(wallet.type);
|
final node = settingsStore.getCurrentNode(wallet.type);
|
||||||
startWalletSyncStatusChangeReaction(wallet);
|
startWalletSyncStatusChangeReaction(wallet);
|
||||||
|
@ -30,8 +32,25 @@ void startCurrentWalletChangeReaction(AppStore appStore,
|
||||||
PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
PreferencesKey.currentWalletType, serializeToInt(wallet.type));
|
||||||
await wallet.connectToNode(node: node);
|
await wallet.connectToNode(node: node);
|
||||||
|
|
||||||
fiatConversionStore.price = await FiatConversionService.fetchPrice(
|
if (wallet.walletInfo.address?.isEmpty ?? true) {
|
||||||
wallet.currency, settingsStore.fiatCurrency);
|
wallet.walletInfo.address = wallet.address;
|
||||||
|
|
||||||
|
if (wallet.walletInfo.isInBox) {
|
||||||
|
await wallet.walletInfo.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print(e.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_onCurrentWalletChangeFiatRateUpdateReaction =
|
||||||
|
reaction((_) => appStore.wallet, (WalletBase<Balance> wallet) async {
|
||||||
|
try {
|
||||||
|
fiatConversionStore.prices[wallet.currency] = 0;
|
||||||
|
fiatConversionStore.prices[wallet.currency] =
|
||||||
|
await FiatConversionService.fetchPrice(
|
||||||
|
wallet.currency, settingsStore.fiatCurrency);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e.toString());
|
print(e.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
import 'package:cake_wallet/entities/balance.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:cake_wallet/core/wallet_base.dart';
|
import 'package:cake_wallet/core/wallet_base.dart';
|
||||||
import 'package:cake_wallet/entities/sync_status.dart';
|
import 'package:cake_wallet/entities/sync_status.dart';
|
||||||
|
|
||||||
ReactionDisposer _onWalletSyncStatusChangeReaction;
|
ReactionDisposer _onWalletSyncStatusChangeReaction;
|
||||||
|
|
||||||
void startWalletSyncStatusChangeReaction(WalletBase wallet) {
|
void startWalletSyncStatusChangeReaction(WalletBase<Balance> wallet) {
|
||||||
_onWalletSyncStatusChangeReaction?.reaction?.dispose();
|
_onWalletSyncStatusChangeReaction?.reaction?.dispose();
|
||||||
_onWalletSyncStatusChangeReaction =
|
_onWalletSyncStatusChangeReaction =
|
||||||
reaction((_) => wallet.syncStatus, (SyncStatus status) async {
|
reaction((_) => wallet.syncStatus, (SyncStatus status) async {
|
||||||
|
|
|
@ -65,30 +65,22 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.newWalletFromWelcome:
|
case Routes.newWalletFromWelcome:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<SetupPinCodePage>(param1:
|
builder: (_) => getIt.get<SetupPinCodePage>(
|
||||||
(PinCodeState<PinCodeWidget> context, dynamic _) async {
|
param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
|
||||||
try {
|
Navigator.of(context.context)
|
||||||
context.changeProcessText(S.current.creating_new_wallet);
|
.pushNamed(Routes.newWalletType)),
|
||||||
final newWalletVM =
|
|
||||||
getIt.get<WalletNewVM>(param1: WalletType.monero);
|
|
||||||
await newWalletVM.create(
|
|
||||||
options: 'English'); // FIXME: Unnamed constant
|
|
||||||
context.hideProgressText();
|
|
||||||
await Navigator.of(context.context).pushNamed(Routes.preSeed);
|
|
||||||
} catch (e) {
|
|
||||||
context.changeProcessText('Error: ${e.toString()}');
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
fullscreenDialog: true);
|
fullscreenDialog: true);
|
||||||
|
|
||||||
case Routes.newWalletType:
|
case Routes.newWalletType:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => NewWalletTypePage(
|
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||||
onTypeSelected: (context, type) => Navigator.of(context)
|
param1: (BuildContext context, WalletType type) =>
|
||||||
.pushNamed(Routes.newWallet, arguments: type)));
|
Navigator.of(context)
|
||||||
|
.pushNamed(Routes.preSeed, arguments: type),
|
||||||
|
param2: true));
|
||||||
|
|
||||||
case Routes.newWallet:
|
case Routes.newWallet:
|
||||||
final type = WalletType.monero; // settings.arguments as WalletType;
|
final type = settings.arguments as WalletType;
|
||||||
final walletNewVM = getIt.get<WalletNewVM>(param1: type);
|
final walletNewVM = getIt.get<WalletNewVM>(param1: type);
|
||||||
|
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
|
@ -107,11 +99,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.restoreWalletType:
|
case Routes.restoreWalletType:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => NewWalletTypePage(
|
builder: (_) => getIt.get<NewWalletTypePage>(
|
||||||
onTypeSelected: (context, type) => Navigator.of(context)
|
param1: (BuildContext context, WalletType type) =>
|
||||||
.pushNamed(Routes.restoreWalletOptions, arguments: type),
|
Navigator.of(context)
|
||||||
isNewWallet: false,
|
.pushNamed(Routes.restoreWallet, arguments: type),
|
||||||
));
|
param2: false));
|
||||||
|
|
||||||
case Routes.restoreOptions:
|
case Routes.restoreOptions:
|
||||||
final type = settings.arguments as WalletType;
|
final type = settings.arguments as WalletType;
|
||||||
|
@ -149,7 +141,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => getIt.get<SetupPinCodePage>(
|
builder: (_) => getIt.get<SetupPinCodePage>(
|
||||||
param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
|
param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
|
||||||
Navigator.pushNamed(context.context, Routes.restoreWallet)),
|
Navigator.pushNamed(
|
||||||
|
context.context, Routes.restoreWalletType)),
|
||||||
fullscreenDialog: true);
|
fullscreenDialog: true);
|
||||||
|
|
||||||
case Routes.seed:
|
case Routes.seed:
|
||||||
|
@ -159,16 +152,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.restoreWallet:
|
case Routes.restoreWallet:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) =>
|
builder: (_) => getIt.get<WalletRestorePage>(
|
||||||
getIt.get<WalletRestorePage>(param1: WalletType.monero));
|
param1: settings.arguments as WalletType));
|
||||||
|
|
||||||
case Routes.restoreWalletFromSeed:
|
case Routes.restoreWalletFromSeed:
|
||||||
// final args = settings.arguments as List<dynamic>;
|
final type = settings.arguments as WalletType;
|
||||||
final type = WalletType.monero; //args.first as WalletType;
|
|
||||||
// final language = type == WalletType.monero
|
|
||||||
// ? args[1] as String
|
|
||||||
// : LanguageList.english;
|
|
||||||
|
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
builder: (_) => RestoreWalletFromSeedPage(type: type));
|
builder: (_) => RestoreWalletFromSeedPage(type: type));
|
||||||
|
|
||||||
|
@ -294,7 +282,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
|
|
||||||
case Routes.tradeDetails:
|
case Routes.tradeDetails:
|
||||||
return MaterialPageRoute<void>(
|
return MaterialPageRoute<void>(
|
||||||
builder: (_) => TradeDetailsPage(settings.arguments as Trade));
|
builder: (_) =>
|
||||||
|
getIt.get<TradeDetailsPage>(param1: settings.arguments as Trade));
|
||||||
|
|
||||||
case Routes.restoreWalletFromSeedDetails:
|
case Routes.restoreWalletFromSeedDetails:
|
||||||
final args = settings.arguments as List;
|
final args = settings.arguments as List;
|
||||||
|
@ -327,7 +316,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
||||||
builder: (_) => getIt.get<LanguageListPage>());
|
builder: (_) => getIt.get<LanguageListPage>());
|
||||||
|
|
||||||
case Routes.preSeed:
|
case Routes.preSeed:
|
||||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<PreSeedPage>());
|
return MaterialPageRoute<void>(
|
||||||
|
builder: (_) =>
|
||||||
|
getIt.get<PreSeedPage>(param1: settings.arguments as WalletType));
|
||||||
|
|
||||||
case Routes.backup:
|
case Routes.backup:
|
||||||
return CupertinoPageRoute<void>(
|
return CupertinoPageRoute<void>(
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/palette.dart';
|
import 'package:cake_wallet/palette.dart';
|
||||||
|
@ -9,13 +10,13 @@ enum AppBarStyle { regular, withShadow, transparent }
|
||||||
|
|
||||||
abstract class BasePage extends StatelessWidget {
|
abstract class BasePage extends StatelessWidget {
|
||||||
BasePage()
|
BasePage()
|
||||||
: _scaffoldKey = GlobalKey<ScaffoldState>(),
|
: _scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
_closeButtonImage = Image.asset('assets/images/close_button.png'),
|
|
||||||
_closeButtonImageDarkTheme =
|
|
||||||
Image.asset('assets/images/close_button_dark_theme.png');
|
|
||||||
final GlobalKey<ScaffoldState> _scaffoldKey;
|
final GlobalKey<ScaffoldState> _scaffoldKey;
|
||||||
final Image _closeButtonImage;
|
|
||||||
final Image _closeButtonImageDarkTheme;
|
final Image closeButtonImage =
|
||||||
|
Image.asset('assets/images/close_button.png');
|
||||||
|
final Image closeButtonImageDarkTheme =
|
||||||
|
Image.asset('assets/images/close_button_dark_theme.png');
|
||||||
|
|
||||||
String get title => null;
|
String get title => null;
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ abstract class BasePage extends StatelessWidget {
|
||||||
|
|
||||||
Widget Function(BuildContext, Widget) get rootWrapper => null;
|
Widget Function(BuildContext, Widget) get rootWrapper => null;
|
||||||
|
|
||||||
bool get isDarkTheme => getIt.get<SettingsStore>().isDarkTheme;
|
ThemeBase get currentTheme => getIt.get<SettingsStore>().currentTheme;
|
||||||
|
|
||||||
void onOpenEndDrawer() => _scaffoldKey.currentState.openEndDrawer();
|
void onOpenEndDrawer() => _scaffoldKey.currentState.openEndDrawer();
|
||||||
|
|
||||||
|
@ -51,8 +52,8 @@ abstract class BasePage extends StatelessWidget {
|
||||||
final _backButton = Icon(Icons.arrow_back_ios,
|
final _backButton = Icon(Icons.arrow_back_ios,
|
||||||
color: titleColor ?? Theme.of(context).primaryTextTheme.title.color,
|
color: titleColor ?? Theme.of(context).primaryTextTheme.title.color,
|
||||||
size: 16,);
|
size: 16,);
|
||||||
final _closeButton =
|
final _closeButton = currentTheme.type == ThemeType.dark
|
||||||
isDarkTheme ? _closeButtonImageDarkTheme : _closeButtonImage;
|
? closeButtonImageDarkTheme : closeButtonImage;
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 37,
|
height: 37,
|
||||||
|
@ -88,8 +89,8 @@ abstract class BasePage extends StatelessWidget {
|
||||||
Widget floatingActionButton(BuildContext context) => null;
|
Widget floatingActionButton(BuildContext context) => null;
|
||||||
|
|
||||||
ObstructingPreferredSizeWidget appBar(BuildContext context) {
|
ObstructingPreferredSizeWidget appBar(BuildContext context) {
|
||||||
final appBarColor =
|
final appBarColor = currentTheme.type == ThemeType.dark
|
||||||
isDarkTheme ? backgroundDarkColor : backgroundLightColor;
|
? backgroundDarkColor : backgroundLightColor;
|
||||||
|
|
||||||
switch (appBarStyle) {
|
switch (appBarStyle) {
|
||||||
case AppBarStyle.regular:
|
case AppBarStyle.regular:
|
||||||
|
@ -131,10 +132,12 @@ abstract class BasePage extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final _backgroundColor = currentTheme.type == ThemeType.dark
|
||||||
|
? backgroundDarkColor : backgroundLightColor;
|
||||||
|
|
||||||
final root = Scaffold(
|
final root = Scaffold(
|
||||||
key: _scaffoldKey,
|
key: _scaffoldKey,
|
||||||
backgroundColor:
|
backgroundColor: _backgroundColor,
|
||||||
isDarkTheme ? backgroundDarkColor : backgroundLightColor,
|
|
||||||
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
|
resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
|
||||||
extendBodyBehindAppBar: extendBodyBehindAppBar,
|
extendBodyBehindAppBar: extendBodyBehindAppBar,
|
||||||
endDrawer: endDrawer,
|
endDrawer: endDrawer,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/entities/contact_base.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -61,107 +62,113 @@ class ContactListPage extends BasePage {
|
||||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||||
child: Observer(
|
child: Observer(
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return contactListViewModel.contacts.isNotEmpty
|
return SectionStandardList(
|
||||||
? SectionStandardList(
|
context: context,
|
||||||
sectionCount: 1,
|
sectionCount: 2,
|
||||||
context: context,
|
sectionTitleBuilder: (_, int sectionIndex) {
|
||||||
itemCounter: (int sectionIndex) =>
|
var title = 'Contacts';
|
||||||
contactListViewModel.contacts.length,
|
|
||||||
itemBuilder: (_, sectionIndex, index) {
|
|
||||||
final contact = contactListViewModel.contacts[index];
|
|
||||||
final image = _getCurrencyImage(contact.type);
|
|
||||||
final content = GestureDetector(
|
|
||||||
onTap: () async {
|
|
||||||
if (!isEditable) {
|
|
||||||
Navigator.of(context).pop(contact);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final isCopied = await showNameAndAddressDialog(
|
if (sectionIndex == 0) {
|
||||||
context, contact.name, contact.address);
|
title = 'My wallets';
|
||||||
|
}
|
||||||
|
|
||||||
if (isCopied != null && isCopied) {
|
return Container(
|
||||||
await Clipboard.setData(
|
padding: EdgeInsets.only(left: 24, bottom: 20),
|
||||||
ClipboardData(text: contact.address));
|
child: Text(title, style: TextStyle(fontSize: 36)));
|
||||||
await showBar<void>(
|
},
|
||||||
context, S.of(context).copied_to_clipboard);
|
itemCounter: (int sectionIndex) => sectionIndex == 0
|
||||||
}
|
? contactListViewModel.walletContacts.length
|
||||||
},
|
: contactListViewModel.contacts.length,
|
||||||
child: Container(
|
itemBuilder: (_, sectionIndex, index) {
|
||||||
color: Colors.transparent,
|
if (sectionIndex == 0) {
|
||||||
padding: const EdgeInsets.only(
|
final walletInfo = contactListViewModel.walletContacts[index];
|
||||||
left: 24, top: 16, bottom: 16, right: 24),
|
return generateRaw(context, walletInfo);
|
||||||
child: Row(
|
}
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: <Widget>[
|
|
||||||
image ?? Offstage(),
|
|
||||||
Padding(
|
|
||||||
padding: image != null
|
|
||||||
? EdgeInsets.only(left: 12)
|
|
||||||
: EdgeInsets.only(left: 0),
|
|
||||||
child: Text(
|
|
||||||
contact.name,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.normal,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.primaryTextTheme
|
|
||||||
.title
|
|
||||||
.color),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return !isEditable
|
final contact = contactListViewModel.contacts[index];
|
||||||
? content
|
final content = generateRaw(context, contact);
|
||||||
: 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) {
|
return !isEditable
|
||||||
await contactListViewModel
|
? content
|
||||||
.delete(contact);
|
: Slidable(
|
||||||
}
|
key: Key('${contact.key}'),
|
||||||
},
|
actionPane: SlidableDrawerActionPane(),
|
||||||
),
|
child: content,
|
||||||
]);
|
secondaryActions: <Widget>[
|
||||||
},
|
IconSlideAction(
|
||||||
)
|
caption: S.of(context).edit,
|
||||||
: Center(
|
color: Colors.blue,
|
||||||
child: Text(
|
icon: Icons.edit,
|
||||||
S.of(context).placeholder_contacts,
|
onTap: () async => await Navigator.of(context)
|
||||||
textAlign: TextAlign.center,
|
.pushNamed(Routes.addressBookAddContact,
|
||||||
style: TextStyle(color: Colors.grey, fontSize: 14),
|
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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget generateRaw(BuildContext context, ContactBase contact) {
|
||||||
|
final image = _getCurrencyImage(contact.type);
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () async {
|
||||||
|
if (!isEditable) {
|
||||||
|
Navigator.of(context).pop(contact);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final isCopied = await showNameAndAddressDialog(
|
||||||
|
context, contact.name, contact.address);
|
||||||
|
|
||||||
|
if (isCopied != null && isCopied) {
|
||||||
|
await Clipboard.setData(ClipboardData(text: contact.address));
|
||||||
|
await showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: Colors.transparent,
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
image ?? Offstage(),
|
||||||
|
Padding(
|
||||||
|
padding: image != null
|
||||||
|
? EdgeInsets.only(left: 12)
|
||||||
|
: EdgeInsets.only(left: 0),
|
||||||
|
child: Text(
|
||||||
|
contact.name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
color: Theme.of(context).primaryTextTheme.title.color),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Image _getCurrencyImage(CryptoCurrency currency) {
|
Image _getCurrencyImage(CryptoCurrency currency) {
|
||||||
Image image;
|
Image image;
|
||||||
switch (currency) {
|
switch (currency) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||||
|
@ -20,7 +21,8 @@ class DashboardPage extends BasePage {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Color get backgroundLightColor => Colors.transparent;
|
Color get backgroundLightColor => currentTheme.type == ThemeType.bright
|
||||||
|
? Colors.transparent : Colors.white;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Color get backgroundDarkColor => Colors.transparent;
|
Color get backgroundDarkColor => Colors.transparent;
|
||||||
|
@ -50,7 +52,8 @@ class DashboardPage extends BasePage {
|
||||||
@override
|
@override
|
||||||
Widget trailing(BuildContext context) {
|
Widget trailing(BuildContext context) {
|
||||||
final menuButton =
|
final menuButton =
|
||||||
Image.asset('assets/images/menu.png', color: Colors.white);
|
Image.asset('assets/images/menu.png',
|
||||||
|
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
alignment: Alignment.centerRight,
|
alignment: Alignment.centerRight,
|
||||||
|
@ -65,12 +68,6 @@ class DashboardPage extends BasePage {
|
||||||
|
|
||||||
final DashboardViewModel walletViewModel;
|
final DashboardViewModel walletViewModel;
|
||||||
final WalletAddressListViewModel addressListViewModel;
|
final WalletAddressListViewModel addressListViewModel;
|
||||||
final sendImage = Image.asset('assets/images/upload.png',
|
|
||||||
height: 22.24, width: 24, color: Colors.white);
|
|
||||||
final exchangeImage = Image.asset('assets/images/transfer.png',
|
|
||||||
height: 24.27, width: 22.25, color: Colors.white);
|
|
||||||
final receiveImage = Image.asset('assets/images/download.png',
|
|
||||||
height: 22.24, width: 24, color: Colors.white);
|
|
||||||
final controller = PageController(initialPage: 1);
|
final controller = PageController(initialPage: 1);
|
||||||
|
|
||||||
var pages = <Widget>[];
|
var pages = <Widget>[];
|
||||||
|
@ -78,6 +75,15 @@ class DashboardPage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
|
final sendImage = Image.asset('assets/images/upload.png',
|
||||||
|
height: 22.24, width: 24,
|
||||||
|
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
|
||||||
|
final exchangeImage = Image.asset('assets/images/transfer.png',
|
||||||
|
height: 24.27, width: 22.25,
|
||||||
|
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
|
||||||
|
final receiveImage = Image.asset('assets/images/download.png',
|
||||||
|
height: 22.24, width: 24,
|
||||||
|
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
|
||||||
_setEffects();
|
_setEffects();
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
|
@ -100,7 +106,8 @@ class DashboardPage extends BasePage {
|
||||||
dotWidth: 6.0,
|
dotWidth: 6.0,
|
||||||
dotHeight: 6.0,
|
dotHeight: 6.0,
|
||||||
dotColor: Theme.of(context).indicatorColor,
|
dotColor: Theme.of(context).indicatorColor,
|
||||||
activeDotColor: Colors.white),
|
activeDotColor: Theme.of(context).accentTextTheme.display1
|
||||||
|
.backgroundColor),
|
||||||
)),
|
)),
|
||||||
Container(
|
Container(
|
||||||
padding: EdgeInsets.only(left: 45, right: 45, bottom: 24),
|
padding: EdgeInsets.only(left: 45, right: 45, bottom: 24),
|
||||||
|
|
|
@ -9,44 +9,53 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||||
// FIXME: terrible design
|
// FIXME: terrible design
|
||||||
|
|
||||||
class WalletMenu {
|
class WalletMenu {
|
||||||
WalletMenu(this.context, this.reconnect);
|
WalletMenu(this.context, this.reconnect, this.hasRescan) : items = [] {
|
||||||
|
items.addAll([
|
||||||
final List<WalletMenuItem> items = [
|
WalletMenuItem(
|
||||||
WalletMenuItem(
|
title: S.current.reconnect,
|
||||||
title: S.current.reconnect,
|
image: Image.asset('assets/images/reconnect_menu.png',
|
||||||
image: Image.asset('assets/images/reconnect_menu.png',
|
height: 16, width: 16)),
|
||||||
height: 16, width: 16)),
|
if (hasRescan)
|
||||||
WalletMenuItem(
|
WalletMenuItem(
|
||||||
title: S.current.rescan,
|
title: S.current.rescan,
|
||||||
image: Image.asset('assets/images/filter_icon.png',
|
image: Image.asset('assets/images/filter_icon.png',
|
||||||
height: 16, width: 16)),
|
height: 16, width: 16)),
|
||||||
WalletMenuItem(
|
WalletMenuItem(
|
||||||
title: S.current.wallets,
|
title: S.current.wallets,
|
||||||
image: Image.asset('assets/images/wallet_menu.png',
|
image: Image.asset('assets/images/wallet_menu.png',
|
||||||
height: 16, width: 16)),
|
height: 16, width: 16)),
|
||||||
WalletMenuItem(
|
WalletMenuItem(
|
||||||
title: S.current.nodes,
|
title: S.current.nodes,
|
||||||
image:
|
image: Image.asset('assets/images/nodes_menu.png',
|
||||||
Image.asset('assets/images/nodes_menu.png', height: 16, width: 16)),
|
height: 16, width: 16)),
|
||||||
WalletMenuItem(
|
WalletMenuItem(
|
||||||
title: S.current.show_keys,
|
title: S.current.show_keys,
|
||||||
image:
|
image:
|
||||||
Image.asset('assets/images/key_menu.png', height: 16, width: 16)),
|
Image.asset('assets/images/key_menu.png', height: 16, width: 16)),
|
||||||
WalletMenuItem(
|
WalletMenuItem(
|
||||||
title: S.current.address_book_menu,
|
title: S.current.address_book_menu,
|
||||||
image: Image.asset('assets/images/open_book_menu.png',
|
image: Image.asset('assets/images/open_book_menu.png',
|
||||||
height: 16, width: 16)),
|
height: 16, width: 16)),
|
||||||
WalletMenuItem(
|
WalletMenuItem(
|
||||||
title: S.current.settings_title,
|
title: S.current.settings_title,
|
||||||
image: Image.asset('assets/images/settings_menu.png',
|
image: Image.asset('assets/images/settings_menu.png',
|
||||||
height: 16, width: 16)),
|
height: 16, width: 16)),
|
||||||
];
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<WalletMenuItem> items;
|
||||||
final BuildContext context;
|
final BuildContext context;
|
||||||
final Future<void> Function() reconnect;
|
final Future<void> Function() reconnect;
|
||||||
|
final bool hasRescan;
|
||||||
|
|
||||||
void action(int index) {
|
void action(int index) {
|
||||||
switch (index) {
|
var indx = index;
|
||||||
|
|
||||||
|
if (index > 0 && !hasRescan) {
|
||||||
|
indx += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (indx) {
|
||||||
case 0:
|
case 0:
|
||||||
_presentReconnectAlert(context);
|
_presentReconnectAlert(context);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -39,7 +39,10 @@ class ActionButton extends StatelessWidget {
|
||||||
SizedBox(height: 15),
|
SizedBox(height: 15),
|
||||||
Text(
|
Text(
|
||||||
title,
|
title,
|
||||||
style: TextStyle(fontSize: 14, color: Colors.white),
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
color: Theme.of(context).accentTextTheme.display3
|
||||||
|
.backgroundColor),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,62 +1,109 @@
|
||||||
|
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart';
|
||||||
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
import 'package:keyboard_actions/keyboard_actions.dart';
|
||||||
|
|
||||||
class AddressPage extends StatelessWidget {
|
class AddressPage extends StatelessWidget {
|
||||||
AddressPage({@required this.addressListViewModel});
|
AddressPage({@required this.addressListViewModel})
|
||||||
|
: _cryptoAmountFocus = FocusNode();
|
||||||
|
|
||||||
final WalletAddressListViewModel addressListViewModel;
|
final WalletAddressListViewModel addressListViewModel;
|
||||||
|
|
||||||
|
final FocusNode _cryptoAmountFocus;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return KeyboardActions(
|
||||||
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
|
autoScroll: false,
|
||||||
child: Column(
|
disableScroll: true,
|
||||||
children: <Widget>[
|
tapOutsideToDismiss: true,
|
||||||
Expanded(
|
config: KeyboardActionsConfig(
|
||||||
child: Center(
|
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||||
child: QRWidget(addressListViewModel: addressListViewModel),
|
keyboardBarColor:
|
||||||
)
|
Theme.of(context).accentTextTheme.body2.backgroundColor,
|
||||||
|
nextFocus: false,
|
||||||
|
actions: [
|
||||||
|
KeyboardActionsItem(
|
||||||
|
focusNode: _cryptoAmountFocus,
|
||||||
|
toolbarButtons: [(_) => KeyboardDoneButton()],
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
child: Container(
|
||||||
|
height: 1,
|
||||||
|
padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: Center(
|
||||||
|
child: QRWidget(
|
||||||
|
addressListViewModel: addressListViewModel,
|
||||||
|
amountTextFieldFocusNode: _cryptoAmountFocus,
|
||||||
|
isAmountFieldShow: !addressListViewModel.hasAccounts),
|
||||||
|
)),
|
||||||
|
Observer(builder: (_) {
|
||||||
|
return addressListViewModel.hasAddressList
|
||||||
|
? GestureDetector(
|
||||||
|
onTap: () =>
|
||||||
|
Navigator.of(context).pushNamed(Routes.receive),
|
||||||
|
child: Container(
|
||||||
|
height: 50,
|
||||||
|
padding: EdgeInsets.only(left: 24, right: 12),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius:
|
||||||
|
BorderRadius.all(Radius.circular(25)),
|
||||||
|
border: Border.all(
|
||||||
|
color:
|
||||||
|
Theme.of(context).textTheme.subhead.color,
|
||||||
|
width: 1),
|
||||||
|
color: Theme.of(context).buttonColor),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Observer(
|
||||||
|
builder: (_) => Text(
|
||||||
|
addressListViewModel.hasAccounts
|
||||||
|
? S
|
||||||
|
.of(context)
|
||||||
|
.accounts_subaddresses
|
||||||
|
: S.of(context).addresses,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.accentTextTheme
|
||||||
|
.display3
|
||||||
|
.backgroundColor),
|
||||||
|
)),
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_ios,
|
||||||
|
size: 14,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.accentTextTheme
|
||||||
|
.display3
|
||||||
|
.backgroundColor,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: PrimaryButton(
|
||||||
|
onPressed: () => addressListViewModel.nextAddress(),
|
||||||
|
text: 'Next address',
|
||||||
|
color: Theme.of(context).buttonColor,
|
||||||
|
textColor: Theme.of(context)
|
||||||
|
.accentTextTheme
|
||||||
|
.display3
|
||||||
|
.backgroundColor);
|
||||||
|
})
|
||||||
|
],
|
||||||
),
|
),
|
||||||
GestureDetector(
|
));
|
||||||
onTap: () => Navigator.of(context).pushNamed(Routes.receive),
|
|
||||||
child: Container(
|
|
||||||
height: 50,
|
|
||||||
padding: EdgeInsets.only(left: 24, right: 12),
|
|
||||||
alignment: Alignment.center,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(25)),
|
|
||||||
border: Border.all(
|
|
||||||
color: Theme.of(context).textTheme.subhead.color,
|
|
||||||
width: 1
|
|
||||||
),
|
|
||||||
color: Theme.of(context).buttonColor
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
|
||||||
Text(
|
|
||||||
S.of(context).accounts_subaddresses,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.white
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Icon(
|
|
||||||
Icons.arrow_forward_ios,
|
|
||||||
size: 14,
|
|
||||||
color: Colors.white,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/entities/crypto_currency.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
@ -11,58 +12,86 @@ class BalancePage extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
|
color: Colors.transparent,
|
||||||
padding: EdgeInsets.all(24),
|
padding: EdgeInsets.all(24),
|
||||||
child: GestureDetector(
|
child: Column(
|
||||||
onTapUp: (_) => dashboardViewModel.balanceViewModel.isReversing = false,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
onTapDown: (_) => dashboardViewModel.balanceViewModel.isReversing = true,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
child: Column(
|
children: <Widget>[
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
Observer(builder: (_) {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
return Text(
|
||||||
children: <Widget>[
|
dashboardViewModel.balanceViewModel.currency.toString(),
|
||||||
Observer(builder: (_) {
|
style: TextStyle(
|
||||||
return Text(
|
fontSize: 40,
|
||||||
dashboardViewModel.balanceViewModel.currency.toString(),
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.accentTextTheme
|
||||||
|
.display2
|
||||||
|
.backgroundColor,
|
||||||
|
height: 1),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Observer(builder: (_) {
|
||||||
|
return Text(
|
||||||
|
'${dashboardViewModel.balanceViewModel.availableBalanceLabel} (${dashboardViewModel.balanceViewModel.availableFiatBalance.toString()})',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.accentTextTheme
|
||||||
|
.display2
|
||||||
|
.backgroundColor,
|
||||||
|
height: 1),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Observer(builder: (_) {
|
||||||
|
return AutoSizeText(
|
||||||
|
dashboardViewModel.balanceViewModel.availableBalance,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 40,
|
fontSize: 54,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: Theme.of(context).indicatorColor,
|
color: Theme.of(context)
|
||||||
|
.accentTextTheme
|
||||||
|
.display3
|
||||||
|
.backgroundColor,
|
||||||
height: 1),
|
height: 1),
|
||||||
);
|
maxLines: 1,
|
||||||
}),
|
textAlign: TextAlign.center);
|
||||||
Observer(builder: (_) {
|
}),
|
||||||
return Text(
|
SizedBox(height: 10),
|
||||||
dashboardViewModel.balanceViewModel.displayMode.toString(),
|
Observer(builder: (_) {
|
||||||
|
return Text(
|
||||||
|
'${dashboardViewModel.balanceViewModel.additionalBalanceLabel} (${dashboardViewModel.balanceViewModel.additionalFiatBalance.toString()})',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.accentTextTheme
|
||||||
|
.display2
|
||||||
|
.backgroundColor,
|
||||||
|
height: 1),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
SizedBox(height: 10),
|
||||||
|
Observer(builder: (_) {
|
||||||
|
return AutoSizeText(
|
||||||
|
dashboardViewModel.balanceViewModel.additionalBalance
|
||||||
|
.toString(),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.bold,
|
||||||
color: Theme.of(context).indicatorColor,
|
color: Theme.of(context)
|
||||||
|
.accentTextTheme
|
||||||
|
.display3
|
||||||
|
.backgroundColor,
|
||||||
height: 1),
|
height: 1),
|
||||||
);
|
maxLines: 1,
|
||||||
}),
|
textAlign: TextAlign.center);
|
||||||
SizedBox(height: 10),
|
}),
|
||||||
Observer(builder: (_) {
|
],
|
||||||
return AutoSizeText(dashboardViewModel.balanceViewModel.cryptoBalance,
|
),
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 54,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
color: Colors.white,
|
|
||||||
height: 1),
|
|
||||||
maxLines: 1,
|
|
||||||
textAlign: TextAlign.center);
|
|
||||||
}),
|
|
||||||
SizedBox(height: 10),
|
|
||||||
Observer(builder: (_) {
|
|
||||||
return Text(dashboardViewModel.balanceViewModel.fiatBalance,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Theme.of(context).indicatorColor,
|
|
||||||
height: 1),
|
|
||||||
textAlign: TextAlign.center);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,14 +27,15 @@ class HeaderRow extends StatelessWidget {
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.white
|
color: Theme.of(context).accentTextTheme.display3.backgroundColor
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
showPopUp<void>(
|
showPopUp<void>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => FilterWidget(dashboardViewModel: dashboardViewModel)
|
builder: (context) =>
|
||||||
|
FilterWidget(dashboardViewModel: dashboardViewModel)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|
|
@ -66,8 +66,10 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final walletMenu =
|
final walletMenu = WalletMenu(
|
||||||
WalletMenu(context, () async => widget.dashboardViewModel.reconnect());
|
context,
|
||||||
|
() async => widget.dashboardViewModel.reconnect(),
|
||||||
|
widget.dashboardViewModel.hasRescan);
|
||||||
final itemCount = walletMenu.items.length;
|
final itemCount = walletMenu.items.length;
|
||||||
|
|
||||||
moneroIcon = Image.asset('assets/images/monero_menu.png',
|
moneroIcon = Image.asset('assets/images/monero_menu.png',
|
||||||
|
@ -148,16 +150,19 @@ class MenuWidgetState extends State<MenuWidget> {
|
||||||
),
|
),
|
||||||
if (widget.dashboardViewModel.subname !=
|
if (widget.dashboardViewModel.subname !=
|
||||||
null)
|
null)
|
||||||
Observer(builder: (_) => Text(
|
Observer(
|
||||||
widget.dashboardViewModel.subname,
|
builder: (_) => Text(
|
||||||
style: TextStyle(
|
widget.dashboardViewModel
|
||||||
color: Theme.of(context)
|
.subname,
|
||||||
.accentTextTheme
|
style: TextStyle(
|
||||||
.overline
|
color: Theme.of(context)
|
||||||
.decorationColor,
|
.accentTextTheme
|
||||||
fontWeight: FontWeight.w500,
|
.overline
|
||||||
fontSize: 12),
|
.decorationColor,
|
||||||
))
|
fontWeight:
|
||||||
|
FontWeight.w500,
|
||||||
|
fontSize: 12),
|
||||||
|
))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
|
|
|
@ -25,54 +25,53 @@ class TradeRow extends StatelessWidget {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 52,
|
padding: EdgeInsets.fromLTRB(24, 8, 24, 8),
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
padding: EdgeInsets.only(left: 24, right: 24),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
_getPoweredImage(provider),
|
children: [
|
||||||
Expanded(
|
_getPoweredImage(provider),
|
||||||
child: Padding(
|
SizedBox(width: 12),
|
||||||
padding: const EdgeInsets.only(left: 12),
|
Expanded(
|
||||||
child: Container(
|
child: Column(
|
||||||
height: 46,
|
mainAxisSize: MainAxisSize.min,
|
||||||
child: Column(
|
children: [
|
||||||
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Row(
|
Text('${from.toString()} → ${to.toString()}',
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
style: TextStyle(
|
||||||
children: <Widget>[
|
fontSize: 16,
|
||||||
Text('${from.toString()} → ${to.toString()}',
|
fontWeight: FontWeight.w500,
|
||||||
style: TextStyle(
|
color: Theme.of(context).accentTextTheme.
|
||||||
fontSize: 16,
|
display3.backgroundColor
|
||||||
fontWeight: FontWeight.w500,
|
)),
|
||||||
color: Colors.white
|
formattedAmount != null
|
||||||
)),
|
? Text(formattedAmount + ' ' + amountCrypto,
|
||||||
formattedAmount != null
|
style: TextStyle(
|
||||||
? Text(formattedAmount + ' ' + amountCrypto,
|
fontSize: 16,
|
||||||
style: TextStyle(
|
fontWeight: FontWeight.w500,
|
||||||
fontSize: 16,
|
color: Theme.of(context).accentTextTheme.
|
||||||
fontWeight: FontWeight.w500,
|
display3.backgroundColor
|
||||||
color: Colors.white
|
))
|
||||||
))
|
: Container()
|
||||||
: Container()
|
]),
|
||||||
]),
|
SizedBox(height: 5),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(createdAtFormattedDate,
|
Text(createdAtFormattedDate,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: Theme.of(context).textTheme
|
color: Theme.of(context).textTheme
|
||||||
.overline.backgroundColor))
|
.overline.backgroundColor))
|
||||||
]),
|
])
|
||||||
],
|
],
|
||||||
),
|
)
|
||||||
),
|
)
|
||||||
))
|
],
|
||||||
]),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,73 +23,73 @@ class TransactionRow extends StatelessWidget {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 62,
|
padding: EdgeInsets.fromLTRB(24, 8, 24, 8),
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
padding: EdgeInsets.only(left: 24, right: 24),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: <Widget>[
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
Container(
|
children: [
|
||||||
height: 36,
|
Container(
|
||||||
width: 36,
|
height: 36,
|
||||||
decoration: BoxDecoration(
|
width: 36,
|
||||||
shape: BoxShape.circle,
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context).textTheme.overline.decorationColor
|
shape: BoxShape.circle,
|
||||||
),
|
color: Theme.of(context).textTheme.overline.decorationColor
|
||||||
child: Image.asset(
|
|
||||||
direction == TransactionDirection.incoming
|
|
||||||
? 'assets/images/down_arrow.png'
|
|
||||||
: 'assets/images/up_arrow.png'),
|
|
||||||
),
|
),
|
||||||
Expanded(
|
child: Image.asset(
|
||||||
child: Container(
|
direction == TransactionDirection.incoming
|
||||||
padding: const EdgeInsets.only(left: 12),
|
? 'assets/images/down_arrow.png'
|
||||||
height: 56,
|
: 'assets/images/up_arrow.png'),
|
||||||
child: Column(
|
),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
SizedBox(width: 12),
|
||||||
mainAxisSize: MainAxisSize.max,
|
Expanded(
|
||||||
children: <Widget>[
|
child: Column(
|
||||||
Row(
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
children: [
|
||||||
children: <Widget>[
|
Row(
|
||||||
Text(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
(direction == TransactionDirection.incoming
|
children: <Widget>[
|
||||||
? S.of(context).received
|
Text(
|
||||||
: S.of(context).sent) +
|
(direction == TransactionDirection.incoming
|
||||||
(isPending ? S.of(context).pending : ''),
|
? S.of(context).received
|
||||||
style: TextStyle(
|
: S.of(context).sent) +
|
||||||
fontSize: 16,
|
(isPending ? S.of(context).pending : ''),
|
||||||
fontWeight: FontWeight.w500,
|
style: TextStyle(
|
||||||
color: Colors.white)),
|
fontSize: 16,
|
||||||
Text(formattedAmount,
|
fontWeight: FontWeight.w500,
|
||||||
style: TextStyle(
|
color: Theme.of(context).accentTextTheme.
|
||||||
fontSize: 16,
|
display3.backgroundColor)),
|
||||||
fontWeight: FontWeight.w500,
|
Text(formattedAmount,
|
||||||
color: Colors.white))
|
style: TextStyle(
|
||||||
]),
|
fontSize: 16,
|
||||||
Row(
|
fontWeight: FontWeight.w500,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
color: Theme.of(context).accentTextTheme.
|
||||||
children: <Widget>[
|
display3.backgroundColor))
|
||||||
Text(formattedDate,
|
]),
|
||||||
style: TextStyle(
|
SizedBox(height: 5),
|
||||||
fontSize: 14,
|
Row(
|
||||||
color: Theme.of(context)
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
.textTheme
|
children: <Widget>[
|
||||||
.overline
|
Text(formattedDate,
|
||||||
.backgroundColor)),
|
style: TextStyle(
|
||||||
Text(formattedFiatAmount,
|
fontSize: 14,
|
||||||
style: TextStyle(
|
color: Theme.of(context)
|
||||||
fontSize: 14,
|
.textTheme
|
||||||
color: Theme.of(context)
|
.overline
|
||||||
.textTheme
|
.backgroundColor)),
|
||||||
.overline
|
Text(formattedFiatAmount,
|
||||||
.backgroundColor))
|
style: TextStyle(
|
||||||
]),
|
fontSize: 14,
|
||||||
],
|
color: Theme.of(context)
|
||||||
),
|
.textTheme
|
||||||
),
|
.overline
|
||||||
)
|
.backgroundColor))
|
||||||
]),
|
])
|
||||||
|
],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,8 @@ class TransactionsPage extends StatelessWidget {
|
||||||
if (item is TransactionListItem) {
|
if (item is TransactionListItem) {
|
||||||
final transaction = item.transaction;
|
final transaction = item.transaction;
|
||||||
|
|
||||||
return TransactionRow(
|
return Observer(
|
||||||
|
builder: (_) => TransactionRow(
|
||||||
onTap: () => Navigator.of(context).pushNamed(
|
onTap: () => Navigator.of(context).pushNamed(
|
||||||
Routes.transactionDetails,
|
Routes.transactionDetails,
|
||||||
arguments: transaction),
|
arguments: transaction),
|
||||||
|
@ -55,7 +56,7 @@ class TransactionsPage extends StatelessWidget {
|
||||||
.format(transaction.date),
|
.format(transaction.date),
|
||||||
formattedAmount: item.formattedCryptoAmount,
|
formattedAmount: item.formattedCryptoAmount,
|
||||||
formattedFiatAmount: item.formattedFiatAmount,
|
formattedFiatAmount: item.formattedFiatAmount,
|
||||||
isPending: transaction.isPending);
|
isPending: transaction.isPending));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item is TradeListItem) {
|
if (item is TradeListItem) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
import 'package:cake_wallet/entities/sync_status.dart';
|
import 'package:cake_wallet/entities/sync_status.dart';
|
||||||
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
import 'package:dotted_border/dotted_border.dart';
|
import 'package:dotted_border/dotted_border.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
@ -62,10 +63,11 @@ class ExchangePage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget trailing(BuildContext context) => TrailButton(
|
Widget trailing(BuildContext context) => TrailButton(
|
||||||
caption: S.of(context).reset, onPressed: () {
|
caption: S.of(context).reset,
|
||||||
|
onPressed: () {
|
||||||
_formKey.currentState.reset();
|
_formKey.currentState.reset();
|
||||||
exchangeViewModel.reset();
|
exchangeViewModel.reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
|
@ -95,9 +97,8 @@ class ExchangePage extends BasePage {
|
||||||
return KeyboardActions(
|
return KeyboardActions(
|
||||||
config: KeyboardActionsConfig(
|
config: KeyboardActionsConfig(
|
||||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||||
keyboardBarColor: isDarkTheme
|
keyboardBarColor:
|
||||||
? Color.fromRGBO(48, 51, 60, 1.0)
|
Theme.of(context).accentTextTheme.body2.backgroundColor,
|
||||||
: Color.fromRGBO(98, 98, 98, 1.0),
|
|
||||||
nextFocus: false,
|
nextFocus: false,
|
||||||
actions: [
|
actions: [
|
||||||
KeyboardActionsItem(
|
KeyboardActionsItem(
|
||||||
|
@ -161,6 +162,11 @@ class ExchangePage extends BasePage {
|
||||||
padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
|
padding: EdgeInsets.fromLTRB(24, 100, 24, 32),
|
||||||
child: Observer(
|
child: Observer(
|
||||||
builder: (_) => ExchangeCard(
|
builder: (_) => ExchangeCard(
|
||||||
|
hasAllAmount: exchangeViewModel.hasAllAmount,
|
||||||
|
allAmount: exchangeViewModel.hasAllAmount
|
||||||
|
? () => exchangeViewModel
|
||||||
|
.calculateDepositAllAmount()
|
||||||
|
: null,
|
||||||
amountFocusNode: _depositAmountFocus,
|
amountFocusNode: _depositAmountFocus,
|
||||||
key: depositKey,
|
key: depositKey,
|
||||||
title: S.of(context).you_will_send,
|
title: S.of(context).you_will_send,
|
||||||
|
@ -178,9 +184,30 @@ class ExchangePage extends BasePage {
|
||||||
isAmountEstimated: false,
|
isAmountEstimated: false,
|
||||||
hasRefundAddress: true,
|
hasRefundAddress: true,
|
||||||
currencies: CryptoCurrency.all,
|
currencies: CryptoCurrency.all,
|
||||||
onCurrencySelected: (currency) =>
|
onCurrencySelected: (currency) {
|
||||||
exchangeViewModel.changeDepositCurrency(
|
// FIXME: need to move it into view model
|
||||||
currency: currency),
|
if (currency == CryptoCurrency.xmr &&
|
||||||
|
exchangeViewModel.wallet.type ==
|
||||||
|
WalletType.bitcoin) {
|
||||||
|
showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (dialogContext) {
|
||||||
|
return AlertWithOneAction(
|
||||||
|
alertTitle: S.of(context).error,
|
||||||
|
alertContent: S
|
||||||
|
.of(context)
|
||||||
|
.exchange_incorrect_current_wallet_for_xmr,
|
||||||
|
buttonText: S.of(context).ok,
|
||||||
|
buttonAction: () =>
|
||||||
|
Navigator.of(dialogContext)
|
||||||
|
.pop());
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exchangeViewModel.changeDepositCurrency(
|
||||||
|
currency: currency);
|
||||||
|
},
|
||||||
imageArrow: arrowBottomPurple,
|
imageArrow: arrowBottomPurple,
|
||||||
currencyButtonColor: Colors.transparent,
|
currencyButtonColor: Colors.transparent,
|
||||||
addressButtonsColor:
|
addressButtonsColor:
|
||||||
|
@ -395,30 +422,35 @@ class ExchangePage extends BasePage {
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
Observer(
|
Observer(
|
||||||
builder: (_) => LoadingPrimaryButton(
|
builder: (_) => LoadingPrimaryButton(
|
||||||
text: S.of(context).exchange,
|
text: S.of(context).exchange,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (_formKey.currentState.validate()) {
|
if (_formKey.currentState.validate()) {
|
||||||
if ((exchangeViewModel.depositCurrency == CryptoCurrency.xmr)
|
if ((exchangeViewModel.depositCurrency ==
|
||||||
&&(!(exchangeViewModel.status is SyncedSyncStatus))) {
|
CryptoCurrency.xmr) &&
|
||||||
showPopUp<void>(
|
(!(exchangeViewModel.status
|
||||||
context: context,
|
is SyncedSyncStatus))) {
|
||||||
builder: (BuildContext context) {
|
showPopUp<void>(
|
||||||
return AlertWithOneAction(
|
context: context,
|
||||||
alertTitle: S.of(context).exchange,
|
builder: (BuildContext context) {
|
||||||
alertContent: S.of(context).exchange_sync_alert_content,
|
return AlertWithOneAction(
|
||||||
buttonText: S.of(context).ok,
|
alertTitle: S.of(context).exchange,
|
||||||
buttonAction: () => Navigator.of(context).pop());
|
alertContent: S
|
||||||
});
|
.of(context)
|
||||||
} else {
|
.exchange_sync_alert_content,
|
||||||
exchangeViewModel.createTrade();
|
buttonText: S.of(context).ok,
|
||||||
}
|
buttonAction: () =>
|
||||||
}
|
Navigator.of(context).pop());
|
||||||
},
|
});
|
||||||
color: Theme.of(context).accentTextTheme.body2.color,
|
} else {
|
||||||
textColor: Colors.white,
|
exchangeViewModel.createTrade();
|
||||||
isLoading: exchangeViewModel.tradeState
|
}
|
||||||
is TradeIsCreating)),
|
}
|
||||||
|
},
|
||||||
|
color: Theme.of(context).accentTextTheme.body2.color,
|
||||||
|
textColor: Colors.white,
|
||||||
|
isLoading:
|
||||||
|
exchangeViewModel.tradeState is TradeIsCreating)),
|
||||||
]),
|
]),
|
||||||
)),
|
)),
|
||||||
));
|
));
|
||||||
|
|
|
@ -27,7 +27,9 @@ class ExchangeCard extends StatefulWidget {
|
||||||
this.borderColor = Colors.transparent,
|
this.borderColor = Colors.transparent,
|
||||||
this.currencyValueValidator,
|
this.currencyValueValidator,
|
||||||
this.addressTextFieldValidator,
|
this.addressTextFieldValidator,
|
||||||
this.amountFocusNode})
|
this.amountFocusNode,
|
||||||
|
this.hasAllAmount = false,
|
||||||
|
this.allAmount})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
final List<CryptoCurrency> currencies;
|
final List<CryptoCurrency> currencies;
|
||||||
|
@ -47,6 +49,8 @@ class ExchangeCard extends StatefulWidget {
|
||||||
final FormFieldValidator<String> currencyValueValidator;
|
final FormFieldValidator<String> currencyValueValidator;
|
||||||
final FormFieldValidator<String> addressTextFieldValidator;
|
final FormFieldValidator<String> addressTextFieldValidator;
|
||||||
final FocusNode amountFocusNode;
|
final FocusNode amountFocusNode;
|
||||||
|
final bool hasAllAmount;
|
||||||
|
Function allAmount;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ExchangeCardState createState() => ExchangeCardState();
|
ExchangeCardState createState() => ExchangeCardState();
|
||||||
|
@ -166,8 +170,8 @@ class ExchangeCardState extends State<ExchangeCard> {
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context)
|
||||||
.textTheme
|
.accentTextTheme
|
||||||
.subhead
|
.display4
|
||||||
.decorationColor),
|
.decorationColor),
|
||||||
validator: _isAmountEditable
|
validator: _isAmountEditable
|
||||||
? widget.currencyValueValidator
|
? widget.currencyValueValidator
|
||||||
|
@ -197,50 +201,88 @@ class ExchangeCardState extends State<ExchangeCard> {
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
|
if (widget.hasAllAmount)
|
||||||
|
Positioned(
|
||||||
|
top: 5,
|
||||||
|
right: 55,
|
||||||
|
child: Container(
|
||||||
|
height: 32,
|
||||||
|
width: 32,
|
||||||
|
margin: EdgeInsets.only(left: 14, top: 4, bottom: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.display1
|
||||||
|
.color,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(6))),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => widget.allAmount?.call(),
|
||||||
|
child: Center(
|
||||||
|
child: Text(S.of(context).all,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.display1
|
||||||
|
.decorationColor)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
))
|
||||||
],
|
],
|
||||||
)),
|
)),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 5),
|
padding: EdgeInsets.only(top: 5),
|
||||||
child: Row(mainAxisAlignment: MainAxisAlignment.start, children: <
|
child: Container(
|
||||||
Widget>[
|
height: 15,
|
||||||
_min != null
|
child: Row(
|
||||||
? Text(
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
S.of(context).min_value(_min, _selectedCurrency.toString()),
|
children: <Widget>[
|
||||||
style: TextStyle(
|
_min != null
|
||||||
fontSize: 10,
|
? Text(
|
||||||
height: 1.2,
|
S
|
||||||
color: Theme.of(context)
|
.of(context)
|
||||||
.textTheme
|
.min_value(_min, _selectedCurrency.toString()),
|
||||||
.subhead
|
style: TextStyle(
|
||||||
.decorationColor),
|
fontSize: 10,
|
||||||
)
|
height: 1.2,
|
||||||
: Offstage(),
|
color: Theme.of(context)
|
||||||
_min != null ? SizedBox(width: 10) : Offstage(),
|
.accentTextTheme
|
||||||
_max != null
|
.display4
|
||||||
? Text(
|
.decorationColor),
|
||||||
S.of(context).max_value(_max, _selectedCurrency.toString()),
|
)
|
||||||
style: TextStyle(
|
: Offstage(),
|
||||||
fontSize: 10,
|
_min != null ? SizedBox(width: 10) : Offstage(),
|
||||||
height: 1.2,
|
_max != null
|
||||||
color: Theme.of(context)
|
? Text(
|
||||||
.textTheme
|
S
|
||||||
.subhead
|
.of(context)
|
||||||
.decorationColor))
|
.max_value(_max, _selectedCurrency.toString()),
|
||||||
: Offstage(),
|
style: TextStyle(
|
||||||
]),
|
fontSize: 10,
|
||||||
|
height: 1.2,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.accentTextTheme
|
||||||
|
.display4
|
||||||
|
.decorationColor))
|
||||||
|
: Offstage(),
|
||||||
|
])),
|
||||||
),
|
),
|
||||||
!_isAddressEditable && widget.hasRefundAddress
|
!_isAddressEditable && widget.hasRefundAddress
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: EdgeInsets.only(top: 20),
|
padding: EdgeInsets.only(top: 20),
|
||||||
child: Text(
|
child: Text(
|
||||||
S.of(context).refund_address,
|
S.of(context).refund_address,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color:
|
color: Theme.of(context)
|
||||||
Theme.of(context).textTheme.subhead.decorationColor),
|
.accentTextTheme
|
||||||
))
|
.display4
|
||||||
|
.decorationColor),
|
||||||
|
))
|
||||||
: Offstage(),
|
: Offstage(),
|
||||||
_isAddressEditable
|
_isAddressEditable
|
||||||
? Padding(
|
? Padding(
|
||||||
|
@ -248,7 +290,8 @@ class ExchangeCardState extends State<ExchangeCard> {
|
||||||
child: AddressTextField(
|
child: AddressTextField(
|
||||||
controller: addressController,
|
controller: addressController,
|
||||||
placeholder: widget.hasRefundAddress
|
placeholder: widget.hasRefundAddress
|
||||||
? S.of(context).refund_address : null,
|
? S.of(context).refund_address
|
||||||
|
: null,
|
||||||
options: [
|
options: [
|
||||||
AddressTextFieldOption.paste,
|
AddressTextFieldOption.paste,
|
||||||
AddressTextFieldOption.qrCode,
|
AddressTextFieldOption.qrCode,
|
||||||
|
@ -262,8 +305,10 @@ class ExchangeCardState extends State<ExchangeCard> {
|
||||||
hintStyle: TextStyle(
|
hintStyle: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color:
|
color: Theme.of(context)
|
||||||
Theme.of(context).textTheme.subhead.decorationColor),
|
.accentTextTheme
|
||||||
|
.display4
|
||||||
|
.decorationColor),
|
||||||
buttonColor: widget.addressButtonsColor,
|
buttonColor: widget.addressButtonsColor,
|
||||||
validator: widget.addressTextFieldValidator,
|
validator: widget.addressTextFieldValidator,
|
||||||
),
|
),
|
||||||
|
@ -275,8 +320,8 @@ class ExchangeCardState extends State<ExchangeCard> {
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(
|
Clipboard.setData(
|
||||||
ClipboardData(text: addressController.text));
|
ClipboardData(text: addressController.text));
|
||||||
showBar<void>(context,
|
showBar<void>(
|
||||||
S.of(context).copied_to_clipboard);
|
context, S.of(context).copied_to_clipboard);
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
|
|
@ -6,8 +6,6 @@ import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/core/execution_state.dart';
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
import 'package:cake_wallet/entities/crypto_currency.dart';
|
|
||||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
|
import 'package:cake_wallet/src/screens/exchange_trade/information_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
|
import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
|
||||||
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||||
|
@ -172,36 +170,14 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
||||||
),
|
),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final item = widget.exchangeTradeViewModel.items[index];
|
final item = widget.exchangeTradeViewModel.items[index];
|
||||||
String value;
|
final value = item.data ?? fetchingLabel;
|
||||||
|
|
||||||
final content = Observer(builder: (_) {
|
final content = StandartListRow(
|
||||||
switch (index) {
|
title: item.title,
|
||||||
case 0:
|
value: value,
|
||||||
value =
|
valueFontSize: 14,
|
||||||
'${widget.exchangeTradeViewModel.trade.id ?? fetchingLabel}';
|
image: item.isCopied ? copyImage : null,
|
||||||
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
|
return item.isCopied
|
||||||
? Builder(
|
? Builder(
|
||||||
|
@ -227,16 +203,15 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
||||||
final sendingState =
|
final sendingState =
|
||||||
widget.exchangeTradeViewModel.sendViewModel.state;
|
widget.exchangeTradeViewModel.sendViewModel.state;
|
||||||
|
|
||||||
return trade.from == CryptoCurrency.xmr && !(sendingState is TransactionCommitted)
|
return widget.exchangeTradeViewModel.isSendable &&
|
||||||
|
!(sendingState is TransactionCommitted)
|
||||||
? LoadingPrimaryButton(
|
? LoadingPrimaryButton(
|
||||||
isDisabled: trade.inputAddress == null ||
|
isDisabled: trade.inputAddress == null ||
|
||||||
trade.inputAddress.isEmpty,
|
trade.inputAddress.isEmpty,
|
||||||
isLoading: sendingState is IsExecutingState,
|
isLoading: sendingState is IsExecutingState,
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
widget.exchangeTradeViewModel.confirmSending(),
|
widget.exchangeTradeViewModel.confirmSending(),
|
||||||
text: trade.provider == ExchangeProviderDescription.xmrto
|
text: S.of(context).confirm,
|
||||||
? S.of(context).confirm
|
|
||||||
: S.of(context).send_xmr,
|
|
||||||
color: Theme.of(context).accentTextTheme.body2.color,
|
color: Theme.of(context).accentTextTheme.body2.color,
|
||||||
textColor: Colors.white)
|
textColor: Colors.white)
|
||||||
: Offstage();
|
: Offstage();
|
||||||
|
@ -306,7 +281,11 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
top: 220, left: 24, right: 24),
|
top: 220, left: 24, right: 24),
|
||||||
child: Text(
|
child: Text(
|
||||||
S.of(context).send_success,
|
S.of(context).send_success(widget
|
||||||
|
.exchangeTradeViewModel
|
||||||
|
.wallet
|
||||||
|
.currency
|
||||||
|
.toString()),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
|
@ -379,7 +358,16 @@ class ExchangeTradeState extends State<ExchangeTradeForm> {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
actionLeftButton: () => Navigator.of(context).pop());
|
actionLeftButton: () => Navigator.of(context).pop(),
|
||||||
|
feeFiatAmount: widget.exchangeTradeViewModel.sendViewModel
|
||||||
|
.pendingTransaction.feeFormatted,
|
||||||
|
fiatAmountValue: widget.exchangeTradeViewModel.sendViewModel
|
||||||
|
.pendingTransactionFeeFiatAmount +
|
||||||
|
' ' +
|
||||||
|
widget.exchangeTradeViewModel.sendViewModel.fiat.title,
|
||||||
|
recipientTitle: S.of(context).recipient_address,
|
||||||
|
recipientAddress:
|
||||||
|
widget.exchangeTradeViewModel.sendViewModel.address);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:cake_wallet/di.dart';
|
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
@ -22,17 +21,24 @@ class NewWalletPage extends BasePage {
|
||||||
|
|
||||||
final WalletNewVM _walletNewVM;
|
final WalletNewVM _walletNewVM;
|
||||||
|
|
||||||
|
final walletNameImage = Image.asset('assets/images/wallet_name.png');
|
||||||
|
final walletNameLightImage =
|
||||||
|
Image.asset('assets/images/wallet_name_light.png');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => S.current.new_wallet;
|
String get title => S.current.new_wallet;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) => WalletNameForm(_walletNewVM);
|
Widget body(BuildContext context) => WalletNameForm(_walletNewVM,
|
||||||
|
currentTheme.type == ThemeType.dark
|
||||||
|
? walletNameImage : walletNameLightImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
class WalletNameForm extends StatefulWidget {
|
class WalletNameForm extends StatefulWidget {
|
||||||
WalletNameForm(this._walletNewVM);
|
WalletNameForm(this._walletNewVM, this.walletImage);
|
||||||
|
|
||||||
final WalletNewVM _walletNewVM;
|
final WalletNewVM _walletNewVM;
|
||||||
|
final Image walletImage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_WalletNameFormState createState() => _WalletNameFormState(_walletNewVM);
|
_WalletNameFormState createState() => _WalletNameFormState(_walletNewVM);
|
||||||
|
@ -43,9 +49,6 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||||
|
|
||||||
static const aspectRatioImage = 1.22;
|
static const aspectRatioImage = 1.22;
|
||||||
|
|
||||||
final walletNameImage = Image.asset('assets/images/wallet_name.png');
|
|
||||||
final walletNameLightImage =
|
|
||||||
Image.asset('assets/images/wallet_name_light.png');
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
|
final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
|
||||||
ReactionDisposer _stateReaction;
|
ReactionDisposer _stateReaction;
|
||||||
|
@ -78,10 +81,6 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final walletImage = getIt.get<SettingsStore>().isDarkTheme
|
|
||||||
? walletNameImage
|
|
||||||
: walletNameLightImage;
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(top: 24),
|
padding: EdgeInsets.only(top: 24),
|
||||||
child: ScrollableWithBottomSection(
|
child: ScrollableWithBottomSection(
|
||||||
|
@ -92,7 +91,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
||||||
padding: EdgeInsets.only(left: 12, right: 12),
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: aspectRatioImage,
|
aspectRatio: aspectRatioImage,
|
||||||
child: FittedBox(child: walletImage, fit: BoxFit.fill)),
|
child: FittedBox(child: widget.walletImage, fit: BoxFit.fill)),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 24),
|
padding: EdgeInsets.only(top: 24),
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/di.dart';
|
||||||
import 'package:cake_wallet/entities/wallet_type.dart';
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
|
||||||
|
import 'package:flushbar/flushbar.dart';
|
||||||
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
@ -10,25 +17,36 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||||
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
|
import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
|
||||||
|
|
||||||
class NewWalletTypePage extends BasePage {
|
class NewWalletTypePage extends BasePage {
|
||||||
NewWalletTypePage({this.onTypeSelected, this.isNewWallet = true});
|
NewWalletTypePage(this.walletNewVM, {this.onTypeSelected, this.isNewWallet});
|
||||||
|
|
||||||
final void Function(BuildContext, WalletType) onTypeSelected;
|
final void Function(BuildContext, WalletType) onTypeSelected;
|
||||||
final bool isNewWallet;
|
final bool isNewWallet;
|
||||||
|
final WalletNewVM walletNewVM;
|
||||||
|
|
||||||
|
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
|
||||||
|
final walletTypeLightImage =
|
||||||
|
Image.asset('assets/images/wallet_type_light.png');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => isNewWallet
|
String get title =>
|
||||||
? S.current.new_wallet
|
isNewWallet ? S.current.new_wallet : S.current.wallet_list_restore_wallet;
|
||||||
: S.current.wallet_list_restore_wallet;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) =>
|
Widget body(BuildContext context) => WalletTypeForm(walletNewVM, isNewWallet,
|
||||||
WalletTypeForm(onTypeSelected: onTypeSelected);
|
onTypeSelected: onTypeSelected,
|
||||||
|
walletImage: currentTheme.type == ThemeType.dark
|
||||||
|
? walletTypeImage
|
||||||
|
: walletTypeLightImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
class WalletTypeForm extends StatefulWidget {
|
class WalletTypeForm extends StatefulWidget {
|
||||||
WalletTypeForm({this.onTypeSelected});
|
WalletTypeForm(this.walletNewVM, this.isNewWallet,
|
||||||
|
{this.onTypeSelected, this.walletImage});
|
||||||
|
|
||||||
final void Function(BuildContext, WalletType) onTypeSelected;
|
final void Function(BuildContext, WalletType) onTypeSelected;
|
||||||
|
final WalletNewVM walletNewVM;
|
||||||
|
final bool isNewWallet;
|
||||||
|
final Image walletImage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
WalletTypeFormState createState() => WalletTypeFormState();
|
WalletTypeFormState createState() => WalletTypeFormState();
|
||||||
|
@ -42,10 +60,12 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
final bitcoinIcon =
|
final bitcoinIcon =
|
||||||
Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
||||||
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
|
final walletTypeImage = Image.asset('assets/images/wallet_type.png');
|
||||||
final walletTypeLightImage = Image.asset('assets/images/wallet_type_light.png');
|
final walletTypeLightImage =
|
||||||
|
Image.asset('assets/images/wallet_type_light.png');
|
||||||
|
|
||||||
WalletType selected;
|
WalletType selected;
|
||||||
List<WalletType> types;
|
List<WalletType> types;
|
||||||
|
Flushbar<void> _progressBar;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -55,11 +75,8 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final walletImage = getIt.get<SettingsStore>().isDarkTheme
|
|
||||||
? walletTypeImage : walletTypeLightImage;
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(top: 24),
|
padding: EdgeInsets.only(top: 24, bottom: 24),
|
||||||
child: ScrollableWithBottomSection(
|
child: ScrollableWithBottomSection(
|
||||||
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||||
content: Column(
|
content: Column(
|
||||||
|
@ -69,7 +86,8 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
padding: EdgeInsets.only(left: 12, right: 12),
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: aspectRatioImage,
|
aspectRatio: aspectRatioImage,
|
||||||
child: FittedBox(child: walletImage, fit: BoxFit.fill)),
|
child:
|
||||||
|
FittedBox(child: widget.walletImage, fit: BoxFit.fill)),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 48),
|
padding: EdgeInsets.only(top: 48),
|
||||||
|
@ -86,7 +104,7 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
padding: EdgeInsets.only(top: 24),
|
padding: EdgeInsets.only(top: 24),
|
||||||
child: SelectButton(
|
child: SelectButton(
|
||||||
image: _iconFor(type),
|
image: _iconFor(type),
|
||||||
text: walletTypeToString(type),
|
text: walletTypeToDisplayName(type),
|
||||||
isSelected: selected == type,
|
isSelected: selected == type,
|
||||||
onTap: () => setState(() => selected = type)),
|
onTap: () => setState(() => selected = type)),
|
||||||
))
|
))
|
||||||
|
@ -94,9 +112,9 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
),
|
),
|
||||||
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||||
bottomSection: PrimaryButton(
|
bottomSection: PrimaryButton(
|
||||||
onPressed: () => widget.onTypeSelected(context, selected),
|
onPressed: () => onTypeSelected(),
|
||||||
text: S.of(context).seed_language_next,
|
text: S.of(context).seed_language_next,
|
||||||
color: Colors.green,
|
color: Theme.of(context).accentTextTheme.body2.color,
|
||||||
textColor: Colors.white,
|
textColor: Colors.white,
|
||||||
isDisabled: selected == null,
|
isDisabled: selected == null,
|
||||||
),
|
),
|
||||||
|
@ -114,4 +132,35 @@ class WalletTypeFormState extends State<WalletTypeForm> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> onTypeSelected() async {
|
||||||
|
if (!widget.isNewWallet) {
|
||||||
|
widget.onTypeSelected(context, selected);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
_changeProcessText(S.of(context).creating_new_wallet);
|
||||||
|
widget.walletNewVM.type = selected;
|
||||||
|
await widget.walletNewVM
|
||||||
|
.create(options: 'English'); // FIXME: Unnamed constant
|
||||||
|
await _progressBar?.dismiss();
|
||||||
|
final state = widget.walletNewVM.state;
|
||||||
|
|
||||||
|
if (state is ExecutedSuccessfullyState) {
|
||||||
|
widget.onTypeSelected(context, selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state is FailureState) {
|
||||||
|
_changeProcessText(
|
||||||
|
S.of(context).creating_new_wallet_error(state.error));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_changeProcessText(S.of(context).creating_new_wallet_error(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _changeProcessText(String text) {
|
||||||
|
_progressBar = createBar<void>(text, duration: null)..show(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ class SelectButton extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final color = isSelected
|
final color = isSelected
|
||||||
? Theme.of(context).accentTextTheme.subtitle.decorationColor
|
? Colors.green
|
||||||
: Theme.of(context).accentTextTheme.caption.color;
|
: Theme.of(context).accentTextTheme.caption.color;
|
||||||
final textColor = isSelected
|
final textColor = isSelected
|
||||||
? Theme.of(context).accentTextTheme.headline.decorationColor
|
? Theme.of(context).accentTextTheme.headline.decorationColor
|
||||||
|
|
|
@ -65,98 +65,90 @@ class NodeListPage extends BasePage {
|
||||||
padding: EdgeInsets.only(top: 10),
|
padding: EdgeInsets.only(top: 10),
|
||||||
child: Observer(
|
child: Observer(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return nodeListViewModel.nodes.isNotEmpty
|
return SectionStandardList(
|
||||||
? SectionStandardList(
|
sectionCount: 2,
|
||||||
sectionCount: 2,
|
context: context,
|
||||||
context: context,
|
itemCounter: (int sectionIndex) {
|
||||||
itemCounter: (int sectionIndex) {
|
if (sectionIndex == 0) {
|
||||||
if (sectionIndex == 0) {
|
return 1;
|
||||||
return 1;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return nodeListViewModel.nodes.length;
|
return nodeListViewModel.nodes.length;
|
||||||
},
|
},
|
||||||
itemBuilder: (_, sectionIndex, index) {
|
itemBuilder: (_, sectionIndex, index) {
|
||||||
if (sectionIndex == 0) {
|
if (sectionIndex == 0) {
|
||||||
return NodeHeaderListRow(
|
return NodeHeaderListRow(
|
||||||
title: S.of(context).add_new_node,
|
title: S.of(context).add_new_node,
|
||||||
onTap: (_) async => await Navigator.of(context)
|
onTap: (_) async => await Navigator.of(context)
|
||||||
.pushNamed(Routes.newNode));
|
.pushNamed(Routes.newNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
final node = nodeListViewModel.nodes[index];
|
final node = nodeListViewModel.nodes[index];
|
||||||
final isSelected = node.keyIndex ==
|
final isSelected =
|
||||||
nodeListViewModel.settingsStore.currentNode.keyIndex;
|
node.keyIndex == nodeListViewModel.currentNode?.keyIndex;
|
||||||
final nodeListRow = NodeListRow(
|
final nodeListRow = NodeListRow(
|
||||||
title: node.uri,
|
title: node.uri,
|
||||||
isSelected: isSelected,
|
isSelected: isSelected,
|
||||||
isAlive: node.requestNode(),
|
isAlive: node.requestNode(),
|
||||||
onTap: (_) async {
|
onTap: (_) async {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await showPopUp<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertWithTwoActions(
|
||||||
|
alertTitle:
|
||||||
|
S.of(context).change_current_node_title,
|
||||||
|
alertContent:
|
||||||
|
S.of(context).change_current_node(node.uri),
|
||||||
|
leftButtonText: S.of(context).cancel,
|
||||||
|
rightButtonText: S.of(context).change,
|
||||||
|
actionLeftButton: () =>
|
||||||
|
Navigator.of(context).pop(),
|
||||||
|
actionRightButton: () async {
|
||||||
|
await nodeListViewModel.setAsCurrent(node);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
final dismissibleRow = Slidable(
|
||||||
|
key: Key('${node.keyIndex}'),
|
||||||
|
actionPane: SlidableDrawerActionPane(),
|
||||||
|
child: nodeListRow,
|
||||||
|
secondaryActions: <Widget>[
|
||||||
|
IconSlideAction(
|
||||||
|
caption: S.of(context).delete,
|
||||||
|
color: Colors.red,
|
||||||
|
icon: CupertinoIcons.delete,
|
||||||
|
onTap: () async {
|
||||||
|
final confirmed = await showPopUp<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return AlertWithTwoActions(
|
||||||
|
alertTitle: S.of(context).remove_node,
|
||||||
|
alertContent:
|
||||||
|
S.of(context).remove_node_message,
|
||||||
|
rightButtonText: S.of(context).remove,
|
||||||
|
leftButtonText: S.of(context).cancel,
|
||||||
|
actionRightButton: () =>
|
||||||
|
Navigator.pop(context, true),
|
||||||
|
actionLeftButton: () =>
|
||||||
|
Navigator.pop(context, false));
|
||||||
|
}) ??
|
||||||
|
false;
|
||||||
|
|
||||||
|
if (confirmed) {
|
||||||
|
await nodeListViewModel.delete(node);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
await showPopUp<void>(
|
return isSelected ? nodeListRow : dismissibleRow;
|
||||||
context: context,
|
});
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertWithTwoActions(
|
|
||||||
alertTitle: S.of(context)
|
|
||||||
.change_current_node_title,
|
|
||||||
alertContent: S
|
|
||||||
.of(context)
|
|
||||||
.change_current_node(node.uri),
|
|
||||||
leftButtonText: S.of(context).cancel,
|
|
||||||
rightButtonText: S.of(context).change,
|
|
||||||
actionLeftButton: () =>
|
|
||||||
Navigator.of(context).pop(),
|
|
||||||
actionRightButton: () async {
|
|
||||||
await nodeListViewModel
|
|
||||||
.setAsCurrent(node);
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
final dismissibleRow = Slidable(
|
|
||||||
key: Key('${node.keyIndex}'),
|
|
||||||
actionPane: SlidableDrawerActionPane(),
|
|
||||||
child: nodeListRow,
|
|
||||||
secondaryActions: <Widget>[
|
|
||||||
IconSlideAction(
|
|
||||||
caption: S.of(context).delete,
|
|
||||||
color: Colors.red,
|
|
||||||
icon: CupertinoIcons.delete,
|
|
||||||
onTap: () async {
|
|
||||||
final confirmed = await showPopUp<bool>(
|
|
||||||
context: context,
|
|
||||||
builder: (BuildContext context) {
|
|
||||||
return AlertWithTwoActions(
|
|
||||||
alertTitle:
|
|
||||||
S.of(context).remove_node,
|
|
||||||
alertContent: S
|
|
||||||
.of(context)
|
|
||||||
.remove_node_message,
|
|
||||||
rightButtonText:
|
|
||||||
S.of(context).remove,
|
|
||||||
leftButtonText:
|
|
||||||
S.of(context).cancel,
|
|
||||||
actionRightButton: () =>
|
|
||||||
Navigator.pop(context, true),
|
|
||||||
actionLeftButton: () =>
|
|
||||||
Navigator.pop(context, false));
|
|
||||||
}) ??
|
|
||||||
false;
|
|
||||||
|
|
||||||
if (confirmed) {
|
|
||||||
await nodeListViewModel.delete(node);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return isSelected ? nodeListRow : dismissibleRow;
|
|
||||||
})
|
|
||||||
: Container();
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
||||||
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
@ -27,16 +28,47 @@ class ReceivePage extends BasePage {
|
||||||
String get title => S.current.receive;
|
String get title => S.current.receive;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Color get backgroundLightColor => Colors.transparent;
|
Color get backgroundLightColor => currentTheme.type == ThemeType.bright
|
||||||
|
? Colors.transparent : Colors.white;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Color get backgroundDarkColor => Colors.transparent;
|
Color get backgroundDarkColor => Colors.transparent;
|
||||||
|
|
||||||
@override
|
|
||||||
Color get titleColor => Colors.white;
|
|
||||||
|
|
||||||
final FocusNode _cryptoAmountFocus;
|
final FocusNode _cryptoAmountFocus;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget leading(BuildContext context) {
|
||||||
|
final _backButton = Icon(Icons.arrow_back_ios,
|
||||||
|
color: Theme.of(context).accentTextTheme.display3.backgroundColor,
|
||||||
|
size: 16,);
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
height: 37,
|
||||||
|
width: 37,
|
||||||
|
child: ButtonTheme(
|
||||||
|
minWidth: double.minPositive,
|
||||||
|
child: FlatButton(
|
||||||
|
highlightColor: Colors.transparent,
|
||||||
|
splashColor: Colors.transparent,
|
||||||
|
padding: EdgeInsets.all(0),
|
||||||
|
onPressed: () => onClose(context),
|
||||||
|
child: _backButton),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget middle(BuildContext context) {
|
||||||
|
return Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 18.0,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: Theme.of(context).accentTextTheme.display3.backgroundColor),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget Function(BuildContext, Widget) get rootWrapper =>
|
Widget Function(BuildContext, Widget) get rootWrapper =>
|
||||||
(BuildContext context, Widget scaffold) => Container(
|
(BuildContext context, Widget scaffold) => Container(
|
||||||
|
@ -51,7 +83,8 @@ class ReceivePage extends BasePage {
|
||||||
@override
|
@override
|
||||||
Widget trailing(BuildContext context) {
|
Widget trailing(BuildContext context) {
|
||||||
final shareImage =
|
final shareImage =
|
||||||
Image.asset('assets/images/share.png', color: Colors.white);
|
Image.asset('assets/images/share.png',
|
||||||
|
color: Theme.of(context).accentTextTheme.display3.backgroundColor);
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 20.0,
|
height: 20.0,
|
||||||
|
@ -74,9 +107,8 @@ class ReceivePage extends BasePage {
|
||||||
return KeyboardActions(
|
return KeyboardActions(
|
||||||
config: KeyboardActionsConfig(
|
config: KeyboardActionsConfig(
|
||||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||||
keyboardBarColor: isDarkTheme
|
keyboardBarColor: Theme.of(context).accentTextTheme.body2
|
||||||
? Color.fromRGBO(48, 51, 60, 1.0)
|
.backgroundColor,
|
||||||
: Color.fromRGBO(98, 98, 98, 1.0),
|
|
||||||
nextFocus: false,
|
nextFocus: false,
|
||||||
actions: [
|
actions: [
|
||||||
KeyboardActionsItem(
|
KeyboardActionsItem(
|
||||||
|
|
|
@ -47,14 +47,14 @@ class QRWidget extends StatelessWidget {
|
||||||
child: QrImage(
|
child: QrImage(
|
||||||
data: addressListViewModel.uri.toString(),
|
data: addressListViewModel.uri.toString(),
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Theme.of(context).accentTextTheme.
|
||||||
//Theme.of(context).textTheme.headline.color,
|
display3.backgroundColor,
|
||||||
))))),
|
))))),
|
||||||
Spacer(flex: 3)
|
Spacer(flex: 3)
|
||||||
]),
|
]),
|
||||||
isAmountFieldShow
|
isAmountFieldShow
|
||||||
? Padding(
|
? Padding(
|
||||||
padding: EdgeInsets.only(top: 60),
|
padding: EdgeInsets.only(top: 40),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -67,11 +67,12 @@ class QRWidget extends StatelessWidget {
|
||||||
decimal: true),
|
decimal: true),
|
||||||
inputFormatters: [
|
inputFormatters: [
|
||||||
BlacklistingTextInputFormatter(
|
BlacklistingTextInputFormatter(
|
||||||
RegExp('[\\-|\\ |\\,]'))
|
RegExp('[\\-|\\ ]'))
|
||||||
],
|
],
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
hintText: S.of(context).receive_amount,
|
hintText: S.of(context).receive_amount,
|
||||||
textColor: Colors.white,
|
textColor: Theme.of(context).accentTextTheme.
|
||||||
|
display3.backgroundColor,
|
||||||
borderColor: Theme.of(context)
|
borderColor: Theme.of(context)
|
||||||
.textTheme
|
.textTheme
|
||||||
.headline
|
.headline
|
||||||
|
@ -108,9 +109,10 @@ class QRWidget extends StatelessWidget {
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 15,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Colors.white),
|
color: Theme.of(context).accentTextTheme.
|
||||||
|
display3.backgroundColor),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
@ -7,12 +9,20 @@ import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||||
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
|
||||||
|
|
||||||
class WalletRestoreFromSeedForm extends StatefulWidget {
|
class WalletRestoreFromSeedForm extends StatefulWidget {
|
||||||
WalletRestoreFromSeedForm({Key key, this.blockHeightFocusNode,
|
WalletRestoreFromSeedForm(
|
||||||
this.onHeightOrDateEntered})
|
{Key key,
|
||||||
|
@required this.displayLanguageSelector,
|
||||||
|
@required this.displayBlockHeightSelector,
|
||||||
|
@required this.type,
|
||||||
|
this.blockHeightFocusNode,
|
||||||
|
this.onHeightOrDateEntered})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
|
final WalletType type;
|
||||||
|
final bool displayLanguageSelector;
|
||||||
|
final bool displayBlockHeightSelector;
|
||||||
final FocusNode blockHeightFocusNode;
|
final FocusNode blockHeightFocusNode;
|
||||||
final Function (bool) onHeightOrDateEntered;
|
final Function(bool) onHeightOrDateEntered;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
WalletRestoreFromSeedFormState createState() =>
|
WalletRestoreFromSeedFormState createState() =>
|
||||||
|
@ -41,32 +51,35 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(left: 25, right: 25),
|
padding: EdgeInsets.only(left: 25, right: 25),
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
SeedWidget(key: seedWidgetStateKey, language: language),
|
SeedWidget(
|
||||||
GestureDetector(
|
key: seedWidgetStateKey, language: language, type: widget.type),
|
||||||
onTap: () async {
|
if (widget.displayLanguageSelector)
|
||||||
final selected = await showPopUp<String>(
|
GestureDetector(
|
||||||
context: context,
|
onTap: () async {
|
||||||
builder: (BuildContext context) =>
|
final selected = await showPopUp<String>(
|
||||||
SeedLanguagePicker(selected: language));
|
context: context,
|
||||||
|
builder: (BuildContext context) =>
|
||||||
|
SeedLanguagePicker(selected: language));
|
||||||
|
|
||||||
if (selected == null || selected.isEmpty) {
|
if (selected == null || selected.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_changeLanguage(selected);
|
_changeLanguage(selected);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
padding: EdgeInsets.only(top: 20.0),
|
padding: EdgeInsets.only(top: 20.0),
|
||||||
child: IgnorePointer(
|
child: IgnorePointer(
|
||||||
child: BaseTextFormField(
|
child: BaseTextFormField(
|
||||||
controller: languageController,
|
controller: languageController,
|
||||||
enableInteractiveSelection: false,
|
enableInteractiveSelection: false,
|
||||||
readOnly: true)))),
|
readOnly: true)))),
|
||||||
BlockchainHeightWidget(
|
if (widget.displayBlockHeightSelector)
|
||||||
focusNode: widget.blockHeightFocusNode,
|
BlockchainHeightWidget(
|
||||||
key: blockchainHeightKey,
|
focusNode: widget.blockHeightFocusNode,
|
||||||
onHeightOrDateEntered: widget.onHeightOrDateEntered)
|
key: blockchainHeightKey,
|
||||||
|
onHeightOrDateEntered: widget.onHeightOrDateEntered)
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,16 +25,30 @@ class WalletRestorePage extends BasePage {
|
||||||
_pages = [],
|
_pages = [],
|
||||||
_blockHeightFocusNode = FocusNode(),
|
_blockHeightFocusNode = FocusNode(),
|
||||||
_controller = PageController(initialPage: 0) {
|
_controller = PageController(initialPage: 0) {
|
||||||
_pages.addAll([
|
walletRestoreViewModel.availableModes.forEach((mode) {
|
||||||
WalletRestoreFromSeedForm(
|
switch (mode) {
|
||||||
key: walletRestoreFromSeedFormKey,
|
case WalletRestoreMode.seed:
|
||||||
blockHeightFocusNode: _blockHeightFocusNode,
|
_pages.add(WalletRestoreFromSeedForm(
|
||||||
onHeightOrDateEntered: (value)
|
displayBlockHeightSelector:
|
||||||
=> walletRestoreViewModel.isButtonEnabled = value),
|
walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
|
||||||
WalletRestoreFromKeysFrom(key: walletRestoreFromKeysFormKey,
|
displayLanguageSelector:
|
||||||
onHeightOrDateEntered: (value)
|
walletRestoreViewModel.hasSeedLanguageSelector,
|
||||||
=> walletRestoreViewModel.isButtonEnabled = value)
|
type: walletRestoreViewModel.type,
|
||||||
]);
|
key: walletRestoreFromSeedFormKey,
|
||||||
|
blockHeightFocusNode: _blockHeightFocusNode,
|
||||||
|
onHeightOrDateEntered: (value) =>
|
||||||
|
walletRestoreViewModel.isButtonEnabled = value));
|
||||||
|
break;
|
||||||
|
case WalletRestoreMode.keys:
|
||||||
|
_pages.add(WalletRestoreFromKeysFrom(
|
||||||
|
key: walletRestoreFromKeysFormKey,
|
||||||
|
onHeightOrDateEntered: (value) =>
|
||||||
|
walletRestoreViewModel.isButtonEnabled = value));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -76,20 +90,19 @@ class WalletRestorePage extends BasePage {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode)
|
reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode) {
|
||||||
{
|
walletRestoreViewModel.isButtonEnabled = false;
|
||||||
walletRestoreViewModel.isButtonEnabled = false;
|
|
||||||
|
|
||||||
walletRestoreFromSeedFormKey.currentState.blockchainHeightKey
|
walletRestoreFromSeedFormKey.currentState.blockchainHeightKey.currentState
|
||||||
.currentState.restoreHeightController.text = '';
|
.restoreHeightController.text = '';
|
||||||
walletRestoreFromSeedFormKey.currentState.blockchainHeightKey
|
walletRestoreFromSeedFormKey.currentState.blockchainHeightKey.currentState
|
||||||
.currentState.dateController.text = '';
|
.dateController.text = '';
|
||||||
|
|
||||||
walletRestoreFromKeysFormKey.currentState.blockchainHeightKey
|
walletRestoreFromKeysFormKey.currentState.blockchainHeightKey.currentState
|
||||||
.currentState.restoreHeightController.text = '';
|
.restoreHeightController.text = '';
|
||||||
walletRestoreFromKeysFormKey.currentState.blockchainHeightKey
|
walletRestoreFromKeysFormKey.currentState.blockchainHeightKey.currentState
|
||||||
.currentState.dateController.text = '';
|
.dateController.text = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -100,40 +113,37 @@ class WalletRestorePage extends BasePage {
|
||||||
},
|
},
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
itemCount: _pages.length,
|
itemCount: _pages.length,
|
||||||
itemBuilder: (_, index) => SingleChildScrollView(child: _pages[index]))),
|
itemBuilder: (_, index) =>
|
||||||
Padding(
|
SingleChildScrollView(child: _pages[index]))),
|
||||||
padding: EdgeInsets.only(top: 10),
|
if (_pages.length > 1)
|
||||||
child: SmoothPageIndicator(
|
Padding(
|
||||||
controller: _controller,
|
padding: EdgeInsets.only(top: 10),
|
||||||
count: _pages.length,
|
child: SmoothPageIndicator(
|
||||||
effect: ColorTransitionEffect(
|
controller: _controller,
|
||||||
spacing: 6.0,
|
count: _pages.length,
|
||||||
radius: 6.0,
|
effect: ColorTransitionEffect(
|
||||||
dotWidth: 6.0,
|
spacing: 6.0,
|
||||||
dotHeight: 6.0,
|
radius: 6.0,
|
||||||
dotColor: Theme.of(context).hintColor.withOpacity(0.5),
|
dotWidth: 6.0,
|
||||||
activeDotColor: Theme.of(context).hintColor),
|
dotHeight: 6.0,
|
||||||
)),
|
dotColor: Theme.of(context).hintColor.withOpacity(0.5),
|
||||||
|
activeDotColor: Theme.of(context).hintColor),
|
||||||
|
)),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 20, bottom: 40, left: 25, right: 25),
|
padding: EdgeInsets.only(top: 20, bottom: 40, left: 25, right: 25),
|
||||||
child: Observer(
|
child: Observer(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return LoadingPrimaryButton(
|
return LoadingPrimaryButton(
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
walletRestoreViewModel.create(options: _credentials()),
|
walletRestoreViewModel.create(options: _credentials()),
|
||||||
text: S.of(context).restore_recover,
|
text: S.of(context).restore_recover,
|
||||||
color: Theme
|
color:
|
||||||
.of(context)
|
Theme.of(context).accentTextTheme.subtitle.decorationColor,
|
||||||
.accentTextTheme
|
textColor:
|
||||||
.subtitle
|
Theme.of(context).accentTextTheme.headline.decorationColor,
|
||||||
.decorationColor,
|
isLoading: walletRestoreViewModel.state is IsExecutingState,
|
||||||
textColor: Theme
|
isDisabled: !walletRestoreViewModel.isButtonEnabled,
|
||||||
.of(context)
|
);
|
||||||
.accentTextTheme
|
|
||||||
.headline
|
|
||||||
.decorationColor,
|
|
||||||
isLoading: walletRestoreViewModel.state is IsExecutingState,
|
|
||||||
isDisabled: !walletRestoreViewModel.isButtonEnabled,);
|
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
]);
|
]);
|
||||||
|
@ -145,8 +155,11 @@ class WalletRestorePage extends BasePage {
|
||||||
if (walletRestoreViewModel.mode == WalletRestoreMode.seed) {
|
if (walletRestoreViewModel.mode == WalletRestoreMode.seed) {
|
||||||
credentials['seed'] = walletRestoreFromSeedFormKey
|
credentials['seed'] = walletRestoreFromSeedFormKey
|
||||||
.currentState.seedWidgetStateKey.currentState.text;
|
.currentState.seedWidgetStateKey.currentState.text;
|
||||||
credentials['height'] = walletRestoreFromSeedFormKey
|
|
||||||
.currentState.blockchainHeightKey.currentState.height;
|
if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
|
||||||
|
credentials['height'] = walletRestoreFromSeedFormKey
|
||||||
|
.currentState.blockchainHeightKey.currentState.height;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
credentials['address'] =
|
credentials['address'] =
|
||||||
walletRestoreFromKeysFormKey.currentState.addressController.text;
|
walletRestoreFromKeysFormKey.currentState.addressController.text;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/entities/wallet_type.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
@ -8,8 +8,17 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
|
||||||
class PreSeedPage extends BasePage {
|
class PreSeedPage extends BasePage {
|
||||||
static final imageLight = Image.asset('assets/images/pre_seed_light.png');
|
PreSeedPage(this.type)
|
||||||
static final imageDark = Image.asset('assets/images/pre_seed_dark.png');
|
: imageLight = Image.asset('assets/images/pre_seed_light.png'),
|
||||||
|
imageDark = Image.asset('assets/images/pre_seed_dark.png'),
|
||||||
|
wordsCount = type == WalletType.monero
|
||||||
|
? 25
|
||||||
|
: 12; // FIXME: Stupid fast implementation
|
||||||
|
|
||||||
|
final Image imageDark;
|
||||||
|
final Image imageLight;
|
||||||
|
final WalletType type;
|
||||||
|
final int wordsCount;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget leading(BuildContext context) => null;
|
Widget leading(BuildContext context) => null;
|
||||||
|
@ -19,8 +28,7 @@ class PreSeedPage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
final image =
|
final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight;
|
||||||
getIt.get<SettingsStore>().isDarkTheme ? imageDark : imageLight;
|
|
||||||
|
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () async => false,
|
onWillPop: () async => false,
|
||||||
|
@ -41,7 +49,7 @@ class PreSeedPage extends BasePage {
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 70, left: 16, right: 16),
|
padding: EdgeInsets.only(top: 70, left: 16, right: 16),
|
||||||
child: Text(
|
child: Text(
|
||||||
S.of(context).pre_seed_description,
|
S.of(context).pre_seed_description(wordsCount.toString()),
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'package:cake_wallet/di.dart';
|
|
||||||
import 'package:cake_wallet/palette.dart';
|
import 'package:cake_wallet/palette.dart';
|
||||||
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
@ -17,8 +16,8 @@ import 'package:cake_wallet/view_model/wallet_seed_view_model.dart';
|
||||||
class WalletSeedPage extends BasePage {
|
class WalletSeedPage extends BasePage {
|
||||||
WalletSeedPage(this.walletSeedViewModel, {@required this.isNewWalletCreated});
|
WalletSeedPage(this.walletSeedViewModel, {@required this.isNewWalletCreated});
|
||||||
|
|
||||||
static final imageLight = Image.asset('assets/images/crypto_lock_light.png');
|
final imageLight = Image.asset('assets/images/crypto_lock_light.png');
|
||||||
static final imageDark = Image.asset('assets/images/crypto_lock.png');
|
final imageDark = Image.asset('assets/images/crypto_lock.png');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => S.current.seed_title;
|
String get title => S.current.seed_title;
|
||||||
|
@ -83,8 +82,7 @@ class WalletSeedPage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
final image =
|
final image = currentTheme.type == ThemeType.dark ? imageDark : imageLight;
|
||||||
getIt.get<SettingsStore>().isDarkTheme ? imageDark : imageLight;
|
|
||||||
|
|
||||||
return WillPopScope(onWillPop: () async => false, child: Container(
|
return WillPopScope(onWillPop: () async => false, child: Container(
|
||||||
padding: EdgeInsets.all(24),
|
padding: EdgeInsets.all(24),
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
|
import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
@ -15,17 +14,26 @@ class SeedLanguage extends BasePage {
|
||||||
|
|
||||||
final Function(BuildContext, String) onConfirm;
|
final Function(BuildContext, String) onConfirm;
|
||||||
|
|
||||||
|
final walletNameImage = Image.asset('assets/images/wallet_name.png');
|
||||||
|
final walletNameLightImage =
|
||||||
|
Image.asset('assets/images/wallet_name_light.png');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => S.current.wallet_list_restore_wallet;
|
String get title => S.current.wallet_list_restore_wallet;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) => SeedLanguageForm(onConfirm: onConfirm);
|
Widget body(BuildContext context) =>
|
||||||
|
SeedLanguageForm(
|
||||||
|
onConfirm: onConfirm,
|
||||||
|
walletImage: currentTheme.type == ThemeType.dark
|
||||||
|
? walletNameImage : walletNameLightImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
class SeedLanguageForm extends StatefulWidget {
|
class SeedLanguageForm extends StatefulWidget {
|
||||||
SeedLanguageForm({this.onConfirm});
|
SeedLanguageForm({this.onConfirm, this.walletImage});
|
||||||
|
|
||||||
final Function(BuildContext, String) onConfirm;
|
final Function(BuildContext, String) onConfirm;
|
||||||
|
final Image walletImage;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
SeedLanguageFormState createState() => SeedLanguageFormState();
|
SeedLanguageFormState createState() => SeedLanguageFormState();
|
||||||
|
@ -33,18 +41,10 @@ class SeedLanguageForm extends StatefulWidget {
|
||||||
|
|
||||||
class SeedLanguageFormState extends State<SeedLanguageForm> {
|
class SeedLanguageFormState extends State<SeedLanguageForm> {
|
||||||
static const aspectRatioImage = 1.22;
|
static const aspectRatioImage = 1.22;
|
||||||
|
|
||||||
final walletNameImage = Image.asset('assets/images/wallet_name.png');
|
|
||||||
final walletNameLightImage =
|
|
||||||
Image.asset('assets/images/wallet_name_light.png');
|
|
||||||
final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
|
final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final walletImage = getIt.get<SettingsStore>().isDarkTheme
|
|
||||||
? walletNameImage
|
|
||||||
: walletNameLightImage;
|
|
||||||
|
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(top: 24),
|
padding: EdgeInsets.only(top: 24),
|
||||||
child: ScrollableWithBottomSection(
|
child: ScrollableWithBottomSection(
|
||||||
|
@ -55,7 +55,8 @@ class SeedLanguageFormState extends State<SeedLanguageForm> {
|
||||||
padding: EdgeInsets.only(left: 12, right: 12),
|
padding: EdgeInsets.only(left: 12, right: 12),
|
||||||
child: AspectRatio(
|
child: AspectRatio(
|
||||||
aspectRatio: aspectRatioImage,
|
aspectRatio: aspectRatioImage,
|
||||||
child: FittedBox(child: walletImage, fit: BoxFit.fill)),
|
child: FittedBox(child: widget.walletImage,
|
||||||
|
fit: BoxFit.fill)),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 40),
|
padding: EdgeInsets.only(top: 40),
|
||||||
|
|
|
@ -31,6 +31,7 @@ class SendPage extends BasePage {
|
||||||
: _addressController = TextEditingController(),
|
: _addressController = TextEditingController(),
|
||||||
_cryptoAmountController = TextEditingController(),
|
_cryptoAmountController = TextEditingController(),
|
||||||
_fiatAmountController = TextEditingController(),
|
_fiatAmountController = TextEditingController(),
|
||||||
|
_noteController = TextEditingController(),
|
||||||
_formKey = GlobalKey<FormState>(),
|
_formKey = GlobalKey<FormState>(),
|
||||||
_cryptoAmountFocus = FocusNode(),
|
_cryptoAmountFocus = FocusNode(),
|
||||||
_fiatAmountFocus = FocusNode(),
|
_fiatAmountFocus = FocusNode(),
|
||||||
|
@ -46,6 +47,7 @@ class SendPage extends BasePage {
|
||||||
final TextEditingController _addressController;
|
final TextEditingController _addressController;
|
||||||
final TextEditingController _cryptoAmountController;
|
final TextEditingController _cryptoAmountController;
|
||||||
final TextEditingController _fiatAmountController;
|
final TextEditingController _fiatAmountController;
|
||||||
|
final TextEditingController _noteController;
|
||||||
final GlobalKey<FormState> _formKey;
|
final GlobalKey<FormState> _formKey;
|
||||||
final FocusNode _cryptoAmountFocus;
|
final FocusNode _cryptoAmountFocus;
|
||||||
final FocusNode _fiatAmountFocus;
|
final FocusNode _fiatAmountFocus;
|
||||||
|
@ -83,9 +85,8 @@ class SendPage extends BasePage {
|
||||||
return KeyboardActions(
|
return KeyboardActions(
|
||||||
config: KeyboardActionsConfig(
|
config: KeyboardActionsConfig(
|
||||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||||
keyboardBarColor: isDarkTheme
|
keyboardBarColor: Theme.of(context).accentTextTheme.body2
|
||||||
? Color.fromRGBO(48, 51, 60, 1.0)
|
.backgroundColor,
|
||||||
: Color.fromRGBO(98, 98, 98, 1.0),
|
|
||||||
nextFocus: false,
|
nextFocus: false,
|
||||||
actions: [
|
actions: [
|
||||||
KeyboardActionsItem(
|
KeyboardActionsItem(
|
||||||
|
@ -133,7 +134,8 @@ class SendPage extends BasePage {
|
||||||
|
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
address = uri.path;
|
address = uri.path;
|
||||||
amount = uri.queryParameters['tx_amount'];
|
amount = uri.queryParameters['tx_amount'] ??
|
||||||
|
uri.queryParameters['amount'];
|
||||||
} else {
|
} else {
|
||||||
address = uri.toString();
|
address = uri.toString();
|
||||||
}
|
}
|
||||||
|
@ -304,6 +306,30 @@ class SendPage extends BasePage {
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
fontSize: 14),
|
fontSize: 14),
|
||||||
)),
|
)),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 20),
|
||||||
|
child: BaseTextFormField(
|
||||||
|
controller: _noteController,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
maxLines: null,
|
||||||
|
borderColor: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headline
|
||||||
|
.color,
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.white),
|
||||||
|
hintText: S.of(context).note_optional,
|
||||||
|
placeholderTextStyle: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headline
|
||||||
|
.decorationColor),
|
||||||
|
),
|
||||||
|
),
|
||||||
Observer(
|
Observer(
|
||||||
builder: (_) => GestureDetector(
|
builder: (_) => GestureDetector(
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
|
@ -313,6 +339,7 @@ class SendPage extends BasePage {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
MainAxisAlignment.spaceBetween,
|
MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
S
|
S
|
||||||
|
@ -326,23 +353,50 @@ class SendPage extends BasePage {
|
||||||
color: Colors.white)),
|
color: Colors.white)),
|
||||||
Container(
|
Container(
|
||||||
child: Row(
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Column(
|
||||||
sendViewModel
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
sendViewModel
|
||||||
.estimatedFee
|
.estimatedFee
|
||||||
.toString() +
|
.toString() +
|
||||||
' ' +
|
' ' +
|
||||||
sendViewModel
|
sendViewModel
|
||||||
.currency.title,
|
.currency.title,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight:
|
fontWeight:
|
||||||
FontWeight.w600,
|
FontWeight.w600,
|
||||||
//color: Theme.of(context).primaryTextTheme.display2.color,
|
//color: Theme.of(context).primaryTextTheme.display2.color,
|
||||||
color:
|
color:
|
||||||
Colors.white)),
|
Colors.white)),
|
||||||
|
Padding(
|
||||||
|
padding:
|
||||||
|
EdgeInsets.only(top: 5),
|
||||||
|
child: Text(
|
||||||
|
sendViewModel
|
||||||
|
.estimatedFeeFiatAmount
|
||||||
|
+ ' ' +
|
||||||
|
sendViewModel
|
||||||
|
.fiat.title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight:
|
||||||
|
FontWeight.w600,
|
||||||
|
color: Theme
|
||||||
|
.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headline
|
||||||
|
.decorationColor))
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
|
top: 2,
|
||||||
left: 5),
|
left: 5),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.arrow_forward_ios,
|
Icons.arrow_forward_ios,
|
||||||
|
@ -497,14 +551,8 @@ class SendPage extends BasePage {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
text: S.of(context).send,
|
text: S.of(context).send,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context).accentTextTheme.body2.color,
|
||||||
.accentTextTheme
|
textColor: Colors.white,
|
||||||
.subtitle
|
|
||||||
.decorationColor,
|
|
||||||
textColor: Theme.of(context)
|
|
||||||
.accentTextTheme
|
|
||||||
.headline
|
|
||||||
.decorationColor,
|
|
||||||
isLoading: sendViewModel.state is IsExecutingState ||
|
isLoading: sendViewModel.state is IsExecutingState ||
|
||||||
sendViewModel.state is TransactionCommitting,
|
sendViewModel.state is TransactionCommitting,
|
||||||
isDisabled:
|
isDisabled:
|
||||||
|
@ -540,6 +588,14 @@ class SendPage extends BasePage {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_noteController.addListener(() {
|
||||||
|
final note = _noteController.text ?? '';
|
||||||
|
|
||||||
|
if (note != sendViewModel.note) {
|
||||||
|
sendViewModel.note = note;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
reaction((_) => sendViewModel.sendAll, (bool all) {
|
reaction((_) => sendViewModel.sendAll, (bool all) {
|
||||||
if (all) {
|
if (all) {
|
||||||
_cryptoAmountController.text = S.current.all;
|
_cryptoAmountController.text = S.current.all;
|
||||||
|
@ -577,6 +633,12 @@ class SendPage extends BasePage {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
reaction((_) => sendViewModel.note, (String note) {
|
||||||
|
if (note != _noteController.text) {
|
||||||
|
_noteController.text = note;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
reaction((_) => sendViewModel.state, (ExecutionState state) {
|
reaction((_) => sendViewModel.state, (ExecutionState state) {
|
||||||
if (state is FailureState) {
|
if (state is FailureState) {
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
|
@ -602,8 +664,14 @@ class SendPage extends BasePage {
|
||||||
amount: S.of(context).send_amount,
|
amount: S.of(context).send_amount,
|
||||||
amountValue:
|
amountValue:
|
||||||
sendViewModel.pendingTransaction.amountFormatted,
|
sendViewModel.pendingTransaction.amountFormatted,
|
||||||
|
fiatAmountValue: sendViewModel.pendingTransactionFiatAmount
|
||||||
|
+ ' ' + sendViewModel.fiat.title,
|
||||||
fee: S.of(context).send_fee,
|
fee: S.of(context).send_fee,
|
||||||
feeValue: sendViewModel.pendingTransaction.feeFormatted,
|
feeValue: sendViewModel.pendingTransaction.feeFormatted,
|
||||||
|
feeFiatAmount: sendViewModel.pendingTransactionFeeFiatAmount
|
||||||
|
+ ' ' + sendViewModel.fiat.title,
|
||||||
|
recipientTitle: S.of(context).recipient_address,
|
||||||
|
recipientAddress: sendViewModel.address,
|
||||||
rightButtonText: S.of(context).ok,
|
rightButtonText: S.of(context).ok,
|
||||||
leftButtonText: S.of(context).cancel,
|
leftButtonText: S.of(context).cancel,
|
||||||
actionRightButton: () {
|
actionRightButton: () {
|
||||||
|
@ -620,94 +688,17 @@ class SendPage extends BasePage {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state is TransactionCommitted) {
|
if (state is TransactionCommitted) {
|
||||||
return Stack(
|
return AlertWithOneAction(
|
||||||
children: <Widget>[
|
alertTitle: '',
|
||||||
Container(
|
alertContent: S.of(context).send_success(
|
||||||
color: Theme.of(context).backgroundColor,
|
sendViewModel.currency
|
||||||
child: Center(
|
.toString()),
|
||||||
child: Image.asset(
|
buttonText: S.of(context).ok,
|
||||||
'assets/images/birthday_cake.png'),
|
buttonAction: () =>
|
||||||
),
|
Navigator.of(context).pop());
|
||||||
),
|
|
||||||
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: Theme.of(context)
|
|
||||||
.accentTextTheme
|
|
||||||
.body2
|
|
||||||
.color,
|
|
||||||
textColor: Colors.white))
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state is TransactionCommitting) {
|
return Offstage();
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Container();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -748,8 +739,9 @@ class SendPage extends BasePage {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _setTransactionPriority(BuildContext context) async {
|
Future<void> _setTransactionPriority(BuildContext context) async {
|
||||||
final items = TransactionPriority.all;
|
final items = TransactionPriority.forWalletType(sendViewModel.walletType);
|
||||||
final selectedItem = items.indexOf(sendViewModel.transactionPriority);
|
final selectedItem = items.indexOf(sendViewModel.transactionPriority);
|
||||||
|
final isShowScrollThumb = items.length > 3;
|
||||||
|
|
||||||
await showPopUp<void>(
|
await showPopUp<void>(
|
||||||
builder: (_) => Picker(
|
builder: (_) => Picker(
|
||||||
|
@ -759,7 +751,6 @@ class SendPage extends BasePage {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
onItemSelected: (TransactionPriority priority) =>
|
onItemSelected: (TransactionPriority priority) =>
|
||||||
sendViewModel.setTransactionPriority(priority),
|
sendViewModel.setTransactionPriority(priority),
|
||||||
isAlwaysShowScrollThumb: true,
|
|
||||||
),
|
),
|
||||||
context: context);
|
context: context);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,8 @@ class SendTemplatePage extends BasePage {
|
||||||
return KeyboardActions(
|
return KeyboardActions(
|
||||||
config: KeyboardActionsConfig(
|
config: KeyboardActionsConfig(
|
||||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
||||||
keyboardBarColor: isDarkTheme
|
keyboardBarColor: Theme.of(context).accentTextTheme.body2
|
||||||
? Color.fromRGBO(48, 51, 60, 1.0)
|
.backgroundColor,
|
||||||
: Color.fromRGBO(98, 98, 98, 1.0),
|
|
||||||
nextFocus: false,
|
nextFocus: false,
|
||||||
actions: [
|
actions: [
|
||||||
KeyboardActionsItem(
|
KeyboardActionsItem(
|
||||||
|
@ -113,7 +112,8 @@ class SendTemplatePage extends BasePage {
|
||||||
|
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
address = uri.path;
|
address = uri.path;
|
||||||
amount = uri.queryParameters['tx_amount'];
|
amount = uri.queryParameters['tx_amount'] ??
|
||||||
|
uri.queryParameters['amount'];
|
||||||
} else {
|
} else {
|
||||||
address = uri.toString();
|
address = uri.toString();
|
||||||
}
|
}
|
||||||
|
@ -246,9 +246,8 @@ class SendTemplatePage extends BasePage {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
text: S.of(context).save,
|
text: S.of(context).save,
|
||||||
color: Theme.of(context).accentTextTheme.subtitle.decorationColor,
|
color: Colors.green,
|
||||||
textColor:
|
textColor: Colors.white,
|
||||||
Theme.of(context).accentTextTheme.headline.decorationColor,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'package:cake_wallet/palette.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
|
import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
|
||||||
|
|
||||||
|
@ -6,8 +7,12 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
||||||
@required this.alertTitle,
|
@required this.alertTitle,
|
||||||
@required this.amount,
|
@required this.amount,
|
||||||
@required this.amountValue,
|
@required this.amountValue,
|
||||||
|
@required this.fiatAmountValue,
|
||||||
@required this.fee,
|
@required this.fee,
|
||||||
@required this.feeValue,
|
@required this.feeValue,
|
||||||
|
@required this.feeFiatAmount,
|
||||||
|
@required this.recipientTitle,
|
||||||
|
@required this.recipientAddress,
|
||||||
@required this.leftButtonText,
|
@required this.leftButtonText,
|
||||||
@required this.rightButtonText,
|
@required this.rightButtonText,
|
||||||
@required this.actionLeftButton,
|
@required this.actionLeftButton,
|
||||||
|
@ -18,8 +23,12 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
||||||
final String alertTitle;
|
final String alertTitle;
|
||||||
final String amount;
|
final String amount;
|
||||||
final String amountValue;
|
final String amountValue;
|
||||||
|
final String fiatAmountValue;
|
||||||
final String fee;
|
final String fee;
|
||||||
final String feeValue;
|
final String feeValue;
|
||||||
|
final String feeFiatAmount;
|
||||||
|
final String recipientTitle;
|
||||||
|
final String recipientAddress;
|
||||||
final String leftButtonText;
|
final String leftButtonText;
|
||||||
final String rightButtonText;
|
final String rightButtonText;
|
||||||
final VoidCallback actionLeftButton;
|
final VoidCallback actionLeftButton;
|
||||||
|
@ -29,74 +38,145 @@ class ConfirmSendingAlert extends BaseAlertDialog {
|
||||||
@override
|
@override
|
||||||
String get titleText => alertTitle;
|
String get titleText => alertTitle;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isDividerExists => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get leftActionButtonText => leftButtonText;
|
String get leftActionButtonText => leftButtonText;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get rightActionButtonText => rightButtonText;
|
String get rightActionButtonText => rightButtonText;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
VoidCallback get actionLeft => actionLeftButton;
|
VoidCallback get actionLeft => actionLeftButton;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
VoidCallback get actionRight => actionRightButton;
|
VoidCallback get actionRight => actionRightButton;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get barrierDismissible => alertBarrierDismissible;
|
bool get barrierDismissible => alertBarrierDismissible;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget content(BuildContext context) {
|
Widget content(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Row(
|
Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text(
|
Text(
|
||||||
amount,
|
amount,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.normal,
|
||||||
fontFamily: 'Lato',
|
fontFamily: 'Lato',
|
||||||
color: Theme.of(context).primaryTextTheme.title.color,
|
color: Theme.of(context).primaryTextTheme.title.color,
|
||||||
decoration: TextDecoration.none,
|
decoration: TextDecoration.none,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
Column(
|
||||||
amountValue,
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
style: TextStyle(
|
children: [
|
||||||
fontSize: 16,
|
Text(
|
||||||
fontWeight: FontWeight.w600,
|
amountValue,
|
||||||
fontFamily: 'Lato',
|
style: TextStyle(
|
||||||
color: Theme.of(context).primaryTextTheme.title.color,
|
fontSize: 18,
|
||||||
decoration: TextDecoration.none,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
fontFamily: 'Lato',
|
||||||
|
color: Theme.of(context).primaryTextTheme.title.color,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
fiatAmountValue,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: PaletteDark.pigeonBlue,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Padding(
|
||||||
mainAxisSize: MainAxisSize.max,
|
padding: EdgeInsets.only(top: 16),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
child: Row(
|
||||||
children: <Widget>[
|
mainAxisSize: MainAxisSize.max,
|
||||||
Text(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
fee,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
style: TextStyle(
|
children: <Widget>[
|
||||||
fontSize: 16,
|
Text(
|
||||||
fontWeight: FontWeight.w600,
|
fee,
|
||||||
fontFamily: 'Lato',
|
style: TextStyle(
|
||||||
color: Theme.of(context).primaryTextTheme.title.color,
|
fontSize: 16,
|
||||||
decoration: TextDecoration.none,
|
fontWeight: FontWeight.normal,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: Theme.of(context).primaryTextTheme.title.color,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Column(
|
||||||
Text(
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
feeValue,
|
children: [
|
||||||
style: TextStyle(
|
Text(
|
||||||
fontSize: 16,
|
feeValue,
|
||||||
fontWeight: FontWeight.w600,
|
style: TextStyle(
|
||||||
fontFamily: 'Lato',
|
fontSize: 18,
|
||||||
color: Theme.of(context).primaryTextTheme.title.color,
|
fontWeight: FontWeight.w600,
|
||||||
decoration: TextDecoration.none,
|
fontFamily: 'Lato',
|
||||||
|
color: Theme.of(context).primaryTextTheme.title.color,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
feeFiatAmount,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: PaletteDark.pigeonBlue,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(0, 16, 0, 0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'$recipientTitle:',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: Theme.of(context).primaryTextTheme.title.color,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
Padding(
|
||||||
],
|
padding: EdgeInsets.only(top: 8),
|
||||||
|
child: Text(
|
||||||
|
recipientAddress,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
fontFamily: 'Lato',
|
||||||
|
color: PaletteDark.pigeonBlue,
|
||||||
|
decoration: TextDecoration.none,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -43,7 +43,6 @@ class SettingsPage extends BasePage {
|
||||||
return SettingsPickerCell<dynamic>(
|
return SettingsPickerCell<dynamic>(
|
||||||
title: item.title,
|
title: item.title,
|
||||||
selectedItem: item.selectedItem(),
|
selectedItem: item.selectedItem(),
|
||||||
isAlwaysShowScrollThumb: item.isAlwaysShowScrollThumb,
|
|
||||||
items: item.items,
|
items: item.items,
|
||||||
onItemSelected: (dynamic value) => item.onItemSelected(value),
|
onItemSelected: (dynamic value) => item.onItemSelected(value),
|
||||||
);
|
);
|
||||||
|
|
|
@ -9,8 +9,7 @@ class SettingsPickerCell<ItemType> extends StandardListRow {
|
||||||
{@required String title,
|
{@required String title,
|
||||||
this.selectedItem,
|
this.selectedItem,
|
||||||
this.items,
|
this.items,
|
||||||
this.onItemSelected,
|
this.onItemSelected})
|
||||||
this.isAlwaysShowScrollThumb})
|
|
||||||
: super(
|
: super(
|
||||||
title: title,
|
title: title,
|
||||||
isSelected: false,
|
isSelected: false,
|
||||||
|
@ -24,7 +23,6 @@ class SettingsPickerCell<ItemType> extends StandardListRow {
|
||||||
selectedAtIndex: selectedAtIndex,
|
selectedAtIndex: selectedAtIndex,
|
||||||
title: S.current.please_select,
|
title: S.current.please_select,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
isAlwaysShowScrollThumb: isAlwaysShowScrollThumb,
|
|
||||||
onItemSelected: (ItemType item) =>
|
onItemSelected: (ItemType item) =>
|
||||||
onItemSelected?.call(item)));
|
onItemSelected?.call(item)));
|
||||||
});
|
});
|
||||||
|
@ -32,7 +30,6 @@ class SettingsPickerCell<ItemType> extends StandardListRow {
|
||||||
final ItemType selectedItem;
|
final ItemType selectedItem;
|
||||||
final List<ItemType> items;
|
final List<ItemType> items;
|
||||||
final void Function(ItemType item) onItemSelected;
|
final void Function(ItemType item) onItemSelected;
|
||||||
final bool isAlwaysShowScrollThumb;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildTrailing(BuildContext context) {
|
Widget buildTrailing(BuildContext context) {
|
||||||
|
|
|
@ -1,82 +1,43 @@
|
||||||
|
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
|
import 'package:cake_wallet/view_model/trade_details_view_model.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:cake_wallet/exchange/trade.dart';
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/utils/date_formatter.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||||
|
|
||||||
class TradeDetailsPage extends BasePage {
|
class TradeDetailsPage extends BasePage {
|
||||||
TradeDetailsPage(this.trade) : _items = [] {
|
TradeDetailsPage(this.tradeDetailsViewModel);
|
||||||
final dateFormat = DateFormatter.withCurrentLocal();
|
|
||||||
_items.addAll([
|
|
||||||
StandartListItem(title: S.current.trade_details_id, value: trade.id),
|
|
||||||
StandartListItem(
|
|
||||||
title: S.current.trade_details_state,
|
|
||||||
value: trade.state != null
|
|
||||||
? trade.state.toString()
|
|
||||||
: S.current.trade_details_fetching)
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (trade.provider != null) {
|
|
||||||
_items.add(StandartListItem(
|
|
||||||
title: S.current.trade_details_provider,
|
|
||||||
value: trade.provider.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trade.createdAt != null) {
|
|
||||||
_items.add(StandartListItem(
|
|
||||||
title: S.current.trade_details_created_at,
|
|
||||||
value: dateFormat.format(trade.createdAt).toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (trade.from != null && trade.to != null) {
|
|
||||||
_items.add(StandartListItem(
|
|
||||||
title: S.current.trade_details_pair,
|
|
||||||
value: '${trade.from.toString()} → ${trade.to.toString()}'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => S.current.trade_details_title;
|
String get title => S.current.trade_details_title;
|
||||||
|
|
||||||
final Trade trade;
|
final TradeDetailsViewModel tradeDetailsViewModel;
|
||||||
final List<StandartListItem> _items;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
return Container(child: Observer(builder: (_) {
|
return Observer(builder: (_) {
|
||||||
return ListView.separated(
|
return SectionStandardList(
|
||||||
separatorBuilder: (_, __) => Container(
|
sectionCount: 1,
|
||||||
height: 1,
|
itemCounter: (int _) => tradeDetailsViewModel.items.length,
|
||||||
padding: EdgeInsets.only(left: 24),
|
itemBuilder: (_, __, index) {
|
||||||
color: Theme.of(context).backgroundColor,
|
final item = tradeDetailsViewModel.items[index];
|
||||||
child: Container(
|
|
||||||
height: 1,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.primaryTextTheme
|
|
||||||
.title
|
|
||||||
.backgroundColor)),
|
|
||||||
itemCount: _items.length,
|
|
||||||
itemBuilder: (BuildContext context, int index) {
|
|
||||||
final item = _items[index];
|
|
||||||
final isDrawBottom = index == _items.length - 1 ? true : false;
|
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(ClipboardData(text: '${item.value}'));
|
Clipboard.setData(ClipboardData(text: '${item.value}'));
|
||||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
showBar<void>(context, S
|
||||||
|
.of(context)
|
||||||
|
.copied_to_clipboard);
|
||||||
},
|
},
|
||||||
child: StandartListRow(
|
child: StandartListRow(
|
||||||
title: '${item.title}',
|
title: '${item.title}',
|
||||||
value: '${item.value}',
|
value: '${item.value}'
|
||||||
isDrawBottom: isDrawBottom,
|
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
class StandartListItem {
|
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
|
||||||
StandartListItem({this.title, this.value});
|
|
||||||
|
|
||||||
final String title;
|
class StandartListItem extends TransactionDetailsListItem {
|
||||||
final String value;
|
StandartListItem({String title, String value})
|
||||||
|
: super(title: title, value: value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import 'package:cake_wallet/src/screens/transaction_details/transaction_details_list_item.dart';
|
||||||
|
|
||||||
|
class TextFieldListItem extends TransactionDetailsListItem {
|
||||||
|
TextFieldListItem({String title, String value, this.onSubmitted})
|
||||||
|
: super(title: title, value: value);
|
||||||
|
|
||||||
|
final Function(String value) onSubmitted;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
abstract class TransactionDetailsListItem {
|
||||||
|
TransactionDetailsListItem({this.title, this.value});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String value;
|
||||||
|
}
|
|
@ -1,104 +1,32 @@
|
||||||
import 'package:cake_wallet/entities/transaction_description.dart';
|
import 'package:cake_wallet/src/screens/transaction_details/textfield_list_item.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/transaction_details/widgets/textfield_list_row.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
|
import 'package:cake_wallet/view_model/transaction_details_view_model.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart';
|
|
||||||
import 'package:cake_wallet/monero/monero_transaction_info.dart';
|
|
||||||
import 'package:cake_wallet/entities/transaction_info.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
import 'package:cake_wallet/src/widgets/standart_list_row.dart';
|
||||||
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart';
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
import 'package:cake_wallet/utils/date_formatter.dart';
|
|
||||||
import 'package:hive/hive.dart';
|
|
||||||
|
|
||||||
class TransactionDetailsPage extends BasePage {
|
class TransactionDetailsPage extends BasePage {
|
||||||
TransactionDetailsPage(this.transactionInfo, bool showRecipientAddress, Box<TransactionDescription> transactionDescriptionBox)
|
TransactionDetailsPage({this.transactionDetailsViewModel});
|
||||||
: _items = [] {
|
|
||||||
final dateFormat = DateFormatter.withCurrentLocal();
|
|
||||||
final tx = transactionInfo;
|
|
||||||
|
|
||||||
if (tx is MoneroTransactionInfo) {
|
|
||||||
final items = [
|
|
||||||
StandartListItem(
|
|
||||||
title: S.current.transaction_details_transaction_id, value: tx.id),
|
|
||||||
StandartListItem(
|
|
||||||
title: S.current.transaction_details_date,
|
|
||||||
value: dateFormat.format(tx.date)),
|
|
||||||
StandartListItem(
|
|
||||||
title: S.current.transaction_details_height, value: '${tx.height}'),
|
|
||||||
StandartListItem(
|
|
||||||
title: S.current.transaction_details_amount,
|
|
||||||
value: tx.amountFormatted()),
|
|
||||||
StandartListItem(
|
|
||||||
title: S.current.send_fee,
|
|
||||||
value: tx.feeFormatted())
|
|
||||||
];
|
|
||||||
|
|
||||||
if (showRecipientAddress) {
|
|
||||||
final recipientAddress = transactionDescriptionBox.values.firstWhere((val) => val.id == transactionInfo.id, orElse: () => null)?.recipientAddress;
|
|
||||||
|
|
||||||
if (recipientAddress?.isNotEmpty ?? false) {
|
|
||||||
items.add(StandartListItem(
|
|
||||||
title: S.current.transaction_details_recipient_address,
|
|
||||||
value: recipientAddress));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tx.key?.isNotEmpty ?? null) {
|
|
||||||
// FIXME: add translation
|
|
||||||
items.add(StandartListItem(title: 'Transaction Key', value: tx.key));
|
|
||||||
}
|
|
||||||
|
|
||||||
_items.addAll(items);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tx is BitcoinTransactionInfo) {
|
|
||||||
final items = [
|
|
||||||
StandartListItem(
|
|
||||||
title: S.current.transaction_details_transaction_id, value: tx.id),
|
|
||||||
StandartListItem(
|
|
||||||
title: S.current.transaction_details_date,
|
|
||||||
value: dateFormat.format(tx.date)),
|
|
||||||
StandartListItem(
|
|
||||||
title: 'Confirmations', value: tx.confirmations?.toString()),
|
|
||||||
StandartListItem(
|
|
||||||
title: S.current.transaction_details_height, value: '${tx.height}'),
|
|
||||||
StandartListItem(
|
|
||||||
title: S.current.transaction_details_amount,
|
|
||||||
value: tx.amountFormatted())
|
|
||||||
];
|
|
||||||
|
|
||||||
_items.addAll(items);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => S.current.transaction_details_title;
|
String get title => S.current.transaction_details_title;
|
||||||
|
|
||||||
final TransactionInfo transactionInfo;
|
final TransactionDetailsViewModel transactionDetailsViewModel;
|
||||||
|
|
||||||
final List<StandartListItem> _items;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
return Container(
|
return SectionStandardList(
|
||||||
child: ListView.separated(
|
sectionCount: 1,
|
||||||
separatorBuilder: (context, index) => Container(
|
itemCounter: (int _) => transactionDetailsViewModel.items.length,
|
||||||
height: 1,
|
itemBuilder: (_, __, index) {
|
||||||
padding: EdgeInsets.only(left: 24),
|
final item = transactionDetailsViewModel.items[index];
|
||||||
color: Theme.of(context).backgroundColor,
|
|
||||||
child: Container(
|
|
||||||
height: 1,
|
|
||||||
color:
|
|
||||||
Theme.of(context).primaryTextTheme.title.backgroundColor,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
itemCount: _items.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final item = _items[index];
|
|
||||||
final isDrawBottom = index == _items.length - 1 ? true : false;
|
|
||||||
|
|
||||||
|
if (item is StandartListItem) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(ClipboardData(text: item.value));
|
Clipboard.setData(ClipboardData(text: item.value));
|
||||||
|
@ -107,10 +35,19 @@ class TransactionDetailsPage extends BasePage {
|
||||||
},
|
},
|
||||||
child: StandartListRow(
|
child: StandartListRow(
|
||||||
title: '${item.title}:',
|
title: '${item.title}:',
|
||||||
value: item.value,
|
value: item.value),
|
||||||
isDrawBottom: isDrawBottom),
|
|
||||||
);
|
);
|
||||||
}),
|
}
|
||||||
);
|
|
||||||
|
if (item is TextFieldListItem) {
|
||||||
|
return TextFieldListRow(
|
||||||
|
title: item.title,
|
||||||
|
value: item.value,
|
||||||
|
onSubmitted: item.onSubmitted,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
|
||||||
|
class TextFieldListRow extends StatelessWidget {
|
||||||
|
TextFieldListRow(
|
||||||
|
{this.title,
|
||||||
|
this.value,
|
||||||
|
this.titleFontSize = 14,
|
||||||
|
this.valueFontSize = 16,
|
||||||
|
this.onSubmitted}) {
|
||||||
|
|
||||||
|
_textController = TextEditingController();
|
||||||
|
_textController.text = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String value;
|
||||||
|
final double titleFontSize;
|
||||||
|
final double valueFontSize;
|
||||||
|
final Function(String value) onSubmitted;
|
||||||
|
|
||||||
|
TextEditingController _textController;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
color: Theme.of(context).backgroundColor,
|
||||||
|
child: Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: titleFontSize,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme.overline.color),
|
||||||
|
textAlign: TextAlign.left),
|
||||||
|
TextField(
|
||||||
|
controller: _textController,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
textInputAction: TextInputAction.done,
|
||||||
|
maxLines: null,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: valueFontSize,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme.title.color),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
isDense: true,
|
||||||
|
contentPadding: EdgeInsets.only(top: 12, bottom: 0),
|
||||||
|
hintText: S.of(context).enter_your_note,
|
||||||
|
hintStyle: TextStyle(
|
||||||
|
fontSize: valueFontSize,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme.overline.color),
|
||||||
|
border: InputBorder.none
|
||||||
|
),
|
||||||
|
onSubmitted: (value) => onSubmitted.call(value),
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
import 'package:cake_wallet/src/screens/auth/auth_page.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_list/widgets/wallet_menu_alert.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
@ -15,7 +14,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
|
||||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||||
import 'package:cake_wallet/src/screens/wallet_list/wallet_menu.dart';
|
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
|
||||||
class WalletListPage extends BasePage {
|
class WalletListPage extends BasePage {
|
||||||
|
@ -51,7 +49,7 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||||
final newWalletImage = Image.asset('assets/images/new_wallet.png',
|
final newWalletImage = Image.asset('assets/images/new_wallet.png',
|
||||||
height: 12,
|
height: 12,
|
||||||
width: 12,
|
width: 12,
|
||||||
color: Theme.of(context).accentTextTheme.headline.decorationColor);
|
color: Colors.white);
|
||||||
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
|
final restoreWalletImage = Image.asset('assets/images/restore_wallet.png',
|
||||||
height: 12,
|
height: 12,
|
||||||
width: 12,
|
width: 12,
|
||||||
|
@ -165,17 +163,16 @@ class WalletListBodyState extends State<WalletListBody> {
|
||||||
),
|
),
|
||||||
bottomSection: Column(children: <Widget>[
|
bottomSection: Column(children: <Widget>[
|
||||||
PrimaryImageButton(
|
PrimaryImageButton(
|
||||||
onPressed: () => _generateNewWallet(),
|
onPressed: () => Navigator.of(context).pushNamed(Routes.newWalletType),
|
||||||
image: newWalletImage,
|
image: newWalletImage,
|
||||||
text: S.of(context).wallet_list_create_new_wallet,
|
text: S.of(context).wallet_list_create_new_wallet,
|
||||||
color: Theme.of(context).accentTextTheme.subtitle.decorationColor,
|
color: Theme.of(context).accentTextTheme.body2.color,
|
||||||
textColor:
|
textColor: Colors.white,
|
||||||
Theme.of(context).accentTextTheme.headline.decorationColor,
|
|
||||||
),
|
),
|
||||||
SizedBox(height: 10.0),
|
SizedBox(height: 10.0),
|
||||||
PrimaryImageButton(
|
PrimaryImageButton(
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
Navigator.of(context).pushNamed(Routes.restoreWallet),
|
Navigator.of(context).pushNamed(Routes.restoreWalletType),
|
||||||
image: restoreWalletImage,
|
image: restoreWalletImage,
|
||||||
text: S.of(context).wallet_list_restore_wallet,
|
text: S.of(context).wallet_list_restore_wallet,
|
||||||
color: Theme.of(context).accentTextTheme.caption.color,
|
color: Theme.of(context).accentTextTheme.caption.color,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'package:cake_wallet/di.dart';
|
import 'package:cake_wallet/themes/theme_base.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
|
@ -23,9 +22,7 @@ class WelcomePage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
final welcomeImage = getIt
|
final welcomeImage = currentTheme.type == ThemeType.dark
|
||||||
.get<SettingsStore>()
|
|
||||||
.isDarkTheme
|
|
||||||
? welcomeImageDark : welcomeImageLight;
|
? welcomeImageDark : welcomeImageLight;
|
||||||
|
|
||||||
final newWalletImage = Image.asset('assets/images/new_wallet.png',
|
final newWalletImage = Image.asset('assets/images/new_wallet.png',
|
||||||
|
|
|
@ -2,8 +2,8 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/routes.dart';
|
import 'package:cake_wallet/routes.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/entities/contact_record.dart';
|
|
||||||
import 'package:cake_wallet/entities/qr_scanner.dart';
|
import 'package:cake_wallet/entities/qr_scanner.dart';
|
||||||
|
import 'package:cake_wallet/entities/contact_base.dart';
|
||||||
|
|
||||||
enum AddressTextFieldOption { paste, qrCode, addressBook }
|
enum AddressTextFieldOption { paste, qrCode, addressBook }
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ class AddressTextField extends StatelessWidget {
|
||||||
final Color iconColor;
|
final Color iconColor;
|
||||||
final TextStyle textStyle;
|
final TextStyle textStyle;
|
||||||
final TextStyle hintStyle;
|
final TextStyle hintStyle;
|
||||||
FocusNode focusNode;
|
final FocusNode focusNode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -204,7 +204,7 @@ class AddressTextField extends StatelessWidget {
|
||||||
onURIScanned(uri);
|
onURIScanned(uri);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error $e');
|
print(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ class AddressTextField extends StatelessWidget {
|
||||||
final contact = await Navigator.of(context, rootNavigator: true)
|
final contact = await Navigator.of(context, rootNavigator: true)
|
||||||
.pushNamed(Routes.pickerAddressBook);
|
.pushNamed(Routes.pickerAddressBook);
|
||||||
|
|
||||||
if (contact is ContactRecord && contact.address != null) {
|
if (contact is ContactBase && contact.address != null) {
|
||||||
controller.text = contact.address;
|
controller.text = contact.address;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|