Cw 397 chatwoot live support (#1011)

* initial button refactor and gradient background

* CW-397 Use a separate Hive instance to avoid Issues with plugins using Hive

* CW-397 Add Support Page Strings

* CW-397 Add new Support Page

* CW-397 Add Support Live Chat Page

* CW-397 Add Hive Type Ids Doc

* CW-397 Use Newer Chatwoot SDK Version and add new Images

* CW-397 Update pubspec_base.yaml

* CW-397 Add own Chatwoot Widget

* Lowercase `s` skip-ci

* CW-397 Fix WebMessageListener

* CW-397 Fix Merge conflicts

* CW-397 Add Erc20 Hive Type ID

* CW-397 Fix Ethereum Hive Error

* CW-397 Revert to Restore Button

* CW-397 Only use In App chat on mobile

* CW-397 Move Chatwoot Website Token to secrets

* CW-397 Add Chatwoot Website Token to workflow

* CW-397 Move Chatwoot fetchUrl to Support View Model

---------

Co-authored-by: Rafael Saes <git@saes.io>
Co-authored-by: Justin Ehrenhofer <justin.ehrenhofer@gmail.com>
This commit is contained in:
Konstantin Ullrich 2023-08-10 15:42:53 +02:00 committed by GitHub
parent e348f12491
commit af9b5ff10c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 762 additions and 260 deletions

View file

@ -127,6 +127,7 @@ jobs:
echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart echo "const fiatApiKey = '${{ secrets.FIAT_API_KEY }}';" >> lib/.secrets.g.dart
echo "const payfuraApiKey = '${{ secrets.PAYFURA_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 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 - name: Rename app
run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties

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/crypto_currency.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
part 'erc20_token.g.dart'; part 'erc20_token.g.dart';
@ -53,7 +54,7 @@ class Erc20Token extends CryptoCurrency with HiveObjectMixin {
iconPath: icon, iconPath: icon,
); );
static const typeId = 12; static const typeId = ERC20_TOKEN_TYPE_ID;
static const boxName = 'Erc20Tokens'; static const boxName = 'Erc20Tokens';
@override @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 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:cw_core/hive_type_ids.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:http/io_client.dart' as ioc; import 'package:http/io_client.dart' as ioc;
@ -37,7 +38,7 @@ class Node extends HiveObject with Keyable {
trusted = map['trusted'] as bool? ?? false, trusted = map['trusted'] as bool? ?? false,
socksProxyAddress = map['socksProxyPort'] as String?; socksProxyAddress = map['socksProxyPort'] as String?;
static const typeId = 1; static const typeId = NODE_TYPE_ID;
static const boxName = 'Nodes'; static const boxName = 'Nodes';
@HiveField(0, defaultValue: '') @HiveField(0, defaultValue: '')

View file

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

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 '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'; part 'wallet_info.g.dart';
@ -30,7 +30,7 @@ class WalletInfo extends HiveObject {
yatEid, yatLastUsedAddressRaw, showIntroCakePayCard); yatEid, yatLastUsedAddressRaw, showIntroCakePayCard);
} }
static const typeId = 4; static const typeId = WALLET_INFO_TYPE_ID;
static const boxName = 'WalletInfo'; static const boxName = 'WalletInfo';
@HiveField(0, defaultValue: '') @HiveField(0, defaultValue: '')

View file

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

View file

