mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 19:49:22 +00:00
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:
parent
68a057b91b
commit
aedf310c9d
48 changed files with 593 additions and 190 deletions
|
@ -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) {
|
||||||
|
try {
|
||||||
|
final utils = const MethodChannel('com.cake_wallet/native_utils');
|
||||||
|
|
||||||
utils.invokeMethod<Uint8List>('setIsAppSecure', {'isAppSecure': isAppSecure});
|
utils.invokeMethod<Uint8List>('setIsAppSecure', {'isAppSecure': isAppSecure});
|
||||||
|
} catch (_) {}
|
||||||
}
|
}
|
|
@ -606,4 +606,9 @@
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||||
|
SystemCapabilities = {
|
||||||
|
com.apple.BackgroundModes = {
|
||||||
|
enabled = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
164
lib/entities/background_tasks.dart
Normal file
164
lib/entities/background_tasks.dart
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -57,8 +57,18 @@ Future<void> main() async {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
final appDir = await getApplicationDocumentsDirectory();
|
|
||||||
await Hive.close();
|
await Hive.close();
|
||||||
|
|
||||||
|
await initializeAppConfigs();
|
||||||
|
|
||||||
|
runApp(App());
|
||||||
|
}, (error, stackTrace) async {
|
||||||
|
ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stackTrace));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> initializeAppConfigs() async {
|
||||||
|
final appDir = await getApplicationDocumentsDirectory();
|
||||||
Hive.init(appDir.path);
|
Hive.init(appDir.path);
|
||||||
|
|
||||||
if (!Hive.isAdapterRegistered(Contact.typeId)) {
|
if (!Hive.isAdapterRegistered(Contact.typeId)) {
|
||||||
|
@ -142,10 +152,6 @@ Future<void> main() async {
|
||||||
secureStorage: secureStorage,
|
secureStorage: secureStorage,
|
||||||
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
anonpayInvoiceInfo: anonpayInvoiceInfo,
|
||||||
initialMigrationVersion: 21);
|
initialMigrationVersion: 21);
|
||||||
runApp(App());
|
|
||||||
}, (error, stackTrace) async {
|
|
||||||
ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stackTrace));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initialSetup(
|
Future<void> initialSetup(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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';
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
|
||||||
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();
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
});
|
}),
|
||||||
},
|
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||||
|
Observer(builder: (context) {
|
||||||
|
return SettingsSwitcherCell(
|
||||||
|
title: S.current.sync_all_wallets,
|
||||||
|
value: dashboardViewModel.syncAll,
|
||||||
|
onValueChange: (_, bool value) => dashboardViewModel.setSyncAll(value),
|
||||||
);
|
);
|
||||||
|
}),
|
||||||
return nodeListRow;
|
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||||
},
|
],
|
||||||
),
|
],
|
||||||
);
|
SettingsCellWithArrow(
|
||||||
},
|
title: S.current.manage_nodes,
|
||||||
|
handler: (context) => Navigator.of(context).pushNamed(Routes.manageNodes),
|
||||||
),
|
),
|
||||||
|
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
85
lib/src/screens/settings/manage_nodes_page.dart
Normal file
85
lib/src/screens/settings/manage_nodes_page.dart
Normal 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;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,10 +20,12 @@ class OtherSettingsPage extends BasePage {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget body(BuildContext context) {
|
Widget body(BuildContext context) {
|
||||||
return Observer(builder: (_) {
|
return Observer(
|
||||||
|
builder: (_) {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(top: 10),
|
padding: EdgeInsets.only(top: 10),
|
||||||
child: Column(children: [
|
child: Column(
|
||||||
|
children: [
|
||||||
SettingsPickerCell(
|
SettingsPickerCell(
|
||||||
title: S.current.settings_fee_priority,
|
title: S.current.settings_fee_priority,
|
||||||
items: priorityForWalletType(_otherSettingsViewModel.walletType),
|
items: priorityForWalletType(_otherSettingsViewModel.walletType),
|
||||||
|
@ -33,13 +35,17 @@ class OtherSettingsPage extends BasePage {
|
||||||
),
|
),
|
||||||
SettingsCellWithArrow(
|
SettingsCellWithArrow(
|
||||||
title: S.current.settings_terms_and_conditions,
|
title: S.current.settings_terms_and_conditions,
|
||||||
handler: (BuildContext context) => Navigator.of(context).pushNamed(Routes.readDisclaimer),
|
handler: (BuildContext context) =>
|
||||||
|
Navigator.of(context).pushNamed(Routes.readDisclaimer),
|
||||||
),
|
),
|
||||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
SettingsVersionCell(title: S.of(context).version(_otherSettingsViewModel.currentVersion))
|
SettingsVersionCell(
|
||||||
]),
|
title: S.of(context).version(_otherSettingsViewModel.currentVersion))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
15
lib/view_model/settings/sync_mode.dart
Normal file
15
lib/view_model/settings/sync_mode.dart
Normal 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)),
|
||||||
|
];
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
|
|
@ -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),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -667,5 +667,6 @@
|
||||||
"share": "يشارك",
|
"share": "يشارك",
|
||||||
"slidable": "قابل للانزلاق",
|
"slidable": "قابل للانزلاق",
|
||||||
"etherscan_history": "Etherscan تاريخ",
|
"etherscan_history": "Etherscan تاريخ",
|
||||||
|
"manage_nodes": "ﺪﻘﻌﻟﺍ ﺓﺭﺍﺩﺇ",
|
||||||
"template_name": "اسم القالب"
|
"template_name": "اسم القالب"
|
||||||
}
|
}
|
||||||
|
|
|
@ -663,5 +663,6 @@
|
||||||
"share": "Дял",
|
"share": "Дял",
|
||||||
"slidable": "Плъзгащ се",
|
"slidable": "Плъзгащ се",
|
||||||
"etherscan_history": "История на Etherscan",
|
"etherscan_history": "История на Etherscan",
|
||||||
|
"manage_nodes": "Управление на възли",
|
||||||
"template_name": "Име на шаблон"
|
"template_name": "Име на шаблон"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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": "टेम्पलेट नाम"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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": "テンプレート名"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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": "템플릿 이름"
|
||||||
}
|
}
|
||||||
|
|
|
@ -668,6 +668,7 @@
|
||||||
"balance_page": "လက်ကျန်စာမျက်နှာ",
|
"balance_page": "လက်ကျန်စာမျက်နှာ",
|
||||||
"share": "မျှဝေပါ။",
|
"share": "မျှဝေပါ။",
|
||||||
"slidable": "လျှောချနိုင်သည်။",
|
"slidable": "လျှောချနိုင်သည်။",
|
||||||
|
"manage_nodes": "ဆုံမှတ်များကို စီမံပါ။",
|
||||||
"etherscan_history": "Etherscan သမိုင်း",
|
"etherscan_history": "Etherscan သမိုင်း",
|
||||||
"template_name": "နမူနာပုံစံ"
|
"template_name": "နမူနာပုံစံ"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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": "Имя Шаблона"
|
||||||
}
|
}
|
||||||
|
|
|
@ -668,6 +668,7 @@
|
||||||
"balance_page": "หน้ายอดคงเหลือ",
|
"balance_page": "หน้ายอดคงเหลือ",
|
||||||
"share": "แบ่งปัน",
|
"share": "แบ่งปัน",
|
||||||
"slidable": "เลื่อนได้",
|
"slidable": "เลื่อนได้",
|
||||||
|
"manage_nodes": "จัดการโหนด",
|
||||||
"etherscan_history": "ประวัติอีเธอร์สแกน",
|
"etherscan_history": "ประวัติอีเธอร์สแกน",
|
||||||
"template_name": "ชื่อแม่แบบ"
|
"template_name": "ชื่อแม่แบบ"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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ı"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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": "Назва шаблону"
|
||||||
}
|
}
|
||||||
|
|
|
@ -662,6 +662,7 @@
|
||||||
"balance_page": "بیلنس صفحہ",
|
"balance_page": "بیلنس صفحہ",
|
||||||
"share": "بانٹیں",
|
"share": "بانٹیں",
|
||||||
"slidable": "سلائیڈ ایبل",
|
"slidable": "سلائیڈ ایبل",
|
||||||
|
"manage_nodes": "۔ﮟﯾﺮﮐ ﻢﻈﻧ ﺎﮐ ﺱﮈﻮﻧ",
|
||||||
"etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ",
|
"etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ",
|
||||||
"template_name": "ٹیمپلیٹ کا نام"
|
"template_name": "ٹیمپلیٹ کا نام"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -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": "模板名称"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue