mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-11-17 01:37:40 +00:00
Merge branch 'main' of https://github.com/cake-tech/cake_wallet into CW-239-fix-restore-from-backup-using-6-digit-pin
Conflicts: lib/store/settings_store.dart
This commit is contained in:
commit
4140742bb9
57 changed files with 753 additions and 350 deletions
|
@ -1,31 +1,24 @@
|
|||
-
|
||||
uri: xmr-node-uk.cakewallet.com:18081
|
||||
uri: xmr-node.cakewallet.com:18081
|
||||
is_default: true
|
||||
-
|
||||
uri: xmr-node-eu.cakewallet.com:18081
|
||||
uri: cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081
|
||||
is_default: false
|
||||
-
|
||||
uri: xmr-node-usa-east.cakewallet.com:18081
|
||||
uri: node.sethforprivacy.com:18089
|
||||
is_default: false
|
||||
-
|
||||
uri: nodes.hashvault.pro:18081
|
||||
is_default: false
|
||||
-
|
||||
uri: node.c3pool.com:18081
|
||||
is_default: false
|
||||
-
|
||||
uri: node.supportxmr.com:18081
|
||||
is_default: false
|
||||
-
|
||||
uri: node.community.rino.io:18081
|
||||
is_default: false
|
||||
-
|
||||
uri: node.moneroworld.com:18089
|
||||
is_default: false
|
||||
-
|
||||
uri: node.xmr.pt:18081
|
||||
is_default: false
|
||||
-
|
||||
uri: node.monero.net:18081
|
||||
is_default: false
|
||||
-
|
||||
uri: opennode.xmr-tw.org:18089
|
||||
is_default: false
|
||||
-
|
||||
uri: node.imonero.org:18081
|
||||
is_default: false
|
||||
|
||||
uri: node.c3pool.com:18081
|
||||
is_default: false
|
||||
|
||||
uri: xmr.prprpr.icu:18081
|
||||
is_default: false
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ class Node extends HiveObject with Keyable {
|
|||
{this.login,
|
||||
this.password,
|
||||
this.useSSL,
|
||||
this.trusted = false,
|
||||
String? uri,
|
||||
WalletType? type,}) {
|
||||
if (uri != null) {
|
||||
|
@ -31,7 +32,8 @@ class Node extends HiveObject with Keyable {
|
|||
: uriRaw = map['uri'] as String? ?? '',
|
||||
login = map['login'] as String?,
|
||||
password = map['password'] as String?,
|
||||
useSSL = map['useSSL'] as bool?;
|
||||
useSSL = map['useSSL'] as bool?,
|
||||
trusted = map['trusted'] as bool? ?? false;
|
||||
|
||||
static const typeId = 1;
|
||||
static const boxName = 'Nodes';
|
||||
|
@ -51,6 +53,9 @@ class Node extends HiveObject with Keyable {
|
|||
@HiveField(4)
|
||||
bool? useSSL;
|
||||
|
||||
@HiveField(5)
|
||||
bool trusted;
|
||||
|
||||
bool get isSSL => useSSL ?? false;
|
||||
|
||||
Uri get uri {
|
||||
|
@ -104,28 +109,28 @@ class Node extends HiveObject with Keyable {
|
|||
final rpcUri = isSSL ? Uri.https(uri.authority, path) : Uri.http(uri.authority, path);
|
||||
final realm = 'monero-rpc';
|
||||
final body = {
|
||||
'jsonrpc': '2.0',
|
||||
'id': '0',
|
||||
'jsonrpc': '2.0',
|
||||
'id': '0',
|
||||
'method': 'get_info'
|
||||
};
|
||||
|
||||
try {
|
||||
final authenticatingClient = HttpClient();
|
||||
|
||||
|
||||
authenticatingClient.addCredentials(
|
||||
rpcUri,
|
||||
realm,
|
||||
realm,
|
||||
HttpClientDigestCredentials(login ?? '', password ?? ''),
|
||||
);
|
||||
|
||||
|
||||
final http.Client client = ioc.IOClient(authenticatingClient);
|
||||
|
||||
|
||||
final response = await client.post(
|
||||
rpcUri,
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: json.encode(body),
|
||||
);
|
||||
|
||||
|
||||
client.close();
|
||||
|
||||
final resBody = json.decode(response.body) as Map<String, dynamic>;
|
||||
|
|
|
@ -927,6 +927,16 @@ extern "C"
|
|||
return static_cast<int32_t>(rates.size());
|
||||
}
|
||||
|
||||
void set_trusted_daemon(bool arg)
|
||||
{
|
||||
m_wallet->setTrustedDaemon(arg);
|
||||
}
|
||||
|
||||
bool trusted_daemon()
|
||||
{
|
||||
return m_wallet->trustedDaemon();
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -137,4 +137,8 @@ typedef get_rate = Pointer<Int64> Function();
|
|||
|
||||
typedef size_of_rate = Int32 Function();
|
||||
|
||||
typedef update_rate = Void Function();
|
||||
typedef update_rate = Void Function();
|
||||
|
||||
typedef set_trusted_daemon = Void Function(Int8 trusted);
|
||||
|
||||
typedef trusted_daemon = Int8 Function();
|
|
@ -135,4 +135,8 @@ typedef GetRate = Pointer<Int64> Function();
|
|||
|
||||
typedef SizeOfRate = int Function();
|
||||
|
||||
typedef UpdateRate = void Function();
|
||||
typedef UpdateRate = void Function();
|
||||
|
||||
typedef SetTrustedDaemon = void Function(int);
|
||||
|
||||
typedef TrustedDaemon = int Function();
|
|
@ -116,6 +116,14 @@ final rescanBlockchainAsyncNative = havenApi
|
|||
.lookup<NativeFunction<rescan_blockchain>>('rescan_blockchain')
|
||||
.asFunction<RescanBlockchainAsync>();
|
||||
|
||||
final setTrustedDaemonNative = havenApi
|
||||
.lookup<NativeFunction<set_trusted_daemon>>('set_trusted_daemon')
|
||||
.asFunction<SetTrustedDaemon>();
|
||||
|
||||
final trustedDaemonNative = havenApi
|
||||
.lookup<NativeFunction<trusted_daemon>>('trusted_daemon')
|
||||
.asFunction<TrustedDaemon>();
|
||||
|
||||
int getSyncingHeight() => getSyncingHeightNative();
|
||||
|
||||
bool isNeededToRefresh() => isNeededToRefreshNative() != 0;
|
||||
|
@ -351,3 +359,7 @@ Future<bool> isConnected() => compute(_isConnected, 0);
|
|||
Future<int> getNodeHeight() => compute(_getNodeHeight, 0);
|
||||
|
||||
void rescanBlockchainAsync() => rescanBlockchainAsyncNative();
|
||||
|
||||
Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted));
|
||||
|
||||
Future<bool> trustedDaemon() async => trustedDaemonNative() != 0;
|
||||
|
|
|
@ -121,6 +121,8 @@ abstract class HavenWalletBase extends WalletBase<MoneroBalance,
|
|||
password: node.password,
|
||||
useSSL: node.useSSL ?? false,
|
||||
isLightWallet: false); // FIXME: hardcoded value
|
||||
|
||||
haven_wallet.setTrustedDaemon(node.trusted);
|
||||
syncStatus = ConnectedSyncStatus();
|
||||
} catch (e) {
|
||||
syncStatus = FailedSyncStatus();
|
||||
|
|
|
@ -783,6 +783,16 @@ extern "C"
|
|||
return strdup(get_current_wallet()->getSubaddressLabel(accountIndex, addressIndex).c_str());
|
||||
}
|
||||
|
||||
void set_trusted_daemon(bool arg)
|
||||
{
|
||||
m_wallet->setTrustedDaemon(arg);
|
||||
}
|
||||
|
||||
bool trusted_daemon()
|
||||
{
|
||||
return m_wallet->trustedDaemon();
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -30,6 +30,9 @@ void set_refresh_from_block_height(uint64_t height);
|
|||
void set_recovering_from_seed(bool is_recovery);
|
||||
void store(char *path);
|
||||
|
||||
void set_trusted_daemon(bool arg);
|
||||
bool trusted_daemon();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -125,4 +125,8 @@ typedef rescan_blockchain = Void Function();
|
|||
|
||||
typedef get_subaddress_label = Pointer<Utf8> Function(
|
||||
Int32 accountIndex,
|
||||
Int32 addressIndex);
|
||||
Int32 addressIndex);
|
||||
|
||||
typedef set_trusted_daemon = Void Function(Int8 trusted);
|
||||
|
||||
typedef trusted_daemon = Int8 Function();
|
|
@ -123,4 +123,8 @@ typedef RescanBlockchainAsync = void Function();
|
|||
|
||||
typedef GetSubaddressLabel = Pointer<Utf8> Function(
|
||||
int accountIndex,
|
||||
int addressIndex);
|
||||
int addressIndex);
|
||||
|
||||
typedef SetTrustedDaemon = void Function(int);
|
||||
|
||||
typedef TrustedDaemon = int Function();
|
|
@ -120,6 +120,14 @@ final getSubaddressLabelNative = moneroApi
|
|||
.lookup<NativeFunction<get_subaddress_label>>('get_subaddress_label')
|
||||
.asFunction<GetSubaddressLabel>();
|
||||
|
||||
final setTrustedDaemonNative = moneroApi
|
||||
.lookup<NativeFunction<set_trusted_daemon>>('set_trusted_daemon')
|
||||
.asFunction<SetTrustedDaemon>();
|
||||
|
||||
final trustedDaemonNative = moneroApi
|
||||
.lookup<NativeFunction<trusted_daemon>>('trusted_daemon')
|
||||
.asFunction<TrustedDaemon>();
|
||||
|
||||
int getSyncingHeight() => getSyncingHeightNative();
|
||||
|
||||
bool isNeededToRefresh() => isNeededToRefreshNative() != 0;
|
||||
|
@ -359,4 +367,8 @@ void rescanBlockchainAsync() => rescanBlockchainAsyncNative();
|
|||
|
||||
String getSubaddressLabel(int accountIndex, int addressIndex) {
|
||||
return convertUTF8ToString(pointer: getSubaddressLabelNative(accountIndex, addressIndex));
|
||||
}
|
||||
}
|
||||
|
||||
Future setTrustedDaemon(bool trusted) async => setTrustedDaemonNative(_boolToInt(trusted));
|
||||
|
||||
Future<bool> trustedDaemon() async => trustedDaemonNative() != 0;
|
|
@ -136,6 +136,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
|
|||
password: node.password,
|
||||
useSSL: node.isSSL,
|
||||
isLightWallet: false); // FIXME: hardcoded value
|
||||
|
||||
monero_wallet.setTrustedDaemon(node.trusted);
|
||||
syncStatus = ConnectedSyncStatus();
|
||||
} catch (e) {
|
||||
syncStatus = FailedSyncStatus();
|
||||
|
|
42
lib/di.dart
42
lib/di.dart
|
@ -35,6 +35,7 @@ import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart
|
|||
import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||
import 'package:cw_core/unspent_coins_info.dart';
|
||||
import 'package:cake_wallet/core/backup_service.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
|
@ -159,6 +160,7 @@ import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
|
|||
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
|
||||
import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart';
|
||||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
final getIt = GetIt.instance;
|
||||
|
||||
|
@ -349,7 +351,7 @@ Future setup(
|
|||
onAuthenticationFinished: onAuthFinished,
|
||||
closable: closable ?? false));
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
getIt.registerFactory(() =>
|
||||
BalancePage(dashboardViewModel: getIt.get<DashboardViewModel>(), settingsStore: getIt.get<SettingsStore>()));
|
||||
|
||||
getIt.registerFactory<DashboardPage>(() => DashboardPage( balancePage: getIt.get<BalancePage>(), walletViewModel: getIt.get<DashboardViewModel>(), addressListViewModel: getIt.get<WalletAddressListViewModel>()));
|
||||
|
@ -471,12 +473,11 @@ Future setup(
|
|||
(ContactRecord? contact, _) =>
|
||||
ContactViewModel(_contactSource, contact: contact));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => ContactListViewModel(_contactSource, _walletInfoSource));
|
||||
getIt.registerFactoryParam<ContactListViewModel, CryptoCurrency?, void>(
|
||||
(CryptoCurrency? cur, _) => ContactListViewModel(_contactSource, _walletInfoSource, cur));
|
||||
|
||||
getIt.registerFactoryParam<ContactListPage, bool, void>(
|
||||
(bool isEditable, _) => ContactListPage(getIt.get<ContactListViewModel>(),
|
||||
isEditable: isEditable));
|
||||
getIt.registerFactoryParam<ContactListPage, CryptoCurrency?, void>((CryptoCurrency? cur, _)
|
||||
=> ContactListPage(getIt.get<ContactListViewModel>(param1: cur)));
|
||||
|
||||
getIt.registerFactoryParam<ContactPage, ContactRecord?, void>(
|
||||
(ContactRecord? contact, _) =>
|
||||
|
@ -498,8 +499,12 @@ Future setup(
|
|||
|
||||
getIt.registerFactory(() => OtherSettingsPage(getIt.get<OtherSettingsViewModel>()));
|
||||
|
||||
getIt.registerFactory(() =>
|
||||
NodeCreateOrEditViewModel(_nodeSource, getIt.get<AppStore>().wallet!));
|
||||
getIt.registerFactoryParam<NodeCreateOrEditViewModel, WalletType?, void>(
|
||||
(WalletType? type, _) => NodeCreateOrEditViewModel(
|
||||
_nodeSource,
|
||||
type ?? getIt.get<AppStore>().wallet!.type,
|
||||
getIt.get<SettingsStore>(),
|
||||
));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => NodeCreateOrEditPage(getIt.get<NodeCreateOrEditViewModel>()));
|
||||
|
@ -698,7 +703,7 @@ Future setup(
|
|||
|
||||
getIt.registerFactoryParam<FullscreenQRPage, String, bool>(
|
||||
(String qrData, bool isLight) => FullscreenQRPage(qrData: qrData, isLight: isLight,));
|
||||
|
||||
|
||||
getIt.registerFactory(() => IoniaApi());
|
||||
|
||||
getIt.registerFactory(() => AnyPayApi());
|
||||
|
@ -718,7 +723,7 @@ Future setup(
|
|||
|
||||
getIt.registerFactoryParam<IoniaMerchPurchaseViewModel, double, IoniaMerchant>((double amount, merchant) {
|
||||
return IoniaMerchPurchaseViewModel(
|
||||
ioniaAnyPayService: getIt.get<IoniaAnyPay>(),
|
||||
ioniaAnyPayService: getIt.get<IoniaAnyPay>(),
|
||||
amount: amount,
|
||||
ioniaMerchant: merchant,
|
||||
sendViewModel: getIt.get<SendViewModel>()
|
||||
|
@ -761,31 +766,31 @@ Future setup(
|
|||
ioniaService: getIt.get<IoniaService>(),
|
||||
giftCard: giftCard);
|
||||
});
|
||||
|
||||
|
||||
getIt.registerFactoryParam<IoniaCustomTipViewModel, List, void>((List args, _) {
|
||||
final amount = args[0] as double;
|
||||
final merchant = args[1] as IoniaMerchant;
|
||||
final tip = args[2] as IoniaTip;
|
||||
|
||||
|
||||
return IoniaCustomTipViewModel(amount: amount, tip: tip, ioniaMerchant: merchant);
|
||||
});
|
||||
|
||||
|
||||
getIt.registerFactoryParam<IoniaGiftCardDetailPage, IoniaGiftCard, void>((IoniaGiftCard giftCard, _) {
|
||||
return IoniaGiftCardDetailPage(getIt.get<IoniaGiftCardDetailsViewModel>(param1: giftCard));
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<IoniaMoreOptionsPage, List, void>((List args, _){
|
||||
final giftCard = args.first as IoniaGiftCard;
|
||||
|
||||
return IoniaMoreOptionsPage(giftCard);
|
||||
|
||||
return IoniaMoreOptionsPage(giftCard);
|
||||
});
|
||||
|
||||
getIt.registerFactoryParam<IoniaCustomRedeemViewModel, IoniaGiftCard, void>((IoniaGiftCard giftCard, _) => IoniaCustomRedeemViewModel(giftCard));
|
||||
|
||||
getIt.registerFactoryParam<IoniaCustomRedeemPage, List, void>((List args, _){
|
||||
final giftCard = args.first as IoniaGiftCard;
|
||||
|
||||
return IoniaCustomRedeemPage(getIt.get<IoniaCustomRedeemViewModel>(param1: giftCard) );
|
||||
|
||||
return IoniaCustomRedeemPage(getIt.get<IoniaCustomRedeemViewModel>(param1: giftCard) );
|
||||
});
|
||||
|
||||
|
||||
|
@ -814,5 +819,8 @@ Future setup(
|
|||
(IoniaAnyPayPaymentInfo paymentInfo, AnyPayPaymentCommittedInfo committedInfo)
|
||||
=> IoniaPaymentStatusPage(getIt.get<IoniaPaymentStatusViewModel>(param1: paymentInfo, param2: committedInfo)));
|
||||
|
||||
getIt.registerFactoryParam<AdvancedPrivacySettingsViewModel, WalletType, void>((type, _) =>
|
||||
AdvancedPrivacySettingsViewModel(type, getIt.get<SettingsStore>()));
|
||||
|
||||
_isSetupFinished = true;
|
||||
}
|
||||
|
|
|
@ -69,6 +69,8 @@ Future defaultSettingsMigration(
|
|||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
await changeLitecoinCurrentElectrumServerToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
await changeHavenCurrentNodeToDefault(
|
||||
sharedPreferences: sharedPreferences, nodes: nodes);
|
||||
|
||||
break;
|
||||
case 2:
|
||||
|
@ -133,19 +135,32 @@ Future defaultSettingsMigration(
|
|||
await changeDefaultHavenNode(nodes);
|
||||
break;
|
||||
|
||||
case 18:
|
||||
await addOnionNode(nodes);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
await sharedPreferences.setInt(
|
||||
'current_default_settings_migration_version', version);
|
||||
PreferencesKey.currentDefaultSettingsMigrationVersion, version);
|
||||
} catch (e) {
|
||||
print('Migration error: ${e.toString()}');
|
||||
}
|
||||
});
|
||||
|
||||
await sharedPreferences.setInt(
|
||||
'current_default_settings_migration_version', version);
|
||||
PreferencesKey.currentDefaultSettingsMigrationVersion, version);
|
||||
}
|
||||
|
||||
Future<void> addOnionNode(Box<Node> nodes) async {
|
||||
final onionNodeUri = "cakexmrl7bonq7ovjka5kuwuyd3f7qnkz6z6s6dmsy3uckwra7bvggyd.onion:18081";
|
||||
|
||||
// check if the user has this node before (added it manually)
|
||||
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == onionNodeUri) == null) {
|
||||
await nodes.add(Node(uri: onionNodeUri, type: WalletType.monero));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> replaceNodesMigration({required Box<Node> nodes}) async {
|
||||
|
@ -176,7 +191,7 @@ Future<void> changeMoneroCurrentNodeToDefault(
|
|||
final node = getMoneroDefaultNode(nodes: nodes);
|
||||
final nodeId = node?.key as int ?? 0; // 0 - England
|
||||
|
||||
await sharedPreferences.setInt('current_node_id', nodeId);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, nodeId);
|
||||
}
|
||||
|
||||
Node? getBitcoinDefaultElectrumServer({required Box<Node> nodes}) {
|
||||
|
@ -223,7 +238,7 @@ Future<void> changeBitcoinCurrentElectrumServerToDefault(
|
|||
final server = getBitcoinDefaultElectrumServer(nodes: nodes);
|
||||
final serverId = server?.key as int ?? 0;
|
||||
|
||||
await sharedPreferences.setInt('current_node_id_btc', serverId);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentBitcoinElectrumSererIdKey, serverId);
|
||||
}
|
||||
|
||||
Future<void> changeLitecoinCurrentElectrumServerToDefault(
|
||||
|
@ -232,7 +247,7 @@ Future<void> changeLitecoinCurrentElectrumServerToDefault(
|
|||
final server = getLitecoinDefaultElectrumServer(nodes: nodes);
|
||||
final serverId = server?.key as int ?? 0;
|
||||
|
||||
await sharedPreferences.setInt('current_node_id_ltc', serverId);
|
||||
await sharedPreferences.setInt(PreferencesKey.currentLitecoinElectrumSererIdKey, serverId);
|
||||
}
|
||||
|
||||
Future<void> changeHavenCurrentNodeToDefault(
|
||||
|
@ -252,7 +267,7 @@ Future<void> replaceDefaultNode(
|
|||
'eu-node.cakewallet.io:18081',
|
||||
'node.cakewallet.io:18081'
|
||||
];
|
||||
final currentNodeId = sharedPreferences.getInt('current_node_id');
|
||||
final currentNodeId = sharedPreferences.getInt(PreferencesKey.currentNodeIdKey);
|
||||
final currentNode =
|
||||
nodes.values.firstWhereOrNull((Node node) => node.key == currentNodeId);
|
||||
final needToReplace =
|
||||
|
@ -277,17 +292,29 @@ Future<void> updateNodeTypes({required Box<Node> nodes}) async {
|
|||
|
||||
Future<void> addBitcoinElectrumServerList({required Box<Node> nodes}) async {
|
||||
final serverList = await loadBitcoinElectrumServerList();
|
||||
await nodes.addAll(serverList);
|
||||
for (var node in serverList) {
|
||||
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||
await nodes.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addLitecoinElectrumServerList({required Box<Node> nodes}) async {
|
||||
final serverList = await loadLitecoinElectrumServerList();
|
||||
await nodes.addAll(serverList);
|
||||
for (var node in serverList) {
|
||||
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||
await nodes.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addHavenNodeList({required Box<Node> nodes}) async {
|
||||
final nodeList = await loadDefaultHavenNodes();
|
||||
await nodes.addAll(nodeList);
|
||||
for (var node in nodeList) {
|
||||
if (nodes.values.firstWhereOrNull((element) => element.uriRaw == node.uriRaw) == null) {
|
||||
await nodes.add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addAddressesForMoneroWallets(
|
||||
|
@ -431,7 +458,7 @@ Future<void> resetBitcoinElectrumServer(
|
|||
final oldElectrumServer = nodeSource.values.firstWhereOrNull(
|
||||
(node) => node.uri.toString().contains('electrumx.cakewallet.com'));
|
||||
var cakeWalletNode = nodeSource.values.firstWhereOrNull(
|
||||
(node) => node.uri.toString() == cakeWalletBitcoinElectrumUri);
|
||||
(node) => node.uriRaw.toString() == cakeWalletBitcoinElectrumUri);
|
||||
|
||||
if (cakeWalletNode == null) {
|
||||
cakeWalletNode =
|
||||
|
|
|
@ -4,7 +4,6 @@ import 'package:cake_wallet/entities/parsed_address.dart';
|
|||
import 'package:cake_wallet/entities/unstoppable_domain_address.dart';
|
||||
import 'package:cake_wallet/entities/emoji_string_extension.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cake_wallet/entities/fio_address_provider.dart';
|
||||
|
||||
class AddressResolver {
|
||||
|
@ -51,7 +50,7 @@ class AddressResolver {
|
|||
return ParsedAddress(addresses: [text]);
|
||||
}
|
||||
|
||||
if (unstoppableDomains.any((domain) => name.contains(domain))) {
|
||||
if (unstoppableDomains.any((domain) => name.trim() == domain)) {
|
||||
final address = await fetchUnstoppableDomainAddress(text, ticker);
|
||||
return ParsedAddress.fetchUnstoppableDomainAddress(address: address, name: text);
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import 'package:cake_wallet/exchange/trade_request.dart';
|
|||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/exchange/changenow/changenow_request.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/trade_not_created_exeption.dart';
|
||||
|
||||
class ChangeNowExchangeProvider extends ExchangeProvider {
|
||||
ChangeNowExchangeProvider()
|
||||
|
@ -21,8 +20,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
.where((i) => i != CryptoCurrency.xhv)
|
||||
.map((i) => CryptoCurrency.all
|
||||
.where((i) => i != CryptoCurrency.xhv)
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true))
|
||||
.where((c) => c != null))
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true)))
|
||||
.expand((i) => i)
|
||||
.toList());
|
||||
|
||||
|
@ -43,6 +41,9 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => true;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description =>
|
||||
ExchangeProviderDescription.changeNow;
|
||||
|
@ -96,25 +97,36 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
apiHeaderKey: apiKey,
|
||||
'Content-Type': 'application/json'};
|
||||
final flow = getFlow(isFixedRateMode);
|
||||
final type = isFixedRateMode ? 'reverse' : 'direct';
|
||||
final body = <String, String>{
|
||||
'fromCurrency': normalizeCryptoCurrency(_request.from),
|
||||
'toCurrency': normalizeCryptoCurrency(_request.to),
|
||||
'fromNetwork': networkFor(_request.from),
|
||||
'toNetwork': networkFor(_request.to),
|
||||
'fromAmount': _request.fromAmount,
|
||||
'toAmount': _request.toAmount,
|
||||
if (!isFixedRateMode) 'fromAmount': _request.fromAmount,
|
||||
if (isFixedRateMode) 'toAmount': _request.toAmount,
|
||||
'address': _request.address,
|
||||
'flow': flow,
|
||||
'type': type,
|
||||
'refundAddress': _request.refundAddress
|
||||
};
|
||||
|
||||
if (isFixedRateMode) {
|
||||
// since we schedule to calculate the rate every 5 seconds we need to ensure that
|
||||
// we have the latest rate id with the given inputs before creating the trade
|
||||
await fetchRate(
|
||||
from: _request.from,
|
||||
to: _request.to,
|
||||
amount: double.tryParse(_request.toAmount) ?? 0,
|
||||
isFixedRateMode: true,
|
||||
isReceiveAmount: true,
|
||||
);
|
||||
body['rateId'] = _lastUsedRateId;
|
||||
}
|
||||
|
||||
final uri = Uri.https(apiAuthority, createTradePath);
|
||||
final response = await post(uri, headers: headers, body: json.encode(body));
|
||||
|
||||
|
||||
if (response.statusCode == 400) {
|
||||
final responseJSON = json.decode(response.body) as Map<String, dynamic>;
|
||||
final error = responseJSON['error'] as String;
|
||||
|
@ -130,7 +142,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
final id = responseJSON['id'] as String;
|
||||
final inputAddress = responseJSON['payinAddress'] as String;
|
||||
final refundAddress = responseJSON['refundAddress'] as String;
|
||||
final extraId = responseJSON['payinExtraId'] as String;
|
||||
final extraId = responseJSON['payinExtraId'] as String?;
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
|
@ -141,7 +153,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
refundAddress: refundAddress,
|
||||
extraId: extraId,
|
||||
createdAt: DateTime.now(),
|
||||
amount: _request.fromAmount,
|
||||
amount: responseJSON['fromAmount']?.toString() ?? _request.fromAmount,
|
||||
state: TradeState.created);
|
||||
}
|
||||
|
||||
|
@ -180,9 +192,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
final extraId = responseJSON['payinExtraId'] as String;
|
||||
final outputTransaction = responseJSON['payoutHash'] as String;
|
||||
final expiredAtRaw = responseJSON['validUntil'] as String;
|
||||
final expiredAt = expiredAtRaw != null
|
||||
? DateTime.parse(expiredAtRaw).toLocal()
|
||||
: null;
|
||||
final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal();
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
|
@ -198,7 +208,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<double> calculateAmount(
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
|
@ -214,10 +224,10 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
final type = isReverse ? 'reverse' : 'direct';
|
||||
final flow = getFlow(isFixedRateMode);
|
||||
final params = <String, String>{
|
||||
'fromCurrency': isReverse ? normalizeCryptoCurrency(to) : normalizeCryptoCurrency(from),
|
||||
'toCurrency': isReverse ? normalizeCryptoCurrency(from) : normalizeCryptoCurrency(to),
|
||||
'fromNetwork': isReverse ? networkFor(to) : networkFor(from),
|
||||
'toNetwork': isReverse ? networkFor(from) : networkFor(to),
|
||||
'fromCurrency': normalizeCryptoCurrency(from),
|
||||
'toCurrency': normalizeCryptoCurrency(to),
|
||||
'fromNetwork': networkFor(from),
|
||||
'toNetwork': networkFor(to),
|
||||
'type': type,
|
||||
'flow': flow};
|
||||
|
||||
|
@ -238,7 +248,7 @@ class ChangeNowExchangeProvider extends ExchangeProvider {
|
|||
_lastUsedRateId = rateId;
|
||||
}
|
||||
|
||||
return isReverse ? fromAmount : toAmount;
|
||||
return isReverse ? (amount / fromAmount) : (toAmount / amount);
|
||||
} catch(e) {
|
||||
print(e.toString());
|
||||
return 0.0;
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_pair.dart';
|
||||
|
@ -14,6 +13,7 @@ abstract class ExchangeProvider {
|
|||
ExchangeProviderDescription get description;
|
||||
bool get isAvailable;
|
||||
bool get isEnabled;
|
||||
bool get supportsFixedRate;
|
||||
|
||||
@override
|
||||
String toString() => title;
|
||||
|
@ -26,7 +26,7 @@ abstract class ExchangeProvider {
|
|||
required TradeRequest request,
|
||||
required bool isFixedRateMode});
|
||||
Future<Trade> findTradeById({required String id});
|
||||
Future<double> calculateAmount({
|
||||
Future<double> fetchRate({
|
||||
required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
|
|
|
@ -66,6 +66,9 @@ class MorphTokenExchangeProvider extends ExchangeProvider {
|
|||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => false;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description =>
|
||||
ExchangeProviderDescription.morphToken;
|
||||
|
@ -200,7 +203,7 @@ class MorphTokenExchangeProvider extends ExchangeProvider {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<double> calculateAmount(
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
|
|
|
@ -12,7 +12,6 @@ import 'package:cw_core/crypto_currency.dart';
|
|||
import 'package:cake_wallet/exchange/trade_request.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/limits.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
class SideShiftExchangeProvider extends ExchangeProvider {
|
||||
|
@ -48,8 +47,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
|||
|
||||
return supportedCurrencies
|
||||
.map((i) => supportedCurrencies
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true))
|
||||
.where((c) => c != null))
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true)))
|
||||
.expand((i) => i)
|
||||
.toList();
|
||||
}
|
||||
|
@ -59,7 +57,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
|||
ExchangeProviderDescription.sideShift;
|
||||
|
||||
@override
|
||||
Future<double> calculateAmount(
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
|
@ -81,9 +79,7 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
|||
|
||||
if (amount > max) return 0.00;
|
||||
|
||||
final estimatedAmount = rate * amount;
|
||||
|
||||
return estimatedAmount;
|
||||
return rate;
|
||||
} catch (_) {
|
||||
return 0.00;
|
||||
}
|
||||
|
@ -249,15 +245,15 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
|||
final expectedSendAmount = responseJSON['depositAmount'].toString();
|
||||
final deposits = responseJSON['deposits'] as List?;
|
||||
TradeState? state;
|
||||
String? status;
|
||||
|
||||
if (deposits != null && deposits.isNotEmpty) {
|
||||
final status = deposits[0]['status'] as String;
|
||||
state = TradeState.deserialize(raw: status);
|
||||
if (deposits?.isNotEmpty ?? false) {
|
||||
status = deposits![0]['status'] as String?;
|
||||
}
|
||||
state = TradeState.deserialize(raw: status ?? 'created');
|
||||
|
||||
final expiredAtRaw = responseJSON['expiresAtISO'] as String;
|
||||
final expiredAt =
|
||||
expiredAtRaw != null ? DateTime.parse(expiredAtRaw).toLocal() : null;
|
||||
final expiredAt = DateTime.tryParse(expiredAtRaw)?.toLocal();
|
||||
|
||||
return Trade(
|
||||
id: id,
|
||||
|
@ -277,6 +273,9 @@ class SideShiftExchangeProvider extends ExchangeProvider {
|
|||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => true;
|
||||
|
||||
@override
|
||||
String get title => 'SideShift';
|
||||
|
||||
|
|
|
@ -20,8 +20,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
|||
.where((i) => i != CryptoCurrency.zaddr)
|
||||
.map((i) => CryptoCurrency.all
|
||||
.where((i) => i != CryptoCurrency.zaddr)
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true))
|
||||
.where((c) => c != null))
|
||||
.map((k) => ExchangePair(from: i, to: k, reverse: true)))
|
||||
.expand((i) => i)
|
||||
.toList());
|
||||
|
||||
|
@ -37,7 +36,7 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
|||
ExchangeProviderDescription.simpleSwap;
|
||||
|
||||
@override
|
||||
Future<double> calculateAmount(
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
|
@ -59,9 +58,9 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
|||
final uri = Uri.https(apiAuthority, getEstimatePath, params);
|
||||
final response = await get(uri);
|
||||
|
||||
if (response.body == null || response.body == "null") return 0.00;
|
||||
if (response.body == "null") return 0.00;
|
||||
final data = json.decode(response.body) as String;
|
||||
return double.parse(data);
|
||||
return double.parse(data) / amount;
|
||||
} catch (_) {
|
||||
return 0.00;
|
||||
}
|
||||
|
@ -210,6 +209,9 @@ class SimpleSwapExchangeProvider extends ExchangeProvider {
|
|||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => false;
|
||||
|
||||
@override
|
||||
String get title => 'SimpleSwap';
|
||||
|
||||
|
|
|
@ -48,6 +48,9 @@ class XMRTOExchangeProvider extends ExchangeProvider {
|
|||
@override
|
||||
bool get isEnabled => true;
|
||||
|
||||
@override
|
||||
bool get supportsFixedRate => false;
|
||||
|
||||
@override
|
||||
ExchangeProviderDescription get description =>
|
||||
ExchangeProviderDescription.xmrto;
|
||||
|
@ -191,7 +194,7 @@ class XMRTOExchangeProvider extends ExchangeProvider {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<double> calculateAmount(
|
||||
Future<double> fetchRate(
|
||||
{required CryptoCurrency from,
|
||||
required CryptoCurrency to,
|
||||
required double amount,
|
||||
|
|
|
@ -128,7 +128,7 @@ Future<void> main() async {
|
|||
exchangeTemplates: exchangeTemplates,
|
||||
transactionDescriptions: transactionDescriptions,
|
||||
secureStorage: secureStorage,
|
||||
initialMigrationVersion: 17);
|
||||
initialMigrationVersion: 18);
|
||||
runApp(App());
|
||||
} catch (e, stacktrace) {
|
||||
runApp(MaterialApp(
|
||||
|
@ -261,7 +261,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
|
|||
final statusBarColor = Colors.transparent;
|
||||
final authenticationStore = getIt.get<AuthenticationStore>();
|
||||
final initialRoute =
|
||||
authenticationStore.state == AuthenticationState.denied
|
||||
authenticationStore.state == AuthenticationState.uninitialized
|
||||
? Routes.disclaimer
|
||||
: Routes.login;
|
||||
final currentTheme = settingsStore.currentTheme;
|
||||
|
|
|
@ -22,9 +22,9 @@ Future<void> bootstrap(GlobalKey<NavigatorState> navigatorKey) async {
|
|||
final currentWalletName = getIt
|
||||
.get<SharedPreferences>()
|
||||
.getString(PreferencesKey.currentWalletName);
|
||||
authenticationStore.state = currentWalletName == null
|
||||
? AuthenticationState.denied
|
||||
: AuthenticationState.installed;
|
||||
if (currentWalletName != null) {
|
||||
authenticationStore.state = AuthenticationState.installed;
|
||||
}
|
||||
|
||||
startAuthenticationStateChange(authenticationStore, navigatorKey);
|
||||
startCurrentWalletChangeReaction(
|
||||
|
|
|
@ -26,10 +26,5 @@ void startAuthenticationStateChange(AuthenticationStore authenticationStore,
|
|||
await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.dashboard, (route) => false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == AuthenticationState.denied) {
|
||||
await navigatorKey.currentState!.pushNamedAndRemoveUntil(Routes.welcome, (_) => false);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dar
|
|||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_tip_page.dart';
|
||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
|
||||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
|
||||
import 'package:cake_wallet/src/screens/new_wallet/advanced_privacy_settings_page.dart';
|
||||
import 'package:cake_wallet/src/screens/order_details/order_details_page.dart';
|
||||
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
|
||||
import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
|
||||
|
@ -25,6 +26,8 @@ import 'package:cake_wallet/src/screens/support/support_page.dart';
|
|||
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
|
||||
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
|
@ -77,6 +80,7 @@ import 'package:cake_wallet/src/screens/ionia/ionia.dart';
|
|||
import 'package:cake_wallet/src/screens/ionia/cards/ionia_payment_status_page.dart';
|
||||
import 'package:cake_wallet/anypay/any_pay_payment_committed_info.dart';
|
||||
import 'package:cake_wallet/ionia/ionia_any_pay_payment_info.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
late RouteSettings currentRouteSettings;
|
||||
|
||||
|
@ -317,11 +321,13 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
|
||||
case Routes.addressBook:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => getIt.get<ContactListPage>(param1: true));
|
||||
builder: (_) =>
|
||||
getIt.get<ContactListPage>());
|
||||
|
||||
case Routes.pickerAddressBook:
|
||||
final selectedCurrency = settings.arguments as CryptoCurrency;
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => getIt.get<ContactListPage>(param1: false));
|
||||
builder: (_) => getIt.get<ContactListPage>(param1: selectedCurrency));
|
||||
|
||||
case Routes.addressBookAddContact:
|
||||
return CupertinoPageRoute<void>(
|
||||
|
@ -491,6 +497,15 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
case Routes.onramperPage:
|
||||
return CupertinoPageRoute<void>(builder: (_) => getIt.get<OnRamperPage>());
|
||||
|
||||
case Routes.advancedPrivacySettings:
|
||||
final type = settings.arguments as WalletType;
|
||||
|
||||
return CupertinoPageRoute<void>(
|
||||
builder: (_) => AdvancedPrivacySettingsPage(
|
||||
getIt.get<AdvancedPrivacySettingsViewModel>(param1: type),
|
||||
getIt.get<NodeCreateOrEditViewModel>(param1: type),
|
||||
));
|
||||
|
||||
default:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => Scaffold(
|
||||
|
|
|
@ -81,4 +81,5 @@ class Routes {
|
|||
static const privacyPage = '/privacy_page';
|
||||
static const displaySettingsPage = '/display_settings_page';
|
||||
static const otherSettingsPage = '/other_settings_page';
|
||||
static const advancedPrivacySettings = '/advanced_privacy_settings';
|
||||
}
|
||||
|
|
|
@ -9,24 +9,22 @@ import 'package:flutter_mobx/flutter_mobx.dart';
|
|||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart';
|
||||
import 'package:cake_wallet/src/widgets/collapsible_standart_list.dart';
|
||||
|
||||
class ContactListPage extends BasePage {
|
||||
ContactListPage(this.contactListViewModel, {this.isEditable = true});
|
||||
ContactListPage(this.contactListViewModel);
|
||||
|
||||
final ContactListViewModel contactListViewModel;
|
||||
final bool isEditable;
|
||||
|
||||
@override
|
||||
String get title => S.current.address_book;
|
||||
|
||||
@override
|
||||
Widget? trailing(BuildContext context) {
|
||||
if (!isEditable) {
|
||||
if (!contactListViewModel.isEditable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -60,11 +58,14 @@ class ContactListPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
|
||||
child: Observer(
|
||||
builder: (_) {
|
||||
return CollapsibleSectionList(
|
||||
builder: (_) {
|
||||
final contacts = contactListViewModel.contactsToShow;
|
||||
final walletContacts = contactListViewModel.walletContactsToShow;
|
||||
return CollapsibleSectionList(
|
||||
context: context,
|
||||
sectionCount: 2,
|
||||
themeColor: Theme.of(context).primaryTextTheme.headline6!.color!,
|
||||
|
@ -82,35 +83,37 @@ class ContactListPage extends BasePage {
|
|||
child: Text(title, style: TextStyle(fontSize: 36)));
|
||||
},
|
||||
itemCounter: (int sectionIndex) => sectionIndex == 0
|
||||
? contactListViewModel.walletContacts.length
|
||||
: contactListViewModel.contacts.length,
|
||||
? walletContacts.length
|
||||
: contacts.length,
|
||||
itemBuilder: (_, sectionIndex, index) {
|
||||
if (sectionIndex == 0) {
|
||||
final walletInfo = contactListViewModel.walletContacts[index];
|
||||
final walletInfo = walletContacts[index];
|
||||
return generateRaw(context, walletInfo);
|
||||
}
|
||||
|
||||
final contact = contactListViewModel.contacts[index];
|
||||
final contact = contacts[index];
|
||||
final content = generateRaw(context, contact);
|
||||
return !isEditable
|
||||
? content
|
||||
: Slidable(
|
||||
return contactListViewModel.isEditable
|
||||
? Slidable(
|
||||
key: Key('${contact.key}'),
|
||||
endActionPane: _actionPane(context, contact),
|
||||
child: content,
|
||||
);
|
||||
)
|
||||
: content;
|
||||
},
|
||||
);
|
||||
},
|
||||
));
|
||||
);})
|
||||
);
|
||||
}
|
||||
|
||||
Widget generateRaw(BuildContext context, ContactBase contact) {
|
||||
final image = _getCurrencyImage(contact.type);
|
||||
final image = contact.type.iconPath;
|
||||
final currencyIcon = image != null ? Image.asset(image, height: 24, width: 24)
|
||||
: const SizedBox(height: 24, width: 24);
|
||||
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
if (!isEditable) {
|
||||
if (!contactListViewModel.isEditable) {
|
||||
Navigator.of(context).pop(contact);
|
||||
return;
|
||||
}
|
||||
|
@ -131,12 +134,10 @@ class ContactListPage extends BasePage {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
image ?? Offstage(),
|
||||
currencyIcon,
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: image != null
|
||||
? EdgeInsets.only(left: 12)
|
||||
: EdgeInsets.only(left: 0),
|
||||
padding: EdgeInsets.only(left: 12),
|
||||
child: Text(
|
||||
contact.name,
|
||||
style: TextStyle(
|
||||
|
@ -152,69 +153,6 @@ class ContactListPage extends BasePage {
|
|||
);
|
||||
}
|
||||
|
||||
Image? _getCurrencyImage(CryptoCurrency currency) {
|
||||
Image? image;
|
||||
|
||||
switch (currency) {
|
||||
case CryptoCurrency.xmr:
|
||||
image =
|
||||
Image.asset('assets/images/monero_logo.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.ada:
|
||||
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.bch:
|
||||
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.bnb:
|
||||
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.btc:
|
||||
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.dai:
|
||||
image = Image.asset('assets/images/dai.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.dash:
|
||||
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.eos:
|
||||
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.eth:
|
||||
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.ltc:
|
||||
image =
|
||||
Image.asset('assets/images/litecoin.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.nano:
|
||||
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.trx:
|
||||
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.usdt:
|
||||
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.usdterc20:
|
||||
image = Image.asset('assets/images/usdterc.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xlm:
|
||||
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xrp:
|
||||
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
|
||||
break;
|
||||
case CryptoCurrency.xhv:
|
||||
image = Image.asset('assets/images/haven_logo.png', height: 24, width: 24);
|
||||
break;
|
||||
default:
|
||||
image = null;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
Future<bool> showAlertDialog(BuildContext context) async {
|
||||
return await showPopUp<bool>(
|
||||
context: context,
|
||||
|
|
|
@ -395,7 +395,8 @@ class ExchangeCardState extends State<ExchangeCard> {
|
|||
buttonColor: widget.addressButtonsColor,
|
||||
validator: widget.addressTextFieldValidator,
|
||||
onPushPasteButton: widget.onPushPasteButton,
|
||||
onPushAddressBookButton: widget.onPushAddressBookButton
|
||||
onPushAddressBookButton: widget.onPushAddressBookButton,
|
||||
selectedCurrency: _selectedCurrency
|
||||
),
|
||||
|
||||
)
|
||||
|
|
104
lib/src/screens/new_wallet/advanced_privacy_settings_page.dart
Normal file
104
lib/src/screens/new_wallet/advanced_privacy_settings_page.dart
Normal file
|
@ -0,0 +1,104 @@
|
|||
import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
|
||||
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
|
||||
class AdvancedPrivacySettingsPage extends BasePage {
|
||||
AdvancedPrivacySettingsPage(this.advancedPrivacySettingsViewModel, this.nodeViewModel);
|
||||
|
||||
final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel;
|
||||
final NodeCreateOrEditViewModel nodeViewModel;
|
||||
|
||||
@override
|
||||
String get title => S.current.privacy_settings;
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) =>
|
||||
AdvancedPrivacySettingsBody(advancedPrivacySettingsViewModel, nodeViewModel);
|
||||
}
|
||||
|
||||
class AdvancedPrivacySettingsBody extends StatefulWidget {
|
||||
const AdvancedPrivacySettingsBody(this.privacySettingsViewModel, this.nodeViewModel, {Key? key})
|
||||
: super(key: key);
|
||||
|
||||
final AdvancedPrivacySettingsViewModel privacySettingsViewModel;
|
||||
final NodeCreateOrEditViewModel nodeViewModel;
|
||||
|
||||
@override
|
||||
_AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState();
|
||||
}
|
||||
|
||||
class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBody> {
|
||||
_AdvancedPrivacySettingsBodyState();
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24),
|
||||
content: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
...widget.privacySettingsViewModel.settings.map(
|
||||
(item) => Observer(
|
||||
builder: (_) => SettingsSwitcherCell(
|
||||
title: item.title,
|
||||
value: item.value(),
|
||||
onValueChange: item.onValueChange,
|
||||
),
|
||||
),
|
||||
),
|
||||
Observer(
|
||||
builder: (_) {
|
||||
if (widget.privacySettingsViewModel.addCustomNode) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
|
||||
child: NodeForm(
|
||||
formKey: _formKey,
|
||||
nodeViewModel: widget.nodeViewModel,
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.all(24),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
LoadingPrimaryButton(
|
||||
onPressed: () {
|
||||
widget.nodeViewModel.save(saveAsCurrent: true);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
text: S.of(context).continue_text,
|
||||
color: Theme.of(context).accentTextTheme.bodyText1!.color!,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: MediaQuery.of(context).size.width * 0.15),
|
||||
child: Text(
|
||||
S.of(context).settings_can_be_changed_later,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).accentTextTheme.headline2?.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -200,18 +200,30 @@ class _WalletNameFormState extends State<WalletNameForm> {
|
|||
]
|
||||
]),
|
||||
bottomSectionPadding:
|
||||
EdgeInsets.only(left: 24, right: 24, bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (context) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: _confirmForm,
|
||||
text: S.of(context).seed_language_next,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isLoading: _walletNewVM.state is IsExecutingState,
|
||||
isDisabled: _walletNewVM.name.isEmpty,
|
||||
);
|
||||
},
|
||||
EdgeInsets.all(24),
|
||||
bottomSection: Column(
|
||||
children: [
|
||||
Observer(
|
||||
builder: (context) {
|
||||
return LoadingPrimaryButton(
|
||||
onPressed: _confirmForm,
|
||||
text: S.of(context).seed_language_next,
|
||||
color: Colors.green,
|
||||
textColor: Colors.white,
|
||||
isLoading: _walletNewVM.state is IsExecutingState,
|
||||
isDisabled: _walletNewVM.name.isEmpty,
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 25),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context)
|
||||
.pushNamed(Routes.advancedPrivacySettings, arguments: _walletNewVM.type);
|
||||
},
|
||||
child: Text(S.of(context).advanced_privacy_settings),
|
||||
),
|
||||
],
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/core/node_address_validator.dart';
|
||||
import 'package:cake_wallet/core/node_port_validator.dart';
|
||||
import 'package:cake_wallet/src/widgets/primary_button.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/src/screens/base_page.dart';
|
||||
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
|
||||
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
|
@ -108,76 +105,10 @@ class NodeCreateOrEditPage extends BasePage {
|
|||
padding: EdgeInsets.only(left: 24, right: 24),
|
||||
child: ScrollableWithBottomSection(
|
||||
contentPadding: EdgeInsets.only(bottom: 24.0),
|
||||
content: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _addressController,
|
||||
hintText: S.of(context).node_address,
|
||||
validator: NodeAddressValidator(),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _portController,
|
||||
hintText: S.of(context).node_port,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: false),
|
||||
validator: NodePortValidator(),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
if (nodeCreateOrEditViewModel.hasAuthCredentials) ...[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _loginController,
|
||||
hintText: S.of(context).login,
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _passwordController,
|
||||
hintText: S.of(context).password,
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => StandardCheckbox(
|
||||
value: nodeCreateOrEditViewModel.useSSL,
|
||||
onChanged: (value) =>
|
||||
nodeCreateOrEditViewModel.useSSL = value,
|
||||
caption: S.of(context).use_ssl,
|
||||
))
|
||||
],
|
||||
))
|
||||
]
|
||||
],
|
||||
)),
|
||||
content: NodeForm(
|
||||
formKey: _formKey,
|
||||
nodeViewModel: nodeCreateOrEditViewModel,
|
||||
),
|
||||
bottomSectionPadding: EdgeInsets.only(bottom: 24),
|
||||
bottomSection: Observer(
|
||||
builder: (_) => Row(
|
||||
|
|
151
lib/src/screens/nodes/widgets/node_form.dart
Normal file
151
lib/src/screens/nodes/widgets/node_form.dart
Normal file
|
@ -0,0 +1,151 @@
|
|||
import 'package:cake_wallet/core/node_address_validator.dart';
|
||||
import 'package:cake_wallet/core/node_port_validator.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/view_model/node_list/node_create_or_edit_view_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
||||
class NodeForm extends StatelessWidget {
|
||||
NodeForm({
|
||||
required this.nodeViewModel,
|
||||
required this.formKey,
|
||||
}) : _addressController = TextEditingController(),
|
||||
_portController = TextEditingController(),
|
||||
_loginController = TextEditingController(),
|
||||
_passwordController = TextEditingController() {
|
||||
reaction((_) => nodeViewModel.address, (String address) {
|
||||
if (address != _addressController.text) {
|
||||
_addressController.text = address;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => nodeViewModel.port, (String port) {
|
||||
if (port != _portController.text) {
|
||||
_portController.text = port;
|
||||
}
|
||||
});
|
||||
|
||||
if (nodeViewModel.hasAuthCredentials) {
|
||||
reaction((_) => nodeViewModel.login, (String login) {
|
||||
if (login != _loginController.text) {
|
||||
_loginController.text = login;
|
||||
}
|
||||
});
|
||||
|
||||
reaction((_) => nodeViewModel.password, (String password) {
|
||||
if (password != _passwordController.text) {
|
||||
_passwordController.text = password;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_addressController
|
||||
.addListener(() => nodeViewModel.address = _addressController.text);
|
||||
_portController
|
||||
.addListener(() => nodeViewModel.port = _portController.text);
|
||||
_loginController
|
||||
.addListener(() => nodeViewModel.login = _loginController.text);
|
||||
_passwordController
|
||||
.addListener(() => nodeViewModel.password = _passwordController.text);
|
||||
}
|
||||
|
||||
final NodeCreateOrEditViewModel nodeViewModel;
|
||||
final GlobalKey<FormState> formKey;
|
||||
|
||||
final TextEditingController _addressController;
|
||||
final TextEditingController _portController;
|
||||
final TextEditingController _loginController;
|
||||
final TextEditingController _passwordController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _addressController,
|
||||
hintText: S.of(context).node_address,
|
||||
validator: NodeAddressValidator(),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _portController,
|
||||
hintText: S.of(context).node_port,
|
||||
keyboardType: TextInputType.numberWithOptions(
|
||||
signed: false, decimal: false),
|
||||
validator: NodePortValidator(),
|
||||
))
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
if (nodeViewModel.hasAuthCredentials) ...[
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _loginController,
|
||||
hintText: S.of(context).login,
|
||||
))
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10.0),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: BaseTextFormField(
|
||||
controller: _passwordController,
|
||||
hintText: S.of(context).password,
|
||||
))
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => StandardCheckbox(
|
||||
value: nodeViewModel.useSSL,
|
||||
onChanged: (value) => nodeViewModel.useSSL = value,
|
||||
caption: S.of(context).use_ssl,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Observer(
|
||||
builder: (_) => StandardCheckbox(
|
||||
value: nodeViewModel.trusted,
|
||||
onChanged: (value) => nodeViewModel.trusted = value,
|
||||
caption: S.of(context).trusted,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -154,6 +154,7 @@ class SendCardState extends State<SendCard>
|
|||
await output.fetchParsedAddress(context);
|
||||
},
|
||||
validator: validator,
|
||||
selectedCurrency: sendViewModel.currency,
|
||||
);
|
||||
}),
|
||||
if (output.isParsedAddress) Padding(
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:cake_wallet/routes.dart';
|
|||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/entities/qr_scanner.dart';
|
||||
import 'package:cake_wallet/entities/contact_base.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
enum AddressTextFieldOption { paste, qrCode, addressBook }
|
||||
|
||||
|
@ -26,7 +27,8 @@ class AddressTextField extends StatelessWidget {
|
|||
this.hintStyle,
|
||||
this.validator,
|
||||
this.onPushPasteButton,
|
||||
this.onPushAddressBookButton});
|
||||
this.onPushAddressBookButton,
|
||||
this.selectedCurrency});
|
||||
|
||||
static const prefixIconWidth = 34.0;
|
||||
static const prefixIconHeight = 34.0;
|
||||
|
@ -47,6 +49,7 @@ class AddressTextField extends StatelessWidget {
|
|||
final FocusNode? focusNode;
|
||||
final Function(BuildContext context)? onPushPasteButton;
|
||||
final Function(BuildContext context)? onPushAddressBookButton;
|
||||
final CryptoCurrency? selectedCurrency;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -207,7 +210,7 @@ class AddressTextField extends StatelessWidget {
|
|||
|
||||
Future<void> _presetAddressBookPicker(BuildContext context) async {
|
||||
final contact = await Navigator.of(context, rootNavigator: true)
|
||||
.pushNamed(Routes.pickerAddressBook);
|
||||
.pushNamed(Routes.pickerAddressBook,arguments: selectedCurrency);
|
||||
|
||||
if (contact is ContactBase && contact.address != null) {
|
||||
controller?.text = contact.address;
|
||||
|
|
|
@ -4,7 +4,7 @@ part 'authentication_store.g.dart';
|
|||
|
||||
class AuthenticationStore = AuthenticationStoreBase with _$AuthenticationStore;
|
||||
|
||||
enum AuthenticationState { uninitialized, installed, allowed, denied }
|
||||
enum AuthenticationState { uninitialized, installed, allowed }
|
||||
|
||||
abstract class AuthenticationStoreBase with Store {
|
||||
AuthenticationStoreBase() : state = AuthenticationState.uninitialized;
|
||||
|
@ -17,7 +17,4 @@ abstract class AuthenticationStoreBase with Store {
|
|||
|
||||
@action
|
||||
void allowed() => state = AuthenticationState.allowed;
|
||||
|
||||
@action
|
||||
void denied() => state = AuthenticationState.denied;
|
||||
}
|
||||
|
|
|
@ -115,8 +115,8 @@ abstract class SettingsStoreBase with Store {
|
|||
PreferencesKey.currentBalanceDisplayModeKey, mode.serialize()));
|
||||
|
||||
reaction(
|
||||
(_) => disableExchange,
|
||||
(bool disableExchange) => sharedPreferences.setBool(
|
||||
(_) => disableExchange,
|
||||
(bool disableExchange) => sharedPreferences.setBool(
|
||||
PreferencesKey.disableExchangeKey, disableExchange));
|
||||
|
||||
this
|
||||
|
|
51
lib/view_model/advanced_privacy_settings_view_model.dart
Normal file
51
lib/view_model/advanced_privacy_settings_view_model.dart
Normal file
|
@ -0,0 +1,51 @@
|
|||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/view_model/settings/switcher_list_item.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
|
||||
part 'advanced_privacy_settings_view_model.g.dart';
|
||||
|
||||
class AdvancedPrivacySettingsViewModel = AdvancedPrivacySettingsViewModelBase
|
||||
with _$AdvancedPrivacySettingsViewModel;
|
||||
|
||||
abstract class AdvancedPrivacySettingsViewModelBase with Store {
|
||||
AdvancedPrivacySettingsViewModelBase(this.type, this._settingsStore)
|
||||
: _disableFiat = false,
|
||||
_addCustomNode = false {
|
||||
settings = [
|
||||
// TODO: uncomment when Disable Fiat PR is merged
|
||||
// SwitcherListItem(
|
||||
// title: S.current.disable_fiat,
|
||||
// value: () => _disableFiat,
|
||||
// onValueChange: (_, bool value) => _disableFiat = value,
|
||||
// ),
|
||||
SwitcherListItem(
|
||||
title: S.current.disable_exchange,
|
||||
value: () => _settingsStore.disableExchange,
|
||||
onValueChange: (_, bool value) {
|
||||
_settingsStore.disableExchange = value;
|
||||
},
|
||||
),
|
||||
SwitcherListItem(
|
||||
title: S.current.add_custom_node,
|
||||
value: () => _addCustomNode,
|
||||
onValueChange: (_, bool value) => _addCustomNode = value,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
late List<SwitcherListItem> settings;
|
||||
|
||||
@observable
|
||||
bool _disableFiat = false;
|
||||
|
||||
@observable
|
||||
bool _addCustomNode = false;
|
||||
|
||||
final WalletType type;
|
||||
final SettingsStore _settingsStore;
|
||||
|
||||
@computed
|
||||
bool get addCustomNode => _addCustomNode;
|
||||
}
|
|
@ -7,27 +7,26 @@ import 'package:mobx/mobx.dart';
|
|||
import 'package:cake_wallet/entities/contact_record.dart';
|
||||
import 'package:cake_wallet/entities/contact.dart';
|
||||
import 'package:cake_wallet/utils/mobx.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
part 'contact_list_view_model.g.dart';
|
||||
|
||||
class ContactListViewModel = ContactListViewModelBase
|
||||
with _$ContactListViewModel;
|
||||
class ContactListViewModel = ContactListViewModelBase with _$ContactListViewModel;
|
||||
|
||||
abstract class ContactListViewModelBase with Store {
|
||||
ContactListViewModelBase(this.contactSource, this.walletInfoSource)
|
||||
ContactListViewModelBase(this.contactSource, this.walletInfoSource, this._currency)
|
||||
: contacts = ObservableList<ContactRecord>(),
|
||||
walletContacts = [] {
|
||||
walletInfoSource.values.forEach((info) {
|
||||
if (info.addresses?.isNotEmpty ?? false) {
|
||||
info.addresses?.forEach((address, label) {
|
||||
final name = label.isNotEmpty
|
||||
? info.name + ' ($label)'
|
||||
: info.name;
|
||||
final name = label.isNotEmpty ? info.name + ' ($label)' : info.name;
|
||||
|
||||
walletContacts.add(WalletContact(
|
||||
address,
|
||||
name,
|
||||
walletTypeToCryptoCurrency(info.type)));
|
||||
address,
|
||||
name,
|
||||
walletTypeToCryptoCurrency(info.type),
|
||||
));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -41,7 +40,18 @@ abstract class ContactListViewModelBase with Store {
|
|||
final Box<WalletInfo> walletInfoSource;
|
||||
final ObservableList<ContactRecord> contacts;
|
||||
final List<WalletContact> walletContacts;
|
||||
final CryptoCurrency? _currency;
|
||||
StreamSubscription<BoxEvent>? _subscription;
|
||||
|
||||
bool get isEditable => _currency == null;
|
||||
|
||||
Future<void> delete(ContactRecord contact) async => contact.original.delete();
|
||||
|
||||
@computed
|
||||
List<ContactRecord> get contactsToShow =>
|
||||
contacts.where((element) => _currency == null || element.type == _currency).toList();
|
||||
|
||||
@computed
|
||||
List<WalletContact> get walletContactsToShow =>
|
||||
walletContacts.where((element) => _currency == null || element.type == _currency).toList();
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ abstract class ExchangeViewModelBase with Store {
|
|||
ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore,
|
||||
this.tradesStore, this._settingsStore, this.sharedPreferences)
|
||||
: _cryptoNumberFormat = NumberFormat(),
|
||||
isReverse = false,
|
||||
isFixedRateMode = false,
|
||||
isReceiveAmountEntered = false,
|
||||
depositAmount = '',
|
||||
|
@ -112,7 +111,11 @@ abstract class ExchangeViewModelBase with Store {
|
|||
loadLimits();
|
||||
reaction(
|
||||
(_) => isFixedRateMode,
|
||||
(Object _) => loadLimits());
|
||||
(Object _) {
|
||||
loadLimits();
|
||||
_bestRate = 0;
|
||||
_calculateBestRate();
|
||||
});
|
||||
}
|
||||
|
||||
final WalletBase wallet;
|
||||
|
@ -227,8 +230,6 @@ abstract class ExchangeViewModelBase with Store {
|
|||
|
||||
Limits limits;
|
||||
|
||||
bool isReverse;
|
||||
|
||||
NumberFormat _cryptoNumberFormat;
|
||||
|
||||
final SettingsStore _settingsStore;
|
||||
|
@ -258,7 +259,6 @@ abstract class ExchangeViewModelBase with Store {
|
|||
@action
|
||||
Future<void> changeReceiveAmount({required String amount}) async {
|
||||
receiveAmount = amount;
|
||||
isReverse = true;
|
||||
|
||||
if (amount.isEmpty) {
|
||||
depositAmount = '';
|
||||
|
@ -283,7 +283,6 @@ abstract class ExchangeViewModelBase with Store {
|
|||
@action
|
||||
Future<void> changeDepositAmount({required String amount}) async {
|
||||
depositAmount = amount;
|
||||
isReverse = false;
|
||||
|
||||
if (amount.isEmpty) {
|
||||
depositAmount = '';
|
||||
|
@ -307,14 +306,17 @@ abstract class ExchangeViewModelBase with Store {
|
|||
}
|
||||
|
||||
Future<void> _calculateBestRate() async {
|
||||
final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1;
|
||||
|
||||
final result = await Future.wait<double>(
|
||||
_tradeAvailableProviders
|
||||
.map((element) => element.calculateAmount(
|
||||
.where((element) => !isFixedRateMode || element.supportsFixedRate)
|
||||
.map((element) => element.fetchRate(
|
||||
from: depositCurrency,
|
||||
to: receiveCurrency,
|
||||
amount: 1,
|
||||
amount: amount,
|
||||
isFixedRateMode: isFixedRateMode,
|
||||
isReceiveAmount: false))
|
||||
isReceiveAmount: isFixedRateMode))
|
||||
);
|
||||
|
||||
_sortedAvailableProviders.clear();
|
||||
|
@ -345,7 +347,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
? depositCurrency
|
||||
: receiveCurrency;
|
||||
|
||||
double lowestMin = double.maxFinite;
|
||||
double? lowestMin = double.maxFinite;
|
||||
double? highestMax = 0.0;
|
||||
|
||||
for (var provider in selectedProviders) {
|
||||
|
@ -360,8 +362,8 @@ abstract class ExchangeViewModelBase with Store {
|
|||
to: to,
|
||||
isFixedRateMode: isFixedRateMode);
|
||||
|
||||
if (tempLimits.min != null && tempLimits.min! < lowestMin) {
|
||||
lowestMin = tempLimits.min!;
|
||||
if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) {
|
||||
lowestMin = tempLimits.min;
|
||||
}
|
||||
if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) {
|
||||
highestMax = tempLimits.max;
|
||||
|
@ -371,7 +373,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
}
|
||||
}
|
||||
|
||||
if (lowestMin < double.maxFinite) {
|
||||
if (lowestMin != double.maxFinite) {
|
||||
limits = Limits(min: lowestMin, max: highestMax);
|
||||
|
||||
limitsState = LimitsLoadedSuccessfully(limits: limits);
|
||||
|
@ -399,7 +401,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
settleAddress: receiveAddress,
|
||||
refundAddress: depositAddress,
|
||||
);
|
||||
amount = depositAmount;
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
if (provider is SimpleSwapExchangeProvider) {
|
||||
|
@ -410,7 +412,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
address: receiveAddress,
|
||||
refundAddress: depositAddress,
|
||||
);
|
||||
amount = depositAmount;
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
if (provider is XMRTOExchangeProvider) {
|
||||
|
@ -422,7 +424,7 @@ abstract class ExchangeViewModelBase with Store {
|
|||
address: receiveAddress,
|
||||
refundAddress: depositAddress,
|
||||
isBTCRequest: isReceiveAmountEntered);
|
||||
amount = depositAmount;
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
if (provider is ChangeNowExchangeProvider) {
|
||||
|
@ -433,8 +435,8 @@ abstract class ExchangeViewModelBase with Store {
|
|||
toAmount: receiveAmount.replaceAll(',', '.'),
|
||||
refundAddress: depositAddress,
|
||||
address: receiveAddress,
|
||||
isReverse: isReverse);
|
||||
amount = isReverse ? receiveAmount : depositAmount;
|
||||
isReverse: isFixedRateMode);
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
if (provider is MorphTokenExchangeProvider) {
|
||||
|
@ -444,13 +446,13 @@ abstract class ExchangeViewModelBase with Store {
|
|||
amount: depositAmount.replaceAll(',', '.'),
|
||||
refundAddress: depositAddress,
|
||||
address: receiveAddress);
|
||||
amount = depositAmount;
|
||||
amount = isFixedRateMode ? receiveAmount : depositAmount;
|
||||
}
|
||||
|
||||
amount = amount.replaceAll(',', '.');
|
||||
|
||||
if (limitsState is LimitsLoadedSuccessfully) {
|
||||
if (double.parse(amount) < limits.min!) {
|
||||
if (limits.max != null && double.parse(amount) < limits.min!) {
|
||||
continue;
|
||||
} else if (limits.max != null && double.parse(amount) > limits.max!) {
|
||||
continue;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:cw_core/wallet_base.dart';
|
||||
import 'package:cw_core/node.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
|
||||
|
@ -11,14 +11,15 @@ class NodeCreateOrEditViewModel = NodeCreateOrEditViewModelBase
|
|||
with _$NodeCreateOrEditViewModel;
|
||||
|
||||
abstract class NodeCreateOrEditViewModelBase with Store {
|
||||
NodeCreateOrEditViewModelBase(this._nodeSource, this._wallet)
|
||||
NodeCreateOrEditViewModelBase(this._nodeSource, this._walletType, this._settingsStore)
|
||||
: state = InitialExecutionState(),
|
||||
connectionState = InitialExecutionState(),
|
||||
useSSL = false,
|
||||
address = '',
|
||||
port = '',
|
||||
login = '',
|
||||
password = '';
|
||||
password = '',
|
||||
trusted = false;
|
||||
|
||||
@observable
|
||||
ExecutionState state;
|
||||
|
@ -41,12 +42,15 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
|||
@observable
|
||||
bool useSSL;
|
||||
|
||||
@observable
|
||||
bool trusted;
|
||||
|
||||
@computed
|
||||
bool get isReady =>
|
||||
address.isNotEmpty && port.isNotEmpty;
|
||||
|
||||
bool get hasAuthCredentials => _wallet.type == WalletType.monero ||
|
||||
_wallet.type == WalletType.haven;
|
||||
bool get hasAuthCredentials => _walletType == WalletType.monero ||
|
||||
_walletType == WalletType.haven;
|
||||
|
||||
String get uri {
|
||||
var uri = address;
|
||||
|
@ -58,8 +62,9 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
|||
return uri;
|
||||
}
|
||||
|
||||
final WalletBase _wallet;
|
||||
final WalletType _walletType;
|
||||
final Box<Node> _nodeSource;
|
||||
final SettingsStore _settingsStore;
|
||||
|
||||
@action
|
||||
void reset() {
|
||||
|
@ -68,16 +73,22 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
|||
login = '';
|
||||
password = '';
|
||||
useSSL = false;
|
||||
trusted = false;
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> save() async {
|
||||
Future<void> save({bool saveAsCurrent = false}) async {
|
||||
try {
|
||||
state = IsExecutingState();
|
||||
final node =
|
||||
Node(uri: uri, type: _wallet.type, login: login, password: password,
|
||||
useSSL: useSSL);
|
||||
Node(uri: uri, type: _walletType, login: login, password: password,
|
||||
useSSL: useSSL, trusted: trusted);
|
||||
await _nodeSource.add(node);
|
||||
|
||||
if (saveAsCurrent) {
|
||||
_settingsStore.nodes[_walletType] = node;
|
||||
}
|
||||
|
||||
state = ExecutedSuccessfullyState();
|
||||
} catch (e) {
|
||||
state = FailureState(e.toString());
|
||||
|
@ -89,7 +100,7 @@ abstract class NodeCreateOrEditViewModelBase with Store {
|
|||
try {
|
||||
connectionState = IsExecutingState();
|
||||
final node =
|
||||
Node(uri: uri, type: _wallet.type, login: login, password: password);
|
||||
Node(uri: uri, type: _walletType, login: login, password: password);
|
||||
final isAlive = await node.requestNode();
|
||||
connectionState = ExecutedSuccessfullyState(payload: isAlive);
|
||||
} catch (e) {
|
||||
|
|
5
model_generator.sh
Normal file
5
model_generator.sh
Normal file
|
@ -0,0 +1,5 @@
|
|||
cd cw_core && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
cd cw_monero && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
cd cw_bitcoin && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
cd cw_haven && flutter pub get && flutter packages pub run build_runner build --delete-conflicting-outputs && cd ..
|
||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider}-Fehler",
|
||||
|
||||
"use_ssl" : "SSL verwenden",
|
||||
"trusted" : "Vertrauenswürdige",
|
||||
|
||||
"color_theme" : "Farbthema",
|
||||
"light_theme" : "Hell",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "Datenschutz",
|
||||
"display_settings": "Anzeigeeinstellungen",
|
||||
"other_settings": "Andere Einstellungen",
|
||||
"disable_exchange": "Exchange deaktivieren"
|
||||
"disable_exchange": "Exchange deaktivieren",
|
||||
"advanced_privacy_settings": "Erweiterte Datenschutzeinstellungen",
|
||||
"settings_can_be_changed_later": "Diese Einstellungen können später in den App-Einstellungen geändert werden",
|
||||
"add_custom_node": "Neuen benutzerdefinierten Knoten hinzufügen"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} error",
|
||||
|
||||
"use_ssl" : "Use SSL",
|
||||
"trusted" : "Trusted",
|
||||
|
||||
"color_theme" : "Color theme",
|
||||
"light_theme" : "Light",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "Privacy",
|
||||
"display_settings": "Display settings",
|
||||
"other_settings": "Other settings",
|
||||
"disable_exchange": "Disable exchange"
|
||||
"disable_exchange": "Disable exchange",
|
||||
"advanced_privacy_settings": "Advanced Privacy Settings",
|
||||
"settings_can_be_changed_later": "These settings can be changed later in the app settings",
|
||||
"add_custom_node": "Add New Custom Node"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} error",
|
||||
|
||||
"use_ssl" : "Utilice SSL",
|
||||
"trusted" : "de confianza",
|
||||
|
||||
"color_theme" : "Tema de color",
|
||||
"light_theme" : "Ligera",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "Privacidad",
|
||||
"display_settings": "Configuración de pantalla",
|
||||
"other_settings": "Otras configuraciones",
|
||||
"disable_exchange": "Deshabilitar intercambio"
|
||||
"disable_exchange": "Deshabilitar intercambio",
|
||||
"advanced_privacy_settings": "Configuración avanzada de privacidad",
|
||||
"settings_can_be_changed_later": "Estas configuraciones se pueden cambiar más tarde en la configuración de la aplicación",
|
||||
"add_custom_node": "Agregar nuevo nodo personalizado"
|
||||
}
|
||||
|
|
|
@ -435,6 +435,7 @@
|
|||
"provider_error" : "Erreur de ${provider}",
|
||||
|
||||
"use_ssl" : "Utiliser SSL",
|
||||
"trusted" : "de confiance",
|
||||
|
||||
"color_theme" : "Thème",
|
||||
"light_theme" : "Clair",
|
||||
|
@ -658,5 +659,8 @@
|
|||
"privacy": "Confidentialité",
|
||||
"display_settings": "Paramètres d'affichage",
|
||||
"other_settings": "Autres paramètres",
|
||||
"disable_exchange": "Désactiver l'échange"
|
||||
"disable_exchange": "Désactiver l'échange",
|
||||
"advanced_privacy_settings": "Paramètres de confidentialité avancés",
|
||||
"settings_can_be_changed_later": "Ces paramètres peuvent être modifiés ultérieurement dans les paramètres de l'application",
|
||||
"add_custom_node": "Ajouter un nouveau nœud personnalisé"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} त्रुटि",
|
||||
|
||||
"use_ssl" : "उपयोग SSL",
|
||||
"trusted" : "भरोसा",
|
||||
|
||||
"color_theme" : "रंग विषय",
|
||||
"light_theme" : "रोशनी",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "गोपनीयता",
|
||||
"display_settings": "प्रदर्शन सेटिंग्स",
|
||||
"other_settings": "अन्य सेटिंग्स",
|
||||
"disable_exchange": "एक्सचेंज अक्षम करें"
|
||||
"disable_exchange": "एक्सचेंज अक्षम करें",
|
||||
"advanced_privacy_settings": "उन्नत गोपनीयता सेटिंग्स",
|
||||
"settings_can_be_changed_later": "इन सेटिंग्स को बाद में ऐप सेटिंग में बदला जा सकता है",
|
||||
"add_custom_node": "नया कस्टम नोड जोड़ें"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} greška",
|
||||
|
||||
"use_ssl" : "Koristi SSL",
|
||||
"trusted" : "vjerovao",
|
||||
|
||||
"color_theme" : "Shema boja",
|
||||
"light_theme" : "Svijetla",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "Privatnost",
|
||||
"display_settings": "Postavke zaslona",
|
||||
"other_settings": "Ostale postavke",
|
||||
"disable_exchange": "Onemogući exchange"
|
||||
"disable_exchange": "Onemogući exchange",
|
||||
"advanced_privacy_settings": "Napredne postavke privatnosti",
|
||||
"settings_can_be_changed_later": "Te se postavke mogu promijeniti kasnije u postavkama aplikacije",
|
||||
"add_custom_node": "Dodaj novi prilagođeni čvor"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} errore",
|
||||
|
||||
"use_ssl" : "Usa SSL",
|
||||
"trusted" : "di fiducia",
|
||||
|
||||
"color_theme" : "Colore tema",
|
||||
"light_theme" : "Bianco",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "Privacy",
|
||||
"display_settings": "Impostazioni di visualizzazione",
|
||||
"other_settings": "Altre impostazioni",
|
||||
"disable_exchange": "Disabilita scambio"
|
||||
"disable_exchange": "Disabilita scambio",
|
||||
"advanced_privacy_settings": "Impostazioni avanzate sulla privacy",
|
||||
"settings_can_be_changed_later": "Queste impostazioni possono essere modificate in seguito nelle impostazioni dell'app",
|
||||
"add_custom_node": "Aggiungi nuovo nodo personalizzato"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} エラー",
|
||||
|
||||
"use_ssl" : "SSLを使用する",
|
||||
"trusted" : "信頼できる",
|
||||
|
||||
"color_theme" : "カラーテーマ",
|
||||
"light_theme" : "光",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "プライバシー",
|
||||
"display_settings": "表示設定",
|
||||
"other_settings": "その他の設定",
|
||||
"disable_exchange": "交換を無効にする"
|
||||
"disable_exchange": "交換を無効にする",
|
||||
"advanced_privacy_settings": "高度なプライバシー設定",
|
||||
"settings_can_be_changed_later": "これらの設定は、後でアプリの設定で変更できます",
|
||||
"add_custom_node": "新しいカスタム ノードを追加"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} 오류",
|
||||
|
||||
"use_ssl" : "SSL 사용",
|
||||
"trusted" : "신뢰할 수 있는",
|
||||
|
||||
"color_theme" : "색상 테마",
|
||||
"light_theme" : "빛",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "프라이버시",
|
||||
"display_settings": "디스플레이 설정",
|
||||
"other_settings": "기타 설정",
|
||||
"disable_exchange": "교환 비활성화"
|
||||
"disable_exchange": "교환 비활성화",
|
||||
"advanced_privacy_settings": "고급 개인 정보 설정",
|
||||
"settings_can_be_changed_later": "이 설정은 나중에 앱 설정에서 변경할 수 있습니다.",
|
||||
"add_custom_node": "새 사용자 정의 노드 추가"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} fout",
|
||||
|
||||
"use_ssl" : "Gebruik SSL",
|
||||
"trusted" : "vertrouwd",
|
||||
|
||||
"color_theme" : "Kleur thema",
|
||||
"light_theme" : "Licht",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "Privacy",
|
||||
"display_settings": "Weergave-instellingen",
|
||||
"other_settings": "Andere instellingen",
|
||||
"disable_exchange": "Uitwisseling uitschakelen"
|
||||
"disable_exchange": "Uitwisseling uitschakelen",
|
||||
"advanced_privacy_settings": "Geavanceerde privacy-instellingen",
|
||||
"settings_can_be_changed_later": "Deze instellingen kunnen later worden gewijzigd in de app-instellingen",
|
||||
"add_custom_node": "Voeg een nieuw aangepast knooppunt toe"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} pomyłka",
|
||||
|
||||
"use_ssl" : "Użyj SSL",
|
||||
"trusted" : "zaufany",
|
||||
|
||||
"color_theme" : "Motyw kolorystyczny",
|
||||
"light_theme" : "Lekki",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "Prywatność",
|
||||
"display_settings": "Ustawienia wyświetlania",
|
||||
"other_settings": "Inne ustawienia",
|
||||
"disable_exchange": "Wyłącz wymianę"
|
||||
"disable_exchange": "Wyłącz wymianę",
|
||||
"advanced_privacy_settings": "Zaawansowane ustawienia prywatności",
|
||||
"settings_can_be_changed_later": "Te ustawienia można później zmienić w ustawieniach aplikacji",
|
||||
"add_custom_node": "Dodaj nowy węzeł niestandardowy"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} erro",
|
||||
|
||||
"use_ssl" : "Use SSL",
|
||||
"trusted" : "confiável",
|
||||
|
||||
"color_theme" : "Tema de cor",
|
||||
"light_theme" : "Luz",
|
||||
|
@ -545,7 +546,6 @@
|
|||
"create_account": "Criar conta",
|
||||
"privacy_policy": "Política de privacidade",
|
||||
"welcome_to_cakepay": "Bem-vindo ao Cake Pay!",
|
||||
"create_account": "Registar-se",
|
||||
"forgot_password": "Esqueci a senha",
|
||||
"reset_password": "Redefinir senha",
|
||||
"gift_cards": "Cartões de presente",
|
||||
|
@ -660,5 +660,8 @@
|
|||
"privacy": "Privacidade",
|
||||
"display_settings": "Configurações de exibição",
|
||||
"other_settings": "Outras configurações",
|
||||
"disable_exchange": "Desativar troca"
|
||||
"disable_exchange": "Desativar troca",
|
||||
"advanced_privacy_settings": "Configurações de privacidade avançadas",
|
||||
"settings_can_be_changed_later": "Essas configurações podem ser alteradas posteriormente nas configurações do aplicativo",
|
||||
"add_custom_node": "Adicionar novo nó personalizado"
|
||||
}
|
||||
|
|
|
@ -437,6 +437,7 @@
|
|||
"provider_error" : "${provider} ошибка",
|
||||
|
||||
"use_ssl" : "Использовать SSL",
|
||||
"trusted" : "доверенный",
|
||||
|
||||
"color_theme" : "Цветовая тема",
|
||||
"light_theme" : "Светлая",
|
||||
|
@ -660,5 +661,8 @@
|
|||
"privacy": "Конфиденциальность",
|
||||
"display_settings": "Настройки отображения",
|
||||
"other_settings": "Другие настройки",
|
||||
"disable_exchange": "Отключить обмен"
|
||||
"disable_exchange": "Отключить обмен",
|
||||
"advanced_privacy_settings": "Расширенные настройки конфиденциальности",
|
||||
"settings_can_be_changed_later": "Эти настройки можно изменить позже в настройках приложения.",
|
||||
"add_custom_node": "Добавить новый пользовательский узел"
|
||||
}
|
||||
|
|
|
@ -436,6 +436,7 @@
|
|||
"provider_error" : "${provider} помилка",
|
||||
|
||||
"use_ssl" : "Використати SSL",
|
||||
"trusted" : "довіряють",
|
||||
|
||||
"color_theme" : "Кольорова тема",
|
||||
"light_theme" : "Світла",
|
||||
|
@ -659,5 +660,8 @@
|
|||
"privacy": "Конфіденційність",
|
||||
"display_settings": "Налаштування дисплея",
|
||||
"other_settings": "Інші налаштування",
|
||||
"disable_exchange": "Вимкнути exchange"
|
||||
"disable_exchange": "Вимкнути exchange",
|
||||
"advanced_privacy_settings": "Розширені налаштування конфіденційності",
|
||||
"settings_can_be_changed_later": "Ці параметри можна змінити пізніше в налаштуваннях програми",
|
||||
"add_custom_node": "Додати новий спеціальний вузол"
|
||||
}
|
||||
|
|
|
@ -436,6 +436,7 @@
|
|||
"provider_error" : "${provider} 错误",
|
||||
|
||||
"use_ssl" : "使用SSL",
|
||||
"trusted" : "值得信赖",
|
||||
|
||||
"color_theme" : "主题",
|
||||
"light_theme" : "艳丽",
|
||||
|
@ -658,5 +659,8 @@
|
|||
"privacy":"隐私",
|
||||
"display_settings": "显示设置",
|
||||
"other_settings": "其他设置",
|
||||
"disable_exchange": "禁用交换"
|
||||
"disable_exchange": "禁用交换",
|
||||
"advanced_privacy_settings": "高级隐私设置",
|
||||
"settings_can_be_changed_later": "稍后可以在应用设置中更改这些设置",
|
||||
"add_custom_node": "添加新的自定义节点"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue