Merge branches 'CW-447-mobile-scanner' and 'main' of https://github.com/cake-tech/cake_wallet into CW-447-mobile-scanner

This commit is contained in:
fosse 2023-08-11 10:41:13 -04:00
commit cda3fb6b02
73 changed files with 1028 additions and 455 deletions

View file

@ -127,6 +127,7 @@ jobs:
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart
echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_ethereum/lib/.secrets.g.dart
echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
- name: Rename app
run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties

View file

@ -1,6 +1,6 @@
Privacy Policy
Last modified: July 21, 2022
Last modified: August 9, 2023
Introduction
============
@ -13,7 +13,7 @@ Introduction
- On this App.
- In email, text, and other electronic messages between you and this App.
It does not apply to information collected by:
- Us offline or through any other means, including on any other App operated by Company or any third party (including our affiliates and subsidiaries)]; or
- Us offline or through any other means, including on any other App operated by Company or any third party (including our affiliates and subsidiaries); or
- Any third party (including our affiliates and subsidiaries), including through any application or content (including advertising) that may link to or be accessible from or on the App.
Please read this policy carefully to understand our policies and practices regarding your information and how we will treat it. If you do not agree with our policies and practices, you have the choice to not use the App. By accessing or using this App, you agree to this privacy policy. This policy may change from time to time. Your continued use of this App after we make changes is deemed to be acceptance of those changes, so please check the policy periodically for updates.
@ -25,7 +25,7 @@ Definitions
- "Node" means a server on a supported cryptocurrency network which transmits data to your App for processing and synchronization, and to which your Device transmits transactions which you would like to submit to the supported cryptocurrency networks. This includes full nodes, Electrum servers, and lightning network nodes.
- "Cake Labs Nodes" refers to the set of cryptocurrency nodes operated and maintained by Cake Labs LLC.
- "Service" refers to the App.
- "Third-party Service" refers to any service integrated into the App. This includes but is not limited to ChangeNOW, Wyre, MoonPay, and BlockBuy.
- "Third-party Service" refers to any service integrated into the App. This includes but is not limited to ChangeNOW, Onramper, and MoonPay.
- "Usage Data" refers to data collected automatically about your usage of an App.
- "You" means the individual, group, corporation, or any other entity accessing or using the Service.
@ -40,26 +40,29 @@ Information We Never Receive Nor Collect
Information We May Receive But Do Not Retain
--------------------------------------------
We receive but do NOT store information from and about users of our App, including:
We may receive but do NOT store information from and about users of our App, including:
- The device IP address, the block height to which your wallet is synchronized, and any transactions or channels which you use our Node to submit to supported cryptocurrency networks.
We receive this information:
- Automatically as you use the App.
- Automatically as you use the App, unless you turn certain features off in your App privacy settings.
This data is provided by connecting to the Nodes and price API maintained by Cake Labs. You have the right to choose not to provide synchronization data to Cake Labs by choosing a different Node. We provide a list of Nodes in the app that include our own and third party Nodes, or you can use your own Node (which we recommend).
This data is provided by connecting to the Nodes and price API maintained by Cake Labs. You have the right to choose not to provide synchronization data to Cake Labs by choosing a different Node. We provide a list of Nodes in the app that include our own and third party Nodes, or you can use your own Node (which we recommend). You have the right to choose not to connect to our Fiat API service by disabling this Fiat API in App privacy settings.
Personal Data sent through the Cake Labs Nodes is limited to your device's IP address, the block height to which your wallet is synchronized, and any transactions or channels which you use our Node to submit to the supported cryptocurrency networks. Personal Data received by Cake Labs in this manner is not stored for any length of time, and thus Cake Labs is both unwilling to and incapable of sharing this data, or using it for any purpose beyond ensuring your appropriate connection to our Nodes.
Personal Data that may be sent through the Cake Labs Nodes is limited to your device's IP address, the block height to which your wallet is synchronized, and any transactions or channels which you use our Node to submit to the supported cryptocurrency networks. Personal Data received by Cake Labs in this manner is not stored for any length of time, and thus Cake Labs is incapable of sharing this data and will not use it for any purpose beyond ensuring your appropriate connection to our Nodes.
If you decide to use a Node offered by any third party, some of which we include in our Apps, said third party will receive this Personal Data instead of Cake Labs. We take no responsibility for the actions of any third-party Node offered within the Application. We recommend connecting to your own Node to limit third party sharing of your Personal Information.
If you use our Fiat API service, you will share your IP address and the cryptocurrency and fiat currency exchange pair for which your wallet requests a spot price quote. You can disable this Fiat API in App privacy settings.
Information We May Collect About You and How We Collect It
----------------------------------------------------------
We collect several types of information from and about users of our App, including information:
- By which you may be personally identified, such as name, e-mail address, or and a/any other identifier by which you may be contacted online or offline ("personal information" or "Personal Data”), ONLY when you provide it to us;
- By which you may be personally identified, such as name, e-mail address, or and a/any other identifier by which you may be contacted online or offline ("personal information" or "Personal Data”);
- Device data and error log data;
We collect this information:
- Directly from you when you provide it to us.
- Directly from you ONLY when you provide it to us.
Personal information is received by Cake Labs ONLY in the event that you choose to provide it to us by voluntarily contacting Cake Labs regarding support, questions or suggestions.
Personal information is received by Cake Labs ONLY in the event that you choose to provide it to us by voluntarily contacting Cake Labs regarding support, questions or suggestions. You may optionally send us Error reports to help us improve the App. These Error reports contain error logs and basic device data. You can review and make modifications to these Error reports before sending them to us, or you may choose not to send them to us at all.
How We Use Your Information
---------------------------
@ -112,7 +115,9 @@ Data Security
Links to Other Websites
-----------------------
The App may contain links to other websites that are not operated by us. If you click on a third-party link, you will be directed to that third party's site. We strongly advise you to review the Privacy Policy of every site you visit. We have no control over and assume no responsibility for the content, privacy policies or practices of any third-party sites or services.
The App may contain links to other websites that are not operated by us. If you click on a Third-Party Service link, you will be directed to that third party's site. We strongly advise you to review the Privacy Policy of every site you visit. We have no control over and assume no responsibility for the content, privacy policies or practices of any third-party sites or services.
The App includes several optional Third-Party Services, which may not be available to all users. If you use Third-Party Services, you must agree to their respective Privacy Policies.
Changes to Our Privacy Policy
-----------------------------

View file

@ -1,4 +1,4 @@
# Cake Wallet for Android and iOS
# Cake Wallet for Mobile and Desktop
## Open Source Multi-Currency Wallet
@ -7,6 +7,7 @@
* Website: https://cakewallet.com
* App Store (iOS / MacOS): https://cakewallet.com/ios
* Google Play: https://cakewallet.com/gp
* F-Droid: https://fdroid.cakelabs.com
* APK: https://github.com/cake-tech/cake_wallet/releases
* Linux: https://github.com/cake-tech/cake_wallet/releases
@ -17,9 +18,8 @@
* Completely noncustodial. *Your keys, your coins.*
* Built-in exchange for dozens of pairs
* Easily pay cryptocurrency invoices with fixed rate exchanges
* Buy cryptocurrency (BTC/LTC/XMR) with credit/debit/bank
* Buy cryptocurrency (BTC/LTC/XMR/ETH) with credit/debit/bank
* Sell cryptocurrency by bank transfer
* Purchase gift cards at a discount using only an email with [Cake Pay](https://cakepay.com), available in-app
* Scan QR codes for easy cryptocurrency transfers
* Create several wallets
* Select your own custom nodes/servers
@ -32,6 +32,7 @@
* Convenient exchange and sending templates for recurring payments
* Create donation links and invoices in the receive screen
* Robust privacy settings (eg: Tor-only connections)
* Robust security settings (eg: Cake 2FA)
### Monero Specific Features
@ -40,13 +41,19 @@
* Specify restore height for faster syncing
* Specify multiple recipients for batch sending
* Optionally set Monero nodes as trusted for faster syncing
* Specify a proxy for Monero nodes, compatible with Tor and i2p
### Bitcoin Specific Features
* Bitcoin coin control (specify specific outputs to spend)
* Automatically generate new addresses
* Specify multiple recipients for batch sending
* Sell BTC for USD
### Ethereum Specific Features
* Store ETH and all ERc-20 tokens
* Add custom tokens by contract address
* Enable or disable Etherscan for transaction history
### Litecoin Specific Features
@ -69,6 +76,7 @@
* Website: https://monero.com
* App Store (iOS): https://apps.apple.com/app/id1601990386
* Google Play: https://play.google.com/store/apps/details?id=com.monero.app
* F-Droid: https://fdroid.cakelabs.com
* APK: https://github.com/cake-tech/cake_wallet/releases
# Support
@ -123,7 +131,7 @@ Edit the applicable `strings_XX.arb` file in `res/values/` and open a pull reque
2. Edit the strings in this file, replacing XXX below with the translation for each string.
`"welcome" : "Welcome to",` -> `"welcome" : "XXX",`
`"welcome": "Welcome to",` -> `"welcome": "XXX",`
3. For strings where there is a variable, denoted by a $ symbol and braces, such as ${status}, the string in braces should not be translated. For example, when editing line 106:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,4 @@
import 'package:hive/hive.dart';
import 'package:hive/src/hive_impl.dart';
final HiveInterface CakeHive = HiveImpl();

View file

@ -1,4 +1,5 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
part 'erc20_token.g.dart';
@ -53,7 +54,7 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin {
iconPath: icon,
);
static const typeId = 12;
static const typeId = ERC20_TOKEN_TYPE_ID;
static const boxName = 'Erc20Tokens';
@override

View file

@ -0,0 +1,13 @@
const CONTACT_TYPE_ID = 0;
const NODE_TYPE_ID = 1;
const TRANSACTION_TYPE_ID = 2;
const TRADE_TYPE_ID = 3;
const WALLET_INFO_TYPE_ID = 4;
const WALLET_TYPE_TYPE_ID = 5;
const TEMPLATE_TYPE_ID = 6;
const EXCHANGE_TEMPLATE_TYPE_ID = 7;
const ORDER_TYPE_ID = 8;
const UNSPENT_COINS_INFO_TYPE_ID = 9;
const ANONPAY_INVOICE_INFO_TYPE_ID = 10;
const ERC20_TOKEN_TYPE_ID = 12;

View file

@ -3,6 +3,7 @@ import 'package:cw_core/keyable.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:hive/hive.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:http/io_client.dart' as ioc;
@ -37,7 +38,7 @@ class Node extends HiveObject with Keyable {
trusted = map['trusted'] as bool? ?? false,
socksProxyAddress = map['socksProxyPort'] as String?;
static const typeId = 1;
static const typeId = NODE_TYPE_ID;
static const boxName = 'Nodes';
@HiveField(0, defaultValue: '')

View file

@ -1,3 +1,4 @@
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
part 'unspent_coins_info.g.dart';
@ -14,7 +15,7 @@ class UnspentCoinsInfo extends HiveObject {
required this.vout,
required this.value});
static const typeId = 9;
static const typeId = UNSPENT_COINS_INFO_TYPE_ID;
static const boxName = 'Unspent';
static const boxKey = 'unspentBoxKey';
@ -45,4 +46,4 @@ class UnspentCoinsInfo extends HiveObject {
String get note => noteRaw ?? '';
set note(String value) => noteRaw = value;
}
}

View file

@ -1,7 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:cw_core/wallet_type.dart';
import 'dart:async';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:hive/hive.dart';
part 'wallet_info.g.dart';
@ -30,7 +30,7 @@ class WalletInfo extends HiveObject {
yatEid, yatLastUsedAddressRaw, showIntroCakePayCard);
}
static const typeId = 4;
static const typeId = WALLET_INFO_TYPE_ID;
static const boxName = 'WalletInfo';
@HiveField(0, defaultValue: '')

View file

@ -1,4 +1,5 @@
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
part 'wallet_type.g.dart';
@ -10,9 +11,8 @@ const walletTypes = [
WalletType.haven,
WalletType.ethereum,
];
const walletTypeTypeId = 5;
@HiveType(typeId: walletTypeTypeId)
@HiveType(typeId: WALLET_TYPE_TYPE_ID)
enum WalletType {
@HiveField(0)
monero,

View file

@ -283,6 +283,13 @@ class DefaultErc20Tokens {
decimal: 18,
enabled: false,
),
Erc20Token(
name: "PayPal USD",
symbol: "PYUSD",
contractAddress: "0x6c3ea9036406852006290770bedfcaba0e23a0e8",
decimal: 6,
enabled: false,
),
];
List<Erc20Token> get initialErc20Tokens => _defaultTokens.map((token) {

View file

@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:math';
import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/pending_transaction.dart';
@ -58,8 +59,8 @@ abstract class EthereumWalletBase
this.walletInfo = walletInfo;
transactionHistory = EthereumTransactionHistory(walletInfo: walletInfo, password: password);
if (!Hive.isAdapterRegistered(Erc20Token.typeId)) {
Hive.registerAdapter(Erc20TokenAdapter());
if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) {
CakeHive.registerAdapter(Erc20TokenAdapter());
}
_sharedPrefs.complete(SharedPreferences.getInstance());
@ -95,7 +96,7 @@ abstract class EthereumWalletBase
Completer<SharedPreferences> _sharedPrefs = Completer();
Future<void> init() async {
erc20TokensBox = await Hive.openBox<Erc20Token>(Erc20Token.boxName);
erc20TokensBox = await CakeHive.openBox<Erc20Token>(Erc20Token.boxName);
await walletAddresses.init();
await transactionHistory.init();
_privateKey = await getPrivateKey(_mnemonic, _password);
@ -180,8 +181,15 @@ abstract class EthereumWalletBase
}
} else {
final output = outputs.first;
final BigInt allAmount =
_erc20Balance.balance - BigInt.from(calculateEstimatedFee(_credentials.priority!, null));
// since the fees are taken from Ethereum
// then no need to subtract the fees from the amount if send all
final BigInt allAmount;
if (transactionCurrency is Erc20Token) {
allAmount = _erc20Balance.balance;
} else {
allAmount = _erc20Balance.balance -
BigInt.from(calculateEstimatedFee(_credentials.priority!, null));
}
final totalOriginalAmount =
EthereumFormatter.parseEthereumAmountToDouble(output.formattedCryptoAmount ?? 0);
totalAmount = output.sendAll
@ -195,7 +203,9 @@ abstract class EthereumWalletBase
final pendingEthereumTransaction = await _client.signTransaction(
privateKey: _privateKey,
toAddress: _credentials.outputs.first.address,
toAddress: _credentials.outputs.first.isParsedAddress
? _credentials.outputs.first.extractedAddress!
: _credentials.outputs.first.address,
amount: totalAmount.toString(),
gas: _estimatedGas!,
priority: _credentials.priority!,

View file

@ -20,13 +20,19 @@ class PendingEthereumTransaction with PendingTransaction {
});
@override
String get amountFormatted => (BigInt.parse(amount) / BigInt.from(pow(10, exponent))).toString();
String get amountFormatted {
final _amount = BigInt.parse(amount) / BigInt.from(pow(10, exponent));
return _amount.toStringAsFixed(min(15, _amount.toString().length));
}
@override
Future<void> commit() async => await sendTransaction();
@override
String get feeFormatted => (fee / BigInt.from(pow(10, 18))).toString();
String get feeFormatted {
final _fee = fee / BigInt.from(pow(10, 18));
return _fee.toStringAsFixed(min(15, _fee.toString().length));
}
@override
String get hex => bytesToHex(signedTransaction, include0x: true);

View file

@ -1,4 +1,5 @@
import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/keyable.dart';
import 'package:hive/hive.dart';
@ -35,7 +36,7 @@ class AnonpayInvoiceInfo extends HiveObject with Keyable implements AnonpayInfoB
@HiveField(13)
final String provider;
static const typeId = 10;
static const typeId = ANONPAY_INVOICE_INFO_TYPE_ID;
static const boxName = 'AnonpayInvoiceInfo';
AnonpayInvoiceInfo({

View file

@ -1,7 +1,8 @@
import 'package:cake_wallet/buy/buy_provider_description.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
part 'order.g.dart';
@ -26,7 +27,7 @@ class Order extends HiveObject {
}
}
static const typeId = 8;
static const typeId = ORDER_TYPE_ID;
static const boxName = 'Orders';
static const boxKey = 'ordersBoxKey';
@ -66,4 +67,4 @@ class Order extends HiveObject {
BuyProviderDescription.deserialize(raw: providerRaw);
String amountFormatted() => formatAmount(amount);
}
}

View file

@ -1,7 +1,6 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
@ -10,6 +9,7 @@ import 'package:path_provider/path_provider.dart';
import 'package:cryptography/cryptography.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:archive/archive_io.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cake_wallet/core/key_service.dart';
import 'package:cake_wallet/entities/encrypt.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
@ -17,6 +17,7 @@ import 'package:cake_wallet/entities/secret_store_key.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
import 'package:cake_wallet/wallet_types.g.dart';
import 'package:cake_backup/backup.dart' as cake_backup;
class BackupService {
@ -170,14 +171,14 @@ class BackupService {
Future<Box<WalletInfo>> _reloadHiveWalletInfoBox() async {
final appDir = await getApplicationDocumentsDirectory();
await Hive.close();
Hive.init(appDir.path);
await CakeHive.close();
CakeHive.init(appDir.path);
if (!Hive.isAdapterRegistered(WalletInfo.typeId)) {
Hive.registerAdapter(WalletInfoAdapter());
if (!CakeHive.isAdapterRegistered(WalletInfo.typeId)) {
CakeHive.registerAdapter(WalletInfoAdapter());
}
return await Hive.openBox<WalletInfo>(WalletInfo.boxName);
return await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
}
Future<void> _importPreferencesDump() async {

View file

@ -37,6 +37,8 @@ import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart';
import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart';
@ -870,6 +872,12 @@ Future setup({
getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>()));
getIt.registerFactory(() =>
SupportChatPage(
getIt.get<SupportViewModel>(), secureStorage: getIt.get<FlutterSecureStorage>()));
getIt.registerFactory(() => SupportOtherLinksPage(getIt.get<SupportViewModel>()));
getIt.registerFactory(() {
final wallet = getIt.get<AppStore>().wallet;

View file

@ -1,8 +1,7 @@
import 'package:flutter/foundation.dart';
import 'package:hive/hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/utils/mobx.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/keyable.dart';
import 'package:hive/hive.dart';
part 'contact.g.dart';
@ -14,7 +13,7 @@ class Contact extends HiveObject with Keyable {
}
}
static const typeId = 0;
static const typeId = CONTACT_TYPE_ID;
static const boxName = 'Contacts';
@HiveField(0, defaultValue: '')

View file

@ -1,23 +1,18 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:hive/hive.dart';
import 'package:cw_core/cake_hive.dart';
Future<List<int>> getEncryptionKey(
{required String forKey, required FlutterSecureStorage secureStorage}) async {
final stringifiedKey =
await secureStorage.read(key: 'transactionDescriptionsBoxKey');
final stringifiedKey = await secureStorage.read(key: 'transactionDescriptionsBoxKey');
List<int> key;
if (stringifiedKey == null) {
key = Hive.generateSecureKey();
key = CakeHive.generateSecureKey();
final keyStringified = key.join(',');
await secureStorage.write(
key: 'transactionDescriptionsBoxKey', value: keyStringified);
await secureStorage.write(key: 'transactionDescriptionsBoxKey', value: keyStringified);
} else {
key = stringifiedKey
.split(',')
.map((i) => int.parse(i))
.toList();
key = stringifiedKey.split(',').map((i) => int.parse(i)).toList();
}
return key;
}
}

View file

@ -47,6 +47,7 @@ class MainActions {
case WalletType.bitcoin:
case WalletType.litecoin:
case WalletType.ethereum:
case WalletType.monero:
if (viewModel.isEnabledBuyAction) {
final uri = getIt.get<OnRamperBuyProvider>().requestUrl();
if (DeviceInfo.instance.isMobile) {
@ -57,13 +58,6 @@ class MainActions {
}
}
break;
case WalletType.monero:
if (viewModel.isEnabledBuyAction) {
// final uri = getIt.get<PayfuraBuyProvider>().requestUrl();
final uri = Uri.parse("https://monero.com/trade");
await launchUrl(uri);
}
break;
default:
await showPopUp<void>(
context: context,

View file

@ -19,14 +19,19 @@ class AddressResolver {
'crypto',
'zil',
'x',
'coin',
'wallet',
'bitcoin',
'888',
'nft',
'dao',
'blockchain',
'polygon'
'polygon',
'klever',
'hi',
'kresus',
'anime',
'manga',
'binanceus'
];
static String? extractAddressByType({required String raw, required CryptoCurrency type}) {

View file

@ -1,3 +1,4 @@
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
part 'template.g.dart';
@ -14,7 +15,7 @@ class Template extends HiveObject {
required this.amountFiatRaw,
this.additionalRecipientsRaw});
static const typeId = 6;
static const typeId = TEMPLATE_TYPE_ID;
static const boxName = 'Template';
@HiveField(0)

View file

@ -1,3 +1,4 @@
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
part 'transaction_description.g.dart';
@ -6,7 +7,7 @@ part 'transaction_description.g.dart';
class TransactionDescription extends HiveObject {
TransactionDescription({required this.id, this.recipientAddress, this.transactionNote});
static const typeId = 2;
static const typeId = TRANSACTION_TYPE_ID;
static const boxName = 'TransactionDescriptions';
static const boxKey = 'transactionDescriptionsBoxKey';

View file

@ -1,3 +1,4 @@
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
part 'exchange_template.g.dart';
@ -14,7 +15,7 @@ class ExchangeTemplate extends HiveObject {
required this.depositCurrencyTitleRaw,
required this.receiveCurrencyTitleRaw});
static const typeId = 7;
static const typeId = EXCHANGE_TEMPLATE_TYPE_ID;
static const boxName = 'ExchangeTemplate';
@HiveField(0)

View file

@ -1,8 +1,9 @@
import 'package:hive/hive.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cw_core/format_amount.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart';
part 'trade.g.dart';
@ -41,7 +42,7 @@ class Trade extends HiveObject {
}
}
static const typeId = 3;
static const typeId = TRADE_TYPE_ID;
static const boxName = 'Trades';
static const boxKey = 'tradesBoxKey';

View file

@ -7,6 +7,7 @@ import 'package:cake_wallet/locales/locale.dart';
import 'package:cake_wallet/store/yat/yat_store.dart';
import 'package:cake_wallet/utils/exception_handler.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@ -38,6 +39,7 @@ import 'package:uni_links/uni_links.dart';
import 'package:cw_core/unspent_coins_info.dart';
import 'package:cake_wallet/monero/monero.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/cake_hive.dart';
final navigatorKey = GlobalKey<NavigatorState>();
final rootKey = GlobalKey<RootState>();
@ -57,7 +59,7 @@ Future<void> main() async {
return true;
};
await Hive.close();
await CakeHive.close();
await initializeAppConfigs();
@ -69,50 +71,50 @@ Future<void> main() async {
Future<void> initializeAppConfigs() async {
final appDir = await getApplicationDocumentsDirectory();
Hive.init(appDir.path);
CakeHive.init(appDir.path);
if (!Hive.isAdapterRegistered(Contact.typeId)) {
Hive.registerAdapter(ContactAdapter());
if (!CakeHive.isAdapterRegistered(Contact.typeId)) {
CakeHive.registerAdapter(ContactAdapter());
}
if (!Hive.isAdapterRegistered(Node.typeId)) {
Hive.registerAdapter(NodeAdapter());
if (!CakeHive.isAdapterRegistered(Node.typeId)) {
CakeHive.registerAdapter(NodeAdapter());
}
if (!Hive.isAdapterRegistered(TransactionDescription.typeId)) {
Hive.registerAdapter(TransactionDescriptionAdapter());
if (!CakeHive.isAdapterRegistered(TransactionDescription.typeId)) {
CakeHive.registerAdapter(TransactionDescriptionAdapter());
}
if (!Hive.isAdapterRegistered(Trade.typeId)) {
Hive.registerAdapter(TradeAdapter());
if (!CakeHive.isAdapterRegistered(Trade.typeId)) {
CakeHive.registerAdapter(TradeAdapter());
}
if (!Hive.isAdapterRegistered(WalletInfo.typeId)) {
Hive.registerAdapter(WalletInfoAdapter());
if (!CakeHive.isAdapterRegistered(WalletInfo.typeId)) {
CakeHive.registerAdapter(WalletInfoAdapter());
}
if (!Hive.isAdapterRegistered(walletTypeTypeId)) {
Hive.registerAdapter(WalletTypeAdapter());
if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) {
CakeHive.registerAdapter(WalletTypeAdapter());
}
if (!Hive.isAdapterRegistered(Template.typeId)) {
Hive.registerAdapter(TemplateAdapter());
if (!CakeHive.isAdapterRegistered(Template.typeId)) {
CakeHive.registerAdapter(TemplateAdapter());
}
if (!Hive.isAdapterRegistered(ExchangeTemplate.typeId)) {
Hive.registerAdapter(ExchangeTemplateAdapter());
if (!CakeHive.isAdapterRegistered(ExchangeTemplate.typeId)) {
CakeHive.registerAdapter(ExchangeTemplateAdapter());
}
if (!Hive.isAdapterRegistered(Order.typeId)) {
Hive.registerAdapter(OrderAdapter());
if (!CakeHive.isAdapterRegistered(Order.typeId)) {
CakeHive.registerAdapter(OrderAdapter());
}
if (!isMoneroOnly && !Hive.isAdapterRegistered(UnspentCoinsInfo.typeId)) {
Hive.registerAdapter(UnspentCoinsInfoAdapter());
if (!isMoneroOnly && !CakeHive.isAdapterRegistered(UnspentCoinsInfo.typeId)) {
CakeHive.registerAdapter(UnspentCoinsInfoAdapter());
}
if (!Hive.isAdapterRegistered(AnonpayInvoiceInfo.typeId)) {
Hive.registerAdapter(AnonpayInvoiceInfoAdapter());
if (!CakeHive.isAdapterRegistered(AnonpayInvoiceInfo.typeId)) {
CakeHive.registerAdapter(AnonpayInvoiceInfoAdapter());
}
final secureStorage = FlutterSecureStorage();
@ -120,21 +122,21 @@ Future<void> initializeAppConfigs() async {
await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey);
final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey);
final ordersBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Order.boxKey);
final contacts = await Hive.openBox<Contact>(Contact.boxName);
final nodes = await Hive.openBox<Node>(Node.boxName);
final transactionDescriptions = await Hive.openBox<TransactionDescription>(
final contacts = await CakeHive.openBox<Contact>(Contact.boxName);
final nodes = await CakeHive.openBox<Node>(Node.boxName);
final transactionDescriptions = await CakeHive.openBox<TransactionDescription>(
TransactionDescription.boxName,
encryptionKey: transactionDescriptionsBoxKey);
final trades = await Hive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
final orders = await Hive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey);
final walletInfoSource = await Hive.openBox<WalletInfo>(WalletInfo.boxName);
final templates = await Hive.openBox<Template>(Template.boxName);
final exchangeTemplates = await Hive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
final anonpayInvoiceInfo = await Hive.openBox<AnonpayInvoiceInfo>(AnonpayInvoiceInfo.boxName);
final trades = await CakeHive.openBox<Trade>(Trade.boxName, encryptionKey: tradesBoxKey);
final orders = await CakeHive.openBox<Order>(Order.boxName, encryptionKey: ordersBoxKey);
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
final templates = await CakeHive.openBox<Template>(Template.boxName);
final exchangeTemplates = await CakeHive.openBox<ExchangeTemplate>(ExchangeTemplate.boxName);
final anonpayInvoiceInfo = await CakeHive.openBox<AnonpayInvoiceInfo>(AnonpayInvoiceInfo.boxName);
Box<UnspentCoinsInfo>? unspentCoinsInfoSource;
if (!isMoneroOnly) {
unspentCoinsInfoSource = await Hive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName);
unspentCoinsInfoSource = await CakeHive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName);
}
await initialSetup(

View file

@ -42,6 +42,8 @@ import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart';
import 'package:cake_wallet/src/screens/support/support_page.dart';
import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart';
import 'package:cake_wallet/src/screens/support_other_links/support_other_links_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/utils/payment_request.dart';
@ -49,7 +51,6 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.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:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
@ -443,9 +444,17 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.support:
return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<SupportPage>());
case Routes.supportLiveChat:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SupportChatPage>());
case Routes.supportOtherLinks:
return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<SupportOtherLinksPage>());
case Routes.unspentCoinsList:
return MaterialPageRoute<void>(
builder: (_) => getIt.get<UnspentCoinsListPage>());

View file

@ -48,6 +48,8 @@ class Routes {
static const editBackupPassword = '/edit_backup_passowrd';
static const restoreFromBackup = '/restore_from_backup';
static const support = '/support';
static const supportLiveChat = '/support/live_chat';
static const supportOtherLinks = '/support/other';
static const orderDetails = '/order_details';
static const preOrder = '/pre_order';
static const buyWebView = '/buy_web_view';

View file

@ -43,30 +43,31 @@ class DashboardPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(body: LayoutBuilder(
builder: (context, constraints) {
if (DeviceInfo.instance.isDesktop) {
if (constraints.maxWidth > ResponsiveLayoutUtil.kDesktopMaxDashBoardWidthConstraint) {
return getIt.get<DesktopSidebarWrapper>();
} else {
return Scaffold(
body: LayoutBuilder(
builder: (context, constraints) {
if (DeviceInfo.instance.isDesktop) {
if (constraints.maxWidth > ResponsiveLayoutUtil.kDesktopMaxDashBoardWidthConstraint) {
return getIt.get<DesktopSidebarWrapper>();
} else {
return _DashboardPageView(
balancePage: balancePage,
dashboardViewModel: dashboardViewModel,
addressListViewModel: addressListViewModel,
);
}
} else if (ResponsiveLayoutUtil.instance.shouldRenderMobileUI()) {
return _DashboardPageView(
balancePage: balancePage,
dashboardViewModel: dashboardViewModel,
addressListViewModel: addressListViewModel,
);
} else {
return getIt.get<DesktopSidebarWrapper>();
}
} else if (ResponsiveLayoutUtil.instance.shouldRenderMobileUI()) {
return _DashboardPageView(
balancePage: balancePage,
dashboardViewModel: dashboardViewModel,
addressListViewModel: addressListViewModel,
);
} else {
return getIt.get<DesktopSidebarWrapper>();
}
},
));
},
),
);
}
}
@ -89,13 +90,19 @@ class _DashboardPageView extends BasePage {
@override
Widget Function(BuildContext, Widget) get rootWrapper =>
(BuildContext context, Widget scaffold) => Container(
decoration: BoxDecoration(
gradient: LinearGradient(colors: [
Theme.of(context).colorScheme.secondary,
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).primaryColor,
], begin: Alignment.topRight, end: Alignment.bottomLeft)),
child: scaffold);
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.secondary,
Theme.of(context).scaffoldBackgroundColor,
Theme.of(context).primaryColor,
],
begin: Alignment.topRight,
end: Alignment.bottomLeft,
),
),
child: scaffold,
);
@override
bool get resizeToAvoidBottomInset => false;
@ -106,29 +113,30 @@ class _DashboardPageView extends BasePage {
@override
Widget middle(BuildContext context) {
return SyncIndicator(
dashboardViewModel: dashboardViewModel,
onTap: () => Navigator.of(context, rootNavigator: true).pushNamed(Routes.connectionSync));
dashboardViewModel: dashboardViewModel,
onTap: () => Navigator.of(context, rootNavigator: true).pushNamed(Routes.connectionSync),
);
}
@override
Widget trailing(BuildContext context) {
final menuButton = Image.asset('assets/images/menu.png',
color: Theme.of(context)
.accentTextTheme!
.displayMedium!
.backgroundColor);
final menuButton = Image.asset(
'assets/images/menu.png',
color: Theme.of(context).accentTextTheme.displayMedium!.backgroundColor,
);
return Container(
alignment: Alignment.centerRight,
width: 40,
child: TextButton(
// FIX-ME: Style
//highlightColor: Colors.transparent,
//splashColor: Colors.transparent,
//padding: EdgeInsets.all(0),
onPressed: () => onOpenEndDrawer(),
child: Semantics(
label: S.of(context).wallet_menu, child: menuButton)));
alignment: Alignment.centerRight,
width: 40,
child: TextButton(
// FIX-ME: Style
//highlightColor: Colors.transparent,
//splashColor: Colors.transparent,
//padding: EdgeInsets.all(0),
onPressed: () => onOpenEndDrawer(),
child: Semantics(label: S.of(context).wallet_menu, child: menuButton),
),
);
}
final DashboardViewModel dashboardViewModel;
@ -142,56 +150,65 @@ class _DashboardPageView extends BasePage {
Widget body(BuildContext context) {
final controller = PageController(initialPage: initialPage);
reaction((_) => dashboardViewModel.shouldShowMarketPlaceInDashboard, (bool value) {
if (!dashboardViewModel.shouldShowMarketPlaceInDashboard) {
controller.jumpToPage(0);
}
pages.clear();
_isEffectsInstalled = false;
_setEffects(context);
reaction(
(_) => dashboardViewModel.shouldShowMarketPlaceInDashboard,
(bool value) {
if (!dashboardViewModel.shouldShowMarketPlaceInDashboard) {
controller.jumpToPage(0);
}
pages.clear();
_isEffectsInstalled = false;
_setEffects(context);
if (value) {
controller.jumpToPage(1);
} else {
controller.jumpToPage(0);
}
});
if (value) {
controller.jumpToPage(1);
} else {
controller.jumpToPage(0);
}
},
);
_setEffects(context);
return SafeArea(
minimum: EdgeInsets.only(bottom: 24),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Observer(builder: (context) {
return PageView.builder(
minimum: EdgeInsets.only(bottom: 24),
child: Column(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Observer(
builder: (context) {
return PageView.builder(
controller: controller,
itemCount: pages.length,
itemBuilder: (context, index) => pages[index]);
})),
Padding(
padding: EdgeInsets.only(bottom: 24, top: 10),
child: Observer(builder: (context) {
return ExcludeSemantics(
child: SmoothPageIndicator(
controller: controller,
count: pages.length,
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).indicatorColor,
activeDotColor: Theme.of(context)
.accentTextTheme!
.headlineMedium!
.backgroundColor!),
itemBuilder: (context, index) => pages[index],
);
},
),
),
Padding(
padding: EdgeInsets.only(bottom: 24, top: 10),
child: Observer(
builder: (context) {
return ExcludeSemantics(
child: SmoothPageIndicator(
controller: controller,
count: pages.length,
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).indicatorColor,
activeDotColor:
Theme.of(context).accentTextTheme.headlineMedium!.backgroundColor!,
),
);
}
)),
Observer(builder: (_) {
),
);
},
),
),
Observer(
builder: (_) {
return ClipRect(
child: Container(
margin: const EdgeInsets.only(left: 16, right: 16),
@ -204,10 +221,7 @@ class _DashboardPageView extends BasePage {
: Colors.transparent,
width: 1,
),
color: Theme.of(context)
.textTheme!
.titleLarge!
.backgroundColor!,
color: Theme.of(context).textTheme.titleLarge!.backgroundColor!,
),
child: Container(
padding: EdgeInsets.only(left: 32, right: 32),
@ -215,48 +229,48 @@ class _DashboardPageView extends BasePage {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: MainActions.all
.where((element) => element.canShow?.call(dashboardViewModel) ?? true)
.map((action) => Semantics(
button: true,
enabled: (action.isEnabled
?.call(dashboardViewModel) ??
true),
child: ActionButton(
image: Image.asset(action.image,
height: 24,
width: 24,
color: action.isEnabled?.call(
dashboardViewModel) ??
true
? Theme.of(context)
.accentTextTheme!
.displayMedium!
.backgroundColor!
: Theme.of(context)
.accentTextTheme!
.displaySmall!
.backgroundColor!),
title: action.name(context),
onClick: () async => await action.onTap(
context, dashboardViewModel),
textColor: action.isEnabled
?.call(dashboardViewModel) ??
true
? null
.map(
(action) => Semantics(
button: true,
enabled: (action.isEnabled?.call(dashboardViewModel) ?? true),
child: ActionButton(
image: Image.asset(
action.image,
height: 24,
width: 24,
color: action.isEnabled?.call(dashboardViewModel) ?? true
? Theme.of(context)
.accentTextTheme
.displayMedium!
.backgroundColor!
: Theme.of(context)
.accentTextTheme!
.accentTextTheme
.displaySmall!
.backgroundColor!,
),
))
title: action.name(context),
onClick: () async =>
await action.onTap(context, dashboardViewModel),
textColor: action.isEnabled?.call(dashboardViewModel) ?? true
? null
: Theme.of(context)
.accentTextTheme
.displaySmall!
.backgroundColor!,
),
),
)
.toList(),
),
),
),
),
);
}),
],
));
},
),
],
),
);
}
void _setEffects(BuildContext context) async {
@ -264,7 +278,8 @@ class _DashboardPageView extends BasePage {
return;
}
if (dashboardViewModel.shouldShowMarketPlaceInDashboard) {
pages.add(Semantics(
pages.add(
Semantics(
label: S.of(context).market_place,
child: MarketPlacePage(
dashboardViewModel: dashboardViewModel,
@ -274,73 +289,92 @@ class _DashboardPageView extends BasePage {
);
}
pages.add(Semantics(label: S.of(context).balance_page, child: balancePage));
pages.add(Semantics(
pages.add(
Semantics(
label: S.of(context).settings_transactions,
child: TransactionsPage(dashboardViewModel: dashboardViewModel)));
child: TransactionsPage(dashboardViewModel: dashboardViewModel),
),
);
_isEffectsInstalled = true;
autorun((_) async {
if (!dashboardViewModel.isOutdatedElectrumWallet) {
return;
}
autorun(
(_) async {
if (!dashboardViewModel.isOutdatedElectrumWallet) {
return;
}
await Future<void>.delayed(Duration(seconds: 1));
if (context.mounted) {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
await Future<void>.delayed(Duration(seconds: 1));
if (context.mounted) {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.of(context).pre_seed_title,
alertContent: S.of(context).outdated_electrum_wallet_description,
buttonText: S.of(context).understand,
buttonAction: () => Navigator.of(context).pop());
});
}
});
buttonAction: () => Navigator.of(context).pop(),
);
},
);
}
},
);
_showReleaseNotesPopup(context);
var needToPresentYat = false;
var isInactive = false;
_onInactiveSub = rootKey.currentState!.isInactive.listen((inactive) {
isInactive = inactive;
_onInactiveSub = rootKey.currentState!.isInactive.listen(
(inactive) {
isInactive = inactive;
if (needToPresentYat) {
Future<void>.delayed(Duration(milliseconds: 500)).then((_) {
showPopUp<void>(
context: navigatorKey.currentContext!,
builder: (_) => YatEmojiId(dashboardViewModel.yatStore.emoji));
needToPresentYat = false;
});
}
});
if (needToPresentYat) {
Future<void>.delayed(Duration(milliseconds: 500)).then(
(_) {
showPopUp<void>(
context: navigatorKey.currentContext!,
builder: (_) => YatEmojiId(dashboardViewModel.yatStore.emoji),
);
needToPresentYat = false;
},
);
}
},
);
dashboardViewModel.yatStore.emojiIncommingStream.listen((String emoji) {
if (!_isEffectsInstalled || emoji.isEmpty) {
return;
}
dashboardViewModel.yatStore.emojiIncommingStream.listen(
(String emoji) {
if (!_isEffectsInstalled || emoji.isEmpty) {
return;
}
needToPresentYat = true;
});
needToPresentYat = true;
},
);
}
void _showReleaseNotesPopup(BuildContext context) async {
final sharedPrefs = await SharedPreferences.getInstance();
final currentAppVersion =
VersionComparator.getExtendedVersionNumber(dashboardViewModel.settingsStore.appVersion);
VersionComparator.getExtendedVersionNumber(dashboardViewModel.settingsStore.appVersion);
final lastSeenAppVersion = sharedPrefs.getInt(PreferencesKey.lastSeenAppVersion);
final isNewInstall = sharedPrefs.getBool(PreferencesKey.isNewInstall);
if (currentAppVersion != lastSeenAppVersion && !isNewInstall!) {
Future<void>.delayed(Duration(seconds: 1), () {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return ReleaseNotesScreen(
title: 'Version ${dashboardViewModel.settingsStore.appVersion}');
});
});
Future<void>.delayed(
Duration(seconds: 1),
() {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return ReleaseNotesScreen(
title: 'Version ${dashboardViewModel.settingsStore.appVersion}',
);
},
);
},
);
sharedPrefs.setInt(PreferencesKey.lastSeenAppVersion, currentAppVersion);
} else if (isNewInstall!) {

View file

@ -2,12 +2,10 @@ import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/language_list.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:flutter/cupertino.dart';

View file

@ -1,12 +1,11 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class RestoreButton extends StatelessWidget {
const RestoreButton({
required this.onPressed,
required this.image,
required this.title,
required this.description});
const RestoreButton(
{required this.onPressed,
required this.image,
required this.title,
required this.description});
final VoidCallback onPressed;
final Image image;
@ -24,10 +23,7 @@ class RestoreButton extends StatelessWidget {
alignment: Alignment.topLeft,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
color: Theme.of(context)
.accentTextTheme!
.bodySmall!
.color!,
color: Theme.of(context).accentTextTheme.bodySmall!.color!,
),
child: Row(
mainAxisSize: MainAxisSize.max,
@ -49,7 +45,7 @@ class RestoreButton extends StatelessWidget {
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context)
.primaryTextTheme!
.primaryTextTheme
.titleLarge!
.color!),
),
@ -61,7 +57,7 @@ class RestoreButton extends StatelessWidget {
fontSize: 14,
fontWeight: FontWeight.normal,
color: Theme.of(context)
.primaryTextTheme!
.primaryTextTheme
.labelSmall!
.color!),
),

View file

@ -6,7 +6,6 @@ class SettingsCellWithArrow extends StandardListRow {
: super(title: title, isSelected: false, onTap: handler);
@override
Widget buildTrailing(BuildContext context) =>
Image.asset('assets/images/select_arrow.png',
color: Theme.of(context).primaryTextTheme!.labelSmall!.color!);
}
Widget buildTrailing(BuildContext context) => Image.asset('assets/images/select_arrow.png',
color: Theme.of(context).primaryTextTheme.labelSmall!.color!);
}

View file

@ -1,55 +1,78 @@
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/settings/link_list_item.dart';
import 'package:cake_wallet/view_model/settings/regular_list_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/support/widgets/support_tiles.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/view_model/support_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:url_launcher/url_launcher.dart';
class SupportPage extends BasePage {
SupportPage(this.supportViewModel);
final SupportViewModel supportViewModel;
final imageLiveSupport = Image.asset('assets/images/live_support.png');
final imageWalletGuides = Image.asset('assets/images/wallet_guides.png');
final imageMoreLinks = Image.asset('assets/images/more_links.png');
@override
String get title => S.current.settings_support;
@override
AppBarStyle get appBarStyle => AppBarStyle.regular;
@override
Widget body(BuildContext context) {
final iconColor = Theme.of(context)
.accentTextTheme!
.displayLarge!
.backgroundColor!;
// FIX-ME: Added `context` it was not used here before, maby bug ?
return Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 500),
child: SectionStandardList(
context: context,
sectionCount: 1,
itemCounter: (int _) => supportViewModel.items.length,
itemBuilder: (_, __, index) {
final item = supportViewModel.items[index];
if (item is RegularListItem) {
return SettingsCellWithArrow(title: item.title, handler: item.handler);
}
if (item is LinkListItem) {
return SettingsLinkProviderCell(
title: item.title,
icon: item.icon,
iconColor: item.hasIconColor ? iconColor : null,
link: item.link,
linkTitle: item.linkTitle);
}
return Container();
}),
return Container(
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 330),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(top: 24),
child: SupportTile(
image: imageLiveSupport,
title: S.of(context).support_title_live_chat,
description: S.of(context).support_description_live_chat,
onPressed: () {
if (DeviceInfo.instance.isDesktop) {
_launchUrl(supportViewModel.fetchUrl());
} else {
Navigator.pushNamed(context, Routes.supportLiveChat);
}
},
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: SupportTile(
image: imageWalletGuides,
title: S.of(context).support_title_guides,
description: S.of(context).support_description_guides,
onPressed: () => _launchUrl(supportViewModel.guidesUrl),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: SupportTile(
image: imageMoreLinks,
title: S.of(context).support_title_other_links,
description: S.of(context).support_description_other_links,
onPressed: () => Navigator.pushNamed(context, Routes.supportOtherLinks),
),
),
],
),
),
),
);
}
void _launchUrl(String url) async {
try {
await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);
} catch (e) {}
}
}

View file

@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
class SupportTile extends StatelessWidget {
const SupportTile(
{required this.onPressed,
required this.image,
required this.title,
required this.description});
final VoidCallback onPressed;
final Image image;
final String title;
final String description;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: onPressed,
child: Container(
width: double.infinity,
padding: EdgeInsets.all(24),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
color: Theme.of(context).accentTextTheme.bodySmall!.color!,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
image,
Expanded(
child: Padding(
padding: EdgeInsets.only(left: 16),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
title,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color: Theme.of(context).primaryTextTheme.titleLarge!.color!,
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Text(
description,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.normal,
color: Theme.of(context).primaryTextTheme.labelSmall!.color!,
),
),
)
],
),
),
)
],
),
),
);
}
}

View file

@ -0,0 +1,38 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/support_chat/widgets/chatwoot_widget.dart';
import 'package:cake_wallet/view_model/support_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SupportChatPage extends BasePage {
SupportChatPage(this.supportViewModel, {required this.secureStorage});
final SupportViewModel supportViewModel;
final FlutterSecureStorage secureStorage;
@override
String get title => S.current.settings_support;
@override
AppBarStyle get appBarStyle => AppBarStyle.regular;
@override
Widget body(BuildContext context) => FutureBuilder<String>(
future: getCookie(),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
print(snapshot.data);
if (snapshot.hasData)
return ChatwootWidget(
secureStorage,
supportUrl: supportViewModel.fetchUrl(authToken: snapshot.data!)
);
return Container();
},
);
Future<String> getCookie() async {
return await secureStorage.read(key: COOKIE_KEY) ?? "";
}
}

View file

@ -0,0 +1,62 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
const COOKIE_KEY = 'chatwootCookie';
class ChatwootWidget extends StatefulWidget {
ChatwootWidget(this.secureStorage, {required this.supportUrl});
final FlutterSecureStorage secureStorage;
final String supportUrl;
@override
ChatwootWidgetState createState() => ChatwootWidgetState();
}
class ChatwootWidgetState extends State<ChatwootWidget> {
final GlobalKey _webViewkey = GlobalKey();
@override
Widget build(BuildContext context) => InAppWebView(
key: _webViewkey,
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(transparentBackground: true),
),
initialUrlRequest: URLRequest(url: Uri.tryParse(widget.supportUrl)),
onWebViewCreated: (InAppWebViewController controller) {
controller.addWebMessageListener(
WebMessageListener(
jsObjectName: 'ReactNativeWebView',
onPostMessage: (String? message, Uri? sourceOrigin, bool isMainFrame,
JavaScriptReplyProxy replyProxy) {
final shortenedMessage = message?.substring(16);
if (shortenedMessage != null && isJsonString(shortenedMessage)) {
final parsedMessage = jsonDecode(shortenedMessage);
final eventType = parsedMessage["event"];
if (eventType == 'loaded') {
final authToken = parsedMessage["config"]["authToken"];
print(authToken);
storeCookie(authToken as String);
}
}
},
),
);
},
);
bool isJsonString(String str) {
try {
jsonDecode(str);
} catch (e) {
return false;
}
return true;
}
Future<void> storeCookie(String value) async =>
await widget.secureStorage.write(key: COOKIE_KEY, value: value);
}

View file

@ -0,0 +1,58 @@
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/view_model/settings/link_list_item.dart';
import 'package:cake_wallet/view_model/settings/regular_list_item.dart';
import 'package:cake_wallet/view_model/support_view_model.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart';
class SupportOtherLinksPage extends BasePage {
SupportOtherLinksPage(this.supportViewModel);
final SupportViewModel supportViewModel;
@override
String get title => S.current.settings_support;
@override
AppBarStyle get appBarStyle => AppBarStyle.transparent;
@override
Widget body(BuildContext context) {
final iconColor =
Theme.of(context).accentTextTheme.displayLarge!.backgroundColor!;
return Container(
child: Center(
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 500),
child: SectionStandardList(
context: context,
sectionCount: 1,
itemCounter: (int _) => supportViewModel.items.length,
itemBuilder: (_, __, index) {
final item = supportViewModel.items[index];
if (item is RegularListItem) {
return SettingsCellWithArrow(
title: item.title, handler: item.handler);
}
if (item is LinkListItem) {
return SettingsLinkProviderCell(
title: item.title,
icon: item.icon,
iconColor: item.hasIconColor ? iconColor : null,
link: item.link,
linkTitle: item.linkTitle);
}
return Container();
}),
),
),
);
}
}

View file

@ -144,7 +144,10 @@ class ExceptionHandler {
"Connection closed before full header was received",
"Connection terminated during handshake",
"PERMISSION_NOT_GRANTED",
"Failed host lookup: ",
"Failed host lookup:",
"CERTIFICATE_VERIFY_FAILED",
"Handshake error in client",
"Error while launching http",
];
static Future<void> _addDeviceInfo(File file) async {

View file

@ -1,12 +1,9 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/view_model/settings/link_list_item.dart';
import 'package:cake_wallet/view_model/settings/regular_list_item.dart';
import 'package:cake_wallet/view_model/settings/settings_list_item.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:mobx/mobx.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:mobx/mobx.dart';
import 'package:cake_wallet/.secrets.g.dart' as secrets;
part 'support_view_model.g.dart';
@ -15,14 +12,6 @@ class SupportViewModel = SupportViewModelBase with _$SupportViewModel;
abstract class SupportViewModelBase with Store {
SupportViewModelBase()
: items = [
RegularListItem(
title: S.current.faq,
handler: (BuildContext context) async {
try {
await launch(url);
} catch (e) {}
},
),
LinkListItem(
title: 'Email',
linkTitle: 'support@cakewallet.com',
@ -85,7 +74,17 @@ abstract class SupportViewModelBase with Store {
// link: 'mailto:support@y.at')
];
static const url = 'https://guides.cakewallet.com';
final guidesUrl = 'https://guides.cakewallet.com';
String fetchUrl({String locale = "en", String authToken = ""}) {
var supportUrl =
"https://support.cakewallet.com/widget?website_token=${secrets.chatwootWebsiteToken}&locale=${locale}";
if (authToken.isNotEmpty)
supportUrl += "&cw_conversation=$authToken";
return supportUrl;
}
List<SettingsListItem> items;
}
}

View file

@ -102,6 +102,7 @@ dev_dependencies:
# check flutter_launcher_icons for usage
pedantic: ^1.8.0
# replace https://github.com/dart-lang/lints#migrating-from-packagepedantic
translator: ^0.1.7
flutter_icons:
image_path: "assets/images/app_logo.png"

View file

@ -630,18 +630,18 @@
"setup_totp_recommended": "إعداد TOTP (موصى به)",
"disable_buy": "تعطيل إجراء الشراء",
"disable_sell": "قم بتعطيل إجراء البيع",
"cake_2fa_preset" : " كعكة 2FA مسبقا",
"cake_2fa_preset": " كعكة 2FA مسبقا",
"narrow": "ضيق",
"normal": "طبيعي",
"aggressive": "عنيف",
"require_for_assessing_wallet": "تتطلب الوصول إلى المحفظة",
"require_for_sends_to_non_contacts" : "تتطلب لارسال لغير جهات الاتصال",
"require_for_sends_to_contacts" : "تتطلب لارسال جهات الاتصال",
"require_for_sends_to_internal_wallets" : "تتطلب عمليات الإرسال إلى المحافظ الداخلية",
"require_for_exchanges_to_internal_wallets" : "تتطلب عمليات التبادل إلى المحافظ الداخلية",
"require_for_adding_contacts" : "تتطلب إضافة جهات اتصال",
"require_for_creating_new_wallets" : "تتطلب إنشاء محافظ جديدة",
"require_for_all_security_and_backup_settings" : "مطلوب لجميع إعدادات الأمان والنسخ الاحتياطي",
"require_for_sends_to_non_contacts": "تتطلب لارسال لغير جهات الاتصال",
"require_for_sends_to_contacts": "تتطلب لارسال جهات الاتصال",
"require_for_sends_to_internal_wallets": "تتطلب عمليات الإرسال إلى المحافظ الداخلية",
"require_for_exchanges_to_internal_wallets": "تتطلب عمليات التبادل إلى المحافظ الداخلية",
"require_for_adding_contacts": "تتطلب إضافة جهات اتصال",
"require_for_creating_new_wallets": "تتطلب إنشاء محافظ جديدة",
"require_for_all_security_and_backup_settings": "مطلوب لجميع إعدادات الأمان والنسخ الاحتياطي",
"available_balance_description": "الرصيد المتاح هو الرصيد الذي يمكنك إنفاقه أو تحويله إلى محفظة أخرى. يتم تجميد الرصيد المتاح للمعاملات الصادرة والمعاملات الواردة غير المؤكدة.",
"syncing_wallet_alert_title": "محفظتك تتم مزامنتها",
"syncing_wallet_alert_content": "قد لا يكتمل رصيدك وقائمة المعاملات الخاصة بك حتى تظهر عبارة “SYNCHRONIZED“ في الأعلى. انقر / اضغط لمعرفة المزيد.",
@ -668,5 +668,11 @@
"slidable": "قابل للانزلاق",
"etherscan_history": "Etherscan تاريخ",
"manage_nodes": "ﺪﻘﻌﻟﺍ ﺓﺭﺍﺩﺇ",
"template_name": "اسم القالب"
}
"template_name": "اسم القالب",
"support_title_live_chat": "الدعم المباشر",
"support_description_live_chat": "حرة وسريعة! ممثلو الدعم المدربين متاحون للمساعدة",
"support_title_guides": "أدلة محفظة كعكة",
"support_description_guides": "توثيق ودعم القضايا المشتركة",
"support_title_other_links": "روابط دعم أخرى",
"support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى"
}

View file

@ -664,5 +664,11 @@
"slidable": "Плъзгащ се",
"etherscan_history": "История на Etherscan",
"manage_nodes": "Управление на възли",
"template_name": "Име на шаблон"
"template_name": "Име на шаблон",
"support_title_live_chat": "Подкрепа на живо",
"support_description_live_chat": "Безплатно и бързо! Обучени представители на подкрепата са на разположение за подпомагане",
"support_title_guides": "Ръководства за портфейл за торта",
"support_description_guides": "Документация и подкрепа за общи проблеми",
"support_title_other_links": "Други връзки за поддръжка",
"support_description_other_links": "Присъединете се към нашите общности или се свържете с нас нашите партньори чрез други методи"
}

View file

@ -664,5 +664,11 @@
"slidable": "Posuvné",
"manage_nodes": "Spravovat uzly",
"etherscan_history": "Historie Etherscanu",
"template_name": "Název šablony"
"template_name": "Název šablony",
"support_title_live_chat": "Živá podpora",
"support_description_live_chat": "Zdarma a rychle! K dispozici jsou zástupci vyškolených podpůrných podpory",
"support_title_guides": "Průvodce peněženkami dortu",
"support_description_guides": "Dokumentace a podpora běžných otázek",
"support_title_other_links": "Další odkazy na podporu",
"support_description_other_links": "Připojte se k našim komunitám nebo se k nám oslovte další metody"
}

View file

@ -211,7 +211,7 @@
"settings_only_trades": "Nur Handel",
"settings_only_transactions": "Nur Transaktionen",
"settings_none": "Keiner",
"settings_support": "Unterstützen",
"settings_support": "Hilfe",
"settings_terms_and_conditions": "Geschäftsbedingungen",
"pin_is_incorrect": "PIN ist falsch",
"setup_pin": "PIN einrichten",
@ -672,5 +672,11 @@
"slidable": "Verschiebbar",
"manage_nodes": "Knoten verwalten",
"etherscan_history": "Etherscan-Geschichte",
"template_name": "Vorlagenname"
"template_name": "Vorlagenname",
"support_title_live_chat": "Live Support",
"support_description_live_chat": "Kostenlos und schnell! Ausgebildete Mitarbeiter stehen zur Unterstützung bereit, um zu helfen",
"support_title_guides": "Cake Wallet Guides",
"support_description_guides": "Dokumentation und Hilfe für bekannte Probleme",
"support_title_other_links": "Andere Support-Links",
"support_description_other_links": "Treten Sie unseren Communities bei oder erreichen Sie uns oder unsere Partner über andere Methoden"
}

View file

@ -672,5 +672,11 @@
"slidable": "Slidable",
"manage_nodes": "Manage nodes",
"etherscan_history": "Etherscan history",
"template_name": "Template Name"
"template_name": "Template Name",
"support_title_live_chat": "Live support",
"support_description_live_chat": "Free and fast! Trained support representatives are available to assist",
"support_title_guides": "Cake Wallet guides",
"support_description_guides": "Documentation and support for common issues",
"support_title_other_links": "Other support links",
"support_description_other_links": "Join our communities or reach us our our partners through other methods"
}

View file

@ -672,5 +672,11 @@
"slidable": "deslizable",
"manage_nodes": "Administrar nodos",
"etherscan_history": "historia de etherscan",
"template_name": "Nombre de la plantilla"
"template_name": "Nombre de la plantilla",
"support_title_live_chat": "Soporte vital",
"support_description_live_chat": "¡GRATIS y RÁPIDO! Los representantes de apoyo capacitado están disponibles para ayudar",
"support_title_guides": "Guías de billetera para pastel",
"support_description_guides": "Documentación y apoyo para problemas comunes",
"support_title_other_links": "Otros enlaces de soporte",
"support_description_other_links": "Únase a nuestras comunidades o comuníquese con nosotros nuestros socios a través de otros métodos"
}

View file

@ -672,5 +672,11 @@
"slidable": "Glissable",
"manage_nodes": "Gérer les nœuds",
"etherscan_history": "Historique d'Etherscan",
"template_name": "Nom du modèle"
"template_name": "Nom du modèle",
"support_title_live_chat": "Support en direct",
"support_description_live_chat": "GRATUIT ET RAPIDE! Des représentants de soutien formé sont disponibles pour aider",
"support_title_guides": "Guides de portefeuille à gâteau",
"support_description_guides": "Documentation et soutien aux problèmes communs",
"support_title_other_links": "Autres liens d'assistance",
"support_description_other_links": "Rejoignez nos communautés ou contactez-nous nos partenaires à travers d'autres méthodes"
}

View file

@ -650,5 +650,11 @@
"slidable": "Mai iya zamewa",
"etherscan_history": "Etherscan tarihin kowane zamani",
"manage_nodes": "Sarrafa nodes",
"template_name": "Sunan Samfura"
"template_name": "Sunan Samfura",
"support_title_live_chat": "Tallafi na Live",
"support_description_live_chat": "Kyauta da sauri! An horar da wakilan tallafi na tallafi don taimakawa",
"support_title_guides": "Jagorar Cake",
"support_description_guides": "Tallafi da tallafi don batutuwa na yau da kullun",
"support_title_other_links": "Sauran hanyoyin tallafi",
"support_description_other_links": "Kasance tare da al'ummominmu ko kuma ka kai mu abokanmu ta hanyar wasu hanyoyi"
}

View file

@ -672,5 +672,11 @@
"slidable": "फिसलने लायक",
"manage_nodes": "नोड्स प्रबंधित करें",
"etherscan_history": "इथरस्कैन इतिहास",
"template_name": "टेम्पलेट नाम"
"template_name": "टेम्पलेट नाम",
"support_title_live_chat": "लाइव सहायता",
"support_description_live_chat": "मुक्त और तेजी से! प्रशिक्षित सहायता प्रतिनिधि सहायता के लिए उपलब्ध हैं",
"support_title_guides": "केक वॉलेट गाइड",
"support_description_guides": "सामान्य मुद्दों के लिए प्रलेखन और समर्थन",
"support_title_other_links": "अन्य समर्थन लिंक",
"support_description_other_links": "हमारे समुदायों में शामिल हों या अन्य तरीकों के माध्यम से हमारे साथी तक पहुंचें"
}

View file

@ -672,5 +672,11 @@
"slidable": "Klizna",
"manage_nodes": "Upravljanje čvorovima",
"etherscan_history": "Etherscan povijest",
"template_name": "Naziv predloška"
"template_name": "Naziv predloška",
"support_title_live_chat": "Podrška uživo",
"support_description_live_chat": "Besplatno i brzo! Obučeni predstavnici podrške dostupni su za pomoć",
"support_title_guides": "Vodiči za torte",
"support_description_guides": "Dokumentacija i podrška za uobičajena pitanja",
"support_title_other_links": "Ostale veze za podršku",
"support_description_other_links": "Pridružite se našim zajednicama ili nam dosegnu naše partnere drugim metodama"
}

View file

@ -660,5 +660,11 @@
"slidable": "Dapat digeser",
"manage_nodes": "Kelola node",
"etherscan_history": "Sejarah Etherscan",
"template_name": "Nama Templat"
"template_name": "Nama Templat",
"support_title_live_chat": "Dukungan langsung",
"support_description_live_chat": "Gratis dan Cepat! Perwakilan dukungan terlatih tersedia untuk membantu",
"support_title_guides": "Panduan Dompet Kue",
"support_description_guides": "Dokumentasi dan dukungan untuk masalah umum",
"support_title_other_links": "Tautan dukungan lainnya",
"support_description_other_links": "Bergabunglah dengan komunitas kami atau hubungi kami mitra kami melalui metode lain"
}

View file

@ -672,5 +672,11 @@
"slidable": "Scorrevole",
"manage_nodes": "Gestisci i nodi",
"etherscan_history": "Storia Etherscan",
"template_name": "Nome modello"
"template_name": "Nome modello",
"support_title_live_chat": "Supporto dal vivo",
"support_description_live_chat": "Gratuito e veloce! I rappresentanti di supporto qualificati sono disponibili per assistere",
"support_title_guides": "Guide del portafoglio per torte",
"support_description_guides": "Documentazione e supporto per problemi comuni",
"support_title_other_links": "Altri collegamenti di supporto",
"support_description_other_links": "Unisciti alle nostre comunità o raggiungici i nostri partner attraverso altri metodi"
}

View file

@ -634,18 +634,18 @@
"setup_totp_recommended": "TOTP を設定する (推奨)",
"disable_buy": "購入アクションを無効にする",
"disable_sell": "販売アクションを無効にする",
"cake_2fa_preset" : "ケーキ 2FA プリセット",
"cake_2fa_preset": "ケーキ 2FA プリセット",
"narrow": "狭い",
"normal": "普通",
"aggressive": "熱心すぎる",
"require_for_assessing_wallet": "ウォレットにアクセスするために必要です",
"require_for_sends_to_non_contacts" : "非連絡先への送信に必須",
"require_for_sends_to_contacts" : "連絡先に送信する場合に必須",
"require_for_sends_to_internal_wallets" : "内部ウォレットへの送信に必須",
"require_for_exchanges_to_internal_wallets" : "内部ウォレットへの交換に必要",
"require_for_adding_contacts" : "連絡先の追加に必要",
"require_for_creating_new_wallets" : "新しいウォレットを作成するために必要です",
"require_for_all_security_and_backup_settings" : "すべてのセキュリティおよびバックアップ設定に必須",
"require_for_sends_to_non_contacts": "非連絡先への送信に必須",
"require_for_sends_to_contacts": "連絡先に送信する場合に必須",
"require_for_sends_to_internal_wallets": "内部ウォレットへの送信に必須",
"require_for_exchanges_to_internal_wallets": "内部ウォレットへの交換に必要",
"require_for_adding_contacts": "連絡先の追加に必要",
"require_for_creating_new_wallets": "新しいウォレットを作成するために必要です",
"require_for_all_security_and_backup_settings": "すべてのセキュリティおよびバックアップ設定に必須",
"available_balance_description": "利用可能な残高は、ウォレットの残高から冷凍残高を差し引いたものです。",
"syncing_wallet_alert_title": "ウォレットは同期中です",
"syncing_wallet_alert_content": "上部に「同期済み」と表示されるまで、残高と取引リストが完了していない可能性があります。詳細については、クリック/タップしてください。",
@ -672,5 +672,11 @@
"slidable": "スライド可能",
"manage_nodes": "ノードの管理",
"etherscan_history": "イーサスキャンの歴史",
"template_name": "テンプレート名"
}
"template_name": "テンプレート名",
"support_title_live_chat": "ライブサポート",
"support_description_live_chat": "無料で速い!訓練されたサポート担当者が支援することができます",
"support_title_guides": "ケーキウォレットガイド",
"support_description_guides": "一般的な問題のドキュメントとサポート",
"support_title_other_links": "その他のサポートリンク",
"support_description_other_links": "私たちのコミュニティに参加するか、他の方法を通して私たちのパートナーに連絡してください"
}

View file

@ -634,18 +634,18 @@
"setup_totp_recommended": "TOTP 설정(권장)",
"disable_buy": "구매 행동 비활성화",
"disable_sell": "판매 조치 비활성화",
"cake_2fa_preset" : "케이크 2FA 프리셋",
"cake_2fa_preset": "케이크 2FA 프리셋",
"narrow": "좁은",
"normal": "정상",
"aggressive": "지나치게 열심인",
"require_for_assessing_wallet": "지갑 접근을 위해 필요",
"require_for_sends_to_non_contacts" : "비접촉자에게 보내는 데 필요",
"require_for_sends_to_contacts" : "연락처로 보내기에 필요",
"require_for_sends_to_internal_wallets" : "내부 지갑으로 보내는 데 필요",
"require_for_exchanges_to_internal_wallets" : "내부 지갑으로의 교환에 필요",
"require_for_adding_contacts" : "연락처 추가에 필요",
"require_for_creating_new_wallets" : "새 지갑 생성에 필요",
"require_for_all_security_and_backup_settings" : "모든 보안 및 백업 설정에 필요",
"require_for_sends_to_non_contacts": "비접촉자에게 보내는 데 필요",
"require_for_sends_to_contacts": "연락처로 보내기에 필요",
"require_for_sends_to_internal_wallets": "내부 지갑으로 보내는 데 필요",
"require_for_exchanges_to_internal_wallets": "내부 지갑으로의 교환에 필요",
"require_for_adding_contacts": "연락처 추가에 필요",
"require_for_creating_new_wallets": "새 지갑 생성에 필요",
"require_for_all_security_and_backup_settings": "모든 보안 및 백업 설정에 필요",
"available_balance_description": "이 지갑에서 사용할 수 있는 잔액입니다. 이 잔액은 블록체인에서 가져온 것이며, Cake Wallet이 사용할 수 없습니다.",
"syncing_wallet_alert_title": "지갑 동기화 중",
"syncing_wallet_alert_content": "상단에 \"동기화됨\"이라고 표시될 때까지 잔액 및 거래 목록이 완전하지 않을 수 있습니다. 자세히 알아보려면 클릭/탭하세요.",
@ -672,5 +672,11 @@
"slidable": "슬라이딩 가능",
"manage_nodes": "노드 관리",
"etherscan_history": "이더스캔 역사",
"template_name": "템플릿 이름"
}
"template_name": "템플릿 이름",
"support_title_live_chat": "실시간 지원",
"support_description_live_chat": "자유롭고 빠릅니다! 훈련 된 지원 담당자가 지원할 수 있습니다",
"support_title_guides": "케이크 지갑 가이드",
"support_description_guides": "일반적인 문제에 대한 문서화 및 지원",
"support_title_other_links": "다른 지원 링크",
"support_description_other_links": "다른 방법을 통해 커뮤니티에 가입하거나 파트너에게 연락하십시오."
}

View file

@ -632,18 +632,18 @@
"setup_totp_recommended": "TOTP ကို ​​စနစ်ထည့်သွင်းပါ (အကြံပြုထားသည်)",
"disable_buy": "ဝယ်ယူမှု လုပ်ဆောင်ချက်ကို ပိတ်ပါ။",
"disable_sell": "ရောင်းချခြင်းလုပ်ဆောင်ချက်ကို ပိတ်ပါ။",
"cake_2fa_preset" : "ကိတ်မုန့် 2FA ကြိုတင်သတ်မှတ်",
"cake_2fa_preset": "ကိတ်မုန့် 2FA ကြိုတင်သတ်မှတ်",
"narrow": "ကျဉ်းသော",
"normal": "ပုံမှန်",
"aggressive": "စိတ်အားထက်သန်ခြင်း။",
"require_for_assessing_wallet": "ပိုက်ဆံအိတ်ကို ဝင်သုံးရန် လိုအပ်သည်။",
"require_for_sends_to_non_contacts" : "အဆက်အသွယ်မရှိသူများထံ ပေးပို့ရန် လိုအပ်သည်။",
"require_for_sends_to_contacts" : "အဆက်အသွယ်များထံ ပေးပို့ရန် လိုအပ်သည်။",
"require_for_sends_to_internal_wallets" : "အတွင်းပိုင်း ပိုက်ဆံအိတ်များသို့ ပေးပို့ရန် လိုအပ်သည်။",
"require_for_exchanges_to_internal_wallets" : "အတွင်းပိုင်းပိုက်ဆံအိတ်များသို့ လဲလှယ်ရန် လိုအပ်သည်။",
"require_for_adding_contacts" : "အဆက်အသွယ်များထည့်ရန် လိုအပ်သည်။",
"require_for_creating_new_wallets" : "ပိုက်ဆံအိတ်အသစ်များ ဖန်တီးရန် လိုအပ်သည်။",
"require_for_all_security_and_backup_settings" : "လုံခြုံရေးနှင့် အရန်ဆက်တင်များအားလုံးအတွက် လိုအပ်ပါသည်။",
"require_for_sends_to_non_contacts": "အဆက်အသွယ်မရှိသူများထံ ပေးပို့ရန် လိုအပ်သည်။",
"require_for_sends_to_contacts": "အဆက်အသွယ်များထံ ပေးပို့ရန် လိုအပ်သည်။",
"require_for_sends_to_internal_wallets": "အတွင်းပိုင်း ပိုက်ဆံအိတ်များသို့ ပေးပို့ရန် လိုအပ်သည်။",
"require_for_exchanges_to_internal_wallets": "အတွင်းပိုင်းပိုက်ဆံအိတ်များသို့ လဲလှယ်ရန် လိုအပ်သည်။",
"require_for_adding_contacts": "အဆက်အသွယ်များထည့်ရန် လိုအပ်သည်။",
"require_for_creating_new_wallets": "ပိုက်ဆံအိတ်အသစ်များ ဖန်တီးရန် လိုအပ်သည်။",
"require_for_all_security_and_backup_settings": "လုံခြုံရေးနှင့် အရန်ဆက်တင်များအားလုံးအတွက် လိုအပ်ပါသည်။",
"available_balance_description": "သင့်ရဲ့ အကောင့်တွင် ရရှိနိုင်သော ငွေကျန်ငွေကို ပြန်လည်ပေးသွင်းပါ။",
"syncing_wallet_alert_title": "သင့်ပိုက်ဆံအိတ်ကို စင့်ခ်လုပ်နေပါသည်။",
"syncing_wallet_alert_content": "သင်၏လက်ကျန်နှင့် ငွေပေးငွေယူစာရင်းသည် ထိပ်တွင် \"Synchronizeed\" ဟုပြောသည်အထိ မပြီးမြောက်နိုင်ပါ။ ပိုမိုလေ့လာရန် နှိပ်/နှိပ်ပါ။",
@ -670,5 +670,11 @@
"slidable": "လျှောချနိုင်သည်။",
"manage_nodes": "ဆုံမှတ်များကို စီမံပါ။",
"etherscan_history": "Etherscan သမိုင်း",
"template_name": "နမူနာပုံစံ"
}
"template_name": "နမူနာပုံစံ",
"support_title_live_chat": "တိုက်ရိုက်ပံ့ပိုးမှု",
"support_description_live_chat": "အခမဲ့နှင့်အစာရှောင်ခြင်း! လေ့ကျင့်ထားသောထောက်ခံသူကိုယ်စားလှယ်များသည်ကူညီနိုင်သည်",
"support_title_guides": "ကိတ်မုန့်ပိုက်ဆံအိတ်လမ်းညွှန်များ",
"support_description_guides": "ဘုံပြ issues နာများအတွက်စာရွက်စာတမ်းများနှင့်ထောက်ခံမှု",
"support_title_other_links": "အခြားအထောက်အပံ့လင့်များ",
"support_description_other_links": "ကျွန်ုပ်တို့၏လူမှုအသိုင်းအဝိုင်းများသို့ 0 င်ရောက်ပါ"
}

View file

@ -672,5 +672,11 @@
"slidable": "Verschuifbaar",
"manage_nodes": "Beheer knooppunten",
"etherscan_history": "Etherscan-geschiedenis",
"template_name": "Sjabloonnaam"
"template_name": "Sjabloonnaam",
"support_title_live_chat": "Live ondersteuning",
"support_description_live_chat": "Gratis en snel! Getrainde ondersteuningsvertegenwoordigers zijn beschikbaar om te helpen",
"support_title_guides": "Cake -portemonnee gidsen",
"support_description_guides": "Documentatie en ondersteuning voor gemeenschappelijke problemen",
"support_title_other_links": "Andere ondersteuningslinks",
"support_description_other_links": "Word lid van onze gemeenschappen of bereik ons onze partners via andere methoden"
}

View file

@ -672,5 +672,11 @@
"slidable": "Przesuwne",
"manage_nodes": "Zarządzaj węzłami",
"etherscan_history": "Historia Etherscanu",
"template_name": "Nazwa szablonu"
"template_name": "Nazwa szablonu",
"support_title_live_chat": "Wsparcie na żywo",
"support_description_live_chat": "Darmowe i szybkie! Do pomocy są dostępni przeszkoleni przedstawiciele wsparcia",
"support_title_guides": "Przewodniki portfela ciasta",
"support_description_guides": "Dokumentacja i wsparcie dla typowych problemów",
"support_title_other_links": "Inne linki wsparcia",
"support_description_other_links": "Dołącz do naszych społeczności lub skontaktuj się z nami naszymi partnerami za pomocą innych metod"
}

View file

@ -671,5 +671,11 @@
"slidable": "Deslizável",
"manage_nodes": "Gerenciar nós",
"etherscan_history": "história Etherscan",
"template_name": "Nome do modelo"
"template_name": "Nome do modelo",
"support_title_live_chat": "Apoio ao vivo",
"support_description_live_chat": "Livre e rápido! Representantes de suporte treinado estão disponíveis para ajudar",
"support_title_guides": "Guias da carteira de bolo",
"support_description_guides": "Documentação e suporte para problemas comuns",
"support_title_other_links": "Outros links de suporte",
"support_description_other_links": "Junte -se às nossas comunidades ou chegue a nós nossos parceiros por meio de outros métodos"
}

View file

@ -270,7 +270,6 @@
"error_text_node_address": "Пожалуйста, введите iPv4 адрес",
"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_xmr": "Значение XMR не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 12",
"error_text_fiat": "Значение суммы не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 2",
@ -635,18 +634,18 @@
"setup_totp_recommended": "Настроить TOTP (рекомендуется)",
"disable_buy": "Отключить действие покупки",
"disable_sell": "Отключить действие продажи",
"cake_2fa_preset" : "Торт 2FA Preset",
"cake_2fa_preset": "Торт 2FA Preset",
"narrow": "Узкий",
"normal": "Нормальный",
"aggressive": "чрезмерно усердный",
"require_for_assessing_wallet": "Требовать для доступа к кошельку",
"require_for_sends_to_non_contacts" : "Требовать для отправки не контактам",
"require_for_sends_to_contacts" : "Требовать для отправки контактам",
"require_for_sends_to_internal_wallets" : "Требовать отправки на внутренние кошельки",
"require_for_exchanges_to_internal_wallets" : "Требовать для обмена на внутренние кошельки",
"require_for_adding_contacts" : "Требовать добавления контактов",
"require_for_creating_new_wallets" : "Требовать для создания новых кошельков",
"require_for_all_security_and_backup_settings" : "Требовать все настройки безопасности и резервного копирования",
"require_for_sends_to_non_contacts": "Требовать для отправки не контактам",
"require_for_sends_to_contacts": "Требовать для отправки контактам",
"require_for_sends_to_internal_wallets": "Требовать отправки на внутренние кошельки",
"require_for_exchanges_to_internal_wallets": "Требовать для обмена на внутренние кошельки",
"require_for_adding_contacts": "Требовать добавления контактов",
"require_for_creating_new_wallets": "Требовать для создания новых кошельков",
"require_for_all_security_and_backup_settings": "Требовать все настройки безопасности и резервного копирования",
"available_balance_description": "Доступный баланс - это средства, которые вы можете использовать для покупки или продажи криптовалюты.",
"syncing_wallet_alert_title": "Ваш кошелек синхронизируется",
"syncing_wallet_alert_content": "Ваш баланс и список транзакций могут быть неполными, пока вверху не будет написано «СИНХРОНИЗИРОВАНО». Щелкните/коснитесь, чтобы узнать больше.",
@ -673,5 +672,11 @@
"slidable": "Скользящий",
"manage_nodes": "Управление узлами",
"etherscan_history": "История Эфириума",
"template_name": "Имя Шаблона"
}
"template_name": "Имя Шаблона",
"support_title_live_chat": "Живая поддержка",
"support_description_live_chat": "Бесплатно и быстро! Обученные представители поддержки доступны для оказания помощи",
"support_title_guides": "Корт -гиды",
"support_description_guides": "Документация и поддержка общих вопросов",
"support_title_other_links": "Другие ссылки на поддержку",
"support_description_other_links": "Присоединяйтесь к нашим сообществам или охватите нас наших партнеров с помощью других методов"
}

View file

@ -632,18 +632,18 @@
"setup_totp_recommended": "ตั้งค่า TOTP (แนะนำ)",
"disable_buy": "ปิดการใช้งานการซื้อ",
"disable_sell": "ปิดการใช้งานการขาย",
"cake_2fa_preset" : "เค้ก 2FA ที่ตั้งไว้ล่วงหน้า",
"cake_2fa_preset": "เค้ก 2FA ที่ตั้งไว้ล่วงหน้า",
"narrow": "แคบ",
"normal": "ปกติ",
"aggressive": "กระตือรือร้นมากเกินไป",
"require_for_assessing_wallet": "จำเป็นสำหรับการเข้าถึงกระเป๋าเงิน",
"require_for_sends_to_non_contacts" : "จำเป็นต้องส่งไปยังผู้ที่ไม่ได้ติดต่อ",
"require_for_sends_to_contacts" : "จำเป็นต้องส่งไปยังผู้ติดต่อ",
"require_for_sends_to_internal_wallets" : "จำเป็นต้องส่งไปยังกระเป๋าเงินภายใน",
"require_for_exchanges_to_internal_wallets" : "ต้องการการแลกเปลี่ยนไปยังกระเป๋าเงินภายใน",
"require_for_adding_contacts" : "ต้องการสำหรับการเพิ่มผู้ติดต่อ",
"require_for_creating_new_wallets" : "จำเป็นสำหรับการสร้างกระเป๋าเงินใหม่",
"require_for_all_security_and_backup_settings" : "จำเป็นสำหรับการตั้งค่าความปลอดภัยและการสำรองข้อมูลทั้งหมด",
"require_for_sends_to_non_contacts": "จำเป็นต้องส่งไปยังผู้ที่ไม่ได้ติดต่อ",
"require_for_sends_to_contacts": "จำเป็นต้องส่งไปยังผู้ติดต่อ",
"require_for_sends_to_internal_wallets": "จำเป็นต้องส่งไปยังกระเป๋าเงินภายใน",
"require_for_exchanges_to_internal_wallets": "ต้องการการแลกเปลี่ยนไปยังกระเป๋าเงินภายใน",
"require_for_adding_contacts": "ต้องการสำหรับการเพิ่มผู้ติดต่อ",
"require_for_creating_new_wallets": "จำเป็นสำหรับการสร้างกระเป๋าเงินใหม่",
"require_for_all_security_and_backup_settings": "จำเป็นสำหรับการตั้งค่าความปลอดภัยและการสำรองข้อมูลทั้งหมด",
"available_balance_description": "จำนวนเงินที่คุณสามารถใช้ได้ในการซื้อหรือขาย",
"syncing_wallet_alert_title": "กระเป๋าสตางค์ของคุณกำลังซิงค์",
"syncing_wallet_alert_content": "รายการยอดเงินและธุรกรรมของคุณอาจไม่สมบูรณ์จนกว่าจะมีข้อความว่า “ซิงโครไนซ์” ที่ด้านบน คลิก/แตะเพื่อเรียนรู้เพิ่มเติม่",
@ -670,5 +670,11 @@
"slidable": "เลื่อนได้",
"manage_nodes": "จัดการโหนด",
"etherscan_history": "ประวัติอีเธอร์สแกน",
"template_name": "ชื่อแม่แบบ"
}
"template_name": "ชื่อแม่แบบ",
"support_title_live_chat": "การสนับสนุนสด",
"support_description_live_chat": "ฟรีและรวดเร็ว! ตัวแทนฝ่ายสนับสนุนที่ผ่านการฝึกอบรมพร้อมให้ความช่วยเหลือ",
"support_title_guides": "คู่มือกระเป๋าเงินเค้ก",
"support_description_guides": "เอกสารและการสนับสนุนสำหรับปัญหาทั่วไป",
"support_title_other_links": "ลิงค์สนับสนุนอื่น ๆ",
"support_description_other_links": "เข้าร่วมชุมชนของเราหรือเข้าถึงเราพันธมิตรของเราผ่านวิธีการอื่น ๆ"
}

View file

@ -671,5 +671,11 @@
"slidable": "kaydırılabilir",
"manage_nodes": "Düğümleri yönet",
"etherscan_history": "Etherscan geçmişi",
"template_name": "şablon adı"
"template_name": "şablon adı",
"support_title_live_chat": "Canlı destek",
"support_description_live_chat": "Ücretsiz ve hızlı! Eğitimli destek temsilcileri yardımcı olmak için mevcuttur",
"support_title_guides": "Kek Cüzdan Kılavuzları",
"support_description_guides": "Ortak sorunlara belge ve destek",
"support_title_other_links": "Diğer destek bağlantıları",
"support_description_other_links": "Topluluklarımıza katılın veya ortaklarımıza diğer yöntemlerle bize ulaşın"
}

View file

@ -672,5 +672,11 @@
"slidable": "Розсувний",
"manage_nodes": "Керуйте вузлами",
"etherscan_history": "Історія Etherscan",
"template_name": "Назва шаблону"
"template_name": "Назва шаблону",
"support_title_live_chat": "Жива підтримка",
"support_description_live_chat": "Безкоштовно і швидко! Навчені представники підтримки доступні для надання допомоги",
"support_title_guides": "Поклики для гаманців тортів",
"support_description_guides": "Документація та підтримка загальних питань",
"support_title_other_links": "Інші посилання на підтримку",
"support_description_other_links": "Приєднуйтесь до наших спільнот або досягайте нас нашими партнерами іншими методами"
}

View file

@ -626,18 +626,18 @@
"setup_totp_recommended": "TOTP ترتیب دیں (تجویز کردہ)",
"disable_buy": "خرید ایکشن کو غیر فعال کریں۔",
"disable_sell": "فروخت کی کارروائی کو غیر فعال کریں۔",
"cake_2fa_preset" : "کیک 2FA پیش سیٹ",
"cake_2fa_preset": "کیک 2FA پیش سیٹ",
"narrow": "تنگ",
"normal": "نارمل",
"aggressive": "حد سے زیادہ پرجوش",
"require_for_assessing_wallet": "بٹوے تک رسائی کے لیے درکار ہے۔",
"require_for_sends_to_non_contacts" : "غیر رابطوں کو بھیجنے کی ضرورت ہے۔",
"require_for_sends_to_contacts" : "رابطوں کو بھیجنے کی ضرورت ہے۔",
"require_for_sends_to_internal_wallets" : "اندرونی بٹوے پر بھیجنے کے لیے درکار ہے۔",
"require_for_exchanges_to_internal_wallets" : "اندرونی بٹوے میں تبادلے کی ضرورت ہے۔",
"require_for_adding_contacts" : "رابطوں کو شامل کرنے کی ضرورت ہے۔",
"require_for_creating_new_wallets" : "نئے بٹوے بنانے کی ضرورت ہے۔",
"require_for_all_security_and_backup_settings" : "تمام سیکورٹی اور بیک اپ کی ترتیبات کے لیے درکار ہے۔",
"require_for_sends_to_non_contacts": "غیر رابطوں کو بھیجنے کی ضرورت ہے۔",
"require_for_sends_to_contacts": "رابطوں کو بھیجنے کی ضرورت ہے۔",
"require_for_sends_to_internal_wallets": "اندرونی بٹوے پر بھیجنے کے لیے درکار ہے۔",
"require_for_exchanges_to_internal_wallets": "اندرونی بٹوے میں تبادلے کی ضرورت ہے۔",
"require_for_adding_contacts": "رابطوں کو شامل کرنے کی ضرورت ہے۔",
"require_for_creating_new_wallets": "نئے بٹوے بنانے کی ضرورت ہے۔",
"require_for_all_security_and_backup_settings": "تمام سیکورٹی اور بیک اپ کی ترتیبات کے لیے درکار ہے۔",
"available_balance_description": "”دستیاب بیلنس” یا ”تصدیق شدہ بیلنس” وہ فنڈز ہیں جو فوری طور پر خرچ کیے جا سکتے ہیں۔ اگر فنڈز کم بیلنس میں ظاہر ہوتے ہیں لیکن اوپر کے بیلنس میں نہیں، تو آپ کو مزید نیٹ ورک کی تصدیقات حاصل کرنے کے لیے آنے والے فنڈز کے لیے چند منٹ انتظار کرنا چاہیے۔ مزید تصدیق حاصل کرنے کے بعد، وہ قابل خرچ ہوں گے۔",
"syncing_wallet_alert_title": "آپ کا بٹوہ مطابقت پذیر ہو رہا ہے۔",
"syncing_wallet_alert_content": "آپ کے بیلنس اور لین دین کی فہرست اس وقت تک مکمل نہیں ہو سکتی جب تک کہ یہ سب سے اوپر \"SYNCRONIZED\" نہ کہے۔ مزید جاننے کے لیے کلک/تھپتھپائیں۔",
@ -664,5 +664,11 @@
"slidable": "سلائیڈ ایبل",
"manage_nodes": "۔ﮟﯾﺮﮐ ﻢﻈﻧ ﺎﮐ ﺱﮈﻮﻧ",
"etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ",
"template_name": "ٹیمپلیٹ کا نام"
}
"template_name": "ٹیمپلیٹ کا نام",
"support_title_live_chat": "براہ راست مدد",
"support_description_live_chat": "مفت اور تیز! تربیت یافتہ معاون نمائندے مدد کے لئے دستیاب ہیں",
"support_title_guides": "کیک پرس گائڈز",
"support_description_guides": "عام مسائل کے لئے دستاویزات اور مدد",
"support_title_other_links": "دوسرے سپورٹ لنکس",
"support_description_other_links": "ہماری برادریوں میں شامل ہوں یا دوسرے طریقوں سے ہمارے شراکت داروں تک پہنچیں"
}

View file

@ -628,18 +628,18 @@
"setup_totp_recommended": "Sọ TOTP (Kẹṣọdọ)",
"disable_buy": "Ko iṣọrọ ọja",
"disable_sell": "Ko iṣọrọ iṣọrọ",
"cake_2fa_preset" : "Cake 2FA Tito",
"cake_2fa_preset": "Cake 2FA Tito",
"narrow": "Taara",
"normal": "Deede",
"aggressive": "Onítara",
"require_for_assessing_wallet": "Beere fun wiwọle si apamọwọ",
"require_for_sends_to_non_contacts" : "Beere fun fifiranṣẹ si awọn ti kii ṣe awọn olubasọrọ",
"require_for_sends_to_contacts" : "Beere fun fifiranṣẹ si awọn olubasọrọ",
"require_for_sends_to_internal_wallets" : "Beere fun fifiranṣẹ si awọn apamọwọ inu",
"require_for_exchanges_to_internal_wallets" : "Beere fun awọn paṣipaarọ si awọn apamọwọ inu",
"require_for_adding_contacts" : "Beere fun fifi awọn olubasọrọ kun",
"require_for_creating_new_wallets" : "Beere fun ṣiṣẹda titun Woleti",
"require_for_all_security_and_backup_settings" : "Beere fun gbogbo aabo ati awọn eto afẹyinti",
"require_for_sends_to_non_contacts": "Beere fun fifiranṣẹ si awọn ti kii ṣe awọn olubasọrọ",
"require_for_sends_to_contacts": "Beere fun fifiranṣẹ si awọn olubasọrọ",
"require_for_sends_to_internal_wallets": "Beere fun fifiranṣẹ si awọn apamọwọ inu",
"require_for_exchanges_to_internal_wallets": "Beere fun awọn paṣipaarọ si awọn apamọwọ inu",
"require_for_adding_contacts": "Beere fun fifi awọn olubasọrọ kun",
"require_for_creating_new_wallets": "Beere fun ṣiṣẹda titun Woleti",
"require_for_all_security_and_backup_settings": "Beere fun gbogbo aabo ati awọn eto afẹyinti",
"available_balance_description": "“Iwọntunwọnsi Wa” tabi “Iwọntunwọnsi Ijẹrisi” jẹ awọn owo ti o le ṣee lo lẹsẹkẹsẹ. Ti awọn owo ba han ni iwọntunwọnsi kekere ṣugbọn kii ṣe iwọntunwọnsi oke, lẹhinna o gbọdọ duro iṣẹju diẹ fun awọn owo ti nwọle lati gba awọn ijẹrisi nẹtiwọọki diẹ sii. Lẹhin ti wọn gba awọn ijẹrisi diẹ sii, wọn yoo jẹ inawo.",
"syncing_wallet_alert_title": "Apamọwọ rẹ n muṣiṣẹpọ",
"syncing_wallet_alert_content": "Iwontunws.funfun rẹ ati atokọ idunadura le ma pari titi ti yoo fi sọ “SYNCHRONIZED” ni oke. Tẹ/tẹ ni kia kia lati ni imọ siwaju sii.",
@ -666,5 +666,11 @@
"slidable": "Slidable",
"manage_nodes": "Ṣakoso awọn apa",
"etherscan_history": "Etherscan itan",
"template_name": "Orukọ Awoṣe"
}
"template_name": "Orukọ Awoṣe",
"support_title_live_chat": "Atilẹyin ifiwe",
"support_description_live_chat": "Free ati sare! Ti oṣiṣẹ awọn aṣoju wa lati ṣe iranlọwọ",
"support_title_guides": "Akara oyinbo Awọn Itọsọna Awọki oyinbo",
"support_description_guides": "Iwe ati atilẹyin fun awọn ọran ti o wọpọ",
"support_title_other_links": "Awọn ọna asopọ atilẹyin miiran",
"support_description_other_links": "Darapọ mọ awọn agbegbe wa tabi de wa awọn alabaṣepọ wa nipasẹ awọn ọna miiran"
}

View file

@ -633,18 +633,18 @@
"setup_totp_recommended": "设置 TOTP推荐",
"disable_buy": "禁用购买操作",
"disable_sell": "禁用卖出操作",
"cake_2fa_preset" : "蛋糕 2FA 预设",
"cake_2fa_preset": "蛋糕 2FA 预设",
"narrow": "狭窄的",
"normal": "普通的",
"aggressive": "过分热心",
"require_for_assessing_wallet": "需要访问钱包",
"require_for_sends_to_non_contacts" : "需要发送给非联系人",
"require_for_sends_to_contacts" : "需要发送给联系人",
"require_for_sends_to_internal_wallets" : "需要发送到内部钱包",
"require_for_exchanges_to_internal_wallets" : "需要兑换到内部钱包",
"require_for_adding_contacts" : "需要添加联系人",
"require_for_creating_new_wallets" : "创建新钱包的要求",
"require_for_all_security_and_backup_settings" : "需要所有安全和备份设置",
"require_for_sends_to_non_contacts": "需要发送给非联系人",
"require_for_sends_to_contacts": "需要发送给联系人",
"require_for_sends_to_internal_wallets": "需要发送到内部钱包",
"require_for_exchanges_to_internal_wallets": "需要兑换到内部钱包",
"require_for_adding_contacts": "需要添加联系人",
"require_for_creating_new_wallets": "创建新钱包的要求",
"require_for_all_security_and_backup_settings": "需要所有安全和备份设置",
"available_balance_description": "可用余额是您可以使用的金额。冻结余额是您当前正在等待确认的金额。",
"syncing_wallet_alert_title": "您的钱包正在同步",
"syncing_wallet_alert_content": "您的余额和交易列表可能不完整,直到顶部显示“已同步”。单击/点击以了解更多信息。",
@ -671,5 +671,11 @@
"slidable": "可滑动",
"manage_nodes": "管理节点",
"etherscan_history": "以太扫描历史",
"template_name": "模板名称"
}
"template_name": "模板名称",
"support_title_live_chat": "实时支持",
"support_description_live_chat": "免费快速!训练有素的支持代表可以协助",
"support_title_guides": "蛋糕钱包指南",
"support_description_guides": "对常见问题的文档和支持",
"support_title_other_links": "其他支持链接",
"support_description_other_links": "加入我们的社区或通过其他方法与我们联系我们的合作伙伴"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

View file

@ -0,0 +1,66 @@
import 'dart:convert';
import 'dart:io';
import 'package:translator/translator.dart';
const defaultLang = "en";
const langs = [
"ar", "bg", "cs", "de", "en", "es", "fr", "ha", "hi", "hr", "id", "it",
"ja", "ko", "my", "nl", "pl", "pt", "ru", "th", "tr", "uk", "ur", "yo",
"zh-cn" // zh, but Google Translate uses zh-cn for Chinese (Simplified)
];
final translator = GoogleTranslator();
void main(List<String> args) async {
if (args.length != 2) {
throw Exception(
'Insufficient arguments!\n\nTry to run `./append_translation.dart greetings "Hello World!"`');
}
final name = args.first;
final text = args.last;
print('Appending "$name": "$text"');
for (var lang in langs) {
final fileName = getFileName(lang);
final translation = await getTranslation(text, lang);
appendArbFile(fileName, name, translation);
}
}
void appendArbFile(String fileName, String name, String text) {
final file = File(fileName);
final inputContent = file.readAsStringSync();
final arbObj = json.decode(inputContent) as Map<String, dynamic>;
if (arbObj.containsKey(name)) {
print("String $name already exists in $fileName!");
return;
}
arbObj.addAll({name: text});
final outputContent = json
.encode(arbObj)
.replaceAll('","', '",\n "')
.replaceAll('{"', '{\n "')
.replaceAll('"}', '"\n}')
.replaceAll('":"', '": "');
file.writeAsStringSync(outputContent);
}
Future<String> getTranslation(String text, String lang) async {
if (lang == defaultLang) return text;
return (await translator.translate(text, from: defaultLang, to: lang)).text;
}
String getFileName(String lang) {
final shortLang = lang
.split("-")
.first;
return "./res/values/strings_$shortLang.arb";
}

View file

@ -33,8 +33,7 @@ Future<void> updateSecretsConfig(List<String> args) async {
});
final fileConfig =
json.decode(configFile.readAsStringSync()) as Map<String, dynamic> ??
<String, dynamic>{};
json.decode(configFile.readAsStringSync()) as Map<String, dynamic>;
fileConfig.forEach((key, dynamic value) {
if (secrets[key] == null) {
secrets[key] = value;

View file

@ -31,6 +31,7 @@ class SecretKey {
SecretKey('anonPayReferralCode', () => ''),
SecretKey('fiatApiKey', () => ''),
SecretKey('payfuraApiKey', () => ''),
SecretKey('chatwootWebsiteToken', () => ''),
];
static final ethereumSecrets = [