Hide broken options in Bitcoin wallets without a private key (#3532)

* hide unavailable receive page options * hide broken options in wallets without a private key * hide exporting unavailable logs * hide sign/verify on airgapped wallets * hide mweb on hwws * hide mweb ad for hww

malik1004x committed Aug 24, 2026 at 22:24 UTC 9b50011e75f7861ccb21f818a0ba4c5f5ab34590
9 files changed +54 -7
cw_bitcoin/lib/bitcoin_wallet.dart
+21
@@ -7,6 +7,7 @@ import 'package:cw_bitcoin/.secrets.g.dart' as secrets;
7 import 'package:cw_bitcoin/address_from_output.dart';
8 import 'package:cw_bitcoin/bitcoin_address_record.dart';
9 import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
10 +import "package:cw_bitcoin/bitcoin_receive_page_option.dart";
11 import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart';
12 import 'package:cw_bitcoin/bitcoin_wallet_addresses.dart';
13 import 'package:cw_bitcoin/electrum_balance.dart';
@@ -33,6 +34,7 @@ import 'package:cw_core/output_info.dart';
34 import 'package:cw_core/payjoin_session.dart';
35 import 'package:cw_core/pending_transaction.dart';
36 import 'package:cw_core/sync_status.dart';
37 +import "package:cw_core/receive_page_option.dart";
38 import 'package:cw_core/unspent_coin_type.dart';
39 import 'package:cw_core/unspent_coins_info.dart';
40 import 'package:cw_core/utils/print_verbose.dart';
@@ -417,6 +419,12 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
419
420 late final PayjoinManager payjoinManager;
421
422 + @override
423 + bool get hasPayjoinSupport => keys.privateKey.isNotEmpty;
424 +
425 + @override
426 + bool get hasLightningSupport => lightningWallet?.sdk != null;
427 +
428 bool get isPayjoinAvailable => unspentCoinsInfo.values
429 .where((element) => element.walletId == id && element.isSending && !element.isFrozen)
430 .isNotEmpty;
@@ -673,4 +681,17 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
681
682 return super.signMessage(message, address: address);
683 }
684 +
685 + @override
686 + bool receiveOptionAvailable(ReceivePageOption option) {
687 + if(option == BitcoinReceivePageOption.lightning) {
688 + return hasLightningSupport;
689 + }
690 +
691 + if(option == BitcoinReceivePageOption.silent_payments) {
692 + return hasSilentPaymentsScanning;
693 + }
694 +
695 + return true;
696 + }
697 }
cw_bitcoin/lib/electrum_wallet.dart
+1
@@ -407,6 +407,7 @@ abstract class ElectrumWalletBase
407 @override
408 bool isTestnet;
409
410 + @override
411 bool get hasSilentPaymentsScanning => type == WalletType.bitcoin && keys.privateKey.isNotEmpty;
412
413 @observable
cw_core/lib/wallet_base.dart
+11
@@ -1,3 +1,4 @@
1 +import "package:cw_core/receive_page_option.dart";
2 import 'package:mobx/mobx.dart';
3 import 'package:cw_core/balance.dart';
4 import 'package:cw_core/transaction_info.dart';
@@ -136,4 +137,14 @@ abstract class WalletBase<BalanceType extends Balance, HistoryType extends Trans
137 /// Each wallet implementation should override this to make a single, efficient call
138 /// Returns true if the node is healthy, false otherwise
139 Future<bool> checkNodeHealth();
140 +
141 + bool get hasPayjoinSupport => false;
142 + bool get hasLightningSupport => false;
143 + bool get hasSilentPaymentsScanning => false;
144 +
145 + // hardware wallet - bitbox, ledger, trezor.
146 + // we also have airgap wallets but those can't sign messages
147 + bool get canSignMessages => walletInfo.hardwareWalletType == null || walletInfo.isHardwareWallet;
148 +
149 + bool receiveOptionAvailable(ReceivePageOption option) => true;
150 }
lib/src/screens/settings/other_settings_page.dart
+3
@@ -143,6 +143,7 @@ class OtherSettingsPage extends BasePage {
143 },
144 }),
145 ),
146 + if(_otherSettingsViewModel.hasSignVerify)
147 ListItemRegularRow(
148 keyValue: "security_backup_page_sign_and_verify",
149 label: S.of(context).sign_verify_title,
@@ -152,10 +153,12 @@ class OtherSettingsPage extends BasePage {
153 ],
154 if (_otherSettingsViewModel.walletType == WalletType.bitcoin)
155 "btc_logging": [
156 + if(_otherSettingsViewModel.hasLightning)
157 ListItemRegularRow(
158 keyValue: "export_lightning_logs",
159 label: S.of(context).export_lightning_logs,
160 onTap: () => onExportLNLog(context)),
161 + if(_otherSettingsViewModel.hasPayjoin)
162 ListItemRegularRow(
163 keyValue: "export_payjoin_logs",
164 label: S.of(context).export_payjoin_logs,
lib/src/screens/settings/privacy_page.dart
+1 -1
@@ -76,7 +76,7 @@ class PrivacyPage extends BasePage {
76 }),
77 ],
78 "": [
79 - if (_privacySettingsViewModel.isBitcoin)
79 + if (_privacySettingsViewModel.hasSilentPaymentsScanning)
80 ListItemRegularRow(
81 iconPath: "assets/new-ui/settings_row_icons/silent-payments.svg",
82 keyValue: "silent_payments",
lib/view_model/dashboard/dashboard_view_model.dart
+1 -1
@@ -635,7 +635,7 @@ abstract class DashboardViewModelBase with Store {
635
636 if (settingsStore.mwebAdDismissed) return false;
637
638 - return Platform.isAndroid || Platform.isIOS;
638 + return (Platform.isAndroid || Platform.isIOS) && !wallet.isHardwareWallet;
639 }
640
641 @action
lib/view_model/dashboard/receive_option_view_model.dart
+2 -1
@@ -35,7 +35,8 @@ abstract class ReceiveOptionViewModelBase with Store {
35 ReceivePageOption selectedReceiveOption;
36
37 @computed
38 - List<ReceivePageOption> get options => _wallet.walletAddresses.receivePageOptions;
38 + List<ReceivePageOption> get options =>
39 + _wallet.walletAddresses.receivePageOptions.where(_wallet.receiveOptionAvailable).toList();
40
41 String get walletTypeString => walletTypeToString(_wallet.type);
42
lib/view_model/settings/other_settings_view_model.dart
+6
@@ -47,6 +47,8 @@ abstract class OtherSettingsViewModelBase with Store {
47 final SettingsStore _settingsStore;
48 final SendViewModel sendViewModel;
49
50 + bool get hasSignVerify => _wallet.canSignMessages;
51 +
52 @computed
53 TransactionPriority get transactionPriority {
54 final priority = _settingsStore.getPriority(walletType, chainId: chainId);
@@ -173,6 +175,10 @@ abstract class OtherSettingsViewModelBase with Store {
175 return null;
176 }
177
178 + bool get hasPayjoin => _wallet.hasPayjoinSupport;
179 +
180 + bool get hasLightning => _wallet.hasLightningSupport;
181 +
182 Future<File?> getLightningLog() async {
183 final path = await pathForWalletDir(name: _wallet.name, type: walletType);
184 final logFile = File("$path/lightning.log");
lib/view_model/settings/privacy_settings_view_model.dart
+8 -4
@@ -25,7 +25,12 @@ abstract class PrivacySettingsViewModelBase with Store {
25 bool get isBitcoin => _wallet.type == WalletType.bitcoin;
26
27 @computed
28 - bool get hasMWEB => _wallet.type == WalletType.litecoin && (Platform.isIOS || Platform.isAndroid);
28 + bool get hasSilentPaymentsScanning => _wallet.hasSilentPaymentsScanning;
29 +
30 + @computed
31 + bool get hasMWEB =>
32 + _wallet.type == WalletType.litecoin && (Platform.isIOS || Platform.isAndroid) &&
33 + !_wallet.isHardwareWallet;
34
35 @computed
36 bool get isAutoGenerateSubaddressesEnabled =>
@@ -108,11 +113,10 @@ abstract class PrivacySettingsViewModelBase with Store {
113 bool get usePayjoin => _settingsStore.usePayjoin;
114
115 @computed
111 - bool get canUsePayjoin => _wallet.type == WalletType.bitcoin && DeviceInfo.instance.isMobile;
116 + bool get canUsePayjoin => _wallet.hasPayjoinSupport && DeviceInfo.instance.isMobile;
117
118 @computed
114 - bool get canUseLightning =>
115 - _wallet.type == WalletType.bitcoin && !Platform.isWindows && !Platform.isLinux;
119 + bool get canUseLightning => _wallet.hasLightningSupport;
120
121 @computed
122 bool get useLightning => _wallet.type == WalletType.bitcoin && bitcoin!.useLightning(_wallet);