From 9ac00e2ed0a1f8f8e7d0d0954ae7d495089ead65 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 2 Jul 2024 23:08:37 -0500 Subject: [PATCH 1/6] add reuseAddress pref --- lib/utilities/prefs.dart | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index 78a461ec7..585bdc1fc 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -1131,4 +1131,30 @@ class Prefs extends ChangeNotifier { ) as bool? ?? false; } + + // Reuse addresses (ie., don't generate new addresses by default). + + bool _reuseAddress = false; + + bool get reuseAddress => _reuseAddress; + + set reuseAddress(bool reuseAddress) { + if (_reuseAddress != reuseAddress) { + DB.instance.put( + boxName: DB.boxNamePrefs, + key: "reuseAddress", + value: reuseAddress, + ); + _reuseAddress = reuseAddress; + notifyListeners(); + } + } + + Future _getReuseAddress() async { + return await DB.instance.get( + boxName: DB.boxNamePrefs, + key: "reuseAddress", + ) as bool? ?? + true; + } } From e88e0fb5b737e32c301aa3e3ad459d49b1d63224 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Tue, 2 Jul 2024 23:40:59 -0500 Subject: [PATCH 2/6] add reuseAddress pref to advanced settings menu now with warning dialog! but why do you not toggle off when cancel is clicked? --- .../advanced_settings_view.dart | 214 ++++++++++++++++++ .../advanced_settings/advanced_settings.dart | 210 +++++++++++++++++ 2 files changed, 424 insertions(+) diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart index 3d5413798..99b724076 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart @@ -16,11 +16,17 @@ import '../../../../providers/global/prefs_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; import '../../../../utilities/text_styles.dart'; +import '../../../../utilities/util.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/choose_coin_view.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; +import '../../../../widgets/desktop/desktop_dialog.dart'; +import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; +import '../../../../widgets/desktop/primary_button.dart'; +import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; +import '../../../../widgets/stack_dialog.dart'; import '../../../stack_privacy_calls.dart'; import 'debug_view.dart'; import 'manage_coin_units/manage_coin_units_view.dart'; @@ -182,6 +188,214 @@ class AdvancedSettingsView extends StatelessWidget { }, ), ), + // reuseAddress pref. + const SizedBox( + height: 8, + ), + RoundedWhiteContainer( + child: Consumer( + builder: (_, ref, __) { + final reuseAddress = + ref.watch(prefsChangeNotifierProvider.select( + (value) => value.reuseAddress, + )); + return RawMaterialButton( + // splashColor: Theme.of(context).extension()!.highlight, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + ), + onPressed: null, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Reuse address", + style: STextStyles.titleBold12(context), + textAlign: TextAlign.left, + ), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: reuseAddress, + onValueChanged: (newValue) { + if (newValue) { + showDialog( + context: context, + builder: (context) { + final isDesktop = Util.isDesktop; + return isDesktop + ? DesktopDialog( + maxWidth: 576, + child: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Padding( + padding: + const EdgeInsets + .only( + left: 32), + child: Text( + "Warning!", + style: STextStyles + .desktopH3( + context), + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Padding( + padding: + const EdgeInsets.only( + top: 8, + left: 32, + right: 32, + bottom: 32, + ), + child: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + Text( + "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", + style: STextStyles + .desktopTextSmall( + context), + ), + const SizedBox( + height: 43, + ), + Row( + children: [ + Expanded( + child: + SecondaryButton( + buttonHeight: + ButtonHeight + .l, + onPressed: + () { + Navigator.of( + context) + .pop( + false); + }, + label: + "Cancel", + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: + PrimaryButton( + buttonHeight: + ButtonHeight + .l, + onPressed: + () { + ref.read(prefsChangeNotifierProvider).reuseAddress = + newValue; + Navigator.of( + context) + .pop( + true); + }, + label: + "Continue", + ), + ), + ], + ), + ], + ), + ), + ], + ), + ) + : WillPopScope( + onWillPop: () async { + return true; + }, + child: StackDialog( + title: "Warning!", + message: + "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", + leftButton: TextButton( + style: Theme.of(context) + .extension< + StackColors>()! + .getSecondaryEnabledButtonStyle( + context), + child: Text( + "Cancel", + style: STextStyles + .itemSubtitle12( + context), + ), + onPressed: () { + Navigator.of(context) + .pop(false); + }, + ), + rightButton: TextButton( + style: Theme.of(context) + .extension< + StackColors>()! + .getPrimaryEnabledButtonStyle( + context), + child: Text( + "Continue", + style: STextStyles.button( + context), + ), + onPressed: () { + Navigator.of(context) + .pop(true); + }, + ), + ), + ); + }, + ).then((confirmed) { + if (confirmed == true) { + ref + .read(prefsChangeNotifierProvider) + .reuseAddress = true; + } else { + ref + .read(prefsChangeNotifierProvider) + .reuseAddress = false; + } + }); + } else { + ref + .read(prefsChangeNotifierProvider) + .reuseAddress = newValue; + } + }, + ), + ), + ], + ), + ), + ); + }, + ), + ), const SizedBox( height: 8, ), diff --git a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart index 029c5e294..9fe5ee8d0 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart @@ -17,9 +17,14 @@ import '../../../../providers/global/prefs_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; import '../../../../utilities/text_styles.dart'; +import '../../../../utilities/util.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; +import '../../../../widgets/desktop/desktop_dialog.dart'; +import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../widgets/desktop/primary_button.dart'; +import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; +import '../../../../widgets/stack_dialog.dart'; import 'debug_info_dialog.dart'; import 'desktop_manage_block_explorers_dialog.dart'; import 'stack_privacy_dialog.dart'; @@ -37,6 +42,11 @@ class _AdvancedSettings extends ConsumerState { @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); + + final reuseAddress = ref.watch(prefsChangeNotifierProvider.select( + (value) => value.reuseAddress, + )); + return SingleChildScrollView( child: Column( children: [ @@ -161,6 +171,206 @@ class _AdvancedSettings extends ConsumerState { ], ), ), + // reuseAddress pref. + const Padding( + padding: EdgeInsets.all(10.0), + child: Divider( + thickness: 0.5, + ), + ), + Padding( + padding: const EdgeInsets.all(10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Reuse addresses", + style: STextStyles.desktopTextExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + textAlign: TextAlign.left, + ), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: reuseAddress, + onValueChanged: (newValue) { + if (newValue) { + showDialog( + context: context, + builder: (context) { + final isDesktop = Util.isDesktop; + return isDesktop + ? DesktopDialog( + maxWidth: 576, + child: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment + .spaceBetween, + children: [ + Padding( + padding: + const EdgeInsets + .only( + left: 32), + child: Text( + "Warning!", + style: STextStyles + .desktopH3( + context), + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Padding( + padding: + const EdgeInsets.only( + top: 8, + left: 32, + right: 32, + bottom: 32, + ), + child: Column( + mainAxisSize: + MainAxisSize.min, + children: [ + Text( + "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", + style: STextStyles + .desktopTextSmall( + context), + ), + const SizedBox( + height: 43, + ), + Row( + children: [ + Expanded( + child: + SecondaryButton( + buttonHeight: + ButtonHeight + .l, + onPressed: + () { + Navigator.of( + context) + .pop(); + }, + label: + "Cancel", + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: + PrimaryButton( + buttonHeight: + ButtonHeight + .l, + onPressed: + () { + ref.read(prefsChangeNotifierProvider).reuseAddress = + newValue; + Navigator.of( + context) + .pop(); + }, + label: + "Continue", + ), + ), + ], + ), + ], + ), + ), + ], + ), + ) + : WillPopScope( + onWillPop: () async { + return true; + }, + child: StackDialog( + title: "Warning!", + message: + "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", + leftButton: TextButton( + style: Theme.of(context) + .extension< + StackColors>()! + .getSecondaryEnabledButtonStyle( + context), + child: Text( + "Cancel", + style: STextStyles + .itemSubtitle12( + context), + ), + onPressed: () { + Navigator.of(context) + .pop(); + }, + ), + rightButton: TextButton( + style: Theme.of(context) + .extension< + StackColors>()! + .getPrimaryEnabledButtonStyle( + context), + child: Text( + "Continue", + style: STextStyles.button( + context), + ), + onPressed: () { + ref + .read( + prefsChangeNotifierProvider) + .reuseAddress = newValue; + Navigator.of(context) + .pop(); + }, + ), + ), + ); + }, + ).then((confirmed) { + if (confirmed == true) { + ref + .read(prefsChangeNotifierProvider) + .reuseAddress = true; + } else { + ref + .read(prefsChangeNotifierProvider) + .reuseAddress = false; + } + }); + } else { + ref + .read( + prefsChangeNotifierProvider, + ) + .reuseAddress = newValue; + } + }, + ), + ), + ], + ), + ), const Padding( padding: EdgeInsets.all(10.0), child: Divider( From 27536fe6426551adc42e7799b3d5bc541ddfd6d1 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 3 Jul 2024 10:16:26 -0500 Subject: [PATCH 3/6] override needsGenerate is reuseAddress pref set --- .../wallet/wallet_mixin_interfaces/electrumx_interface.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart index 035ae649f..83752c3a7 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart @@ -1333,6 +1333,11 @@ mixin ElectrumXInterface needsGenerate = txCount > 0 || currentReceiving.derivationIndex < 0; } + // If the reuseAddress flag is set, don't generate a new address. + if (prefs.reuseAddress) { + return; + } + if (needsGenerate) { await generateNewReceivingAddress(); From 8fb49ef0292ead5503f395b26f32313855dddeb7 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Wed, 3 Jul 2024 14:50:52 -0500 Subject: [PATCH 4/6] move reuseAddress pref from global prefs to walletInfo --- .../advanced_settings_view.dart | 214 ------------------ .../more_features/more_features_dialog.dart | 177 +++++++++++++++ .../advanced_settings/advanced_settings.dart | 210 ----------------- lib/utilities/prefs.dart | 26 --- lib/wallets/isar/models/wallet_info.dart | 1 + .../electrumx_interface.dart | 15 +- 6 files changed, 190 insertions(+), 453 deletions(-) diff --git a/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart b/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart index 99b724076..3d5413798 100644 --- a/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/advanced_views/advanced_settings_view.dart @@ -16,17 +16,11 @@ import '../../../../providers/global/prefs_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/constants.dart'; import '../../../../utilities/text_styles.dart'; -import '../../../../utilities/util.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/choose_coin_view.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; -import '../../../../widgets/desktop/desktop_dialog.dart'; -import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; -import '../../../../widgets/desktop/primary_button.dart'; -import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; -import '../../../../widgets/stack_dialog.dart'; import '../../../stack_privacy_calls.dart'; import 'debug_view.dart'; import 'manage_coin_units/manage_coin_units_view.dart'; @@ -188,214 +182,6 @@ class AdvancedSettingsView extends StatelessWidget { }, ), ), - // reuseAddress pref. - const SizedBox( - height: 8, - ), - RoundedWhiteContainer( - child: Consumer( - builder: (_, ref, __) { - final reuseAddress = - ref.watch(prefsChangeNotifierProvider.select( - (value) => value.reuseAddress, - )); - return RawMaterialButton( - // splashColor: Theme.of(context).extension()!.highlight, - materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - onPressed: null, - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Reuse address", - style: STextStyles.titleBold12(context), - textAlign: TextAlign.left, - ), - SizedBox( - height: 20, - width: 40, - child: DraggableSwitchButton( - isOn: reuseAddress, - onValueChanged: (newValue) { - if (newValue) { - showDialog( - context: context, - builder: (context) { - final isDesktop = Util.isDesktop; - return isDesktop - ? DesktopDialog( - maxWidth: 576, - child: Column( - mainAxisSize: - MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 32), - child: Text( - "Warning!", - style: STextStyles - .desktopH3( - context), - ), - ), - const DesktopDialogCloseButton(), - ], - ), - Padding( - padding: - const EdgeInsets.only( - top: 8, - left: 32, - right: 32, - bottom: 32, - ), - child: Column( - mainAxisSize: - MainAxisSize.min, - children: [ - Text( - "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", - style: STextStyles - .desktopTextSmall( - context), - ), - const SizedBox( - height: 43, - ), - Row( - children: [ - Expanded( - child: - SecondaryButton( - buttonHeight: - ButtonHeight - .l, - onPressed: - () { - Navigator.of( - context) - .pop( - false); - }, - label: - "Cancel", - ), - ), - const SizedBox( - width: 16, - ), - Expanded( - child: - PrimaryButton( - buttonHeight: - ButtonHeight - .l, - onPressed: - () { - ref.read(prefsChangeNotifierProvider).reuseAddress = - newValue; - Navigator.of( - context) - .pop( - true); - }, - label: - "Continue", - ), - ), - ], - ), - ], - ), - ), - ], - ), - ) - : WillPopScope( - onWillPop: () async { - return true; - }, - child: StackDialog( - title: "Warning!", - message: - "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", - leftButton: TextButton( - style: Theme.of(context) - .extension< - StackColors>()! - .getSecondaryEnabledButtonStyle( - context), - child: Text( - "Cancel", - style: STextStyles - .itemSubtitle12( - context), - ), - onPressed: () { - Navigator.of(context) - .pop(false); - }, - ), - rightButton: TextButton( - style: Theme.of(context) - .extension< - StackColors>()! - .getPrimaryEnabledButtonStyle( - context), - child: Text( - "Continue", - style: STextStyles.button( - context), - ), - onPressed: () { - Navigator.of(context) - .pop(true); - }, - ), - ), - ); - }, - ).then((confirmed) { - if (confirmed == true) { - ref - .read(prefsChangeNotifierProvider) - .reuseAddress = true; - } else { - ref - .read(prefsChangeNotifierProvider) - .reuseAddress = false; - } - }); - } else { - ref - .read(prefsChangeNotifierProvider) - .reuseAddress = newValue; - } - }, - ), - ), - ], - ), - ), - ); - }, - ), - ), const SizedBox( height: 8, ), diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart index 76fa097a4..352b9e378 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart @@ -19,6 +19,7 @@ import '../../../../../providers/global/wallets_provider.dart'; import '../../../../../themes/stack_colors.dart'; import '../../../../../utilities/assets.dart'; import '../../../../../utilities/text_styles.dart'; +import '../../../../../utilities/util.dart'; import '../../../../../wallets/crypto_currency/crypto_currency.dart'; import '../../../../../wallets/isar/models/wallet_info.dart'; import '../../../../../wallets/isar/providers/wallet_info_provider.dart'; @@ -32,7 +33,10 @@ import '../../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.da import '../../../../../widgets/custom_buttons/draggable_switch_button.dart'; import '../../../../../widgets/desktop/desktop_dialog.dart'; import '../../../../../widgets/desktop/desktop_dialog_close_button.dart'; +import '../../../../../widgets/desktop/primary_button.dart'; +import '../../../../../widgets/desktop/secondary_button.dart'; import '../../../../../widgets/rounded_container.dart'; +import '../../../../../widgets/stack_dialog.dart'; class MoreFeaturesDialog extends ConsumerStatefulWidget { const MoreFeaturesDialog({ @@ -102,6 +106,147 @@ class _MoreFeaturesDialogState extends ConsumerState { } } + bool _switchReuseAddressToggledLock = false; // Mutex. + Future _switchReuseAddressToggled(bool newValue) async { + if (newValue) { + await showDialog( + context: context, + builder: (context) { + final isDesktop = Util.isDesktop; + return isDesktop + ? DesktopDialog( + maxWidth: 576, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.only(left: 32), + child: Text( + "Warning!", + style: STextStyles.desktopH3(context), + ), + ), + const DesktopDialogCloseButton(), + ], + ), + Padding( + padding: const EdgeInsets.only( + top: 8, + left: 32, + right: 32, + bottom: 32, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", + style: STextStyles.desktopTextSmall(context), + ), + const SizedBox( + height: 43, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + buttonHeight: ButtonHeight.l, + onPressed: () { + Navigator.of(context).pop(false); + }, + label: "Cancel", + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + buttonHeight: ButtonHeight.l, + onPressed: () { + Navigator.of(context).pop(true); + }, + label: "Continue", + ), + ), + ], + ), + ], + ), + ), + ], + ), + ) + : StackDialog( + title: "Warning!", + message: + "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", + leftButton: TextButton( + style: Theme.of(context) + .extension()! + .getSecondaryEnabledButtonStyle(context), + child: Text( + "Cancel", + style: STextStyles.itemSubtitle12(context), + ), + onPressed: () { + Navigator.of(context).pop(false); + }, + ), + rightButton: TextButton( + style: Theme.of(context) + .extension()! + .getPrimaryEnabledButtonStyle(context), + child: Text( + "Continue", + style: STextStyles.button(context), + ), + onPressed: () { + Navigator.of(context).pop(true); + }, + ), + ); + }, + ).then((confirmed) async { + if (_switchReuseAddressToggledLock) { + return; + } + _switchReuseAddressToggledLock = true; // Lock mutex. + + try { + if (confirmed == true) { + await ref.read(pWalletInfo(widget.walletId)).updateOtherData( + newEntries: { + WalletInfoKeys.reuseAddress: true, + }, + isar: ref.read(mainDBProvider).isar, + ); + } else { + await ref.read(pWalletInfo(widget.walletId)).updateOtherData( + newEntries: { + WalletInfoKeys.reuseAddress: false, + }, + isar: ref.read(mainDBProvider).isar, + ); + } + } finally { + // ensure _switchReuseAddressToggledLock is set to false no matter what. + _switchReuseAddressToggledLock = false; + } + }); + } else { + await ref.read(pWalletInfo(widget.walletId)).updateOtherData( + newEntries: { + WalletInfoKeys.reuseAddress: false, + }, + isar: ref.read(mainDBProvider).isar, + ); + } + } + @override Widget build(BuildContext context) { final wallet = ref.watch( @@ -253,6 +398,38 @@ class _MoreFeaturesDialogState extends ConsumerState { ], ), ), + // reuseAddress preference. + _MoreFeaturesItemBase( + child: Row( + children: [ + const SizedBox(width: 3), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: ref.watch( + pWalletInfo(widget.walletId) + .select((value) => value.otherData), + )[WalletInfoKeys.reuseAddress] as bool? ?? + false, + onValueChanged: _switchReuseAddressToggled, + ), + ), + const SizedBox( + width: 16, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Reuse addresses by default", + style: STextStyles.w600_20(context), + ), + ], + ), + ], + ), + ), const SizedBox( height: 28, ), diff --git a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart index 9fe5ee8d0..029c5e294 100644 --- a/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart +++ b/lib/pages_desktop_specific/settings/settings_menu/advanced_settings/advanced_settings.dart @@ -17,14 +17,9 @@ import '../../../../providers/global/prefs_provider.dart'; import '../../../../themes/stack_colors.dart'; import '../../../../utilities/assets.dart'; import '../../../../utilities/text_styles.dart'; -import '../../../../utilities/util.dart'; import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; -import '../../../../widgets/desktop/desktop_dialog.dart'; -import '../../../../widgets/desktop/desktop_dialog_close_button.dart'; import '../../../../widgets/desktop/primary_button.dart'; -import '../../../../widgets/desktop/secondary_button.dart'; import '../../../../widgets/rounded_white_container.dart'; -import '../../../../widgets/stack_dialog.dart'; import 'debug_info_dialog.dart'; import 'desktop_manage_block_explorers_dialog.dart'; import 'stack_privacy_dialog.dart'; @@ -42,11 +37,6 @@ class _AdvancedSettings extends ConsumerState { @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); - - final reuseAddress = ref.watch(prefsChangeNotifierProvider.select( - (value) => value.reuseAddress, - )); - return SingleChildScrollView( child: Column( children: [ @@ -171,206 +161,6 @@ class _AdvancedSettings extends ConsumerState { ], ), ), - // reuseAddress pref. - const Padding( - padding: EdgeInsets.all(10.0), - child: Divider( - thickness: 0.5, - ), - ), - Padding( - padding: const EdgeInsets.all(10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Reuse addresses", - style: STextStyles.desktopTextExtraSmall(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ), - textAlign: TextAlign.left, - ), - SizedBox( - height: 20, - width: 40, - child: DraggableSwitchButton( - isOn: reuseAddress, - onValueChanged: (newValue) { - if (newValue) { - showDialog( - context: context, - builder: (context) { - final isDesktop = Util.isDesktop; - return isDesktop - ? DesktopDialog( - maxWidth: 576, - child: Column( - mainAxisSize: - MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: - MainAxisAlignment - .spaceBetween, - children: [ - Padding( - padding: - const EdgeInsets - .only( - left: 32), - child: Text( - "Warning!", - style: STextStyles - .desktopH3( - context), - ), - ), - const DesktopDialogCloseButton(), - ], - ), - Padding( - padding: - const EdgeInsets.only( - top: 8, - left: 32, - right: 32, - bottom: 32, - ), - child: Column( - mainAxisSize: - MainAxisSize.min, - children: [ - Text( - "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", - style: STextStyles - .desktopTextSmall( - context), - ), - const SizedBox( - height: 43, - ), - Row( - children: [ - Expanded( - child: - SecondaryButton( - buttonHeight: - ButtonHeight - .l, - onPressed: - () { - Navigator.of( - context) - .pop(); - }, - label: - "Cancel", - ), - ), - const SizedBox( - width: 16, - ), - Expanded( - child: - PrimaryButton( - buttonHeight: - ButtonHeight - .l, - onPressed: - () { - ref.read(prefsChangeNotifierProvider).reuseAddress = - newValue; - Navigator.of( - context) - .pop(); - }, - label: - "Continue", - ), - ), - ], - ), - ], - ), - ), - ], - ), - ) - : WillPopScope( - onWillPop: () async { - return true; - }, - child: StackDialog( - title: "Warning!", - message: - "Reusing addresses reduces your privacy and security. Are you sure you want to reuse addresses by default?", - leftButton: TextButton( - style: Theme.of(context) - .extension< - StackColors>()! - .getSecondaryEnabledButtonStyle( - context), - child: Text( - "Cancel", - style: STextStyles - .itemSubtitle12( - context), - ), - onPressed: () { - Navigator.of(context) - .pop(); - }, - ), - rightButton: TextButton( - style: Theme.of(context) - .extension< - StackColors>()! - .getPrimaryEnabledButtonStyle( - context), - child: Text( - "Continue", - style: STextStyles.button( - context), - ), - onPressed: () { - ref - .read( - prefsChangeNotifierProvider) - .reuseAddress = newValue; - Navigator.of(context) - .pop(); - }, - ), - ), - ); - }, - ).then((confirmed) { - if (confirmed == true) { - ref - .read(prefsChangeNotifierProvider) - .reuseAddress = true; - } else { - ref - .read(prefsChangeNotifierProvider) - .reuseAddress = false; - } - }); - } else { - ref - .read( - prefsChangeNotifierProvider, - ) - .reuseAddress = newValue; - } - }, - ), - ), - ], - ), - ), const Padding( padding: EdgeInsets.all(10.0), child: Divider( diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index 585bdc1fc..78a461ec7 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -1131,30 +1131,4 @@ class Prefs extends ChangeNotifier { ) as bool? ?? false; } - - // Reuse addresses (ie., don't generate new addresses by default). - - bool _reuseAddress = false; - - bool get reuseAddress => _reuseAddress; - - set reuseAddress(bool reuseAddress) { - if (_reuseAddress != reuseAddress) { - DB.instance.put( - boxName: DB.boxNamePrefs, - key: "reuseAddress", - value: reuseAddress, - ); - _reuseAddress = reuseAddress; - notifyListeners(); - } - } - - Future _getReuseAddress() async { - return await DB.instance.get( - boxName: DB.boxNamePrefs, - key: "reuseAddress", - ) as bool? ?? - true; - } } diff --git a/lib/wallets/isar/models/wallet_info.dart b/lib/wallets/isar/models/wallet_info.dart index 6f6e12a42..6a7a9b54c 100644 --- a/lib/wallets/isar/models/wallet_info.dart +++ b/lib/wallets/isar/models/wallet_info.dart @@ -511,4 +511,5 @@ abstract class WalletInfoKeys { static const String firoSparkCacheSetTimestampCache = "firoSparkCacheSetTimestampCacheKey"; static const String enableOptInRbf = "enableOptInRbfKey"; + static const String reuseAddress = "reuseAddressKey"; } diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart index 83752c3a7..621209910 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:math'; import 'dart:typed_data'; @@ -23,6 +24,7 @@ import '../../../utilities/logger.dart'; import '../../../utilities/paynym_is_api.dart'; import '../../crypto_currency/coins/firo.dart'; import '../../crypto_currency/interfaces/electrumx_currency_interface.dart'; +import '../../isar/models/wallet_info.dart'; import '../../models/tx_data.dart'; import '../impl/bitcoin_wallet.dart'; import '../impl/firo_wallet.dart'; @@ -1333,9 +1335,16 @@ mixin ElectrumXInterface needsGenerate = txCount > 0 || currentReceiving.derivationIndex < 0; } - // If the reuseAddress flag is set, don't generate a new address. - if (prefs.reuseAddress) { - return; + // If reuseAddress is set, don't generate an address by default. + final WalletInfo info = mainDB.isar.walletInfo + .where() + .walletIdEqualTo(walletId) + .findFirstSync()!; + if (info.otherDataJsonString != null) { + final otherData = jsonDecode(info.otherDataJsonString!); + if (otherData[WalletInfoKeys.reuseAddress] as bool? ?? false) { + return; + } } if (needsGenerate) { From 4f4057236653a0eae5c6ee9c1dcf25a6ce6a62ab Mon Sep 17 00:00:00 2001 From: julian Date: Thu, 4 Jul 2024 10:34:45 -0600 Subject: [PATCH 5/6] small tweaks --- .../more_features/more_features_dialog.dart | 2 +- .../electrumx_interface.dart | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart index 352b9e378..a474fb7bb 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/more_features/more_features_dialog.dart @@ -422,7 +422,7 @@ class _MoreFeaturesDialogState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Reuse addresses by default", + "Reuse receiving address by default", style: STextStyles.w600_20(context), ), ], diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart index 621209910..44bebf17a 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:math'; import 'dart:typed_data'; @@ -1335,16 +1334,10 @@ mixin ElectrumXInterface needsGenerate = txCount > 0 || currentReceiving.derivationIndex < 0; } + // TODO this in the wrong place // If reuseAddress is set, don't generate an address by default. - final WalletInfo info = mainDB.isar.walletInfo - .where() - .walletIdEqualTo(walletId) - .findFirstSync()!; - if (info.otherDataJsonString != null) { - final otherData = jsonDecode(info.otherDataJsonString!); - if (otherData[WalletInfoKeys.reuseAddress] as bool? ?? false) { - return; - } + if (info.otherData[WalletInfoKeys.reuseAddress] as bool? ?? false) { + return; } if (needsGenerate) { From bb7f0ff46f0f3e53b123e4cbf0d42f2525a07b98 Mon Sep 17 00:00:00 2001 From: sneurlax Date: Thu, 4 Jul 2024 15:05:25 -0500 Subject: [PATCH 6/6] avoid checkReceivingAddressForTransactions altogether when reuse is set --- lib/wallets/wallet/impl/firo_wallet.dart | 4 ++- lib/wallets/wallet/wallet.dart | 6 +++-- .../cw_based_interface.dart | 22 +++++++++++++--- .../electrumx_interface.dart | 26 ++++++++++++------- 4 files changed, 43 insertions(+), 15 deletions(-) diff --git a/lib/wallets/wallet/impl/firo_wallet.dart b/lib/wallets/wallet/impl/firo_wallet.dart index 33ae3fb24..f9fcb11b5 100644 --- a/lib/wallets/wallet/impl/firo_wallet.dart +++ b/lib/wallets/wallet/impl/firo_wallet.dart @@ -787,7 +787,9 @@ class FiroWallet extends Bip39HDWallet for (final tuple in receiveResults) { if (tuple.addresses.isEmpty) { - await checkReceivingAddressForTransactions(); + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + await checkReceivingAddressForTransactions(); + } } else { highestReceivingIndexWithHistory = max( tuple.index, diff --git a/lib/wallets/wallet/wallet.dart b/lib/wallets/wallet/wallet.dart index 1bca3c858..46540eef7 100644 --- a/lib/wallets/wallet/wallet.dart +++ b/lib/wallets/wallet/wallet.dart @@ -522,8 +522,10 @@ abstract class Wallet { // TODO: [prio=low] handle this differently. Extra modification of this file for coin specific functionality should be avoided. if (this is MultiAddressInterface) { - await (this as MultiAddressInterface) - .checkReceivingAddressForTransactions(); + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + await (this as MultiAddressInterface) + .checkReceivingAddressForTransactions(); + } } GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.2, walletId)); diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart index de1f7a699..661b10bd3 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/cw_based_interface.dart @@ -25,6 +25,7 @@ import '../../../utilities/amount/amount.dart'; import '../../../utilities/logger.dart'; import '../../../utilities/stack_file_system.dart'; import '../../crypto_currency/intermediate/cryptonote_currency.dart'; +import '../../isar/models/wallet_info.dart'; import '../intermediate/cryptonote_wallet.dart'; import 'multi_address_interface.dart'; @@ -286,7 +287,9 @@ mixin CwBasedInterface on CryptonoteWallet await updateTransactions(); await updateBalance(); - await checkReceivingAddressForTransactions(); + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + await checkReceivingAddressForTransactions(); + } if (cwWalletBase?.syncStatus is SyncedSyncStatus) { refreshMutex.release(); @@ -342,6 +345,17 @@ mixin CwBasedInterface on CryptonoteWallet @override Future checkReceivingAddressForTransactions() async { + if (info.otherData[WalletInfoKeys.reuseAddress] == true) { + try { + throw Exception(); + } catch (_, s) { + Logging.instance.log( + "checkReceivingAddressForTransactions called but reuse address flag set: $s", + level: LogLevel.Error, + ); + } + } + try { int highestIndex = -1; final entries = cwWalletBase?.transactionHistory?.transactions?.entries; @@ -380,8 +394,10 @@ mixin CwBasedInterface on CryptonoteWallet // we need to update the address await mainDB.updateAddress(existing, newReceivingAddress); } - // keep checking until address with no tx history is set as current - await checkReceivingAddressForTransactions(); + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + // keep checking until address with no tx history is set as current + await checkReceivingAddressForTransactions(); + } } } on SocketException catch (se, s) { Logging.instance.log( diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart index 44bebf17a..c31f7cd4a 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart @@ -1316,6 +1316,17 @@ mixin ElectrumXInterface @override Future checkReceivingAddressForTransactions() async { + if (info.otherData[WalletInfoKeys.reuseAddress] == true) { + try { + throw Exception(); + } catch (_, s) { + Logging.instance.log( + "checkReceivingAddressForTransactions called but reuse address flag set: $s", + level: LogLevel.Error, + ); + } + } + try { final currentReceiving = await getCurrentReceivingAddress(); @@ -1334,18 +1345,15 @@ mixin ElectrumXInterface needsGenerate = txCount > 0 || currentReceiving.derivationIndex < 0; } - // TODO this in the wrong place - // If reuseAddress is set, don't generate an address by default. - if (info.otherData[WalletInfoKeys.reuseAddress] as bool? ?? false) { - return; - } - if (needsGenerate) { await generateNewReceivingAddress(); - // TODO: get rid of this? Could cause problems (long loading/infinite loop or something) - // keep checking until address with no tx history is set as current - await checkReceivingAddressForTransactions(); + // TODO: [prio=low] Make sure we scan all addresses but only show one. + if (info.otherData[WalletInfoKeys.reuseAddress] != true) { + // TODO: get rid of this? Could cause problems (long loading/infinite loop or something) + // keep checking until address with no tx history is set as current + await checkReceivingAddressForTransactions(); + } } } catch (e, s) { Logging.instance.log(