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:
David Adegoke 2025-06-04 16:24:56 +01:00 committed by GitHub
parent 1134915920
commit 1d6e594e04
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 455 additions and 82 deletions

247
lib/core/trade_monitor.dart Normal file
View 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());
}
}

View file

@ -275,6 +275,7 @@ import 'src/screens/buy/buy_sell_page.dart';
import 'cake_pay/cake_pay_payment_credantials.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/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/src/screens/dev/background_sync_logs_page.dart';
import 'package:cake_wallet/core/trade_monitor.dart';
final getIt = GetIt.instance; final getIt = GetIt.instance;
@ -507,19 +508,42 @@ Future<void> setup({
settingsStore: getIt.get<SettingsStore>(), settingsStore: getIt.get<SettingsStore>(),
fiatConvertationStore: getIt.get<FiatConversionStore>())); fiatConvertationStore: getIt.get<FiatConversionStore>()));
getIt.registerFactory(() => DashboardViewModel( getIt.registerFactory(
balanceViewModel: getIt.get<BalanceViewModel>(), () => ExchangeViewModel(
appStore: getIt.get<AppStore>(), 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>(), tradesStore: getIt.get<TradesStore>(),
tradeFilterStore: getIt.get<TradeFilterStore>(), trades: _tradesSource,
transactionFilterStore: getIt.get<TransactionFilterStore>(), appStore: getIt.get<AppStore>(),
settingsStore: settingsStore, preferences: getIt.get<SharedPreferences>(),
yatStore: getIt.get<YatStore>(), ),
ordersStore: getIt.get<OrdersStore>(), );
anonpayTransactionsStore: getIt.get<AnonpayTransactionsStore>(),
payjoinTransactionsStore: getIt.get<PayjoinTransactionsStore>(), getIt.registerFactory(() => DashboardViewModel(
sharedPreferences: getIt.get<SharedPreferences>(), tradeMonitor: getIt.get<TradeMonitor>(),
keyService: getIt.get<KeyService>())); 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>( getIt.registerFactory<AuthService>(
() => AuthService( () => AuthService(
@ -1051,19 +1075,6 @@ Future<void> setup({
getIt.registerFactoryParam<WebViewPage, String, Uri>((title, uri) => WebViewPage(title, uri)); 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>( getIt.registerFactory<FeesViewModel>(
() => FeesViewModel( () => FeesViewModel(
getIt.get<AppStore>(), getIt.get<AppStore>(),

View file

@ -24,6 +24,7 @@ class PreferencesKey {
static const shouldSaveRecipientAddressKey = 'save_recipient_address'; static const shouldSaveRecipientAddressKey = 'save_recipient_address';
static const isAppSecureKey = 'is_app_secure'; static const isAppSecureKey = 'is_app_secure';
static const disableTradeOption = 'disable_buy'; static const disableTradeOption = 'disable_buy';
static const disableAutomaticExchangeStatusUpdates = 'disable_automatic_exchange_status_updates';
static const disableBulletinKey = 'disable_bulletin'; static const disableBulletinKey = 'disable_bulletin';
static const walletListOrder = 'wallet_list_order'; static const walletListOrder = 'wallet_list_order';
static const contactListOrder = 'contact_list_order'; static const contactListOrder = 'contact_list_order';

View file

@ -50,6 +50,7 @@ import 'package:cw_core/root_dir.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:cw_core/window_size.dart'; import 'package:cw_core/window_size.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:cake_wallet/core/trade_monitor.dart';
final navigatorKey = GlobalKey<NavigatorState>(); final navigatorKey = GlobalKey<NavigatorState>();
final rootKey = GlobalKey<RootState>(); final rootKey = GlobalKey<RootState>();
@ -297,6 +298,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
final appStore = getIt.get<AppStore>(); final appStore = getIt.get<AppStore>();
final authService = getIt.get<AuthService>(); final authService = getIt.get<AuthService>();
final linkViewModel = getIt.get<LinkViewModel>(); final linkViewModel = getIt.get<LinkViewModel>();
final tradeMonitor = getIt.get<TradeMonitor>();
final statusBarColor = Colors.transparent; final statusBarColor = Colors.transparent;
final authenticationStore = getIt.get<AuthenticationStore>(); final authenticationStore = getIt.get<AuthenticationStore>();
final initialRoute = authenticationStore.state == AuthenticationState.uninitialized final initialRoute = authenticationStore.state == AuthenticationState.uninitialized
@ -317,6 +319,7 @@ class AppState extends State<App> with SingleTickerProviderStateMixin {
navigatorKey: navigatorKey, navigatorKey: navigatorKey,
authService: authService, authService: authService,
linkViewModel: linkViewModel, linkViewModel: linkViewModel,
tradeMonitor: tradeMonitor,
child: ThemeProvider( child: ThemeProvider(
themeStore: appStore.themeStore, themeStore: appStore.themeStore,
materialAppBuilder: (context, theme, darkTheme, themeMode) => MaterialApp( materialAppBuilder: (context, theme, darkTheme, themeMode) => MaterialApp(

View file

@ -166,17 +166,19 @@ class TransactionsPage extends StatelessWidget {
return Observer( return Observer(
builder: (_) => TradeRow( builder: (_) => TradeRow(
key: item.key, key: item.key,
onTap: () => Navigator.of(context) onTap: () => Navigator.of(context)
.pushNamed(Routes.tradeDetails, arguments: trade), .pushNamed(Routes.tradeDetails, arguments: trade),
swapState: trade.state,
provider: trade.provider, provider: trade.provider,
from: trade.from, from: trade.from,
to: trade.to, to: trade.to,
createdAtFormattedDate: trade.createdAt != null createdAtFormattedDate: trade.createdAt != null
? DateFormat('HH:mm').format(trade.createdAt!) ? DateFormat('HH:mm').format(trade.createdAt!)
: null, : null,
formattedAmount: item.tradeFormattedAmount, formattedAmount: item.tradeFormattedAmount,
formattedReceiveAmount: item.tradeFormattedReceiveAmount), formattedReceiveAmount: item.tradeFormattedReceiveAmount
),
); );
} }
if (item is OrderListItem) { if (item is OrderListItem) {

View file

@ -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:cake_wallet/utils/image_utill.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart';
import 'package:cw_core/crypto_currency.dart';
class TradeRow extends StatelessWidget { class TradeRow extends StatelessWidget {
TradeRow({ TradeRow({
@ -12,6 +14,7 @@ class TradeRow extends StatelessWidget {
this.onTap, this.onTap,
this.formattedAmount, this.formattedAmount,
this.formattedReceiveAmount, this.formattedReceiveAmount,
required this.swapState,
super.key, super.key,
}); });
@ -22,6 +25,7 @@ class TradeRow extends StatelessWidget {
final String? createdAtFormattedDate; final String? createdAtFormattedDate;
final String? formattedAmount; final String? formattedAmount;
final String? formattedReceiveAmount; final String? formattedReceiveAmount;
final TradeState swapState;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -29,25 +33,39 @@ class TradeRow extends StatelessWidget {
final receiveAmountCrypto = to.toString(); final receiveAmountCrypto = to.toString();
return InkWell( return InkWell(
onTap: onTap, onTap: onTap,
child: Container( child: Container(
padding: EdgeInsets.fromLTRB(24, 8, 24, 8), padding: EdgeInsets.fromLTRB(24, 8, 24, 8),
color: Colors.transparent, color: Colors.transparent,
child: Row( child: Row(
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
ClipRRect( Stack(
borderRadius: BorderRadius.circular(50), clipBehavior: Clip.none,
child: ImageUtil.getImageFromPath( children: [
imagePath: provider.image, ClipRRect(
height: 36, borderRadius: BorderRadius.circular(50),
width: 36, 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),
SizedBox(width: 12), Expanded(
Expanded( child: Column(
child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Row( 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);
}
}
} }

View file

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin/bitcoin.dart';
import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/core/totp_request_details.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/utils/device_info.dart';
import 'package:cake_wallet/view_model/link_view_model.dart'; import 'package:cake_wallet/view_model/link_view_model.dart';
import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/utils/print_verbose.dart';
@ -27,6 +28,7 @@ class Root extends StatefulWidget {
required this.navigatorKey, required this.navigatorKey,
required this.authService, required this.authService,
required this.linkViewModel, required this.linkViewModel,
required this.tradeMonitor,
}) : super(key: key); }) : super(key: key);
final AuthenticationStore authenticationStore; final AuthenticationStore authenticationStore;
@ -35,6 +37,7 @@ class Root extends StatefulWidget {
final AuthService authService; final AuthService authService;
final Widget child; final Widget child;
final LinkViewModel linkViewModel; final LinkViewModel linkViewModel;
final TradeMonitor tradeMonitor;
@override @override
RootState createState() => RootState(); RootState createState() => RootState();
@ -141,6 +144,8 @@ class RootState extends State<Root> with WidgetsBindingObserver {
bitcoin!.stopPayjoinSessions(widget.appStore.wallet!); bitcoin!.stopPayjoinSessions(widget.appStore.wallet!);
} }
widget.tradeMonitor.stopTradeMonitoring();
break; break;
case AppLifecycleState.resumed: case AppLifecycleState.resumed:
widget.authService.requireAuth().then((value) { widget.authService.requireAuth().then((value) {
@ -154,6 +159,8 @@ class RootState extends State<Root> with WidgetsBindingObserver {
widget.appStore.settingsStore.usePayjoin) { widget.appStore.settingsStore.usePayjoin) {
bitcoin!.resumePayjoinSessions(widget.appStore.wallet!); bitcoin!.resumePayjoinSessions(widget.appStore.wallet!);
} }
widget.tradeMonitor.resumeTradeMonitoring();
break; break;
default: default:
break; break;

View file

@ -75,30 +75,41 @@ class PrivacyPage extends BasePage {
), ),
if (DeviceInfo.instance.isMobile) if (DeviceInfo.instance.isMobile)
SettingsSwitcherCell( SettingsSwitcherCell(
title: S.current.prevent_screenshots, title: S.current.prevent_screenshots,
value: _privacySettingsViewModel.isAppSecure, value: _privacySettingsViewModel.isAppSecure,
onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setIsAppSecure(value);
}),
SettingsSwitcherCell(
title: S.current.disable_buy,
value: _privacySettingsViewModel.disableTradeOption,
onValueChange: (BuildContext _, bool value) { onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setDisableTradeOption(value); _privacySettingsViewModel.setIsAppSecure(value);
}), },
),
SettingsSwitcherCell( SettingsSwitcherCell(
title: S.current.disable_bulletin, title: S.current.disable_buy,
value: _privacySettingsViewModel.disableBulletin, value: _privacySettingsViewModel.disableTradeOption,
onValueChange: (BuildContext _, bool value) { onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setDisableBulletin(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) if (_privacySettingsViewModel.canUseEtherscan)
SettingsSwitcherCell( SettingsSwitcherCell(
title: S.current.etherscan_history, title: S.current.etherscan_history,
value: _privacySettingsViewModel.useEtherscan, value: _privacySettingsViewModel.useEtherscan,
onValueChange: (BuildContext _, bool value) { onValueChange: (BuildContext _, bool value) {
_privacySettingsViewModel.setUseEtherscan(value); _privacySettingsViewModel.setUseEtherscan(value);
}), },
),
if (_privacySettingsViewModel.canUsePolygonScan) if (_privacySettingsViewModel.canUsePolygonScan)
SettingsSwitcherCell( SettingsSwitcherCell(
title: S.current.polygonscan_history, title: S.current.polygonscan_history,

View file

@ -64,6 +64,7 @@ abstract class SettingsStoreBase with Store {
required NanoSeedType initialNanoSeedType, required NanoSeedType initialNanoSeedType,
required bool initialAppSecure, required bool initialAppSecure,
required bool initialDisableTrade, required bool initialDisableTrade,
required bool initialDisableAutomaticExchangeStatusUpdates,
required FilterListOrderType initialWalletListOrder, required FilterListOrderType initialWalletListOrder,
required FilterListOrderType initialContactListOrder, required FilterListOrderType initialContactListOrder,
required bool initialDisableBulletin, required bool initialDisableBulletin,
@ -156,6 +157,7 @@ abstract class SettingsStoreBase with Store {
numberOfFailedTokenTrials = initialFailedTokenTrial, numberOfFailedTokenTrials = initialFailedTokenTrial,
isAppSecure = initialAppSecure, isAppSecure = initialAppSecure,
disableTradeOption = initialDisableTrade, disableTradeOption = initialDisableTrade,
disableAutomaticExchangeStatusUpdates = initialDisableAutomaticExchangeStatusUpdates,
disableBulletin = initialDisableBulletin, disableBulletin = initialDisableBulletin,
walletListOrder = initialWalletListOrder, walletListOrder = initialWalletListOrder,
contactListOrder = initialContactListOrder, contactListOrder = initialContactListOrder,
@ -307,6 +309,9 @@ abstract class SettingsStoreBase with Store {
reaction((_) => disableTradeOption, reaction((_) => disableTradeOption,
(bool disableTradeOption) => sharedPreferences.setBool(PreferencesKey.disableTradeOption, disableTradeOption)); (bool disableTradeOption) => sharedPreferences.setBool(PreferencesKey.disableTradeOption, disableTradeOption));
reaction((_) => disableAutomaticExchangeStatusUpdates,
(bool disableAutomaticExchangeStatusUpdates) => sharedPreferences.setBool(PreferencesKey.disableAutomaticExchangeStatusUpdates, disableAutomaticExchangeStatusUpdates));
reaction( reaction(
(_) => disableBulletin, (_) => disableBulletin,
(bool disableBulletin) => (bool disableBulletin) =>
@ -675,6 +680,9 @@ abstract class SettingsStoreBase with Store {
@observable @observable
bool disableTradeOption; bool disableTradeOption;
@observable
bool disableAutomaticExchangeStatusUpdates;
@observable @observable
FilterListOrderType contactListOrder; FilterListOrderType contactListOrder;
@ -956,6 +964,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false; sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? false;
final isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false; final isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false;
final disableTradeOption = sharedPreferences.getBool(PreferencesKey.disableTradeOption) ?? false; final disableTradeOption = sharedPreferences.getBool(PreferencesKey.disableTradeOption) ?? false;
final disableAutomaticExchangeStatusUpdates = sharedPreferences.getBool(PreferencesKey.disableAutomaticExchangeStatusUpdates) ?? false;
final disableBulletin = sharedPreferences.getBool(PreferencesKey.disableBulletinKey) ?? false; final disableBulletin = sharedPreferences.getBool(PreferencesKey.disableBulletinKey) ?? false;
final walletListOrder = final walletListOrder =
FilterListOrderType.values[sharedPreferences.getInt(PreferencesKey.walletListOrder) ?? 0]; FilterListOrderType.values[sharedPreferences.getInt(PreferencesKey.walletListOrder) ?? 0];
@ -1273,6 +1282,7 @@ abstract class SettingsStoreBase with Store {
initialNanoSeedType: nanoSeedType, initialNanoSeedType: nanoSeedType,
initialAppSecure: isAppSecure, initialAppSecure: isAppSecure,
initialDisableTrade: disableTradeOption, initialDisableTrade: disableTradeOption,
initialDisableAutomaticExchangeStatusUpdates: disableAutomaticExchangeStatusUpdates,
initialDisableBulletin: disableBulletin, initialDisableBulletin: disableBulletin,
initialWalletListOrder: walletListOrder, initialWalletListOrder: walletListOrder,
initialWalletListAscending: walletListAscending, initialWalletListAscending: walletListAscending,
@ -1436,6 +1446,7 @@ abstract class SettingsStoreBase with Store {
sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials; sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials;
isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure; isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure;
disableTradeOption = sharedPreferences.getBool(PreferencesKey.disableTradeOption) ?? disableTradeOption; disableTradeOption = sharedPreferences.getBool(PreferencesKey.disableTradeOption) ?? disableTradeOption;
disableAutomaticExchangeStatusUpdates = sharedPreferences.getBool(PreferencesKey.disableAutomaticExchangeStatusUpdates) ?? disableAutomaticExchangeStatusUpdates;
disableBulletin = disableBulletin =
sharedPreferences.getBool(PreferencesKey.disableBulletinKey) ?? disableBulletin; sharedPreferences.getBool(PreferencesKey.disableBulletinKey) ?? disableBulletin;
walletListOrder = walletListOrder =

View file

@ -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/dashboard/transaction_filter_store.dart';
import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart';
import 'package:cake_wallet/store/yat/yat_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/action_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
import 'package:cake_wallet/view_model/dashboard/balance_view_model.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:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:cake_wallet/core/trade_monitor.dart';
part 'dashboard_view_model.g.dart'; part 'dashboard_view_model.g.dart';
class DashboardViewModel = DashboardViewModelBase with _$DashboardViewModel; class DashboardViewModel = DashboardViewModelBase with _$DashboardViewModel;
@ -63,6 +64,7 @@ class DashboardViewModel = DashboardViewModelBase with _$DashboardViewModel;
abstract class DashboardViewModelBase with Store { abstract class DashboardViewModelBase with Store {
DashboardViewModelBase( DashboardViewModelBase(
{required this.balanceViewModel, {required this.balanceViewModel,
required this.tradeMonitor,
required this.appStore, required this.appStore,
required this.tradesStore, required this.tradesStore,
required this.tradeFilterStore, required this.tradeFilterStore,
@ -270,6 +272,9 @@ abstract class DashboardViewModelBase with Store {
_checkMweb(); _checkMweb();
showDecredInfoCard = wallet?.type == WalletType.decred && showDecredInfoCard = wallet?.type == WalletType.decred &&
sharedPreferences.getBool(PreferencesKey.showDecredInfoCard) != false; sharedPreferences.getBool(PreferencesKey.showDecredInfoCard) != false;
tradeMonitor.stopTradeMonitoring();
tradeMonitor.monitorActiveTrades(wallet!.id);
}); });
_transactionDisposer?.reaction.dispose(); _transactionDisposer?.reaction.dispose();
@ -298,6 +303,10 @@ abstract class DashboardViewModelBase with Store {
_checkMweb(); _checkMweb();
reaction((_) => settingsStore.mwebAlwaysScan, (bool value) => _checkMweb()); reaction((_) => settingsStore.mwebAlwaysScan, (bool value) => _checkMweb());
reaction((_) => tradesStore.trades, (_) => tradeMonitor.monitorActiveTrades(wallet.id));
tradeMonitor.monitorActiveTrades(wallet.id);
} }
bool _isTransactionDisposerCallbackRunning = false; bool _isTransactionDisposerCallbackRunning = false;
@ -773,6 +782,8 @@ abstract class DashboardViewModelBase with Store {
BalanceViewModel balanceViewModel; BalanceViewModel balanceViewModel;
TradeMonitor tradeMonitor;
AppStore appStore; AppStore appStore;
SettingsStore settingsStore; SettingsStore settingsStore;

View file

@ -33,9 +33,8 @@ abstract class PrivacySettingsViewModelBase with Store {
@action @action
void setAutoGenerateSubaddresses(bool value) { void setAutoGenerateSubaddresses(bool value) {
_wallet.isEnabledAutoGenerateSubaddress = value; _wallet.isEnabledAutoGenerateSubaddress = value;
_settingsStore.autoGenerateSubaddressStatus = value _settingsStore.autoGenerateSubaddressStatus =
? AutoGenerateSubaddressStatus.enabled value ? AutoGenerateSubaddressStatus.enabled : AutoGenerateSubaddressStatus.disabled;
: AutoGenerateSubaddressStatus.disabled;
} }
bool get isAutoGenerateSubaddressesVisible => [ bool get isAutoGenerateSubaddressesVisible => [
@ -61,6 +60,10 @@ abstract class PrivacySettingsViewModelBase with Store {
@computed @computed
bool get disableTradeOption => _settingsStore.disableTradeOption; bool get disableTradeOption => _settingsStore.disableTradeOption;
@computed
bool get disableAutomaticExchangeStatusUpdates =>
_settingsStore.disableAutomaticExchangeStatusUpdates;
@computed @computed
bool get disableBulletin => _settingsStore.disableBulletin; bool get disableBulletin => _settingsStore.disableBulletin;
@ -129,6 +132,10 @@ abstract class PrivacySettingsViewModelBase with Store {
@action @action
void setDisableTradeOption(bool value) => _settingsStore.disableTradeOption = value; void setDisableTradeOption(bool value) => _settingsStore.disableTradeOption = value;
@action
void setDisableAutomaticExchangeStatusUpdates(bool value) =>
_settingsStore.disableAutomaticExchangeStatusUpdates = value;
@action @action
void setDisableBulletin(bool value) => _settingsStore.disableBulletin = value; void setDisableBulletin(bool value) => _settingsStore.disableBulletin = value;
@ -175,8 +182,7 @@ abstract class PrivacySettingsViewModelBase with Store {
} }
@action @action
void setUseMempoolFeeAPI(bool value) => void setUseMempoolFeeAPI(bool value) => _settingsStore.useMempoolFeeAPI = value;
_settingsStore.useMempoolFeeAPI = value;
@action @action
void setUsePayjoin(bool value) { void setUsePayjoin(bool value) {

View file

@ -250,6 +250,7 @@
"digit_pin": "-رقم PIN", "digit_pin": "-رقم PIN",
"digital_and_physical_card": " بطاقة ائتمان رقمية ومادية مسبقة الدفع", "digital_and_physical_card": " بطاقة ائتمان رقمية ومادية مسبقة الدفع",
"disable": "إبطال", "disable": "إبطال",
"disable_automatic_exchange_status_updates": "تعطيل تحديثات حالة التبادل التلقائي",
"disable_bulletin": "تعطيل نشرة حالة الخدمة", "disable_bulletin": "تعطيل نشرة حالة الخدمة",
"disable_buy": "تعطيل إجراء الشراء", "disable_buy": "تعطيل إجراء الشراء",
"disable_cake_2fa": "تعطيل 2 عامل المصادقة", "disable_cake_2fa": "تعطيل 2 عامل المصادقة",

View file

@ -250,6 +250,7 @@
"digit_pin": "-цифрен PIN", "digit_pin": "-цифрен PIN",
"digital_and_physical_card": " дигитална или физическа предплатена дебитна карта", "digital_and_physical_card": " дигитална или физическа предплатена дебитна карта",
"disable": "Деактивиране", "disable": "Деактивиране",
"disable_automatic_exchange_status_updates": "Деактивирайте актуализациите на състоянието на автоматичния обмен",
"disable_bulletin": "Деактивирайте бюлетина за състоянието на услугата", "disable_bulletin": "Деактивирайте бюлетина за състоянието на услугата",
"disable_buy": "Деактивирайте действието за покупка", "disable_buy": "Деактивирайте действието за покупка",
"disable_cake_2fa": "Деактивирайте Cake 2FA", "disable_cake_2fa": "Деактивирайте Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-číselný PIN", "digit_pin": "-číselný PIN",
"digital_and_physical_card": " digitální a fyzické předplacené debetní karty,", "digital_and_physical_card": " digitální a fyzické předplacené debetní karty,",
"disable": "Zakázat", "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_bulletin": "Zakázat status servisního stavu",
"disable_buy": "Zakázat akci nákupu", "disable_buy": "Zakázat akci nákupu",
"disable_cake_2fa": "Zakázat Cake 2FA", "disable_cake_2fa": "Zakázat Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-stellige PIN", "digit_pin": "-stellige PIN",
"digital_and_physical_card": "digitale und physische Prepaid-Debitkarte", "digital_and_physical_card": "digitale und physische Prepaid-Debitkarte",
"disable": "Deaktivieren", "disable": "Deaktivieren",
"disable_automatic_exchange_status_updates": "Deaktivieren Sie die automatischen Austauschstatusaktualisierungen",
"disable_bulletin": "Deaktivieren Sie das Bulletin des Service Status", "disable_bulletin": "Deaktivieren Sie das Bulletin des Service Status",
"disable_buy": "Kaufaktion deaktivieren", "disable_buy": "Kaufaktion deaktivieren",
"disable_cake_2fa": "Cake 2FA deaktivieren", "disable_cake_2fa": "Cake 2FA deaktivieren",

View file

@ -250,6 +250,7 @@
"digit_pin": "-digit PIN", "digit_pin": "-digit PIN",
"digital_and_physical_card": " digital and physical prepaid debit card", "digital_and_physical_card": " digital and physical prepaid debit card",
"disable": "Disable", "disable": "Disable",
"disable_automatic_exchange_status_updates": "Disable Automatic Exchange Status Updates",
"disable_bulletin": "Disable service status bulletin", "disable_bulletin": "Disable service status bulletin",
"disable_buy": "Disable buy action", "disable_buy": "Disable buy action",
"disable_cake_2fa": "Disable Cake 2FA", "disable_cake_2fa": "Disable Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-dígito PIN", "digit_pin": "-dígito PIN",
"digital_and_physical_card": " tarjeta de débito prepago digital y física", "digital_and_physical_card": " tarjeta de débito prepago digital y física",
"disable": "Desactivar", "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_bulletin": "Desactivar el boletín de estado del servicio",
"disable_buy": "Desactivar acción de compra", "disable_buy": "Desactivar acción de compra",
"disable_cake_2fa": "Desactivar 2FA", "disable_cake_2fa": "Desactivar 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": " chiffres", "digit_pin": " chiffres",
"digital_and_physical_card": "carte de débit prépayée numérique et physique", "digital_and_physical_card": "carte de débit prépayée numérique et physique",
"disable": "Désactiver", "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_bulletin": "Désactiver le bulletin de statut de service",
"disable_buy": "Désactiver l'action d'achat", "disable_buy": "Désactiver l'action d'achat",
"disable_cake_2fa": "Désactiver Cake 2FA", "disable_cake_2fa": "Désactiver Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-lambar PIN", "digit_pin": "-lambar PIN",
"digital_and_physical_card": "katin zare kudi na dijital da na zahiri", "digital_and_physical_card": "katin zare kudi na dijital da na zahiri",
"disable": "Kashe", "disable": "Kashe",
"disable_automatic_exchange_status_updates": "Musaki sabuntawar yanayin canji na atomatik",
"disable_bulletin": "Musaki ma'aunin sabis na sabis", "disable_bulletin": "Musaki ma'aunin sabis na sabis",
"disable_buy": "Kashe alama", "disable_buy": "Kashe alama",
"disable_cake_2fa": "Musaki Cake 2FA", "disable_cake_2fa": "Musaki Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-अंक पिन", "digit_pin": "-अंक पिन",
"digital_and_physical_card": "डिजिटल और भौतिक प्रीपेड डेबिट कार्ड", "digital_and_physical_card": "डिजिटल और भौतिक प्रीपेड डेबिट कार्ड",
"disable": "अक्षम करना", "disable": "अक्षम करना",
"disable_automatic_exchange_status_updates": "स्वचालित एक्सचेंज स्टेटस अपडेट अक्षम करें",
"disable_bulletin": "सेवा स्थिति बुलेटिन अक्षम करें", "disable_bulletin": "सेवा स्थिति बुलेटिन अक्षम करें",
"disable_buy": "खरीद कार्रवाई अक्षम करें", "disable_buy": "खरीद कार्रवाई अक्षम करें",
"disable_cake_2fa": "केक 2FA अक्षम करें", "disable_cake_2fa": "केक 2FA अक्षम करें",
@ -568,8 +569,8 @@
"payjoin_unavailable_sheet_title": "Payjoin अनुपलब्ध क्यों है?", "payjoin_unavailable_sheet_title": "Payjoin अनुपलब्ध क्यों है?",
"payment_id": "भुगतान ID: ", "payment_id": "भुगतान ID: ",
"payment_made_easy": "भुगतान आसान किया गया", "payment_made_easy": "भुगतान आसान किया गया",
"Payment_was_received": "आपका भुगतान प्राप्त हो गया था।",
"payment_was_received": "आपका भुगतान प्राप्त हुआ था।", "payment_was_received": "आपका भुगतान प्राप्त हुआ था।",
"Payment_was_received": "आपका भुगतान प्राप्त हो गया था।",
"payments": "भुगतान", "payments": "भुगतान",
"pending": " (अपूर्ण)", "pending": " (अपूर्ण)",
"percentageOf": "${amount} का", "percentageOf": "${amount} का",

View file

@ -250,6 +250,7 @@
"digit_pin": "-znamenkasti PIN", "digit_pin": "-znamenkasti PIN",
"digital_and_physical_card": "digitalna i fizička unaprijed plaćena debitna kartica", "digital_and_physical_card": "digitalna i fizička unaprijed plaćena debitna kartica",
"disable": "Onemogući", "disable": "Onemogući",
"disable_automatic_exchange_status_updates": "Onemogućite ažuriranja automatskog statusa razmjene",
"disable_bulletin": "Onemogućite bilten o statusu usluge", "disable_bulletin": "Onemogućite bilten o statusu usluge",
"disable_buy": "Onemogući kupnju", "disable_buy": "Onemogući kupnju",
"disable_cake_2fa": "Onemogući Cake 2FA", "disable_cake_2fa": "Onemogući Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-նիշ ՊԻՆ", "digit_pin": "-նիշ ՊԻՆ",
"digital_and_physical_card": " թվային և ֆիզիկական նախավճարային դեբետային քարտ", "digital_and_physical_card": " թվային և ֆիզիկական նախավճարային դեբետային քարտ",
"disable": "Անջատել", "disable": "Անջատել",
"disable_automatic_exchange_status_updates": "Անջատեք ավտոմատ փոխանակման կարգավիճակի թարմացումները",
"disable_bulletin": "Անջատել ծառայության վիճակի տեղեկագիրը", "disable_bulletin": "Անջատել ծառայության վիճակի տեղեկագիրը",
"disable_buy": "Անջատել գնում գործողությունը", "disable_buy": "Անջատել գնում գործողությունը",
"disable_cake_2fa": "Անջատել Cake 2FA", "disable_cake_2fa": "Անջատել Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-digit PIN", "digit_pin": "-digit PIN",
"digital_and_physical_card": " kartu debit pra-bayar digital dan fisik", "digital_and_physical_card": " kartu debit pra-bayar digital dan fisik",
"disable": "Cacat", "disable": "Cacat",
"disable_automatic_exchange_status_updates": "Nonaktifkan Pembaruan Status Pertukaran Otomatis",
"disable_bulletin": "Nonaktifkan Buletin Status Layanan", "disable_bulletin": "Nonaktifkan Buletin Status Layanan",
"disable_buy": "Nonaktifkan tindakan beli", "disable_buy": "Nonaktifkan tindakan beli",
"disable_cake_2fa": "Nonaktifkan Kue 2FA", "disable_cake_2fa": "Nonaktifkan Kue 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-cifre PIN", "digit_pin": "-cifre PIN",
"digital_and_physical_card": "carta di debito prepagata digitale e fisica", "digital_and_physical_card": "carta di debito prepagata digitale e fisica",
"disable": "Disabilita", "disable": "Disabilita",
"disable_automatic_exchange_status_updates": "Disabilita gli aggiornamenti sullo stato automatico di scambio",
"disable_bulletin": "Disabilita bollettino dello stato del servizio", "disable_bulletin": "Disabilita bollettino dello stato del servizio",
"disable_buy": "Disabilita l'azione di acquisto", "disable_buy": "Disabilita l'azione di acquisto",
"disable_cake_2fa": "Disabilita Cake 2FA", "disable_cake_2fa": "Disabilita Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "桁ピン", "digit_pin": "桁ピン",
"digital_and_physical_card": "デジタルおよび物理プリペイドデビットカード", "digital_and_physical_card": "デジタルおよび物理プリペイドデビットカード",
"disable": "無効にする", "disable": "無効にする",
"disable_automatic_exchange_status_updates": "自動交換ステータスの更新を無効にします",
"disable_bulletin": "サービスステータス速報を無効にします", "disable_bulletin": "サービスステータス速報を無効にします",
"disable_buy": "購入アクションを無効にする", "disable_buy": "購入アクションを無効にする",
"disable_cake_2fa": "Cake 2FA を無効にする", "disable_cake_2fa": "Cake 2FA を無効にする",

View file

@ -250,6 +250,7 @@
"digit_pin": "자리 PIN", "digit_pin": "자리 PIN",
"digital_and_physical_card": " 디지털 및 실물 선불 직불 카드", "digital_and_physical_card": " 디지털 및 실물 선불 직불 카드",
"disable": "비활성화", "disable": "비활성화",
"disable_automatic_exchange_status_updates": "자동 교환 상태 업데이트를 비활성화합니다",
"disable_bulletin": "서비스 상태 게시판 비활성화", "disable_bulletin": "서비스 상태 게시판 비활성화",
"disable_buy": "구매 기능 비활성화", "disable_buy": "구매 기능 비활성화",
"disable_cake_2fa": "Cake 2FA 비활성화", "disable_cake_2fa": "Cake 2FA 비활성화",

View file

@ -250,6 +250,7 @@
"digit_pin": "-ဂဏန်း PIN", "digit_pin": "-ဂဏန်း PIN",
"digital_and_physical_card": " ဒစ်ဂျစ်တယ်နှင့် ရုပ်ပိုင်းဆိုင်ရာ ကြိုတင်ငွေပေးချေသော ဒက်ဘစ်ကတ်", "digital_and_physical_card": " ဒစ်ဂျစ်တယ်နှင့် ရုပ်ပိုင်းဆိုင်ရာ ကြိုတင်ငွေပေးချေသော ဒက်ဘစ်ကတ်",
"disable": "ပိတ်ပါ။", "disable": "ပိတ်ပါ။",
"disable_automatic_exchange_status_updates": "အလိုအလျောက်လဲလှယ် status ကို updates များကို disable လုပ်ပါ",
"disable_bulletin": "ဝန်ဆောင်မှုအခြေအနေစာစောင်ကိုပိတ်ပါ", "disable_bulletin": "ဝန်ဆောင်မှုအခြေအနေစာစောင်ကိုပိတ်ပါ",
"disable_buy": "ဝယ်ယူမှု လုပ်ဆောင်ချက်ကို ပိတ်ပါ။", "disable_buy": "ဝယ်ယူမှု လုပ်ဆောင်ချက်ကို ပိတ်ပါ။",
"disable_cake_2fa": "ကိတ်မုန့် 2FA ကို ပိတ်ပါ။", "disable_cake_2fa": "ကိတ်မုန့် 2FA ကို ပိတ်ပါ။",

View file

@ -250,6 +250,7 @@
"digit_pin": "-cijferige PIN", "digit_pin": "-cijferige PIN",
"digital_and_physical_card": "digitale en fysieke prepaid debetkaart", "digital_and_physical_card": "digitale en fysieke prepaid debetkaart",
"disable": "Uitzetten", "disable": "Uitzetten",
"disable_automatic_exchange_status_updates": "Schakel automatische uitwisselingsstatusupdates uit",
"disable_bulletin": "Schakel servicestatus Bulletin uit", "disable_bulletin": "Schakel servicestatus Bulletin uit",
"disable_buy": "Koopactie uitschakelen", "disable_buy": "Koopactie uitschakelen",
"disable_cake_2fa": "Taart 2FA uitschakelen", "disable_cake_2fa": "Taart 2FA uitschakelen",

View file

@ -250,6 +250,7 @@
"digit_pin": "-znakowy PIN", "digit_pin": "-znakowy PIN",
"digital_and_physical_card": " cyfrowa i fizyczna przedpłacona karta debetowa", "digital_and_physical_card": " cyfrowa i fizyczna przedpłacona karta debetowa",
"disable": "Wyłącz", "disable": "Wyłącz",
"disable_automatic_exchange_status_updates": "Wyłącz automatyczne aktualizacje statusu wymiany",
"disable_bulletin": "Wyłącz biuletyn", "disable_bulletin": "Wyłącz biuletyn",
"disable_buy": "Wyłącz akcję kupna", "disable_buy": "Wyłącz akcję kupna",
"disable_cake_2fa": "Wyłącz Cake 2FA", "disable_cake_2fa": "Wyłącz Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "dígitos", "digit_pin": "dígitos",
"digital_and_physical_card": "cartão de débito pré-pago digital e físico", "digital_and_physical_card": "cartão de débito pré-pago digital e físico",
"disable": "Desativar", "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_bulletin": "Desativar boletim de status de serviço",
"disable_buy": "Desativar ação de compra", "disable_buy": "Desativar ação de compra",
"disable_cake_2fa": "Desabilitar o Cake 2FA", "disable_cake_2fa": "Desabilitar o Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-значный PIN", "digit_pin": "-значный PIN",
"digital_and_physical_card": "цифровая и физическая предоплаченная дебетовая карта", "digital_and_physical_card": "цифровая и физическая предоплаченная дебетовая карта",
"disable": "Запрещать", "disable": "Запрещать",
"disable_automatic_exchange_status_updates": "Отключить обновления автоматического статуса обмена",
"disable_bulletin": "Отключить бюллетень статуса обслуживания", "disable_bulletin": "Отключить бюллетень статуса обслуживания",
"disable_buy": "Отключить действие покупки", "disable_buy": "Отключить действие покупки",
"disable_cake_2fa": "Отключить торт 2FA", "disable_cake_2fa": "Отключить торт 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-หลัก PIN", "digit_pin": "-หลัก PIN",
"digital_and_physical_card": "บัตรเดบิตดิจิตอลและบัตรพื้นฐาน", "digital_and_physical_card": "บัตรเดบิตดิจิตอลและบัตรพื้นฐาน",
"disable": "ปิดการใช้งาน", "disable": "ปิดการใช้งาน",
"disable_automatic_exchange_status_updates": "ปิดใช้งานการอัปเดตสถานะการแลกเปลี่ยนอัตโนมัติ",
"disable_bulletin": "ปิดการใช้งาน Bulletin สถานะบริการ", "disable_bulletin": "ปิดการใช้งาน Bulletin สถานะบริการ",
"disable_buy": "ปิดการใช้งานการซื้อ", "disable_buy": "ปิดการใช้งานการซื้อ",
"disable_cake_2fa": "ปิดการใช้งานเค้ก 2FA", "disable_cake_2fa": "ปิดการใช้งานเค้ก 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-digit PIN", "digit_pin": "-digit PIN",
"digital_and_physical_card": " digital at pisikal na prepaid debit card", "digital_and_physical_card": " digital at pisikal na prepaid debit card",
"disable": "Huwag paganahin", "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_bulletin": "Huwag paganahin ang bulletin ng katayuan ng serbisyo",
"disable_buy": "Huwag paganahin ang pagkilos ng pagbili", "disable_buy": "Huwag paganahin ang pagkilos ng pagbili",
"disable_cake_2fa": "Huwag paganahin ang Cake 2FA", "disable_cake_2fa": "Huwag paganahin ang Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": " haneli PIN", "digit_pin": " haneli PIN",
"digital_and_physical_card": " Dijital para birimleri ile para yükleyebileceğiniz ve ek bilgiye gerek olmayan", "digital_and_physical_card": " Dijital para birimleri ile para yükleyebileceğiniz ve ek bilgiye gerek olmayan",
"disable": "Devre dışı bırakmak", "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_bulletin": "Hizmet Durumu Bültenini Devre Dışı Bırak",
"disable_buy": "Satın alma işlemini 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", "disable_cake_2fa": "Cake 2FA'yı Devre Dışı Bırak",

View file

@ -250,6 +250,7 @@
"digit_pin": "-значний PIN", "digit_pin": "-значний PIN",
"digital_and_physical_card": " цифрова та фізична передплачена дебетова картка", "digital_and_physical_card": " цифрова та фізична передплачена дебетова картка",
"disable": "Вимкнути", "disable": "Вимкнути",
"disable_automatic_exchange_status_updates": "Вимкнути автоматичні оновлення стану обміну",
"disable_bulletin": "Вимкнути статус послуги", "disable_bulletin": "Вимкнути статус послуги",
"disable_buy": "Вимкнути дію покупки", "disable_buy": "Вимкнути дію покупки",
"disable_cake_2fa": "Вимкнути Cake 2FA", "disable_cake_2fa": "Вимкнути Cake 2FA",

View file

@ -250,6 +250,7 @@
"digit_pin": "-ہندسوں کا پن", "digit_pin": "-ہندسوں کا پن",
"digital_and_physical_card": " ڈیجیٹل اور فزیکل پری پیڈ ڈیبٹ کارڈ", "digital_and_physical_card": " ڈیجیٹل اور فزیکل پری پیڈ ڈیبٹ کارڈ",
"disable": "غیر فعال کریں۔", "disable": "غیر فعال کریں۔",
"disable_automatic_exchange_status_updates": "خودکار تبادلہ کی حیثیت کی تازہ کاریوں کو غیر فعال کریں",
"disable_bulletin": "خدمت کی حیثیت کا بلیٹن کو غیر فعال کریں", "disable_bulletin": "خدمت کی حیثیت کا بلیٹن کو غیر فعال کریں",
"disable_buy": "خرید ایکشن کو غیر فعال کریں۔", "disable_buy": "خرید ایکشن کو غیر فعال کریں۔",
"disable_cake_2fa": "کیک 2FA کو غیر فعال کریں۔", "disable_cake_2fa": "کیک 2FA کو غیر فعال کریں۔",

View file

@ -249,6 +249,7 @@
"digit_pin": "Mã PIN - số", "digit_pin": "Mã PIN - số",
"digital_and_physical_card": "thẻ ghi nợ trả trước kỹ thuật số và vật lý", "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": "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_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_buy": "Vô hiệu hóa chức năng mua",
"disable_cake_2fa": "Vô hiệu hóa 2FA Cake", "disable_cake_2fa": "Vô hiệu hóa 2FA Cake",

View file

@ -250,6 +250,7 @@
"digit_pin": "-díjíìtì òǹkà ìdánimọ̀ àdáni", "digit_pin": "-díjíìtì òǹkà ìdánimọ̀ àdáni",
"digital_and_physical_card": " káàdì ìrajà t'ara àti ti ayélujára", "digital_and_physical_card": " káàdì ìrajà t'ara àti ti ayélujára",
"disable": "Ko si", "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_bulletin": "Mu blogti ipo ipo ṣiṣẹ",
"disable_buy": "Ko iṣọrọ ọja", "disable_buy": "Ko iṣọrọ ọja",
"disable_cake_2fa": "Ko 2FA Cake sii", "disable_cake_2fa": "Ko 2FA Cake sii",

View file

@ -250,6 +250,7 @@
"digit_pin": "位 PIN", "digit_pin": "位 PIN",
"digital_and_physical_card": "数字和物理预付借记卡", "digital_and_physical_card": "数字和物理预付借记卡",
"disable": "停用", "disable": "停用",
"disable_automatic_exchange_status_updates": "禁用自动交换状态更新",
"disable_bulletin": "禁用服务状态公告", "disable_bulletin": "禁用服务状态公告",
"disable_buy": "禁用购买操作", "disable_buy": "禁用购买操作",
"disable_cake_2fa": "禁用蛋糕 2FA", "disable_cake_2fa": "禁用蛋糕 2FA",