diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 653faddf4..20a399a88 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -35,6 +35,13 @@ part 'bitcoin_wallet.g.dart'; class BitcoinWallet = BitcoinWalletBase with _$BitcoinWallet; abstract class BitcoinWalletBase extends ElectrumWallet with Store { + @observable + bool nodeSupportsSilentPayments = true; + @observable + bool silentPaymentsScanningActive = false; + @observable + bool allowedToSwitchNodesForScanning = false; + BitcoinWalletBase({ required String password, required WalletInfo walletInfo, @@ -307,63 +314,68 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { } Future getNodeIsElectrs() async { - if (node?.uri.host.contains("electrs") ?? false) { - return true; - } - - final version = await sendWorker(ElectrumWorkerGetVersionRequest()); - - if (version is List && version.isNotEmpty) { - final server = version[0]; - - if (server.toLowerCase().contains('electrs')) { - node!.isElectrs = true; - node!.save(); - return node!.isElectrs!; - } - } else if (version is String && version.toLowerCase().contains('electrs')) { - node!.isElectrs = true; - node!.save(); + if (node?.isElectrs != null) { return node!.isElectrs!; } - node!.isElectrs = false; + final isNamedElectrs = node?.uri.host.contains("electrs") ?? false; + if (isNamedElectrs) { + node!.isElectrs = true; + } + + final isNamedFulcrum = node!.uri.host.contains("fulcrum"); + if (isNamedFulcrum) { + node!.isElectrs = false; + } + + if (node!.isElectrs == null) { + final version = await sendWorker(ElectrumWorkerGetVersionRequest()); + + if (version is List && version.isNotEmpty) { + final server = version[0]; + + if (server.toLowerCase().contains('electrs')) { + node!.isElectrs = true; + } + } else if (version is String && version.toLowerCase().contains('electrs')) { + node!.isElectrs = true; + } else { + node!.isElectrs = false; + } + } + node!.save(); return node!.isElectrs!; } Future getNodeSupportsSilentPayments() async { - // TODO: handle disconnection on check - // TODO: use cached values - if (node == null) { - return false; - } - - final isFulcrum = node!.uri.host.contains("fulcrum"); - if (isFulcrum) { - return false; + if (node?.supportsSilentPayments != null) { + return node!.supportsSilentPayments!; } // As of today (august 2024), only ElectrumRS supports silent payments - if (!(await getNodeIsElectrs())) { - return false; + final isElectrs = await getNodeIsElectrs(); + if (!isElectrs) { + node!.supportsSilentPayments = false; } - try { - final workerResponse = (await sendWorker(ElectrumWorkerCheckTweaksRequest())) as String; - final tweaksResponse = ElectrumWorkerCheckTweaksResponse.fromJson( - json.decode(workerResponse) as Map, - ); - final supportsScanning = tweaksResponse.result == true; + if (node!.supportsSilentPayments == null) { + try { + final workerResponse = (await sendWorker(ElectrumWorkerCheckTweaksRequest())) as String; + final tweaksResponse = ElectrumWorkerCheckTweaksResponse.fromJson( + json.decode(workerResponse) as Map, + ); + final supportsScanning = tweaksResponse.result == true; - if (supportsScanning) { - node!.supportsSilentPayments = true; - node!.save(); - return node!.supportsSilentPayments!; + if (supportsScanning) { + node!.supportsSilentPayments = true; + } else { + node!.supportsSilentPayments = false; + } + } catch (_) { + node!.supportsSilentPayments = false; } - } catch (_) {} - - node!.supportsSilentPayments = false; + } node!.save(); return node!.supportsSilentPayments!; } @@ -437,8 +449,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { @action Future setSilentPaymentsScanning(bool active) async { silentPaymentsScanningActive = active; + final nodeSupportsSilentPayments = await getNodeSupportsSilentPayments(); + final isAllowedToScan = nodeSupportsSilentPayments || allowedToSwitchNodesForScanning; - if (active) { + if (active && isAllowedToScan) { syncStatus = AttemptingScanSyncStatus(); final tip = currentChainTip!; @@ -730,8 +744,6 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { await walletInfo.updateRestoreHeight(height); } } - - await save(); } @action @@ -765,6 +777,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { .map((addr) => addr.labelIndex) .toList(), isSingleScan: doSingleScan ?? false, + shouldSwitchNodes: + !(await getNodeSupportsSilentPayments()) && allowedToSwitchNodesForScanning, ), ).toJson(), ); diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index fb6fc92b1..60ad22635 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -276,11 +276,6 @@ abstract class ElectrumWalletBase @override bool isTestnet; - @observable - bool nodeSupportsSilentPayments = true; - @observable - bool silentPaymentsScanningActive = false; - bool _isTryingToConnect = false; Completer sharedPrefs = Completer(); diff --git a/cw_bitcoin/lib/electrum_worker/electrum_worker.dart b/cw_bitcoin/lib/electrum_worker/electrum_worker.dart index 14bb0c4db..14b1bab7f 100644 --- a/cw_bitcoin/lib/electrum_worker/electrum_worker.dart +++ b/cw_bitcoin/lib/electrum_worker/electrum_worker.dart @@ -14,17 +14,15 @@ import 'package:cw_bitcoin/electrum_worker/electrum_worker_methods.dart'; import 'package:cw_bitcoin/electrum_worker/electrum_worker_params.dart'; import 'package:cw_bitcoin/electrum_worker/methods/methods.dart'; import 'package:cw_core/sync_status.dart'; -import 'package:cw_core/transaction_direction.dart'; -import 'package:cw_core/wallet_type.dart'; import 'package:http/http.dart' as http; +import 'package:rxdart/rxdart.dart'; import 'package:sp_scanner/sp_scanner.dart'; class ElectrumWorker { final SendPort sendPort; ElectrumApiProvider? _electrumClient; BasedUtxoNetwork? _network; - bool _isScanning = false; - bool _stopScanRequested = false; + BehaviorSubject>? _scanningStream; ElectrumWorker._(this.sendPort, {ElectrumApiProvider? electrumClient}) : _electrumClient = electrumClient; @@ -47,7 +45,7 @@ class ElectrumWorker { } void handleMessage(dynamic message) async { - print("Worker received message: $message"); + print("Worker: received message: $message"); try { Map messageJson; @@ -105,27 +103,15 @@ class ElectrumWorker { ); break; case ElectrumWorkerMethods.stopScanningMethod: + print("Worker: received message: $message"); await _handleStopScanning( ElectrumWorkerStopScanningRequest.fromJson(messageJson), ); break; case ElectrumRequestMethods.tweaksSubscribeMethod: - if (_isScanning) { - _stopScanRequested = false; - } - - if (!_stopScanRequested) { - await _handleScanSilentPayments( - ElectrumWorkerTweaksSubscribeRequest.fromJson(messageJson), - ); - } else { - _stopScanRequested = false; - _sendResponse( - ElectrumWorkerTweaksSubscribeResponse( - result: TweaksSyncResponse(syncStatus: SyncedSyncStatus()), - ), - ); - } + await _handleScanSilentPayments( + ElectrumWorkerTweaksSubscribeRequest.fromJson(messageJson), + ); break; case ElectrumRequestMethods.estimateFeeMethod: @@ -550,28 +536,25 @@ class ElectrumWorker { } Future _handleStopScanning(ElectrumWorkerStopScanningRequest request) async { - _stopScanRequested = true; + _scanningStream?.close(); + _scanningStream = null; _sendResponse( ElectrumWorkerStopScanningResponse(result: true, id: request.id), ); } Future _handleScanSilentPayments(ElectrumWorkerTweaksSubscribeRequest request) async { - _isScanning = true; final scanData = request.scanData; - // TODO: confirmedSwitch use new connection - // final _electrumClient = await ElectrumApiProvider.connect( - // ElectrumTCPService.connect( - // Uri.parse("tcp://electrs.cakewallet.com:50001"), - // onConnectionStatusChange: (status) { - // _sendResponse( - // ElectrumWorkerConnectionResponse(status: status, id: request.id), - // ); - // }, - // ), - // ); + var scanningClient = _electrumClient; + if (scanData.shouldSwitchNodes) { + scanningClient = await ElectrumApiProvider.connect( + ElectrumTCPService.connect( + Uri.parse("tcp://electrs.cakewallet.com:50001"), + ), + ); + } int syncHeight = scanData.height; int initialSyncHeight = syncHeight; @@ -587,6 +570,15 @@ class ElectrumWorker { }, ); + int getCountPerRequest(int syncHeight) { + if (scanData.isSingleScan) { + return 1; + } + + final amountLeft = scanData.chainTip - syncHeight + 1; + return amountLeft; + } + // Initial status UI update, send how many blocks in total to scan _sendResponse(ElectrumWorkerTweaksSubscribeResponse( result: TweaksSyncResponse( @@ -597,17 +589,16 @@ class ElectrumWorker { final req = ElectrumTweaksSubscribe( height: syncHeight, - count: 1, + count: getCountPerRequest(syncHeight), historicalMode: false, ); - final stream = await _electrumClient!.subscribe(req); + _scanningStream = await scanningClient!.subscribe(req); void listenFn(Map event, ElectrumTweaksSubscribe req) { final response = req.onResponse(event); - if (_stopScanRequested || response == null) { - _stopScanRequested = false; - _isScanning = false; + + if (response == null || _scanningStream == null) { return; } @@ -623,10 +614,10 @@ class ElectrumWorker { final nextHeight = syncHeight + 1; if (nextHeight <= scanData.chainTip) { - final nextStream = _electrumClient!.subscribe( + final nextStream = scanningClient!.subscribe( ElectrumTweaksSubscribe( height: nextHeight, - count: 1, + count: getCountPerRequest(nextHeight), historicalMode: false, ), ); @@ -710,6 +701,7 @@ class ElectrumWorker { receivingOutputAddress, labelIndex: 1, // TODO: get actual index/label isUsed: true, + // TODO: use right wallet spendKey: scanData.silentPaymentsWallets.first.b_spend.tweakAdd( BigintUtils.fromBytes(BytesUtils.fromHexString(t_k)), ), @@ -753,24 +745,27 @@ class ElectrumWorker { ), ); - stream?.close(); + _scanningStream?.close(); + _scanningStream = null; return; } } - stream?.listen((event) => listenFn(event, req)); - _isScanning = false; + _scanningStream?.listen((event) => listenFn(event, req)); } Future _handleGetVersion(ElectrumWorkerGetVersionRequest request) async { - _sendResponse(ElectrumWorkerGetVersionResponse( - result: (await _electrumClient!.request( + _sendResponse( + ElectrumWorkerGetVersionResponse( + result: await _electrumClient!.request( ElectrumVersion( clientName: "", - protocolVersion: ["1.4"], + protocolVersion: "1.4", ), - )), - id: request.id)); + ), + id: request.id, + ), + ); } } diff --git a/cw_bitcoin/lib/electrum_worker/methods/tweaks_subscribe.dart b/cw_bitcoin/lib/electrum_worker/methods/tweaks_subscribe.dart index c51670cdc..d84dce667 100644 --- a/cw_bitcoin/lib/electrum_worker/methods/tweaks_subscribe.dart +++ b/cw_bitcoin/lib/electrum_worker/methods/tweaks_subscribe.dart @@ -9,6 +9,7 @@ class ScanData { final Map labels; final List labelIndexes; final bool isSingleScan; + final bool shouldSwitchNodes; ScanData({ required this.silentPaymentsWallets, @@ -19,6 +20,7 @@ class ScanData { required this.labels, required this.labelIndexes, required this.isSingleScan, + required this.shouldSwitchNodes, }); factory ScanData.fromHeight(ScanData scanData, int newHeight) { @@ -31,6 +33,7 @@ class ScanData { labels: scanData.labels, labelIndexes: scanData.labelIndexes, isSingleScan: scanData.isSingleScan, + shouldSwitchNodes: scanData.shouldSwitchNodes, ); } @@ -44,6 +47,7 @@ class ScanData { 'labels': labels, 'labelIndexes': labelIndexes, 'isSingleScan': isSingleScan, + 'shouldSwitchNodes': shouldSwitchNodes, }; } @@ -60,6 +64,7 @@ class ScanData { labels: json['labels'] as Map, labelIndexes: (json['labelIndexes'] as List).map((e) => e as int).toList(), isSingleScan: json['isSingleScan'] as bool, + shouldSwitchNodes: json['shouldSwitchNodes'] as bool, ); } } diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 3eb560ba7..0c402e7dd 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -288,6 +288,11 @@ class CWBitcoin extends Bitcoin { return BitcoinReceivePageOption.fromType(bitcoinWallet.walletAddresses.addressPageType); } + @override + bool isReceiveOptionSP(ReceivePageOption option) { + return option.value == BitcoinReceivePageOption.silent_payments.value; + } + @override bool hasSelectedSilentPayments(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; @@ -610,7 +615,7 @@ class CWBitcoin extends Bitcoin { @override @computed bool getScanningActive(Object wallet) { - final bitcoinWallet = wallet as ElectrumWallet; + final bitcoinWallet = wallet as BitcoinWallet; return bitcoinWallet.silentPaymentsScanningActive; } @@ -620,6 +625,12 @@ class CWBitcoin extends Bitcoin { bitcoinWallet.setSilentPaymentsScanning(active); } + @override + Future allowToSwitchNodesForScanning(Object wallet, bool allow) async { + final bitcoinWallet = wallet as BitcoinWallet; + bitcoinWallet.allowedToSwitchNodesForScanning = allow; + } + @override bool isTestnet(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; diff --git a/lib/di.dart b/lib/di.dart index 19d3e8b8f..a98f474b2 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -472,10 +472,14 @@ Future setup({ getIt.get(), type: type)); - getIt.registerFactory(() => WalletAddressListViewModel( + getIt.registerFactoryParam( + (ReceivePageOption? addressType, _) => WalletAddressListViewModel( appStore: getIt.get(), yatStore: getIt.get(), - fiatConversionStore: getIt.get())); + fiatConversionStore: getIt.get(), + addressType: addressType, + ), + ); getIt.registerFactory(() => BalanceViewModel( appStore: getIt.get(), @@ -704,12 +708,26 @@ Future setup({ getIt.get(param1: pageOption)); }); - getIt.registerFactory( - () => ReceivePage(addressListViewModel: getIt.get())); - getIt.registerFactory(() => AddressPage( - addressListViewModel: getIt.get(), + getIt.registerFactoryParam( + (ReceivePageOption? addressType, _) => ReceivePage( + addressListViewModel: getIt.get( + param1: addressType, + ), + ), + ); + + getIt.registerFactoryParam( + (ReceivePageOption? addressType, _) => AddressPage( + addressListViewModel: getIt.get( + param1: addressType, + ), dashboardViewModel: getIt.get(), - receiveOptionViewModel: getIt.get())); + receiveOptionViewModel: getIt.get( + param1: addressType, + ), + addressType: addressType, + ), + ); getIt.registerFactoryParam( (WalletAddressListItem? item, _) => @@ -904,11 +922,11 @@ Future setup({ getIt.registerFactory(() => WalletKeysViewModel(getIt.get())); getIt.registerFactory(() => WalletKeysPage(getIt.get())); - + getIt.registerFactory(() => AnimatedURModel(getIt.get())); - getIt.registerFactoryParam((String urQr, _) => - AnimatedURPage(getIt.get(), urQr: urQr)); + getIt.registerFactoryParam( + (String urQr, _) => AnimatedURPage(getIt.get(), urQr: urQr)); getIt.registerFactoryParam( (ContactRecord? contact, _) => ContactViewModel(_contactSource, contact: contact)); @@ -1004,8 +1022,8 @@ Future setup({ )); getIt.registerFactory(() => MeldBuyProvider( - wallet: getIt.get().wallet!, - )); + wallet: getIt.get().wallet!, + )); getIt.registerFactoryParam((title, uri) => WebViewPage(title, uri)); @@ -1207,16 +1225,15 @@ Future setup({ final items = args.first as List; final pickAnOption = args[1] as void Function(SelectableOption option)?; final confirmOption = args[2] as void Function(BuildContext contex)?; - return BuyOptionsPage( - items: items, pickAnOption: pickAnOption, confirmOption: confirmOption); + return BuyOptionsPage(items: items, pickAnOption: pickAnOption, confirmOption: confirmOption); }); - getIt.registerFactoryParam, void>((List args, _) { + getIt + .registerFactoryParam, void>((List args, _) { final items = args.first as List; final pickAnOption = args[1] as void Function(SelectableOption option)?; - return PaymentMethodOptionsPage( - items: items, pickAnOption: pickAnOption); + return PaymentMethodOptionsPage(items: items, pickAnOption: pickAnOption); }); getIt.registerFactory(() { @@ -1300,9 +1317,8 @@ Future setup({ getIt.registerFactory( () => CakePayService(getIt.get(), getIt.get())); - getIt.registerFactory( - () => CakePayCardsListViewModel(cakePayService: getIt.get(), - settingsStore: getIt.get())); + getIt.registerFactory(() => CakePayCardsListViewModel( + cakePayService: getIt.get(), settingsStore: getIt.get())); getIt.registerFactory(() => CakePayAuthViewModel(cakePayService: getIt.get())); diff --git a/lib/router.dart b/lib/router.dart index 1382da28f..c461966a6 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -124,6 +124,7 @@ import 'package:cake_wallet/wallet_types.g.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/nano_account.dart'; import 'package:cw_core/node.dart'; +import 'package:cw_core/receive_page_option.dart'; import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/unspent_coin_type.dart'; import 'package:cw_core/wallet_info.dart'; @@ -376,11 +377,21 @@ Route createRoute(RouteSettings settings) { fullscreenDialog: true, builder: (_) => getIt.get()); case Routes.receive: - return CupertinoPageRoute(builder: (_) => getIt.get()); + final args = settings.arguments as Map?; + final addressType = args?['addressType'] as ReceivePageOption?; + + return CupertinoPageRoute( + builder: (_) => getIt.get(param1: addressType), + ); case Routes.addressPage: + final args = settings.arguments as Map?; + final addressType = args?['addressType'] as ReceivePageOption?; + return CupertinoPageRoute( - fullscreenDialog: true, builder: (_) => getIt.get()); + fullscreenDialog: true, + builder: (_) => getIt.get(param1: addressType), + ); case Routes.transactionDetails: return CupertinoPageRoute( @@ -588,7 +599,8 @@ Route createRoute(RouteSettings settings) { case Routes.paymentMethodOptionsPage: final args = settings.arguments as List; - return MaterialPageRoute(builder: (_) => getIt.get(param1: args)); + return MaterialPageRoute( + builder: (_) => getIt.get(param1: args)); case Routes.buyWebView: final args = settings.arguments as List; @@ -751,7 +763,8 @@ Route createRoute(RouteSettings settings) { return MaterialPageRoute(builder: (_) => getIt.get()); case Routes.urqrAnimatedPage: - return MaterialPageRoute(builder: (_) => getIt.get(param1: settings.arguments)); + return MaterialPageRoute( + builder: (_) => getIt.get(param1: settings.arguments)); case Routes.homeSettings: return CupertinoPageRoute( diff --git a/lib/src/screens/dashboard/pages/address_page.dart b/lib/src/screens/dashboard/pages/address_page.dart index c81bca484..f39cd6a6e 100644 --- a/lib/src/screens/dashboard/pages/address_page.dart +++ b/lib/src/screens/dashboard/pages/address_page.dart @@ -32,9 +32,11 @@ class AddressPage extends BasePage { required this.addressListViewModel, required this.dashboardViewModel, required this.receiveOptionViewModel, + ReceivePageOption? addressType, }) : _cryptoAmountFocus = FocusNode(), _formKey = GlobalKey(), - _amountController = TextEditingController() { + _amountController = TextEditingController(), + _addressType = addressType { _amountController.addListener(() { if (_formKey.currentState!.validate()) { addressListViewModel.changeAmount( @@ -49,6 +51,7 @@ class AddressPage extends BasePage { final ReceiveOptionViewModel receiveOptionViewModel; final TextEditingController _amountController; final GlobalKey _formKey; + ReceivePageOption? _addressType; final FocusNode _cryptoAmountFocus; @@ -190,7 +193,11 @@ class AddressPage extends BasePage { if (addressListViewModel.hasAddressList) { return SelectButton( text: addressListViewModel.buttonTitle, - onTap: () => Navigator.of(context).pushNamed(Routes.receive), + onTap: () => Navigator.pushNamed( + context, + Routes.receive, + arguments: {'addressType': _addressType}, + ), textColor: Theme.of(context).extension()!.textColor, color: Theme.of(context).extension()!.syncedBackgroundColor, borderColor: Theme.of(context).extension()!.cardBorderColor, diff --git a/lib/src/screens/dashboard/pages/balance_page.dart b/lib/src/screens/dashboard/pages/balance_page.dart index b16a7b090..ca7688b22 100644 --- a/lib/src/screens/dashboard/pages/balance_page.dart +++ b/lib/src/screens/dashboard/pages/balance_page.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:math'; import 'package:auto_size_text/auto_size_text.dart'; @@ -354,12 +355,108 @@ class CryptoBalanceWidget extends StatelessWidget { ], ), ), - Observer( - builder: (_) => StandardSwitch( - value: dashboardViewModel.silentPaymentsScanningActive, - onTaped: () => _toggleSilentPaymentsScanning(context), + ], + ), + SizedBox(height: 8), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Semantics( + label: S.of(context).receive, + child: OutlinedButton( + onPressed: () { + Navigator.pushNamed( + context, + Routes.addressPage, + arguments: { + 'addressType': bitcoin! + .getBitcoinReceivePageOptions() + .where( + (option) => option.value == "Silent Payments", + ) + .first + }, + ); + }, + style: OutlinedButton.styleFrom( + backgroundColor: Colors.grey.shade400.withAlpha(50), + side: BorderSide( + color: Colors.grey.shade400.withAlpha(50), width: 0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + ), + child: Container( + padding: EdgeInsets.symmetric(vertical: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + height: 30, + width: 30, + 'assets/images/received.png', + color: Theme.of(context) + .extension()! + .balanceAmountColor, + ), + const SizedBox(width: 8), + Text( + S.of(context).receive, + style: TextStyle( + color: Theme.of(context) + .extension()! + .textColor, + ), + ), + ], + ), + ), + ), ), - ) + ), + SizedBox(width: 24), + Expanded( + child: Semantics( + label: S.of(context).scan, + child: OutlinedButton( + onPressed: () => _toggleSilentPaymentsScanning(context), + style: OutlinedButton.styleFrom( + backgroundColor: Colors.grey.shade400.withAlpha(50), + side: BorderSide( + color: Colors.grey.shade400.withAlpha(50), width: 0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20), + ), + ), + child: Container( + padding: EdgeInsets.symmetric(vertical: 12), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Observer( + builder: (_) => StandardSwitch( + value: + dashboardViewModel.silentPaymentsScanningActive, + onTaped: () => + _toggleSilentPaymentsScanning(context), + ), + ), + SizedBox(width: 8), + Text( + S.of(context).scan, + style: TextStyle( + color: Theme.of(context) + .extension()! + .textColor, + ), + ), + ], + ), + ), + ), + ), + ), ], ), ], @@ -466,29 +563,40 @@ class CryptoBalanceWidget extends StatelessWidget { Future _toggleSilentPaymentsScanning(BuildContext context) async { final isSilentPaymentsScanningActive = dashboardViewModel.silentPaymentsScanningActive; final newValue = !isSilentPaymentsScanningActive; - + final willScan = newValue == true; dashboardViewModel.silentPaymentsScanningActive = newValue; - final needsToSwitch = !isSilentPaymentsScanningActive && - await bitcoin!.getNodeIsElectrsSPEnabled(dashboardViewModel.wallet) == false; + if (willScan) { + late bool isElectrsSPEnabled; + try { + isElectrsSPEnabled = await bitcoin! + .getNodeIsElectrsSPEnabled(dashboardViewModel.wallet) + .timeout(const Duration(seconds: 3)); + } on TimeoutException { + isElectrsSPEnabled = false; + } - if (needsToSwitch) { - return showPopUp( + final needsToSwitch = isElectrsSPEnabled == false; + if (needsToSwitch) { + return showPopUp( context: context, builder: (BuildContext context) => AlertWithTwoActions( - alertTitle: S.of(context).change_current_node_title, - alertContent: S.of(context).confirm_silent_payments_switch_node, - rightButtonText: S.of(context).confirm, - leftButtonText: S.of(context).cancel, - actionRightButton: () { - dashboardViewModel.setSilentPaymentsScanning(newValue); - Navigator.of(context).pop(); - }, - actionLeftButton: () { - dashboardViewModel.silentPaymentsScanningActive = isSilentPaymentsScanningActive; - Navigator.of(context).pop(); - }, - )); + alertTitle: S.of(context).change_current_node_title, + alertContent: S.of(context).confirm_silent_payments_switch_node, + rightButtonText: S.of(context).confirm, + leftButtonText: S.of(context).cancel, + actionRightButton: () { + dashboardViewModel.allowSilentPaymentsScanning(true); + dashboardViewModel.setSilentPaymentsScanning(true); + Navigator.of(context).pop(); + }, + actionLeftButton: () { + dashboardViewModel.silentPaymentsScanningActive = isSilentPaymentsScanningActive; + Navigator.of(context).pop(); + }, + ), + ); + } } return dashboardViewModel.setSilentPaymentsScanning(newValue); @@ -1045,10 +1153,9 @@ class BalanceRowWidget extends StatelessWidget { ); }, style: OutlinedButton.styleFrom( - backgroundColor: Colors.grey.shade400 - .withAlpha(50), - side: BorderSide(color: Colors.grey.shade400 - .withAlpha(50), width: 0), + backgroundColor: Colors.grey.shade400.withAlpha(50), + side: + BorderSide(color: Colors.grey.shade400.withAlpha(50), width: 0), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), @@ -1104,10 +1211,9 @@ class BalanceRowWidget extends StatelessWidget { ); }, style: OutlinedButton.styleFrom( - backgroundColor: Colors.grey.shade400 - .withAlpha(50), - side: BorderSide(color: Colors.grey.shade400 - .withAlpha(50), width: 0), + backgroundColor: Colors.grey.shade400.withAlpha(50), + side: + BorderSide(color: Colors.grey.shade400.withAlpha(50), width: 0), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), diff --git a/lib/src/screens/rescan/rescan_page.dart b/lib/src/screens/rescan/rescan_page.dart index 2c1c213c1..a12552712 100644 --- a/lib/src/screens/rescan/rescan_page.dart +++ b/lib/src/screens/rescan/rescan_page.dart @@ -1,5 +1,6 @@ +import 'dart:async'; + import 'package:cake_wallet/bitcoin/bitcoin.dart'; -import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; @@ -35,7 +36,8 @@ class RescanPage extends BasePage { isSilentPaymentsScan: _rescanViewModel.isSilentPaymentsScan, isMwebScan: _rescanViewModel.isMwebScan, doSingleScan: _rescanViewModel.doSingleScan, - hasDatePicker: !_rescanViewModel.isMwebScan,// disable date picker for mweb for now + hasDatePicker: + !_rescanViewModel.isMwebScan, // disable date picker for mweb for now toggleSingleScan: () => _rescanViewModel.doSingleScan = !_rescanViewModel.doSingleScan, walletType: _rescanViewModel.wallet.type, @@ -69,24 +71,32 @@ class RescanPage extends BasePage { Navigator.of(context).pop(); - final needsToSwitch = - await bitcoin!.getNodeIsElectrsSPEnabled(_rescanViewModel.wallet) == false; + late bool isElectrsSPEnabled; + try { + isElectrsSPEnabled = await bitcoin! + .getNodeIsElectrsSPEnabled(_rescanViewModel.wallet) + .timeout(const Duration(seconds: 3)); + } on TimeoutException { + isElectrsSPEnabled = false; + } + final needsToSwitch = isElectrsSPEnabled == false; if (needsToSwitch) { return showPopUp( - context: navigatorKey.currentState!.context, - builder: (BuildContext _dialogContext) => AlertWithTwoActions( - alertTitle: S.of(_dialogContext).change_current_node_title, - alertContent: S.of(_dialogContext).confirm_silent_payments_switch_node, - rightButtonText: S.of(_dialogContext).confirm, - leftButtonText: S.of(_dialogContext).cancel, - actionRightButton: () async { - Navigator.of(_dialogContext).pop(); + context: context, + builder: (BuildContext _dialogContext) => AlertWithTwoActions( + alertTitle: S.of(_dialogContext).change_current_node_title, + alertContent: S.of(_dialogContext).confirm_silent_payments_switch_node, + rightButtonText: S.of(_dialogContext).confirm, + leftButtonText: S.of(_dialogContext).cancel, + actionRightButton: () async { + Navigator.of(_dialogContext).pop(); - _rescanViewModel.rescanCurrentWallet(restoreHeight: height); - }, - actionLeftButton: () => Navigator.of(_dialogContext).pop(), - )); + _rescanViewModel.rescanCurrentWallet(restoreHeight: height); + }, + actionLeftButton: () => Navigator.of(_dialogContext).pop(), + ), + ); } _rescanViewModel.rescanCurrentWallet(restoreHeight: height); diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 808657f66..413bbbdaf 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -428,7 +428,8 @@ abstract class DashboardViewModelBase with Store { // to not cause work duplication, this will do the job as well, it will be slightly less precise // about what happened - but still enough. // if (keys['privateSpendKey'] == List.generate(64, (index) => "0").join("")) "Private spend key is 0", - if (keys['privateViewKey'] == List.generate(64, (index) => "0").join("") && !wallet.isHardwareWallet) + if (keys['privateViewKey'] == List.generate(64, (index) => "0").join("") && + !wallet.isHardwareWallet) "private view key is 0", // if (keys['publicSpendKey'] == List.generate(64, (index) => "0").join("")) "public spend key is 0", if (keys['publicViewKey'] == List.generate(64, (index) => "0").join("")) @@ -454,6 +455,13 @@ abstract class DashboardViewModelBase with Store { @observable bool silentPaymentsScanningActive = false; + @action + void allowSilentPaymentsScanning(bool allow) { + if (hasSilentPayments) { + bitcoin!.allowToSwitchNodesForScanning(wallet, allow); + } + } + @action void setSilentPaymentsScanning(bool active) { silentPaymentsScanningActive = active; diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index d263b2a11..9d8136d75 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -25,6 +25,7 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/amount_converter.dart'; import 'package:cw_core/currency.dart'; +import 'package:cw_core/receive_page_option.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:intl/intl.dart'; import 'package:mobx/mobx.dart'; @@ -209,6 +210,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo required AppStore appStore, required this.yatStore, required this.fiatConversionStore, + ReceivePageOption? addressType, }) : _baseItems = [], selectedCurrency = walletTypeToCryptoCurrency(appStore.wallet!.type), _cryptoNumberFormat = NumberFormat(_cryptoNumberPattern), @@ -216,6 +218,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo .contains(appStore.wallet!.type), amount = '', _settingsStore = appStore.settingsStore, + _addressType = addressType, super(appStore: appStore) { _init(); } @@ -234,6 +237,7 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo final FiatConversionStore fiatConversionStore; final SettingsStore _settingsStore; + final ReceivePageOption? _addressType; double? _fiatRate; String _rawAmount = ''; @@ -264,8 +268,19 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo WalletType get type => wallet.type; @computed - WalletAddressListItem get address => - WalletAddressListItem(address: wallet.walletAddresses.address, isPrimary: false); + WalletAddressListItem get address { + if (_addressType != null) { + final shouldForceSP = _addressType != null && bitcoin!.isReceiveOptionSP(_addressType!); + if (shouldForceSP) { + return WalletAddressListItem( + address: bitcoin!.getSilentPaymentAddresses(wallet).first.address, + isPrimary: true, + ); + } + } + + return WalletAddressListItem(address: wallet.walletAddresses.address, isPrimary: false); + } @computed PaymentURI get uri { @@ -354,7 +369,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo } if (isElectrumWallet) { - if (bitcoin!.hasSelectedSilentPayments(wallet)) { + final hasSelectedSP = bitcoin!.hasSelectedSilentPayments(wallet); + final shouldForceSP = _addressType != null && bitcoin!.isReceiveOptionSP(_addressType!); + + if (hasSelectedSP || shouldForceSP) { final addressItems = bitcoin!.getSilentPaymentAddresses(wallet).map((address) { final isPrimary = address.id == 0; diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 69c7f73c7..aa228308b 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "حفظ كلمة المرور الاحتياطية", "save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ", "saved_the_trade_id": "لقد تم حفظ معرف العملية", + "scan": "مسح", "scan_one_block": "مسح كتلة واحدة", "scan_qr_code": "امسح رمز QR ضوئيًا", "scan_qr_code_to_get_address": "امسح ال QR للحصول على العنوان", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index ca1b17ec6..e43d7e1b8 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "Запазване на паролата за възстановяване", "save_to_downloads": "Запазване в Изтегляния", "saved_the_trade_id": "Запазих trade ID-то", + "scan": "Сканиране", "scan_one_block": "Сканирайте един блок", "scan_qr_code": "Сканирайте QR кода, за да получите адреса", "scan_qr_code_to_get_address": "Сканирайте QR кода, за да получите адреса", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index f5e86ecdb..89160858a 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "Uložit heslo pro zálohy", "save_to_downloads": "Uložit do Stažených souborů", "saved_the_trade_id": "Uložil jsem si ID transakce (trade ID)", + "scan": "Skenovat", "scan_one_block": "Prohledejte jeden blok", "scan_qr_code": "Naskenujte QR kód pro získání adresy", "scan_qr_code_to_get_address": "Prohledejte QR kód a získejte adresu", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index d33aa8cc1..267bcd1cd 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -595,6 +595,7 @@ "save_backup_password_alert": "Sicherungskennwort speichern", "save_to_downloads": "Unter „Downloads“ speichern", "saved_the_trade_id": "Ich habe die Handels-ID gespeichert", + "scan": "Scan", "scan_one_block": "Einen Block scannen", "scan_qr_code": "QR-Code scannen", "scan_qr_code_to_get_address": "Scannen Sie den QR-Code, um die Adresse zu erhalten", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 3669bd64d..876213c95 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "Save backup password", "save_to_downloads": "Save to Downloads", "saved_the_trade_id": "I've saved the trade ID", + "scan": "Scan", "scan_one_block": "Scan one block", "scan_qr_code": "Scan QR code", "scan_qr_code_to_get_address": "Scan the QR code to get the address", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index abdf69b44..913925fb1 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -595,6 +595,7 @@ "save_backup_password_alert": "Guardar contraseña de respaldo", "save_to_downloads": "Guardar en Descargas", "saved_the_trade_id": "He salvado comercial ID", + "scan": "Escanear", "scan_one_block": "Escanear un bloque", "scan_qr_code": "Escanear código QR", "scan_qr_code_to_get_address": "Escanea el código QR para obtener la dirección", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 0661582f3..8232c9b7f 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "Enregistrer le mot de passe de sauvegarde", "save_to_downloads": "Enregistrer dans les téléchargements", "saved_the_trade_id": "J'ai sauvegardé l'ID d'échange", + "scan": "Balayage", "scan_one_block": "Scanner un bloc", "scan_qr_code": "Scannez le QR code", "scan_qr_code_to_get_address": "Scannez le QR code pour obtenir l'adresse", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 76a73ca8e..9a237eea1 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -596,6 +596,7 @@ "save_backup_password_alert": "Ajiye kalmar sirri ta ajiya", "save_to_downloads": "Ajiye zuwa Zazzagewa", "saved_the_trade_id": "Na ajiye ID na ciniki", + "scan": "Scan", "scan_one_block": "Duba toshe daya", "scan_qr_code": "Gani QR kodin", "scan_qr_code_to_get_address": "Duba lambar QR don samun adireshin", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index ee0f77da7..4845c1ce7 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -596,6 +596,7 @@ "save_backup_password_alert": "बैकअप पासवर्ड सेव करें", "save_to_downloads": "डाउनलोड में सहेजें", "saved_the_trade_id": "मैंने व्यापार बचा लिया है ID", + "scan": "स्कैन", "scan_one_block": "एक ब्लॉक को स्कैन करना", "scan_qr_code": "स्कैन क्यू आर कोड", "scan_qr_code_to_get_address": "पता प्राप्त करने के लिए QR कोड स्कैन करें", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 9ce575ec1..0da2b2990 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "Spremi lozinku za sigurnosnu kopiju", "save_to_downloads": "Spremi u Preuzimanja", "saved_the_trade_id": "Spremio/la sam transakcijski ID", + "scan": "Skenirati", "scan_one_block": "Skenirajte jedan blok", "scan_qr_code": "Skenirajte QR kod", "scan_qr_code_to_get_address": "Skeniraj QR kod za dobivanje adrese", diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index 2d3c336e3..c1faf5c15 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "Պահպանել կրկնօրինակի գաղտնաբառը", "save_to_downloads": "Պահպանել ներբեռնումներում", "saved_the_trade_id": "Ես պահպանել եմ առևտրի ID-ն", + "scan": "Սկանավորել", "scan_one_block": "Սկանավորել մեկ բլոկ", "scan_qr_code": "Սկանավորել QR կոդ", "scan_qr_code_to_get_address": "Սկանավորել QR կոդը հասցեն ստանալու համար", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 5b1c1c993..14199976b 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -597,6 +597,7 @@ "save_backup_password_alert": "Simpan kata sandi cadangan", "save_to_downloads": "Simpan ke Unduhan", "saved_the_trade_id": "Saya telah menyimpan ID perdagangan", + "scan": "Pindai", "scan_one_block": "Pindai satu blok", "scan_qr_code": "Scan kode QR untuk mendapatkan alamat", "scan_qr_code_to_get_address": "Pindai kode QR untuk mendapatkan alamat", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 9daafca74..c11a9edd8 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -596,6 +596,7 @@ "save_backup_password_alert": "Salva password Backup", "save_to_downloads": "Salva in Download", "saved_the_trade_id": "Ho salvato l'ID dello scambio", + "scan": "Scansione", "scan_one_block": "Scansionare un blocco", "scan_qr_code": "Scansiona il codice QR", "scan_qr_code_to_get_address": "Scansiona il codice QR per ottenere l'indirizzo", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 31882a7fb..f6fcfee59 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -595,6 +595,7 @@ "save_backup_password_alert": "バックアップパスワードを保存する", "save_to_downloads": "ダウンロードに保存", "saved_the_trade_id": "取引IDを保存しました", + "scan": "スキャン", "scan_one_block": "1つのブロックをスキャンします", "scan_qr_code": "QRコードをスキャン", "scan_qr_code_to_get_address": "QRコードをスキャンして住所を取得します", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 905b1fb8e..e34b9b08f 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -595,6 +595,7 @@ "save_backup_password_alert": "백업 비밀번호 저장", "save_to_downloads": "다운로드에 저장", "saved_the_trade_id": "거래 ID를 저장했습니다", + "scan": "주사", "scan_one_block": "하나의 블록을 스캔하십시오", "scan_qr_code": "QR 코드 스캔", "scan_qr_code_to_get_address": "QR 코드를 스캔하여 주소를 얻습니다.", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 52df23964..219f9618b 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "အရန်စကားဝှက်ကို သိမ်းဆည်းပါ။", "save_to_downloads": "ဒေါင်းလုဒ်များထံ သိမ်းဆည်းပါ။", "saved_the_trade_id": "ကုန်သွယ်မှု ID ကို သိမ်းဆည်းပြီးပါပြီ။", + "scan": "စကင်ဖတ်", "scan_one_block": "တစ်ကွက်ကိုစကင်ဖတ်စစ်ဆေးပါ", "scan_qr_code": "QR ကုဒ်ကို စကင်န်ဖတ်ပါ။", "scan_qr_code_to_get_address": "လိပ်စာရယူရန် QR ကုဒ်ကို စကင်န်ဖတ်ပါ။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 0b9d1e7ad..d0d54c70b 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "Bewaar back-upwachtwoord", "save_to_downloads": "Opslaan in downloads", "saved_the_trade_id": "Ik heb de ruil-ID opgeslagen", + "scan": "Scannen", "scan_one_block": "Scan een blok", "scan_qr_code": "Scan QR-code", "scan_qr_code_to_get_address": "Scan de QR-code om het adres te krijgen", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 4243df066..c1507a85a 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "Zapisz hasło kopii zapasowej", "save_to_downloads": "Zapisz w Pobranych", "saved_the_trade_id": "Zapisałem ID", + "scan": "Skandować", "scan_one_block": "Zeskanuj jeden blok", "scan_qr_code": "Skanowania QR code", "scan_qr_code_to_get_address": "Zeskanuj kod QR, aby uzyskać adres", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 8699a106a..f5a2e10e7 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -596,6 +596,7 @@ "save_backup_password_alert": "Salvar senha de backup", "save_to_downloads": "Salvar em Downloads", "saved_the_trade_id": "ID da troca salvo", + "scan": "Scan", "scan_one_block": "Escanear um bloco", "scan_qr_code": "Escanear código QR", "scan_qr_code_to_get_address": "Digitalize o código QR para obter o endereço", @@ -956,4 +957,4 @@ "you_will_get": "Converter para", "you_will_send": "Converter de", "yy": "aa" -} +} \ No newline at end of file diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 0f1974f29..e50ebcb5d 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -595,6 +595,7 @@ "save_backup_password_alert": "Сохранить пароль резервной копии", "save_to_downloads": "Сохранить в загрузках", "saved_the_trade_id": "Я сохранил ID сделки", + "scan": "Сканирование", "scan_one_block": "Сканируйте один блок", "scan_qr_code": "Сканировать QR-код", "scan_qr_code_to_get_address": "Отсканируйте QR-код для получения адреса", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 96dcb91b4..d44ae1108 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "บันทึกรหัสผ่านสำรอง", "save_to_downloads": "บันทึกลงดาวน์โหลด", "saved_the_trade_id": "ฉันได้บันทึก ID ของการซื้อขายแล้ว", + "scan": "สแกน", "scan_one_block": "สแกนหนึ่งบล็อก", "scan_qr_code": "สแกนรหัส QR", "scan_qr_code_to_get_address": "สแกน QR code เพื่อรับที่อยู่", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 7c5ff15d3..7d8ff4b0b 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "I-save ang backup na password", "save_to_downloads": "I-save sa mga Pag-download", "saved_the_trade_id": "Nai-save ko na ang trade ID", + "scan": "I -scan", "scan_one_block": "I-scan ang isang bloke", "scan_qr_code": "I-scan ang QR code", "scan_qr_code_to_get_address": "I-scan ang QR code upang makuha ang address", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 53e288ae5..df3dba146 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "Yedek parolasını kaydet", "save_to_downloads": "İndirilenlere Kaydet", "saved_the_trade_id": "Takas ID'imi kaydettim", + "scan": "Taramak", "scan_one_block": "Bir bloğu tara", "scan_qr_code": "QR kodunu tarayın", "scan_qr_code_to_get_address": "Adresi getirmek için QR kodunu tara", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 3bcdf74ff..35caa6ffc 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -595,6 +595,7 @@ "save_backup_password_alert": "Зберегти пароль резервної копії", "save_to_downloads": "Зберегти до завантажень", "saved_the_trade_id": "Я зберіг ID операції", + "scan": "Сканувати", "scan_one_block": "Сканувати один блок", "scan_qr_code": "Відскануйте QR-код", "scan_qr_code_to_get_address": "Скануйте QR-код для одержання адреси", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 2742e21bb..fb07eec66 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -596,6 +596,7 @@ "save_backup_password_alert": "بیک اپ پاس ورڈ محفوظ کریں۔", "save_to_downloads": "۔ﮟﯾﺮﮐ ﻅﻮﻔﺤﻣ ﮟﯿﻣ ﺯﮈﻮﻟ ﻥﺅﺍﮈ", "saved_the_trade_id": "میں نے تجارتی ID محفوظ کر لی ہے۔", + "scan": "اسکین", "scan_one_block": "ایک بلاک اسکین کریں", "scan_qr_code": "پتہ حاصل کرنے کے لیے QR کوڈ اسکین کریں۔", "scan_qr_code_to_get_address": "پتہ حاصل کرنے کے لئے QR کوڈ کو اسکین کریں", diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb index a805809c7..7ed592bf9 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -593,6 +593,7 @@ "save_backup_password_alert": "Lưu mật khẩu sao lưu", "save_to_downloads": "Lưu vào Tải xuống", "saved_the_trade_id": "Tôi đã lưu ID giao dịch", + "scan": "Quét", "scan_one_block": "Quét một khối", "scan_qr_code": "Quét mã QR", "scan_qr_code_to_get_address": "Quét mã QR để nhận địa chỉ", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index e4dc69363..3c71e8400 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -595,6 +595,7 @@ "save_backup_password_alert": "Pamọ́ ọ̀rọ̀ aṣínà ti ẹ̀dà", "save_to_downloads": "Fipamọ si Awọn igbasilẹ", "saved_the_trade_id": "Mo ti pamọ́ àmì ìdánimọ̀ pàṣípààrọ̀", + "scan": "Ọlọjẹ", "scan_one_block": "Ọlọjẹ ọkan bulọki", "scan_qr_code": "Yan QR koodu", "scan_qr_code_to_get_address": "Ṣayẹwo koodu QR naa lati gba adirẹsi naa", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 90b67252c..da9b004ed 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -594,6 +594,7 @@ "save_backup_password_alert": "保存备份密码", "save_to_downloads": "保存到下载", "saved_the_trade_id": "我已经保存了交易编号", + "scan": "扫描", "scan_one_block": "扫描一个街区", "scan_qr_code": "扫描二维码", "scan_qr_code_to_get_address": "扫描二维码获取地址", diff --git a/tool/configure.dart b/tool/configure.dart index 9254fe311..10ceaf0b1 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -227,11 +227,13 @@ abstract class Bitcoin { List getBitcoinReceivePageOptions(); List getLitecoinReceivePageOptions(); BitcoinAddressType getBitcoinAddressType(ReceivePageOption option); + bool isReceiveOptionSP(ReceivePageOption option); bool hasSelectedSilentPayments(Object wallet); bool isBitcoinReceivePageOption(ReceivePageOption option); BitcoinAddressType getOptionToType(ReceivePageOption option); bool hasTaprootInput(PendingTransaction pendingTransaction); bool getScanningActive(Object wallet); + Future allowToSwitchNodesForScanning(Object wallet, bool allow); Future setScanningActive(Object wallet, bool active); bool isTestnet(Object wallet);