mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 11:39: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';
|
||||
|
||||
const utils = const MethodChannel('com.cake_wallet/native_utils');
|
||||
|
||||
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 (_) {}
|
||||
}
|
||||
|
|
|
@ -606,4 +606,9 @@
|
|||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 97C146E61CF9000F007C117D /* Project object */;
|
||||
SystemCapabilities = {
|
||||
com.apple.BackgroundModes = {
|
||||
enabled = 1;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import UIKit
|
||||
import Flutter
|
||||
import UnstoppableDomainsResolution
|
||||
import workmanager
|
||||
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
|
@ -16,6 +17,15 @@ import UnstoppableDomainsResolution
|
|||
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()
|
||||
|
||||
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">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>com.fotolockr.cakewallet.monero_sync_task</string>
|
||||
</array>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
|
@ -113,6 +117,7 @@
|
|||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>processing</string>
|
||||
<string>remote-notification</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
|
|
|
@ -243,6 +243,8 @@ class BackupService {
|
|||
final sortBalanceTokensBy = data[PreferencesKey.sortBalanceBy] as int?;
|
||||
final pinNativeTokenAtTop = data[PreferencesKey.pinNativeTokenAtTop] 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);
|
||||
|
||||
|
@ -361,6 +363,12 @@ class BackupService {
|
|||
if (useEtherscan != null)
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -510,6 +518,10 @@ class BackupService {
|
|||
_sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop),
|
||||
PreferencesKey.useEtherscan:
|
||||
_sharedPreferences.getBool(PreferencesKey.useEtherscan),
|
||||
PreferencesKey.syncModeKey:
|
||||
_sharedPreferences.getInt(PreferencesKey.syncModeKey),
|
||||
PreferencesKey.syncAllKey:
|
||||
_sharedPreferences.getBool(PreferencesKey.syncAllKey),
|
||||
};
|
||||
|
||||
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/payfura/payfura_buy_provider.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/parse_address_from_domain.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_receive_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/privacy_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.registerFactory(() => BackgroundTasks());
|
||||
|
||||
final isBitcoinBuyEnabled = (secrets.wyreSecretKey.isNotEmpty) &&
|
||||
(secrets.wyreApiKey.isNotEmpty) &&
|
||||
(secrets.wyreAccountId.isNotEmpty);
|
||||
|
@ -681,8 +685,7 @@ Future setup({
|
|||
return NodeListViewModel(_nodeSource, appStore);
|
||||
});
|
||||
|
||||
getIt.registerFactory(
|
||||
() => ConnectionSyncPage(getIt.get<NodeListViewModel>(), getIt.get<DashboardViewModel>()));
|
||||
getIt.registerFactory(() => ConnectionSyncPage(getIt.get<DashboardViewModel>()));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => SecurityBackupPage(getIt.get<SecuritySettingsViewModel>(), getIt.get<AuthService>()));
|
||||
|
@ -1055,5 +1058,7 @@ Future setup({
|
|||
),
|
||||
);
|
||||
|
||||
getIt.registerFactory<ManageNodesPage>(() => ManageNodesPage(getIt.get<NodeListViewModel>()));
|
||||
|
||||
_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:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:cake_wallet/core/key_service.dart';
|
||||
import 'package:cw_core/wallet_service.dart';
|
||||
import 'package:cake_wallet/entities/background_tasks.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:cake_wallet/core/wallet_loading_service.dart';
|
||||
|
@ -24,4 +23,6 @@ Future<void> loadCurrentWallet() async {
|
|||
final walletLoadingService = getIt.get<WalletLoadingService>();
|
||||
final wallet = await walletLoadingService.load(type, name);
|
||||
appStore.changeCurrentWallet(wallet);
|
||||
|
||||
getIt.get<BackgroundTasks>().registerSyncTask();
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ class PreferencesKey {
|
|||
static const shouldShowReceiveWarning = 'should_show_receive_warning';
|
||||
static const shouldShowYatPopup = 'should_show_yat_popup';
|
||||
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 lastAuthTimeMilliseconds = 'last_auth_time_milliseconds';
|
||||
static const lastPopupDate = 'last_popup_date';
|
||||
|
|
172
lib/main.dart
172
lib/main.dart
|
@ -57,97 +57,103 @@ Future<void> main() async {
|
|||
return true;
|
||||
};
|
||||
|
||||
final appDir = await getApplicationDocumentsDirectory();
|
||||
await Hive.close();
|
||||
Hive.init(appDir.path);
|
||||
|
||||
if (!Hive.isAdapterRegistered(Contact.typeId)) {
|
||||
Hive.registerAdapter(ContactAdapter());
|
||||
}
|
||||
await initializeAppConfigs();
|
||||
|
||||
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());
|
||||
}, (error, stackTrace) async {
|
||||
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(
|
||||
{required SharedPreferences sharedPreferences,
|
||||
required Box<Node> nodes,
|
||||
|
@ -319,7 +325,7 @@ class _HomeState extends State<_Home> {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const SizedBox.shrink();
|
||||
|
|
|
@ -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/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/manage_nodes_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/security_backup_page.dart';
|
||||
|
@ -605,6 +606,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
|
|||
),
|
||||
);
|
||||
|
||||
case Routes.manageNodes:
|
||||
return MaterialPageRoute<void>(builder: (_) => getIt.get<ManageNodesPage>());
|
||||
|
||||
default:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (_) => Scaffold(
|
||||
|
|
|
@ -90,4 +90,5 @@ class Routes {
|
|||
static const modify2FAPage = '/modify_2fa_page';
|
||||
static const homeSettings = '/home_settings';
|
||||
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_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/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/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/generated/i18n.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/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 {
|
||||
ConnectionSyncPage(this.nodeListViewModel, this.dashboardViewModel);
|
||||
ConnectionSyncPage(this.dashboardViewModel);
|
||||
|
||||
@override
|
||||
String get title => S.current.connection_sync;
|
||||
|
||||
final NodeListViewModel nodeListViewModel;
|
||||
final DashboardViewModel dashboardViewModel;
|
||||
|
||||
@override
|
||||
|
@ -33,72 +33,39 @@ class ConnectionSyncPage extends BasePage {
|
|||
title: S.current.reconnect,
|
||||
handler: (context) => _presentReconnectAlert(context),
|
||||
),
|
||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
if (dashboardViewModel.hasRescan)
|
||||
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
if (dashboardViewModel.hasRescan) ...[
|
||||
SettingsCellWithArrow(
|
||||
title: S.current.rescan,
|
||||
handler: (context) => Navigator.of(context).pushNamed(Routes.rescan),
|
||||
),
|
||||
StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
Semantics(
|
||||
button: true,
|
||||
child: NodeHeaderListRow(
|
||||
title: S.of(context).add_new_node,
|
||||
onTap: (_) async =>
|
||||
await Navigator.of(context).pushNamed(Routes.newNode),
|
||||
),
|
||||
),
|
||||
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();
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return nodeListRow;
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
const StandardListSeparator(padding: EdgeInsets.symmetric(horizontal: 24)),
|
||||
if (DeviceInfo.instance.isMobile) ...[
|
||||
Observer(builder: (context) {
|
||||
return SettingsPickerCell<SyncMode>(
|
||||
title: S.current.background_sync_mode,
|
||||
items: SyncMode.all,
|
||||
displayItem: (SyncMode syncMode) => syncMode.name,
|
||||
selectedItem: dashboardViewModel.syncMode,
|
||||
onItemSelected: dashboardViewModel.setSyncMode,
|
||||
);
|
||||
}),
|
||||
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),
|
||||
);
|
||||
}),
|
||||
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,26 +20,32 @@ class OtherSettingsPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
return Observer(builder: (_) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(children: [
|
||||
SettingsPickerCell(
|
||||
title: S.current.settings_fee_priority,
|
||||
items: priorityForWalletType(_otherSettingsViewModel.walletType),
|
||||
displayItem: _otherSettingsViewModel.getDisplayPriority,
|
||||
selectedItem: _otherSettingsViewModel.transactionPriority,
|
||||
onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected,
|
||||
return Observer(
|
||||
builder: (_) {
|
||||
return Container(
|
||||
padding: EdgeInsets.only(top: 10),
|
||||
child: Column(
|
||||
children: [
|
||||
SettingsPickerCell(
|
||||
title: S.current.settings_fee_priority,
|
||||
items: priorityForWalletType(_otherSettingsViewModel.walletType),
|
||||
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))
|
||||
]),
|
||||
);
|
||||
});
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ class SectionHeaderListRow extends StatelessWidget {
|
|||
|
||||
class StandardListSeparator extends StatelessWidget {
|
||||
|
||||
StandardListSeparator({this.padding, this.height = 1});
|
||||
const StandardListSeparator({this.padding, this.height = 1});
|
||||
|
||||
final EdgeInsets? padding;
|
||||
final double height;
|
||||
|
|
|
@ -2,10 +2,12 @@ import 'dart:io';
|
|||
|
||||
import 'package:cake_wallet/bitcoin/bitcoin.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/pin_code_required_duration.dart';
|
||||
import 'package:cake_wallet/entities/preferences_key.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/ethereum/ethereum.dart';
|
||||
import 'package:cw_core/transaction_priority.dart';
|
||||
|
@ -34,7 +36,8 @@ class SettingsStore = SettingsStoreBase with _$SettingsStore;
|
|||
|
||||
abstract class SettingsStoreBase with Store {
|
||||
SettingsStoreBase(
|
||||
{required SharedPreferences sharedPreferences,
|
||||
{required BackgroundTasks backgroundTasks,
|
||||
required SharedPreferences sharedPreferences,
|
||||
required bool initialShouldShowMarketPlaceInDashboard,
|
||||
required FiatCurrency initialFiatCurrency,
|
||||
required BalanceDisplayMode initialBalanceDisplayMode,
|
||||
|
@ -51,6 +54,8 @@ abstract class SettingsStoreBase with Store {
|
|||
required ThemeBase initialTheme,
|
||||
required int initialPinLength,
|
||||
required String initialLanguageCode,
|
||||
required SyncMode initialSyncMode,
|
||||
required bool initialSyncAll,
|
||||
// required String initialCurrentLocale,
|
||||
required this.appVersion,
|
||||
required this.deviceName,
|
||||
|
@ -78,6 +83,7 @@ abstract class SettingsStoreBase with Store {
|
|||
TransactionPriority? initialEthereumTransactionPriority})
|
||||
: nodes = ObservableMap<WalletType, Node>.of(nodes),
|
||||
_sharedPreferences = sharedPreferences,
|
||||
_backgroundTasks = backgroundTasks,
|
||||
fiatCurrency = initialFiatCurrency,
|
||||
balanceDisplayMode = initialBalanceDisplayMode,
|
||||
shouldSaveRecipientAddress = initialSaveRecipientAddress,
|
||||
|
@ -107,6 +113,8 @@ abstract class SettingsStoreBase with Store {
|
|||
initialShouldRequireTOTP2FAForCreatingNewWallets,
|
||||
shouldRequireTOTP2FAForAllSecurityAndBackupSettings =
|
||||
initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
currentSyncMode = initialSyncMode,
|
||||
currentSyncAll = initialSyncAll,
|
||||
priority = ObservableMap<WalletType, TransactionPriority>() {
|
||||
//this.nodes = ObservableMap<WalletType, Node>.of(nodes);
|
||||
|
||||
|
@ -287,6 +295,18 @@ abstract class SettingsStoreBase with Store {
|
|||
(BalanceDisplayMode mode) => sharedPreferences.setInt(
|
||||
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(
|
||||
(_) => exchangeStatus,
|
||||
(ExchangeApiMode mode) =>
|
||||
|
@ -422,11 +442,18 @@ abstract class SettingsStoreBase with Store {
|
|||
@observable
|
||||
bool useEtherscan;
|
||||
|
||||
@observable
|
||||
SyncMode currentSyncMode;
|
||||
|
||||
@observable
|
||||
bool currentSyncAll;
|
||||
|
||||
String appVersion;
|
||||
|
||||
String deviceName;
|
||||
|
||||
SharedPreferences _sharedPreferences;
|
||||
final SharedPreferences _sharedPreferences;
|
||||
final BackgroundTasks _backgroundTasks;
|
||||
|
||||
ObservableMap<WalletType, Node> nodes;
|
||||
|
||||
|
@ -455,6 +482,7 @@ abstract class SettingsStoreBase with Store {
|
|||
BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance,
|
||||
ThemeBase? initialTheme}) async {
|
||||
final sharedPreferences = await getIt.getAsync<SharedPreferences>();
|
||||
final backgroundTasks = getIt.get<BackgroundTasks>();
|
||||
final currentFiatCurrency = FiatCurrency.deserialize(
|
||||
raw: sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey)!);
|
||||
|
||||
|
@ -597,6 +625,11 @@ abstract class SettingsStoreBase with Store {
|
|||
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(
|
||||
sharedPreferences: sharedPreferences,
|
||||
initialShouldShowMarketPlaceInDashboard: shouldShowMarketPlaceInDashboard,
|
||||
|
@ -641,6 +674,9 @@ abstract class SettingsStoreBase with Store {
|
|||
initialShouldRequireTOTP2FAForAllSecurityAndBackupSettings:
|
||||
shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
|
||||
initialEthereumTransactionPriority: ethereumTransactionPriority,
|
||||
backgroundTasks: backgroundTasks,
|
||||
initialSyncMode: savedSyncMode,
|
||||
initialSyncAll: savedSyncAll,
|
||||
shouldShowYatPopup: shouldShowYatPopup);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:cake_wallet/entities/exchange_api_mode.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/settings/sync_mode.dart';
|
||||
import 'package:cake_wallet/wallet_type_utils.dart';
|
||||
import 'package:cw_core/transaction_history.dart';
|
||||
import 'package:cw_core/balance.dart';
|
||||
|
@ -403,4 +404,16 @@ abstract class DashboardViewModelBase with Store {
|
|||
hasBuyAction = !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:flutter/material.dart';
|
||||
|
||||
class ChoicesListItem<ItemType> extends SettingsListItem {
|
||||
ChoicesListItem(
|
||||
{required String title,
|
||||
required this.selectedItem,
|
||||
required this.items,
|
||||
this.displayItem,
|
||||
String Function(ItemType item)? displayItem,
|
||||
void Function(ItemType item)? onItemSelected})
|
||||
: _onItemSelected = onItemSelected,
|
||||
_displayItem = displayItem,
|
||||
super(title);
|
||||
|
||||
final ItemType selectedItem;
|
||||
final List<ItemType> items;
|
||||
final String Function(ItemType item)? displayItem;
|
||||
final String Function(ItemType item)? _displayItem;
|
||||
final void Function(ItemType item)? _onItemSelected;
|
||||
|
||||
void onItemSelected(dynamic item) {
|
||||
|
@ -22,4 +21,11 @@ class ChoicesListItem<ItemType> extends SettingsListItem {
|
|||
_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/di.dart';
|
||||
import 'package:cake_wallet/entities/background_tasks.dart';
|
||||
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -71,6 +73,7 @@ abstract class WalletCreationVMBase with Store {
|
|||
walletInfo.address = wallet.walletAddresses.address;
|
||||
await _walletInfoSource.add(walletInfo);
|
||||
_appStore.changeCurrentWallet(wallet);
|
||||
getIt.get<BackgroundTasks>().registerSyncTask();
|
||||
_appStore.authenticationStore.allowed();
|
||||
state = ExecutedSuccessfullyState();
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'package:cake_wallet/core/auth_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:mobx/mobx.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
|
@ -58,8 +57,8 @@ abstract class WalletListViewModelBase with Store {
|
|||
name: info.name,
|
||||
type: info.type,
|
||||
key: info.key,
|
||||
isCurrent: info.name == _appStore.wallet!.name &&
|
||||
info.type == _appStore.wallet!.type,
|
||||
isCurrent: info.name == _appStore.wallet?.name &&
|
||||
info.type == _appStore.wallet?.type,
|
||||
isEnabled: availableWalletTypes.contains(info.type),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -66,6 +66,7 @@ dependencies:
|
|||
# check unorm_dart for usage and for replace
|
||||
permission_handler: ^10.0.0
|
||||
device_display_brightness: ^0.0.6
|
||||
workmanager: ^0.5.1
|
||||
platform_device_id: ^1.0.1
|
||||
wakelock: ^0.6.2
|
||||
flutter_mailer: ^2.0.2
|
||||
|
|
|
@ -667,5 +667,6 @@
|
|||
"share": "يشارك",
|
||||
"slidable": "قابل للانزلاق",
|
||||
"etherscan_history": "Etherscan تاريخ",
|
||||
"manage_nodes": "ﺪﻘﻌﻟﺍ ﺓﺭﺍﺩﺇ",
|
||||
"template_name": "اسم القالب"
|
||||
}
|
||||
|
|
|
@ -663,5 +663,6 @@
|
|||
"share": "Дял",
|
||||
"slidable": "Плъзгащ се",
|
||||
"etherscan_history": "История на Etherscan",
|
||||
"manage_nodes": "Управление на възли",
|
||||
"template_name": "Име на шаблон"
|
||||
}
|
||||
|
|
|
@ -662,6 +662,7 @@
|
|||
"balance_page": "Stránka zůstatku",
|
||||
"share": "Podíl",
|
||||
"slidable": "Posuvné",
|
||||
"manage_nodes": "Spravovat uzly",
|
||||
"etherscan_history": "Historie Etherscanu",
|
||||
"template_name": "Název šablony"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "Geschenkkarte öffnen",
|
||||
"contact_support": "Support kontaktieren",
|
||||
"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!",
|
||||
"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",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "Balance-Seite",
|
||||
"share": "Aktie",
|
||||
"slidable": "Verschiebbar",
|
||||
"manage_nodes": "Knoten verwalten",
|
||||
"etherscan_history": "Etherscan-Geschichte",
|
||||
"template_name": "Vorlagenname"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "Open Gift Card",
|
||||
"contact_support": "Contact Support",
|
||||
"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!",
|
||||
"cake_pay_learn_more": "Instantly purchase and redeem gift cards in the app!\nSwipe left to right to learn more.",
|
||||
"automatic": "Automatic",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "Balance Page",
|
||||
"share": "Share",
|
||||
"slidable": "Slidable",
|
||||
"manage_nodes": "Manage nodes",
|
||||
"etherscan_history": "Etherscan history",
|
||||
"template_name": "Template Name"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "Abrir tarjeta de regalo",
|
||||
"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",
|
||||
"background_sync_mode": "Modo de sincronización en segundo plano",
|
||||
"sync_all_wallets": "Sincronizar todas las billeteras",
|
||||
"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.",
|
||||
"automatic": "Automático",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "Página de saldo",
|
||||
"share": "Compartir",
|
||||
"slidable": "deslizable",
|
||||
"manage_nodes": "Administrar nodos",
|
||||
"etherscan_history": "historia de etherscan",
|
||||
"template_name": "Nombre de la plantilla"
|
||||
}
|
||||
|
|
|
@ -536,8 +536,10 @@
|
|||
"gift_card_is_generated": "La carte-cadeau est générée",
|
||||
"open_gift_card": "Ouvrir la carte-cadeau",
|
||||
"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",
|
||||
"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 !",
|
||||
"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",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "Page Solde",
|
||||
"share": "Partager",
|
||||
"slidable": "Glissable",
|
||||
"manage_nodes": "Gérer les nœuds",
|
||||
"etherscan_history": "Historique d'Etherscan",
|
||||
"template_name": "Nom du modèle"
|
||||
}
|
||||
|
|
|
@ -649,5 +649,6 @@
|
|||
"share": "Raba",
|
||||
"slidable": "Mai iya zamewa",
|
||||
"etherscan_history": "Etherscan tarihin kowane zamani",
|
||||
"manage_nodes": "Sarrafa nodes",
|
||||
"template_name": "Sunan Samfura"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "गिफ्ट कार्ड खोलें",
|
||||
"contact_support": "सहायता से संपर्क करें",
|
||||
"gift_cards_unavailable": "उपहार कार्ड इस समय केवल मोनेरो, बिटकॉइन और लिटकोइन के माध्यम से खरीदने के लिए उपलब्ध हैं",
|
||||
"background_sync_mode": "बैकग्राउंड सिंक मोड",
|
||||
"sync_all_wallets": "सभी वॉलेट सिंक करें",
|
||||
"introducing_cake_pay": "परिचय Cake Pay!",
|
||||
"cake_pay_learn_more": "ऐप में उपहार कार्ड तुरंत खरीदें और रिडीम करें!\nअधिक जानने के लिए बाएं से दाएं स्वाइप करें।",
|
||||
"automatic": "स्वचालित",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "बैलेंस पेज",
|
||||
"share": "शेयर करना",
|
||||
"slidable": "फिसलने लायक",
|
||||
"manage_nodes": "नोड्स प्रबंधित करें",
|
||||
"etherscan_history": "इथरस्कैन इतिहास",
|
||||
"template_name": "टेम्पलेट नाम"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "Otvori darovnu karticu",
|
||||
"contact_support": "Kontaktirajte podršku",
|
||||
"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!",
|
||||
"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",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "Stranica sa stanjem",
|
||||
"share": "Udio",
|
||||
"slidable": "Klizna",
|
||||
"manage_nodes": "Upravljanje čvorovima",
|
||||
"etherscan_history": "Etherscan povijest",
|
||||
"template_name": "Naziv predloška"
|
||||
}
|
||||
|
|
|
@ -658,6 +658,7 @@
|
|||
"balance_page": "Halaman Saldo",
|
||||
"share": "Membagikan",
|
||||
"slidable": "Dapat digeser",
|
||||
"manage_nodes": "Kelola node",
|
||||
"etherscan_history": "Sejarah Etherscan",
|
||||
"template_name": "Nama Templat"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "Apri carta regalo",
|
||||
"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",
|
||||
"background_sync_mode": "Modalità di sincronizzazione in background",
|
||||
"sync_all_wallets": "Sincronizza tutti i portafogli",
|
||||
"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ù.",
|
||||
"automatic": "Automatico",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "Pagina di equilibrio",
|
||||
"share": "Condividere",
|
||||
"slidable": "Scorrevole",
|
||||
"manage_nodes": "Gestisci i nodi",
|
||||
"etherscan_history": "Storia Etherscan",
|
||||
"template_name": "Nome modello"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "オープンギフトカード",
|
||||
"contact_support": "サポートに連絡する",
|
||||
"gift_cards_unavailable": "現時点では、ギフトカードはMonero、Bitcoin、Litecoinからのみ購入できます。",
|
||||
"background_sync_mode": "バックグラウンド同期モード",
|
||||
"sync_all_wallets": "すべてのウォレットを同期",
|
||||
"introducing_cake_pay": "序章Cake Pay!",
|
||||
"cake_pay_learn_more": "アプリですぐにギフトカードを購入して引き換えましょう!\n左から右にスワイプして詳細をご覧ください。",
|
||||
"automatic": "自動",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "残高ページ",
|
||||
"share": "共有",
|
||||
"slidable": "スライド可能",
|
||||
"manage_nodes": "ノードの管理",
|
||||
"etherscan_history": "イーサスキャンの歴史",
|
||||
"template_name": "テンプレート名"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "기프트 카드 열기",
|
||||
"contact_support": "지원팀에 문의",
|
||||
"gift_cards_unavailable": "기프트 카드는 현재 Monero, Bitcoin 및 Litecoin을 통해서만 구매할 수 있습니다.",
|
||||
"background_sync_mode": "백그라운드 동기화 모드",
|
||||
"sync_all_wallets": "모든 지갑 동기화",
|
||||
"introducing_cake_pay": "소개 Cake Pay!",
|
||||
"cake_pay_learn_more": "앱에서 즉시 기프트 카드를 구매하고 사용하세요!\n자세히 알아보려면 왼쪽에서 오른쪽으로 스와이프하세요.",
|
||||
"automatic": "자동적 인",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "잔액 페이지",
|
||||
"share": "공유하다",
|
||||
"slidable": "슬라이딩 가능",
|
||||
"manage_nodes": "노드 관리",
|
||||
"etherscan_history": "이더스캔 역사",
|
||||
"template_name": "템플릿 이름"
|
||||
}
|
||||
|
|
|
@ -668,6 +668,7 @@
|
|||
"balance_page": "လက်ကျန်စာမျက်နှာ",
|
||||
"share": "မျှဝေပါ။",
|
||||
"slidable": "လျှောချနိုင်သည်။",
|
||||
"manage_nodes": "ဆုံမှတ်များကို စီမံပါ။",
|
||||
"etherscan_history": "Etherscan သမိုင်း",
|
||||
"template_name": "နမူနာပုံစံ"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "Geschenkkaart openen",
|
||||
"contact_support": "Contact opnemen met ondersteuning",
|
||||
"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!",
|
||||
"cake_pay_learn_more": "Koop en wissel cadeaubonnen direct in de app in!\nSwipe van links naar rechts voor meer informatie.",
|
||||
"automatic": "automatisch",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "Saldo pagina",
|
||||
"share": "Deel",
|
||||
"slidable": "Verschuifbaar",
|
||||
"manage_nodes": "Beheer knooppunten",
|
||||
"etherscan_history": "Etherscan-geschiedenis",
|
||||
"template_name": "Sjabloonnaam"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "Otwórz kartę podarunkową",
|
||||
"contact_support": "Skontaktuj się z pomocą techniczną",
|
||||
"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!",
|
||||
"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",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "Strona salda",
|
||||
"share": "Udział",
|
||||
"slidable": "Przesuwne",
|
||||
"manage_nodes": "Zarządzaj węzłami",
|
||||
"etherscan_history": "Historia Etherscanu",
|
||||
"template_name": "Nazwa szablonu"
|
||||
}
|
||||
|
|
|
@ -537,6 +537,8 @@
|
|||
"open_gift_card": "Abrir vale-presente",
|
||||
"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",
|
||||
"background_sync_mode": "Modo de sincronização em segundo plano",
|
||||
"sync_all_wallets": "Sincronize todas as carteiras",
|
||||
"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.",
|
||||
"automatic": "Automático",
|
||||
|
@ -667,6 +669,7 @@
|
|||
"balance_page": "Página de saldo",
|
||||
"share": "Compartilhar",
|
||||
"slidable": "Deslizável",
|
||||
"manage_nodes": "Gerenciar nós",
|
||||
"etherscan_history": "história Etherscan",
|
||||
"template_name": "Nome do modelo"
|
||||
}
|
||||
|
|
|
@ -539,6 +539,8 @@
|
|||
"open_gift_card": "Открыть подарочную карту",
|
||||
"contact_support": "Связаться со службой поддержки",
|
||||
"gift_cards_unavailable": "В настоящее время подарочные карты можно приобрести только через Monero, Bitcoin и Litecoin.",
|
||||
"background_sync_mode": "Режим фоновой синхронизации",
|
||||
"sync_all_wallets": "Синхронизировать все кошельки",
|
||||
"introducing_cake_pay": "Представляем Cake Pay!",
|
||||
"cake_pay_learn_more": "Мгновенно покупайте и используйте подарочные карты в приложении!\nПроведите по экрану слева направо, чтобы узнать больше.",
|
||||
"automatic": "автоматический",
|
||||
|
@ -669,6 +671,7 @@
|
|||
"balance_page": "Страница баланса",
|
||||
"share": "Делиться",
|
||||
"slidable": "Скользящий",
|
||||
"manage_nodes": "Управление узлами",
|
||||
"etherscan_history": "История Эфириума",
|
||||
"template_name": "Имя Шаблона"
|
||||
}
|
||||
|
|
|
@ -668,6 +668,7 @@
|
|||
"balance_page": "หน้ายอดคงเหลือ",
|
||||
"share": "แบ่งปัน",
|
||||
"slidable": "เลื่อนได้",
|
||||
"manage_nodes": "จัดการโหนด",
|
||||
"etherscan_history": "ประวัติอีเธอร์สแกน",
|
||||
"template_name": "ชื่อแม่แบบ"
|
||||
}
|
||||
|
|
|
@ -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_totp_recommended": "TOTP'yi kurun (Önerilir)",
|
||||
"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ı",
|
||||
"narrow": "Dar",
|
||||
"normal": "Normal",
|
||||
|
@ -669,6 +669,7 @@
|
|||
"balance_page": "Bakiye Sayfası",
|
||||
"share": "Paylaşmak",
|
||||
"slidable": "kaydırılabilir",
|
||||
"manage_nodes": "Düğümleri yönet",
|
||||
"etherscan_history": "Etherscan geçmişi",
|
||||
"template_name": "şablon adı"
|
||||
}
|
||||
|
|
|
@ -538,6 +538,8 @@
|
|||
"open_gift_card": "Відкрити подарункову картку",
|
||||
"contact_support": "Звернутися до служби підтримки",
|
||||
"gift_cards_unavailable": "Наразі подарункові картки можна придбати лише через Monero, Bitcoin і Litecoin",
|
||||
"background_sync_mode": "Фоновий режим синхронізації",
|
||||
"sync_all_wallets": "Синхронізувати всі гаманці",
|
||||
"introducing_cake_pay": "Представляємо Cake Pay!",
|
||||
"cake_pay_learn_more": "Миттєво купуйте та активуйте подарункові картки в додатку!\nПроведіть пальцем зліва направо, щоб дізнатися більше.",
|
||||
"automatic": "Автоматичний",
|
||||
|
@ -668,6 +670,7 @@
|
|||
"balance_page": "Сторінка балансу",
|
||||
"share": "Поділіться",
|
||||
"slidable": "Розсувний",
|
||||
"manage_nodes": "Керуйте вузлами",
|
||||
"etherscan_history": "Історія Etherscan",
|
||||
"template_name": "Назва шаблону"
|
||||
}
|
||||
|
|
|
@ -662,6 +662,7 @@
|
|||
"balance_page": "بیلنس صفحہ",
|
||||
"share": "بانٹیں",
|
||||
"slidable": "سلائیڈ ایبل",
|
||||
"manage_nodes": "۔ﮟﯾﺮﮐ ﻢﻈﻧ ﺎﮐ ﺱﮈﻮﻧ",
|
||||
"etherscan_history": "ﺦﯾﺭﺎﺗ ﯽﮐ ﻦﯿﮑﺳﺍ ﺮﮭﺘﯾﺍ",
|
||||
"template_name": "ٹیمپلیٹ کا نام"
|
||||
}
|
||||
|
|
|
@ -664,6 +664,7 @@
|
|||
"balance_page": "Oju-iwe iwọntunwọnsi",
|
||||
"share": "Pinpin",
|
||||
"slidable": "Slidable",
|
||||
"manage_nodes": "Ṣakoso awọn apa",
|
||||
"etherscan_history": "Etherscan itan",
|
||||
"template_name": "Orukọ Awoṣe"
|
||||
}
|
||||
|
|
|
@ -537,6 +537,8 @@
|
|||
"open_gift_card": "打开礼品卡",
|
||||
"contact_support": "联系支持",
|
||||
"gift_cards_unavailable": "目前只能通过门罗币、比特币和莱特币购买礼品卡",
|
||||
"background_sync_mode": "后台同步模式",
|
||||
"sync_all_wallets": "同步所有钱包",
|
||||
"introducing_cake_pay": "介绍 Cake Pay!",
|
||||
"cake_pay_learn_more": "立即在应用中购买和兑换礼品卡!\n从左向右滑动以了解详情。",
|
||||
"automatic": "自动的",
|
||||
|
@ -667,6 +669,7 @@
|
|||
"balance_page": "余额页",
|
||||
"share": "分享",
|
||||
"slidable": "可滑动",
|
||||
"manage_nodes": "管理节点",
|
||||
"etherscan_history": "以太扫描历史",
|
||||
"template_name": "模板名称"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue