mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 12:29:51 +00:00
CW-959: Swap Status on Transaction Screen (#2299)
* feat(swap-status-monitor): add real-time swap status monitoring and UI updates - Introduce SwapManager for automatic tracking of active-wallet swaps. - Automatically queues new or updated trades from the Hive box. - Periodically fetch and persist swap statuses via the corresponding trade provider. - Implement start(wallet, providers), stop(), and dispose() for lifecycle control. - Apply user's ExchangeApiMode(disabled, tor-only, enabled) when fetching updates. - Remove swaps from the watchlist on any final state (completed, expired, failed). - Dispose SwapManager in AppState.dispose() to cancel polling and the Hive subscription. * refactor(swap-status): replace SwapManager with TradeMonitor for improved trade monitoring. This change improves the flow by simplifying the trade monitoring logic. - Removes SwapManager class and replace with TradeMonitor implementation - Update di and Appstate to register and dispose TradeMonitor - Modify DashboardViewModel to use TradeMonitor instead of SwapManager * fix: Modify trade monitoring logic to ensure trade timers are properly disposed when wallet switching occurs * fix(swap-status): Fix receive amount for exchanges showing as .00 because of null values * feat(swap-status): Enhance Trade Monitoring This change: - Adds a privacy settings option to disable automatic exchange status updates. - Prevents trade monitoring when privacy settings option is enabled. - Disables trade monitoring when the app is in background, we only want to run these checks in foreground. - Refactors the trade monitoring logic to remove unneccessary checks and use of resources. * feat(swap-status): Enhance Trade Monitoring This change: - Adds a privacy settings option to disable automatic exchange status updates. - Prevents trade monitoring when privacy settings option is enabled. - Disables trade monitoring when the app is in background, we only want to run these checks in foreground. - Refactors the trade monitoring logic to remove unneccessary checks and use of resources. * fix(swap-staus): Prevent unneccessary calls * feat(swap-status): Prevent api request calls as long as last update time is less than specified interval
This commit is contained in:
parent
1134915920
commit
1d6e594e04
39 changed files with 455 additions and 82 deletions
247
lib/core/trade_monitor.dart
Normal file
247
lib/core/trade_monitor.dart
Normal file
|
@ -0,0 +1,247 @@
|
|||
import 'dart:async';
|
||||
import 'package:cake_wallet/exchange/provider/simpleswap_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/trade.dart';
|
||||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
||||
import 'package:cake_wallet/entities/exchange_api_mode.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cake_wallet/exchange/provider/chainflip_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/changenow_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/exolix_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/letsexchange_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/swaptrade_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/sideshift_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/stealth_ex_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/thorchain_exchange.provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
|
||||
import 'package:cake_wallet/exchange/provider/xoswap_exchange_provider.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:cake_wallet/store/app_store.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class TradeMonitor {
|
||||
static const int _tradeCheckIntervalMinutes = 5;
|
||||
static const int _maxTradeAgeHours = 24;
|
||||
|
||||
TradeMonitor({
|
||||
required this.tradesStore,
|
||||
required this.trades,
|
||||
required this.appStore,
|
||||
required this.preferences,
|
||||
});
|
||||
|
||||
final TradesStore tradesStore;
|
||||
final Box<Trade> trades;
|
||||
final AppStore appStore;
|
||||
final Map<String, Timer> _tradeTimers = {};
|
||||
final SharedPreferences preferences;
|
||||
|
||||
ExchangeProvider? _getProviderByDescription(ExchangeProviderDescription description) {
|
||||
switch (description) {
|
||||
case ExchangeProviderDescription.changeNow:
|
||||
return ChangeNowExchangeProvider(settingsStore: appStore.settingsStore);
|
||||
case ExchangeProviderDescription.sideShift:
|
||||
return SideShiftExchangeProvider();
|
||||
case ExchangeProviderDescription.simpleSwap:
|
||||
return SimpleSwapExchangeProvider();
|
||||
case ExchangeProviderDescription.trocador:
|
||||
return TrocadorExchangeProvider();
|
||||
case ExchangeProviderDescription.exolix:
|
||||
return ExolixExchangeProvider();
|
||||
case ExchangeProviderDescription.thorChain:
|
||||
return ThorChainExchangeProvider(tradesStore: trades);
|
||||
case ExchangeProviderDescription.swapTrade:
|
||||
return SwapTradeExchangeProvider();
|
||||
case ExchangeProviderDescription.letsExchange:
|
||||
return LetsExchangeExchangeProvider();
|
||||
case ExchangeProviderDescription.stealthEx:
|
||||
return StealthExExchangeProvider();
|
||||
case ExchangeProviderDescription.chainflip:
|
||||
return ChainflipExchangeProvider(tradesStore: trades);
|
||||
case ExchangeProviderDescription.xoSwap:
|
||||
return XOSwapExchangeProvider();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void monitorActiveTrades(String walletId) {
|
||||
// Checks if the trade monitoring is permitted
|
||||
// i.e the user has not disabled the exchange api mode or the status updates
|
||||
final isTradeMonitoringPermitted = _isTradeMonitoringPermitted();
|
||||
if (!isTradeMonitoringPermitted) {
|
||||
return;
|
||||
}
|
||||
|
||||
final trades = tradesStore.trades;
|
||||
final tradesToCancel = <String>[];
|
||||
|
||||
for (final item in trades) {
|
||||
final trade = item.trade;
|
||||
|
||||
final provider = _getProviderByDescription(trade.provider);
|
||||
|
||||
// Multiple checks to see if to skip the trade, if yes, we cancel the timer if it exists
|
||||
if (_shouldSkipTrade(trade, walletId, provider)) {
|
||||
tradesToCancel.add(trade.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_tradeTimers.containsKey(trade.id)) {
|
||||
printV('Trade ${trade.id} is already being monitored');
|
||||
continue;
|
||||
} else {
|
||||
_startTradeMonitoring(trade, provider!);
|
||||
}
|
||||
}
|
||||
|
||||
// After going through the list of available trades, we cancel the timers in the tradesToCancel list
|
||||
_cancelMultipleTradeTimers(tradesToCancel);
|
||||
}
|
||||
|
||||
bool _isTradeMonitoringPermitted() {
|
||||
final disableAutomaticExchangeStatusUpdates =
|
||||
appStore.settingsStore.disableAutomaticExchangeStatusUpdates;
|
||||
if (disableAutomaticExchangeStatusUpdates) {
|
||||
printV('Automatic exchange status updates are disabled');
|
||||
return false;
|
||||
}
|
||||
|
||||
final exchangeApiMode = appStore.settingsStore.exchangeStatus;
|
||||
if (exchangeApiMode == ExchangeApiMode.disabled) {
|
||||
printV('Exchange API mode is disabled');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _shouldSkipTrade(Trade trade, String walletId, ExchangeProvider? provider) {
|
||||
if (trade.walletId != walletId) {
|
||||
printV('Skipping trade ${trade.id} because it\'s not for this wallet');
|
||||
return true;
|
||||
}
|
||||
|
||||
final createdAt = trade.createdAt;
|
||||
if (createdAt == null) {
|
||||
printV('Skipping trade ${trade.id} because it has no createdAt');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (DateTime.now().difference(createdAt).inHours > _maxTradeAgeHours) {
|
||||
printV('Skipping trade ${trade.id} because it\'s older than ${_maxTradeAgeHours} hours');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_isFinalState(trade.state)) {
|
||||
printV('Skipping trade ${trade.id} because it\'s in a final state');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (provider == null) {
|
||||
printV('Skipping trade ${trade.id} because the provider is not supported');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (appStore.settingsStore.exchangeStatus == ExchangeApiMode.torOnly &&
|
||||
!provider.supportsOnionAddress) {
|
||||
printV('Skipping ${provider.description}, no TOR support');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void _startTradeMonitoring(Trade trade, ExchangeProvider provider) {
|
||||
final timer = Timer.periodic(
|
||||
Duration(minutes: _tradeCheckIntervalMinutes),
|
||||
(_) => _checkTradeStatus(trade, provider),
|
||||
);
|
||||
|
||||
_checkTradeStatus(trade, provider);
|
||||
|
||||
_tradeTimers[trade.id] = timer;
|
||||
}
|
||||
|
||||
Future<void> _checkTradeStatus(Trade trade, ExchangeProvider provider) async {
|
||||
final lastUpdatedAtFromPrefs = preferences.getString('trade_${trade.id}_updated_at');
|
||||
|
||||
if (lastUpdatedAtFromPrefs != null) {
|
||||
final lastUpdatedAtDateTime = DateTime.parse(lastUpdatedAtFromPrefs);
|
||||
final timeSinceLastUpdate = DateTime.now().difference(lastUpdatedAtDateTime).inMinutes;
|
||||
|
||||
if (timeSinceLastUpdate < _tradeCheckIntervalMinutes) {
|
||||
printV(
|
||||
'Skipping trade ${trade.id} status update check because it was updated less than ${_tradeCheckIntervalMinutes} minutes ago ($timeSinceLastUpdate minutes ago)',
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final updated = await provider.findTradeById(id: trade.id);
|
||||
trade
|
||||
..stateRaw = updated.state.raw
|
||||
..receiveAmount = updated.receiveAmount ?? trade.receiveAmount
|
||||
..outputTransaction = updated.outputTransaction ?? trade.outputTransaction;
|
||||
printV('Trade ${trade.id} updated: ${trade.state}');
|
||||
await trade.save();
|
||||
|
||||
await preferences.setString('trade_${trade.id}_updated_at', DateTime.now().toIso8601String());
|
||||
printV('Trade ${trade.id} updated at: ${DateTime.now().toIso8601String()}');
|
||||
|
||||
// If the updated trade is in a final state, we cancel the timer
|
||||
if (_isFinalState(updated.state)) {
|
||||
printV('Trade ${trade.id} is in final state');
|
||||
_cancelSingleTradeTimer(trade.id);
|
||||
}
|
||||
} catch (e) {
|
||||
printV('Error fetching status for ${trade.id}: $e');
|
||||
}
|
||||
}
|
||||
|
||||
bool _isFinalState(TradeState state) {
|
||||
return {
|
||||
TradeState.completed.raw,
|
||||
TradeState.success.raw,
|
||||
TradeState.confirmed.raw,
|
||||
TradeState.settled.raw,
|
||||
TradeState.finished.raw,
|
||||
TradeState.expired.raw,
|
||||
TradeState.failed.raw,
|
||||
TradeState.notFound.raw,
|
||||
}.contains(state.raw);
|
||||
}
|
||||
|
||||
void _cancelSingleTradeTimer(String tradeId) {
|
||||
if (_tradeTimers.containsKey(tradeId)) {
|
||||
_tradeTimers[tradeId]?.cancel();
|
||||
_tradeTimers.remove(tradeId);
|
||||
printV('Trade timer for ${tradeId} cancelled');
|
||||
}
|
||||
}
|
||||
|
||||
void _cancelMultipleTradeTimers(List<String> tradeIds) {
|
||||
for (final tradeId in tradeIds) {
|
||||
_cancelSingleTradeTimer(tradeId);
|
||||
}
|
||||
}
|
||||
|
||||
/// This is called when the app is brought back to foreground.
|
||||
void resumeTradeMonitoring() {
|
||||
if (appStore.wallet != null) {
|
||||
monitorActiveTrades(appStore.wallet!.id);
|
||||
}
|
||||
}
|
||||
|
||||
/// There's no need to run the trade checks when the app is in background.
|
||||
/// We only want to update the trade status when the app is in foreground.
|
||||
/// This helps to reduce the battery usage, network usage and enhance overall privacy.
|
||||
///
|
||||
/// This is called when the app is sent to background or when the app is closed.
|
||||
void stopTradeMonitoring() {
|
||||
printV('Stopping trade monitoring');
|
||||
_cancelMultipleTradeTimers(_tradeTimers.keys.toList());
|
||||
}
|
||||
}
|
61
lib/di.dart
61
lib/di.dart
|
@ -275,6 +275,7 @@ import 'src/screens/buy/buy_sell_page.dart';
|
|||
import 'cake_pay/cake_pay_payment_credantials.dart';
|
||||
import 'package:cake_wallet/view_model/dev/background_sync_logs_view_model.dart';
|
||||
import 'package:cake_wallet/src/screens/dev/background_sync_logs_page.dart';
|
||||
import 'package:cake_wallet/core/trade_monitor.dart';
|
||||
|
||||
final getIt = GetIt.instance;
|
||||
|
||||
|
@ -507,19 +508,42 @@ Future<void> setup({
|
|||
settingsStore: getIt.get<SettingsStore>(),
|
||||
fiatConvertationStore: getIt.get<FiatConversionStore>()));
|
||||
|
||||
getIt.registerFactory(() => DashboardViewModel(
|
||||
balanceViewModel: getIt.get<BalanceViewModel>(),
|
||||
appStore: getIt.get<AppStore>(),
|
||||
getIt.registerFactory(
|
||||
() => ExchangeViewModel(
|
||||
getIt.get<AppStore>(),
|
||||
_tradesSource,
|
||||
getIt.get<ExchangeTemplateStore>(),
|
||||
getIt.get<TradesStore>(),
|
||||
getIt.get<AppStore>().settingsStore,
|
||||
getIt.get<SharedPreferences>(),
|
||||
getIt.get<ContactListViewModel>(),
|
||||
getIt.get<FeesViewModel>(),
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerSingleton(
|
||||
TradeMonitor(
|
||||
tradesStore: getIt.get<TradesStore>(),
|
||||
tradeFilterStore: getIt.get<TradeFilterStore>(),
|
||||
transactionFilterStore: getIt.get<TransactionFilterStore>(),
|
||||
settingsStore: settingsStore,
|
||||
yatStore: getIt.get<YatStore>(),
|
||||
ordersStore: getIt.get<OrdersStore>(),
|
||||
anonpayTransactionsStore: getIt.get<AnonpayTransactionsStore>(),
|
||||
payjoinTransactionsStore: getIt.get<PayjoinTransactionsStore>(),
|
||||
sharedPreferences: getIt.get<SharedPreferences>(),
|
||||
keyService: getIt.get<KeyService>()));
|
||||
trades: _tradesSource,
|
||||
appStore: getIt.get<AppStore>(),
|
||||
preferences: getIt.get<SharedPreferences>(),
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactory(() => DashboardViewModel(
|
||||
tradeMonitor: getIt.get<TradeMonitor>(),
|
||||
balanceViewModel: getIt.get<BalanceViewModel>(),
|
||||
appStore: getIt.get<AppStore>(),
|
||||
tradesStore: getIt.get<TradesStore>(),
|
||||
tradeFilterStore: getIt.get<TradeFilterStore>(),
|
||||
transactionFilterStore: getIt.get<TransactionFilterStore>(),
|
||||
settingsStore: settingsStore,
|
||||
yatStore: getIt.get<YatStore>(),
|
||||
ordersStore: getIt.get<OrdersStore>(),
|
||||
anonpayTransactionsStore: getIt.get<AnonpayTransactionsStore>(),
|
||||
payjoinTransactionsStore: getIt.get<PayjoinTransactionsStore>(),
|
||||
sharedPreferences: getIt.get<SharedPreferences>(),
|
||||
keyService: getIt.get<KeyService>()));
|
||||
|
||||
getIt.registerFactory<AuthService>(
|
||||
() => AuthService(
|
||||
|
@ -1051,19 +1075,6 @@ Future<void> setup({
|
|||
|
||||
getIt.registerFactoryParam<WebViewPage, String, Uri>((title, uri) => WebViewPage(title, uri));
|
||||
|
||||
getIt.registerFactory(
|
||||
() => ExchangeViewModel(
|
||||
getIt.get<AppStore>(),
|
||||
_tradesSource,
|
||||
getIt.get<ExchangeTemplateStore>(),
|
||||
getIt.get<TradesStore>(),
|
||||
getIt.get<SettingsStore>(),
|
||||
getIt.get<SharedPreferences>(),
|
||||
getIt.get<ContactListViewModel>(),
|
||||
getIt.get<FeesViewModel>(),
|
||||
),
|
||||
);
|
||||
|
||||
getIt.registerFactory<FeesViewModel>(
|
||||
() => FeesViewModel(
|
||||
getIt.get<AppStore>(),
|
||||
|
|
|
@ -24,6 +24,7 @@ class PreferencesKey {
|
|||
static const shouldSaveRecipientAddressKey = 'save_recipient_address';
|
||||
static const isAppSecureKey = 'is_app_secure';
|
||||
static const disableTradeOption = 'disable_buy';
|
||||
static const disableAutomaticExchangeStatusUpdates = 'disable_automatic_exchange_status_updates';
|
||||
static const disableBulletinKey = 'disable_bulletin';
|
||||
static const walletListOrder = 'wallet_list_order';
|
||||
static const contactListOrder = 'contact_list_order';
|
||||
|
|
|
@ -50,6 +50,7 @@ import 'package:cw_core/root_dir.dart';
|
|||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:cw_core/window_size.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:cake_wallet/core/trade_monitor.dart';
|
||||
|
||||
final navigatorKey = GlobalKey<NavigatorState>();
|
||||
final rootKey = GlobalKey<RootState>();
|
||||
|
@ -297,6 +298,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
|
|||
final appStore = getIt.get<AppStore>();
|
||||
final authService = getIt.get<AuthService>();
|
||||
final linkViewModel = getIt.get<LinkViewModel>();
|
||||
final tradeMonitor = getIt.get<TradeMonitor>();
|
||||
final statusBarColor = Colors.transparent;
|
||||
final authenticationStore = getIt.get<AuthenticationStore>();
|
||||
final initialRoute = authenticationStore.state == AuthenticationState.uninitialized
|
||||
|
@ -317,6 +319,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
|
|||
navigatorKey: navigatorKey,
|
||||
authService: authService,
|
||||
linkViewModel: linkViewModel,
|
||||
tradeMonitor: tradeMonitor,
|
||||
child: ThemeProvider(
|
||||
themeStore: appStore.themeStore,
|
||||
materialAppBuilder: (context, theme, darkTheme, themeMode) => MaterialApp(
|
||||
|
|
|
@ -166,17 +166,19 @@ class TransactionsPage extends StatelessWidget {
|
|||
|
||||
return Observer(
|
||||
builder: (_) => TradeRow(
|
||||
key: item.key,
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.tradeDetails, arguments: trade),
|
||||
key: item.key,
|
||||
onTap: () => Navigator.of(context)
|
||||
.pushNamed(Routes.tradeDetails, arguments: trade),
|
||||
swapState: trade.state,
|
||||
provider: trade.provider,
|
||||
from: trade.from,
|
||||
to: trade.to,
|
||||
createdAtFormattedDate: trade.createdAt != null
|
||||
? DateFormat('HH:mm').format(trade.createdAt!)
|
||||
: null,
|
||||
formattedAmount: item.tradeFormattedAmount,
|
||||
formattedReceiveAmount: item.tradeFormattedReceiveAmount),
|
||||
from: trade.from,
|
||||
to: trade.to,
|
||||
createdAtFormattedDate: trade.createdAt != null
|
||||
? DateFormat('HH:mm').format(trade.createdAt!)
|
||||
: null,
|
||||
formattedAmount: item.tradeFormattedAmount,
|
||||
formattedReceiveAmount: item.tradeFormattedReceiveAmount
|
||||
),
|
||||
);
|
||||
}
|
||||
if (item is OrderListItem) {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import 'package:cake_wallet/exchange/trade_state.dart';
|
||||
import 'package:cake_wallet/palette.dart';
|
||||
import 'package:cake_wallet/utils/image_utill.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cake_wallet/exchange/exchange_provider_description.dart';
|
||||
import 'package:cw_core/crypto_currency.dart';
|
||||
|
||||
class TradeRow extends StatelessWidget {
|
||||
TradeRow({
|
||||
|
@ -12,6 +14,7 @@ class TradeRow extends StatelessWidget {
|
|||
this.onTap,
|
||||
this.formattedAmount,
|
||||
this.formattedReceiveAmount,
|
||||
required this.swapState,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
@ -22,6 +25,7 @@ class TradeRow extends StatelessWidget {
|
|||
final String? createdAtFormattedDate;
|
||||
final String? formattedAmount;
|
||||
final String? formattedReceiveAmount;
|
||||
final TradeState swapState;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -29,25 +33,39 @@ class TradeRow extends StatelessWidget {
|
|||
final receiveAmountCrypto = to.toString();
|
||||
|
||||
return InkWell(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: EdgeInsets.fromLTRB(24, 8, 24, 8),
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: ImageUtil.getImageFromPath(
|
||||
imagePath: provider.image,
|
||||
height: 36,
|
||||
width: 36,
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: EdgeInsets.fromLTRB(24, 8, 24, 8),
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
child: ImageUtil.getImageFromPath(
|
||||
imagePath: provider.image, height: 36, width: 36),),
|
||||
Positioned(
|
||||
right: 0,
|
||||
bottom: 2,
|
||||
child: Container(
|
||||
height: 8,
|
||||
width: 8,
|
||||
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: _statusColor(context, swapState),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
|
@ -104,4 +122,21 @@ class TradeRow extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
Color _statusColor(BuildContext context, TradeState status) {
|
||||
switch (status) {
|
||||
case TradeState.complete:
|
||||
case TradeState.completed:
|
||||
case TradeState.finished:
|
||||
case TradeState.success:
|
||||
case TradeState.settled:
|
||||
return PaletteDark.brightGreen;
|
||||
case TradeState.failed:
|
||||
case TradeState.expired:
|
||||
case TradeState.notFound:
|
||||
return Palette.darkRed;
|
||||
default:
|
||||
return const Color(0xffff6600);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
|||
import 'package:cake_wallet/bitcoin/bitcoin.dart';
|
||||
import 'package:cake_wallet/core/auth_service.dart';
|
||||
import 'package:cake_wallet/core/totp_request_details.dart';
|
||||
import 'package:cake_wallet/core/trade_monitor.dart';
|
||||
import 'package:cake_wallet/utils/device_info.dart';
|
||||
import 'package:cake_wallet/view_model/link_view_model.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
|
@ -27,6 +28,7 @@ class Root extends StatefulWidget {
|
|||
required this.navigatorKey,
|
||||
required this.authService,
|
||||
required this.linkViewModel,
|
||||
required this.tradeMonitor,
|
||||
}) : super(key: key);
|
||||
|
||||
final AuthenticationStore authenticationStore;
|
||||
|
@ -35,6 +37,7 @@ class Root extends StatefulWidget {
|
|||
final AuthService authService;
|
||||
final Widget child;
|
||||
final LinkViewModel linkViewModel;
|
||||
final TradeMonitor tradeMonitor;
|
||||
|
||||
@override
|
||||
RootState createState() => RootState();
|
||||
|
@ -141,6 +144,8 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
bitcoin!.stopPayjoinSessions(widget.appStore.wallet!);
|
||||
}
|
||||
|
||||
widget.tradeMonitor.stopTradeMonitoring();
|
||||
|
||||
break;
|
||||
case AppLifecycleState.resumed:
|
||||
widget.authService.requireAuth().then((value) {
|
||||
|
@ -154,6 +159,8 @@ class RootState extends State<Root> with WidgetsBindingObserver {
|
|||
widget.appStore.settingsStore.usePayjoin) {
|
||||
bitcoin!.resumePayjoinSessions(widget.appStore.wallet!);
|
||||
}
|
||||
|
||||
widget.tradeMonitor.resumeTradeMonitoring();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -75,30 +75,41 @@ class PrivacyPage extends BasePage {
|
|||
),
|
||||
if (DeviceInfo.instance.isMobile)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.prevent_screenshots,
|
||||
value: _privacySettingsViewModel.isAppSecure,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setIsAppSecure(value);
|
||||
}),
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.disable_buy,
|
||||
value: _privacySettingsViewModel.disableTradeOption,
|
||||
title: S.current.prevent_screenshots,
|
||||
value: _privacySettingsViewModel.isAppSecure,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setDisableTradeOption(value);
|
||||
}),
|
||||
_privacySettingsViewModel.setIsAppSecure(value);
|
||||
},
|
||||
),
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.disable_bulletin,
|
||||
value: _privacySettingsViewModel.disableBulletin,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setDisableBulletin(value);
|
||||
}),
|
||||
title: S.current.disable_buy,
|
||||
value: _privacySettingsViewModel.disableTradeOption,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setDisableTradeOption(value);
|
||||
},
|
||||
),
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.disable_automatic_exchange_status_updates,
|
||||
value: _privacySettingsViewModel.disableAutomaticExchangeStatusUpdates,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setDisableAutomaticExchangeStatusUpdates(value);
|
||||
},
|
||||
),
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.disable_bulletin,
|
||||
value: _privacySettingsViewModel.disableBulletin,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setDisableBulletin(value);
|
||||
},
|
||||
),
|
||||
if (_privacySettingsViewModel.canUseEtherscan)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.etherscan_history,
|
||||
value: _privacySettingsViewModel.useEtherscan,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setUseEtherscan(value);
|
||||
}),
|
||||
title: S.current.etherscan_history,
|
||||
value: _privacySettingsViewModel.useEtherscan,
|
||||
onValueChange: (BuildContext _, bool value) {
|
||||
_privacySettingsViewModel.setUseEtherscan(value);
|
||||
},
|
||||
),
|
||||
if (_privacySettingsViewModel.canUsePolygonScan)
|
||||
SettingsSwitcherCell(
|
||||
title: S.current.polygonscan_history,
|
||||
|
|
|
@ -64,6 +64,7 @@ abstract class SettingsStoreBase with Store {
|
|||
required NanoSeedType initialNanoSeedType,
|
||||
required bool initialAppSecure,
|
||||
required bool initialDisableTrade,
|
||||
required bool initialDisableAutomaticExchangeStatusUpdates,
|
||||
required FilterListOrderType initialWalletListOrder,
|
||||
required FilterListOrderType initialContactListOrder,
|
||||
required bool initialDisableBulletin,
|
||||
|
@ -156,6 +157,7 @@ abstract class SettingsStoreBase with Store {
|
|||
numberOfFailedTokenTrials = initialFailedTokenTrial,
|
||||
isAppSecure = initialAppSecure,
|
||||
disableTradeOption = initialDisableTrade,
|
||||
disableAutomaticExchangeStatusUpdates = initialDisableAutomaticExchangeStatusUpdates,
|
||||
disableBulletin = initialDisableBulletin,
|
||||
walletListOrder = initialWalletListOrder,
|
||||
contactListOrder = initialContactListOrder,
|
||||
|
@ -307,6 +309,9 @@ abstract class SettingsStoreBase with Store {
|
|||
reaction((_) => disableTradeOption,
|
||||
(bool disableTradeOption) => sharedPreferences.setBool(PreferencesKey.disableTradeOption, disableTradeOption));
|
||||
|
||||
reaction((_) => disableAutomaticExchangeStatusUpdates,
|
||||
(bool disableAutomaticExchangeStatusUpdates) => sharedPreferences.setBool(PreferencesKey.disableAutomaticExchangeStatusUpdates, disableAutomaticExchangeStatusUpdates));
|
||||
|
||||
reaction(
|
||||
(_) => disableBulletin,
|
||||
(bool disableBulletin) =>
|
||||
|
@ -675,6 +680,9 @@ abstract class SettingsStoreBase with Store {
|
|||
@observable
|
||||
bool disableTradeOption;
|
||||
|
||||
@observable
|
||||
bool disableAutomaticExchangeStatusUpdates;
|
||||
|
||||
@observable
|
||||
FilterListOrderType contactListOrder;
|
||||
|
||||
|
@ -956,6 +964,7 @@ abstract class SettingsStoreBase with Store {
|
|||
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false;
|
||||
final isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false;
|
||||
final disableTradeOption = sharedPreferences.getBool(PreferencesKey.disableTradeOption) ?? false;
|
||||
final disableAutomaticExchangeStatusUpdates = sharedPreferences.getBool(PreferencesKey.disableAutomaticExchangeStatusUpdates) ?? false;
|
||||
final disableBulletin = sharedPreferences.getBool(PreferencesKey.disableBulletinKey) ?? false;
|
||||
final walletListOrder =
|
||||
FilterListOrderType.values[sharedPreferences.getInt(PreferencesKey.walletListOrder) ?? 0];
|
||||
|
@ -1273,6 +1282,7 @@ abstract class SettingsStoreBase with Store {
|
|||
initialNanoSeedType: nanoSeedType,
|
||||
initialAppSecure: isAppSecure,
|
||||
initialDisableTrade: disableTradeOption,
|
||||
initialDisableAutomaticExchangeStatusUpdates: disableAutomaticExchangeStatusUpdates,
|
||||
initialDisableBulletin: disableBulletin,
|
||||
initialWalletListOrder: walletListOrder,
|
||||
initialWalletListAscending: walletListAscending,
|
||||
|
@ -1436,6 +1446,7 @@ abstract class SettingsStoreBase with Store {
|
|||
sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials;
|
||||
isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure;
|
||||
disableTradeOption = sharedPreferences.getBool(PreferencesKey.disableTradeOption) ?? disableTradeOption;
|
||||
disableAutomaticExchangeStatusUpdates = sharedPreferences.getBool(PreferencesKey.disableAutomaticExchangeStatusUpdates) ?? disableAutomaticExchangeStatusUpdates;
|
||||
disableBulletin =
|
||||
sharedPreferences.getBool(PreferencesKey.disableBulletinKey) ?? disableBulletin;
|
||||
walletListOrder =
|
||||
|
|
|
@ -23,7 +23,6 @@ import 'package:cake_wallet/store/dashboard/trades_store.dart';
|
|||
import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart';
|
||||
import 'package:cake_wallet/store/settings_store.dart';
|
||||
import 'package:cake_wallet/store/yat/yat_store.dart';
|
||||
import 'package:cake_wallet/themes/core/material_base_theme.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/action_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/balance_view_model.dart';
|
||||
|
@ -56,6 +55,8 @@ import 'package:mobx/mobx.dart';
|
|||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'package:cake_wallet/core/trade_monitor.dart';
|
||||
|
||||
part 'dashboard_view_model.g.dart';
|
||||
|
||||
class DashboardViewModel = DashboardViewModelBase with _$DashboardViewModel;
|
||||
|
@ -63,6 +64,7 @@ class DashboardViewModel = DashboardViewModelBase with _$DashboardViewModel;
|
|||
abstract class DashboardViewModelBase with Store {
|
||||
DashboardViewModelBase(
|
||||
{required this.balanceViewModel,
|
||||
required this.tradeMonitor,
|
||||
required this.appStore,
|
||||
required this.tradesStore,
|
||||
required this.tradeFilterStore,
|
||||
|
@ -270,6 +272,9 @@ abstract class DashboardViewModelBase with Store {
|
|||
_checkMweb();
|
||||
showDecredInfoCard = wallet?.type == WalletType.decred &&
|
||||
sharedPreferences.getBool(PreferencesKey.showDecredInfoCard) != false;
|
||||
|
||||
tradeMonitor.stopTradeMonitoring();
|
||||
tradeMonitor.monitorActiveTrades(wallet!.id);
|
||||
});
|
||||
|
||||
_transactionDisposer?.reaction.dispose();
|
||||
|
@ -298,6 +303,10 @@ abstract class DashboardViewModelBase with Store {
|
|||
|
||||
_checkMweb();
|
||||
reaction((_) => settingsStore.mwebAlwaysScan, (bool value) => _checkMweb());
|
||||
|
||||
reaction((_) => tradesStore.trades, (_) => tradeMonitor.monitorActiveTrades(wallet.id));
|
||||
|
||||
tradeMonitor.monitorActiveTrades(wallet.id);
|
||||
}
|
||||
|
||||
bool _isTransactionDisposerCallbackRunning = false;
|
||||
|
@ -773,6 +782,8 @@ abstract class DashboardViewModelBase with Store {
|
|||
|
||||
BalanceViewModel balanceViewModel;
|
||||
|
||||
TradeMonitor tradeMonitor;
|
||||
|
||||
AppStore appStore;
|
||||
|
||||
SettingsStore settingsStore;
|
||||
|
|
|
@ -33,9 +33,8 @@ abstract class PrivacySettingsViewModelBase with Store {
|
|||
@action
|
||||
void setAutoGenerateSubaddresses(bool value) {
|
||||
_wallet.isEnabledAutoGenerateSubaddress = value;
|
||||
_settingsStore.autoGenerateSubaddressStatus = value
|
||||
? AutoGenerateSubaddressStatus.enabled
|
||||
: AutoGenerateSubaddressStatus.disabled;
|
||||
_settingsStore.autoGenerateSubaddressStatus =
|
||||
value ? AutoGenerateSubaddressStatus.enabled : AutoGenerateSubaddressStatus.disabled;
|
||||
}
|
||||
|
||||
bool get isAutoGenerateSubaddressesVisible => [
|
||||
|
@ -61,6 +60,10 @@ abstract class PrivacySettingsViewModelBase with Store {
|
|||
@computed
|
||||
bool get disableTradeOption => _settingsStore.disableTradeOption;
|
||||
|
||||
@computed
|
||||
bool get disableAutomaticExchangeStatusUpdates =>
|
||||
_settingsStore.disableAutomaticExchangeStatusUpdates;
|
||||
|
||||
@computed
|
||||
bool get disableBulletin => _settingsStore.disableBulletin;
|
||||
|
||||
|
@ -129,6 +132,10 @@ abstract class PrivacySettingsViewModelBase with Store {
|
|||
@action
|
||||
void setDisableTradeOption(bool value) => _settingsStore.disableTradeOption = value;
|
||||
|
||||
@action
|
||||
void setDisableAutomaticExchangeStatusUpdates(bool value) =>
|
||||
_settingsStore.disableAutomaticExchangeStatusUpdates = value;
|
||||
|
||||
@action
|
||||
void setDisableBulletin(bool value) => _settingsStore.disableBulletin = value;
|
||||
|
||||
|
@ -146,7 +153,7 @@ abstract class PrivacySettingsViewModelBase with Store {
|
|||
|
||||
@action
|
||||
void setLookupsWellKnown(bool value) => _settingsStore.lookupsWellKnown = value;
|
||||
|
||||
|
||||
@action
|
||||
void setLookupsYatService(bool value) => _settingsStore.lookupsYatService = value;
|
||||
|
||||
|
@ -175,8 +182,7 @@ abstract class PrivacySettingsViewModelBase with Store {
|
|||
}
|
||||
|
||||
@action
|
||||
void setUseMempoolFeeAPI(bool value) =>
|
||||
_settingsStore.useMempoolFeeAPI = value;
|
||||
void setUseMempoolFeeAPI(bool value) => _settingsStore.useMempoolFeeAPI = value;
|
||||
|
||||
@action
|
||||
void setUsePayjoin(bool value) {
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-رقم PIN",
|
||||
"digital_and_physical_card": " بطاقة ائتمان رقمية ومادية مسبقة الدفع",
|
||||
"disable": "إبطال",
|
||||
"disable_automatic_exchange_status_updates": "تعطيل تحديثات حالة التبادل التلقائي",
|
||||
"disable_bulletin": "تعطيل نشرة حالة الخدمة",
|
||||
"disable_buy": "تعطيل إجراء الشراء",
|
||||
"disable_cake_2fa": "تعطيل 2 عامل المصادقة",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-цифрен PIN",
|
||||
"digital_and_physical_card": " дигитална или физическа предплатена дебитна карта",
|
||||
"disable": "Деактивиране",
|
||||
"disable_automatic_exchange_status_updates": "Деактивирайте актуализациите на състоянието на автоматичния обмен",
|
||||
"disable_bulletin": "Деактивирайте бюлетина за състоянието на услугата",
|
||||
"disable_buy": "Деактивирайте действието за покупка",
|
||||
"disable_cake_2fa": "Деактивирайте Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-číselný PIN",
|
||||
"digital_and_physical_card": " digitální a fyzické předplacené debetní karty,",
|
||||
"disable": "Zakázat",
|
||||
"disable_automatic_exchange_status_updates": "Zakázat aktualizace stavu automatické výměny",
|
||||
"disable_bulletin": "Zakázat status servisního stavu",
|
||||
"disable_buy": "Zakázat akci nákupu",
|
||||
"disable_cake_2fa": "Zakázat Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-stellige PIN",
|
||||
"digital_and_physical_card": "digitale und physische Prepaid-Debitkarte",
|
||||
"disable": "Deaktivieren",
|
||||
"disable_automatic_exchange_status_updates": "Deaktivieren Sie die automatischen Austauschstatusaktualisierungen",
|
||||
"disable_bulletin": "Deaktivieren Sie das Bulletin des Service Status",
|
||||
"disable_buy": "Kaufaktion deaktivieren",
|
||||
"disable_cake_2fa": "Cake 2FA deaktivieren",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-digit PIN",
|
||||
"digital_and_physical_card": " digital and physical prepaid debit card",
|
||||
"disable": "Disable",
|
||||
"disable_automatic_exchange_status_updates": "Disable Automatic Exchange Status Updates",
|
||||
"disable_bulletin": "Disable service status bulletin",
|
||||
"disable_buy": "Disable buy action",
|
||||
"disable_cake_2fa": "Disable Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-dígito PIN",
|
||||
"digital_and_physical_card": " tarjeta de débito prepago digital y física",
|
||||
"disable": "Desactivar",
|
||||
"disable_automatic_exchange_status_updates": "Deshabilitar actualizaciones de estado de intercambio automático",
|
||||
"disable_bulletin": "Desactivar el boletín de estado del servicio",
|
||||
"disable_buy": "Desactivar acción de compra",
|
||||
"disable_cake_2fa": "Desactivar 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": " chiffres",
|
||||
"digital_and_physical_card": "carte de débit prépayée numérique et physique",
|
||||
"disable": "Désactiver",
|
||||
"disable_automatic_exchange_status_updates": "Désactiver les mises à jour de l'état d'échange automatique",
|
||||
"disable_bulletin": "Désactiver le bulletin de statut de service",
|
||||
"disable_buy": "Désactiver l'action d'achat",
|
||||
"disable_cake_2fa": "Désactiver Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-lambar PIN",
|
||||
"digital_and_physical_card": "katin zare kudi na dijital da na zahiri",
|
||||
"disable": "Kashe",
|
||||
"disable_automatic_exchange_status_updates": "Musaki sabuntawar yanayin canji na atomatik",
|
||||
"disable_bulletin": "Musaki ma'aunin sabis na sabis",
|
||||
"disable_buy": "Kashe alama",
|
||||
"disable_cake_2fa": "Musaki Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-अंक पिन",
|
||||
"digital_and_physical_card": "डिजिटल और भौतिक प्रीपेड डेबिट कार्ड",
|
||||
"disable": "अक्षम करना",
|
||||
"disable_automatic_exchange_status_updates": "स्वचालित एक्सचेंज स्टेटस अपडेट अक्षम करें",
|
||||
"disable_bulletin": "सेवा स्थिति बुलेटिन अक्षम करें",
|
||||
"disable_buy": "खरीद कार्रवाई अक्षम करें",
|
||||
"disable_cake_2fa": "केक 2FA अक्षम करें",
|
||||
|
@ -568,8 +569,8 @@
|
|||
"payjoin_unavailable_sheet_title": "Payjoin अनुपलब्ध क्यों है?",
|
||||
"payment_id": "भुगतान ID: ",
|
||||
"payment_made_easy": "भुगतान आसान किया गया",
|
||||
"Payment_was_received": "आपका भुगतान प्राप्त हो गया था।",
|
||||
"payment_was_received": "आपका भुगतान प्राप्त हुआ था।",
|
||||
"Payment_was_received": "आपका भुगतान प्राप्त हो गया था।",
|
||||
"payments": "भुगतान",
|
||||
"pending": " (अपूर्ण)",
|
||||
"percentageOf": "${amount} का",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-znamenkasti PIN",
|
||||
"digital_and_physical_card": "digitalna i fizička unaprijed plaćena debitna kartica",
|
||||
"disable": "Onemogući",
|
||||
"disable_automatic_exchange_status_updates": "Onemogućite ažuriranja automatskog statusa razmjene",
|
||||
"disable_bulletin": "Onemogućite bilten o statusu usluge",
|
||||
"disable_buy": "Onemogući kupnju",
|
||||
"disable_cake_2fa": "Onemogući Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-նիշ ՊԻՆ",
|
||||
"digital_and_physical_card": " թվային և ֆիզիկական նախավճարային դեբետային քարտ",
|
||||
"disable": "Անջատել",
|
||||
"disable_automatic_exchange_status_updates": "Անջատեք ավտոմատ փոխանակման կարգավիճակի թարմացումները",
|
||||
"disable_bulletin": "Անջատել ծառայության վիճակի տեղեկագիրը",
|
||||
"disable_buy": "Անջատել գնում գործողությունը",
|
||||
"disable_cake_2fa": "Անջատել Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-digit PIN",
|
||||
"digital_and_physical_card": " kartu debit pra-bayar digital dan fisik",
|
||||
"disable": "Cacat",
|
||||
"disable_automatic_exchange_status_updates": "Nonaktifkan Pembaruan Status Pertukaran Otomatis",
|
||||
"disable_bulletin": "Nonaktifkan Buletin Status Layanan",
|
||||
"disable_buy": "Nonaktifkan tindakan beli",
|
||||
"disable_cake_2fa": "Nonaktifkan Kue 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-cifre PIN",
|
||||
"digital_and_physical_card": "carta di debito prepagata digitale e fisica",
|
||||
"disable": "Disabilita",
|
||||
"disable_automatic_exchange_status_updates": "Disabilita gli aggiornamenti sullo stato automatico di scambio",
|
||||
"disable_bulletin": "Disabilita bollettino dello stato del servizio",
|
||||
"disable_buy": "Disabilita l'azione di acquisto",
|
||||
"disable_cake_2fa": "Disabilita Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "桁ピン",
|
||||
"digital_and_physical_card": "デジタルおよび物理プリペイドデビットカード",
|
||||
"disable": "無効にする",
|
||||
"disable_automatic_exchange_status_updates": "自動交換ステータスの更新を無効にします",
|
||||
"disable_bulletin": "サービスステータス速報を無効にします",
|
||||
"disable_buy": "購入アクションを無効にする",
|
||||
"disable_cake_2fa": "Cake 2FA を無効にする",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "자리 PIN",
|
||||
"digital_and_physical_card": " 디지털 및 실물 선불 직불 카드",
|
||||
"disable": "비활성화",
|
||||
"disable_automatic_exchange_status_updates": "자동 교환 상태 업데이트를 비활성화합니다",
|
||||
"disable_bulletin": "서비스 상태 게시판 비활성화",
|
||||
"disable_buy": "구매 기능 비활성화",
|
||||
"disable_cake_2fa": "Cake 2FA 비활성화",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-ဂဏန်း PIN",
|
||||
"digital_and_physical_card": " ဒစ်ဂျစ်တယ်နှင့် ရုပ်ပိုင်းဆိုင်ရာ ကြိုတင်ငွေပေးချေသော ဒက်ဘစ်ကတ်",
|
||||
"disable": "ပိတ်ပါ။",
|
||||
"disable_automatic_exchange_status_updates": "အလိုအလျောက်လဲလှယ် status ကို updates များကို disable လုပ်ပါ",
|
||||
"disable_bulletin": "ဝန်ဆောင်မှုအခြေအနေစာစောင်ကိုပိတ်ပါ",
|
||||
"disable_buy": "ဝယ်ယူမှု လုပ်ဆောင်ချက်ကို ပိတ်ပါ။",
|
||||
"disable_cake_2fa": "ကိတ်မုန့် 2FA ကို ပိတ်ပါ။",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-cijferige PIN",
|
||||
"digital_and_physical_card": "digitale en fysieke prepaid debetkaart",
|
||||
"disable": "Uitzetten",
|
||||
"disable_automatic_exchange_status_updates": "Schakel automatische uitwisselingsstatusupdates uit",
|
||||
"disable_bulletin": "Schakel servicestatus Bulletin uit",
|
||||
"disable_buy": "Koopactie uitschakelen",
|
||||
"disable_cake_2fa": "Taart 2FA uitschakelen",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-znakowy PIN",
|
||||
"digital_and_physical_card": " cyfrowa i fizyczna przedpłacona karta debetowa",
|
||||
"disable": "Wyłącz",
|
||||
"disable_automatic_exchange_status_updates": "Wyłącz automatyczne aktualizacje statusu wymiany",
|
||||
"disable_bulletin": "Wyłącz biuletyn",
|
||||
"disable_buy": "Wyłącz akcję kupna",
|
||||
"disable_cake_2fa": "Wyłącz Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "dígitos",
|
||||
"digital_and_physical_card": "cartão de débito pré-pago digital e físico",
|
||||
"disable": "Desativar",
|
||||
"disable_automatic_exchange_status_updates": "Desativar atualizações automáticas de status de troca",
|
||||
"disable_bulletin": "Desativar boletim de status de serviço",
|
||||
"disable_buy": "Desativar ação de compra",
|
||||
"disable_cake_2fa": "Desabilitar o Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-значный PIN",
|
||||
"digital_and_physical_card": "цифровая и физическая предоплаченная дебетовая карта",
|
||||
"disable": "Запрещать",
|
||||
"disable_automatic_exchange_status_updates": "Отключить обновления автоматического статуса обмена",
|
||||
"disable_bulletin": "Отключить бюллетень статуса обслуживания",
|
||||
"disable_buy": "Отключить действие покупки",
|
||||
"disable_cake_2fa": "Отключить торт 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-หลัก PIN",
|
||||
"digital_and_physical_card": "บัตรเดบิตดิจิตอลและบัตรพื้นฐาน",
|
||||
"disable": "ปิดการใช้งาน",
|
||||
"disable_automatic_exchange_status_updates": "ปิดใช้งานการอัปเดตสถานะการแลกเปลี่ยนอัตโนมัติ",
|
||||
"disable_bulletin": "ปิดการใช้งาน Bulletin สถานะบริการ",
|
||||
"disable_buy": "ปิดการใช้งานการซื้อ",
|
||||
"disable_cake_2fa": "ปิดการใช้งานเค้ก 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-digit PIN",
|
||||
"digital_and_physical_card": " digital at pisikal na prepaid debit card",
|
||||
"disable": "Huwag paganahin",
|
||||
"disable_automatic_exchange_status_updates": "Huwag paganahin ang mga awtomatikong pag -update ng katayuan ng palitan",
|
||||
"disable_bulletin": "Huwag paganahin ang bulletin ng katayuan ng serbisyo",
|
||||
"disable_buy": "Huwag paganahin ang pagkilos ng pagbili",
|
||||
"disable_cake_2fa": "Huwag paganahin ang Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": " haneli PIN",
|
||||
"digital_and_physical_card": " Dijital para birimleri ile para yükleyebileceğiniz ve ek bilgiye gerek olmayan",
|
||||
"disable": "Devre dışı bırakmak",
|
||||
"disable_automatic_exchange_status_updates": "Otomatik Değişim Durum Güncellemelerini Devre Dışı Bırak",
|
||||
"disable_bulletin": "Hizmet Durumu Bültenini Devre Dışı Bırak",
|
||||
"disable_buy": "Satın alma işlemini devre dışı bırak",
|
||||
"disable_cake_2fa": "Cake 2FA'yı Devre Dışı Bırak",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-значний PIN",
|
||||
"digital_and_physical_card": " цифрова та фізична передплачена дебетова картка",
|
||||
"disable": "Вимкнути",
|
||||
"disable_automatic_exchange_status_updates": "Вимкнути автоматичні оновлення стану обміну",
|
||||
"disable_bulletin": "Вимкнути статус послуги",
|
||||
"disable_buy": "Вимкнути дію покупки",
|
||||
"disable_cake_2fa": "Вимкнути Cake 2FA",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-ہندسوں کا پن",
|
||||
"digital_and_physical_card": " ڈیجیٹل اور فزیکل پری پیڈ ڈیبٹ کارڈ",
|
||||
"disable": "غیر فعال کریں۔",
|
||||
"disable_automatic_exchange_status_updates": "خودکار تبادلہ کی حیثیت کی تازہ کاریوں کو غیر فعال کریں",
|
||||
"disable_bulletin": "خدمت کی حیثیت کا بلیٹن کو غیر فعال کریں",
|
||||
"disable_buy": "خرید ایکشن کو غیر فعال کریں۔",
|
||||
"disable_cake_2fa": "کیک 2FA کو غیر فعال کریں۔",
|
||||
|
|
|
@ -249,6 +249,7 @@
|
|||
"digit_pin": "Mã PIN - số",
|
||||
"digital_and_physical_card": "thẻ ghi nợ trả trước kỹ thuật số và vật lý",
|
||||
"disable": "Vô hiệu hóa",
|
||||
"disable_automatic_exchange_status_updates": "Tắt các bản cập nhật trạng thái trao đổi tự động",
|
||||
"disable_bulletin": "Vô hiệu hóa bản tin tình trạng dịch vụ",
|
||||
"disable_buy": "Vô hiệu hóa chức năng mua",
|
||||
"disable_cake_2fa": "Vô hiệu hóa 2FA Cake",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "-díjíìtì òǹkà ìdánimọ̀ àdáni",
|
||||
"digital_and_physical_card": " káàdì ìrajà t'ara àti ti ayélujára",
|
||||
"disable": "Ko si",
|
||||
"disable_automatic_exchange_status_updates": "Mu awọn imudojuiwọn ipo paṣipaarọ aifọwọyi",
|
||||
"disable_bulletin": "Mu blogti ipo ipo ṣiṣẹ",
|
||||
"disable_buy": "Ko iṣọrọ ọja",
|
||||
"disable_cake_2fa": "Ko 2FA Cake sii",
|
||||
|
|
|
@ -250,6 +250,7 @@
|
|||
"digit_pin": "位 PIN",
|
||||
"digital_and_physical_card": "数字和物理预付借记卡",
|
||||
"disable": "停用",
|
||||
"disable_automatic_exchange_status_updates": "禁用自动交换状态更新",
|
||||
"disable_bulletin": "禁用服务状态公告",
|
||||
"disable_buy": "禁用购买操作",
|
||||
"disable_cake_2fa": "禁用蛋糕 2FA",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue