Merged 4.1.0

This commit is contained in:
M 2021-01-13 19:18:28 +02:00
commit 9a79fcdc23
149 changed files with 6461 additions and 2150 deletions

View file

@ -1,2 +1,2 @@
- -
uri: electrumx.cakewallet.com:50002 uri: electrum.cakewallet.com:50002

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 760 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

After

Width:  |  Height:  |  Size: 440 B

View file

@ -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));

View file

@ -0,0 +1,8 @@
class WalletOpeningException implements Exception {
WalletOpeningException({this.message});
final String message;
@override
String toString() => message;
}

View file

@ -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();

View file

@ -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();

View file

@ -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) {

View file

@ -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";

View 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);
}
}

View file

@ -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});
} }

View file

@ -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;
}

View file

@ -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});

File diff suppressed because it is too large Load diff

View 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.';
}

View file

@ -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;
} }

View file

@ -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) {

View file

@ -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;
} }
} }

View file

@ -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;
}
}
} }

View file

@ -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;
} }

View file

@ -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();

View file

@ -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 {

View file

@ -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

View file

@ -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.
}; };

View file

@ -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

View file

@ -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();
} }

View file

@ -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;

View file

@ -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)));
} }

View file

@ -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;
} }

View file

@ -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 '';
} }

View file

@ -0,0 +1,9 @@
import 'package:cake_wallet/entities/crypto_currency.dart';
abstract class ContactBase {
String name;
String address;
CryptoCurrency type;
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
} }

View file

@ -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);
} }

View file

@ -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';

View file

@ -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 ?? '';
} }

View file

@ -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

View file

@ -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) {

View 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;
}

View file

@ -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);
} }

View file

@ -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;
}
}

View file

@ -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);

File diff suppressed because it is too large Load diff

View file

@ -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,

View file

@ -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;
}

View file

@ -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());
}
} }
} }

View file

@ -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();
} }
} }
} }

View file

@ -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);
} }

View file

@ -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));
} }

View file

@ -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;

View file

@ -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);
}); });
} }

View file

@ -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());
} }

View file

@ -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());
} }

View file

@ -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 {

View file

@ -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>(

View file

@ -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,

View file

@ -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) {

View file

@ -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),

View file

@ -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;

View file

@ -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),
) )
], ],
), ),

View file

@ -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,
)
],
),
),
)
],
),
);
} }
} }

View file

@ -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);
}),
],
),
)
); );
} }
} }

View file

@ -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(

View file

@ -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),
))
], ],
), ),
)) ))

View file

@ -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))
]), ])
], ],
), )
), )
)) ],
]), ),
)); ));
} }

View file

@ -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))
]), ])
],
)
)
],
),
)); ));
} }
} }

View file

@ -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) {

View file

@ -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)),
]), ]),
)), )),
)); ));

View file

@ -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,

View file

@ -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);
}); });
}); });
} }

View file

@ -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),

View file

@ -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);
}
} }

View file

@ -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

View file

@ -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();
}, },
), ),
); );

View file

@ -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(

View file

@ -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(

View file

@ -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)
])); ]));
} }

View file

@ -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;

View file

@ -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,

View file

@ -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),

View file

@ -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),

View file

@ -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);
} }

View file

@ -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,
), ),
), ),
)); ));

View file

@ -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,
),
)
)
],
),
) )
], ],
); );

View file

@ -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),
); );

View file

@ -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) {

View file

@ -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,
)); ));
}); });
})); });
} }
} }

View file

@ -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);
} }

View file

@ -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;
}

View file

@ -0,0 +1,6 @@
abstract class TransactionDetailsListItem {
TransactionDetailsListItem({this.title, this.value});
final String title;
final String value;
}

View file

@ -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;
});
} }
} }

View file

@ -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),
)
]),
),
);
}
}

View file

@ -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,

View file

@ -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',

View file

@ -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;
} }
} }

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