mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-01-22 18:54:47 +00:00
Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-438-add-nano
This commit is contained in:
commit
c8d9fcbaf4
64 changed files with 1069 additions and 602 deletions
|
@ -45,8 +45,8 @@ android {
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId appProperties['id']
|
applicationId appProperties['id']
|
||||||
minSdkVersion 21
|
minSdkVersion 24
|
||||||
targetSdkVersion 31
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
|
@ -12,14 +12,14 @@ Uri createUriFromElectrumAddress(String address) => Uri.tryParse('tcp://$address
|
||||||
|
|
||||||
@HiveType(typeId: Node.typeId)
|
@HiveType(typeId: Node.typeId)
|
||||||
class Node extends HiveObject with Keyable {
|
class Node extends HiveObject with Keyable {
|
||||||
Node({
|
Node(
|
||||||
this.login,
|
{this.login,
|
||||||
this.password,
|
this.password,
|
||||||
this.useSSL,
|
this.useSSL,
|
||||||
this.trusted = false,
|
this.trusted = false,
|
||||||
String? uri,
|
this.socksProxyAddress,
|
||||||
WalletType? type,
|
String? uri,
|
||||||
}) {
|
WalletType? type,}) {
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
uriRaw = uri;
|
uriRaw = uri;
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,8 @@ class Node extends HiveObject with Keyable {
|
||||||
login = map['login'] as String?,
|
login = map['login'] as String?,
|
||||||
password = map['password'] as String?,
|
password = map['password'] as String?,
|
||||||
useSSL = map['useSSL'] as bool?,
|
useSSL = map['useSSL'] as bool?,
|
||||||
trusted = map['trusted'] as bool? ?? false;
|
trusted = map['trusted'] as bool? ?? false,
|
||||||
|
socksProxyAddress = map['socksProxyPort'] as String?;
|
||||||
|
|
||||||
static const typeId = 1;
|
static const typeId = 1;
|
||||||
static const boxName = 'Nodes';
|
static const boxName = 'Nodes';
|
||||||
|
@ -56,8 +57,13 @@ class Node extends HiveObject with Keyable {
|
||||||
@HiveField(5, defaultValue: false)
|
@HiveField(5, defaultValue: false)
|
||||||
bool trusted;
|
bool trusted;
|
||||||
|
|
||||||
|
@HiveField(6)
|
||||||
|
String? socksProxyAddress;
|
||||||
|
|
||||||
bool get isSSL => useSSL ?? false;
|
bool get isSSL => useSSL ?? false;
|
||||||
|
|
||||||
|
bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty;
|
||||||
|
|
||||||
Uri get uri {
|
Uri get uri {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
|
@ -85,12 +91,13 @@ class Node extends HiveObject with Keyable {
|
||||||
@override
|
@override
|
||||||
bool operator ==(other) =>
|
bool operator ==(other) =>
|
||||||
other is Node &&
|
other is Node &&
|
||||||
(other.uriRaw == uriRaw &&
|
(other.uriRaw == uriRaw &&
|
||||||
other.login == login &&
|
other.login == login &&
|
||||||
other.password == password &&
|
other.password == password &&
|
||||||
other.typeRaw == typeRaw &&
|
other.typeRaw == typeRaw &&
|
||||||
other.useSSL == useSSL &&
|
other.useSSL == useSSL &&
|
||||||
other.trusted == trusted);
|
other.trusted == trusted &&
|
||||||
|
other.socksProxyAddress == socksProxyAddress);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
|
@ -99,7 +106,8 @@ class Node extends HiveObject with Keyable {
|
||||||
password.hashCode ^
|
password.hashCode ^
|
||||||
typeRaw.hashCode ^
|
typeRaw.hashCode ^
|
||||||
useSSL.hashCode ^
|
useSSL.hashCode ^
|
||||||
trusted.hashCode;
|
trusted.hashCode ^
|
||||||
|
socksProxyAddress.hashCode;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
dynamic get keyIndex {
|
dynamic get keyIndex {
|
||||||
|
@ -117,7 +125,7 @@ class Node extends HiveObject with Keyable {
|
||||||
try {
|
try {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case WalletType.monero:
|
case WalletType.monero:
|
||||||
return requestMoneroNode();
|
return useSocksProxy ? requestNodeWithProxy(socksProxyAddress ?? '') : requestMoneroNode();
|
||||||
case WalletType.bitcoin:
|
case WalletType.bitcoin:
|
||||||
return requestElectrumServer();
|
return requestElectrumServer();
|
||||||
case WalletType.litecoin:
|
case WalletType.litecoin:
|
||||||
|
@ -166,6 +174,22 @@ class Node extends HiveObject with Keyable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> requestNodeWithProxy(String proxy) async {
|
||||||
|
|
||||||
|
if (proxy.isEmpty || !proxy.contains(':')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final proxyAddress = proxy.split(':')[0];
|
||||||
|
final proxyPort = int.parse(proxy.split(':')[1]);
|
||||||
|
try {
|
||||||
|
final socket = await Socket.connect(proxyAddress, proxyPort, timeout: Duration(seconds: 5));
|
||||||
|
socket.destroy();
|
||||||
|
return true;
|
||||||
|
} catch (_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> requestElectrumServer() async {
|
Future<bool> requestElectrumServer() async {
|
||||||
try {
|
try {
|
||||||
await SecureSocket.connect(uri.host, uri.port,
|
await SecureSocket.connect(uri.host, uri.port,
|
||||||
|
|
|
@ -39,7 +39,7 @@ typedef get_node_height = Int64 Function();
|
||||||
typedef is_connected = Int8 Function();
|
typedef is_connected = Int8 Function();
|
||||||
|
|
||||||
typedef setup_node = Int8 Function(
|
typedef setup_node = Int8 Function(
|
||||||
Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, Int8, Int8, Pointer<Utf8>);
|
Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, Int8, Int8, Pointer<Utf8>?, Pointer<Utf8>);
|
||||||
|
|
||||||
typedef start_refresh = Void Function();
|
typedef start_refresh = Void Function();
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ typedef GetNodeHeight = int Function();
|
||||||
typedef IsConnected = int Function();
|
typedef IsConnected = int Function();
|
||||||
|
|
||||||
typedef SetupNode = int Function(
|
typedef SetupNode = int Function(
|
||||||
Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, int, int, Pointer<Utf8>);
|
Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, int, int, Pointer<Utf8>?, Pointer<Utf8>);
|
||||||
|
|
||||||
typedef StartRefresh = void Function();
|
typedef StartRefresh = void Function();
|
||||||
|
|
||||||
|
|
|
@ -154,9 +154,11 @@ bool setupNodeSync(
|
||||||
String? login,
|
String? login,
|
||||||
String? password,
|
String? password,
|
||||||
bool useSSL = false,
|
bool useSSL = false,
|
||||||
bool isLightWallet = false}) {
|
bool isLightWallet = false,
|
||||||
|
String? socksProxyAddress}) {
|
||||||
final addressPointer = address.toNativeUtf8();
|
final addressPointer = address.toNativeUtf8();
|
||||||
Pointer<Utf8>? loginPointer;
|
Pointer<Utf8>? loginPointer;
|
||||||
|
Pointer<Utf8>? socksProxyAddressPointer;
|
||||||
Pointer<Utf8>? passwordPointer;
|
Pointer<Utf8>? passwordPointer;
|
||||||
|
|
||||||
if (login != null) {
|
if (login != null) {
|
||||||
|
@ -167,6 +169,10 @@ bool setupNodeSync(
|
||||||
passwordPointer = password.toNativeUtf8();
|
passwordPointer = password.toNativeUtf8();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (socksProxyAddress != null) {
|
||||||
|
socksProxyAddressPointer = socksProxyAddress.toNativeUtf8();
|
||||||
|
}
|
||||||
|
|
||||||
final errorMessagePointer = ''.toNativeUtf8();
|
final errorMessagePointer = ''.toNativeUtf8();
|
||||||
final isSetupNode = setupNodeNative(
|
final isSetupNode = setupNodeNative(
|
||||||
addressPointer,
|
addressPointer,
|
||||||
|
@ -174,6 +180,7 @@ bool setupNodeSync(
|
||||||
passwordPointer,
|
passwordPointer,
|
||||||
_boolToInt(useSSL),
|
_boolToInt(useSSL),
|
||||||
_boolToInt(isLightWallet),
|
_boolToInt(isLightWallet),
|
||||||
|
socksProxyAddressPointer,
|
||||||
errorMessagePointer) !=
|
errorMessagePointer) !=
|
||||||
0;
|
0;
|
||||||
|
|
||||||
|
@ -323,13 +330,15 @@ bool _setupNodeSync(Map args) {
|
||||||
final password = (args['password'] ?? '') as String;
|
final password = (args['password'] ?? '') as String;
|
||||||
final useSSL = args['useSSL'] as bool;
|
final useSSL = args['useSSL'] as bool;
|
||||||
final isLightWallet = args['isLightWallet'] as bool;
|
final isLightWallet = args['isLightWallet'] as bool;
|
||||||
|
final socksProxyAddress = (args['socksProxyAddress'] ?? '') as String;
|
||||||
|
|
||||||
return setupNodeSync(
|
return setupNodeSync(
|
||||||
address: address,
|
address: address,
|
||||||
login: login,
|
login: login,
|
||||||
password: password,
|
password: password,
|
||||||
useSSL: useSSL,
|
useSSL: useSSL,
|
||||||
isLightWallet: isLightWallet);
|
isLightWallet: isLightWallet,
|
||||||
|
socksProxyAddress: socksProxyAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isConnected(Object _) => isConnectedSync();
|
bool _isConnected(Object _) => isConnectedSync();
|
||||||
|
@ -343,13 +352,15 @@ Future<void> setupNode(
|
||||||
String? login,
|
String? login,
|
||||||
String? password,
|
String? password,
|
||||||
bool useSSL = false,
|
bool useSSL = false,
|
||||||
|
String? socksProxyAddress,
|
||||||
bool isLightWallet = false}) =>
|
bool isLightWallet = false}) =>
|
||||||
compute<Map<String, Object?>, void>(_setupNodeSync, {
|
compute<Map<String, Object?>, void>(_setupNodeSync, {
|
||||||
'address': address,
|
'address': address,
|
||||||
'login': login,
|
'login': login,
|
||||||
'password': password,
|
'password': password,
|
||||||
'useSSL': useSSL,
|
'useSSL': useSSL,
|
||||||
'isLightWallet': isLightWallet
|
'isLightWallet': isLightWallet,
|
||||||
|
'socksProxyAddress': socksProxyAddress
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> store() => compute<int, void>(_storeSync, 0);
|
Future<void> store() => compute<int, void>(_storeSync, 0);
|
||||||
|
|
|
@ -125,7 +125,8 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
||||||
login: node.login,
|
login: node.login,
|
||||||
password: node.password,
|
password: node.password,
|
||||||
useSSL: node.useSSL ?? false,
|
useSSL: node.useSSL ?? false,
|
||||||
isLightWallet: false); // FIXME: hardcoded value
|
isLightWallet: false, // FIXME: hardcoded value
|
||||||
|
socksProxyAddress: node.socksProxyAddress);
|
||||||
|
|
||||||
haven_wallet.setTrustedDaemon(node.trusted);
|
haven_wallet.setTrustedDaemon(node.trusted);
|
||||||
syncStatus = ConnectedSyncStatus();
|
syncStatus = ConnectedSyncStatus();
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include "thread"
|
#include "thread"
|
||||||
|
@ -405,13 +406,14 @@ extern "C"
|
||||||
return is_connected;
|
return is_connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error)
|
bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *socksProxyAddress, char *error)
|
||||||
{
|
{
|
||||||
nice(19);
|
nice(19);
|
||||||
Monero::Wallet *wallet = get_current_wallet();
|
Monero::Wallet *wallet = get_current_wallet();
|
||||||
|
|
||||||
std::string _login = "";
|
std::string _login = "";
|
||||||
std::string _password = "";
|
std::string _password = "";
|
||||||
|
std::string _socksProxyAddress = "";
|
||||||
|
|
||||||
if (login != nullptr)
|
if (login != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -423,7 +425,12 @@ extern "C"
|
||||||
_password = std::string(password);
|
_password = std::string(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet);
|
if (socksProxyAddress != nullptr)
|
||||||
|
{
|
||||||
|
_socksProxyAddress = std::string(socksProxyAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet, _socksProxyAddress);
|
||||||
|
|
||||||
if (!inited)
|
if (!inited)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,7 +35,7 @@ typedef get_node_height = Int64 Function();
|
||||||
typedef is_connected = Int8 Function();
|
typedef is_connected = Int8 Function();
|
||||||
|
|
||||||
typedef setup_node = Int8 Function(
|
typedef setup_node = Int8 Function(
|
||||||
Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, Int8, Int8, Pointer<Utf8>);
|
Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, Int8, Int8, Pointer<Utf8>?, Pointer<Utf8>);
|
||||||
|
|
||||||
typedef start_refresh = Void Function();
|
typedef start_refresh = Void Function();
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ typedef GetNodeHeight = int Function();
|
||||||
typedef IsConnected = int Function();
|
typedef IsConnected = int Function();
|
||||||
|
|
||||||
typedef SetupNode = int Function(
|
typedef SetupNode = int Function(
|
||||||
Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, int, int, Pointer<Utf8>);
|
Pointer<Utf8>, Pointer<Utf8>?, Pointer<Utf8>?, int, int, Pointer<Utf8>?, Pointer<Utf8>);
|
||||||
|
|
||||||
typedef StartRefresh = void Function();
|
typedef StartRefresh = void Function();
|
||||||
|
|
||||||
|
|
|
@ -158,9 +158,11 @@ bool setupNodeSync(
|
||||||
String? login,
|
String? login,
|
||||||
String? password,
|
String? password,
|
||||||
bool useSSL = false,
|
bool useSSL = false,
|
||||||
bool isLightWallet = false}) {
|
bool isLightWallet = false,
|
||||||
|
String? socksProxyAddress}) {
|
||||||
final addressPointer = address.toNativeUtf8();
|
final addressPointer = address.toNativeUtf8();
|
||||||
Pointer<Utf8>? loginPointer;
|
Pointer<Utf8>? loginPointer;
|
||||||
|
Pointer<Utf8>? socksProxyAddressPointer;
|
||||||
Pointer<Utf8>? passwordPointer;
|
Pointer<Utf8>? passwordPointer;
|
||||||
|
|
||||||
if (login != null) {
|
if (login != null) {
|
||||||
|
@ -171,6 +173,10 @@ bool setupNodeSync(
|
||||||
passwordPointer = password.toNativeUtf8();
|
passwordPointer = password.toNativeUtf8();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (socksProxyAddress != null) {
|
||||||
|
socksProxyAddressPointer = socksProxyAddress.toNativeUtf8();
|
||||||
|
}
|
||||||
|
|
||||||
final errorMessagePointer = ''.toNativeUtf8();
|
final errorMessagePointer = ''.toNativeUtf8();
|
||||||
final isSetupNode = setupNodeNative(
|
final isSetupNode = setupNodeNative(
|
||||||
addressPointer,
|
addressPointer,
|
||||||
|
@ -178,6 +184,7 @@ bool setupNodeSync(
|
||||||
passwordPointer,
|
passwordPointer,
|
||||||
_boolToInt(useSSL),
|
_boolToInt(useSSL),
|
||||||
_boolToInt(isLightWallet),
|
_boolToInt(isLightWallet),
|
||||||
|
socksProxyAddressPointer,
|
||||||
errorMessagePointer) !=
|
errorMessagePointer) !=
|
||||||
0;
|
0;
|
||||||
|
|
||||||
|
@ -328,13 +335,15 @@ bool _setupNodeSync(Map<String, Object?> args) {
|
||||||
final password = (args['password'] ?? '') as String;
|
final password = (args['password'] ?? '') as String;
|
||||||
final useSSL = args['useSSL'] as bool;
|
final useSSL = args['useSSL'] as bool;
|
||||||
final isLightWallet = args['isLightWallet'] as bool;
|
final isLightWallet = args['isLightWallet'] as bool;
|
||||||
|
final socksProxyAddress = (args['socksProxyAddress'] ?? '') as String;
|
||||||
|
|
||||||
return setupNodeSync(
|
return setupNodeSync(
|
||||||
address: address,
|
address: address,
|
||||||
login: login,
|
login: login,
|
||||||
password: password,
|
password: password,
|
||||||
useSSL: useSSL,
|
useSSL: useSSL,
|
||||||
isLightWallet: isLightWallet);
|
isLightWallet: isLightWallet,
|
||||||
|
socksProxyAddress: socksProxyAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isConnected(Object _) => isConnectedSync();
|
bool _isConnected(Object _) => isConnectedSync();
|
||||||
|
@ -348,13 +357,15 @@ Future<void> setupNode(
|
||||||
String? login,
|
String? login,
|
||||||
String? password,
|
String? password,
|
||||||
bool useSSL = false,
|
bool useSSL = false,
|
||||||
|
String? socksProxyAddress,
|
||||||
bool isLightWallet = false}) =>
|
bool isLightWallet = false}) =>
|
||||||
compute<Map<String, Object?>, void>(_setupNodeSync, {
|
compute<Map<String, Object?>, void>(_setupNodeSync, {
|
||||||
'address': address,
|
'address': address,
|
||||||
'login': login ,
|
'login': login ,
|
||||||
'password': password,
|
'password': password,
|
||||||
'useSSL': useSSL,
|
'useSSL': useSSL,
|
||||||
'isLightWallet': isLightWallet
|
'isLightWallet': isLightWallet,
|
||||||
|
'socksProxyAddress': socksProxyAddress
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> store() => compute<int, void>(_storeSync, 0);
|
Future<void> store() => compute<int, void>(_storeSync, 0);
|
||||||
|
|
|
@ -138,7 +138,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
||||||
login: node.login,
|
login: node.login,
|
||||||
password: node.password,
|
password: node.password,
|
||||||
useSSL: node.isSSL,
|
useSSL: node.isSSL,
|
||||||
isLightWallet: false); // FIXME: hardcoded value
|
isLightWallet: false, // FIXME: hardcoded value
|
||||||
|
socksProxyAddress: node.socksProxyAddress);
|
||||||
|
|
||||||
monero_wallet.setTrustedDaemon(node.trusted);
|
monero_wallet.setTrustedDaemon(node.trusted);
|
||||||
syncStatus = ConnectedSyncStatus();
|
syncStatus = ConnectedSyncStatus();
|
||||||
|
|
|
@ -20,6 +20,8 @@ class OnRamperBuyProvider {
|
||||||
switch (_wallet.currency) {
|
switch (_wallet.currency) {
|
||||||
case CryptoCurrency.ltc:
|
case CryptoCurrency.ltc:
|
||||||
return "LTC_LITECOIN";
|
return "LTC_LITECOIN";
|
||||||
|
case CryptoCurrency.xmr:
|
||||||
|
return "XMR_MONERO";
|
||||||
default:
|
default:
|
||||||
return _wallet.currency.title;
|
return _wallet.currency.title;
|
||||||
}
|
}
|
||||||
|
@ -60,11 +62,12 @@ class OnRamperBuyProvider {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final networkName = _wallet.currency.fullName?.toUpperCase().replaceAll(" ", "");
|
||||||
|
|
||||||
return Uri.https(_baseUrl, '', <String, dynamic>{
|
return Uri.https(_baseUrl, '', <String, dynamic>{
|
||||||
'apiKey': _apiKey,
|
'apiKey': _apiKey,
|
||||||
'defaultCrypto': _normalizeCryptoCurrency,
|
'defaultCrypto': _normalizeCryptoCurrency,
|
||||||
'wallets': '${_wallet.currency.title}:${_wallet.walletAddresses.address}',
|
'networkWallets': '${networkName}:${_wallet.walletAddresses.address}',
|
||||||
'supportSell': "false",
|
'supportSell': "false",
|
||||||
'supportSwap': "false",
|
'supportSwap': "false",
|
||||||
'primaryColor': primaryColor,
|
'primaryColor': primaryColor,
|
||||||
|
|
10
lib/core/socks_proxy_node_address_validator.dart
Normal file
10
lib/core/socks_proxy_node_address_validator.dart
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import 'package:cake_wallet/core/validator.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
|
||||||
|
class SocksProxyNodeAddressValidator extends TextValidator {
|
||||||
|
SocksProxyNodeAddressValidator()
|
||||||
|
: super(
|
||||||
|
errorMessage: S.current.error_text_node_proxy_address,
|
||||||
|
pattern:
|
||||||
|
'^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]+\$');
|
||||||
|
}
|
|
@ -37,7 +37,7 @@ class OpenaliasRecord {
|
||||||
required String ticker,
|
required String ticker,
|
||||||
required List<RRecord> txtRecord,
|
required List<RRecord> txtRecord,
|
||||||
}) {
|
}) {
|
||||||
String address = formattedName;
|
String address = '';
|
||||||
String name = formattedName;
|
String name = formattedName;
|
||||||
String note = '';
|
String note = '';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import 'package:cake_wallet/entities/openalias_record.dart';
|
import 'package:cake_wallet/entities/openalias_record.dart';
|
||||||
import 'package:cake_wallet/entities/yat_record.dart';
|
import 'package:cake_wallet/entities/yat_record.dart';
|
||||||
|
|
||||||
enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter }
|
enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter, contact }
|
||||||
|
|
||||||
class ParsedAddress {
|
class ParsedAddress {
|
||||||
ParsedAddress({
|
ParsedAddress({
|
||||||
|
@ -40,13 +40,17 @@ class ParsedAddress {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory ParsedAddress.fetchOpenAliasAddress({required OpenaliasRecord record, required String name}){
|
factory ParsedAddress.fetchOpenAliasAddress(
|
||||||
return ParsedAddress(
|
{required OpenaliasRecord record, required String name}) {
|
||||||
addresses: [record.address],
|
if (record.address.isEmpty) {
|
||||||
name: record.name,
|
return ParsedAddress(addresses: [name]);
|
||||||
description: record.description,
|
}
|
||||||
parseFrom: ParseFrom.openAlias,
|
return ParsedAddress(
|
||||||
);
|
addresses: [record.address],
|
||||||
|
name: record.name,
|
||||||
|
description: record.description,
|
||||||
|
parseFrom: ParseFrom.openAlias,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
factory ParsedAddress.fetchFioAddress({required String address, required String name}){
|
factory ParsedAddress.fetchFioAddress({required String address, required String name}){
|
||||||
|
@ -65,6 +69,14 @@ class ParsedAddress {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory ParsedAddress.fetchContactAddress({required String address, required String name}){
|
||||||
|
return ParsedAddress(
|
||||||
|
addresses: [address],
|
||||||
|
name: name,
|
||||||
|
parseFrom: ParseFrom.contact,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
final List<String> addresses;
|
final List<String> addresses;
|
||||||
final String name;
|
final String name;
|
||||||
final String description;
|
final String description;
|
||||||
|
|
|
@ -4,14 +4,15 @@ part 'template.g.dart';
|
||||||
|
|
||||||
@HiveType(typeId: Template.typeId)
|
@HiveType(typeId: Template.typeId)
|
||||||
class Template extends HiveObject {
|
class Template extends HiveObject {
|
||||||
Template({
|
Template(
|
||||||
required this.nameRaw,
|
{required this.nameRaw,
|
||||||
required this.isCurrencySelectedRaw,
|
required this.isCurrencySelectedRaw,
|
||||||
required this.addressRaw,
|
required this.addressRaw,
|
||||||
required this.cryptoCurrencyRaw,
|
required this.cryptoCurrencyRaw,
|
||||||
required this.amountRaw,
|
required this.amountRaw,
|
||||||
required this.fiatCurrencyRaw,
|
required this.fiatCurrencyRaw,
|
||||||
required this.amountFiatRaw});
|
required this.amountFiatRaw,
|
||||||
|
this.additionalRecipientsRaw});
|
||||||
|
|
||||||
static const typeId = 6;
|
static const typeId = 6;
|
||||||
static const boxName = 'Template';
|
static const boxName = 'Template';
|
||||||
|
@ -37,6 +38,9 @@ class Template extends HiveObject {
|
||||||
@HiveField(6)
|
@HiveField(6)
|
||||||
String? amountFiatRaw;
|
String? amountFiatRaw;
|
||||||
|
|
||||||
|
@HiveField(7)
|
||||||
|
List<Template>? additionalRecipientsRaw;
|
||||||
|
|
||||||
bool get isCurrencySelected => isCurrencySelectedRaw ?? false;
|
bool get isCurrencySelected => isCurrencySelectedRaw ?? false;
|
||||||
|
|
||||||
String get fiatCurrency => fiatCurrencyRaw ?? '';
|
String get fiatCurrency => fiatCurrencyRaw ?? '';
|
||||||
|
@ -50,5 +54,6 @@ class Template extends HiveObject {
|
||||||
String get cryptoCurrency => cryptoCurrencyRaw ?? '';
|
String get cryptoCurrency => cryptoCurrencyRaw ?? '';
|
||||||
|
|
||||||
String get amount => amountRaw ?? '';
|
String get amount => amountRaw ?? '';
|
||||||
}
|
|
||||||
|
|
||||||
|
List<Template>? get additionalRecipients => additionalRecipientsRaw ?? null;
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:cake_wallet/store/app_store.dart';
|
||||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ Future<void> startFiatRateUpdate(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_timer = Timer.periodic(Duration(seconds: 30), (_) async {
|
final _updateFiat = (_) async {
|
||||||
try {
|
try {
|
||||||
if (appStore.wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) {
|
if (appStore.wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) {
|
||||||
return;
|
return;
|
||||||
|
@ -45,5 +46,20 @@ Future<void> startFiatRateUpdate(
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print(e);
|
print(e);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_timer = Timer.periodic(Duration(seconds: 30), _updateFiat);
|
||||||
|
// also run immediately:
|
||||||
|
_updateFiat(null);
|
||||||
|
|
||||||
|
// setup autorun to listen to changes in fiatApiMode
|
||||||
|
autorun((_) {
|
||||||
|
// restart the timer if fiatApiMode was re-enabled
|
||||||
|
if (settingsStore.fiatApiMode != FiatApiMode.disabled) {
|
||||||
|
_timer = Timer.periodic(Duration(seconds: 30), _updateFiat);
|
||||||
|
_updateFiat(null);
|
||||||
|
} else {
|
||||||
|
_timer?.cancel();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,22 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'package:cake_wallet/core/execution_state.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/palette.dart';
|
import 'package:cake_wallet/palette.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/utils/clipboard_util.dart';
|
||||||
import 'package:cake_wallet/utils/exception_handler.dart';
|
import 'package:cake_wallet/utils/exception_handler.dart';
|
||||||
import 'package:cake_wallet/utils/share_util.dart';
|
import 'package:cake_wallet/utils/share_util.dart';
|
||||||
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
|
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||||
|
import 'package:cake_wallet/view_model/backup_view_model.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/utils/show_bar.dart';
|
|
||||||
import 'package:cake_wallet/routes.dart';
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
|
||||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
|
||||||
import 'package:cake_wallet/view_model/backup_view_model.dart';
|
|
||||||
import 'package:cake_wallet/core/execution_state.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
class BackupPage extends BasePage {
|
class BackupPage extends BasePage {
|
||||||
|
@ -52,7 +53,7 @@ class BackupPage extends BasePage {
|
||||||
child: Observer(
|
child: Observer(
|
||||||
builder: (_) => GestureDetector(
|
builder: (_) => GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(
|
ClipboardUtil.setSensitiveDataToClipboard(
|
||||||
ClipboardData(text: backupViewModelBase.backupPassword));
|
ClipboardData(text: backupViewModelBase.backupPassword));
|
||||||
showBar<void>(
|
showBar<void>(
|
||||||
context,
|
context,
|
||||||
|
@ -75,15 +76,14 @@ class BackupPage extends BasePage {
|
||||||
]))),
|
]))),
|
||||||
Positioned(
|
Positioned(
|
||||||
child: Observer(
|
child: Observer(
|
||||||
builder: (_) => LoadingPrimaryButton(
|
builder: (_) => LoadingPrimaryButton(
|
||||||
isLoading: backupViewModelBase.state is IsExecutingState,
|
isLoading: backupViewModelBase.state is IsExecutingState,
|
||||||
onPressed: () => onExportBackup(context),
|
onPressed: () => onExportBackup(context),
|
||||||
text: S.of(context).export_backup,
|
text: S.of(context).export_backup,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context).accentTextTheme.bodyLarge!.color!,
|
||||||
.accentTextTheme!
|
textColor: Colors.white,
|
||||||
.bodyLarge!
|
),
|
||||||
.color!,
|
),
|
||||||
textColor: Colors.white)),
|
|
||||||
bottom: 24,
|
bottom: 24,
|
||||||
left: 24,
|
left: 24,
|
||||||
right: 24,
|
right: 24,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:cake_wallet/core/node_address_validator.dart';
|
import 'package:cake_wallet/core/node_address_validator.dart';
|
||||||
import 'package:cake_wallet/core/node_port_validator.dart';
|
import 'package:cake_wallet/core/node_port_validator.dart';
|
||||||
|
import 'package:cake_wallet/core/socks_proxy_node_address_validator.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/src/widgets/standard_checkbox.dart';
|
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||||
|
@ -17,7 +18,8 @@ class NodeForm extends StatelessWidget {
|
||||||
}) : _addressController = TextEditingController(text: editingNode?.uri.host.toString()),
|
}) : _addressController = TextEditingController(text: editingNode?.uri.host.toString()),
|
||||||
_portController = TextEditingController(text: editingNode?.uri.port.toString()),
|
_portController = TextEditingController(text: editingNode?.uri.port.toString()),
|
||||||
_loginController = TextEditingController(text: editingNode?.login),
|
_loginController = TextEditingController(text: editingNode?.login),
|
||||||
_passwordController = TextEditingController(text: editingNode?.password) {
|
_passwordController = TextEditingController(text: editingNode?.password),
|
||||||
|
_socksAddressController = TextEditingController(text: editingNode?.socksProxyAddress){
|
||||||
if (editingNode != null) {
|
if (editingNode != null) {
|
||||||
nodeViewModel
|
nodeViewModel
|
||||||
..setAddress((editingNode!.uri.host.toString()))
|
..setAddress((editingNode!.uri.host.toString()))
|
||||||
|
@ -25,7 +27,9 @@ class NodeForm extends StatelessWidget {
|
||||||
..setPassword((editingNode!.password ?? ''))
|
..setPassword((editingNode!.password ?? ''))
|
||||||
..setLogin((editingNode!.login ?? ''))
|
..setLogin((editingNode!.login ?? ''))
|
||||||
..setSSL((editingNode!.isSSL))
|
..setSSL((editingNode!.isSSL))
|
||||||
..setTrusted((editingNode!.trusted));
|
..setTrusted((editingNode!.trusted))
|
||||||
|
..setSocksProxy((editingNode!.useSocksProxy))
|
||||||
|
..setSocksProxyAddress((editingNode!.socksProxyAddress ?? ''));
|
||||||
}
|
}
|
||||||
if (nodeViewModel.hasAuthCredentials) {
|
if (nodeViewModel.hasAuthCredentials) {
|
||||||
reaction((_) => nodeViewModel.login, (String login) {
|
reaction((_) => nodeViewModel.login, (String login) {
|
||||||
|
@ -45,6 +49,7 @@ class NodeForm extends StatelessWidget {
|
||||||
_portController.addListener(() => nodeViewModel.port = _portController.text);
|
_portController.addListener(() => nodeViewModel.port = _portController.text);
|
||||||
_loginController.addListener(() => nodeViewModel.login = _loginController.text);
|
_loginController.addListener(() => nodeViewModel.login = _loginController.text);
|
||||||
_passwordController.addListener(() => nodeViewModel.password = _passwordController.text);
|
_passwordController.addListener(() => nodeViewModel.password = _passwordController.text);
|
||||||
|
_socksAddressController.addListener(() => nodeViewModel.socksProxyAddress = _socksAddressController.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
final NodeCreateOrEditViewModel nodeViewModel;
|
final NodeCreateOrEditViewModel nodeViewModel;
|
||||||
|
@ -55,6 +60,7 @@ class NodeForm extends StatelessWidget {
|
||||||
final TextEditingController _portController;
|
final TextEditingController _portController;
|
||||||
final TextEditingController _loginController;
|
final TextEditingController _loginController;
|
||||||
final TextEditingController _passwordController;
|
final TextEditingController _passwordController;
|
||||||
|
final TextEditingController _socksAddressController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -138,6 +144,43 @@ class NodeForm extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Observer(
|
||||||
|
builder: (_) => Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
StandardCheckbox(
|
||||||
|
value: nodeViewModel.useSocksProxy,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (!value) {
|
||||||
|
_socksAddressController.text = '';
|
||||||
|
}
|
||||||
|
nodeViewModel.useSocksProxy = value;
|
||||||
|
},
|
||||||
|
caption: 'SOCKS Proxy',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (nodeViewModel.useSocksProxy) ...[
|
||||||
|
SizedBox(height: 10.0),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: BaseTextFormField(
|
||||||
|
controller: _socksAddressController,
|
||||||
|
hintText: '[<ip>:]<port>',
|
||||||
|
validator: SocksProxyNodeAddressValidator(),
|
||||||
|
))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
],
|
||||||
|
)),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cake_wallet/src/widgets/primary_button.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:cake_wallet/themes/theme_base.dart';
|
||||||
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
|
||||||
|
|
||||||
class PreSeedPage extends BasePage {
|
class PreSeedPage extends BasePage {
|
||||||
PreSeedPage(this.type)
|
PreSeedPage(this.type)
|
||||||
|
@ -61,7 +61,7 @@ class PreSeedPage extends BasePage {
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
Navigator.of(context).popAndPushNamed(Routes.seed, arguments: true),
|
Navigator.of(context).popAndPushNamed(Routes.seed, arguments: true),
|
||||||
text: S.of(context).pre_seed_button_text,
|
text: S.of(context).pre_seed_button_text,
|
||||||
color: Theme.of(context).accentTextTheme!.bodyLarge!.color!,
|
color: Theme.of(context).accentTextTheme.bodyLarge!.color!,
|
||||||
textColor: Colors.white)
|
textColor: Colors.white)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:cake_wallet/palette.dart';
|
import 'package:cake_wallet/palette.dart';
|
||||||
import 'package:cake_wallet/themes/theme_base.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/utils/clipboard_util.dart';
|
||||||
import 'package:cake_wallet/utils/share_util.dart';
|
import 'package:cake_wallet/utils/share_util.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
|
@ -92,8 +93,7 @@ class WalletSeedPage extends BasePage {
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints:
|
constraints: BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.3),
|
||||||
BoxConstraints(maxHeight: MediaQuery.of(context).size.height * 0.3),
|
|
||||||
child: AspectRatio(aspectRatio: 1, child: image),
|
child: AspectRatio(aspectRatio: 1, child: image),
|
||||||
),
|
),
|
||||||
Observer(builder: (_) {
|
Observer(builder: (_) {
|
||||||
|
@ -159,7 +159,7 @@ class WalletSeedPage extends BasePage {
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) => PrimaryButton(
|
builder: (context) => PrimaryButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Clipboard.setData(
|
ClipboardUtil.setSensitiveDataToClipboard(
|
||||||
ClipboardData(text: walletSeedViewModel.seed));
|
ClipboardData(text: walletSeedViewModel.seed));
|
||||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
import 'package:cake_wallet/entities/fiat_currency.dart';
|
||||||
|
import 'package:cake_wallet/entities/template.dart';
|
||||||
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
|
import 'package:cake_wallet/src/screens/dashboard/widgets/sync_indicator_icon.dart';
|
||||||
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
|
import 'package:cake_wallet/src/screens/send/widgets/send_card.dart';
|
||||||
import 'package:cake_wallet/src/widgets/add_template_button.dart';
|
import 'package:cake_wallet/src/widgets/add_template_button.dart';
|
||||||
|
@ -241,6 +242,11 @@ class SendPage extends BasePage {
|
||||||
return TemplateTile(
|
return TemplateTile(
|
||||||
key: UniqueKey(),
|
key: UniqueKey(),
|
||||||
to: template.name,
|
to: template.name,
|
||||||
|
hasMultipleRecipients:
|
||||||
|
template.additionalRecipients !=
|
||||||
|
null &&
|
||||||
|
template.additionalRecipients!
|
||||||
|
.length > 1,
|
||||||
amount: template.isCurrencySelected
|
amount: template.isCurrencySelected
|
||||||
? template.amount
|
? template.amount
|
||||||
: template.amountFiat,
|
: template.amountFiat,
|
||||||
|
@ -248,25 +254,36 @@ class SendPage extends BasePage {
|
||||||
? template.cryptoCurrency
|
? template.cryptoCurrency
|
||||||
: template.fiatCurrency,
|
: template.fiatCurrency,
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final fiatFromTemplate = FiatCurrency
|
if (template.additionalRecipients !=
|
||||||
.all
|
null) {
|
||||||
.singleWhere((element) =>
|
sendViewModel.clearOutputs();
|
||||||
element.title ==
|
|
||||||
template.fiatCurrency);
|
template.additionalRecipients!
|
||||||
final output = _defineCurrentOutput();
|
.forEach((currentElement) async {
|
||||||
output.address = template.address;
|
int i = template
|
||||||
if (template.isCurrencySelected) {
|
.additionalRecipients!
|
||||||
output
|
.indexOf(currentElement);
|
||||||
.setCryptoAmount(template.amount);
|
|
||||||
|
Output output;
|
||||||
|
try {
|
||||||
|
output = sendViewModel.outputs[i];
|
||||||
|
} catch (e) {
|
||||||
|
sendViewModel.addOutput();
|
||||||
|
output = sendViewModel.outputs[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
await _setInputsFromTemplate(
|
||||||
|
context,
|
||||||
|
output: output,
|
||||||
|
template: currentElement);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
sendViewModel.setFiatCurrency(
|
final output = _defineCurrentOutput();
|
||||||
fiatFromTemplate);
|
await _setInputsFromTemplate(
|
||||||
output.setFiatAmount(
|
context,
|
||||||
template.amountFiat);
|
output: output,
|
||||||
|
template: template);
|
||||||
}
|
}
|
||||||
output.resetParsedAddress();
|
|
||||||
await output
|
|
||||||
.fetchParsedAddress(context);
|
|
||||||
},
|
},
|
||||||
onRemove: () {
|
onRemove: () {
|
||||||
showPopUp<void>(
|
showPopUp<void>(
|
||||||
|
@ -477,6 +494,24 @@ class SendPage extends BasePage {
|
||||||
_effectsInstalled = true;
|
_effectsInstalled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _setInputsFromTemplate(BuildContext context,
|
||||||
|
{required Output output, required Template template}) async {
|
||||||
|
final fiatFromTemplate = FiatCurrency.all
|
||||||
|
.singleWhere((element) => element.title == template.fiatCurrency);
|
||||||
|
|
||||||
|
output.address = template.address;
|
||||||
|
|
||||||
|
if (template.isCurrencySelected) {
|
||||||
|
output.setCryptoAmount(template.amount);
|
||||||
|
} else {
|
||||||
|
sendViewModel.setFiatCurrency(fiatFromTemplate);
|
||||||
|
output.setFiatAmount(template.amountFiat);
|
||||||
|
}
|
||||||
|
|
||||||
|
output.resetParsedAddress();
|
||||||
|
await output.fetchParsedAddress(context);
|
||||||
|
}
|
||||||
|
|
||||||
Output _defineCurrentOutput() {
|
Output _defineCurrentOutput() {
|
||||||
if (controller.page == null) {
|
if (controller.page == null) {
|
||||||
throw Exception('Controller page is null');
|
throw Exception('Controller page is null');
|
||||||
|
|
|
@ -1,35 +1,21 @@
|
||||||
import 'package:cake_wallet/utils/payment_request.dart';
|
import 'package:cake_wallet/src/widgets/trail_button.dart';
|
||||||
|
import 'package:cake_wallet/view_model/send/template_view_model.dart';
|
||||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
|
||||||
import 'package:flutter/cupertino.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:keyboard_actions/keyboard_actions.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
|
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
|
||||||
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||||
import 'package:cake_wallet/src/screens/send/widgets/prefix_currency_icon_widget.dart';
|
import 'package:cake_wallet/src/screens/send/widgets/send_template_card.dart';
|
||||||
|
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
|
||||||
|
|
||||||
class SendTemplatePage extends BasePage {
|
class SendTemplatePage extends BasePage {
|
||||||
SendTemplatePage({required this.sendTemplateViewModel}) {
|
SendTemplatePage({required this.sendTemplateViewModel});
|
||||||
sendTemplateViewModel.output.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
final SendTemplateViewModel sendTemplateViewModel;
|
final SendTemplateViewModel sendTemplateViewModel;
|
||||||
final _addressController = TextEditingController();
|
|
||||||
final _cryptoAmountController = TextEditingController();
|
|
||||||
final _fiatAmountController = TextEditingController();
|
|
||||||
final _nameController = TextEditingController();
|
|
||||||
final _formKey = GlobalKey<FormState>();
|
final _formKey = GlobalKey<FormState>();
|
||||||
final FocusNode _cryptoAmountFocus = FocusNode();
|
final controller = PageController(initialPage: 0);
|
||||||
final FocusNode _fiatAmountFocus = FocusNode();
|
|
||||||
|
|
||||||
bool _effectsInstalled = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get title => S.current.exchange_new_template;
|
String get title => S.current.exchange_new_template;
|
||||||
|
@ -44,273 +30,146 @@ class SendTemplatePage extends BasePage {
|
||||||
AppBarStyle get appBarStyle => AppBarStyle.transparent;
|
AppBarStyle get appBarStyle => AppBarStyle.transparent;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget trailing(context) => Observer(builder: (_) {
|
||||||
_setEffects(context);
|
return sendTemplateViewModel.recipients.length > 1
|
||||||
|
? TrailButton(
|
||||||
|
caption: S.of(context).remove,
|
||||||
|
onPressed: () {
|
||||||
|
int pageToJump = (controller.page?.round() ?? 0) - 1;
|
||||||
|
pageToJump = pageToJump > 0 ? pageToJump : 0;
|
||||||
|
final recipient = _defineCurrentRecipient();
|
||||||
|
sendTemplateViewModel.removeRecipient(recipient);
|
||||||
|
controller.jumpToPage(pageToJump);
|
||||||
|
})
|
||||||
|
: TrailButton(
|
||||||
|
caption: S.of(context).clear,
|
||||||
|
onPressed: () {
|
||||||
|
final recipient = _defineCurrentRecipient();
|
||||||
|
_formKey.currentState?.reset();
|
||||||
|
recipient.reset();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return KeyboardActions(
|
@override
|
||||||
config: KeyboardActionsConfig(
|
Widget body(BuildContext context) {
|
||||||
keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
|
return Form(
|
||||||
keyboardBarColor: Theme.of(context)
|
key: _formKey,
|
||||||
.accentTextTheme!
|
child: ScrollableWithBottomSection(
|
||||||
.bodyLarge!
|
|
||||||
.backgroundColor!,
|
|
||||||
nextFocus: false,
|
|
||||||
actions: [
|
|
||||||
KeyboardActionsItem(
|
|
||||||
focusNode: _cryptoAmountFocus,
|
|
||||||
toolbarButtons: [(_) => KeyboardDoneButton()],
|
|
||||||
),
|
|
||||||
KeyboardActionsItem(
|
|
||||||
focusNode: _fiatAmountFocus,
|
|
||||||
toolbarButtons: [(_) => KeyboardDoneButton()],
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
child: Container(
|
|
||||||
height: 0,
|
|
||||||
color: Theme.of(context).colorScheme.background,
|
|
||||||
child: ScrollableWithBottomSection(
|
|
||||||
contentPadding: EdgeInsets.only(bottom: 24),
|
contentPadding: EdgeInsets.only(bottom: 24),
|
||||||
content: Container(
|
content: FocusTraversalGroup(
|
||||||
decoration: BoxDecoration(
|
policy: OrderedTraversalPolicy(),
|
||||||
borderRadius: BorderRadius.only(
|
child: Column(children: [
|
||||||
bottomLeft: Radius.circular(24),
|
Container(
|
||||||
bottomRight: Radius.circular(24),
|
height: 460,
|
||||||
),
|
child: Observer(builder: (_) {
|
||||||
gradient: LinearGradient(colors: [
|
return PageView.builder(
|
||||||
Theme.of(context).primaryTextTheme!.titleMedium!.color!,
|
scrollDirection: Axis.horizontal,
|
||||||
Theme.of(context)
|
controller: controller,
|
||||||
.primaryTextTheme!
|
itemCount: sendTemplateViewModel.recipients.length,
|
||||||
.titleMedium!
|
itemBuilder: (_, index) {
|
||||||
.decorationColor!,
|
final template =
|
||||||
], begin: Alignment.topLeft, end: Alignment.bottomRight),
|
sendTemplateViewModel.recipients[index];
|
||||||
),
|
return SendTemplateCard(
|
||||||
child: Form(
|
template: template,
|
||||||
key: _formKey,
|
index: index,
|
||||||
child: Column(
|
sendTemplateViewModel: sendTemplateViewModel);
|
||||||
children: <Widget>[
|
});
|
||||||
Padding(
|
})),
|
||||||
padding: EdgeInsets.fromLTRB(24, 90, 24, 32),
|
Padding(
|
||||||
child: Column(
|
padding: EdgeInsets.only(
|
||||||
children: <Widget>[
|
top: 10, left: 24, right: 24, bottom: 10),
|
||||||
BaseTextFormField(
|
child: Container(
|
||||||
controller: _nameController,
|
height: 10,
|
||||||
hintText: S.of(context).send_name,
|
child: Observer(
|
||||||
borderColor: Theme.of(context)
|
builder: (_) {
|
||||||
.primaryTextTheme!
|
final count = sendTemplateViewModel.recipients.length;
|
||||||
.headlineSmall!
|
|
||||||
.color!,
|
return count > 1
|
||||||
textStyle: TextStyle(
|
? SmoothPageIndicator(
|
||||||
fontSize: 14,
|
controller: controller,
|
||||||
fontWeight: FontWeight.w500,
|
count: count,
|
||||||
color: Colors.white),
|
effect: ScrollingDotsEffect(
|
||||||
placeholderTextStyle: TextStyle(
|
spacing: 6.0,
|
||||||
color: Theme.of(context)
|
radius: 6.0,
|
||||||
.primaryTextTheme!
|
dotWidth: 6.0,
|
||||||
.headlineSmall!
|
dotHeight: 6.0,
|
||||||
.decorationColor!,
|
dotColor: Theme.of(context)
|
||||||
fontWeight: FontWeight.w500,
|
.primaryTextTheme
|
||||||
fontSize: 14),
|
.displaySmall!
|
||||||
validator: sendTemplateViewModel.templateValidator,
|
.backgroundColor!,
|
||||||
),
|
activeDotColor: Theme.of(context)
|
||||||
Padding(
|
.primaryTextTheme
|
||||||
padding: EdgeInsets.only(top: 20),
|
.displayMedium!
|
||||||
child: AddressTextField(
|
.backgroundColor!))
|
||||||
controller: _addressController,
|
: Offstage();
|
||||||
onURIScanned: (uri) {
|
},
|
||||||
final paymentRequest = PaymentRequest.fromUri(uri);
|
|
||||||
_addressController.text = paymentRequest.address;
|
|
||||||
_cryptoAmountController.text = paymentRequest.amount;
|
|
||||||
},
|
|
||||||
options: [
|
|
||||||
AddressTextFieldOption.paste,
|
|
||||||
AddressTextFieldOption.qrCode,
|
|
||||||
AddressTextFieldOption.addressBook
|
|
||||||
],
|
|
||||||
buttonColor: Theme.of(context)
|
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineMedium!
|
|
||||||
.color!,
|
|
||||||
borderColor: Theme.of(context)
|
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.color!,
|
|
||||||
textStyle: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.white),
|
|
||||||
hintStyle: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.decorationColor!),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 20),
|
|
||||||
child: Focus(
|
|
||||||
onFocusChange: (hasFocus) {
|
|
||||||
if (hasFocus) {
|
|
||||||
sendTemplateViewModel.selectCurrency();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: BaseTextFormField(
|
|
||||||
focusNode: _cryptoAmountFocus,
|
|
||||||
controller: _cryptoAmountController,
|
|
||||||
keyboardType: TextInputType.numberWithOptions(
|
|
||||||
signed: false, decimal: true),
|
|
||||||
inputFormatters: [
|
|
||||||
FilteringTextInputFormatter.deny(
|
|
||||||
RegExp('[\\-|\\ ]'))
|
|
||||||
],
|
|
||||||
prefixIcon: Observer(
|
|
||||||
builder: (_) => PrefixCurrencyIcon(
|
|
||||||
title: sendTemplateViewModel
|
|
||||||
.currency.title,
|
|
||||||
isSelected:
|
|
||||||
sendTemplateViewModel
|
|
||||||
.isCurrencySelected,
|
|
||||||
)),
|
|
||||||
hintText: '0.0000',
|
|
||||||
borderColor: Theme.of(context)
|
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.color!,
|
|
||||||
textStyle: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.white),
|
|
||||||
placeholderTextStyle: TextStyle(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.decorationColor!,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 14),
|
|
||||||
validator:
|
|
||||||
sendTemplateViewModel.amountValidator))),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 20),
|
|
||||||
child: Focus(
|
|
||||||
onFocusChange: (hasFocus) {
|
|
||||||
if (hasFocus) {
|
|
||||||
sendTemplateViewModel.selectFiat();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: BaseTextFormField(
|
|
||||||
focusNode: _fiatAmountFocus,
|
|
||||||
controller: _fiatAmountController,
|
|
||||||
keyboardType: TextInputType.numberWithOptions(
|
|
||||||
signed: false, decimal: true),
|
|
||||||
inputFormatters: [
|
|
||||||
FilteringTextInputFormatter.deny(
|
|
||||||
RegExp('[\\-|\\ ]'))
|
|
||||||
],
|
|
||||||
prefixIcon: Observer(
|
|
||||||
builder: (_) => PrefixCurrencyIcon(
|
|
||||||
title: sendTemplateViewModel
|
|
||||||
.fiat.title,
|
|
||||||
isSelected: sendTemplateViewModel
|
|
||||||
.isFiatSelected,
|
|
||||||
)),
|
|
||||||
hintText: '0.00',
|
|
||||||
borderColor: Theme.of(context)
|
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.color!,
|
|
||||||
textStyle: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Colors.white),
|
|
||||||
placeholderTextStyle: TextStyle(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.primaryTextTheme!
|
|
||||||
.headlineSmall!
|
|
||||||
.decorationColor!,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
fontSize: 14),
|
|
||||||
))),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
),
|
||||||
),
|
])),
|
||||||
),
|
|
||||||
),
|
|
||||||
bottomSectionPadding:
|
bottomSectionPadding:
|
||||||
EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||||
bottomSection: PrimaryButton(
|
bottomSection: Column(children: [
|
||||||
onPressed: () {
|
// if (sendViewModel.hasMultiRecipient)
|
||||||
if (_formKey.currentState != null && _formKey.currentState!.validate()) {
|
Padding(
|
||||||
sendTemplateViewModel.addTemplate(
|
padding: EdgeInsets.only(bottom: 12),
|
||||||
isCurrencySelected: sendTemplateViewModel.isCurrencySelected,
|
child: PrimaryButton(
|
||||||
name: _nameController.text,
|
onPressed: () {
|
||||||
address: _addressController.text,
|
sendTemplateViewModel.addRecipient();
|
||||||
cryptoCurrency:sendTemplateViewModel.currency.title,
|
Future.delayed(const Duration(milliseconds: 250), () {
|
||||||
fiatCurrency: sendTemplateViewModel.fiat.title,
|
controller.jumpToPage(
|
||||||
amount: _cryptoAmountController.text,
|
sendTemplateViewModel.recipients.length - 1);
|
||||||
amountFiat: _fiatAmountController.text);
|
});
|
||||||
Navigator.of(context).pop();
|
},
|
||||||
}
|
text: S.of(context).add_receiver,
|
||||||
},
|
color: Colors.transparent,
|
||||||
text: S.of(context).save,
|
textColor: Theme.of(context)
|
||||||
color: Colors.green,
|
.accentTextTheme
|
||||||
textColor: Colors.white,
|
.displaySmall!
|
||||||
),
|
.decorationColor!,
|
||||||
),
|
isDottedBorder: true,
|
||||||
));
|
borderColor: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.displaySmall!
|
||||||
|
.decorationColor!)),
|
||||||
|
PrimaryButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (_formKey.currentState != null &&
|
||||||
|
_formKey.currentState!.validate()) {
|
||||||
|
final mainTemplate = sendTemplateViewModel.recipients[0];
|
||||||
|
print(sendTemplateViewModel.recipients.map((element) =>
|
||||||
|
element.toTemplate(
|
||||||
|
cryptoCurrency:
|
||||||
|
sendTemplateViewModel.cryptoCurrency.title,
|
||||||
|
fiatCurrency:
|
||||||
|
sendTemplateViewModel.fiatCurrency)));
|
||||||
|
sendTemplateViewModel.addTemplate(
|
||||||
|
isCurrencySelected: mainTemplate.isCurrencySelected,
|
||||||
|
name: mainTemplate.name,
|
||||||
|
address: mainTemplate.address,
|
||||||
|
amount: mainTemplate.output.cryptoAmount,
|
||||||
|
amountFiat: mainTemplate.output.fiatAmount,
|
||||||
|
additionalRecipients: sendTemplateViewModel.recipients
|
||||||
|
.map((element) => element.toTemplate(
|
||||||
|
cryptoCurrency: sendTemplateViewModel
|
||||||
|
.cryptoCurrency.title,
|
||||||
|
fiatCurrency:
|
||||||
|
sendTemplateViewModel.fiatCurrency))
|
||||||
|
.toList());
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text: S.of(context).save,
|
||||||
|
color: Colors.green,
|
||||||
|
textColor: Colors.white)
|
||||||
|
])));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setEffects(BuildContext context) {
|
TemplateViewModel _defineCurrentRecipient() {
|
||||||
if (_effectsInstalled) {
|
if (controller.page == null) {
|
||||||
return;
|
throw Exception('Controller page is null');
|
||||||
}
|
}
|
||||||
|
final itemCount = controller.page!.round();
|
||||||
final output = sendTemplateViewModel.output;
|
return sendTemplateViewModel.recipients[itemCount];
|
||||||
|
|
||||||
reaction((_) => output.fiatAmount, (String amount) {
|
|
||||||
if (amount != _fiatAmountController.text) {
|
|
||||||
_fiatAmountController.text = amount;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
reaction((_) => output.cryptoAmount, (String amount) {
|
|
||||||
if (amount != _cryptoAmountController.text) {
|
|
||||||
_cryptoAmountController.text = amount;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
reaction((_) => output.address, (String address) {
|
|
||||||
if (address != _addressController.text) {
|
|
||||||
_addressController.text = address;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_cryptoAmountController.addListener(() {
|
|
||||||
final amount = _cryptoAmountController.text;
|
|
||||||
|
|
||||||
if (amount != output.cryptoAmount) {
|
|
||||||
output.setCryptoAmount(amount);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_fiatAmountController.addListener(() {
|
|
||||||
final amount = _fiatAmountController.text;
|
|
||||||
|
|
||||||
if (amount != output.fiatAmount) {
|
|
||||||
output.setFiatAmount(amount);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_addressController.addListener(() {
|
|
||||||
final address = _addressController.text;
|
|
||||||
|
|
||||||
if (output.address != address) {
|
|
||||||
output.address = address;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
_effectsInstalled = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ Future<String> extractAddressFromParsed(
|
||||||
}
|
}
|
||||||
|
|
||||||
return address;
|
return address;
|
||||||
|
case ParseFrom.contact:
|
||||||
case ParseFrom.notParsed:
|
case ParseFrom.notParsed:
|
||||||
address = parsedAddress.addresses.first;
|
address = parsedAddress.addresses.first;
|
||||||
return address;
|
return address;
|
||||||
|
@ -85,4 +86,4 @@ Future<String> extractAddressFromParsed(
|
||||||
});
|
});
|
||||||
|
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
|
@ -172,8 +172,10 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
hintStyle: TextStyle(
|
hintStyle: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color:
|
color: Theme.of(context)
|
||||||
Theme.of(context).primaryTextTheme.headlineSmall!.decorationColor!),
|
.primaryTextTheme!
|
||||||
|
.headlineSmall!
|
||||||
|
.decorationColor!),
|
||||||
onPushPasteButton: (context) async {
|
onPushPasteButton: (context) async {
|
||||||
output.resetParsedAddress();
|
output.resetParsedAddress();
|
||||||
await output.fetchParsedAddress(context);
|
await output.fetchParsedAddress(context);
|
||||||
|
@ -182,6 +184,9 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
output.resetParsedAddress();
|
output.resetParsedAddress();
|
||||||
await output.fetchParsedAddress(context);
|
await output.fetchParsedAddress(context);
|
||||||
},
|
},
|
||||||
|
onSelectedContact: (contact) {
|
||||||
|
output.loadContact(contact);
|
||||||
|
},
|
||||||
validator: validator,
|
validator: validator,
|
||||||
selectedCurrency: sendViewModel.currency,
|
selectedCurrency: sendViewModel.currency,
|
||||||
);
|
);
|
||||||
|
@ -464,7 +469,6 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
//color: Theme.of(context).primaryTextTheme!.displaySmall!.color!,
|
|
||||||
color: Colors.white),
|
color: Colors.white),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
|
267
lib/src/screens/send/widgets/send_template_card.dart
Normal file
267
lib/src/screens/send/widgets/send_template_card.dart
Normal file
|
@ -0,0 +1,267 @@
|
||||||
|
import 'package:cake_wallet/src/screens/send/widgets/prefix_currency_icon_widget.dart';
|
||||||
|
import 'package:cake_wallet/utils/payment_request.dart';
|
||||||
|
import 'package:cake_wallet/view_model/send/template_view_model.dart';
|
||||||
|
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/address_text_field.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
|
||||||
|
class SendTemplateCard extends StatelessWidget {
|
||||||
|
SendTemplateCard(
|
||||||
|
{super.key,
|
||||||
|
required this.template,
|
||||||
|
required this.index,
|
||||||
|
required this.sendTemplateViewModel});
|
||||||
|
|
||||||
|
final TemplateViewModel template;
|
||||||
|
final int index;
|
||||||
|
final SendTemplateViewModel sendTemplateViewModel;
|
||||||
|
|
||||||
|
final _addressController = TextEditingController();
|
||||||
|
final _cryptoAmountController = TextEditingController();
|
||||||
|
final _fiatAmountController = TextEditingController();
|
||||||
|
final _nameController = TextEditingController();
|
||||||
|
final FocusNode _cryptoAmountFocus = FocusNode();
|
||||||
|
final FocusNode _fiatAmountFocus = FocusNode();
|
||||||
|
|
||||||
|
bool _effectsInstalled = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
_setEffects(context);
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
bottomLeft: Radius.circular(24),
|
||||||
|
bottomRight: Radius.circular(24)),
|
||||||
|
gradient: LinearGradient(colors: [
|
||||||
|
Theme.of(context).primaryTextTheme.titleMedium!.color!,
|
||||||
|
Theme.of(context).primaryTextTheme.titleMedium!.decorationColor!
|
||||||
|
], begin: Alignment.topLeft, end: Alignment.bottomRight)),
|
||||||
|
child: Column(children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(24, 90, 24, 32),
|
||||||
|
child: Column(children: <Widget>[
|
||||||
|
if (index == 0)
|
||||||
|
BaseTextFormField(
|
||||||
|
controller: _nameController,
|
||||||
|
hintText: sendTemplateViewModel.recipients.length > 1
|
||||||
|
? S.of(context).template_name
|
||||||
|
: S.of(context).send_name,
|
||||||
|
borderColor: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headlineSmall!
|
||||||
|
.color!,
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.white),
|
||||||
|
placeholderTextStyle: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headlineSmall!
|
||||||
|
.decorationColor!,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14),
|
||||||
|
validator: sendTemplateViewModel.templateValidator),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(top: 20),
|
||||||
|
child: AddressTextField(
|
||||||
|
selectedCurrency: sendTemplateViewModel.cryptoCurrency,
|
||||||
|
controller: _addressController,
|
||||||
|
onURIScanned: (uri) {
|
||||||
|
final paymentRequest = PaymentRequest.fromUri(uri);
|
||||||
|
_addressController.text = paymentRequest.address;
|
||||||
|
_cryptoAmountController.text = paymentRequest.amount;
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
AddressTextFieldOption.paste,
|
||||||
|
AddressTextFieldOption.qrCode,
|
||||||
|
AddressTextFieldOption.addressBook
|
||||||
|
],
|
||||||
|
onPushPasteButton: (context) async {
|
||||||
|
template.output.resetParsedAddress();
|
||||||
|
await template.output.fetchParsedAddress(context);
|
||||||
|
},
|
||||||
|
onPushAddressBookButton: (context) async {
|
||||||
|
template.output.resetParsedAddress();
|
||||||
|
await template.output.fetchParsedAddress(context);
|
||||||
|
},
|
||||||
|
buttonColor: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headlineMedium!
|
||||||
|
.color!,
|
||||||
|
borderColor: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headlineSmall!
|
||||||
|
.color!,
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.white),
|
||||||
|
hintStyle: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headlineSmall!
|
||||||
|
.decorationColor!),
|
||||||
|
validator: sendTemplateViewModel.addressValidator)),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 20),
|
||||||
|
child: Focus(
|
||||||
|
onFocusChange: (hasFocus) {
|
||||||
|
if (hasFocus) {
|
||||||
|
template.selectCurrency();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: BaseTextFormField(
|
||||||
|
focusNode: _cryptoAmountFocus,
|
||||||
|
controller: _cryptoAmountController,
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
signed: false, decimal: true),
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.deny(
|
||||||
|
RegExp('[\\-|\\ ]'))
|
||||||
|
],
|
||||||
|
prefixIcon: Observer(
|
||||||
|
builder: (_) => PrefixCurrencyIcon(
|
||||||
|
title: sendTemplateViewModel
|
||||||
|
.cryptoCurrency.title,
|
||||||
|
isSelected: template.isCurrencySelected)),
|
||||||
|
hintText: '0.0000',
|
||||||
|
borderColor: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headlineSmall!
|
||||||
|
.color!,
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.white),
|
||||||
|
placeholderTextStyle: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headlineSmall!
|
||||||
|
.decorationColor!,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14),
|
||||||
|
validator: sendTemplateViewModel.amountValidator))),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 20),
|
||||||
|
child: Focus(
|
||||||
|
onFocusChange: (hasFocus) {
|
||||||
|
if (hasFocus) {
|
||||||
|
template.selectFiat();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: BaseTextFormField(
|
||||||
|
focusNode: _fiatAmountFocus,
|
||||||
|
controller: _fiatAmountController,
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
signed: false, decimal: true),
|
||||||
|
inputFormatters: [
|
||||||
|
FilteringTextInputFormatter.deny(
|
||||||
|
RegExp('[\\-|\\ ]'))
|
||||||
|
],
|
||||||
|
prefixIcon: Observer(
|
||||||
|
builder: (_) => PrefixCurrencyIcon(
|
||||||
|
title: sendTemplateViewModel.fiatCurrency,
|
||||||
|
isSelected: template.isFiatSelected)),
|
||||||
|
hintText: '0.00',
|
||||||
|
borderColor: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headlineSmall!
|
||||||
|
.color!,
|
||||||
|
textStyle: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.white),
|
||||||
|
placeholderTextStyle: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.primaryTextTheme
|
||||||
|
.headlineSmall!
|
||||||
|
.decorationColor!,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
fontSize: 14))))
|
||||||
|
]))
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setEffects(BuildContext context) {
|
||||||
|
if (_effectsInstalled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final output = template.output;
|
||||||
|
|
||||||
|
if (template.address.isNotEmpty) {
|
||||||
|
_addressController.text = template.address;
|
||||||
|
}
|
||||||
|
if (template.name.isNotEmpty) {
|
||||||
|
_nameController.text = template.name;
|
||||||
|
}
|
||||||
|
if (template.output.cryptoAmount.isNotEmpty) {
|
||||||
|
_cryptoAmountController.text = template.output.cryptoAmount;
|
||||||
|
}
|
||||||
|
if (template.output.fiatAmount.isNotEmpty) {
|
||||||
|
_fiatAmountController.text = template.output.fiatAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
_addressController.addListener(() {
|
||||||
|
final address = _addressController.text;
|
||||||
|
|
||||||
|
if (template.address != address) {
|
||||||
|
template.address = address;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_cryptoAmountController.addListener(() {
|
||||||
|
final amount = _cryptoAmountController.text;
|
||||||
|
|
||||||
|
if (amount != output.cryptoAmount) {
|
||||||
|
output.setCryptoAmount(amount);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_fiatAmountController.addListener(() {
|
||||||
|
final amount = _fiatAmountController.text;
|
||||||
|
|
||||||
|
if (amount != output.fiatAmount) {
|
||||||
|
output.setFiatAmount(amount);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_nameController.addListener(() {
|
||||||
|
final name = _nameController.text;
|
||||||
|
|
||||||
|
if (name != template.name) {
|
||||||
|
template.name = name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
reaction((_) => template.address, (String address) {
|
||||||
|
if (address != _addressController.text) {
|
||||||
|
_addressController.text = address;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reaction((_) => output.cryptoAmount, (String amount) {
|
||||||
|
if (amount != _cryptoAmountController.text) {
|
||||||
|
_cryptoAmountController.text = amount;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reaction((_) => output.fiatAmount, (String amount) {
|
||||||
|
if (amount != _fiatAmountController.text) {
|
||||||
|
_fiatAmountController.text = amount;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
reaction((_) => template.name, (String name) {
|
||||||
|
if (name != _nameController.text) {
|
||||||
|
_nameController.text = name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_effectsInstalled = true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,18 @@
|
||||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
|
||||||
import 'package:cake_wallet/routes.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/receive/widgets/qr_image.dart';
|
import 'package:cake_wallet/src/screens/receive/widgets/qr_image.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/standard_list.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/palette.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/utils/clipboard_util.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:qr_flutter/qr_flutter.dart' as qr;
|
import 'package:qr_flutter/qr_flutter.dart' as qr;
|
||||||
import '../../../palette.dart';
|
|
||||||
import '../../widgets/primary_button.dart';
|
|
||||||
import '../../widgets/standard_list.dart';
|
|
||||||
|
|
||||||
class Setup2FAQRPage extends BasePage {
|
class Setup2FAQRPage extends BasePage {
|
||||||
Setup2FAQRPage({required this.setup2FAViewModel});
|
Setup2FAQRPage({required this.setup2FAViewModel});
|
||||||
|
@ -106,7 +108,8 @@ class Setup2FAQRPage extends BasePage {
|
||||||
height: 32,
|
height: 32,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(ClipboardData(text: '${setup2FAViewModel.secretKey}'));
|
ClipboardUtil.setSensitiveDataToClipboard(
|
||||||
|
ClipboardData(text: '${setup2FAViewModel.secretKey}'));
|
||||||
showBar<void>(context, S.of(context).copied_to_clipboard);
|
showBar<void>(context, S.of(context).copied_to_clipboard);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import 'package:auto_size_text/auto_size_text.dart';
|
import 'package:auto_size_text/auto_size_text.dart';
|
||||||
import 'package:cake_wallet/entities/qr_view_data.dart';
|
import 'package:cake_wallet/entities/qr_view_data.dart';
|
||||||
|
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||||
|
import 'package:cake_wallet/src/widgets/list_row.dart';
|
||||||
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
import 'package:cake_wallet/src/widgets/section_divider.dart';
|
||||||
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
|
import 'package:cake_wallet/routes.dart';
|
||||||
|
import 'package:cake_wallet/utils/clipboard_util.dart';
|
||||||
import 'package:cake_wallet/utils/show_bar.dart';
|
import 'package:cake_wallet/utils/show_bar.dart';
|
||||||
|
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
|
||||||
import 'package:device_display_brightness/device_display_brightness.dart';
|
import 'package:device_display_brightness/device_display_brightness.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/generated/i18n.dart';
|
|
||||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
|
||||||
import 'package:cake_wallet/src/widgets/list_row.dart';
|
|
||||||
import 'package:cake_wallet/view_model/wallet_keys_view_model.dart';
|
|
||||||
import 'package:cake_wallet/routes.dart';
|
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
|
|
||||||
class WalletKeysPage extends BasePage {
|
class WalletKeysPage extends BasePage {
|
||||||
|
@ -56,7 +57,7 @@ class WalletKeysPage extends BasePage {
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12.0),
|
borderRadius: BorderRadius.circular(12.0),
|
||||||
color: Theme.of(context).accentTextTheme!.bodySmall!.color!,
|
color: Theme.of(context).accentTextTheme.bodySmall!.color!,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
@ -84,7 +85,7 @@ class WalletKeysPage extends BasePage {
|
||||||
separatorBuilder: (context, index) => Container(
|
separatorBuilder: (context, index) => Container(
|
||||||
height: 1,
|
height: 1,
|
||||||
padding: EdgeInsets.only(left: 24),
|
padding: EdgeInsets.only(left: 24),
|
||||||
color: Theme.of(context).accentTextTheme!.titleLarge!.backgroundColor!,
|
color: Theme.of(context).accentTextTheme.titleLarge!.backgroundColor!,
|
||||||
child: const SectionDivider(),
|
child: const SectionDivider(),
|
||||||
),
|
),
|
||||||
itemCount: walletKeysViewModel.items.length,
|
itemCount: walletKeysViewModel.items.length,
|
||||||
|
@ -93,7 +94,7 @@ class WalletKeysPage extends BasePage {
|
||||||
|
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Clipboard.setData(ClipboardData(text: item.value));
|
ClipboardUtil.setSensitiveDataToClipboard(ClipboardData(text: item.value));
|
||||||
showBar<void>(context, S.of(context).copied_key_to_clipboard(item.title));
|
showBar<void>(context, S.of(context).copied_key_to_clipboard(item.title));
|
||||||
},
|
},
|
||||||
child: ListRow(
|
child: ListRow(
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import 'package:cake_wallet/utils/device_info.dart';
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
@ -16,10 +15,7 @@ class AddressTextField extends StatelessWidget {
|
||||||
{required this.controller,
|
{required this.controller,
|
||||||
this.isActive = true,
|
this.isActive = true,
|
||||||
this.placeholder,
|
this.placeholder,
|
||||||
this.options = const [
|
this.options = const [AddressTextFieldOption.qrCode, AddressTextFieldOption.addressBook],
|
||||||
AddressTextFieldOption.qrCode,
|
|
||||||
AddressTextFieldOption.addressBook
|
|
||||||
],
|
|
||||||
this.onURIScanned,
|
this.onURIScanned,
|
||||||
this.focusNode,
|
this.focusNode,
|
||||||
this.isBorderExist = true,
|
this.isBorderExist = true,
|
||||||
|
@ -31,6 +27,7 @@ class AddressTextField extends StatelessWidget {
|
||||||
this.validator,
|
this.validator,
|
||||||
this.onPushPasteButton,
|
this.onPushPasteButton,
|
||||||
this.onPushAddressBookButton,
|
this.onPushAddressBookButton,
|
||||||
|
this.onSelectedContact,
|
||||||
this.selectedCurrency});
|
this.selectedCurrency});
|
||||||
|
|
||||||
static const prefixIconWidth = 34.0;
|
static const prefixIconWidth = 34.0;
|
||||||
|
@ -52,6 +49,7 @@ class AddressTextField extends StatelessWidget {
|
||||||
final FocusNode? focusNode;
|
final FocusNode? focusNode;
|
||||||
final Function(BuildContext context)? onPushPasteButton;
|
final Function(BuildContext context)? onPushPasteButton;
|
||||||
final Function(BuildContext context)? onPushAddressBookButton;
|
final Function(BuildContext context)? onPushAddressBookButton;
|
||||||
|
final Function(ContactBase contact)? onSelectedContact;
|
||||||
final CryptoCurrency? selectedCurrency;
|
final CryptoCurrency? selectedCurrency;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -66,34 +64,27 @@ class AddressTextField extends StatelessWidget {
|
||||||
controller: controller,
|
controller: controller,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
style: textStyle ??
|
style: textStyle ??
|
||||||
TextStyle(
|
TextStyle(fontSize: 16, color: Theme.of(context).primaryTextTheme.titleLarge!.color!),
|
||||||
fontSize: 16,
|
|
||||||
color: Theme.of(context).primaryTextTheme!.titleLarge!.color!),
|
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
suffixIcon: SizedBox(
|
suffixIcon: SizedBox(
|
||||||
width: prefixIconWidth * options.length +
|
width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length),
|
||||||
(spaceBetweenPrefixIcons * options.length),
|
|
||||||
),
|
),
|
||||||
hintStyle: hintStyle ??
|
hintStyle: hintStyle ?? TextStyle(fontSize: 16, color: Theme.of(context).hintColor),
|
||||||
TextStyle(fontSize: 16, color: Theme.of(context).hintColor),
|
|
||||||
hintText: placeholder ?? S.current.widgets_address,
|
hintText: placeholder ?? S.current.widgets_address,
|
||||||
focusedBorder: isBorderExist
|
focusedBorder: isBorderExist
|
||||||
? UnderlineInputBorder(
|
? UnderlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: borderColor ?? Theme.of(context).dividerColor,
|
color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
|
||||||
width: 1.0))
|
|
||||||
: InputBorder.none,
|
: InputBorder.none,
|
||||||
disabledBorder: isBorderExist
|
disabledBorder: isBorderExist
|
||||||
? UnderlineInputBorder(
|
? UnderlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: borderColor ?? Theme.of(context).dividerColor,
|
color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
|
||||||
width: 1.0))
|
|
||||||
: InputBorder.none,
|
: InputBorder.none,
|
||||||
enabledBorder: isBorderExist
|
enabledBorder: isBorderExist
|
||||||
? UnderlineInputBorder(
|
? UnderlineInputBorder(
|
||||||
borderSide: BorderSide(
|
borderSide: BorderSide(
|
||||||
color: borderColor ?? Theme.of(context).dividerColor,
|
color: borderColor ?? Theme.of(context).dividerColor, width: 1.0))
|
||||||
width: 1.0))
|
|
||||||
: InputBorder.none,
|
: InputBorder.none,
|
||||||
),
|
),
|
||||||
validator: validator,
|
validator: validator,
|
||||||
|
@ -102,11 +93,11 @@ class AddressTextField extends StatelessWidget {
|
||||||
top: 2,
|
top: 2,
|
||||||
right: 0,
|
right: 0,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: prefixIconWidth * options.length +
|
width: prefixIconWidth * options.length + (spaceBetweenPrefixIcons * options.length),
|
||||||
(spaceBetweenPrefixIcons * options.length),
|
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: ResponsiveLayoutUtil.instance.isMobile
|
mainAxisAlignment: ResponsiveLayoutUtil.instance.isMobile
|
||||||
? MainAxisAlignment.spaceBetween : MainAxisAlignment.end,
|
? MainAxisAlignment.spaceBetween
|
||||||
|
: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
if (this.options.contains(AddressTextFieldOption.paste)) ...[
|
if (this.options.contains(AddressTextFieldOption.paste)) ...[
|
||||||
|
@ -122,20 +113,14 @@ class AddressTextField extends StatelessWidget {
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: buttonColor ??
|
color: buttonColor ??
|
||||||
Theme.of(context)
|
Theme.of(context).accentTextTheme.titleLarge!.color!,
|
||||||
.accentTextTheme
|
borderRadius: BorderRadius.all(Radius.circular(6))),
|
||||||
!
|
|
||||||
.titleLarge!
|
|
||||||
.color!,
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.all(Radius.circular(6))),
|
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'assets/images/paste_ios.png',
|
'assets/images/paste_ios.png',
|
||||||
color: iconColor ??
|
color: iconColor ??
|
||||||
Theme.of(context)
|
Theme.of(context)
|
||||||
.primaryTextTheme
|
.primaryTextTheme
|
||||||
!
|
.headlineMedium!
|
||||||
.headlineMedium!
|
|
||||||
.decorationColor!,
|
.decorationColor!,
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
|
@ -155,28 +140,21 @@ class AddressTextField extends StatelessWidget {
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: buttonColor ??
|
color: buttonColor ??
|
||||||
Theme.of(context)
|
Theme.of(context).accentTextTheme.titleLarge!.color!,
|
||||||
.accentTextTheme
|
borderRadius: BorderRadius.all(Radius.circular(6))),
|
||||||
|
|
||||||
.titleLarge!
|
|
||||||
.color!,
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.all(Radius.circular(6))),
|
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'assets/images/qr_code_icon.png',
|
'assets/images/qr_code_icon.png',
|
||||||
color: iconColor ??
|
color: iconColor ??
|
||||||
Theme.of(context)
|
Theme.of(context)
|
||||||
.primaryTextTheme
|
.primaryTextTheme
|
||||||
!.headlineMedium!
|
.headlineMedium!
|
||||||
.decorationColor!,
|
.decorationColor!,
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
] else
|
] else
|
||||||
SizedBox(width: 5),
|
SizedBox(width: 5),
|
||||||
if (this
|
if (this.options.contains(AddressTextFieldOption.addressBook)) ...[
|
||||||
.options
|
|
||||||
.contains(AddressTextFieldOption.addressBook)) ...[
|
|
||||||
Container(
|
Container(
|
||||||
width: prefixIconWidth,
|
width: prefixIconWidth,
|
||||||
height: prefixIconHeight,
|
height: prefixIconHeight,
|
||||||
|
@ -184,26 +162,19 @@ class AddressTextField extends StatelessWidget {
|
||||||
child: Semantics(
|
child: Semantics(
|
||||||
label: S.of(context).address_book,
|
label: S.of(context).address_book,
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () async =>
|
onTap: () async => _presetAddressBookPicker(context),
|
||||||
_presetAddressBookPicker(context),
|
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.all(8),
|
padding: EdgeInsets.all(8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: buttonColor ??
|
color: buttonColor ??
|
||||||
Theme.of(context)
|
Theme.of(context).accentTextTheme.titleLarge!.color!,
|
||||||
.accentTextTheme
|
borderRadius: BorderRadius.all(Radius.circular(6))),
|
||||||
!
|
|
||||||
.titleLarge!
|
|
||||||
.color!,
|
|
||||||
borderRadius:
|
|
||||||
BorderRadius.all(Radius.circular(6))),
|
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'assets/images/open_book.png',
|
'assets/images/open_book.png',
|
||||||
color: iconColor ??
|
color: iconColor ??
|
||||||
Theme.of(context)
|
Theme.of(context)
|
||||||
.primaryTextTheme
|
.primaryTextTheme
|
||||||
!
|
.headlineMedium!
|
||||||
.headlineMedium!
|
|
||||||
.decorationColor!,
|
.decorationColor!,
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
|
@ -221,30 +192,31 @@ class AddressTextField extends StatelessWidget {
|
||||||
if (code.isEmpty) {
|
if (code.isEmpty) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final uri = Uri.parse(code);
|
final uri = Uri.parse(code);
|
||||||
controller?.text = uri.path;
|
controller?.text = uri.path;
|
||||||
onURIScanned?.call(uri);
|
onURIScanned?.call(uri);
|
||||||
} catch(_){
|
} catch (_) {
|
||||||
controller?.text = code;
|
controller?.text = code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _presetAddressBookPicker(BuildContext context) async {
|
Future<void> _presetAddressBookPicker(BuildContext context) async {
|
||||||
final contact = await Navigator.of(context)
|
final contact = await Navigator.of(context)
|
||||||
.pushNamed(Routes.pickerAddressBook,arguments: selectedCurrency);
|
.pushNamed(Routes.pickerAddressBook, arguments: selectedCurrency);
|
||||||
|
|
||||||
if (contact is ContactBase && contact.address != null) {
|
if (contact is ContactBase) {
|
||||||
controller?.text = contact.address;
|
controller?.text = contact.address;
|
||||||
onPushAddressBookButton?.call(context);
|
onPushAddressBookButton?.call(context);
|
||||||
|
onSelectedContact?.call(contact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pasteAddress(BuildContext context) async {
|
Future<void> _pasteAddress(BuildContext context) async {
|
||||||
final clipboard = await Clipboard.getData('text/plain');
|
final clipboard = await Clipboard.getData('text/plain');
|
||||||
final address = clipboard?.text ?? '';
|
final address = clipboard?.text ?? '';
|
||||||
|
|
||||||
if (address.isNotEmpty) {
|
if (address.isNotEmpty) {
|
||||||
controller?.text = address;
|
controller?.text = address;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,7 @@ class NavBar extends StatelessWidget implements ObstructingPreferredSizeWidget {
|
||||||
leading: leading,
|
leading: leading,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
automaticallyImplyMiddle: false,
|
automaticallyImplyMiddle: false,
|
||||||
|
transitionBetweenRoutes: false,
|
||||||
middle: middle,
|
middle: middle,
|
||||||
trailing: trailing,
|
trailing: trailing,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
|
|
|
@ -8,7 +8,8 @@ class TemplateTile extends StatefulWidget {
|
||||||
required this.amount,
|
required this.amount,
|
||||||
required this.from,
|
required this.from,
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
required this.onRemove
|
required this.onRemove,
|
||||||
|
this.hasMultipleRecipients,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final String to;
|
final String to;
|
||||||
|
@ -16,6 +17,7 @@ class TemplateTile extends StatefulWidget {
|
||||||
final String from;
|
final String from;
|
||||||
final VoidCallback onTap;
|
final VoidCallback onTap;
|
||||||
final VoidCallback onRemove;
|
final VoidCallback onRemove;
|
||||||
|
final bool? hasMultipleRecipients;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TemplateTileState createState() => TemplateTileState(
|
TemplateTileState createState() => TemplateTileState(
|
||||||
|
@ -51,45 +53,47 @@ class TemplateTileState extends State<TemplateTile> {
|
||||||
final toIcon = Image.asset('assets/images/to_icon.png', color: color);
|
final toIcon = Image.asset('assets/images/to_icon.png', color: color);
|
||||||
|
|
||||||
final content = Row(
|
final content = Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: <Widget>[
|
children: widget.hasMultipleRecipients ?? false
|
||||||
Text(
|
? [
|
||||||
amount,
|
Text(
|
||||||
style: TextStyle(
|
to,
|
||||||
fontSize: 16,
|
style: TextStyle(
|
||||||
fontWeight: FontWeight.w600,
|
fontSize: 16, fontWeight: FontWeight.w600, color: color),
|
||||||
color: color
|
),
|
||||||
),
|
]
|
||||||
),
|
: [
|
||||||
Padding(
|
Text(
|
||||||
padding: EdgeInsets.only(left: 5),
|
amount,
|
||||||
child: Text(
|
style: TextStyle(
|
||||||
from,
|
fontSize: 16, fontWeight: FontWeight.w600, color: color),
|
||||||
style: TextStyle(
|
),
|
||||||
fontSize: 16,
|
Padding(
|
||||||
fontWeight: FontWeight.w600,
|
padding: EdgeInsets.only(left: 5),
|
||||||
color: color
|
child: Text(
|
||||||
),
|
from,
|
||||||
),
|
style: TextStyle(
|
||||||
),
|
fontSize: 16,
|
||||||
Padding(
|
fontWeight: FontWeight.w600,
|
||||||
padding: EdgeInsets.only(left: 5),
|
color: color),
|
||||||
child: toIcon,
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(left: 5),
|
padding: EdgeInsets.only(left: 5),
|
||||||
child: Text(
|
child: toIcon,
|
||||||
to,
|
),
|
||||||
style: TextStyle(
|
Padding(
|
||||||
fontSize: 16,
|
padding: EdgeInsets.only(left: 5),
|
||||||
fontWeight: FontWeight.w600,
|
child: Text(
|
||||||
color: color
|
to,
|
||||||
),
|
style: TextStyle(
|
||||||
),
|
fontSize: 16,
|
||||||
),
|
fontWeight: FontWeight.w600,
|
||||||
],
|
color: color),
|
||||||
);
|
),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
final tile = Container(
|
final tile = Container(
|
||||||
padding: EdgeInsets.only(right: 10),
|
padding: EdgeInsets.only(right: 10),
|
||||||
|
|
|
@ -23,25 +23,27 @@ abstract class SendTemplateBase with Store {
|
||||||
templates.replaceRange(0, templates.length, templateSource.values.toList());
|
templates.replaceRange(0, templates.length, templateSource.values.toList());
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> addTemplate({
|
Future<void> addTemplate(
|
||||||
required String name,
|
{required String name,
|
||||||
required bool isCurrencySelected,
|
required bool isCurrencySelected,
|
||||||
required String address,
|
required String address,
|
||||||
required String cryptoCurrency,
|
required String cryptoCurrency,
|
||||||
required String fiatCurrency,
|
required String fiatCurrency,
|
||||||
required String amount,
|
required String amount,
|
||||||
required String amountFiat}) async {
|
required String amountFiat,
|
||||||
|
required List<Template> additionalRecipients}) async {
|
||||||
final template = Template(
|
final template = Template(
|
||||||
nameRaw: name,
|
nameRaw: name,
|
||||||
isCurrencySelectedRaw: isCurrencySelected,
|
isCurrencySelectedRaw: isCurrencySelected,
|
||||||
addressRaw: address,
|
addressRaw: address,
|
||||||
cryptoCurrencyRaw: cryptoCurrency,
|
cryptoCurrencyRaw: cryptoCurrency,
|
||||||
fiatCurrencyRaw: fiatCurrency,
|
fiatCurrencyRaw: fiatCurrency,
|
||||||
amountRaw: amount,
|
amountRaw: amount,
|
||||||
amountFiatRaw: amountFiat);
|
amountFiatRaw: amountFiat,
|
||||||
|
additionalRecipientsRaw: additionalRecipients);
|
||||||
await templateSource.add(template);
|
await templateSource.add(template);
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> remove({required Template template}) async => await template.delete();
|
Future<void> remove({required Template template}) async => await template.delete();
|
||||||
}
|
}
|
||||||
|
|
15
lib/utils/clipboard_util.dart
Normal file
15
lib/utils/clipboard_util.dart
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/utils/device_info.dart';
|
||||||
|
import 'package:sensitive_clipboard/sensitive_clipboard.dart';
|
||||||
|
|
||||||
|
class ClipboardUtil {
|
||||||
|
static Future<void> setSensitiveDataToClipboard(ClipboardData data) async {
|
||||||
|
if (DeviceInfo.instance.isMobile) {
|
||||||
|
await SensitiveClipboard.copy(data.text);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Clipboard.setData(data);
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,7 +67,8 @@ abstract class BalanceViewModelBase with Store {
|
||||||
final price = fiatConvertationStore.prices[appStore.wallet!.currency];
|
final price = fiatConvertationStore.prices[appStore.wallet!.currency];
|
||||||
|
|
||||||
if (price == null) {
|
if (price == null) {
|
||||||
throw Exception('No price for ${appStore.wallet!.currency} (current wallet)');
|
// price should update on next fetch:
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return price;
|
return price;
|
||||||
|
|
|
@ -19,7 +19,9 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||||
port = '',
|
port = '',
|
||||||
login = '',
|
login = '',
|
||||||
password = '',
|
password = '',
|
||||||
trusted = false;
|
trusted = false,
|
||||||
|
useSocksProxy = false,
|
||||||
|
socksProxyAddress = '';
|
||||||
|
|
||||||
@observable
|
@observable
|
||||||
ExecutionState state;
|
ExecutionState state;
|
||||||
|
@ -45,6 +47,12 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||||
@observable
|
@observable
|
||||||
bool trusted;
|
bool trusted;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool useSocksProxy;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
String socksProxyAddress;
|
||||||
|
|
||||||
@computed
|
@computed
|
||||||
bool get isReady => address.isNotEmpty && port.isNotEmpty;
|
bool get isReady => address.isNotEmpty && port.isNotEmpty;
|
||||||
|
|
||||||
|
@ -73,6 +81,8 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||||
password = '';
|
password = '';
|
||||||
useSSL = false;
|
useSSL = false;
|
||||||
trusted = false;
|
trusted = false;
|
||||||
|
useSocksProxy = false;
|
||||||
|
socksProxyAddress = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -93,6 +103,12 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||||
@action
|
@action
|
||||||
void setTrusted(bool val) => trusted = val;
|
void setTrusted(bool val) => trusted = val;
|
||||||
|
|
||||||
|
@action
|
||||||
|
void setSocksProxy(bool val) => useSocksProxy = val;
|
||||||
|
|
||||||
|
@action
|
||||||
|
void setSocksProxyAddress(String val) => socksProxyAddress = val;
|
||||||
|
|
||||||
@action
|
@action
|
||||||
Future<void> save({Node? editingNode, bool saveAsCurrent = false}) async {
|
Future<void> save({Node? editingNode, bool saveAsCurrent = false}) async {
|
||||||
final node = Node(
|
final node = Node(
|
||||||
|
@ -101,7 +117,8 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||||
login: login,
|
login: login,
|
||||||
password: password,
|
password: password,
|
||||||
useSSL: useSSL,
|
useSSL: useSSL,
|
||||||
trusted: trusted);
|
trusted: trusted,
|
||||||
|
socksProxyAddress: socksProxyAddress);
|
||||||
try {
|
try {
|
||||||
state = IsExecutingState();
|
state = IsExecutingState();
|
||||||
if (editingNode != null) {
|
if (editingNode != null) {
|
||||||
|
@ -130,7 +147,8 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
||||||
login: login,
|
login: login,
|
||||||
password: password,
|
password: password,
|
||||||
useSSL: useSSL,
|
useSSL: useSSL,
|
||||||
trusted: trusted);
|
trusted: trusted,
|
||||||
|
socksProxyAddress: socksProxyAddress);
|
||||||
try {
|
try {
|
||||||
connectionState = IsExecutingState();
|
connectionState = IsExecutingState();
|
||||||
final isAlive = await node.requestNode();
|
final isAlive = await node.requestNode();
|
||||||
|
|
|
@ -18,6 +18,8 @@ import 'package:cake_wallet/store/settings_store.dart';
|
||||||
import 'package:cake_wallet/generated/i18n.dart';
|
import 'package:cake_wallet/generated/i18n.dart';
|
||||||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||||
|
|
||||||
|
import 'package:cake_wallet/entities/contact_base.dart';
|
||||||
|
|
||||||
part 'output.g.dart';
|
part 'output.g.dart';
|
||||||
|
|
||||||
const String cryptoNumberPattern = '0.0';
|
const String cryptoNumberPattern = '0.0';
|
||||||
|
@ -244,4 +246,11 @@ abstract class OutputBase with Store {
|
||||||
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
|
extractedAddress = await extractAddressFromParsed(context, parsedAddress);
|
||||||
note = parsedAddress.description;
|
note = parsedAddress.description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadContact(ContactBase contact) {
|
||||||
|
address = contact.name;
|
||||||
|
parsedAddress = ParsedAddress.fetchContactAddress(address: contact.address, name: contact.name);
|
||||||
|
extractedAddress = parsedAddress.addresses.first;
|
||||||
|
note = parsedAddress.description;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:cake_wallet/view_model/send/output.dart';
|
import 'package:cake_wallet/view_model/send/template_view_model.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
import 'package:cw_core/wallet_type.dart';
|
import 'package:cw_core/wallet_type.dart';
|
||||||
import 'package:mobx/mobx.dart';
|
import 'package:mobx/mobx.dart';
|
||||||
import 'package:cake_wallet/entities/template.dart';
|
import 'package:cake_wallet/entities/template.dart';
|
||||||
|
@ -6,10 +7,7 @@ import 'package:cake_wallet/store/templates/send_template_store.dart';
|
||||||
import 'package:cake_wallet/core/template_validator.dart';
|
import 'package:cake_wallet/core/template_validator.dart';
|
||||||
import 'package:cake_wallet/core/address_validator.dart';
|
import 'package:cake_wallet/core/address_validator.dart';
|
||||||
import 'package:cake_wallet/core/amount_validator.dart';
|
import 'package:cake_wallet/core/amount_validator.dart';
|
||||||
import 'package:cake_wallet/core/validator.dart';
|
|
||||||
import 'package:cw_core/wallet_base.dart';
|
import 'package:cw_core/wallet_base.dart';
|
||||||
import 'package:cw_core/crypto_currency.dart';
|
|
||||||
import 'package:cake_wallet/entities/fiat_currency.dart';
|
|
||||||
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||||
import 'package:cake_wallet/store/settings_store.dart';
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
|
||||||
|
@ -19,72 +17,74 @@ class SendTemplateViewModel = SendTemplateViewModelBase
|
||||||
with _$SendTemplateViewModel;
|
with _$SendTemplateViewModel;
|
||||||
|
|
||||||
abstract class SendTemplateViewModelBase with Store {
|
abstract class SendTemplateViewModelBase with Store {
|
||||||
SendTemplateViewModelBase(this._wallet, this._settingsStore,
|
|
||||||
this._sendTemplateStore, this._fiatConversationStore)
|
|
||||||
: output = Output(_wallet, _settingsStore, _fiatConversationStore, () => _wallet.currency) {
|
|
||||||
output = Output(_wallet, _settingsStore, _fiatConversationStore, () => currency);
|
|
||||||
}
|
|
||||||
|
|
||||||
Output output;
|
|
||||||
|
|
||||||
Validator get amountValidator =>
|
|
||||||
AmountValidator(currency: walletTypeToCryptoCurrency(_wallet.type));
|
|
||||||
|
|
||||||
Validator get addressValidator => AddressValidator(type: _wallet.currency);
|
|
||||||
|
|
||||||
Validator get templateValidator => TemplateValidator();
|
|
||||||
|
|
||||||
CryptoCurrency get currency => _wallet.currency;
|
|
||||||
|
|
||||||
FiatCurrency get fiat => _settingsStore.fiatCurrency;
|
|
||||||
|
|
||||||
@observable
|
|
||||||
bool isCurrencySelected = true;
|
|
||||||
|
|
||||||
@observable
|
|
||||||
bool isFiatSelected = false;
|
|
||||||
|
|
||||||
@action
|
|
||||||
void selectCurrency () {
|
|
||||||
isCurrencySelected = true;
|
|
||||||
isFiatSelected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@action
|
|
||||||
void selectFiat () {
|
|
||||||
isFiatSelected = true;
|
|
||||||
isCurrencySelected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@computed
|
|
||||||
ObservableList<Template> get templates => _sendTemplateStore.templates;
|
|
||||||
|
|
||||||
final WalletBase _wallet;
|
final WalletBase _wallet;
|
||||||
final SettingsStore _settingsStore;
|
final SettingsStore _settingsStore;
|
||||||
final SendTemplateStore _sendTemplateStore;
|
final SendTemplateStore _sendTemplateStore;
|
||||||
final FiatConversionStore _fiatConversationStore;
|
final FiatConversionStore _fiatConversationStore;
|
||||||
|
|
||||||
|
SendTemplateViewModelBase(this._wallet, this._settingsStore,
|
||||||
|
this._sendTemplateStore, this._fiatConversationStore)
|
||||||
|
: recipients = ObservableList<TemplateViewModel>() {
|
||||||
|
addRecipient();
|
||||||
|
}
|
||||||
|
|
||||||
|
ObservableList<TemplateViewModel> recipients;
|
||||||
|
|
||||||
|
@action
|
||||||
|
void addRecipient() {
|
||||||
|
recipients.add(TemplateViewModel(
|
||||||
|
cryptoCurrency: cryptoCurrency,
|
||||||
|
wallet: _wallet,
|
||||||
|
settingsStore: _settingsStore,
|
||||||
|
fiatConversationStore: _fiatConversationStore));
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void removeRecipient(TemplateViewModel recipient) {
|
||||||
|
recipients.remove(recipient);
|
||||||
|
}
|
||||||
|
|
||||||
|
AmountValidator get amountValidator =>
|
||||||
|
AmountValidator(currency: walletTypeToCryptoCurrency(_wallet.type));
|
||||||
|
|
||||||
|
AddressValidator get addressValidator =>
|
||||||
|
AddressValidator(type: _wallet.currency);
|
||||||
|
|
||||||
|
TemplateValidator get templateValidator => TemplateValidator();
|
||||||
|
|
||||||
|
@computed
|
||||||
|
CryptoCurrency get cryptoCurrency => _wallet.currency;
|
||||||
|
|
||||||
|
@computed
|
||||||
|
String get fiatCurrency => _settingsStore.fiatCurrency.title;
|
||||||
|
|
||||||
|
@computed
|
||||||
|
ObservableList<Template> get templates => _sendTemplateStore.templates;
|
||||||
|
|
||||||
|
@action
|
||||||
void updateTemplate() => _sendTemplateStore.update();
|
void updateTemplate() => _sendTemplateStore.update();
|
||||||
|
|
||||||
|
@action
|
||||||
void addTemplate(
|
void addTemplate(
|
||||||
{required String name,
|
{required String name,
|
||||||
required bool isCurrencySelected,
|
required bool isCurrencySelected,
|
||||||
required String address,
|
required String address,
|
||||||
required String cryptoCurrency,
|
required String amount,
|
||||||
required String fiatCurrency,
|
required String amountFiat,
|
||||||
required String amount,
|
required List<Template> additionalRecipients}) {
|
||||||
required String amountFiat}) {
|
|
||||||
_sendTemplateStore.addTemplate(
|
_sendTemplateStore.addTemplate(
|
||||||
name: name,
|
name: name,
|
||||||
isCurrencySelected: isCurrencySelected,
|
isCurrencySelected: isCurrencySelected,
|
||||||
address: address,
|
address: address,
|
||||||
cryptoCurrency: cryptoCurrency,
|
cryptoCurrency: cryptoCurrency.title,
|
||||||
fiatCurrency: fiatCurrency,
|
fiatCurrency: fiatCurrency,
|
||||||
amount: amount,
|
amount: amount,
|
||||||
amountFiat: amountFiat);
|
amountFiat: amountFiat,
|
||||||
|
additionalRecipients: additionalRecipients);
|
||||||
updateTemplate();
|
updateTemplate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
void removeTemplate({required Template template}) {
|
void removeTemplate({required Template template}) {
|
||||||
_sendTemplateStore.remove(template: template);
|
_sendTemplateStore.remove(template: template);
|
||||||
updateTemplate();
|
updateTemplate();
|
||||||
|
|
80
lib/view_model/send/template_view_model.dart
Normal file
80
lib/view_model/send/template_view_model.dart
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
import 'package:cake_wallet/entities/template.dart';
|
||||||
|
import 'package:cake_wallet/view_model/send/output.dart';
|
||||||
|
import 'package:mobx/mobx.dart';
|
||||||
|
import 'package:cw_core/wallet_base.dart';
|
||||||
|
import 'package:cw_core/crypto_currency.dart';
|
||||||
|
import 'package:cake_wallet/store/dashboard/fiat_conversion_store.dart';
|
||||||
|
import 'package:cake_wallet/store/settings_store.dart';
|
||||||
|
|
||||||
|
part 'template_view_model.g.dart';
|
||||||
|
|
||||||
|
class TemplateViewModel = TemplateViewModelBase with _$TemplateViewModel;
|
||||||
|
|
||||||
|
abstract class TemplateViewModelBase with Store {
|
||||||
|
final CryptoCurrency cryptoCurrency;
|
||||||
|
final WalletBase _wallet;
|
||||||
|
final SettingsStore _settingsStore;
|
||||||
|
final FiatConversionStore _fiatConversationStore;
|
||||||
|
|
||||||
|
TemplateViewModelBase(
|
||||||
|
{required this.cryptoCurrency,
|
||||||
|
required WalletBase wallet,
|
||||||
|
required SettingsStore settingsStore,
|
||||||
|
required FiatConversionStore fiatConversationStore})
|
||||||
|
: _wallet = wallet,
|
||||||
|
_settingsStore = settingsStore,
|
||||||
|
_fiatConversationStore = fiatConversationStore,
|
||||||
|
output = Output(wallet, settingsStore, fiatConversationStore,
|
||||||
|
() => wallet.currency) {
|
||||||
|
output = Output(
|
||||||
|
_wallet, _settingsStore, _fiatConversationStore, () => cryptoCurrency);
|
||||||
|
}
|
||||||
|
|
||||||
|
@observable
|
||||||
|
Output output;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
String name = '';
|
||||||
|
|
||||||
|
@observable
|
||||||
|
String address = '';
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool isCurrencySelected = true;
|
||||||
|
|
||||||
|
@observable
|
||||||
|
bool isFiatSelected = false;
|
||||||
|
|
||||||
|
@action
|
||||||
|
void selectCurrency() {
|
||||||
|
isCurrencySelected = true;
|
||||||
|
isFiatSelected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void selectFiat() {
|
||||||
|
isFiatSelected = true;
|
||||||
|
isCurrencySelected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
void reset() {
|
||||||
|
name = '';
|
||||||
|
address = '';
|
||||||
|
isCurrencySelected = true;
|
||||||
|
isFiatSelected = false;
|
||||||
|
output.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
Template toTemplate(
|
||||||
|
{required String cryptoCurrency, required String fiatCurrency}) {
|
||||||
|
return Template(
|
||||||
|
isCurrencySelectedRaw: isCurrencySelected,
|
||||||
|
nameRaw: name,
|
||||||
|
addressRaw: address,
|
||||||
|
cryptoCurrencyRaw: cryptoCurrency,
|
||||||
|
fiatCurrencyRaw: fiatCurrency,
|
||||||
|
amountRaw: output.cryptoAmount,
|
||||||
|
amountFiatRaw: output.fiatAmount);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ dependencies:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_cupertino_localizations: ^1.0.1
|
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
url_launcher: ^6.1.4
|
url_launcher: ^6.1.4
|
||||||
qr_flutter:
|
qr_flutter:
|
||||||
|
@ -82,6 +81,7 @@ dependencies:
|
||||||
path_provider_android: 2.0.24
|
path_provider_android: 2.0.24
|
||||||
shared_preferences_android: 2.0.17
|
shared_preferences_android: 2.0.17
|
||||||
url_launcher_android: 6.0.24
|
url_launcher_android: 6.0.24
|
||||||
|
sensitive_clipboard: ^1.0.0
|
||||||
bitcoin_flutter:
|
bitcoin_flutter:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/cake-tech/bitcoin_flutter.git
|
url: https://github.com/cake-tech/bitcoin_flutter.git
|
||||||
|
@ -130,4 +130,4 @@ flutter:
|
||||||
- asset: assets/fonts/Lato-Regular.ttf
|
- asset: assets/fonts/Lato-Regular.ttf
|
||||||
- asset: assets/fonts/Lato-Medium.ttf
|
- asset: assets/fonts/Lato-Medium.ttf
|
||||||
- asset: assets/fonts/Lato-Semibold.ttf
|
- asset: assets/fonts/Lato-Semibold.ttf
|
||||||
- asset: assets/fonts/Lato-Bold.ttf
|
- asset: assets/fonts/Lato-Bold.ttf
|
||||||
|
|
|
@ -250,8 +250,8 @@
|
||||||
"transaction_details_recipient_address": "عناوين المستلم",
|
"transaction_details_recipient_address": "عناوين المستلم",
|
||||||
"wallet_list_title": "محفظة Monero",
|
"wallet_list_title": "محفظة Monero",
|
||||||
"wallet_list_create_new_wallet": "إنشاء محفظة جديدة",
|
"wallet_list_create_new_wallet": "إنشاء محفظة جديدة",
|
||||||
"wallet_list_edit_wallet": "تحرير المحفظة",
|
"wallet_list_edit_wallet" : "تحرير المحفظة",
|
||||||
"wallet_list_wallet_name": "اسم المحفظة",
|
"wallet_list_wallet_name" : "اسم المحفظة",
|
||||||
"wallet_list_restore_wallet": "استعادة المحفظة",
|
"wallet_list_restore_wallet": "استعادة المحفظة",
|
||||||
"wallet_list_load_wallet": "تحميل المحفظة",
|
"wallet_list_load_wallet": "تحميل المحفظة",
|
||||||
"wallet_list_loading_wallet": "جار تحميل محفظة ${wallet_name}",
|
"wallet_list_loading_wallet": "جار تحميل محفظة ${wallet_name}",
|
||||||
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "يجب أن يتوافق عنوان المحفظة مع نوع\nالعملة المشفرة",
|
"error_text_address": "يجب أن يتوافق عنوان المحفظة مع نوع\nالعملة المشفرة",
|
||||||
"error_text_node_address": "الرجاء إدخال عنوان IPv4",
|
"error_text_node_address": "الرجاء إدخال عنوان IPv4",
|
||||||
"error_text_node_port": "منفذ العقدة يمكن أن يحتوي فقط على أرقام بين 0 و 65535",
|
"error_text_node_port": "منفذ العقدة يمكن أن يحتوي فقط على أرقام بين 0 و 65535",
|
||||||
|
"error_text_node_proxy_address": "الرجاء إدخال <عنوان IPv4>: <port> ، على سبيل المثال 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "يمكن أن يحتوي معرّف الدفع فقط من 16 إلى 64 حرفًا hex",
|
"error_text_payment_id": "يمكن أن يحتوي معرّف الدفع فقط من 16 إلى 64 حرفًا hex",
|
||||||
"error_text_xmr": "لا يمكن أن تتجاوز قيمة XMR الرصيد المتاح.\nيجب أن يكون عدد الكسور أقل من أو يساوي 12",
|
"error_text_xmr": "لا يمكن أن تتجاوز قيمة XMR الرصيد المتاح.\nيجب أن يكون عدد الكسور أقل من أو يساوي 12",
|
||||||
"error_text_fiat": "لا يمكن أن تتجاوز قيمة المبلغ الرصيد المتاح.\nيجب أن يكون عدد الكسور أقل أو يساوي 2",
|
"error_text_fiat": "لا يمكن أن تتجاوز قيمة المبلغ الرصيد المتاح.\nيجب أن يكون عدد الكسور أقل أو يساوي 2",
|
||||||
|
@ -652,5 +653,6 @@
|
||||||
"generate_name": "توليد الاسم",
|
"generate_name": "توليد الاسم",
|
||||||
"balance_page": "صفحة التوازن",
|
"balance_page": "صفحة التوازن",
|
||||||
"share": "يشارك",
|
"share": "يشارك",
|
||||||
"slidable": "قابل للانزلاق"
|
"slidable": "قابل للانزلاق",
|
||||||
|
"template_name": "اسم القالب"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Адресът на портфейла трябва да отговаря \n на вида криптовалута",
|
"error_text_address": "Адресът на портфейла трябва да отговаря \n на вида криптовалута",
|
||||||
"error_text_node_address": "Моля, въведете iPv4 адрес",
|
"error_text_node_address": "Моля, въведете iPv4 адрес",
|
||||||
"error_text_node_port": "Node port-ът е цяло число между 0 и 65535",
|
"error_text_node_port": "Node port-ът е цяло число между 0 и 65535",
|
||||||
|
"error_text_node_proxy_address": "Моля, въведете <IPv4 адрес>:<порт>, например 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "Payment ID-то може да съдържа само между 16 и 64 шестнайсетични символа",
|
"error_text_payment_id": "Payment ID-то може да съдържа само между 16 и 64 шестнайсетични символа",
|
||||||
"error_text_xmr": "XMR сумата не може да надхвърля наличния баланс.\nБроят на цифрите след десетичната запетая може да бъде най-много 12",
|
"error_text_xmr": "XMR сумата не може да надхвърля наличния баланс.\nБроят на цифрите след десетичната запетая може да бъде най-много 12",
|
||||||
"error_text_fiat": "Сумата не може да надвишава наличния баланс.\nThe number of fraction digits must be less or equal to 2",
|
"error_text_fiat": "Сумата не може да надвишава наличния баланс.\nThe number of fraction digits must be less or equal to 2",
|
||||||
|
@ -648,5 +649,6 @@
|
||||||
"generate_name": "Генериране на име",
|
"generate_name": "Генериране на име",
|
||||||
"balance_page": "Страница за баланс",
|
"balance_page": "Страница за баланс",
|
||||||
"share": "Дял",
|
"share": "Дял",
|
||||||
"slidable": "Плъзгащ се"
|
"slidable": "Плъзгащ се",
|
||||||
|
"template_name": "Име на шаблон"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Adresa peněženky musí odpovídat typu\nkryptoměny",
|
"error_text_address": "Adresa peněženky musí odpovídat typu\nkryptoměny",
|
||||||
"error_text_node_address": "prosím zadejte IPv4 adresu",
|
"error_text_node_address": "prosím zadejte IPv4 adresu",
|
||||||
"error_text_node_port": "Port uzlu musí být číslo mezi 0 a 65535",
|
"error_text_node_port": "Port uzlu musí být číslo mezi 0 a 65535",
|
||||||
|
"error_text_node_proxy_address": "Zadejte prosím <adresa IPv4>:<port>, například 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "ID platby se musí skládat z 16 až 64 hexadecimálních znaků",
|
"error_text_payment_id": "ID platby se musí skládat z 16 až 64 hexadecimálních znaků",
|
||||||
"error_text_xmr": "Hodnota XMR nemůže překročit dostupný zůstatek.\nPočet desetinných míst musí být menší, nebo roven 12",
|
"error_text_xmr": "Hodnota XMR nemůže překročit dostupný zůstatek.\nPočet desetinných míst musí být menší, nebo roven 12",
|
||||||
"error_text_fiat": "Částka nemůže překročit dostupný zůstatek.\nPočet desetinných míst musí být menší, nebo roven 2",
|
"error_text_fiat": "Částka nemůže překročit dostupný zůstatek.\nPočet desetinných míst musí být menší, nebo roven 2",
|
||||||
|
@ -648,5 +649,6 @@
|
||||||
"generate_name": "Generovat jméno",
|
"generate_name": "Generovat jméno",
|
||||||
"balance_page": "Stránka zůstatku",
|
"balance_page": "Stránka zůstatku",
|
||||||
"share": "Podíl",
|
"share": "Podíl",
|
||||||
"slidable": "Posuvné"
|
"slidable": "Posuvné",
|
||||||
|
"template_name": "Název šablony"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Die Walletadresse muss dem Typ der Kryptowährung\nentsprechen",
|
"error_text_address": "Die Walletadresse muss dem Typ der Kryptowährung\nentsprechen",
|
||||||
"error_text_node_address": "Bitte geben Sie eine iPv4-Adresse ein",
|
"error_text_node_address": "Bitte geben Sie eine iPv4-Adresse ein",
|
||||||
"error_text_node_port": "Der Knotenport darf nur Nummern zwischen 0 und 65535 enthalten",
|
"error_text_node_port": "Der Knotenport darf nur Nummern zwischen 0 und 65535 enthalten",
|
||||||
|
"error_text_node_proxy_address": "Bitte geben Sie <IPv4-Adresse>:<Port> ein, zum Beispiel 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "Die Zahlungs-ID darf nur 16 bis 64 hexadezimale Zeichen enthalten",
|
"error_text_payment_id": "Die Zahlungs-ID darf nur 16 bis 64 hexadezimale Zeichen enthalten",
|
||||||
"error_text_xmr": "Der XMR-Wert darf das verfügbare Guthaben nicht überschreiten.\nDie Anzahl der Nachkommastellen muss kleiner oder gleich 12 sein",
|
"error_text_xmr": "Der XMR-Wert darf das verfügbare Guthaben nicht überschreiten.\nDie Anzahl der Nachkommastellen muss kleiner oder gleich 12 sein",
|
||||||
"error_text_fiat": "Der Wert des Betrags darf den verfügbaren Kontostand nicht überschreiten.\nDie Anzahl der Nachkommastellen muss kleiner oder gleich 2 sein",
|
"error_text_fiat": "Der Wert des Betrags darf den verfügbaren Kontostand nicht überschreiten.\nDie Anzahl der Nachkommastellen muss kleiner oder gleich 2 sein",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "Namen generieren",
|
"generate_name": "Namen generieren",
|
||||||
"balance_page": "Balance-Seite",
|
"balance_page": "Balance-Seite",
|
||||||
"share": "Aktie",
|
"share": "Aktie",
|
||||||
"slidable": "Verschiebbar"
|
"slidable": "Verschiebbar",
|
||||||
|
"template_name": "Vorlagenname"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Wallet address must correspond to the type\nof cryptocurrency",
|
"error_text_address": "Wallet address must correspond to the type\nof cryptocurrency",
|
||||||
"error_text_node_address": "Please enter a iPv4 address",
|
"error_text_node_address": "Please enter a iPv4 address",
|
||||||
"error_text_node_port": "Node port can only contain numbers between 0 and 65535",
|
"error_text_node_port": "Node port can only contain numbers between 0 and 65535",
|
||||||
|
"error_text_node_proxy_address": "Please enter <IPv4 address>:<port>, for example 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "Payment ID can only contain from 16 to 64 chars in hex",
|
"error_text_payment_id": "Payment ID can only contain from 16 to 64 chars in hex",
|
||||||
"error_text_xmr": "XMR value can't exceed available balance.\nThe number of fraction digits must be less or equal to 12",
|
"error_text_xmr": "XMR value can't exceed available balance.\nThe number of fraction digits must be less or equal to 12",
|
||||||
"error_text_fiat": "Value of amount can't exceed available balance.\nThe number of fraction digits must be less or equal to 2",
|
"error_text_fiat": "Value of amount can't exceed available balance.\nThe number of fraction digits must be less or equal to 2",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "Generate Name",
|
"generate_name": "Generate Name",
|
||||||
"balance_page": "Balance Page",
|
"balance_page": "Balance Page",
|
||||||
"share": "Share",
|
"share": "Share",
|
||||||
"slidable": "Slidable"
|
"slidable": "Slidable",
|
||||||
|
"template_name": "Template Name"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "La dirección de la billetera debe corresponder al tipo \nde criptomoneda",
|
"error_text_address": "La dirección de la billetera debe corresponder al tipo \nde criptomoneda",
|
||||||
"error_text_node_address": "Por favor, introduzca una dirección iPv4",
|
"error_text_node_address": "Por favor, introduzca una dirección iPv4",
|
||||||
"error_text_node_port": "El puerto de nodo solo puede contener números entre 0 y 65535",
|
"error_text_node_port": "El puerto de nodo solo puede contener números entre 0 y 65535",
|
||||||
|
"error_text_node_proxy_address": "Ingrese <dirección IPv4>:<puerto>, por ejemplo 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "La ID de pago solo puede contener de 16 a 64 caracteres en hexadecimal",
|
"error_text_payment_id": "La ID de pago solo puede contener de 16 a 64 caracteres en hexadecimal",
|
||||||
"error_text_xmr": "El valor XMR no puede exceder el saldo disponible.\nTEl número de dígitos de fracción debe ser menor o igual a 12",
|
"error_text_xmr": "El valor XMR no puede exceder el saldo disponible.\nTEl número de dígitos de fracción debe ser menor o igual a 12",
|
||||||
"error_text_fiat": "El valor de la cantidad no puede exceder el saldo disponible.\nEl número de dígitos de fracción debe ser menor o igual a 2",
|
"error_text_fiat": "El valor de la cantidad no puede exceder el saldo disponible.\nEl número de dígitos de fracción debe ser menor o igual a 2",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "Generar nombre",
|
"generate_name": "Generar nombre",
|
||||||
"balance_page": "Página de saldo",
|
"balance_page": "Página de saldo",
|
||||||
"share": "Compartir",
|
"share": "Compartir",
|
||||||
"slidable": "deslizable"
|
"slidable": "deslizable",
|
||||||
|
"template_name": "Nombre de la plantilla"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "L'adresse du portefeuille (wallet) doit correspondre au type de\ncryptomonnaie",
|
"error_text_address": "L'adresse du portefeuille (wallet) doit correspondre au type de\ncryptomonnaie",
|
||||||
"error_text_node_address": "Merci d'entrer une adresse IPv4",
|
"error_text_node_address": "Merci d'entrer une adresse IPv4",
|
||||||
"error_text_node_port": "Le port d'un nœud doit être un nombre compris entre 0 et 65535",
|
"error_text_node_port": "Le port d'un nœud doit être un nombre compris entre 0 et 65535",
|
||||||
|
"error_text_node_proxy_address": "Veuillez saisir <adresse IPv4> :<port>, par exemple 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "Un ID de paiement ne peut être constitué que de 16 à 64 caractères hexadécimaux",
|
"error_text_payment_id": "Un ID de paiement ne peut être constitué que de 16 à 64 caractères hexadécimaux",
|
||||||
"error_text_xmr": "La valeur de XMR dépasse le solde disponible.\nLa partie décimale doit comporter au plus 12 chiffres",
|
"error_text_xmr": "La valeur de XMR dépasse le solde disponible.\nLa partie décimale doit comporter au plus 12 chiffres",
|
||||||
"error_text_fiat": "La valeur du montant ne peut dépasser le solde disponible.\nLa partie décimale doit comporter au plus 2 chiffres",
|
"error_text_fiat": "La valeur du montant ne peut dépasser le solde disponible.\nLa partie décimale doit comporter au plus 2 chiffres",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "Générer un nom",
|
"generate_name": "Générer un nom",
|
||||||
"balance_page": "Page Solde",
|
"balance_page": "Page Solde",
|
||||||
"share": "Partager",
|
"share": "Partager",
|
||||||
"slidable": "Glissable"
|
"slidable": "Glissable",
|
||||||
|
"template_name": "Nom du modèle"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Adireshin hujja ya kamata ya dace da irin\nna cryptocurrency",
|
"error_text_address": "Adireshin hujja ya kamata ya dace da irin\nna cryptocurrency",
|
||||||
"error_text_node_address": "Da fatan a shigar da iPv4 adireshin",
|
"error_text_node_address": "Da fatan a shigar da iPv4 adireshin",
|
||||||
"error_text_node_port": "Node tashar jiragen ruwa zai iya ƙunsar lambobi tsakanin 0 zuwa 65535 kawai",
|
"error_text_node_port": "Node tashar jiragen ruwa zai iya ƙunsar lambobi tsakanin 0 zuwa 65535 kawai",
|
||||||
|
"error_text_node_proxy_address": "Da fatan za a shigar da <IPv4 address>:<port>, misali 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "ID na biyan kudi kawai zai iya ɗaukar daga 16 zuwa 64 haruffa a cikin hex",
|
"error_text_payment_id": "ID na biyan kudi kawai zai iya ɗaukar daga 16 zuwa 64 haruffa a cikin hex",
|
||||||
"error_text_xmr": "XMR adadin ba zai iya wuce available balance.\nAdadin haruffan gaba zai kamata ya zama ko ƙasa daga na 12",
|
"error_text_xmr": "XMR adadin ba zai iya wuce available balance.\nAdadin haruffan gaba zai kamata ya zama ko ƙasa daga na 12",
|
||||||
"error_text_fiat": "Adadin kudin ba zai iya wuce available balance.\nAdadin haruffan gaba zai kamata ya zama ko ƙasa daga na 2",
|
"error_text_fiat": "Adadin kudin ba zai iya wuce available balance.\nAdadin haruffan gaba zai kamata ya zama ko ƙasa daga na 2",
|
||||||
|
@ -634,5 +635,6 @@
|
||||||
"generate_name": "Ƙirƙirar Suna",
|
"generate_name": "Ƙirƙirar Suna",
|
||||||
"balance_page": "Ma'auni Page",
|
"balance_page": "Ma'auni Page",
|
||||||
"share": "Raba",
|
"share": "Raba",
|
||||||
"slidable": "Mai iya zamewa"
|
"slidable": "Mai iya zamewa",
|
||||||
|
"template_name": "Sunan Samfura"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "वॉलेट पता प्रकार के अनुरूप होना चाहिए\nक्रिप्टोकरेंसी का",
|
"error_text_address": "वॉलेट पता प्रकार के अनुरूप होना चाहिए\nक्रिप्टोकरेंसी का",
|
||||||
"error_text_node_address": "कृपया एक IPv4 पता दर्ज करें",
|
"error_text_node_address": "कृपया एक IPv4 पता दर्ज करें",
|
||||||
"error_text_node_port": "नोड पोर्ट में केवल 0 और 65535 के बीच संख्याएँ हो सकती हैं",
|
"error_text_node_port": "नोड पोर्ट में केवल 0 और 65535 के बीच संख्याएँ हो सकती हैं",
|
||||||
|
"error_text_node_proxy_address": "कृपया <IPv4 पता> दर्ज करें: <पोर्ट>, उदाहरण के लिए 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "पेमेंट आईडी केवल हेक्स में 16 से 64 चार्ट तक हो सकती है",
|
"error_text_payment_id": "पेमेंट आईडी केवल हेक्स में 16 से 64 चार्ट तक हो सकती है",
|
||||||
"error_text_xmr": "एक्सएमआर मूल्य उपलब्ध शेष राशि से अधिक नहीं हो सकता.\nअंश अंकों की संख्या 12 से कम या इसके बराबर होनी चाहिए",
|
"error_text_xmr": "एक्सएमआर मूल्य उपलब्ध शेष राशि से अधिक नहीं हो सकता.\nअंश अंकों की संख्या 12 से कम या इसके बराबर होनी चाहिए",
|
||||||
"error_text_fiat": "राशि का मूल्य उपलब्ध शेष राशि से अधिक नहीं हो सकता.\nअंश अंकों की संख्या कम या 2 के बराबर होनी चाहिए",
|
"error_text_fiat": "राशि का मूल्य उपलब्ध शेष राशि से अधिक नहीं हो सकता.\nअंश अंकों की संख्या कम या 2 के बराबर होनी चाहिए",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "नाम जनरेट करें",
|
"generate_name": "नाम जनरेट करें",
|
||||||
"balance_page": "बैलेंस पेज",
|
"balance_page": "बैलेंस पेज",
|
||||||
"share": "शेयर करना",
|
"share": "शेयर करना",
|
||||||
"slidable": "फिसलने लायक"
|
"slidable": "फिसलने लायक",
|
||||||
|
"template_name": "टेम्पलेट नाम"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Adresa novčanika mora odgovarati\nvrsti kriptovalute",
|
"error_text_address": "Adresa novčanika mora odgovarati\nvrsti kriptovalute",
|
||||||
"error_text_node_address": "Molimo unesite iPv4 adresu",
|
"error_text_node_address": "Molimo unesite iPv4 adresu",
|
||||||
"error_text_node_port": "Node port smije sadržavati samo brojeve između 0 i 65535",
|
"error_text_node_port": "Node port smije sadržavati samo brojeve između 0 i 65535",
|
||||||
|
"error_text_node_proxy_address": "Unesite <IPv4 adresa>:<port>, na primjer 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "ID plaćanja smije sadržavati samo od 16 do 64 znakova hex vrijednosti",
|
"error_text_payment_id": "ID plaćanja smije sadržavati samo od 16 do 64 znakova hex vrijednosti",
|
||||||
"error_text_xmr": "XMR vrijednost ne smije biti veća od raspoloživog iznosa.\nBroj decimala smije biti 12 ili manji.",
|
"error_text_xmr": "XMR vrijednost ne smije biti veća od raspoloživog iznosa.\nBroj decimala smije biti 12 ili manji.",
|
||||||
"error_text_fiat": "Vrijednost iznosa ne smije biti veća od raspoloživog iznosa.\nBroj decimala smije biti 2 ili manji.",
|
"error_text_fiat": "Vrijednost iznosa ne smije biti veća od raspoloživog iznosa.\nBroj decimala smije biti 2 ili manji.",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "Generiraj ime",
|
"generate_name": "Generiraj ime",
|
||||||
"balance_page": "Stranica sa stanjem",
|
"balance_page": "Stranica sa stanjem",
|
||||||
"share": "Udio",
|
"share": "Udio",
|
||||||
"slidable": "Klizna"
|
"slidable": "Klizna",
|
||||||
|
"template_name": "Naziv predloška"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Alamat dompet harus sesuai dengan tipe\nmata uang kripto",
|
"error_text_address": "Alamat dompet harus sesuai dengan tipe\nmata uang kripto",
|
||||||
"error_text_node_address": "Silakan masukkan alamat iPv4",
|
"error_text_node_address": "Silakan masukkan alamat iPv4",
|
||||||
"error_text_node_port": "Port node hanya dapat berisi angka antara 0 dan 65535",
|
"error_text_node_port": "Port node hanya dapat berisi angka antara 0 dan 65535",
|
||||||
|
"error_text_node_proxy_address": "Masukkan <alamat IPv4>:<port>, misalnya 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "ID pembayaran hanya dapat berisi dari 16 hingga 64 karakter dalam hex",
|
"error_text_payment_id": "ID pembayaran hanya dapat berisi dari 16 hingga 64 karakter dalam hex",
|
||||||
"error_text_xmr": "Nilai XMR tidak boleh melebihi saldo yang tersedia.\nJumlah digit pecahan harus kurang atau sama dengan 12",
|
"error_text_xmr": "Nilai XMR tidak boleh melebihi saldo yang tersedia.\nJumlah digit pecahan harus kurang atau sama dengan 12",
|
||||||
"error_text_fiat": "Nilai jumlah tidak boleh melebihi saldo yang tersedia.\nJumlah digit pecahan harus kurang atau sama dengan 2",
|
"error_text_fiat": "Nilai jumlah tidak boleh melebihi saldo yang tersedia.\nJumlah digit pecahan harus kurang atau sama dengan 2",
|
||||||
|
@ -644,5 +645,6 @@
|
||||||
"generate_name": "Hasilkan Nama",
|
"generate_name": "Hasilkan Nama",
|
||||||
"balance_page": "Halaman Saldo",
|
"balance_page": "Halaman Saldo",
|
||||||
"share": "Membagikan",
|
"share": "Membagikan",
|
||||||
"slidable": "Dapat digeser"
|
"slidable": "Dapat digeser",
|
||||||
|
"template_name": "Nama Templat"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "L'indirizzo del Portafoglio deve corrispondere alla tipologia\ndi criptovaluta",
|
"error_text_address": "L'indirizzo del Portafoglio deve corrispondere alla tipologia\ndi criptovaluta",
|
||||||
"error_text_node_address": "Gentilmente inserisci un indirizzo iPv4",
|
"error_text_node_address": "Gentilmente inserisci un indirizzo iPv4",
|
||||||
"error_text_node_port": "La porta del nodo può contenere solo numeri compresi tra 0 e 65535",
|
"error_text_node_port": "La porta del nodo può contenere solo numeri compresi tra 0 e 65535",
|
||||||
|
"error_text_node_proxy_address": "Inserisci <indirizzo IPv4>:<porta>, ad esempio 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "L'ID del pagamento può contenere solo da 16 a 64 caratteri in esadecimale",
|
"error_text_payment_id": "L'ID del pagamento può contenere solo da 16 a 64 caratteri in esadecimale",
|
||||||
"error_text_xmr": "Il valore in XMR non può eccedere il saldo disponibile.\nIl numero delle cifre decimali deve essere inferiore o uguale a 12",
|
"error_text_xmr": "Il valore in XMR non può eccedere il saldo disponibile.\nIl numero delle cifre decimali deve essere inferiore o uguale a 12",
|
||||||
"error_text_fiat": "L'ammontare non può eccedere il saldo dispoinibile.\nIl numero di cifre decimali deve essere inferiore o uguale a 2",
|
"error_text_fiat": "L'ammontare non può eccedere il saldo dispoinibile.\nIl numero di cifre decimali deve essere inferiore o uguale a 2",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "Genera nome",
|
"generate_name": "Genera nome",
|
||||||
"balance_page": "Pagina di equilibrio",
|
"balance_page": "Pagina di equilibrio",
|
||||||
"share": "Condividere",
|
"share": "Condividere",
|
||||||
"slidable": "Scorrevole"
|
"slidable": "Scorrevole",
|
||||||
|
"template_name": "Nome modello"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "ウォレットアドレスは、\n暗号通貨",
|
"error_text_address": "ウォレットアドレスは、\n暗号通貨",
|
||||||
"error_text_node_address": "iPv4アドレスを入力してください",
|
"error_text_node_address": "iPv4アドレスを入力してください",
|
||||||
"error_text_node_port": "ノードポートには、0〜65535の数字のみを含めることができます",
|
"error_text_node_port": "ノードポートには、0〜65535の数字のみを含めることができます",
|
||||||
|
"error_text_node_proxy_address": "<IPv4 アドレス>:<ポート> を入力してください (例: 127.0.0.1:9050)",
|
||||||
"error_text_payment_id": "支払いIDには、16進数で16〜64文字しか含めることができません",
|
"error_text_payment_id": "支払いIDには、16進数で16〜64文字しか含めることができません",
|
||||||
"error_text_xmr": "XMR値は利用可能な残高を超えることはできません.\n小数桁数は12以下でなければなりません",
|
"error_text_xmr": "XMR値は利用可能な残高を超えることはできません.\n小数桁数は12以下でなければなりません",
|
||||||
"error_text_fiat": "金額は利用可能な残高を超えることはできません.\n小数桁の数は2以下でなければなりません",
|
"error_text_fiat": "金額は利用可能な残高を超えることはできません.\n小数桁の数は2以下でなければなりません",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "名前の生成",
|
"generate_name": "名前の生成",
|
||||||
"balance_page": "残高ページ",
|
"balance_page": "残高ページ",
|
||||||
"share": "共有",
|
"share": "共有",
|
||||||
"slidable": "スライド可能"
|
"slidable": "スライド可能",
|
||||||
|
"template_name": "テンプレート名"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "지갑 주소는 유형과 일치해야합니다\n암호 화폐",
|
"error_text_address": "지갑 주소는 유형과 일치해야합니다\n암호 화폐",
|
||||||
"error_text_node_address": "iPv4 주소를 입력하십시오",
|
"error_text_node_address": "iPv4 주소를 입력하십시오",
|
||||||
"error_text_node_port": "노드 포트는 0에서 65535 사이의 숫자 만 포함 할 수 있습니다",
|
"error_text_node_port": "노드 포트는 0에서 65535 사이의 숫자 만 포함 할 수 있습니다",
|
||||||
|
"error_text_node_proxy_address": "<IPv4 주소>:<포트>를 입력하십시오(예: 127.0.0.1:9050).",
|
||||||
"error_text_payment_id": "지불 ID는 16 ~ 64 자의 16 진 문자 만 포함 할 수 있습니다",
|
"error_text_payment_id": "지불 ID는 16 ~ 64 자의 16 진 문자 만 포함 할 수 있습니다",
|
||||||
"error_text_xmr": "XMR 값은 사용 가능한 잔액을 초과 할 수 없습니다.\n소수 자릿수는 12 이하 여야합니다",
|
"error_text_xmr": "XMR 값은 사용 가능한 잔액을 초과 할 수 없습니다.\n소수 자릿수는 12 이하 여야합니다",
|
||||||
"error_text_fiat": "금액은 사용 가능한 잔액을 초과 할 수 없습니다.\n소수 자릿수는 2보다 작거나 같아야합니다",
|
"error_text_fiat": "금액은 사용 가능한 잔액을 초과 할 수 없습니다.\n소수 자릿수는 2보다 작거나 같아야합니다",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "이름 생성",
|
"generate_name": "이름 생성",
|
||||||
"balance_page": "잔액 페이지",
|
"balance_page": "잔액 페이지",
|
||||||
"share": "공유하다",
|
"share": "공유하다",
|
||||||
"slidable": "슬라이딩 가능"
|
"slidable": "슬라이딩 가능",
|
||||||
|
"template_name": "템플릿 이름"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Wallet လိပ်စာသည် အမျိုးအစား\no cryptocurrency နှင့် ကိုက်ညီရပါမည်။",
|
"error_text_address": "Wallet လိပ်စာသည် အမျိုးအစား\no cryptocurrency နှင့် ကိုက်ညီရပါမည်။",
|
||||||
"error_text_node_address": "ကျေးဇူးပြု၍ iPv4 လိပ်စာကို ထည့်ပါ။",
|
"error_text_node_address": "ကျေးဇူးပြု၍ iPv4 လိပ်စာကို ထည့်ပါ။",
|
||||||
"error_text_node_port": "နော်ဒီဆိပ်ကမ်း တွင် 0 နှင့် 65535 အကြား နံပါတ်များသာ ပါဝင်နိုင်သည်။",
|
"error_text_node_port": "နော်ဒီဆိပ်ကမ်း တွင် 0 နှင့် 65535 အကြား နံပါတ်များသာ ပါဝင်နိုင်သည်။",
|
||||||
|
"error_text_node_proxy_address": "ကျေးဇူးပြု၍ <IPv4 လိပ်စာ>:<port>၊ ဥပမာ 127.0.0.1:9050 ထည့်ပါ",
|
||||||
"error_text_payment_id": "ငွေပေးချေမှု ID တွင် hex တွင် စာလုံး 16 လုံးမှ 64 လုံးသာ ပါဝင်နိုင်သည်။",
|
"error_text_payment_id": "ငွေပေးချေမှု ID တွင် hex တွင် စာလုံး 16 လုံးမှ 64 လုံးသာ ပါဝင်နိုင်သည်။",
|
||||||
"error_text_xmr": "XMR တန်ဖိုးသည် ရနိုင်သောလက်ကျန်ကို ကျော်လွန်၍မရပါ။\nအပိုင်းကိန်းဂဏန်းများ သည် 12 နှင့် လျော့နည်းရမည်",
|
"error_text_xmr": "XMR တန်ဖိုးသည် ရနိုင်သောလက်ကျန်ကို ကျော်လွန်၍မရပါ။\nအပိုင်းကိန်းဂဏန်းများ သည် 12 နှင့် လျော့နည်းရမည်",
|
||||||
"error_text_fiat": "ပမာဏ၏တန်ဖိုးသည် ရနိုင်သောလက်ကျန်ကို မကျော်လွန်နိုင်ပါ။\nအပိုင်းကိန်းဂဏန်းအရေအတွက်သည် 2 နှင့် လျော့နည်းရမည်",
|
"error_text_fiat": "ပမာဏ၏တန်ဖိုးသည် ရနိုင်သောလက်ကျန်ကို မကျော်လွန်နိုင်ပါ။\nအပိုင်းကိန်းဂဏန်းအရေအတွက်သည် 2 နှင့် လျော့နည်းရမည်",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "အမည်ဖန်တီးပါ။",
|
"generate_name": "အမည်ဖန်တီးပါ။",
|
||||||
"balance_page": "လက်ကျန်စာမျက်နှာ",
|
"balance_page": "လက်ကျန်စာမျက်နှာ",
|
||||||
"share": "မျှဝေပါ။",
|
"share": "မျှဝေပါ။",
|
||||||
"slidable": "လျှောချနိုင်သည်။"
|
"slidable": "လျှောချနိုင်သည်။",
|
||||||
|
"template_name": "နမူနာပုံစံ"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Portemonnee-adres moet overeenkomen met het type\nvan cryptocurrency",
|
"error_text_address": "Portemonnee-adres moet overeenkomen met het type\nvan cryptocurrency",
|
||||||
"error_text_node_address": "Voer een iPv4-adres in",
|
"error_text_node_address": "Voer een iPv4-adres in",
|
||||||
"error_text_node_port": "Knooppuntpoort kan alleen nummers tussen 0 en 65535 bevatten",
|
"error_text_node_port": "Knooppuntpoort kan alleen nummers tussen 0 en 65535 bevatten",
|
||||||
|
"error_text_node_proxy_address": "Voer <IPv4-adres>:<poort> in, bijvoorbeeld 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "Betalings-ID kan alleen 16 tot 64 tekens bevatten in hexadecimale volgorde",
|
"error_text_payment_id": "Betalings-ID kan alleen 16 tot 64 tekens bevatten in hexadecimale volgorde",
|
||||||
"error_text_xmr": "XMR-waarde kan het beschikbare saldo niet overschrijden.\nHet aantal breukcijfers moet kleiner zijn dan of gelijk zijn aan 12",
|
"error_text_xmr": "XMR-waarde kan het beschikbare saldo niet overschrijden.\nHet aantal breukcijfers moet kleiner zijn dan of gelijk zijn aan 12",
|
||||||
"error_text_fiat": "Waarde van bedrag kan het beschikbare saldo niet overschrijden.\nHet aantal breukcijfers moet kleiner zijn dan of gelijk zijn aan 2",
|
"error_text_fiat": "Waarde van bedrag kan het beschikbare saldo niet overschrijden.\nHet aantal breukcijfers moet kleiner zijn dan of gelijk zijn aan 2",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "Naam genereren",
|
"generate_name": "Naam genereren",
|
||||||
"balance_page": "Saldo pagina",
|
"balance_page": "Saldo pagina",
|
||||||
"share": "Deel",
|
"share": "Deel",
|
||||||
"slidable": "Verschuifbaar"
|
"slidable": "Verschuifbaar",
|
||||||
|
"template_name": "Sjabloonnaam"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Adres musi odpowiadać typowi kryptowaluty",
|
"error_text_address": "Adres musi odpowiadać typowi kryptowaluty",
|
||||||
"error_text_node_address": "Wpisz adres iPv4",
|
"error_text_node_address": "Wpisz adres iPv4",
|
||||||
"error_text_node_port": "Port węzła może zawierać tylko liczby od 0 do 65535",
|
"error_text_node_port": "Port węzła może zawierać tylko liczby od 0 do 65535",
|
||||||
|
"error_text_node_proxy_address": "Wprowadź <adres IPv4>:<port>, na przykład 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "ID może zawierać od 16 do 64 znaków w formacie szesnastkowym",
|
"error_text_payment_id": "ID może zawierać od 16 do 64 znaków w formacie szesnastkowym",
|
||||||
"error_text_xmr": "Wartość XMR nie może przekraczać dostępnego salda.\nLiczba cyfr ułamkowych musi być mniejsza lub równa 12",
|
"error_text_xmr": "Wartość XMR nie może przekraczać dostępnego salda.\nLiczba cyfr ułamkowych musi być mniejsza lub równa 12",
|
||||||
"error_text_fiat": "Wartość kwoty nie może przekroczyć dostępnego salda.\nLiczba cyfr ułamkowych musi być mniejsza lub równa 2",
|
"error_text_fiat": "Wartość kwoty nie może przekroczyć dostępnego salda.\nLiczba cyfr ułamkowych musi być mniejsza lub równa 2",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "Wygeneruj nazwę",
|
"generate_name": "Wygeneruj nazwę",
|
||||||
"balance_page": "Strona salda",
|
"balance_page": "Strona salda",
|
||||||
"share": "Udział",
|
"share": "Udział",
|
||||||
"slidable": "Przesuwne"
|
"slidable": "Przesuwne",
|
||||||
|
"template_name": "Nazwa szablonu"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "O endereço da carteira deve corresponder à\ncriptomoeda selecionada",
|
"error_text_address": "O endereço da carteira deve corresponder à\ncriptomoeda selecionada",
|
||||||
"error_text_node_address": "Digite um endereço iPv4",
|
"error_text_node_address": "Digite um endereço iPv4",
|
||||||
"error_text_node_port": "A porta do nó deve conter apenas números entre 0 e 65535",
|
"error_text_node_port": "A porta do nó deve conter apenas números entre 0 e 65535",
|
||||||
|
"error_text_node_proxy_address": "Insira <endereço IPv4>:<porta>, por exemplo 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "O ID de pagamento pode conter apenas de 16 a 64 caracteres em hexadecimal",
|
"error_text_payment_id": "O ID de pagamento pode conter apenas de 16 a 64 caracteres em hexadecimal",
|
||||||
"error_text_xmr": "A quantia em XMR não pode exceder o saldo disponível.\nTO número de dígitos decimais deve ser menor ou igual a 12",
|
"error_text_xmr": "A quantia em XMR não pode exceder o saldo disponível.\nTO número de dígitos decimais deve ser menor ou igual a 12",
|
||||||
"error_text_fiat": "O valor do valor não pode exceder o saldo disponível.\nO número de dígitos decimais deve ser menor ou igual a 2",
|
"error_text_fiat": "O valor do valor não pode exceder o saldo disponível.\nO número de dígitos decimais deve ser menor ou igual a 2",
|
||||||
|
@ -653,5 +654,6 @@
|
||||||
"generate_name": "Gerar nome",
|
"generate_name": "Gerar nome",
|
||||||
"balance_page": "Página de saldo",
|
"balance_page": "Página de saldo",
|
||||||
"share": "Compartilhar",
|
"share": "Compartilhar",
|
||||||
"slidable": "Deslizável"
|
"slidable": "Deslizável",
|
||||||
|
"template_name": "Nome do modelo"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,8 @@
|
||||||
"error_text_address": "Адрес кошелька должен соответствовать типу\nкриптовалюты",
|
"error_text_address": "Адрес кошелька должен соответствовать типу\nкриптовалюты",
|
||||||
"error_text_node_address": "Пожалуйста, введите iPv4 адрес",
|
"error_text_node_address": "Пожалуйста, введите iPv4 адрес",
|
||||||
"error_text_node_port": "Порт ноды может содержать только цифры от 0 до 65535",
|
"error_text_node_port": "Порт ноды может содержать только цифры от 0 до 65535",
|
||||||
|
"error_text_node_proxy_address": "Введите <IPv4-адрес>:<порт>, например 127.0.0.1:9050.",
|
||||||
|
|
||||||
"error_text_payment_id": "Идентификатор платежа может содержать от 16 до 64 символов в hex",
|
"error_text_payment_id": "Идентификатор платежа может содержать от 16 до 64 символов в hex",
|
||||||
"error_text_xmr": "Значение XMR не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 12",
|
"error_text_xmr": "Значение XMR не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 12",
|
||||||
"error_text_fiat": "Значение суммы не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 2",
|
"error_text_fiat": "Значение суммы не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 2",
|
||||||
|
@ -654,5 +656,6 @@
|
||||||
"generate_name": "Создать имя",
|
"generate_name": "Создать имя",
|
||||||
"balance_page": "Страница баланса",
|
"balance_page": "Страница баланса",
|
||||||
"share": "Делиться",
|
"share": "Делиться",
|
||||||
"slidable": "Скользящий"
|
"slidable": "Скользящий",
|
||||||
|
"template_name": "Имя Шаблона"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "ที่อยู่กระเป๋าจะต้องสอดคล้องกับประเภท\nของเหรียญคริปโตเนียม",
|
"error_text_address": "ที่อยู่กระเป๋าจะต้องสอดคล้องกับประเภท\nของเหรียญคริปโตเนียม",
|
||||||
"error_text_node_address": "โปรดป้อนที่อยู่ iPv4",
|
"error_text_node_address": "โปรดป้อนที่อยู่ iPv4",
|
||||||
"error_text_node_port": "พอร์ตโหนดสามารถมีตัวเลขเท่านั้นระหว่าง 0 ถึง 65535",
|
"error_text_node_port": "พอร์ตโหนดสามารถมีตัวเลขเท่านั้นระหว่าง 0 ถึง 65535",
|
||||||
|
"error_text_node_proxy_address": "โปรดป้อน <ที่อยู่ IPv4>:<พอร์ต> เช่น 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "Payment ID สามารถมีขนาดระหว่าง 16 ถึง 64 ตัวอักษรตามแบบ hex",
|
"error_text_payment_id": "Payment ID สามารถมีขนาดระหว่าง 16 ถึง 64 ตัวอักษรตามแบบ hex",
|
||||||
"error_text_xmr": "มูลค่า XMR ไม่สามารถเกินยอดคงเหลือได้\nจำนวนสตริงทศนิยมต้องน้อยกว่าหรือเท่ากับ 12",
|
"error_text_xmr": "มูลค่า XMR ไม่สามารถเกินยอดคงเหลือได้\nจำนวนสตริงทศนิยมต้องน้อยกว่าหรือเท่ากับ 12",
|
||||||
"error_text_fiat": "มูลค่าของจำนวนเงินไม่สามารถเกินยอดคงเหลือได้\nจำนวนสตริงทศนิยมต้องน้อยกว่าหรือเท่ากับ 2",
|
"error_text_fiat": "มูลค่าของจำนวนเงินไม่สามารถเกินยอดคงเหลือได้\nจำนวนสตริงทศนิยมต้องน้อยกว่าหรือเท่ากับ 2",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "สร้างชื่อ",
|
"generate_name": "สร้างชื่อ",
|
||||||
"balance_page": "หน้ายอดคงเหลือ",
|
"balance_page": "หน้ายอดคงเหลือ",
|
||||||
"share": "แบ่งปัน",
|
"share": "แบ่งปัน",
|
||||||
"slidable": "เลื่อนได้"
|
"slidable": "เลื่อนได้",
|
||||||
|
"template_name": "ชื่อแม่แบบ"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Cüzdan adresi kripto para biriminin\ntürüne karşılık gelmelidir",
|
"error_text_address": "Cüzdan adresi kripto para biriminin\ntürüne karşılık gelmelidir",
|
||||||
"error_text_node_address": "Lütfen iPv4 adresi girin",
|
"error_text_node_address": "Lütfen iPv4 adresi girin",
|
||||||
"error_text_node_port": "Düğüm port'u yalnızca 0 ve 65535 arasında rakam içerebilir",
|
"error_text_node_port": "Düğüm port'u yalnızca 0 ve 65535 arasında rakam içerebilir",
|
||||||
|
"error_text_node_proxy_address": "Lütfen <IPv4 adresi>:<bağlantı noktası> girin, örneğin 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "Ödeme ID'si yalnızca onaltılık (hex) olarak 16 veya 64 karakter içerebilir",
|
"error_text_payment_id": "Ödeme ID'si yalnızca onaltılık (hex) olarak 16 veya 64 karakter içerebilir",
|
||||||
"error_text_xmr": "XMR miktarı kullanılabilir bakiyeyi aşamaz.\nKesir basamaklarının sayısı 12'ye eşit veya daha az olmalıdır",
|
"error_text_xmr": "XMR miktarı kullanılabilir bakiyeyi aşamaz.\nKesir basamaklarının sayısı 12'ye eşit veya daha az olmalıdır",
|
||||||
"error_text_fiat": "Tutarın değeri, mevcut bakiyeyi aşamaz.\nKesir basamaklarının sayısı 2'ye eşit veya daha az olmalıdır",
|
"error_text_fiat": "Tutarın değeri, mevcut bakiyeyi aşamaz.\nKesir basamaklarının sayısı 2'ye eşit veya daha az olmalıdır",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "İsim Oluştur",
|
"generate_name": "İsim Oluştur",
|
||||||
"balance_page": "Bakiye Sayfası",
|
"balance_page": "Bakiye Sayfası",
|
||||||
"share": "Paylaşmak",
|
"share": "Paylaşmak",
|
||||||
"slidable": "kaydırılabilir"
|
"slidable": "kaydırılabilir",
|
||||||
|
"template_name": "şablon adı"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Адреса гаманця повинна відповідати типу\nкриптовалюти",
|
"error_text_address": "Адреса гаманця повинна відповідати типу\nкриптовалюти",
|
||||||
"error_text_node_address": "Будь ласка, введіть iPv4 адресу",
|
"error_text_node_address": "Будь ласка, введіть iPv4 адресу",
|
||||||
"error_text_node_port": "Порт вузла може містити тільки цифри від 0 до 65535",
|
"error_text_node_port": "Порт вузла може містити тільки цифри від 0 до 65535",
|
||||||
|
"error_text_node_proxy_address": "Будь ласка, введіть <IPv4-адреса>:<порт>, наприклад 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "Ідентифікатор платежу може містити від 16 до 64 символів в hex",
|
"error_text_payment_id": "Ідентифікатор платежу може містити від 16 до 64 символів в hex",
|
||||||
"error_text_xmr": "Значення XMR не може перевищувати доступний баланс.\nКількість цифр після коми повинно бути меншим або дорівнювати 12",
|
"error_text_xmr": "Значення XMR не може перевищувати доступний баланс.\nКількість цифр після коми повинно бути меншим або дорівнювати 12",
|
||||||
"error_text_fiat": "Значення суми не може перевищувати доступний баланс.\nКількість цифр після коми повинно бути меншим або дорівнювати 2",
|
"error_text_fiat": "Значення суми не може перевищувати доступний баланс.\nКількість цифр після коми повинно бути меншим або дорівнювати 2",
|
||||||
|
@ -654,5 +655,6 @@
|
||||||
"generate_name": "Згенерувати назву",
|
"generate_name": "Згенерувати назву",
|
||||||
"balance_page": "Сторінка балансу",
|
"balance_page": "Сторінка балансу",
|
||||||
"share": "Поділіться",
|
"share": "Поділіться",
|
||||||
"slidable": "Розсувний"
|
"slidable": "Розсувний",
|
||||||
|
"template_name": "Назва шаблону"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "والیٹ کا پتہ cryptocurrency کی قسم\\nکے مطابق ہونا چاہیے۔",
|
"error_text_address": "والیٹ کا پتہ cryptocurrency کی قسم\\nکے مطابق ہونا چاہیے۔",
|
||||||
"error_text_node_address": "براہ کرم ایک iPv4 پتہ درج کریں۔",
|
"error_text_node_address": "براہ کرم ایک iPv4 پتہ درج کریں۔",
|
||||||
"error_text_node_port": "نوڈ پورٹ میں صرف 0 اور 65535 کے درمیان نمبر ہوسکتے ہیں۔",
|
"error_text_node_port": "نوڈ پورٹ میں صرف 0 اور 65535 کے درمیان نمبر ہوسکتے ہیں۔",
|
||||||
|
"error_text_node_proxy_address": "براہ کرم <IPv4 ایڈریس>:<port> درج کریں، مثال کے طور پر 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "ادائیگی کی ID ہیکس میں صرف 16 سے 64 حروف پر مشتمل ہو سکتی ہے۔",
|
"error_text_payment_id": "ادائیگی کی ID ہیکس میں صرف 16 سے 64 حروف پر مشتمل ہو سکتی ہے۔",
|
||||||
"error_text_xmr": "XMR قدر دستیاب بیلنس سے زیادہ نہیں ہو سکتی۔\\nفرکشن ہندسوں کی تعداد 12 سے کم یا اس کے برابر ہونی چاہیے۔",
|
"error_text_xmr": "XMR قدر دستیاب بیلنس سے زیادہ نہیں ہو سکتی۔\\nفرکشن ہندسوں کی تعداد 12 سے کم یا اس کے برابر ہونی چاہیے۔",
|
||||||
"error_text_fiat": "رقم کی قدر دستیاب بیلنس سے زیادہ نہیں ہو سکتی۔\\nفرکشن ہندسوں کی تعداد 2 کے برابر یا کم ہونی چاہیے۔",
|
"error_text_fiat": "رقم کی قدر دستیاب بیلنس سے زیادہ نہیں ہو سکتی۔\\nفرکشن ہندسوں کی تعداد 2 کے برابر یا کم ہونی چاہیے۔",
|
||||||
|
@ -648,5 +649,6 @@
|
||||||
"generate_name": "نام پیدا کریں۔",
|
"generate_name": "نام پیدا کریں۔",
|
||||||
"balance_page": "بیلنس صفحہ",
|
"balance_page": "بیلنس صفحہ",
|
||||||
"share": "بانٹیں",
|
"share": "بانٹیں",
|
||||||
"slidable": "سلائیڈ ایبل"
|
"slidable": "سلائیڈ ایبل",
|
||||||
|
"template_name": "ٹیمپلیٹ کا نام"
|
||||||
}
|
}
|
||||||
|
|
|
@ -250,8 +250,8 @@
|
||||||
"transaction_details_recipient_address": "Àwọn àdírẹ́sì olùgbà",
|
"transaction_details_recipient_address": "Àwọn àdírẹ́sì olùgbà",
|
||||||
"wallet_list_title": "Àpamọ́wọ́ Monero",
|
"wallet_list_title": "Àpamọ́wọ́ Monero",
|
||||||
"wallet_list_create_new_wallet": "Ṣe àpamọ́wọ́ títun",
|
"wallet_list_create_new_wallet": "Ṣe àpamọ́wọ́ títun",
|
||||||
"wallet_list_edit_wallet": "Ṣatunkọ apamọwọ",
|
"wallet_list_edit_wallet" : "Ṣatunkọ apamọwọ",
|
||||||
"wallet_list_wallet_name": "Orukọ apamọwọ",
|
"wallet_list_wallet_name" : "Orukọ apamọwọ",
|
||||||
"wallet_list_restore_wallet": "Restore àpamọ́wọ́",
|
"wallet_list_restore_wallet": "Restore àpamọ́wọ́",
|
||||||
"wallet_list_load_wallet": "Load àpamọ́wọ́",
|
"wallet_list_load_wallet": "Load àpamọ́wọ́",
|
||||||
"wallet_list_loading_wallet": "Ń ṣí àpamọ́wọ́ ${wallet_name}",
|
"wallet_list_loading_wallet": "Ń ṣí àpamọ́wọ́ ${wallet_name}",
|
||||||
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "Àdírẹ́sì àpamọ́wọ́ gbọ́dọ̀ báramu irú owó",
|
"error_text_address": "Àdírẹ́sì àpamọ́wọ́ gbọ́dọ̀ báramu irú owó",
|
||||||
"error_text_node_address": "Ẹ jọ̀wọ́ tẹ̀ àdírẹ́sì iPv4",
|
"error_text_node_address": "Ẹ jọ̀wọ́ tẹ̀ àdírẹ́sì iPv4",
|
||||||
"error_text_node_port": "Ojú ìkànpọ̀ apẹka lè ni nìkan nínú òǹkà l'áàárín òdo àti márùn-úndínlógojí lé ní ẹ̀ẹ́dẹgbẹ̀ta lé ní ọ̀kẹ́ mẹ́ta lé ní ẹ̀ẹ́dẹ́gbàta",
|
"error_text_node_port": "Ojú ìkànpọ̀ apẹka lè ni nìkan nínú òǹkà l'áàárín òdo àti márùn-úndínlógojí lé ní ẹ̀ẹ́dẹgbẹ̀ta lé ní ọ̀kẹ́ mẹ́ta lé ní ẹ̀ẹ́dẹ́gbàta",
|
||||||
|
"error_text_node_proxy_address": "Jọwọ tẹ <IPv4 adirẹsi>:<port>, fun apẹẹrẹ 127.0.0.1:9050",
|
||||||
"error_text_payment_id": "Iye ẹyọ ọ̀rọ̀ nínú àmì ìdánimọ̀ àránṣẹ́ gbọ́dọ̀ wà l'áàárín aárùndínlógún dé ẹẹ́rinlélọ́gọ́ta.",
|
"error_text_payment_id": "Iye ẹyọ ọ̀rọ̀ nínú àmì ìdánimọ̀ àránṣẹ́ gbọ́dọ̀ wà l'áàárín aárùndínlógún dé ẹẹ́rinlélọ́gọ́ta.",
|
||||||
"error_text_xmr": "Iye XMR kò lè tóbi ju ìyókù.\nIye díjíìtì léyìn ẹsẹ kò gbọ́dọ̀ tóbi ju eéjìlá.",
|
"error_text_xmr": "Iye XMR kò lè tóbi ju ìyókù.\nIye díjíìtì léyìn ẹsẹ kò gbọ́dọ̀ tóbi ju eéjìlá.",
|
||||||
"error_text_fiat": "Iye àránṣẹ́ kò tóbi ju ìyókù owó.\nIye díjíìtì léyìn ẹsẹ kò gbọ́dọ̀ tóbi ju eéjì.",
|
"error_text_fiat": "Iye àránṣẹ́ kò tóbi ju ìyókù owó.\nIye díjíìtì léyìn ẹsẹ kò gbọ́dọ̀ tóbi ju eéjì.",
|
||||||
|
@ -650,5 +651,6 @@
|
||||||
"generate_name": "Ṣẹda Orukọ",
|
"generate_name": "Ṣẹda Orukọ",
|
||||||
"balance_page": "Oju-iwe iwọntunwọnsi",
|
"balance_page": "Oju-iwe iwọntunwọnsi",
|
||||||
"share": "Pinpin",
|
"share": "Pinpin",
|
||||||
"slidable": "Slidable"
|
"slidable": "Slidable",
|
||||||
|
"template_name": "Orukọ Awoṣe"
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,6 +269,7 @@
|
||||||
"error_text_address": "钱包地址必须与类型对应\n加密货币",
|
"error_text_address": "钱包地址必须与类型对应\n加密货币",
|
||||||
"error_text_node_address": "请输入一个IPv4地址",
|
"error_text_node_address": "请输入一个IPv4地址",
|
||||||
"error_text_node_port": "节点端口只能包含0到65535之间的数字",
|
"error_text_node_port": "节点端口只能包含0到65535之间的数字",
|
||||||
|
"error_text_node_proxy_address": "请输入<IPv4地址>:<端口>,例如127.0.0.1:9050",
|
||||||
"error_text_payment_id": "付款ID只能包含16到64个字符(十六进制)",
|
"error_text_payment_id": "付款ID只能包含16到64个字符(十六进制)",
|
||||||
"error_text_xmr": "XMR值不能超过可用余额.\n小数位数必须小于或等于12",
|
"error_text_xmr": "XMR值不能超过可用余额.\n小数位数必须小于或等于12",
|
||||||
"error_text_fiat": "金额不能超过可用余额.\n小数位数必须小于或等于2",
|
"error_text_fiat": "金额不能超过可用余额.\n小数位数必须小于或等于2",
|
||||||
|
@ -653,5 +654,6 @@
|
||||||
"generate_name": "生成名称",
|
"generate_name": "生成名称",
|
||||||
"balance_page": "余额页",
|
"balance_page": "余额页",
|
||||||
"share": "分享",
|
"share": "分享",
|
||||||
"slidable": "可滑动"
|
"slidable": "可滑动",
|
||||||
|
"template_name": "模板名称"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue