Merge branch 'main' of github.com:cake-tech/cake_wallet into ionia

This commit is contained in:
M 2022-07-21 13:57:35 +01:00
commit 69577e192a
147 changed files with 1868 additions and 1176 deletions

View file

@ -22,10 +22,11 @@
* Select your own custom nodes/servers
* Address book
* Backup to external location or iCloud
* OpenAlias and Unstoppable Domains
* Send to OpenAlias, Unstoppable Domains, and Yats
* Set custom fee levels
* Store local transaction notes
* Extremely simple user experience
* Convenient exchange and sending templates for recurring payments
### Monero Specific Features

View file

@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name=".Application"

BIN
assets/images/flags/aus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
assets/images/flags/bgr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 B

BIN
assets/images/flags/bra.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/images/flags/cad.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,005 B

BIN
assets/images/flags/che.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 855 B

BIN
assets/images/flags/chn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

BIN
assets/images/flags/czk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

BIN
assets/images/flags/deu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 703 B

BIN
assets/images/flags/dnk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

BIN
assets/images/flags/esp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/images/flags/eur.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,005 B

BIN
assets/images/flags/fra.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 B

BIN
assets/images/flags/gbr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
assets/images/flags/hkg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,023 B

BIN
assets/images/flags/hrv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/images/flags/hun.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

BIN
assets/images/flags/idn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

BIN
assets/images/flags/ind.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

BIN
assets/images/flags/isl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

BIN
assets/images/flags/isr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 B

BIN
assets/images/flags/ita.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

BIN
assets/images/flags/jpn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 896 B

BIN
assets/images/flags/kor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 997 B

BIN
assets/images/flags/mex.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,013 B

BIN
assets/images/flags/mys.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/images/flags/nld.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 762 B

BIN
assets/images/flags/nor.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 898 B

BIN
assets/images/flags/nzl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
assets/images/flags/phl.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/images/flags/pol.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 707 B

BIN
assets/images/flags/prt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/images/flags/rou.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

BIN
assets/images/flags/rus.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 702 B

BIN
assets/images/flags/saf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
assets/images/flags/sgp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 902 B

BIN
assets/images/flags/swe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

BIN
assets/images/flags/tha.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

BIN
assets/images/flags/ukr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 695 B

BIN
assets/images/flags/usa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/images/flags/ven.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,009 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 536 B

After

Width:  |  Height:  |  Size: 469 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
assets/images/zec_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -24,7 +24,7 @@ abstract class ElectrumTransactionHistoryBase
}
final WalletInfo walletInfo;
final String _password;
String _password;
int _height;
Future<void> init() async => await _load();
@ -51,6 +51,11 @@ abstract class ElectrumTransactionHistoryBase
}
}
Future<void> changePassword(String password) async {
_password = password;
await save();
}
Future<Map<String, Object>> _read() async {
final dirPath =
await pathForWalletDir(name: walletInfo.name, type: walletInfo.type);

View file

@ -109,7 +109,7 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
BitcoinWalletKeys get keys => BitcoinWalletKeys(
wif: hd.wif, privateKey: hd.privKey, publicKey: hd.pubKey);
final String _password;
String _password;
List<BitcoinUnspent> unspentCoins;
List<int> _feeRates;
Map<String, BehaviorSubject<Object>> _scripthashesUpdateSubject;
@ -422,6 +422,13 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
await transactionHistory.save();
}
@override
Future<void> changePassword(String password) async {
_password = password;
await save();
await transactionHistory.changePassword(password);
}
bitcoin.ECPair keyPairFor({@required int index}) =>
generateKeyPair(hd: hd, index: index, network: networkType);

View file

@ -5,9 +5,13 @@ part 'crypto_currency.g.dart';
@HiveType(typeId: 0)
class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
const CryptoCurrency({final String title, final int raw})
const CryptoCurrency({final String title, this.tag, this.name, this.iconPath, final int raw})
: super(title: title, raw: raw);
final String tag;
final String name;
final String iconPath;
static const all = [
CryptoCurrency.xmr,
CryptoCurrency.ada,
@ -24,39 +28,44 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
CryptoCurrency.usdterc20,
CryptoCurrency.xlm,
CryptoCurrency.xrp,
CryptoCurrency.xhv
CryptoCurrency.xhv,
//CryptoCurrency.zaddr,
//CryptoCurrency.zec
];
static const xmr = CryptoCurrency(title: 'XMR', raw: 0);
static const ada = CryptoCurrency(title: 'ADA', raw: 1);
static const bch = CryptoCurrency(title: 'BCH', raw: 2);
static const bnb = CryptoCurrency(title: 'BNB BEP2', raw: 3);
static const btc = CryptoCurrency(title: 'BTC', raw: 4);
static const dai = CryptoCurrency(title: 'DAI', raw: 5);
static const dash = CryptoCurrency(title: 'DASH', raw: 6);
static const eos = CryptoCurrency(title: 'EOS', raw: 7);
static const eth = CryptoCurrency(title: 'ETH', raw: 8);
static const ltc = CryptoCurrency(title: 'LTC', raw: 9);
static const xmr = CryptoCurrency(title: 'XMR', iconPath: 'assets/images/monero_icon.png', name: 'Monero', raw: 0);
static const ada = CryptoCurrency(title: 'ADA', iconPath: 'assets/images/ada_icon.png', name: 'Cardano', raw: 1);
static const bch = CryptoCurrency(title: 'BCH', iconPath: 'assets/images/bch_icon.png',name: 'Bitcoin Cash', raw: 2);
static const bnb = CryptoCurrency(title: 'BNB', iconPath: 'assets/images/bnb_icon.png', tag: 'BSC', name: 'Binance Coin', raw: 3);
static const btc = CryptoCurrency(title: 'BTC', iconPath: 'assets/images/btc.png', name: 'Bitcoin', raw: 4);
static const dai = CryptoCurrency(title: 'DAI', iconPath: 'assets/images/dai_icon.png', tag: 'ETH', name: 'Dai', raw: 5);
static const dash = CryptoCurrency(title: 'DASH', iconPath: 'assets/images/dash_icon.png', name: 'Dash', raw: 6);
static const eos = CryptoCurrency(title: 'EOS', iconPath: 'assets/images/eos_icon.png', name: 'EOS', raw: 7);
static const eth = CryptoCurrency(title: 'ETH', iconPath: 'assets/images/eth_icon.png', name: 'Ethereum', raw: 8);
static const ltc = CryptoCurrency(title: 'LTC', iconPath: 'assets/images/litecoin-ltc_icon.png', name: 'Litecoin',raw: 9);
static const nano = CryptoCurrency(title: 'NANO', raw: 10);
static const trx = CryptoCurrency(title: 'TRX', raw: 11);
static const usdt = CryptoCurrency(title: 'USDT', raw: 12);
static const usdterc20 = CryptoCurrency(title: 'USDTERC20', raw: 13);
static const xlm = CryptoCurrency(title: 'XLM', raw: 14);
static const xrp = CryptoCurrency(title: 'XRP', raw: 15);
static const xhv = CryptoCurrency(title: 'XHV', raw: 16);
static const xag = CryptoCurrency(title: 'XAG', raw: 17);
static const xau = CryptoCurrency(title: 'XAU', raw: 18);
static const xaud = CryptoCurrency(title: 'XAUD', raw: 19);
static const xbtc = CryptoCurrency(title: 'XBTC', raw: 20);
static const xcad = CryptoCurrency(title: 'XCAD', raw: 21);
static const xchf = CryptoCurrency(title: 'XCHF', raw: 22);
static const xcny = CryptoCurrency(title: 'XCNY', raw: 23);
static const xeur = CryptoCurrency(title: 'XEUR', raw: 24);
static const xgbp = CryptoCurrency(title: 'XGBP', raw: 25);
static const xjpy = CryptoCurrency(title: 'XJPY', raw: 26);
static const xnok = CryptoCurrency(title: 'XNOK', raw: 27);
static const xnzd = CryptoCurrency(title: 'XNZD', raw: 28);
static const xusd = CryptoCurrency(title: 'XUSD', raw: 29);
static const trx = CryptoCurrency(title: 'TRX', iconPath: 'assets/images/trx_icon.png', name: 'TRON', raw: 11);
static const usdt = CryptoCurrency(title: 'USDT', iconPath: 'assets/images/usdt_icon.png', tag: 'OMNI', name: 'USDT', raw: 12);
static const usdterc20 = CryptoCurrency(title: 'USDT', iconPath: 'assets/images/usdterc20_icon.png', tag: 'ETH', name: 'USDT', raw: 13);
static const xlm = CryptoCurrency(title: 'XLM', iconPath: 'assets/images/xlm_icon.png', name: 'Stellar', raw: 14);
static const xrp = CryptoCurrency(title: 'XRP', iconPath: 'assets/images/xrp_icon.png', name: 'Ripple', raw: 15);
static const xhv = CryptoCurrency(title: 'XHV', iconPath: 'assets/images/xhv_logo.png', name: 'Haven Protocol', raw: 16);
static const xag = CryptoCurrency(title: 'XAG', tag: 'XHV', raw: 17);
static const xau = CryptoCurrency(title: 'XAU', tag: 'XHV', raw: 18);
static const xaud = CryptoCurrency(title: 'XAUD', tag: 'XHV', raw: 19);
static const xbtc = CryptoCurrency(title: 'XBTC', tag: 'XHV', raw: 20);
static const xcad = CryptoCurrency(title: 'XCAD', tag: 'XHV', raw: 21);
static const xchf = CryptoCurrency(title: 'XCHF', tag: 'XHV', raw: 22);
static const xcny = CryptoCurrency(title: 'XCNY', tag: 'XHV', raw: 23);
static const xeur = CryptoCurrency(title: 'XEUR', tag: 'XHV', raw: 24);
static const xgbp = CryptoCurrency(title: 'XGBP', tag: 'XHV', raw: 25);
static const xjpy = CryptoCurrency(title: 'XJPY', tag: 'XHV', raw: 26);
static const xnok = CryptoCurrency(title: 'XNOK', tag: 'XHV', raw: 27);
static const xnzd = CryptoCurrency(title: 'XNZD', tag: 'XHV', raw: 28);
static const xusd = CryptoCurrency(title: 'XUSD', tag: 'XHV', raw: 29);
static const zaddr = CryptoCurrency(title: 'ZZEC', tag: 'ZEC', name: 'Shielded Zcash', iconPath: 'assets/images/zaddr_icon.png', raw: 30);
static const zec = CryptoCurrency(title: 'TZEC', tag: 'ZEC', name: 'Transparent Zcash', iconPath: 'assets/images/zec_icon.png', raw: 31);
static CryptoCurrency deserialize({int raw}) {
switch (raw) {
@ -120,6 +129,10 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
return CryptoCurrency.xnzd;
case 29:
return CryptoCurrency.xusd;
case 30:
return CryptoCurrency.zaddr;
case 31:
return CryptoCurrency.zec;
default:
return null;
}
@ -187,6 +200,10 @@ class CryptoCurrency extends EnumerableItem<int> with Serializable<int> {
return CryptoCurrency.xnzd;
case 'xusd':
return CryptoCurrency.xusd;
case 'zaddr':
return CryptoCurrency.zaddr;
case 'zec':
return CryptoCurrency.zec;
default:
return null;
}

View file

@ -88,6 +88,8 @@ class Node extends HiveObject with Keyable {
return requestElectrumServer();
case WalletType.litecoin:
return requestElectrumServer();
case WalletType.haven:
return requestMoneroNode();
default:
return false;
}

View file

@ -69,4 +69,6 @@ abstract class WalletBase<
Future<void> rescan({int height});
void close();
Future<void> changePassword(String password);
}

View file

@ -565,6 +565,16 @@ extern "C"
store_lock.unlock();
}
bool set_password(char *password, Utf8Box &error) {
bool is_changed = get_current_wallet()->setPassword(std::string(password));
if (!is_changed) {
error = Utf8Box(strdup(get_current_wallet()->errorString().c_str()));
}
return is_changed;
}
bool transaction_create(char *address, char *asset_type, char *payment_id, char *amount,
uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction)
{

View file

@ -51,6 +51,8 @@ typedef set_recovering_from_seed = Void Function(Int8);
typedef store_c = Void Function(Pointer<Utf8>);
typedef set_password = Int8 Function(Pointer<Utf8> password, Pointer<Utf8Box> error);
typedef set_listener = Void Function();
typedef get_syncing_height = Int64 Function();

View file

@ -51,6 +51,8 @@ typedef SetRecoveringFromSeed = void Function(int);
typedef Store = void Function(Pointer<Utf8>);
typedef SetPassword = int Function(Pointer<Utf8> password, Pointer<Utf8Box> error);
typedef SetListener = void Function();
typedef GetSyncingHeight = int Function();

View file

@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:cw_haven/api/structs/ut8_box.dart';
import 'package:cw_haven/api/convert_utf8_to_string.dart';
import 'package:cw_haven/api/signatures.dart';
import 'package:cw_haven/api/types.dart';
@ -67,6 +68,9 @@ final setRecoveringFromSeedNative = havenApi
final storeNative =
havenApi.lookup<NativeFunction<store_c>>('store').asFunction<Store>();
final setPasswordNative =
havenApi.lookup<NativeFunction<set_password>>('set_password').asFunction<SetPassword>();
final setListenerNative = havenApi
.lookup<NativeFunction<set_listener>>('set_listener')
.asFunction<SetListener>();
@ -193,6 +197,21 @@ void storeSync() {
free(pathPointer);
}
void setPasswordSync(String password) {
final passwordPointer = Utf8.toUtf8(password);
final errorMessagePointer = allocate<Utf8Box>();
final changed = setPasswordNative(passwordPointer, errorMessagePointer) != 0;
free(passwordPointer);
if (!changed) {
final message = errorMessagePointer.ref.getValue();
free(errorMessagePointer);
throw Exception(message);
}
free(errorMessagePointer);
}
void closeCurrentWallet() => closeCurrentWalletNative();
String getSecretViewKey() =>

View file

@ -240,6 +240,11 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
await haven_wallet.store();
}
@override
Future<void> changePassword(String password) async {
haven_wallet.setPasswordSync(password);
}
Future<int> getNodeHeight() async => haven_wallet.getNodeHeight();
Future<bool> isConnected() async => haven_wallet.isConnected();

View file

@ -469,6 +469,16 @@ extern "C"
store_lock.unlock();
}
bool set_password(char *password, Utf8Box &error) {
bool is_changed = get_current_wallet()->setPassword(std::string(password));
if (!is_changed) {
error = Utf8Box(strdup(get_current_wallet()->errorString().c_str()));
}
return is_changed;
}
bool transaction_create(char *address, char *payment_id, char *amount,
uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction)
{

View file

@ -47,6 +47,8 @@ typedef set_recovering_from_seed = Void Function(Int8);
typedef store_c = Void Function(Pointer<Utf8>);
typedef set_password = Int8 Function(Pointer<Utf8> password, Pointer<Utf8Box> error);
typedef set_listener = Void Function();
typedef get_syncing_height = Int64 Function();

View file

@ -47,6 +47,8 @@ typedef SetRecoveringFromSeed = void Function(int);
typedef Store = void Function(Pointer<Utf8>);
typedef SetPassword = int Function(Pointer<Utf8> password, Pointer<Utf8Box> error);
typedef SetListener = void Function();
typedef GetSyncingHeight = int Function();

View file

@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'package:cw_monero/api/structs/ut8_box.dart';
import 'package:cw_monero/api/convert_utf8_to_string.dart';
import 'package:cw_monero/api/signatures.dart';
import 'package:cw_monero/api/types.dart';
@ -67,6 +68,9 @@ final setRecoveringFromSeedNative = moneroApi
final storeNative =
moneroApi.lookup<NativeFunction<store_c>>('store').asFunction<Store>();
final setPasswordNative =
moneroApi.lookup<NativeFunction<set_password>>('set_password').asFunction<SetPassword>();
final setListenerNative = moneroApi
.lookup<NativeFunction<set_listener>>('set_listener')
.asFunction<SetListener>();
@ -197,6 +201,21 @@ void storeSync() {
free(pathPointer);
}
void setPasswordSync(String password) {
final passwordPointer = Utf8.toUtf8(password);
final errorMessagePointer = allocate<Utf8Box>();
final changed = setPasswordNative(passwordPointer, errorMessagePointer) != 0;
free(passwordPointer);
if (!changed) {
final message = errorMessagePointer.ref.getValue();
free(errorMessagePointer);
throw Exception(message);
}
free(errorMessagePointer);
}
void closeCurrentWallet() => closeCurrentWalletNative();
String getSecretViewKey() =>

View file

@ -258,6 +258,11 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
await monero_wallet.store();
}
@override
Future<void> changePassword(String password) async {
monero_wallet.setPasswordSync(password);
}
Future<int> getNodeHeight() async => monero_wallet.getNodeHeight();
Future<bool> isConnected() async => monero_wallet.isConnected();

View file

