Cw 155 monero synchronization (#1014)

* Run Monero Synchronization task in background on Android

* Add monero sync task in the load function to be registered/cancelled when user changes wallets

* Revert unused file changes

* Register Sync task on all monero wallets if any

* Add Sync Modes and change task frequency based on user's choice

* Register background task after current wallet is set

* Add Sync All toggle and change task wallets to sync accordingly

* Enable background notifications in release mode temporarily

* Disable constraints and increase the frequency of tasks

* Decrease frequency of background tasks

* Delay the background task thread till the syncing thread finish (Dummy Trial-1)

* Start Sync process and wait for it to finish

* Wait for synchronization to finish before ending the background thread
Add 10 minutes timeout duration for sync process

* Connect to node before syncing wallet

* replace testing configuration with the configurations agreed on

* Fix Conflicts with main

* Update and Migrate Background tasks to null safety

* Update workmanager version in pubspec_base also

* Move Sync options to Connection and sync page
Show Sync options only for Monero and Haven
Minor Enhancements

* Remove debugging notifications
Revert aggressive mode frequency to 6 hours [skip ci]

* Add iOS configs

* Revert debugging changes
Fix conflicts with main

* Add/Extract Sync configurations to/from backup file [skip ci]
This commit is contained in:
Omar Hatem 2023-08-04 20:55:56 +03:00 committed by GitHub
parent 68a057b91b
commit aedf310c9d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 593 additions and 190 deletions

View file

@ -1,7 +1,9 @@
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
const utils = const MethodChannel('com.cake_wallet/native_utils');
void setIsAppSecureNative(bool isAppSecure) { void setIsAppSecureNative(bool isAppSecure) {
utils.invokeMethod<Uint8List>('setIsAppSecure', {'isAppSecure': isAppSecure}); try {
} final utils = const MethodChannel('com.cake_wallet/native_utils');
utils.invokeMethod<Uint8List>('setIsAppSecure', {'isAppSecure': isAppSecure});
} catch (_) {}
}

View file

@ -606,4 +606,9 @@
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };
rootObject = 97C146E61CF9000F007C117D /* Project object */; rootObject = 97C146E61CF9000F007C117D /* Project object */;
SystemCapabilities = {
com.apple.BackgroundModes = {
enabled = 1;
};
};
} }

View file

@ -1,6 +1,7 @@
import UIKit import UIKit
import Flutter import Flutter
import UnstoppableDomainsResolution import UnstoppableDomainsResolution
import workmanager
@UIApplicationMain @UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate { @objc class AppDelegate: FlutterAppDelegate {
@ -16,6 +17,15 @@ import UnstoppableDomainsResolution
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
} }
WorkmanagerPlugin.setPluginRegistrantCallback { registry in
// Registry in this case is the FlutterEngine that is created in Workmanager's
// performFetchWithCompletionHandler or BGAppRefreshTask.
// This will make other plugins available during a background operation.
GeneratedPluginRegistrant.register(with: registry)
}
WorkmanagerPlugin.registerTask(withIdentifier: "com.fotolockr.cakewallet.monero_sync_task")
makeSecure() makeSecure()
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let controller : FlutterViewController = window?.rootViewController as! FlutterViewController

View file

@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.fotolockr.cakewallet.monero_sync_task</string>
</array>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
@ -113,6 +117,7 @@
<key>UIBackgroundModes</key> <key>UIBackgroundModes</key>
<array> <array>
<string>fetch</string> <string>fetch</string>
<string>processing</string>
<string>remote-notification</string> <string>remote-notification</string>
</array> </array>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>

View file

@ -243,6 +243,8 @@ class BackupService {
final sortBalanceTokensBy = data[PreferencesKey.sortBalanceBy] as int?; final sortBalanceTokensBy = data[PreferencesKey.sortBalanceBy] as int?;
final pinNativeTokenAtTop = data[PreferencesKey.pinNativeTokenAtTop] as bool?; final pinNativeTokenAtTop = data[PreferencesKey.pinNativeTokenAtTop] as bool?;
final useEtherscan = data[PreferencesKey.useEtherscan] as bool?; final useEtherscan = data[PreferencesKey.useEtherscan] as bool?;
final syncAll = data[PreferencesKey.syncAllKey] as bool?;
final syncMode = data[PreferencesKey.syncModeKey] as int?;
await _sharedPreferences.setString(PreferencesKey.currentWalletName, currentWalletName); await _sharedPreferences.setString(PreferencesKey.currentWalletName, currentWalletName);
@ -361,6 +363,12 @@ class BackupService {
if (useEtherscan != null) if (useEtherscan != null)
await _sharedPreferences.setBool(PreferencesKey.useEtherscan, useEtherscan); await _sharedPreferences.setBool(PreferencesKey.useEtherscan, useEtherscan);
if (syncAll != null)
await _sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll);
if (syncMode != null)
await _sharedPreferences.setInt(PreferencesKey.syncModeKey, syncMode);
await preferencesFile.delete(); await preferencesFile.delete();
} }
@ -510,6 +518,10 @@ class BackupService {
_sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop), _sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop),
PreferencesKey.useEtherscan: PreferencesKey.useEtherscan:
_sharedPreferences.getBool(PreferencesKey.useEtherscan), _sharedPreferences.getBool(PreferencesKey.useEtherscan),
PreferencesKey.syncModeKey:
_sharedPreferences.getInt(PreferencesKey.syncModeKey),
PreferencesKey.syncAllKey:
_sharedPreferences.getBool(PreferencesKey.syncAllKey),
}; };
return json.encode(preferences); return json.encode(preferences);

View file

@ -4,6 +4,7 @@ import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart'; import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
import 'package:cake_wallet/core/yat_service.dart'; import 'package:cake_wallet/core/yat_service.dart';
import 'package:cake_wallet/entities/background_tasks.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart';
import 'package:cake_wallet/entities/receive_page_option.dart'; import 'package:cake_wallet/entities/receive_page_option.dart';
@ -23,6 +24,7 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart'; import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
@ -244,6 +246,8 @@ Future setup({
getIt.registerSingletonAsync<SharedPreferences>(() => SharedPreferences.getInstance()); getIt.registerSingletonAsync<SharedPreferences>(() => SharedPreferences.getInstance());
} }
getIt.registerFactory(() => BackgroundTasks());
final isBitcoinBuyEnabled = (secrets.wyreSecretKey.isNotEmpty) && final isBitcoinBuyEnabled = (secrets.wyreSecretKey.isNotEmpty) &&
(secrets.wyreApiKey.isNotEmpty) && (secrets.wyreApiKey.isNotEmpty) &&
(secrets.wyreAccountId.isNotEmpty); (secrets.wyreAccountId.isNotEmpty);
@ -681,8 +685,7 @@ Future setup({
return NodeListViewModel(_nodeSource, appStore); return NodeListViewModel(_nodeSource, appStore);
}); });
getIt.registerFactory( getIt.registerFactory(() => ConnectionSyncPage(getIt.get<DashboardViewModel>()));
() => ConnectionSyncPage(getIt.get<NodeListViewModel>(), getIt.get<DashboardViewModel>()));
getIt.registerFactory( getIt.registerFactory(
() => SecurityBackupPage(getIt.get<SecuritySettingsViewModel>(), getIt.get<AuthService>())); () => SecurityBackupPage(getIt.get<SecuritySettingsViewModel>(), getIt.get<AuthService>()));
@ -1055,5 +1058,7 @@ Future setup({
), ),
); );
getIt.registerFactory<ManageNodesPage>(() => ManageNodesPage(getIt.get<NodeListViewModel>()));
_isSetupFinished = true; _isSetupFinished = true;
} }

View file

@ -0,0 +1,164 @@
import 'dart:io';
import 'package:cake_wallet/core/wallet_loading_service.dart';
import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:workmanager/workmanager.dart';
import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/di.dart';
const moneroSyncTaskKey = "com.fotolockr.cakewallet.monero_sync_task";
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
try {
switch (task) {
case moneroSyncTaskKey:
/// The work manager runs on a separate isolate from the main flutter isolate.
/// thus we initialize app configs first; hive, getIt, etc...
await initializeAppConfigs();
final walletLoadingService = getIt.get<WalletLoadingService>();
final node = getIt.get<SettingsStore>().getCurrentNode(WalletType.monero);
final typeRaw = getIt.get<SharedPreferences>().getInt(PreferencesKey.currentWalletType);
WalletBase? wallet;
if (inputData!['sync_all'] as bool) {
/// get all Monero wallets of the user and sync them
final List<WalletListItem> moneroWallets = getIt
.get<WalletListViewModel>()
.wallets
.where((element) => element.type == WalletType.monero)
.toList();
for (int i = 0; i < moneroWallets.length; i++) {
wallet = await walletLoadingService.load(WalletType.monero, moneroWallets[i].name);
await wallet.connectToNode(node: node);
await wallet.startSync();
}
} else {
/// if the user chose to sync only active wallet
/// if the current wallet is monero; sync it only
if (typeRaw == WalletType.monero.index) {
final name =
getIt.get<SharedPreferences>().getString(PreferencesKey.currentWalletName);
wallet = await walletLoadingService.load(WalletType.monero, name!);
await wallet.connectToNode(node: node);
await wallet.startSync();
}
}
if (wallet?.syncStatus.progress() == null) {
return Future.error("No Monero wallet found");
}
for (int i = 0;; i++) {
await Future<void>.delayed(const Duration(seconds: 1));
if (wallet?.syncStatus.progress() == 1.0) {
break;
}
if (i > 600) {
return Future.error("Synchronization Timed out");
}
}
break;
}
return Future.value(true);
} catch (error, stackTrace) {
print(error);
print(stackTrace);
return Future.error(error);
}
});
}
class BackgroundTasks {
void registerSyncTask({bool changeExisting = false}) async {
try {
bool hasMonero = getIt
.get<WalletListViewModel>()
.wallets
.any((element) => element.type == WalletType.monero);
/// if its not android nor ios, or the user has no monero wallets; exit
if (!DeviceInfo.instance.isMobile || !hasMonero) {
return;
}
final settingsStore = getIt.get<SettingsStore>();
final SyncMode syncMode = settingsStore.currentSyncMode;
final bool syncAll = settingsStore.currentSyncAll;
if (syncMode.type == SyncType.disabled) {
cancelSyncTask();
return;
}
await Workmanager().initialize(
callbackDispatcher,
isInDebugMode: kDebugMode,
);
final inputData = <String, dynamic>{"sync_all": syncAll};
final constraints = Constraints(
networkType:
syncMode.type == SyncType.unobtrusive ? NetworkType.unmetered : NetworkType.connected,
requiresBatteryNotLow: syncMode.type == SyncType.unobtrusive,
requiresCharging: syncMode.type == SyncType.unobtrusive,
requiresDeviceIdle: syncMode.type == SyncType.unobtrusive,
);
if (Platform.isIOS) {
await Workmanager().registerOneOffTask(
moneroSyncTaskKey,
moneroSyncTaskKey,
initialDelay: syncMode.frequency,
existingWorkPolicy: ExistingWorkPolicy.replace,
inputData: inputData,
constraints: constraints,
);
return;
}
await Workmanager().registerPeriodicTask(
moneroSyncTaskKey,
moneroSyncTaskKey,
initialDelay: syncMode.frequency,
frequency: syncMode.frequency,
existingWorkPolicy: changeExisting ? ExistingWorkPolicy.replace : ExistingWorkPolicy.keep,
inputData: inputData,
constraints: constraints,
);
} catch (error, stackTrace) {
print(error);
print(stackTrace);
}
}
void cancelSyncTask() {
try {
Workmanager().cancelByUniqueName(moneroSyncTaskKey);
} catch (error, stackTrace) {
print(error);
print(stackTrace);
}
}
}

View file

@ -1,8 +1,7 @@
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/entities/background_tasks.dart';
import 'package:cw_core/wallet_service.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/core/wallet_loading_service.dart'; import 'package:cake_wallet/core/wallet_loading_service.dart';
@ -24,4 +23,6 @@ Future<void> loadCurrentWallet() async {
final walletLoadingService = getIt.get<WalletLoadingService>(); final walletLoadingService = getIt.get<WalletLoadingService>();
final wallet = await walletLoadingService.load(type, name); final wallet = await walletLoadingService.load(type, name);
appStore.changeCurrentWallet(wallet); appStore.changeCurrentWallet(wallet);
getIt.get<BackgroundTasks>().registerSyncTask();
} }

View file

@ -36,6 +36,8 @@ class PreferencesKey {
static const shouldShowReceiveWarning = 'should_show_receive_warning'; static const shouldShowReceiveWarning = 'should_show_receive_warning';
static const shouldShowYatPopup = 'should_show_yat_popup'; static const shouldShowYatPopup = 'should_show_yat_popup';
static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1'; static const moneroWalletPasswordUpdateV1Base = 'monero_wallet_update_v1';
static const syncModeKey = 'sync_mode';
static const syncAllKey = 'sync_all';
static const pinTimeOutDuration = 'pin_timeout_duration'; static const pinTimeOutDuration = 'pin_timeout_duration';
static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds'; static const lastAuthTimeMilliseconds = 'last_auth_time_milliseconds';
static const lastPopupDate = 'last_popup_date'; static const lastPopupDate = 'last_popup_date';

View file

@ -57,97 +57,103 @@ Future<void> main() async {
return true; return true;
}; };
final appDir = await getApplicationDocumentsDirectory();
await Hive.close(); await Hive.close();
Hive.init(appDir.path);
if (!Hive.isAdapterRegistered(Contact.typeId)) { await initializeAppConfigs();
Hive.registerAdapter(ContactAdapter());
}
if (!Hive.isAdapterRegistered(Node.typeId)) {
Hive.registerAdapter(NodeAdapter());
}
if (!Hive.isAdapterRegistered(TransactionDescription.typeId)) {
Hive.registerAdapter(TransactionDescriptionAdapter());
}
if (!Hive.isAdapterRegistered(Trade.typeId)) {
Hive.registerAdapter(TradeAdapter());
}
if (!Hive.isAdapterRegistered(WalletInfo.typeId)) {
Hive.registerAdapter(WalletInfoAdapter());
}
if (!Hive.isAdapterRegistered(walletTypeTypeId)) {
Hive.registerAdapter(WalletTypeAdapter());
}
if (!Hive.isAdapterRegistered(Template.typeId)) {
Hive.registerAdapter(TemplateAdapter());
}
if (!Hive.isAdapterRegistered(ExchangeTemplate.typeId)) {
Hive.registerAdapter(ExchangeTemplateAdapter());
}
if (!Hive.isAdapterRegistered(Order.typeId)) {
Hive.registerAdapter(OrderAdapter());
}
if (!isMoneroOnly && !Hive.isAdapterRegistered(UnspentCoinsInfo.typeId)) {
Hive.registerAdapter(UnspentCoinsInfoAdapter());
}
if (!Hive.isAdapterRegistered(AnonpayInvoiceInfo.typeId)) {
Hive.registerAdapter(AnonpayInvoiceInfoAdapter());
}
final secureStorage = FlutterSecureStorage();
final transactionDescriptionsBoxKey =
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>(
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);
Box<UnspentCoinsInfo>? unspentCoinsInfoSource;
if (!isMoneroOnly) {
unspentCoinsInfoSource = await Hive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName);
}
await initialSetup(
sharedPreferences: await SharedPreferences.getInstance(),
nodes: nodes,
walletInfoSource: walletInfoSource,
contactSource: contacts,
tradesSource: trades,
ordersSource: orders,
unspentCoinsInfoSource: unspentCoinsInfoSource,
// fiatConvertationService: fiatConvertationService,
templates: templates,
exchangeTemplates: exchangeTemplates,
transactionDescriptions: transactionDescriptions,
secureStorage: secureStorage,
anonpayInvoiceInfo: anonpayInvoiceInfo,
initialMigrationVersion: 21);
runApp(App()); runApp(App());
}, (error, stackTrace) async { }, (error, stackTrace) async {
ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stackTrace)); ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stackTrace));
}); });
} }
Future<void> initializeAppConfigs() async {
final appDir = await getApplicationDocumentsDirectory();
Hive.init(appDir.path);
if (!Hive.isAdapterRegistered(Contact.typeId)) {
Hive.registerAdapter(ContactAdapter());
}
if (!Hive.isAdapterRegistered(Node.typeId)) {
Hive.registerAdapter(NodeAdapter());
}
if (!Hive.isAdapterRegistered(TransactionDescription.typeId)) {
Hive.registerAdapter(TransactionDescriptionAdapter());
}
if (!Hive.isAdapterRegistered(Trade.typeId)) {
Hive.registerAdapter(TradeAdapter());
}
if (!Hive.isAdapterRegistered(WalletInfo.typeId)) {
Hive.registerAdapter(WalletInfoAdapter());
}
if (!Hive.isAdapterRegistered(walletTypeTypeId)) {
Hive.registerAdapter(WalletTypeAdapter());
}
if (!Hive.isAdapterRegistered(Template.typeId)) {
Hive.registerAdapter(TemplateAdapter());
}
if (!Hive.isAdapterRegistered(ExchangeTemplate.typeId)) {
Hive.registerAdapter(ExchangeTemplateAdapter());
}
if (!Hive.isAdapterRegistered(Order.typeId)) {
Hive.registerAdapter(OrderAdapter());
}
if (!isMoneroOnly && !Hive.isAdapterRegistered(UnspentCoinsInfo.typeId)) {
Hive.registerAdapter(UnspentCoinsInfoAdapter());
}
if (!Hive.isAdapterRegistered(AnonpayInvoiceInfo.typeId)) {
Hive.registerAdapter(AnonpayInvoiceInfoAdapter());
}
final secureStorage = FlutterSecureStorage();
final transactionDescriptionsBoxKey =
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>(
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);
Box<UnspentCoinsInfo>? unspentCoinsInfoSource;
if (!isMoneroOnly) {
unspentCoinsInfoSource = await Hive.openBox<UnspentCoinsInfo>(UnspentCoinsInfo.boxName);
}
await initialSetup(
sharedPreferences: await SharedPreferences.getInstance(),
nodes: nodes,
walletInfoSource: walletInfoSource,
contactSource: contacts,
tradesSource: trades,
ordersSource: orders,
unspentCoinsInfoSource: unspentCoinsInfoSource,
// fiatConvertationService: fiatConvertationService,
templates: templates,
exchangeTemplates: exchangeTemplates,
transactionDescriptions: transactionDescriptions,
secureStorage: secureStorage,
anonpayInvoiceInfo: anonpayInvoiceInfo,
initialMigrationVersion: 21);
}
Future<void> initialSetup( Future<void> initialSetup(
{required SharedPreferences sharedPreferences, {required SharedPreferences sharedPreferences,
required Box<Node> nodes, required Box<Node> nodes,
@ -319,7 +325,7 @@ class _HomeState extends State<_Home> {
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const SizedBox.shrink(); return const SizedBox.shrink();

View file

@ -19,6 +19,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_dashbo
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/desktop_settings/desktop_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
import 'package:cake_wallet/src/screens/settings/security_backup_page.dart'; import 'package:cake_wallet/src/screens/settings/security_backup_page.dart';
@ -605,6 +606,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
), ),
); );
case Routes.manageNodes:
return MaterialPageRoute<void>(builder: (_) => getIt.get<ManageNodesPage>());
default: default:
return MaterialPageRoute<void>( return MaterialPageRoute<void>(
builder: (_) => Scaffold( builder: (_) => Scaffold(

View file

@ -90,4 +90,5 @@ class Routes {
static const modify2FAPage = '/modify_2fa_page'; static const modify2FAPage = '/modify_2fa_page';
static const homeSettings = '/home_settings'; static const homeSettings = '/home_settings';
static const editToken = '/edit_token'; static const editToken = '/edit_token';
static const manageNodes = '/manage_nodes';
} }

View file

@ -1,25 +1,25 @@
import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
import 'package:cake_wallet/utils/device_info.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/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cw_core/node.dart'; import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
class ConnectionSyncPage extends BasePage { class ConnectionSyncPage extends BasePage {
ConnectionSyncPage(this.nodeListViewModel, this.dashboardViewModel); ConnectionSyncPage(this.dashboardViewModel);
@override @override
String get title => S.current.connection_sync; String get title => S.current.connection_sync;
final NodeListViewModel nodeListViewModel;
final DashboardViewModel dashboardViewModel; final DashboardViewModel dashboardViewModel;
@override @override
@ -33,72 +33,39 @@ class ConnectionSyncPage extends BasePage {
title: S.current.reconnect, title: S.current.reconnect,
handler: (context) => _presentReconnectAlert(context), handler: (context) => _presentReconnectAlert(context),
), ),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
if (dashboardViewModel.hasRescan) if (dashboardViewModel.hasRescan) ...[
SettingsCellWithArrow( SettingsCellWithArrow(
title: S.current.rescan, title: S.current.rescan,
handler: (context) => Navigator.of(context).pushNamed(Routes.rescan), handler: (context) => Navigator.of(context).pushNamed(Routes.rescan),
), ),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
Semantics( if (DeviceInfo.instance.isMobile) ...[
button: true, Observer(builder: (context) {
child: NodeHeaderListRow( return SettingsPickerCell<SyncMode>(
title: S.of(context).add_new_node, title: S.current.background_sync_mode,
onTap: (_) async => items: SyncMode.all,
await Navigator.of(context).pushNamed(Routes.newNode), displayItem: (SyncMode syncMode) => syncMode.name,
), selectedItem: dashboardViewModel.syncMode,
), onItemSelected: dashboardViewModel.setSyncMode,
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)), );
SizedBox(height: 100), }),
Observer( const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
builder: (BuildContext context) { Observer(builder: (context) {
return Flexible( return SettingsSwitcherCell(
child: SectionStandardList( title: S.current.sync_all_wallets,
sectionCount: 1, value: dashboardViewModel.syncAll,
context: context, onValueChange: (_, bool value) => dashboardViewModel.setSyncAll(value),
dividerPadding: EdgeInsets.symmetric(horizontal: 24), );
itemCounter: (int sectionIndex) { }),
return nodeListViewModel.nodes.length; const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
}, ],
itemBuilder: (_, sectionIndex, index) { ],
final node = nodeListViewModel.nodes[index]; SettingsCellWithArrow(
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex; title: S.current.manage_nodes,
final nodeListRow = NodeListRow( handler: (context) => Navigator.of(context).pushNamed(Routes.manageNodes),
title: node.uriRaw,
node: node,
isSelected: isSelected,
onTap: (_) async {
if (isSelected) {
return;
}
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle:
S.of(context).change_current_node_title,
alertContent: nodeListViewModel
.getAlertContent(node.uriRaw),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () =>
Navigator.of(context).pop(),
actionRightButton: () async {
await nodeListViewModel.setAsCurrent(node);
Navigator.of(context).pop();
},
);
});
},
);
return nodeListRow;
},
),
);
},
), ),
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
], ],
), ),
); );

View file

@ -0,0 +1,85 @@
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
import 'package:cake_wallet/src/widgets/standard_list.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
class ManageNodesPage extends BasePage {
ManageNodesPage(this.nodeListViewModel);
final NodeListViewModel nodeListViewModel;
@override
String get title => S.current.manage_nodes;
@override
Widget body(BuildContext context) {
return Padding(
padding: EdgeInsets.only(top: 10),
child: Column(
children: [
Semantics(
button: true,
child: NodeHeaderListRow(
title: S.of(context).add_new_node,
onTap: (_) async => await Navigator.of(context).pushNamed(Routes.newNode),
),
),
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
SizedBox(height: 20),
Observer(
builder: (BuildContext context) {
return Flexible(
child: SectionStandardList(
sectionCount: 1,
context: context,
dividerPadding: EdgeInsets.symmetric(horizontal: 24),
itemCounter: (int sectionIndex) {
return nodeListViewModel.nodes.length;
},
itemBuilder: (_, sectionIndex, index) {
final node = nodeListViewModel.nodes[index];
final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex;
final nodeListRow = NodeListRow(
title: node.uriRaw,
node: node,
isSelected: isSelected,
onTap: (_) async {
if (isSelected) {
return;
}
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithTwoActions(
alertTitle: S.of(context).change_current_node_title,
alertContent: nodeListViewModel.getAlertContent(node.uriRaw),
leftButtonText: S.of(context).cancel,
rightButtonText: S.of(context).change,
actionLeftButton: () => Navigator.of(context).pop(),
actionRightButton: () async {
await nodeListViewModel.setAsCurrent(node);
Navigator.of(context).pop();
},
);
});
},
);
return nodeListRow;
},
),
);
},
),
],
),
);
}
}

View file

@ -20,26 +20,32 @@ class OtherSettingsPage extends BasePage {
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
return Observer(builder: (_) { return Observer(
return Container( builder: (_) {
padding: EdgeInsets.only(top: 10), return Container(
child: Column(children: [ padding: EdgeInsets.only(top: 10),
SettingsPickerCell( child: Column(
title: S.current.settings_fee_priority, children: [
items: priorityForWalletType(_otherSettingsViewModel.walletType), SettingsPickerCell(
displayItem: _otherSettingsViewModel.getDisplayPriority, title: S.current.settings_fee_priority,
selectedItem: _otherSettingsViewModel.transactionPriority, items: priorityForWalletType(_otherSettingsViewModel.walletType),
onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected, displayItem: _otherSettingsViewModel.getDisplayPriority,
selectedItem: _otherSettingsViewModel.transactionPriority,
onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected,
),
SettingsCellWithArrow(
title: S.current.settings_terms_and_conditions,
handler: (BuildContext context) =>
Navigator.of(context).pushNamed(Routes.readDisclaimer),
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
Spacer(),
SettingsVersionCell(
title: S.of(context).version(_otherSettingsViewModel.currentVersion))
],
), ),
SettingsCellWithArrow( );
title: S.current.settings_terms_and_conditions, },
handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.readDisclaimer), );
),
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
Spacer(),
SettingsVersionCell(title: S.of(context).version(_otherSettingsViewModel.currentVersion))
]),
);
});
} }
} }

View file

@ -78,7 +78,7 @@ class SectionHeaderListRow extends StatelessWidget {
class StandardListSeparator extends StatelessWidget { class StandardListSeparator extends StatelessWidget {
StandardListSeparator({this.padding, this.height = 1}); const StandardListSeparator({this.padding, this.height = 1});
final EdgeInsets? padding; final EdgeInsets? padding;
final double height; final double height;

View file

@ -2,10 +2,12 @@ import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/entities/cake_2fa_preset_options.dart'; import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
import 'package:cake_wallet/entities/background_tasks.dart';
import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/entities/pin_code_required_duration.dart'; import 'package:cake_wallet/entities/pin_code_required_duration.dart';
import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/preferences_key.dart';
import 'package:cake_wallet/entities/sort_balance_types.dart'; import 'package:cake_wallet/entities/sort_balance_types.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/device_info.dart';
import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/ethereum/ethereum.dart';
import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/transaction_priority.dart';
@ -34,7 +36,8 @@ class SettingsStore = SettingsStoreBase with _$SettingsStore;
abstract class SettingsStoreBase with Store { abstract class SettingsStoreBase with Store {
SettingsStoreBase( SettingsStoreBase(
{required SharedPreferences sharedPreferences, {required BackgroundTasks backgroundTasks,
required SharedPreferences sharedPreferences,
required bool initialShouldShowMarketPlaceInDashboard, required bool initialShouldShowMarketPlaceInDashboard,
required FiatCurrency initialFiatCurrency, required FiatCurrency initialFiatCurrency,
required BalanceDisplayMode initialBalanceDisplayMode, required BalanceDisplayMode initialBalanceDisplayMode,
@ -51,6 +54,8 @@ abstract class SettingsStoreBase with Store {
required ThemeBase initialTheme, required ThemeBase initialTheme,
required int initialPinLength, required int initialPinLength,
required String initialLanguageCode, required String initialLanguageCode,
required SyncMode initialSyncMode,
required bool initialSyncAll,
// required String initialCurrentLocale, // required String initialCurrentLocale,
required this.appVersion, required this.appVersion,
required this.deviceName, required this.deviceName,
@ -78,6 +83,7 @@ abstract class SettingsStoreBase with Store {
TransactionPriority? initialEthereumTransactionPriority}) TransactionPriority? initialEthereumTransactionPriority})
: nodes = ObservableMap<WalletType, Node>.of(nodes), : nodes = ObservableMap<WalletType, Node>.of(nodes),
_sharedPreferences = sharedPreferences, _sharedPreferences = sharedPreferences,
_backgroundTasks = backgroundTasks,
fiatCurrency = initialFiatCurrency, fiatCurrency = initialFiatCurrency,
balanceDisplayMode = initialBalanceDisplayMode, balanceDisplayMode = initialBalanceDisplayMode,
shouldSaveRecipientAddress = initialSaveRecipientAddress, shouldSaveRecipientAddress = initialSaveRecipientAddress,
@ -107,6 +113,8 @@ abstract class SettingsStoreBase with Store {
initialShouldRequireTOTP2FAForCreatingNewWallets, initialShouldRequireTOTP2FAForCreatingNewWallets,
shouldRequireTOTP2FAForAllSecurityAndBackupSettings = shouldRequireTOTP2FAForAllSecurityAndBackupSettings =
initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings, initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings,
currentSyncMode = initialSyncMode,
currentSyncAll = initialSyncAll,
priority = ObservableMap<WalletType, TransactionPriority>() { priority = ObservableMap<WalletType, TransactionPriority>() {
//this.nodes = ObservableMap<WalletType, Node>.of(nodes); //this.nodes = ObservableMap<WalletType, Node>.of(nodes);
@ -287,6 +295,18 @@ abstract class SettingsStoreBase with Store {
(BalanceDisplayMode mode) => sharedPreferences.setInt( (BalanceDisplayMode mode) => sharedPreferences.setInt(
PreferencesKey.currentBalanceDisplayModeKey, mode.serialize())); PreferencesKey.currentBalanceDisplayModeKey, mode.serialize()));
reaction((_) => currentSyncMode, (SyncMode syncMode) {
sharedPreferences.setInt(PreferencesKey.syncModeKey, syncMode.type.index);
_backgroundTasks.registerSyncTask(changeExisting: true);
});
reaction((_) => currentSyncAll, (bool syncAll) {
sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll);
_backgroundTasks.registerSyncTask(changeExisting: true);
});
reaction( reaction(
(_) => exchangeStatus, (_) => exchangeStatus,
(ExchangeApiMode mode) => (ExchangeApiMode mode) =>
@ -422,11 +442,18 @@ abstract class SettingsStoreBase with Store {
@observable @observable
bool useEtherscan; bool useEtherscan;
@observable
SyncMode currentSyncMode;
@observable
bool currentSyncAll;
String appVersion; String appVersion;
String deviceName; String deviceName;
SharedPreferences _sharedPreferences; final SharedPreferences _sharedPreferences;
final BackgroundTasks _backgroundTasks;
ObservableMap<WalletType, Node> nodes; ObservableMap<WalletType, Node> nodes;
@ -455,6 +482,7 @@ abstract class SettingsStoreBase with Store {
BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance, BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance,
ThemeBase? initialTheme}) async { ThemeBase? initialTheme}) async {
final sharedPreferences = await getIt.getAsync<SharedPreferences>(); final sharedPreferences = await getIt.getAsync<SharedPreferences>();
final backgroundTasks = getIt.get<BackgroundTasks>();
final currentFiatCurrency = FiatCurrency.deserialize( final currentFiatCurrency = FiatCurrency.deserialize(
raw: sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!); raw: sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!);
@ -597,6 +625,11 @@ abstract class SettingsStoreBase with Store {
nodes[WalletType.ethereum] = ethereumNode; nodes[WalletType.ethereum] = ethereumNode;
} }
final savedSyncMode = SyncMode.all.firstWhere((element) {
return element.type.index == (sharedPreferences.getInt(PreferencesKey.syncModeKey) ?? 1);
});
final savedSyncAll = sharedPreferences.getBool(PreferencesKey.syncAllKey) ?? true;
return SettingsStore( return SettingsStore(
sharedPreferences: sharedPreferences, sharedPreferences: sharedPreferences,
initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard, initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard,
@ -641,6 +674,9 @@ abstract class SettingsStoreBase with Store {
initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings: initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings:
shouldRequireTOTP2FAForAllSecurityAndBackupSettings, shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
initialEthereumTransactionPriority: ethereumTransactionPriority, initialEthereumTransactionPriority: ethereumTransactionPriority,
backgroundTasks: backgroundTasks,
initialSyncMode: savedSyncMode,
initialSyncAll: savedSyncAll,
shouldShowYatPopup: shouldShowYatPopup); shouldShowYatPopup: shouldShowYatPopup);
} }

View file

@ -1,6 +1,7 @@
import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart';
import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
import 'package:cake_wallet/view_model/settings/sync_mode.dart';
import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:cw_core/transaction_history.dart'; import 'package:cw_core/transaction_history.dart';
import 'package:cw_core/balance.dart'; import 'package:cw_core/balance.dart';
@ -403,4 +404,16 @@ abstract class DashboardViewModelBase with Store {
hasBuyAction = !isHaven; hasBuyAction = !isHaven;
hasSellAction = !isHaven; hasSellAction = !isHaven;
} }
@computed
SyncMode get syncMode => settingsStore.currentSyncMode;
@action
void setSyncMode(SyncMode syncMode) => settingsStore.currentSyncMode = syncMode;
@computed
bool get syncAll => settingsStore.currentSyncAll;
@action
void setSyncAll(bool value) => settingsStore.currentSyncAll = value;
} }

View file

@ -1,20 +1,19 @@
import 'package:flutter/foundation.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/material.dart';
class ChoicesListItem<ItemType> extends SettingsListItem { class ChoicesListItem<ItemType> extends SettingsListItem {
ChoicesListItem( ChoicesListItem(
{required String title, {required String title,
required this.selectedItem, required this.selectedItem,
required this.items, required this.items,
this.displayItem, String Function(ItemType item)? displayItem,
void Function(ItemType item)? onItemSelected}) void Function(ItemType item)? onItemSelected})
: _onItemSelected = onItemSelected, : _onItemSelected = onItemSelected,
_displayItem = displayItem,
super(title); super(title);
final ItemType selectedItem; final ItemType selectedItem;
final List<ItemType> items; final List<ItemType> items;
final String Function(ItemType item)? displayItem; final String Function(ItemType item)? _displayItem;
final void Function(ItemType item)? _onItemSelected; final void Function(ItemType item)? _onItemSelected;
void onItemSelected(dynamic item) { void onItemSelected(dynamic item) {
@ -22,4 +21,11 @@ class ChoicesListItem<ItemType> extends SettingsListItem {
_onItemSelected?.call(item); _onItemSelected?.call(item);
} }
} }
String displayItem(dynamic item) {
if (item is ItemType && _displayItem != null) {
return _displayItem!.call(item);
}
return item.toString();
}
} }

View file

@ -0,0 +1,15 @@
enum SyncType { disabled, unobtrusive, aggressive }
class SyncMode {
SyncMode(this.name, this.type, this.frequency);
final String name;
final SyncType type;
final Duration frequency;
static final all = [
SyncMode("Disabled", SyncType.disabled, Duration.zero),
SyncMode("Unobtrusive", SyncType.unobtrusive, Duration(days: 1)),
SyncMode("Aggressive", SyncType.aggressive, Duration(hours: 6)),
];
}

View file

@ -1,4 +1,6 @@
import 'package:cake_wallet/core/wallet_creation_service.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/background_tasks.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
@ -71,6 +73,7 @@ abstract class WalletCreationVMBase with Store {
walletInfo.address = wallet.walletAddresses.address; walletInfo.address = wallet.walletAddresses.address;
await _walletInfoSource.add(walletInfo); await _walletInfoSource.add(walletInfo);
_appStore.changeCurrentWallet(wallet); _appStore.changeCurrentWallet(wallet);
getIt.get<BackgroundTasks>().registerSyncTask();
_appStore.authenticationStore.allowed(); _appStore.authenticationStore.allowed();
state = ExecutedSuccessfullyState(); state = ExecutedSuccessfullyState();
} catch (e) { } catch (e) {

View file

@ -1,6 +1,5 @@
import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/wallet_loading_service.dart'; import 'package:cake_wallet/core/wallet_loading_service.dart';
import 'package:cw_core/wallet_base.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/store/app_store.dart';
@ -58,8 +57,8 @@ abstract class WalletListViewModelBase with Store {
name: info.name, name: info.name,
type: info.type, type: info.type,
key: info.key, key: info.key,
isCurrent: info.name == _appStore.wallet!.name && isCurrent: info.name == _appStore.wallet?.name &&
info.type == _appStore.wallet!.type, info.type == _appStore.wallet?.type,
isEnabled: availableWalletTypes.contains(info.type), isEnabled: availableWalletTypes.contains(info.type),
), ),
), ),

View file

@ -66,6 +66,7 @@ dependencies:
# check unorm_dart for usage and for replace # check unorm_dart for usage and for replace
permission_handler: ^10.0.0 permission_handler: ^10.0.0
device_display_brightness: ^0.0.6 device_display_brightness: ^0.0.6
workmanager: ^0.5.1
platform_device_id: ^1.0.1 platform_device_id: ^1.0.1
wakelock: ^0.6.2 wakelock: ^0.6.2
flutter_mailer: ^2.0.2 flutter_mailer: ^2.0.2

View file

@ -667,5 +667,6 @@
"share": "يشارك", "share": "يشارك",
"slidable": "قابل للانزلاق", "slidable": "قابل للانزلاق",
"etherscan_history": "Etherscan تاريخ", "etherscan_history": "Etherscan تاريخ",
"manage_nodes": "ﺪﻘﻌﻟﺍ ﺓﺭﺍﺩﺇ",
"template_name": "اسم القالب" "template_name": "اسم القالب"
} }