@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
import 'package:cw_core/cake_hive.dart';
import 'package:cw_core/node.dart'; import 'package:cw_core/node.dart';
import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/pathForWallet.dart';
import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/pending_transaction.dart';
@ -58,8 +59,8 @@ abstract class EthereumWalletBase
this.walletInfo = walletInfo; this.walletInfo = walletInfo;
transactionHistory = EthereumTransactionHistory(walletInfo: walletInfo, password: password); transactionHistory = EthereumTransactionHistory(walletInfo: walletInfo, password: password);
if (!Hive.isAdapterRegistered(Erc20Token.typeId)) { if (!CakeHive.isAdapterRegistered(Erc20Token.typeId)) {
Hive.registerAdapter(Erc20TokenAdapter()); CakeHive.registerAdapter(Erc20TokenAdapter());
} }
_sharedPrefs.complete(SharedPreferences.getInstance()); _sharedPrefs.complete(SharedPreferences.getInstance());
@ -95,7 +96,7 @@ abstract class EthereumWalletBase
Completer<SharedPreferences> _sharedPrefs = Completer(); Completer<SharedPreferences> _sharedPrefs = Completer();
Future<void> init() async { Future<void> init() async {
erc20TokensBox = await Hive.openBox<Erc20Token>(Erc20Token.boxName); erc20TokensBox = await CakeHive.openBox<Erc20Token>(Erc20Token.boxName);
await walletAddresses.init(); await walletAddresses.init();
await transactionHistory.init(); await transactionHistory.init();
_privateKey = await getPrivateKey(_mnemonic, _password); _privateKey = await getPrivateKey(_mnemonic, _password);

View file

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

View file

@ -1,7 +1,8 @@
import 'package:cake_wallet/buy/buy_provider_description.dart'; import 'package:cake_wallet/buy/buy_provider_description.dart';
import 'package:hive/hive.dart';
import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/exchange/trade_state.dart';
import 'package:cw_core/format_amount.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'; 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 boxName = 'Orders';
static const boxKey = 'ordersBoxKey'; static const boxKey = 'ordersBoxKey';

View file

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

View file

@ -36,6 +36,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_qr_page.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.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/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/src/screens/wallet/wallet_edit_page.dart';
import 'package:cake_wallet/themes/theme_list.dart'; import 'package:cake_wallet/themes/theme_list.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
@ -869,6 +871,12 @@ Future setup({
getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>())); getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>()));
getIt.registerFactory(() =>
SupportChatPage(
getIt.get<SupportViewModel>(), secureStorage: getIt.get<FlutterSecureStorage>()));
getIt.registerFactory(() => SupportOtherLinksPage(getIt.get<SupportViewModel>()));
getIt.registerFactory(() { getIt.registerFactory(() {
final wallet = getIt.get<AppStore>().wallet; 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: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:cw_core/keyable.dart';
import 'package:hive/hive.dart';
part 'contact.g.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'; static const boxName = 'Contacts';
@HiveField(0, defaultValue: '') @HiveField(0, defaultValue: '')

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -41,6 +41,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.dart';
import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.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/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_details_page.dart';
import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/utils/payment_request.dart';
@ -48,7 +50,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/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/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/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:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -442,9 +443,17 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.support: case Routes.support:
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (_) => getIt.get<SupportPage>()); 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: case Routes.unspentCoinsList:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
builder: (_) => getIt.get<UnspentCoinsListPage>()); builder: (_) => getIt.get<UnspentCoinsListPage>());

View file

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

View file

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

View file

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

View file

@ -6,7 +6,6 @@ class SettingsCellWithArrow extends StandardListRow {
: super(title: title, isSelected: false, onTap: handler); : super(title: title, isSelected: false, onTap: handler);
@override @override
Widget buildTrailing(BuildContext context) => Widget buildTrailing(BuildContext context) => Image.asset('assets/images/select_arrow.png',
Image.asset('assets/images/select_arrow.png', color: Theme.of(context).primaryTextTheme.labelSmall!.color!);
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/generated/i18n.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_link_provider_cell.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/src/screens/support/widgets/support_tiles.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/view_model/settings/link_list_item.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/view_model/settings/regular_list_item.dart';
import 'package:cake_wallet/view_model/support_view_model.dart'; import 'package:cake_wallet/view_model/support_view_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:cake_wallet/generated/i18n.dart';
class SupportPage extends BasePage { class SupportPage extends BasePage {
SupportPage(this.supportViewModel); SupportPage(this.supportViewModel);
final SupportViewModel 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 @override
String get title => S.current.settings_support; String get title => S.current.settings_support;
@override
AppBarStyle get appBarStyle => AppBarStyle.regular;
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
final iconColor = Theme.of(context) return Container(
.accentTextTheme! child: Center(
.displayLarge!
.backgroundColor!;
// FIX-ME: Added `context` it was not used here before, maby bug ?
return Center(
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: 500), constraints: BoxConstraints(maxWidth: 330),
child: SectionStandardList( child: Column(
context: context, children: [
sectionCount: 1, Padding(
itemCounter: (int _) => supportViewModel.items.length, padding: EdgeInsets.only(top: 24),
itemBuilder: (_, __, index) { child: SupportTile(
final item = supportViewModel.items[index]; image: imageLiveSupport,
title: S.of(context).support_title_live_chat,
if (item is RegularListItem) { description: S.of(context).support_description_live_chat,
return SettingsCellWithArrow(title: item.title, handler: item.handler); onPressed: () {
if (DeviceInfo.instance.isDesktop) {
_launchUrl(supportViewModel.fetchUrl());
} else {
Navigator.pushNamed(context, Routes.supportLiveChat);
} }
},
if (item is LinkListItem) { ),
return SettingsLinkProviderCell( ),
title: item.title, Padding(
icon: item.icon, padding: EdgeInsets.only(top: 24),
iconColor: item.hasIconColor ? iconColor : null, child: SupportTile(
link: item.link, image: imageWalletGuides,
linkTitle: item.linkTitle); title: S.of(context).support_title_guides,
} description: S.of(context).support_description_guides,
onPressed: () => _launchUrl(supportViewModel.guidesUrl),
return Container(); ),
}), ),
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

@ -1,12 +1,9 @@
import 'package:cake_wallet/generated/i18n.dart'; 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/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: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: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'; part 'support_view_model.g.dart';
@ -15,14 +12,6 @@ class SupportViewModel = SupportViewModelBase with _$SupportViewModel;
abstract class SupportViewModelBase with Store { abstract class SupportViewModelBase with Store {
SupportViewModelBase() SupportViewModelBase()
: items = [ : items = [
RegularListItem(
title: S.current.faq,
handler: (BuildContext context) async {
try {
await launch(url);
} catch (e) {}
},
),
LinkListItem( LinkListItem(
title: 'Email', title: 'Email',
linkTitle: 'support@cakewallet.com', linkTitle: 'support@cakewallet.com',
@ -85,7 +74,17 @@ abstract class SupportViewModelBase with Store {
// link: 'mailto:support@y.at') // 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; List<SettingsListItem> items;
} }

View file

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

View file

@ -668,5 +668,11 @@
"slidable": "قابل للانزلاق", "slidable": "قابل للانزلاق",
"etherscan_history": "Etherscan تاريخ", "etherscan_history": "Etherscan تاريخ",
"manage_nodes": "ﺪﻘﻌﻟﺍ ﺓﺭﺍﺩﺇ", "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": "Плъзгащ се", "slidable": "Плъзгащ се",
"etherscan_history": "История на Etherscan", "etherscan_history": "История на Etherscan",
"manage_nodes": "Управление на възли", "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é", "slidable": "Posuvné",
"manage_nodes": "Spravovat uzly", "manage_nodes": "Spravovat uzly",
"etherscan_history": "Historie Etherscanu", "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_trades": "Nur Handel",
"settings_only_transactions": "Nur Transaktionen", "settings_only_transactions": "Nur Transaktionen",
"settings_none": "Keiner", "settings_none": "Keiner",
"settings_support": "Unterstützen", "settings_support": "Hilfe",
"settings_terms_and_conditions": "Geschäftsbedingungen", "settings_terms_and_conditions": "Geschäftsbedingungen",
"pin_is_incorrect": "PIN ist falsch", "pin_is_incorrect": "PIN ist falsch",
"setup_pin": "PIN einrichten", "setup_pin": "PIN einrichten",
@ -672,5 +672,11 @@
"slidable": "Verschiebbar", "slidable": "Verschiebbar",
"manage_nodes": "Knoten verwalten", "manage_nodes": "Knoten verwalten",
"etherscan_history": "Etherscan-Geschichte", "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", "slidable": "Slidable",
"manage_nodes": "Manage nodes", "manage_nodes": "Manage nodes",
"etherscan_history": "Etherscan history", "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", "slidable": "deslizable",
"manage_nodes": "Administrar nodos", "manage_nodes": "Administrar nodos",
"etherscan_history": "historia de etherscan", "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", "slidable": "Glissable",
"manage_nodes": "Gérer les nœuds", "manage_nodes": "Gérer les nœuds",
"etherscan_history": "Historique d'Etherscan", "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", "slidable": "Mai iya zamewa",
"etherscan_history": "Etherscan tarihin kowane zamani", "etherscan_history": "Etherscan tarihin kowane zamani",
"manage_nodes": "Sarrafa nodes", "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": "फिसलने लायक", "slidable": "फिसलने लायक",
"manage_nodes": "नोड्स प्रबंधित करें", "manage_nodes": "नोड्स प्रबंधित करें",
"etherscan_history": "इथरस्कैन इतिहास", "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", "slidable": "Klizna",
"manage_nodes": "Upravljanje čvorovima", "manage_nodes": "Upravljanje čvorovima",
"etherscan_history": "Etherscan povijest", "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", "slidable": "Dapat digeser",
"manage_nodes": "Kelola node", "manage_nodes": "Kelola node",
"etherscan_history": "Sejarah Etherscan", "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", "slidable": "Scorrevole",
"manage_nodes": "Gestisci i nodi", "manage_nodes": "Gestisci i nodi",
"etherscan_history": "Storia Etherscan", "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

@ -672,5 +672,11 @@
"slidable": "スライド可能", "slidable": "スライド可能",
"manage_nodes": "ノードの管理", "manage_nodes": "ノードの管理",
"etherscan_history": "イーサスキャンの歴史", "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": "슬라이딩 가능", "slidable": "슬라이딩 가능",
"manage_nodes": "노드 관리", "manage_nodes": "노드 관리",
"etherscan_history": "이더스캔 역사", "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

@ -670,5 +670,11 @@
"slidable": "လျှောချနိုင်သည်။", "slidable": "လျှောချနိုင်သည်။",
"manage_nodes": "ဆုံမှတ်များကို စီမံပါ။", "manage_nodes": "ဆုံမှတ်များကို စီမံပါ။",
"etherscan_history": "Etherscan သမိုင်း", "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", "slidable": "Verschuifbaar",
"manage_nodes": "Beheer knooppunten", "manage_nodes": "Beheer knooppunten",
"etherscan_history": "Etherscan-geschiedenis", "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", "slidable": "Przesuwne",
"manage_nodes": "Zarządzaj węzłami", "manage_nodes": "Zarządzaj węzłami",
"etherscan_history": "Historia Etherscanu", "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", "slidable": "Deslizável",
"manage_nodes": "Gerenciar nós", "manage_nodes": "Gerenciar nós",
"etherscan_history": "história Etherscan", "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_address": "Пожалуйста, введите iPv4 адрес",
"error_text_node_port": "Порт ноды может содержать только цифры от 0 до 65535", "error_text_node_port": "Порт ноды может содержать только цифры от 0 до 65535",
"error_text_node_proxy_address": "Введите <IPv4-адрес>:<порт>, например 127.0.0.1:9050.", "error_text_node_proxy_address": "Введите <IPv4-адрес>:<порт>, например 127.0.0.1:9050.",
"error_text_payment_id": "Идентификатор платежа может содержать от 16 до 64 символов в hex", "error_text_payment_id": "Идентификатор платежа может содержать от 16 до 64 символов в hex",
"error_text_xmr": "Значение XMR не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 12", "error_text_xmr": "Значение XMR не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 12",
"error_text_fiat": "Значение суммы не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 2", "error_text_fiat": "Значение суммы не может превышать доступный баланс.\nКоличество цифр после запятой должно быть меньше или равно 2",
@ -673,5 +672,11 @@
"slidable": "Скользящий", "slidable": "Скользящий",
"manage_nodes": "Управление узлами", "manage_nodes": "Управление узлами",
"etherscan_history": "История Эфириума", "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

@ -670,5 +670,11 @@
"slidable": "เลื่อนได้", "slidable": "เลื่อนได้",
"manage_nodes": "จัดการโหนด", "manage_nodes": "จัดการโหนด",
"etherscan_history": "ประวัติอีเธอร์สแกน", "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", "slidable": "kaydırılabilir",
"manage_nodes": "Düğümleri yönet", "manage_nodes": "Düğümleri yönet",
"etherscan_history": "Etherscan geçmişi", "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": "Розсувний", "slidable": "Розсувний",
"manage_nodes": "Керуйте вузлами", "manage_nodes": "Керуйте вузлами",
"etherscan_history": "Історія Etherscan", "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

@ -664,5 +664,11 @@
"slidable": "سلائیڈ ایبل", "slidable": "سلائیڈ ایبل",
"manage_nodes": "۔ﮟﯾﺮﮐ ﻢﻈﻧ ﺎﮐ ﺱﮈﻮﻧ", "manage_nodes": "۔ﮟﯾﺮﮐ ﻢﻈﻧ ﺎﮐ ﺱﮈﻮﻧ",
"etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ", "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

@ -666,5 +666,11 @@
"slidable": "Slidable", "slidable": "Slidable",
"manage_nodes": "Ṣakoso awọn apa", "manage_nodes": "Ṣakoso awọn apa",
"etherscan_history": "Etherscan itan", "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

@ -671,5 +671,11 @@
"slidable": "可滑动", "slidable": "可滑动",
"manage_nodes": "管理节点", "manage_nodes": "管理节点",
"etherscan_history": "以太扫描历史", "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

@ -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 = final fileConfig =
json.decode(configFile.readAsStringSync()) as Map<String, dynamic> ?? json.decode(configFile.readAsStringSync()) as Map<String, dynamic>;
<String, dynamic>{};
fileConfig.forEach((key, dynamic value) { fileConfig.forEach((key, dynamic value) {
if (secrets[key] == null) { if (secrets[key] == null) {
secrets[key] = value; secrets[key] = value;

View file

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