@ -47,7 +47,7 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.xrp:
return '^[0-9a-zA-Z]{34}\$|^X[0-9a-zA-Z]{46}\$';
case CryptoCurrency.xhv:
case CryptoCurrency.xhv:
return '^hvx|hvi|hvs[0-9a-zA-Z]';
case CryptoCurrency.xag:
case CryptoCurrency.xau:
case CryptoCurrency.xaud:
@ -102,7 +102,6 @@ class AddressValidator extends TextValidator {
case CryptoCurrency.xrp:
return null;
case CryptoCurrency.xhv:
case CryptoCurrency.xhv:
case CryptoCurrency.xag:
case CryptoCurrency.xau:
case CryptoCurrency.xaud:

View file

@ -1,12 +1,6 @@
import 'package:uuid/uuid.dart';
import 'package:cw_core/key.dart';
import 'package:cw_core/wallet_type.dart';
String generateWalletPassword(WalletType type) {
switch (type) {
case WalletType.monero:
return Uuid().v4();
default:
return generateKey();
}
String generateWalletPassword() {
return generateKey();
}

View file

@ -40,8 +40,3 @@ class TextValidator extends Validator<String> {
bool match(String value) => RegExp(pattern).hasMatch(value);
}
class WalletNameValidator extends TextValidator {
WalletNameValidator()
: super(minLength: 1, maxLength: 15, pattern: '^[a-zA-Z0-9_]\$');
}

View file

@ -1,6 +1,9 @@
import 'package:cake_wallet/di.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/core/key_service.dart';
import 'package:cw_core/wallet_base.dart';
@ -14,7 +17,8 @@ class WalletCreationService {
{WalletType initialType,
this.secureStorage,
this.keyService,
this.sharedPreferences})
this.sharedPreferences,
this.walletInfoSource})
: type = initialType {
if (type != null) {
changeWalletType(type: type);
@ -25,34 +29,80 @@ class WalletCreationService {
final FlutterSecureStorage secureStorage;
final SharedPreferences sharedPreferences;
final KeyService keyService;
final Box<WalletInfo> walletInfoSource;
WalletService _service;
static const _isNewMoneroWalletPasswordUpdated = true;
void changeWalletType({@required WalletType type}) {
this.type = type;
_service = getIt.get<WalletService>(param1: type);
}
bool exists(String name) {
final walletName = name.toLowerCase();
return walletInfoSource
.values
.any((walletInfo) => walletInfo.name.toLowerCase() == walletName);
}
void checkIfExists(String name) {
if (exists(name)) {
throw Exception('Wallet with name ${name} already exists!');
}
}
Future<WalletBase> create(WalletCredentials credentials) async {
final password = generateWalletPassword(type);
checkIfExists(credentials.name);
final password = generateWalletPassword();
credentials.password = password;
await keyService.saveWalletPassword(
password: password, walletName: credentials.name);
return await _service.create(credentials);
final wallet = await _service.create(credentials);
if (wallet.type == WalletType.monero) {
await sharedPreferences
.setBool(
PreferencesKey.moneroWalletUpdateV1Key(wallet.name),
_isNewMoneroWalletPasswordUpdated);
}
return wallet;
}
Future<WalletBase> restoreFromKeys(WalletCredentials credentials) async {
final password = generateWalletPassword(type);
checkIfExists(credentials.name);
final password = generateWalletPassword();
credentials.password = password;
await keyService.saveWalletPassword(
password: password, walletName: credentials.name);
return await _service.restoreFromKeys(credentials);
final wallet = await _service.restoreFromKeys(credentials);
if (wallet.type == WalletType.monero) {
await sharedPreferences
.setBool(
PreferencesKey.moneroWalletUpdateV1Key(wallet.name),
_isNewMoneroWalletPasswordUpdated);
}
return wallet;
}
Future<WalletBase> restoreFromSeed(WalletCredentials credentials) async {
final password = generateWalletPassword(type);
checkIfExists(credentials.name);
final password = generateWalletPassword();
credentials.password = password;
await keyService.saveWalletPassword(
password: password, walletName: credentials.name);
return await _service.restoreFromSeed(credentials);
final wallet = await _service.restoreFromSeed(credentials);
if (wallet.type == WalletType.monero) {
await sharedPreferences
.setBool(
PreferencesKey.moneroWalletUpdateV1Key(wallet.name),
_isNewMoneroWalletPasswordUpdated);
}
return wallet;
}
}

View file

@ -0,0 +1,52 @@
import 'package:cake_wallet/core/generate_wallet_password.dart';
import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:shared_preferences/shared_preferences.dart';
class WalletLoadingService {
WalletLoadingService(
this.sharedPreferences,
this.keyService,
this.walletServiceFactory);
final SharedPreferences sharedPreferences;
final KeyService keyService;
final WalletService Function(WalletType type) walletServiceFactory;
Future<WalletBase> load(WalletType type, String name) async {
if (walletServiceFactory == null) {
throw Exception('WalletLoadingService.walletServiceFactory is not set');
}
final walletService = walletServiceFactory?.call(type);
final password = await keyService.getWalletPassword(walletName: name);
final wallet = await walletService.openWallet(name, password);
if (type == WalletType.monero) {
await upateMoneroWalletPassword(wallet);
}
return wallet;
}
Future<void> upateMoneroWalletPassword(WalletBase wallet) async {
final key = PreferencesKey.moneroWalletUpdateV1Key(wallet.name);
var isPasswordUpdated = sharedPreferences.getBool(key) ?? false;
if (isPasswordUpdated) {
return;
}
final password = generateWalletPassword();
// Save new generated password with backup key for case
// if wallet will change password, but it will faild to updated in secure storage
final bakWalletName = '#__${wallet.name}_bak__#';
await keyService.saveWalletPassword(walletName: bakWalletName, password: password);
await wallet.changePassword(password);
await keyService.saveWalletPassword(walletName: wallet.name, password: password);
isPasswordUpdated = true;
await sharedPreferences.setBool(key, isPasswordUpdated);
}
}

View file

@ -0,0 +1,11 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/validator.dart';
class WalletNameValidator extends TextValidator {
WalletNameValidator()
: super(
errorMessage: S.current.error_text_wallet_name,
pattern: '^[a-zA-Z0-9\-_ ]+\$',
minLength: 1,
maxLength: 33);
}

View file

@ -13,7 +13,6 @@ import 'package:cake_wallet/ionia/ionia_api.dart';
import 'package:cake_wallet/ionia/ionia_merchant.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/haven/haven.dart';
import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_cards_page.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_account_page.dart';
@ -56,7 +55,6 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart';
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/settings.dart';
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
@ -150,6 +148,8 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.da
import 'package:cake_wallet/view_model/ionia/ionia_payment_status_view_model.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
import 'package:cake_wallet/core/wallet_loading_service.dart';
final getIt = GetIt.instance;
@ -241,7 +241,14 @@ Future setup(
initialType: type,
keyService: getIt.get<KeyService>(),
secureStorage: getIt.get<FlutterSecureStorage>(),
sharedPreferences: getIt.get<SharedPreferences>()));
sharedPreferences: getIt.get<SharedPreferences>(),
walletInfoSource: _walletInfoSource));
getIt.registerFactory<WalletLoadingService>(
() => WalletLoadingService(
getIt.get<SharedPreferences>(),
getIt.get<KeyService>(),
(WalletType type) => getIt.get<WalletService>(param1: type)));
getIt.registerFactory(() => IoniaTokenService(getIt.get<FlutterSecureStorage>()));
@ -378,7 +385,7 @@ Future setup(
getIt.registerFactory(() => WalletListViewModel(
_walletInfoSource,
getIt.get<AppStore>(),
getIt.get<KeyService>()));
getIt.get<WalletLoadingService>()));
getIt.registerFactory(() =>
WalletListPage(walletListViewModel: getIt.get<WalletListViewModel>()));
@ -533,8 +540,6 @@ Future setup(
getIt.registerFactory(() => FaqPage(getIt.get<SettingsStore>()));
getIt.registerFactory(() => LanguageListPage(getIt.get<SettingsStore>()));
getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, void>(
(type, _) => WalletRestoreViewModel(getIt.get<AppStore>(),
getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
@ -658,6 +663,11 @@ Future setup(
getIt.registerFactory(() => YatService());
getIt.registerFactory(() => AddressResolver(yatService: getIt.get<YatService>(),
walletType: getIt.get<AppStore>().wallet.type));
getIt.registerFactoryParam<FullscreenQRPage, String, bool>(
(String qrData, bool isLight) => FullscreenQRPage(qrData: qrData, isLight: isLight,));
getIt.registerFactory(() => AddressResolver(yatService: getIt.get<YatService>()));
getIt.registerFactory(() => IoniaApi());

View file

@ -1,43 +1,49 @@
import 'package:cw_core/enumerable_item.dart';
class FiatCurrency extends EnumerableItem<String> with Serializable<String> {
const FiatCurrency({String symbol}) : super(title: symbol, raw: symbol);
const FiatCurrency({String symbol, this.countryCode, this.fullName}) : super(title: symbol, raw: symbol);
final String countryCode;
final String fullName;
static List<FiatCurrency> get all => _all.values.toList();
static const aud = FiatCurrency(symbol: 'AUD');
static const bgn = FiatCurrency(symbol: 'BGN');
static const brl = FiatCurrency(symbol: 'BRL');
static const cad = FiatCurrency(symbol: 'CAD');
static const chf = FiatCurrency(symbol: 'CHF');
static const cny = FiatCurrency(symbol: 'CNY');
static const czk = FiatCurrency(symbol: 'CZK');
static const eur = FiatCurrency(symbol: 'EUR');
static const dkk = FiatCurrency(symbol: 'DKK');
static const gbp = FiatCurrency(symbol: 'GBP');
static const hkd = FiatCurrency(symbol: 'HKD');
static const hrk = FiatCurrency(symbol: 'HRK');
static const huf = FiatCurrency(symbol: 'HUF');
static const idr = FiatCurrency(symbol: 'IDR');
static const ils = FiatCurrency(symbol: 'ILS');
static const inr = FiatCurrency(symbol: 'INR');
static const isk = FiatCurrency(symbol: 'ISK');
static const jpy = FiatCurrency(symbol: 'JPY');
static const krw = FiatCurrency(symbol: 'KRW');
static const mxn = FiatCurrency(symbol: 'MXN');
static const myr = FiatCurrency(symbol: 'MYR');
static const nok = FiatCurrency(symbol: 'NOK');
static const nzd = FiatCurrency(symbol: 'NZD');
static const php = FiatCurrency(symbol: 'PHP');
static const pln = FiatCurrency(symbol: 'PLN');
static const ron = FiatCurrency(symbol: 'RON');
static const rub = FiatCurrency(symbol: 'RUB');
static const sek = FiatCurrency(symbol: 'SEK');
static const sgd = FiatCurrency(symbol: 'SGD');
static const thb = FiatCurrency(symbol: 'THB');
static const usd = FiatCurrency(symbol: 'USD');
static const zar = FiatCurrency(symbol: 'ZAR');
static const vef = FiatCurrency(symbol: 'VEF');
static List<FiatCurrency> get currenciesAvailableToBuyWith =>
[aud, brl, cad, chf, czk, eur, dkk, gbp, hkd, ils, jpy, krw, mxn, myr, nok, nzd, pln, sek, sgd, thb, usd, zar];
static const aud = FiatCurrency(symbol: 'AUD', countryCode: "aus", fullName: "Australian Dollar");
static const bgn = FiatCurrency(symbol: 'BGN', countryCode: "bgr", fullName: "Bulgarian Lev");
static const brl = FiatCurrency(symbol: 'BRL', countryCode: "bra", fullName: "Brazilian Real");
static const cad = FiatCurrency(symbol: 'CAD', countryCode: "cad", fullName: "Canadian Dollar");
static const chf = FiatCurrency(symbol: 'CHF', countryCode: "che", fullName: "Swiss Franc");
static const cny = FiatCurrency(symbol: 'CNY', countryCode: "chn", fullName: "Chinese Yuan");
static const czk = FiatCurrency(symbol: 'CZK', countryCode: "czk", fullName: "Czech Koruna");
static const eur = FiatCurrency(symbol: 'EUR', countryCode: "eur", fullName: "Euro");
static const dkk = FiatCurrency(symbol: 'DKK', countryCode: "dnk", fullName: "Danish Krone");
static const gbp = FiatCurrency(symbol: 'GBP', countryCode: "gbr", fullName: "Pound sterling");
static const hkd = FiatCurrency(symbol: 'HKD', countryCode: "hkg", fullName: "Hong Kong Dollar");
static const hrk = FiatCurrency(symbol: 'HRK', countryCode: "hrv", fullName: "Croatian Kuna");
static const huf = FiatCurrency(symbol: 'HUF', countryCode: "hun", fullName: "Hungarian Forint");
static const idr = FiatCurrency(symbol: 'IDR', countryCode: "idn", fullName: "Indonesian Rupiah");
static const ils = FiatCurrency(symbol: 'ILS', countryCode: "isr", fullName: "Israeli New Shekel");
static const inr = FiatCurrency(symbol: 'INR', countryCode: "ind", fullName: "Indian Rupee");
static const isk = FiatCurrency(symbol: 'ISK', countryCode: "isl", fullName: "Icelandic Króna");
static const jpy = FiatCurrency(symbol: 'JPY', countryCode: "jpn", fullName: "Japanese Yen equals");
static const krw = FiatCurrency(symbol: 'KRW', countryCode: "kor", fullName: "South Korean won");
static const mxn = FiatCurrency(symbol: 'MXN', countryCode: "mex", fullName: "Mexican Peso");
static const myr = FiatCurrency(symbol: 'MYR', countryCode: "mys", fullName: "Malaysian Ringgit");
static const nok = FiatCurrency(symbol: 'NOK', countryCode: "nor", fullName: "Norwegian Krone");
static const nzd = FiatCurrency(symbol: 'NZD', countryCode: "nzl", fullName: "New Zealand Dollar");
static const php = FiatCurrency(symbol: 'PHP', countryCode: "phl", fullName: "Philippine peso");
static const pln = FiatCurrency(symbol: 'PLN', countryCode: "pol", fullName: "Poland złoty");
static const ron = FiatCurrency(symbol: 'RON', countryCode: "rou", fullName: "Romanian Leu");
static const rub = FiatCurrency(symbol: 'RUB', countryCode: "rus", fullName: "Russian Ruble");
static const sek = FiatCurrency(symbol: 'SEK', countryCode: "swe", fullName: "Swedish Krona");
static const sgd = FiatCurrency(symbol: 'SGD', countryCode: "sgp", fullName: "Singapore Dollar");
static const thb = FiatCurrency(symbol: 'THB', countryCode: "tha", fullName: "Thai Baht");
static const usd = FiatCurrency(symbol: 'USD', countryCode: "usa", fullName: "United States Dollar");
static const zar = FiatCurrency(symbol: 'ZAR', countryCode: "saf", fullName: "South African Rand");
static const vef = FiatCurrency(symbol: 'VEF', countryCode: "ven", fullName: "Venezuelan Bolívar");
static final _all = {
FiatCurrency.aud.raw: FiatCurrency.aud,

View file

@ -0,0 +1,57 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
class FioAddressProvider {
static const apiAuthority = 'fio.blockpane.com';
static const availCheck = '/v1/chain/avail_check';
static const getAddress = '/v1/chain/get_pub_address';
static Future<bool> checkAvail(String fioAddress) async {
bool isFioRegistered = false;
final headers = {'Content-Type': 'application/json'};
final body = <String, String>{"fio_name": fioAddress};
final uri = Uri.https(apiAuthority, availCheck);
final response =
await http.post(uri, headers: headers, body: json.encode(body));
if (response.statusCode != 200) {
return isFioRegistered;
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
isFioRegistered = responseJSON['is_registered'] as int == 1;
return isFioRegistered;
}
static Future<String> getPubAddress(String fioAddress, String token) async {
final headers = {'Content-Type': 'application/json'};
final body = <String, String>{
"fio_address": fioAddress,
"chain_code": token.toUpperCase(),
"token_code": token.toUpperCase(),
};
final uri = Uri.https(apiAuthority, getAddress);
final response =
await http.post(uri, headers: headers, body: json.encode(body));
if (response.statusCode == 400) {
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final error = responseJSON['error'] as String;
final message = responseJSON['message'] as String;
throw Exception('${error}\n$message');
}
if (response.statusCode != 200) {
return null;
}
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
final String pubAddress = responseJSON['public_address'] as String;
return pubAddress;
}
}

View file

@ -1,3 +1,4 @@
import 'dart:convert';
import 'dart:math';
import 'package:flutter/services.dart';
@ -14,8 +15,11 @@ Future<String> generateName() async {
await rootBundle.loadString('assets/text/Wallet_Adjectives.txt');
final nounStringRaw =
await rootBundle.loadString('assets/text/Wallet_Nouns.txt');
final adjectives = List<String>.from(adjectiveStringRaw.split('\n'));
final nouns = List<String>.from(nounStringRaw.split('\n'));
final ls = LineSplitter();
final adjectives = ls.convert(adjectiveStringRaw);
final nouns = ls.convert(nounStringRaw);
final chosenAdjective = adjectives[randomThing.nextInt(adjectives.length)];
final chosenNoun = nouns[randomThing.nextInt(nouns.length)];
final returnString =

View file

@ -20,6 +20,25 @@ class LanguageService {
'hr': 'Hrvatski (Croatian)',
'it': 'Italiano (Italian)'
};
static const Map<String, String> localeCountryCode = {
'en': 'usa',
'de': 'deu',
'es': 'esp',
'fr': 'fra',
'hi': 'ind',
'ja': 'jpn',
'ko': 'kor',
'nl': 'nld',
'pl': 'pol',
'pt': 'prt',
'ru': 'rus',
'uk': 'ukr',
'zh': 'chn',
'hr': 'hrv',
'it': 'ita'
};
static final list = <String, String> {};
static void loadLocaleList() {

View file

@ -5,6 +5,7 @@ import 'package:cake_wallet/core/key_service.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/core/wallet_loading_service.dart';
Future<void> loadCurrentWallet() async {
final appStore = getIt.get<AppStore>();
@ -15,9 +16,7 @@ Future<void> loadCurrentWallet() async {
getIt.get<SharedPreferences>().getInt(PreferencesKey.currentWalletType) ??
0;
final type = deserializeFromInt(typeRaw);
final password =
await getIt.get<KeyService>().getWalletPassword(walletName: name);
final _service = getIt.get<WalletService>(param1: type);
final wallet = await _service.openWallet(name, password);
final walletLoadingService = getIt.get<WalletLoadingService>();
final wallet = await walletLoadingService.load(type, name);
appStore.changeCurrentWallet(wallet);
}

View file

@ -3,13 +3,16 @@ import 'package:cake_wallet/entities/openalias_record.dart';
import 'package:cake_wallet/entities/parsed_address.dart';
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
import 'package:cake_wallet/entities/emoji_string_extension.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:cake_wallet/entities/fio_address_provider.dart';
class AddressResolver {
AddressResolver({@required this.yatService});
AddressResolver({@required this.yatService, this.walletType});
final YatService yatService;
final WalletType walletType;
static const unstoppableDomains = [
'crypto',
@ -26,9 +29,19 @@ class AddressResolver {
Future<ParsedAddress> resolve(String text, String ticker) async {
try {
if (text.contains('@') && !text.contains('.')) {
final bool isFioRegistered = await FioAddressProvider.checkAvail(text);
if (isFioRegistered) {
final address = await FioAddressProvider.getPubAddress(text, ticker);
return ParsedAddress.fetchFioAddress(address: address, name: text);
}
}
if (text.hasOnlyEmojis) {
final addresses = await yatService.fetchYatAddress(text, ticker);
return ParsedAddress.fetchEmojiAddress(addresses: addresses, name: text);
if (walletType != WalletType.haven) {
final addresses = await yatService.fetchYatAddress(text, ticker);
return ParsedAddress.fetchEmojiAddress(addresses: addresses, name: text);
}
}
final formattedName = OpenaliasRecord.formatDomainName(text);
final domainParts = formattedName.split('.');

View file

@ -2,7 +2,7 @@ import 'package:cake_wallet/entities/openalias_record.dart';
import 'package:cake_wallet/entities/yat_record.dart';
import 'package:flutter/material.dart';
enum ParseFrom { unstoppableDomains, openAlias, yatRecord, notParsed }
enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed }
class ParsedAddress {
ParsedAddress({
@ -58,4 +58,13 @@ class ParsedAddress {
parseFrom: ParseFrom.openAlias,
);
}
factory ParsedAddress.fetchFioAddress({@required String address, @required String name}){
return ParsedAddress(
addresses: [address],
name: name,
parseFrom: ParseFrom.fio,
);
}
}

View file

@ -22,4 +22,8 @@ class PreferencesKey {
static const bitcoinTransactionPriority = 'current_fee_priority_bitcoin';
static const shouldShowReceiveWarning = 'should_show_receive_warning';
static const shouldShowYatPopup = 'should_show_yat_popup';
static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
static String moneroWalletUpdateV1Key(String name)
=> '${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}';
}

View file

@ -240,33 +240,23 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
}
String networkFor(CryptoCurrency currency) {
const bnbTitle = 'bnb';
switch (currency) {
case CryptoCurrency.usdt:
return CryptoCurrency.btc.title.toLowerCase();
case CryptoCurrency.usdterc20:
return CryptoCurrency.eth.title.toLowerCase();
case CryptoCurrency.bnb:
return bnbTitle;
case CryptoCurrency.dai:
return CryptoCurrency.eth.title.toLowerCase();
default:
return currency.title.toLowerCase();
return currency.tag != null
? currency.tag.toLowerCase()
: currency.title.toLowerCase();
}
}
}
static String normalizeCryptoCurrency(CryptoCurrency currency) {
const bnbTitle = 'bnb';
switch(currency) {
case CryptoCurrency.bnb:
return bnbTitle;
case CryptoCurrency.usdterc20:
return CryptoCurrency.usdt.title.toLowerCase();
String normalizeCryptoCurrency(CryptoCurrency currency) {
switch(currency) {
case CryptoCurrency.zec:
return 'zec';
default:
return currency.title.toLowerCase();
}
}
}

View file

@ -250,14 +250,15 @@ class SideShiftExchangeProvider extends ExchangeProvider {
String get title => 'SideShift';
static String normalizeCryptoCurrency(CryptoCurrency currency) {
const bnbTitle = 'bsc';
const usdterc20 = 'usdtErc20';
switch (currency) {
case CryptoCurrency.zaddr:
return 'zaddr';
case CryptoCurrency.zec:
return 'zec';
case CryptoCurrency.bnb:
return bnbTitle;
return currency.tag.toLowerCase();
case CryptoCurrency.usdterc20:
return usdterc20;
return 'usdtErc20';
default:
return currency.title.toLowerCase();
}

View file

@ -297,9 +297,9 @@ class CWHaven extends Haven {
}
CryptoCurrency assetOfTransaction(TransactionInfo tx) {
final tx = transaction as HavenTransactionInfo;
final asset = CryptoCurrency.fromString(tx.assetType);
return asset;
final transaction = tx as HavenTransactionInfo;
final asset = CryptoCurrency.fromString(transaction.assetType);
return asset;
}
List<AssetRate> getAssetRate()

View file

@ -1,6 +1,5 @@
import 'package:cake_wallet/entities/contact_record.dart';
import 'package:cake_wallet/buy/order.dart';
import 'package:cake_wallet/entities/transaction_description.dart';
import 'package:cake_wallet/src/screens/backup/backup_page.dart';
import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
@ -17,10 +16,7 @@ import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/unspent_coins/unspent_coins_item.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
@ -30,7 +26,6 @@ import 'package:cake_wallet/utils/language_list.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
import 'package:cake_wallet/view_model/wallet_restoration_from_keys_vm.dart';
import 'package:cake_wallet/entities/contact.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_type.dart';
@ -56,7 +51,6 @@ import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_c
import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
import 'package:cake_wallet/src/screens/contact/contact_page.dart';
import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
import 'package:cake_wallet/src/screens/settings/change_language.dart';
import 'package:cake_wallet/src/screens/restore/restore_wallet_from_seed_details.dart';
import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
import 'package:cake_wallet/src/screens/settings/settings.dart';
@ -70,10 +64,9 @@ import 'package:cake_wallet/src/screens/exchange/exchange_template_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:flutter/services.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/address_page.dart';
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
import 'package:cake_wallet/src/screens/ionia/ionia.dart';
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
@ -83,7 +76,7 @@ RouteSettings currentRouteSettings;
Route<dynamic> createRoute(RouteSettings settings) {
currentRouteSettings = settings;
switch (settings.name) {
case Routes.welcome:
return MaterialPageRoute<void>(builder: (_) => createWelcomePage());
@ -92,7 +85,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>(
param1: (PinCodeState<PinCodeWidget> context, dynamic _) {
if (availableWalletTypes.length == 1) {
if (availableWalletTypes.length == 1) {
Navigator.of(context.context).pushNamed(Routes.newWallet, arguments: availableWalletTypes.first);
} else {
Navigator.of(context.context).pushNamed(Routes.newWalletType);
@ -373,10 +366,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.faq:
return MaterialPageRoute<void>(builder: (_) => getIt.get<FaqPage>());
case Routes.changeLanguage:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<LanguageListPage>());
case Routes.preSeed:
return MaterialPageRoute<void>(
builder: (_) =>
@ -410,6 +399,16 @@ Route<dynamic> createRoute(RouteSettings settings) {
getIt.get<UnspentCoinsDetailsPage>(
param1: args));
case Routes.fullscreenQR:
final args = settings.arguments as Map<String, dynamic>;
return MaterialPageRoute<void>(
builder: (_) =>
getIt.get<FullscreenQRPage>(
param1: args['qrData'] as String,
param2: args['isLight'] as bool,
));
case Routes.ioniaWelcomePage:
return CupertinoPageRoute<void>(builder: (_) => getIt.get<IoniaWelcomePage>());

View file

@ -41,7 +41,6 @@ class Routes {
static const unlock = '/auth_not_closable';
static const rescan = '/rescan';
static const faq = '/faq';
static const changeLanguage = '/change_language';
static const newWalletType = '/new_wallet_type';
static const sendTemplate = '/send_template';
static const exchangeTemplate = '/exchange_template';
@ -60,6 +59,7 @@ class Routes {
static const moneroRestoreWalletFromWelcome = '/monero_restore_wallet';
static const moneroNewWalletFromWelcome = '/monero_new_wallet';
static const addressPage = '/address_page';
static const fullscreenQR = '/fullscreen_qr';
static const ioniaWelcomePage = '/cake_pay_welcome_page';
static const ioniaCreateAccountPage = '/cake_pay_create_account_page';
static const ioniaLoginPage = '/cake_pay_login_page';

View file

@ -2,6 +2,8 @@ import 'dart:ui';
import 'package:cake_wallet/buy/buy_amount.dart';
import 'package:cake_wallet/buy/buy_provider.dart';
import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
import 'package:cake_wallet/entities/fiat_currency.dart';
import 'package:cake_wallet/src/widgets/picker.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/src/screens/buy/widgets/buy_list_item.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
@ -27,7 +29,6 @@ class PreOrderPage extends BasePage {
PreOrderPage({@required this.buyViewModel})
: _amountFocus = FocusNode(),
_amountController = TextEditingController() {
_amountController.addListener(() {
final amount = _amountController.text;
@ -110,52 +111,70 @@ class PreOrderPage extends BasePage {
.decorationColor,
], begin: Alignment.topLeft, end: Alignment.bottomRight),
),
child: Padding(
padding: EdgeInsets.only(top: 100, bottom: 65),
child: Center(
child: Container(
width: 210,
child: BaseTextFormField(
focusNode: _amountFocus,
controller: _amountController,
keyboardType:
TextInputType.numberWithOptions(
signed: false, decimal: true),
inputFormatters: [
FilteringTextInputFormatter
.allow(RegExp(_amountPattern))
],
prefixIcon: Padding(
padding: EdgeInsets.only(top: 2),
child:
Text(buyViewModel.fiatCurrency.title + ': ',
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.w600,
color: Colors.white,
)),
),
hintText: '0.00',
borderColor: Theme.of(context)
.primaryTextTheme
.body2
.decorationColor,
borderWidth: 0.5,
textStyle: TextStyle(
child: Padding(
padding: EdgeInsets.only(top: 100, bottom: 65),
child: Center(
child: Container(
width: 210,
child: BaseTextFormField(
focusNode: _amountFocus,
controller: _amountController,
keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
inputFormatters: [FilteringTextInputFormatter.allow(RegExp(_amountPattern))],
prefixIcon: GestureDetector(
onTap: () {
showPopUp<void>(
context: context,
builder: (_) => Picker(
hintText: S.current.search_currency,
items: FiatCurrency.currenciesAvailableToBuyWith,
selectedAtIndex:
FiatCurrency.currenciesAvailableToBuyWith.indexOf(buyViewModel.fiatCurrency),
onItemSelected: (FiatCurrency selectedCurrency) {
buyViewModel.buyAmountViewModel.fiatCurrency = selectedCurrency;
},
images: FiatCurrency.currenciesAvailableToBuyWith
.map((e) => Image.asset("assets/images/flags/${e.countryCode}.png"))
.toList(),
isGridView: true,
matchingCriteria: (FiatCurrency currency, String searchText) {
return currency.title.toLowerCase().contains(searchText) ||
currency.fullName.toLowerCase().contains(searchText);
},
),
);
},
child: Padding(
padding: EdgeInsets.only(top: 2),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.keyboard_arrow_down, color: Colors.white),
Text(
buyViewModel.fiatCurrency.title + ': ',
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.w500,
color: Colors.white),
placeholderTextStyle: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.headline
.decorationColor,
fontWeight: FontWeight.w500,
fontSize: 36),
)
)
)
)
fontWeight: FontWeight.w600,
color: Colors.white,
),
),
],
),
),
),
hintText: '0.00',
borderColor: Theme.of(context).primaryTextTheme.body2.decorationColor,
borderWidth: 0.5,
textStyle: TextStyle(fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white),
placeholderTextStyle: TextStyle(
color: Theme.of(context).primaryTextTheme.headline.decorationColor,
fontWeight: FontWeight.w500,
fontSize: 36,
),
),
),
),
),
),
if (buyViewModel.isShowProviderButtons) Padding(
padding: EdgeInsets.only(top: 38, bottom: 18),
@ -273,7 +292,7 @@ class PreOrderPage extends BasePage {
buyViewModel.isRunning = true;
final url = await buyViewModel.fetchUrl();
if (url.isNotEmpty) {
if (buyViewModel.selectedProvider is MoonPayBuyProvider) {
if (await canLaunch(url)) await launch(url);
@ -287,4 +306,4 @@ class PreOrderPage extends BasePage {
buyViewModel.reset();
buyViewModel.isRunning = false;
}
}
}

View file

@ -94,7 +94,10 @@ class ContactPage extends BasePage {
child: Observer(
builder: (_) => AddressTextField(
controller: _addressController,
options: [AddressTextFieldOption.qrCode],
options: [
AddressTextFieldOption.paste,
AddressTextFieldOption.qrCode,
],
buttonColor: Theme.of(context).accentTextTheme.display2.color,
iconColor: PaletteDark.gray,
borderColor: Theme.of(context).primaryTextTheme.title.backgroundColor,
@ -147,6 +150,7 @@ class ContactPage extends BasePage {
contactViewModel.currencies.indexOf(contactViewModel.currency),
items: contactViewModel.currencies,
title: S.of(context).please_select,
hintText: S.of(context).search_currency,
onItemSelected: (CryptoCurrency item) =>
contactViewModel.currency = item),
context: context);

View file

@ -52,24 +52,6 @@ class WalletMenu {
image: Image.asset('assets/images/open_book_menu.png',
height: 16, width: 16),
handler: () => Navigator.of(context).pushNamed(Routes.addressBook)),
WalletMenuItem(
title: S.current.backup,
image: Image.asset('assets/images/restore_wallet.png',
height: 16,
width: 16,
color: Palette.darkBlue),
handler: () {
Navigator
.of(context)
.pushNamed(
Routes.auth,
arguments: (bool isAuthenticatedSuccessfully, AuthPageState auth) {
if (isAuthenticatedSuccessfully) {
auth.close();
Navigator.of(auth.context).pushNamed(Routes.backup);
}
});
}),
WalletMenuItem(
title: S.current.settings_title,
image: Image.asset('assets/images/settings_menu.png',

View file

@ -1,8 +1,7 @@
import 'dart:ui';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_utils.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker_item_widget.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/picker_item.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cw_core/crypto_currency.dart';
@ -13,8 +12,9 @@ class CurrencyPicker extends StatefulWidget {
CurrencyPicker(
{@required this.selectedAtIndex,
@required this.items,
@required this.title,
@required this.onItemSelected,
this.title,
this.hintText,
this.isMoneroWallet = false,
this.isConvertFrom = false});
@ -24,6 +24,7 @@ class CurrencyPicker extends StatefulWidget {
final Function(CryptoCurrency) onItemSelected;
final bool isMoneroWallet;
final bool isConvertFrom;
final String hintText;
@override
CurrencyPickerState createState() => CurrencyPickerState(items);
@ -33,169 +34,137 @@ class CurrencyPickerState extends State<CurrencyPicker> {
CurrencyPickerState(this.items)
: isSearchBarActive = false,
textFieldValue = '',
subPickerItemsList = [],
appBarTextStyle = TextStyle(
fontSize: 20,
fontFamily: 'Lato',
backgroundColor: Colors.transparent,
color: Colors.white);
subPickerItemsList = items,
appBarTextStyle =
TextStyle(fontSize: 20, fontFamily: 'Lato', backgroundColor: Colors.transparent, color: Colors.white);
@override
void initState() {
pickerItemsList = CryptoCurrency.all
.map((CryptoCurrency cur) => PickerItem<CryptoCurrency>(cur,
title: CurrencyUtils.titleForCurrency(cur),
iconPath: CurrencyUtils.iconPathForCurrency(cur),
tag: CurrencyUtils.tagForCurrency(cur),
description: CurrencyUtils.descriptionForCurrency(cur)))
.toList();
cleanSubPickerItemsList();
super.initState();
}
List<PickerItem<CryptoCurrency>> pickerItemsList;
List<CryptoCurrency> items;
bool isSearchBarActive;
String textFieldValue;
List<PickerItem<CryptoCurrency>> subPickerItemsList;
List<CryptoCurrency> subPickerItemsList;
TextStyle appBarTextStyle;
void cleanSubPickerItemsList() {
subPickerItemsList = pickerItemsList
.where((element) => items.contains(element.original))
.toList();
}
void cleanSubPickerItemsList() => subPickerItemsList = items;
void currencySearchBySubstring(
String subString, List<PickerItem<CryptoCurrency>> list) {
void currencySearchBySubstring(String subString) {
setState(() {
if (subString.isNotEmpty) {
subPickerItemsList = subPickerItemsList
subPickerItemsList = items
.where((element) =>
element.title.contains(subString.toUpperCase()) ||
element.description.contains(subString.toLowerCase()))
(element.title != null ? element.title.toLowerCase().contains(subString.toLowerCase()) : false) ||
(element.tag != null ? element.tag.toLowerCase().contains(subString.toLowerCase()) : false) ||
(element.name != null ? element.name.toLowerCase().contains(subString.toLowerCase()) : false))
.toList();
} else {
cleanSubPickerItemsList();
return;
}
cleanSubPickerItemsList();
});
}
@override
Widget build(BuildContext context) {
return AlertBackground(
child: SafeArea(
child: Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: Colors.transparent,
body: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 26.0, vertical: 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
isSearchBarActive
? Expanded(
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
InkWell(
child: Text(
S.of(context).cancel,
style: appBarTextStyle,
),
onTap: () {
setState(() {
isSearchBarActive = false;
textFieldValue = '';
cleanSubPickerItemsList();
});
}),
Container(
width: 100.0,
child: CupertinoTextField(
autofocus: true,
placeholder:
S.of(context).search + '...',
placeholderStyle: appBarTextStyle,
decoration: BoxDecoration(
color: Colors.transparent),
cursorColor: Colors.white,
cursorHeight: 23.0,
style: appBarTextStyle,
onChanged: (value) {
this.textFieldValue = value;
cleanSubPickerItemsList();
currencySearchBySubstring(
textFieldValue,
subPickerItemsList);
}),
)
],
),
)
: Text(
widget.title,
style: appBarTextStyle,
),
IconButton(
splashRadius: 23,
icon: Icon(Icons.search, color: Colors.white),
onPressed: () {
setState(() {
isSearchBarActive = true;
});
},
)
]),
),
Expanded(
flex: 12,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 26.0, vertical: 26.0),
child: Container(
child: CurrencyPickerWidget(
crossAxisCount: 2,
selectedAtIndex: widget.selectedAtIndex,
itemsCount: subPickerItemsList.length,
pickerItemsList: subPickerItemsList,
pickListItem: (int index) {
setState(() {
widget.selectedAtIndex = index;
});
widget
.onItemSelected(subPickerItemsList[index].original);
if (widget.isConvertFrom &&
!widget.isMoneroWallet &&
(subPickerItemsList[index].original ==
CryptoCurrency.xmr)) {
} else {
Navigator.of(context).pop();
}
},
child: Stack(
alignment: Alignment.center,
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
if (widget.title?.isNotEmpty ?? false)
Container(
padding: EdgeInsets.symmetric(horizontal: 24),
child: Text(
widget.title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white,
),
),
),
),
Expanded(
flex: 2,
child: Container(
width: 42.0,
alignment: Alignment.topCenter,
child: FittedBox(
child: FloatingActionButton(
elevation: 0,
backgroundColor: Colors.white,
onPressed: () {
Navigator.of(context).pop();
},
child: Icon(
Icons.close_outlined,
color: Palette.darkBlueCraiola,
size: 30.0,
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(30)),
child: Container(
color: Theme.of(context).accentTextTheme.title.color,
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.65,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.hintText != null)
Padding(
padding: const EdgeInsets.all(16),
child: TextFormField(
style: TextStyle(color: Theme.of(context).primaryTextTheme.title.color),
decoration: InputDecoration(
hintText: widget.hintText,
prefixIcon: Image.asset("assets/images/search_icon.png"),
filled: true,
fillColor: const Color(0xffF2F0FA),
alignLabelWithHint: false,
contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(
color: Colors.transparent,
)),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(14),
borderSide: const BorderSide(
color: Colors.transparent,
)),
),
onChanged: (value) {
this.textFieldValue = value;
cleanSubPickerItemsList();
currencySearchBySubstring(textFieldValue);
},
),
),
Divider(
color: Theme.of(context).accentTextTheme.title.backgroundColor,
height: 1,
),
if (widget.selectedAtIndex != -1)
AspectRatio(
aspectRatio: 6,
child: PickerItemWidget(
title: items[widget.selectedAtIndex].title,
iconPath: items[widget.selectedAtIndex].iconPath,
isSelected: true,
tag: items[widget.selectedAtIndex].tag,
),
),
Flexible(
child: CurrencyPickerWidget(
crossAxisCount: 2,
selectedAtIndex: widget.selectedAtIndex,
pickerItemsList: subPickerItemsList,
pickListItem: (int index) {
setState(() {
widget.selectedAtIndex = index;
});
widget.onItemSelected(subPickerItemsList[index]);
if (widget.isConvertFrom &&
!widget.isMoneroWallet &&
(subPickerItemsList[index] == CryptoCurrency.xmr)) {
} else {
Navigator.of(context).pop();
}
},
),
),
],
),
),
),
@ -203,7 +172,8 @@ class CurrencyPickerState extends State<CurrencyPicker> {
),
],
),
),
AlertCloseButton(),
],
),
);
}

View file

@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
class PickerItemWidget extends StatelessWidget {
const PickerItemWidget(
{this.iconPath, this.title, this.isSelected, this.tag, this.onTap});
const PickerItemWidget({this.iconPath, this.title, this.isSelected = false, this.tag, this.onTap});
final String iconPath;
final String title;
@ -16,74 +15,56 @@ class PickerItemWidget extends StatelessWidget {
return GestureDetector(
onTap: onTap,
child: Container(
color: isSelected
? Theme.of(context).textTheme.bodyText1.color
: Theme.of(context).accentTextTheme.headline6.color,
child: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Container(
child: Image.asset(
iconPath,
height: 32.0,
width: 32.0,
),
),
color: Theme.of(context).accentTextTheme.headline6.color,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 24),
child: Row(
children: [
Container(
child: Image.asset(
iconPath,
height: 20.0,
width: 20.0,
),
Expanded(
child: Stack(
clipBehavior: Clip.none,
alignment: Alignment.centerLeft,
children: [
Text(
title,
style: TextStyle(
color: isSelected
? Palette.blueCraiola
: Theme.of(context).primaryTextTheme.title.color,
fontSize: 18.0,
fontFamily: 'Lato',
),
const SizedBox(width: 6),
Expanded(
child: Row(
children: [
Text(
title,
style: TextStyle(
color: isSelected ? Palette.blueCraiola : Theme.of(context).primaryTextTheme.title.color,
fontSize: isSelected ? 16 : 14.0,
fontFamily: 'Lato',
fontWeight: FontWeight.w600,
),
),
if (tag != null)
Align(
alignment: Alignment.topCenter,
child: Container(
width: 35.0,
height: 18.0,
child: Center(
child: Text(
tag,
style: TextStyle(
fontSize: 7.0, fontFamily: 'Lato', color: Theme.of(context).textTheme.body1.color),
),
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
//border: Border.all(color: ),
color: Theme.of(context).textTheme.body1.decorationColor,
),
),
),
tag != null
? Positioned(
top: -20.0,
right: 7.0,
child: Container(
width: 35.0,
height: 18.0,
child: Center(
child: Text(
tag,
style: TextStyle(
fontSize: 7.0,
fontFamily: 'Lato',
color: Theme.of(context)
.textTheme
.body1
.color),
),
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0),
//border: Border.all(color: ),
color: Theme.of(context)
.textTheme
.body1
.decorationColor,
),
),
)
: Container(),
],
),
],
),
],
),
),
if (isSelected) Icon(Icons.check_circle, color: Theme.of(context).accentTextTheme.body2.color)
],
),
),
),

View file

@ -4,58 +4,47 @@ import 'picker_item.dart';
import 'currency_picker_item_widget.dart';
class CurrencyPickerWidget extends StatelessWidget {
const CurrencyPickerWidget({
CurrencyPickerWidget({
@required this.crossAxisCount,
@required this.selectedAtIndex,
@required this.itemsCount,
@required this.pickerItemsList,
@required this.pickListItem,
});
final int crossAxisCount;
final int selectedAtIndex;
final int itemsCount;
final Function pickListItem;
final List<PickerItem<CryptoCurrency>> pickerItemsList;
final List<CryptoCurrency> pickerItemsList;
final ScrollController _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).accentTextTheme.headline6.backgroundColor,
borderRadius: BorderRadius.circular(14.0),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(14.0),
child: Scrollbar(
showTrackOnHover: true,
isAlwaysShown: true,
thickness: 6.0,
radius: Radius.circular(3),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: 1,
mainAxisExtent: constraints.maxHeight / 8,
mainAxisSpacing: 1),
itemCount: pickerItemsList.length,
itemBuilder: (BuildContext ctx, index) {
return PickerItemWidget(
onTap: () {
pickListItem(index);
},
title: pickerItemsList[index].title,
iconPath: pickerItemsList[index].iconPath,
isSelected: index == selectedAtIndex,
tag: pickerItemsList[index].tag,
);
}),
return Container(
color: Theme.of(context).accentTextTheme.headline6.backgroundColor,
child: Scrollbar(
controller: _scrollController,
child: GridView.builder(
controller: _scrollController,
padding: EdgeInsets.zero,
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: 2,
childAspectRatio: 3,
),
),
);
},
itemCount: pickerItemsList.length,
itemBuilder: (BuildContext ctx, index) {
return PickerItemWidget(
onTap: () {
pickListItem(index);
},
title: pickerItemsList[index].title,
iconPath: pickerItemsList[index].iconPath,
tag: pickerItemsList[index].tag,
);
}),
),
);
}
}

View file

@ -1,101 +0,0 @@
import 'package:cw_core/crypto_currency.dart';
class CurrencyUtils {
static String tagForCurrency(CryptoCurrency cur) {
switch (cur) {
case CryptoCurrency.bnb:
return 'BEP2';
case CryptoCurrency.dai:
return 'ETH';
case CryptoCurrency.usdt:
return 'OMNI';
case CryptoCurrency.usdterc20:
return 'ETH';
default:
return null;
}
}
static String iconPathForCurrency(CryptoCurrency cur) {
switch (cur) {
case CryptoCurrency.xmr:
return 'assets/images/monero_icon.png';
case CryptoCurrency.ada:
return 'assets/images/ada_icon.png';
case CryptoCurrency.bch:
return 'assets/images/bch_icon.png';
case CryptoCurrency.bnb:
return 'assets/images/bnb_icon.png';
case CryptoCurrency.btc:
return 'assets/images/btc.png';
case CryptoCurrency.dai:
return 'assets/images/dai_icon.png';
case CryptoCurrency.dash:
return 'assets/images/dash_icon.png';
case CryptoCurrency.eos:
return 'assets/images/eos_icon.png';
case CryptoCurrency.eth:
return 'assets/images/eth_icon.png';
case CryptoCurrency.ltc:
return 'assets/images/litecoin-ltc_icon.png';
case CryptoCurrency.trx:
return 'assets/images/trx_icon.png';
case CryptoCurrency.usdt:
return 'assets/images/usdt_icon.png';
case CryptoCurrency.usdterc20:
return 'assets/images/usdterc20_icon.png';
case CryptoCurrency.xlm:
return 'assets/images/xlm_icon.png';
case CryptoCurrency.xrp:
return 'assets/images/xrp_icon.png';
case CryptoCurrency.xhv:
return 'assets/images/xhv_logo.png';
default:
return null;
}
}
static String titleForCurrency(CryptoCurrency cur) {
switch (cur) {
case CryptoCurrency.bnb:
return 'BNB';
case CryptoCurrency.usdterc20:
return 'USDT';
default:
return cur.title;
}
}
static String descriptionForCurrency(CryptoCurrency cur) {
switch (cur) {
case CryptoCurrency.xmr:
return 'monero';
case CryptoCurrency.ada:
return 'cardano';
case CryptoCurrency.bch:
return 'bitcoin cash';
case CryptoCurrency.bnb:
return 'binance bep2';
case CryptoCurrency.btc:
return 'bitcoin';
case CryptoCurrency.dai:
return 'dai eth';
case CryptoCurrency.eth:
return 'ethereum';
case CryptoCurrency.ltc:
return 'litecoin';
case CryptoCurrency.trx:
return 'tron';
case CryptoCurrency.usdt:
return 'usdt omni';
case CryptoCurrency.usdterc20:
return 'tether ERC20 eth';
case CryptoCurrency.xlm:
return 'lumens';
case CryptoCurrency.xrp:
return 'ripple';
default:
return cur.title;
}
}
}

View file

@ -176,7 +176,7 @@ class ExchangeCardState extends State<ExchangeCard> {
padding: EdgeInsets.only(right: 5),
child: widget.imageArrow,
),
Text(_selectedCurrency.toString() + ':',
Text(_selectedCurrency.toString(),
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
@ -184,6 +184,40 @@ class ExchangeCardState extends State<ExchangeCard> {
]),
),
),
_selectedCurrency.tag != null ? Padding(
padding: const EdgeInsets.only(right:3.0),
child: Container(
height: 32,
decoration: BoxDecoration(
color: widget.addressButtonsColor ?? Theme.of(context)
.primaryTextTheme
.display1
.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Center(
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text(_selectedCurrency.tag,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
color: Theme.of(context)
.primaryTextTheme
.display1
.decorationColor)),
),
),
),
) : Container(),
Padding(
padding: const EdgeInsets.only(right: 4.0),
child: Text(':',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16,
color: Colors.white)),
),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -316,9 +350,10 @@ class ExchangeCardState extends State<ExchangeCard> {
if (amountController.text.isNotEmpty) {
_showAmountPopup(context, paymentRequest);
} else {
amountController.text = paymentRequest.amount;
return;
}
widget.amountFocusNode.requestFocus();
amountController.text = paymentRequest.amount;
},
placeholder: widget.hasRefundAddress
? S.of(context).refund_address
@ -445,7 +480,7 @@ class ExchangeCardState extends State<ExchangeCard> {
builder: (_) => CurrencyPicker(
selectedAtIndex: widget.currencies.indexOf(_selectedCurrency),
items: widget.currencies,
title: S.of(context).change_currency,
hintText: S.of(context).search_currency,
isMoneroWallet: _isMoneroWallet,
isConvertFrom: widget.hasRefundAddress,
onItemSelected: (CryptoCurrency item) =>
@ -465,6 +500,7 @@ class ExchangeCardState extends State<ExchangeCard> {
rightButtonText: S.of(context).ok,
leftButtonText: S.of(context).cancel,
actionRightButton: () {
widget.amountFocusNode.requestFocus();
amountController.text = paymentRequest.amount;
Navigator.of(context).pop();
},

View file

@ -258,7 +258,7 @@ class _SearchWidget extends StatelessWidget {
final searchIcon = Padding(
padding: EdgeInsets.all(8),
child: Image.asset(
'assets/images/search_icon.png',
'assets/images/mini_search_icon.png',
color: Theme.of(context).textTheme.caption.decorationColor,
),
);

View file

@ -23,7 +23,7 @@ class IoniaFilterModal extends StatelessWidget {
final searchIcon = Padding(
padding: EdgeInsets.all(10),
child: Image.asset(
'assets/images/search_icon.png',
'assets/images/mini_search_icon.png',
color: Theme.of(context).accentColor,
),
);

View file

@ -7,7 +7,7 @@ import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
@ -218,10 +218,21 @@ class _WalletNameFormState extends State<WalletNameForm> {
if (!_formKey.currentState.validate()) {
return;
}
_walletNewVM.create(
options: _walletNewVM.hasLanguageSelector
? _languageSelectorKey.currentState.selected
: null);
if (_walletNewVM.nameExists(_walletNewVM.name)) {
showPopUp<void>(
context: context,
builder: (_) {
return AlertWithOneAction(
alertTitle: '',
alertContent: S.of(context).wallet_name_exists,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
} else {
_walletNewVM.create(
options: _walletNewVM.hasLanguageSelector
? _languageSelectorKey.currentState.selected
: null);
}
}
}

View file

@ -0,0 +1,85 @@
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
import 'package:cake_wallet/themes/theme_base.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
class FullscreenQRPage extends BasePage {
FullscreenQRPage({@required this.qrData, @required this.isLight});
final bool isLight;
final String qrData;
@override
Color get backgroundLightColor => currentTheme.type == ThemeType.bright ? Colors.transparent : Colors.white;
@override
Color get backgroundDarkColor => Colors.transparent;
@override
bool get resizeToAvoidBottomInset => false;
@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 Function(BuildContext, Widget) get rootWrapper => (BuildContext context, Widget scaffold) => Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).accentColor,
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).primaryColor,
],
begin: Alignment.topRight,
end: Alignment.bottomLeft,
),
),
child: scaffold);
@override
Widget body(BuildContext context) {
return Padding(
padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.05),
child: Hero(
tag: Key(qrData),
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(width: 3, color: Theme.of(context).accentTextTheme.display3.backgroundColor)),
child: QrImage(
data: qrData,
backgroundColor: isLight ? Colors.transparent : Colors.black,
foregroundColor: Theme.of(context).accentTextTheme.display3.backgroundColor,
),
),
),
),
),
);
}
}

View file

@ -1,4 +1,6 @@
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/utils/show_bar.dart';
import 'package:device_display_brightness/device_display_brightness.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
@ -38,101 +40,134 @@ class QRWidget extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(children: <Widget>[
Spacer(flex: 3),
Observer(
builder: (_) => Flexible(
flex: 5,
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: Theme.of(context).accentTextTheme.
display3.backgroundColor
)
),
child: QrImage(
data: addressListViewModel.uri.toString(),
backgroundColor: isLight ? Colors.transparent : Colors.black,
foregroundColor: Theme.of(context).accentTextTheme.
display3.backgroundColor,
),
))))),
Spacer(flex: 3)
]),
if (isAmountFieldShow)
Column(
children: [
Padding(
padding: EdgeInsets.only(top: 10),
child: Row(
children: <Widget>[
Expanded(
child: Form(
key: _formKey,
child: BaseTextFormField(
focusNode: amountTextFieldFocusNode,
controller: amountController,
keyboardType: TextInputType.numberWithOptions(
decimal: true),
inputFormatters: [
BlacklistingTextInputFormatter(
RegExp('[\\-|\\ ]'))
],
textAlign: TextAlign.center,
hintText: S.of(context).receive_amount,
textColor: Theme.of(context).accentTextTheme.
display3.backgroundColor,
borderColor: Theme.of(context)
.textTheme
.headline
.decorationColor,
validator: AmountValidator(
type: addressListViewModel.type,
isAutovalidate: true),
autovalidate: true,
placeholderTextStyle: TextStyle(
color: Theme.of(context).hoverColor,
fontSize: 18,
fontWeight: FontWeight.w500))))
],
),
padding: const EdgeInsets.only(bottom: 12),
child: Text(
S.of(context).qr_fullscreen,
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.display3.backgroundColor),
),
),
Row(
children: <Widget>[
Spacer(flex: 3),
Observer(
builder: (_) => Flexible(
flex: 5,
child: GestureDetector(
onTap: () async {
// Get the current brightness:
final double brightness = await DeviceDisplayBrightness.getBrightness();
// ignore: unawaited_futures
DeviceDisplayBrightness.setBrightness(1.0);
await Navigator.pushNamed(
context,
Routes.fullscreenQR,
arguments: {
'qrData': addressListViewModel.uri.toString(),
'isLight': isLight,
},
);
// ignore: unawaited_futures
DeviceDisplayBrightness.setBrightness(brightness);
},
child: Hero(
tag: Key(addressListViewModel.uri.toString()),
child: Center(
child: AspectRatio(
aspectRatio: 1.0,
child: Container(
padding: EdgeInsets.all(5),
decoration: BoxDecoration(
border: Border.all(
width: 3,
color: Theme.of(context).accentTextTheme.display3.backgroundColor,
),
),
child: QrImage(
data: addressListViewModel.uri.toString(),
backgroundColor: isLight ? Colors.transparent : Colors.black,
foregroundColor: Theme.of(context).accentTextTheme.display3.backgroundColor,
),
),
),
),
),
),
),
),
Spacer(flex: 3)
],
),
],
),
if (isAmountFieldShow)
Padding(
padding: EdgeInsets.only(top: 10),
child: Row(
children: <Widget>[
Expanded(
child: Form(
key: _formKey,
child: BaseTextFormField(
focusNode: amountTextFieldFocusNode,
controller: amountController,
keyboardType: TextInputType.numberWithOptions(decimal: true),
inputFormatters: [BlacklistingTextInputFormatter(RegExp('[\\-|\\ ]'))],
textAlign: TextAlign.center,
hintText: S.of(context).receive_amount,
textColor: Theme.of(context).accentTextTheme.display3.backgroundColor,
borderColor: Theme.of(context).textTheme.headline.decorationColor,
validator: AmountValidator(type: addressListViewModel.type, isAutovalidate: true),
autovalidate: true,
placeholderTextStyle: TextStyle(
color: Theme.of(context).hoverColor,
fontSize: 18,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
),
Padding(
padding: EdgeInsets.only(top: 8, bottom: 8),
child: Builder(
builder: (context) => Observer(
builder: (context) => GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(
text: addressListViewModel.address.address));
showBar<void>(
context, S.of(context).copied_to_clipboard);
},
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(
addressListViewModel.address.address,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.
display3.backgroundColor),
),
),
Padding(
padding: EdgeInsets.only(left: 12),
child: copyImage,
)
],
),
))),
builder: (context) => Observer(
builder: (context) => GestureDetector(
onTap: () {
Clipboard.setData(ClipboardData(text: addressListViewModel.address.address));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
child: Row(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Text(
addressListViewModel.address.address,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: Theme.of(context).accentTextTheme.display3.backgroundColor),
),
),
Padding(
padding: EdgeInsets.only(left: 12),
child: copyImage,
)
],
),
),
),
),
)
],
);

View file

@ -1,4 +1,4 @@
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/palette.dart';
import 'package:flutter/services.dart';
import 'package:flutter/cupertino.dart';

View file

@ -3,7 +3,7 @@ import 'package:mobx/mobx.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';

View file

@ -1,3 +1,5 @@
import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/services.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@ -6,14 +8,15 @@ import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/entities/generate_name.dart';
class WalletRestoreFromKeysFrom extends StatefulWidget {
WalletRestoreFromKeysFrom({Key key, this.onHeightOrDateEntered})
WalletRestoreFromKeysFrom({Key key, this.onHeightOrDateEntered, this.walletRestoreViewModel})
: super(key: key);
final Function(bool) onHeightOrDateEntered;
final WalletRestoreViewModel walletRestoreViewModel;
@override
WalletRestoreFromKeysFromState createState() =>
@ -113,6 +116,7 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
maxLines: null)),
BlockchainHeightWidget(
key: blockchainHeightKey,
hasDatePicker: widget.walletRestoreViewModel.type != WalletType.haven,
onHeightChange: (_) => null,
onHeightOrDateEntered: widget.onHeightOrDateEntered)
]),

View file

@ -10,7 +10,7 @@ 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/base_text_form_field.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/core/validator.dart';
import 'package:cake_wallet/core/wallet_name_validator.dart';
class WalletRestoreFromSeedForm extends StatefulWidget {
WalletRestoreFromSeedForm(
@ -41,6 +41,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
WalletRestoreFromSeedFormState(this.language)
: seedWidgetStateKey = GlobalKey<SeedWidgetState>(),
blockchainHeightKey = GlobalKey<BlockchainHeightState>(),
formKey = GlobalKey<FormState>(),
languageController = TextEditingController(),
nameTextEditingController = TextEditingController();
@ -48,6 +49,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
final GlobalKey<BlockchainHeightState> blockchainHeightKey;
final TextEditingController languageController;
final TextEditingController nameTextEditingController;
final GlobalKey<FormState> formKey;
String language;
@override
@ -61,7 +63,9 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Column(children: [
Stack(
Form(
key: formKey,
child: Stack(
alignment: Alignment.centerRight,
children: [
BaseTextFormField(
@ -97,7 +101,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
),
),
],
),
)),
Container(height: 20),
SeedWidget(
key: seedWidgetStateKey,

View file

@ -83,6 +83,7 @@ class WalletRestorePage extends BasePage {
case WalletRestoreMode.keys:
_pages.add(WalletRestoreFromKeysFrom(
key: walletRestoreFromKeysFormKey,
walletRestoreViewModel: walletRestoreViewModel,
onHeightOrDateEntered: (value) =>
walletRestoreViewModel.isButtonEnabled = value));
break;
@ -192,8 +193,7 @@ class WalletRestorePage extends BasePage {
child: Observer(
builder: (context) {
return LoadingPrimaryButton(
onPressed: () =>
walletRestoreViewModel.create(options: _credentials()),
onPressed: _confirmForm,
text: S.of(context).restore_recover,
color:
Theme.of(context).accentTextTheme.subtitle.decorationColor,
@ -267,4 +267,45 @@ class WalletRestorePage extends BasePage {
return credentials;
}
void _confirmForm() {
final formContext = walletRestoreViewModel.mode == WalletRestoreMode.seed
? walletRestoreFromSeedFormKey.currentContext
: walletRestoreFromKeysFormKey.currentContext;
final formKey = walletRestoreViewModel.mode == WalletRestoreMode.seed
? walletRestoreFromSeedFormKey.currentState.formKey
: walletRestoreFromKeysFormKey.currentState.formKey;
final name = walletRestoreViewModel.mode == WalletRestoreMode.seed
? walletRestoreFromSeedFormKey
.currentState.nameTextEditingController.value.text
: walletRestoreFromKeysFormKey
.currentState.nameTextEditingController.value.text;
if (!formKey.currentState.validate()) {
return;
}
if (walletRestoreViewModel.nameExists(name)) {
showNameExistsAlert(formContext);
return;
}
walletRestoreViewModel.create(options: _credentials());
}
Future<void> showNameExistsAlert(BuildContext context) {
return showPopUp<void>(
context: context,
builder: (_) {
return AlertWithOneAction(
alertTitle: '',
alertContent: S.of(context).wallet_name_exists,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop());
});
}
}

View file

@ -23,6 +23,11 @@ Future<String> extractAddressFromParsed(
content = S.of(context).openalias_alert_content(parsedAddress.name);
address = parsedAddress.addresses.first;
break;
case ParseFrom.fio:
title = S.of(context).address_detected;
content = S.of(context).openalias_alert_content(parsedAddress.name);
address = parsedAddress.addresses.first;
break;
case ParseFrom.yatRecord:
if (parsedAddress.name.isEmpty) {
title = S.of(context).yat_error;

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