View file

@ -663,5 +663,6 @@
"share": "Дял", "share": "Дял",
"slidable": "Плъзгащ се", "slidable": "Плъзгащ се",
"etherscan_history": "История на Etherscan", "etherscan_history": "История на Etherscan",
"manage_nodes": "Управление на възли",
"template_name": "Име на шаблон" "template_name": "Име на шаблон"
} }

View file

@ -662,6 +662,7 @@
"balance_page": "Stránka zůstatku", "balance_page": "Stránka zůstatku",
"share": "Podíl", "share": "Podíl",
"slidable": "Posuvné", "slidable": "Posuvné",
"manage_nodes": "Spravovat uzly",
"etherscan_history": "Historie Etherscanu", "etherscan_history": "Historie Etherscanu",
"template_name": "Název šablony" "template_name": "Název šablony"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "Geschenkkarte öffnen", "open_gift_card": "Geschenkkarte öffnen",
"contact_support": "Support kontaktieren", "contact_support": "Support kontaktieren",
"gift_cards_unavailable": "Geschenkkarten können derzeit nur über Monero, Bitcoin und Litecoin erworben werden", "gift_cards_unavailable": "Geschenkkarten können derzeit nur über Monero, Bitcoin und Litecoin erworben werden",
"background_sync_mode": "Hintergrundsynchronisierungsmodus",
"sync_all_wallets": "Alle Wallets synchronisieren",
"introducing_cake_pay": "Einführung von Cake Pay!", "introducing_cake_pay": "Einführung von Cake Pay!",
"cake_pay_learn_more": "Kaufen und lösen Sie Geschenkkarten sofort in der App ein!\nWischen Sie von links nach rechts, um mehr zu erfahren.", "cake_pay_learn_more": "Kaufen und lösen Sie Geschenkkarten sofort in der App ein!\nWischen Sie von links nach rechts, um mehr zu erfahren.",
"automatic": "Automatisch", "automatic": "Automatisch",
@ -668,6 +670,7 @@
"balance_page": "Balance-Seite", "balance_page": "Balance-Seite",
"share": "Aktie", "share": "Aktie",
"slidable": "Verschiebbar", "slidable": "Verschiebbar",
"manage_nodes": "Knoten verwalten",
"etherscan_history": "Etherscan-Geschichte", "etherscan_history": "Etherscan-Geschichte",
"template_name": "Vorlagenname" "template_name": "Vorlagenname"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "Open Gift Card", "open_gift_card": "Open Gift Card",
"contact_support": "Contact Support", "contact_support": "Contact Support",
"gift_cards_unavailable": "Gift cards are available for purchase only with Monero, Bitcoin, and Litecoin at this time", "gift_cards_unavailable": "Gift cards are available for purchase only with Monero, Bitcoin, and Litecoin at this time",
"background_sync_mode": "Background sync mode",
"sync_all_wallets": "Sync all wallets",
"introducing_cake_pay": "Introducing Cake Pay!", "introducing_cake_pay": "Introducing Cake Pay!",
"cake_pay_learn_more": "Instantly purchase and redeem gift cards in the app!\nSwipe left to right to learn more.", "cake_pay_learn_more": "Instantly purchase and redeem gift cards in the app!\nSwipe left to right to learn more.",
"automatic": "Automatic", "automatic": "Automatic",
@ -668,6 +670,7 @@
"balance_page": "Balance Page", "balance_page": "Balance Page",
"share": "Share", "share": "Share",
"slidable": "Slidable", "slidable": "Slidable",
"manage_nodes": "Manage nodes",
"etherscan_history": "Etherscan history", "etherscan_history": "Etherscan history",
"template_name": "Template Name" "template_name": "Template Name"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "Abrir tarjeta de regalo", "open_gift_card": "Abrir tarjeta de regalo",
"contact_support": "Contactar con Soporte", "contact_support": "Contactar con Soporte",
"gift_cards_unavailable": "Las tarjetas de regalo están disponibles para comprar solo a través de Monero, Bitcoin y Litecoin en este momento", "gift_cards_unavailable": "Las tarjetas de regalo están disponibles para comprar solo a través de Monero, Bitcoin y Litecoin en este momento",
"background_sync_mode": "Modo de sincronización en segundo plano",
"sync_all_wallets": "Sincronizar todas las billeteras",
"introducing_cake_pay": "¡Presentamos Cake Pay!", "introducing_cake_pay": "¡Presentamos Cake Pay!",
"cake_pay_learn_more": "¡Compre y canjee tarjetas de regalo al instante en la aplicación!\nDeslice el dedo de izquierda a derecha para obtener más información.", "cake_pay_learn_more": "¡Compre y canjee tarjetas de regalo al instante en la aplicación!\nDeslice el dedo de izquierda a derecha para obtener más información.",
"automatic": "Automático", "automatic": "Automático",
@ -668,6 +670,7 @@
"balance_page": "Página de saldo", "balance_page": "Página de saldo",
"share": "Compartir", "share": "Compartir",
"slidable": "deslizable", "slidable": "deslizable",
"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"
} }

View file

@ -536,8 +536,10 @@
"gift_card_is_generated": "La carte-cadeau est générée", "gift_card_is_generated": "La carte-cadeau est générée",
"open_gift_card": "Ouvrir la carte-cadeau", "open_gift_card": "Ouvrir la carte-cadeau",
"contact_support": "Contacter l'assistance", "contact_support": "Contacter l'assistance",
"gift_cards": "Cartes-Cadeaux",
"gift_cards_unavailable": "Les cartes-cadeaux ne sont disponibles à l'achat que via Monero, Bitcoin et Litecoin pour le moment", "gift_cards_unavailable": "Les cartes-cadeaux ne sont disponibles à l'achat que via Monero, Bitcoin et Litecoin pour le moment",
"background_sync_mode": "Mode de synchronisation en arrière-plan",
"sync_all_wallets": "Synchroniser tous les portefeuilles",
"gift_cards": "Cartes-Cadeaux",
"introducing_cake_pay": "Présentation de Cake Pay !", "introducing_cake_pay": "Présentation de Cake Pay !",
"cake_pay_learn_more": "Achetez et utilisez instantanément des cartes-cadeaux dans l'application !\nBalayer de gauche à droite pour en savoir plus.", "cake_pay_learn_more": "Achetez et utilisez instantanément des cartes-cadeaux dans l'application !\nBalayer de gauche à droite pour en savoir plus.",
"automatic": "Automatique", "automatic": "Automatique",
@ -668,6 +670,7 @@
"balance_page": "Page Solde", "balance_page": "Page Solde",
"share": "Partager", "share": "Partager",
"slidable": "Glissable", "slidable": "Glissable",
"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"
} }

View file

@ -649,5 +649,6 @@
"share": "Raba", "share": "Raba",
"slidable": "Mai iya zamewa", "slidable": "Mai iya zamewa",
"etherscan_history": "Etherscan tarihin kowane zamani", "etherscan_history": "Etherscan tarihin kowane zamani",
"manage_nodes": "Sarrafa nodes",
"template_name": "Sunan Samfura" "template_name": "Sunan Samfura"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "गिफ्ट कार्ड खोलें", "open_gift_card": "गिफ्ट कार्ड खोलें",
"contact_support": "सहायता से संपर्क करें", "contact_support": "सहायता से संपर्क करें",
"gift_cards_unavailable": "उपहार कार्ड इस समय केवल मोनेरो, बिटकॉइन और लिटकोइन के माध्यम से खरीदने के लिए उपलब्ध हैं", "gift_cards_unavailable": "उपहार कार्ड इस समय केवल मोनेरो, बिटकॉइन और लिटकोइन के माध्यम से खरीदने के लिए उपलब्ध हैं",
"background_sync_mode": "बैकग्राउंड सिंक मोड",
"sync_all_wallets": "सभी वॉलेट सिंक करें",
"introducing_cake_pay": "परिचय Cake Pay!", "introducing_cake_pay": "परिचय Cake Pay!",
"cake_pay_learn_more": "ऐप में उपहार कार्ड तुरंत खरीदें और रिडीम करें!\nअधिक जानने के लिए बाएं से दाएं स्वाइप करें।", "cake_pay_learn_more": "ऐप में उपहार कार्ड तुरंत खरीदें और रिडीम करें!\nअधिक जानने के लिए बाएं से दाएं स्वाइप करें।",
"automatic": "स्वचालित", "automatic": "स्वचालित",
@ -668,6 +670,7 @@
"balance_page": "बैलेंस पेज", "balance_page": "बैलेंस पेज",
"share": "शेयर करना", "share": "शेयर करना",
"slidable": "फिसलने लायक", "slidable": "फिसलने लायक",
"manage_nodes": "नोड्स प्रबंधित करें",
"etherscan_history": "इथरस्कैन इतिहास", "etherscan_history": "इथरस्कैन इतिहास",
"template_name": "टेम्पलेट नाम" "template_name": "टेम्पलेट नाम"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "Otvori darovnu karticu", "open_gift_card": "Otvori darovnu karticu",
"contact_support": "Kontaktirajte podršku", "contact_support": "Kontaktirajte podršku",
"gift_cards_unavailable": "Poklon kartice trenutno su dostupne za kupnju samo putem Monera, Bitcoina i Litecoina", "gift_cards_unavailable": "Poklon kartice trenutno su dostupne za kupnju samo putem Monera, Bitcoina i Litecoina",
"background_sync_mode": "Sinkronizacija u pozadini",
"sync_all_wallets": "Sinkronizirajte sve novčanike",
"introducing_cake_pay": "Predstavljamo Cake Pay!", "introducing_cake_pay": "Predstavljamo Cake Pay!",
"cake_pay_learn_more": "Azonnal vásárolhat és válthat be ajándékutalványokat az alkalmazásban!\nTovábbi információért csúsztassa balról jobbra az ujját.", "cake_pay_learn_more": "Azonnal vásárolhat és válthat be ajándékutalványokat az alkalmazásban!\nTovábbi információért csúsztassa balról jobbra az ujját.",
"automatic": "Automatski", "automatic": "Automatski",
@ -668,6 +670,7 @@
"balance_page": "Stranica sa stanjem", "balance_page": "Stranica sa stanjem",
"share": "Udio", "share": "Udio",
"slidable": "Klizna", "slidable": "Klizna",
"manage_nodes": "Upravljanje čvorovima",
"etherscan_history": "Etherscan povijest", "etherscan_history": "Etherscan povijest",
"template_name": "Naziv predloška" "template_name": "Naziv predloška"
} }

View file

@ -658,6 +658,7 @@
"balance_page": "Halaman Saldo", "balance_page": "Halaman Saldo",
"share": "Membagikan", "share": "Membagikan",
"slidable": "Dapat digeser", "slidable": "Dapat digeser",
"manage_nodes": "Kelola node",
"etherscan_history": "Sejarah Etherscan", "etherscan_history": "Sejarah Etherscan",
"template_name": "Nama Templat" "template_name": "Nama Templat"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "Apri carta regalo", "open_gift_card": "Apri carta regalo",
"contact_support": "Contatta l'assistenza", "contact_support": "Contatta l'assistenza",
"gift_cards_unavailable": "Le carte regalo sono disponibili per l'acquisto solo tramite Monero, Bitcoin e Litecoin in questo momento", "gift_cards_unavailable": "Le carte regalo sono disponibili per l'acquisto solo tramite Monero, Bitcoin e Litecoin in questo momento",
"background_sync_mode": "Modalità di sincronizzazione in background",
"sync_all_wallets": "Sincronizza tutti i portafogli",
"introducing_cake_pay": "Presentazione di Cake Pay!", "introducing_cake_pay": "Presentazione di Cake Pay!",
"cake_pay_learn_more": "Acquista e riscatta istantaneamente carte regalo nell'app!\nScorri da sinistra a destra per saperne di più.", "cake_pay_learn_more": "Acquista e riscatta istantaneamente carte regalo nell'app!\nScorri da sinistra a destra per saperne di più.",
"automatic": "Automatico", "automatic": "Automatico",
@ -668,6 +670,7 @@
"balance_page": "Pagina di equilibrio", "balance_page": "Pagina di equilibrio",
"share": "Condividere", "share": "Condividere",
"slidable": "Scorrevole", "slidable": "Scorrevole",
"manage_nodes": "Gestisci i nodi",
"etherscan_history": "Storia Etherscan", "etherscan_history": "Storia Etherscan",
"template_name": "Nome modello" "template_name": "Nome modello"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "オープンギフトカード", "open_gift_card": "オープンギフトカード",
"contact_support": "サポートに連絡する", "contact_support": "サポートに連絡する",
"gift_cards_unavailable": "現時点では、ギフトカードはMonero、Bitcoin、Litecoinからのみ購入できます。", "gift_cards_unavailable": "現時点では、ギフトカードはMonero、Bitcoin、Litecoinからのみ購入できます。",
"background_sync_mode": "バックグラウンド同期モード",
"sync_all_wallets": "すべてのウォレットを同期",
"introducing_cake_pay": "序章Cake Pay", "introducing_cake_pay": "序章Cake Pay",
"cake_pay_learn_more": "アプリですぐにギフトカードを購入して引き換えましょう!\n左から右にスワイプして詳細をご覧ください。", "cake_pay_learn_more": "アプリですぐにギフトカードを購入して引き換えましょう!\n左から右にスワイプして詳細をご覧ください。",
"automatic": "自動", "automatic": "自動",
@ -668,6 +670,7 @@
"balance_page": "残高ページ", "balance_page": "残高ページ",
"share": "共有", "share": "共有",
"slidable": "スライド可能", "slidable": "スライド可能",
"manage_nodes": "ノードの管理",
"etherscan_history": "イーサスキャンの歴史", "etherscan_history": "イーサスキャンの歴史",
"template_name": "テンプレート名" "template_name": "テンプレート名"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "기프트 카드 열기", "open_gift_card": "기프트 카드 열기",
"contact_support": "지원팀에 문의", "contact_support": "지원팀에 문의",
"gift_cards_unavailable": "기프트 카드는 현재 Monero, Bitcoin 및 Litecoin을 통해서만 구매할 수 있습니다.", "gift_cards_unavailable": "기프트 카드는 현재 Monero, Bitcoin 및 Litecoin을 통해서만 구매할 수 있습니다.",
"background_sync_mode": "백그라운드 동기화 모드",
"sync_all_wallets": "모든 지갑 동기화",
"introducing_cake_pay": "소개 Cake Pay!", "introducing_cake_pay": "소개 Cake Pay!",
"cake_pay_learn_more": "앱에서 즉시 기프트 카드를 구매하고 사용하세요!\n자세히 알아보려면 왼쪽에서 오른쪽으로 스와이프하세요.", "cake_pay_learn_more": "앱에서 즉시 기프트 카드를 구매하고 사용하세요!\n자세히 알아보려면 왼쪽에서 오른쪽으로 스와이프하세요.",
"automatic": "자동적 인", "automatic": "자동적 인",
@ -668,6 +670,7 @@
"balance_page": "잔액 페이지", "balance_page": "잔액 페이지",
"share": "공유하다", "share": "공유하다",
"slidable": "슬라이딩 가능", "slidable": "슬라이딩 가능",
"manage_nodes": "노드 관리",
"etherscan_history": "이더스캔 역사", "etherscan_history": "이더스캔 역사",
"template_name": "템플릿 이름" "template_name": "템플릿 이름"
} }

View file

@ -668,6 +668,7 @@
"balance_page": "လက်ကျန်စာမျက်နှာ", "balance_page": "လက်ကျန်စာမျက်နှာ",
"share": "မျှဝေပါ။", "share": "မျှဝေပါ။",
"slidable": "လျှောချနိုင်သည်။", "slidable": "လျှောချနိုင်သည်။",
"manage_nodes": "ဆုံမှတ်များကို စီမံပါ။",
"etherscan_history": "Etherscan သမိုင်း", "etherscan_history": "Etherscan သမိုင်း",
"template_name": "နမူနာပုံစံ" "template_name": "နမူနာပုံစံ"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "Geschenkkaart openen", "open_gift_card": "Geschenkkaart openen",
"contact_support": "Contact opnemen met ondersteuning", "contact_support": "Contact opnemen met ondersteuning",
"gift_cards_unavailable": "Cadeaubonnen kunnen momenteel alleen worden gekocht via Monero, Bitcoin en Litecoin", "gift_cards_unavailable": "Cadeaubonnen kunnen momenteel alleen worden gekocht via Monero, Bitcoin en Litecoin",
"background_sync_mode": "Achtergrondsynchronisatiemodus",
"sync_all_wallets": "Alle portemonnees synchroniseren",
"introducing_cake_pay": "Introductie van Cake Pay!", "introducing_cake_pay": "Introductie van Cake Pay!",
"cake_pay_learn_more": "Koop en wissel cadeaubonnen direct in de app in!\nSwipe van links naar rechts voor meer informatie.", "cake_pay_learn_more": "Koop en wissel cadeaubonnen direct in de app in!\nSwipe van links naar rechts voor meer informatie.",
"automatic": "automatisch", "automatic": "automatisch",
@ -668,6 +670,7 @@
"balance_page": "Saldo pagina", "balance_page": "Saldo pagina",
"share": "Deel", "share": "Deel",
"slidable": "Verschuifbaar", "slidable": "Verschuifbaar",
"manage_nodes": "Beheer knooppunten",
"etherscan_history": "Etherscan-geschiedenis", "etherscan_history": "Etherscan-geschiedenis",
"template_name": "Sjabloonnaam" "template_name": "Sjabloonnaam"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "Otwórz kartę podarunkową", "open_gift_card": "Otwórz kartę podarunkową",
"contact_support": "Skontaktuj się z pomocą techniczną", "contact_support": "Skontaktuj się z pomocą techniczną",
"gift_cards_unavailable": "Karty podarunkowe można obecnie kupić tylko za pośrednictwem Monero, Bitcoin i Litecoin", "gift_cards_unavailable": "Karty podarunkowe można obecnie kupić tylko za pośrednictwem Monero, Bitcoin i Litecoin",
"background_sync_mode": "Tryb synchronizacji w tle",
"sync_all_wallets": "Synchronizuj wszystkie portfele",
"introducing_cake_pay": "Przedstawiamy Cake Pay!", "introducing_cake_pay": "Przedstawiamy Cake Pay!",
"cake_pay_learn_more": "Kupuj i wykorzystuj karty podarunkowe od razu w aplikacji!\nPrzesuń od lewej do prawej, aby dowiedzieć się więcej.", "cake_pay_learn_more": "Kupuj i wykorzystuj karty podarunkowe od razu w aplikacji!\nPrzesuń od lewej do prawej, aby dowiedzieć się więcej.",
"automatic": "Automatyczny", "automatic": "Automatyczny",
@ -668,6 +670,7 @@
"balance_page": "Strona salda", "balance_page": "Strona salda",
"share": "Udział", "share": "Udział",
"slidable": "Przesuwne", "slidable": "Przesuwne",
"manage_nodes": "Zarządzaj węzłami",
"etherscan_history": "Historia Etherscanu", "etherscan_history": "Historia Etherscanu",
"template_name": "Nazwa szablonu" "template_name": "Nazwa szablonu"
} }

View file

@ -537,6 +537,8 @@
"open_gift_card": "Abrir vale-presente", "open_gift_card": "Abrir vale-presente",
"contact_support": "Contatar Suporte", "contact_support": "Contatar Suporte",
"gift_cards_unavailable": "Os cartões-presente estão disponíveis para compra apenas através do Monero, Bitcoin e Litecoin no momento", "gift_cards_unavailable": "Os cartões-presente estão disponíveis para compra apenas através do Monero, Bitcoin e Litecoin no momento",
"background_sync_mode": "Modo de sincronização em segundo plano",
"sync_all_wallets": "Sincronize todas as carteiras",
"introducing_cake_pay": "Apresentando o Cake Pay!", "introducing_cake_pay": "Apresentando o Cake Pay!",
"cake_pay_learn_more": "Compre e resgate vales-presente instantaneamente no app!\nDeslize da esquerda para a direita para saber mais.", "cake_pay_learn_more": "Compre e resgate vales-presente instantaneamente no app!\nDeslize da esquerda para a direita para saber mais.",
"automatic": "Automático", "automatic": "Automático",
@ -667,6 +669,7 @@
"balance_page": "Página de saldo", "balance_page": "Página de saldo",
"share": "Compartilhar", "share": "Compartilhar",
"slidable": "Deslizável", "slidable": "Deslizável",
"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"
} }

View file

@ -539,6 +539,8 @@
"open_gift_card": "Открыть подарочную карту", "open_gift_card": "Открыть подарочную карту",
"contact_support": "Связаться со службой поддержки", "contact_support": "Связаться со службой поддержки",
"gift_cards_unavailable": "В настоящее время подарочные карты можно приобрести только через Monero, Bitcoin и Litecoin.", "gift_cards_unavailable": "В настоящее время подарочные карты можно приобрести только через Monero, Bitcoin и Litecoin.",
"background_sync_mode": "Режим фоновой синхронизации",
"sync_all_wallets": "Синхронизировать все кошельки",
"introducing_cake_pay": "Представляем Cake Pay!", "introducing_cake_pay": "Представляем Cake Pay!",
"cake_pay_learn_more": "Мгновенно покупайте и используйте подарочные карты в приложении!\nПроведите по экрану слева направо, чтобы узнать больше.", "cake_pay_learn_more": "Мгновенно покупайте и используйте подарочные карты в приложении!\nПроведите по экрану слева направо, чтобы узнать больше.",
"automatic": "автоматический", "automatic": "автоматический",
@ -669,6 +671,7 @@
"balance_page": "Страница баланса", "balance_page": "Страница баланса",
"share": "Делиться", "share": "Делиться",
"slidable": "Скользящий", "slidable": "Скользящий",
"manage_nodes": "Управление узлами",
"etherscan_history": "История Эфириума", "etherscan_history": "История Эфириума",
"template_name": "Имя Шаблона" "template_name": "Имя Шаблона"
} }

View file

@ -668,6 +668,7 @@
"balance_page": "หน้ายอดคงเหลือ", "balance_page": "หน้ายอดคงเหลือ",
"share": "แบ่งปัน", "share": "แบ่งปัน",
"slidable": "เลื่อนได้", "slidable": "เลื่อนได้",
"manage_nodes": "จัดการโหนด",
"etherscan_history": "ประวัติอีเธอร์สแกน", "etherscan_history": "ประวัติอีเธอร์สแกน",
"template_name": "ชื่อแม่แบบ" "template_name": "ชื่อแม่แบบ"
} }

View file

@ -631,7 +631,7 @@
"setup_2fa_text": "Cake 2FA, soğuk hava deposu kadar güvenli DEĞİLDİR. 2FA, siz uyurken arkadaşınızın parmak izinizi sağlaması gibi temel saldırı türlerine karşı koruma sağlar.\n\n Cake 2FA, gelişmiş bir saldırgan tarafından güvenliği ihlal edilmiş bir cihaza karşı koruma SAĞLAMAZ.\n\n 2FA kodlarınıza erişimi kaybederseniz , BU CÜZDANA ERİŞİMİNİZİ KAYBEDECEKSİNİZ. Mnemonic seed'den cüzdanınızı geri yüklemeniz gerekecek. BU NEDENLE HATIRLAYICI TOHUMLARINIZI YEDEKLEMELİSİNİZ! Ayrıca anımsatıcı tohumlarınıza erişimi olan biri, Cake 2FA'yı atlayarak paranızı çalabilir.\n\n Cake, anımsatıcı tohumlarınıza erişimi kaybederseniz size yardımcı olamaz, çünkü Cake bir saklama dışı cüzdan.", "setup_2fa_text": "Cake 2FA, soğuk hava deposu kadar güvenli DEĞİLDİR. 2FA, siz uyurken arkadaşınızın parmak izinizi sağlaması gibi temel saldırı türlerine karşı koruma sağlar.\n\n Cake 2FA, gelişmiş bir saldırgan tarafından güvenliği ihlal edilmiş bir cihaza karşı koruma SAĞLAMAZ.\n\n 2FA kodlarınıza erişimi kaybederseniz , BU CÜZDANA ERİŞİMİNİZİ KAYBEDECEKSİNİZ. Mnemonic seed'den cüzdanınızı geri yüklemeniz gerekecek. BU NEDENLE HATIRLAYICI TOHUMLARINIZI YEDEKLEMELİSİNİZ! Ayrıca anımsatıcı tohumlarınıza erişimi olan biri, Cake 2FA'yı atlayarak paranızı çalabilir.\n\n Cake, anımsatıcı tohumlarınıza erişimi kaybederseniz size yardımcı olamaz, çünkü Cake bir saklama dışı cüzdan.",
"setup_totp_recommended": "TOTP'yi kurun (Önerilir)", "setup_totp_recommended": "TOTP'yi kurun (Önerilir)",
"disable_buy": "Satın alma işlemini devre dışı bırak", "disable_buy": "Satın alma işlemini devre dışı bırak",
"disable_sell": "Satış işlemini devre dışı bırak", "disable_sell": "Satış işlemini devre dışı bırak",
"cake_2fa_preset" : "Kek 2FA Ön Ayarı", "cake_2fa_preset" : "Kek 2FA Ön Ayarı",
"narrow": "Dar", "narrow": "Dar",
"normal": "Normal", "normal": "Normal",
@ -669,6 +669,7 @@
"balance_page": "Bakiye Sayfası", "balance_page": "Bakiye Sayfası",
"share": "Paylaşmak", "share": "Paylaşmak",
"slidable": "kaydırılabilir", "slidable": "kaydırılabilir",
"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ı"
} }

View file

@ -538,6 +538,8 @@
"open_gift_card": "Відкрити подарункову картку", "open_gift_card": "Відкрити подарункову картку",
"contact_support": "Звернутися до служби підтримки", "contact_support": "Звернутися до служби підтримки",
"gift_cards_unavailable": "Наразі подарункові картки можна придбати лише через Monero, Bitcoin і Litecoin", "gift_cards_unavailable": "Наразі подарункові картки можна придбати лише через Monero, Bitcoin і Litecoin",
"background_sync_mode": "Фоновий режим синхронізації",
"sync_all_wallets": "Синхронізувати всі гаманці",
"introducing_cake_pay": "Представляємо Cake Pay!", "introducing_cake_pay": "Представляємо Cake Pay!",
"cake_pay_learn_more": "Миттєво купуйте та активуйте подарункові картки в додатку!\nПроведіть пальцем зліва направо, щоб дізнатися більше.", "cake_pay_learn_more": "Миттєво купуйте та активуйте подарункові картки в додатку!\nПроведіть пальцем зліва направо, щоб дізнатися більше.",
"automatic": "Автоматичний", "automatic": "Автоматичний",
@ -668,6 +670,7 @@
"balance_page": "Сторінка балансу", "balance_page": "Сторінка балансу",
"share": "Поділіться", "share": "Поділіться",
"slidable": "Розсувний", "slidable": "Розсувний",
"manage_nodes": "Керуйте вузлами",
"etherscan_history": "Історія Etherscan", "etherscan_history": "Історія Etherscan",
"template_name": "Назва шаблону" "template_name": "Назва шаблону"
} }

View file

@ -662,6 +662,7 @@
"balance_page": "بیلنس صفحہ", "balance_page": "بیلنس صفحہ",
"share": "بانٹیں", "share": "بانٹیں",
"slidable": "سلائیڈ ایبل", "slidable": "سلائیڈ ایبل",
"manage_nodes": "۔ﮟﯾﺮﮐ ﻢﻈﻧ ﺎﮐ ﺱﮈﻮﻧ",
"etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ", "etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ",
"template_name": "ٹیمپلیٹ کا نام" "template_name": "ٹیمپلیٹ کا نام"
} }

View file

@ -664,6 +664,7 @@
"balance_page": "Oju-iwe iwọntunwọnsi", "balance_page": "Oju-iwe iwọntunwọnsi",
"share": "Pinpin", "share": "Pinpin",
"slidable": "Slidable", "slidable": "Slidable",
"manage_nodes": "Ṣakoso awọn apa",
"etherscan_history": "Etherscan itan", "etherscan_history": "Etherscan itan",
"template_name": "Orukọ Awoṣe" "template_name": "Orukọ Awoṣe"
} }

View file

@ -537,6 +537,8 @@
"open_gift_card": "打开礼品卡", "open_gift_card": "打开礼品卡",
"contact_support": "联系支持", "contact_support": "联系支持",
"gift_cards_unavailable": "目前只能通过门罗币、比特币和莱特币购买礼品卡", "gift_cards_unavailable": "目前只能通过门罗币、比特币和莱特币购买礼品卡",
"background_sync_mode": "后台同步模式",
"sync_all_wallets": "同步所有钱包",
"introducing_cake_pay": "介绍 Cake Pay!", "introducing_cake_pay": "介绍 Cake Pay!",
"cake_pay_learn_more": "立即在应用中购买和兑换礼品卡!\n从左向右滑动以了解详情。", "cake_pay_learn_more": "立即在应用中购买和兑换礼品卡!\n从左向右滑动以了解详情。",
"automatic": "自动的", "automatic": "自动的",
@ -667,6 +669,7 @@
"balance_page": "余额页", "balance_page": "余额页",
"share": "分享", "share": "分享",
"slidable": "可滑动", "slidable": "可滑动",
"manage_nodes": "管理节点",
"etherscan_history": "以太扫描历史", "etherscan_history": "以太扫描历史",
"template_name": "模板名称" "template_name": "模板名称"
} }