diff --git a/assets/svg/connected-button.svg b/assets/svg/connected-button.svg new file mode 100644 index 000000000..96a9970c0 --- /dev/null +++ b/assets/svg/connected-button.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/connecting-button.svg b/assets/svg/connecting-button.svg new file mode 100644 index 000000000..1bc6e953b --- /dev/null +++ b/assets/svg/connecting-button.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/disconnected-button.svg b/assets/svg/disconnected-button.svg new file mode 100644 index 000000000..03a8067d7 --- /dev/null +++ b/assets/svg/disconnected-button.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/tor-circle.svg b/assets/svg/tor-circle.svg new file mode 100644 index 000000000..8268a00f6 --- /dev/null +++ b/assets/svg/tor-circle.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/assets/svg/tor-synced.svg b/assets/svg/tor-synced.svg new file mode 100644 index 000000000..20cff1f37 --- /dev/null +++ b/assets/svg/tor-synced.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/tor-syncing.svg b/assets/svg/tor-syncing.svg new file mode 100644 index 000000000..b51803c70 --- /dev/null +++ b/assets/svg/tor-syncing.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/svg/tor.svg b/assets/svg/tor.svg new file mode 100644 index 000000000..a893c0907 --- /dev/null +++ b/assets/svg/tor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/pages/home_view/home_view.dart b/lib/pages/home_view/home_view.dart index c3cad7a97..759c7760d 100644 --- a/lib/pages/home_view/home_view.dart +++ b/lib/pages/home_view/home_view.dart @@ -17,9 +17,11 @@ import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/pages/buy_view/buy_view.dart'; import 'package:stackwallet/pages/exchange_view/exchange_view.dart'; import 'package:stackwallet/pages/home_view/sub_widgets/home_view_button_bar.dart'; +import 'package:stackwallet/pages/home_view/sub_widgets/tor_sync_status_changed_event.dart'; import 'package:stackwallet/pages/notification_views/notifications_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/global_settings_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/hidden_settings.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart'; import 'package:stackwallet/pages/wallets_view/wallets_view.dart'; import 'package:stackwallet/providers/global/notifications_provider.dart'; import 'package:stackwallet/providers/ui/home_view_index_provider.dart'; @@ -55,6 +57,8 @@ class _HomeViewState extends ConsumerState { bool _exitEnabled = false; + late TorSyncStatus _currentSyncStatus; + // final _buyDataLoadingService = BuyDataLoadingService(); Future _onWillPop() async { @@ -113,6 +117,32 @@ class _HomeViewState extends ConsumerState { ); } + Widget _buildTorIcon(TorSyncStatus status) { + switch (status) { + case TorSyncStatus.unableToSync: + return SvgPicture.asset( + Assets.svg.tor, + color: Theme.of(context).extension()!.textSubtitle3, + width: 20, + height: 20, + ); + case TorSyncStatus.synced: + return SvgPicture.asset( + Assets.svg.tor, + color: Theme.of(context).extension()!.accentColorGreen, + width: 20, + height: 20, + ); + case TorSyncStatus.syncing: + return SvgPicture.asset( + Assets.svg.tor, + color: Theme.of(context).extension()!.accentColorYellow, + width: 20, + height: 20, + ); + } + } + @override void initState() { _pageController = PageController(); @@ -125,6 +155,20 @@ class _HomeViewState extends ConsumerState { ref.read(notificationsProvider).startCheckingWatchedNotifications(); + /// todo change to watch tor network + // if (ref.read(managerProvider).isRefreshing) { + // _currentSyncStatus = WalletSyncStatus.syncing; + // _currentNodeStatus = NodeConnectionStatus.connected; + // } else { + // _currentSyncStatus = WalletSyncStatus.synced; + // if (ref.read(managerProvider).isConnected) { + // _currentNodeStatus = NodeConnectionStatus.connected; + // } else { + // _currentNodeStatus = NodeConnectionStatus.disconnected; + // _currentSyncStatus = WalletSyncStatus.unableToSync; + // } + // } + super.initState(); } @@ -200,6 +244,31 @@ class _HomeViewState extends ConsumerState { ], ), actions: [ + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 10, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + semanticsLabel: + "Tor Settings Button. Takes To Tor Settings Page.", + key: const Key("walletsViewTorButton"), + size: 36, + shadows: const [], + color: Theme.of(context) + .extension()! + .backgroundAppBar, + icon: _buildTorIcon(TorSyncStatus.unableToSync), + onPressed: () { + Navigator.of(context) + .pushNamed(TorSettingsView.routeName); + }, + ), + ), + ), Padding( padding: const EdgeInsets.only( top: 10, diff --git a/lib/pages/home_view/sub_widgets/tor_sync_status_changed_event.dart b/lib/pages/home_view/sub_widgets/tor_sync_status_changed_event.dart new file mode 100644 index 000000000..87910b806 --- /dev/null +++ b/lib/pages/home_view/sub_widgets/tor_sync_status_changed_event.dart @@ -0,0 +1,22 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ +import 'package:stackwallet/utilities/logger.dart'; + +enum TorSyncStatus { unableToSync, synced, syncing } + +class TorSyncStatusChangedEvent { + TorSyncStatus newStatus; + + TorSyncStatusChangedEvent(this.newStatus) { + Logging.instance.log( + "TorSyncStatusChangedEvent fired with arg newStatus = $newStatus", + level: LogLevel.Info); + } +} diff --git a/lib/pages/settings_views/global_settings_view/global_settings_view.dart b/lib/pages/settings_views/global_settings_view/global_settings_view.dart index 7cadc1a77..29984b928 100644 --- a/lib/pages/settings_views/global_settings_view/global_settings_view.dart +++ b/lib/pages/settings_views/global_settings_view/global_settings_view.dart @@ -25,6 +25,7 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/stack_back import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/support_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart'; import 'package:stackwallet/pages/settings_views/sub_widgets/settings_list_button.dart'; import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/themes/stack_colors.dart'; @@ -159,6 +160,18 @@ class GlobalSettingsView extends StatelessWidget { const SizedBox( height: 8, ), + SettingsListButton( + iconAssetName: Assets.svg.tor, + iconSize: 18, + title: "Tor Settings", + onPressed: () { + Navigator.of(context) + .pushNamed(TorSettingsView.routeName); + }, + ), + const SizedBox( + height: 8, + ), SettingsListButton( iconAssetName: Assets.svg.node, iconSize: 16, diff --git a/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart b/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart new file mode 100644 index 000000000..8304eb851 --- /dev/null +++ b/lib/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart @@ -0,0 +1,302 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/pages/home_view/sub_widgets/tor_sync_status_changed_event.dart'; +import 'package:stackwallet/providers/global/prefs_provider.dart'; +import 'package:stackwallet/themes/stack_colors.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/constants.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; +import 'package:stackwallet/widgets/stack_dialog.dart'; + +class TorSettingsView extends ConsumerStatefulWidget { + const TorSettingsView({Key? key}) : super(key: key); + + static const String routeName = "/torSettings"; + + @override + ConsumerState createState() => _TorSettingsViewState(); +} + +class _TorSettingsViewState extends ConsumerState { + TorSyncStatus _networkStatus = TorSyncStatus.unableToSync; + + Widget _buildTorIcon(TorSyncStatus status) { + switch (status) { + case TorSyncStatus.unableToSync: + return Stack( + alignment: AlignmentDirectional.center, + children: [ + SvgPicture.asset( + Assets.svg.tor, + color: Theme.of(context).extension()!.textSubtitle3, + width: 200, + height: 200, + ), + Text( + "CONNECT", + style: STextStyles.smallMed14(context).copyWith( + color: Theme.of(context).extension()!.popupBG), + ) + ], + ); + case TorSyncStatus.synced: + return Stack( + alignment: AlignmentDirectional.center, + children: [ + SvgPicture.asset( + Assets.svg.tor, + color: + Theme.of(context).extension()!.accentColorGreen, + width: 200, + height: 200, + ), + Text( + "CONNECTED", + style: STextStyles.smallMed14(context).copyWith( + color: Theme.of(context).extension()!.popupBG), + ) + ], + ); + case TorSyncStatus.syncing: + return Stack( + alignment: AlignmentDirectional.center, + children: [ + SvgPicture.asset( + Assets.svg.tor, + color: + Theme.of(context).extension()!.accentColorYellow, + width: 200, + height: 200, + ), + Text( + "CONNECTING", + style: STextStyles.smallMed14(context).copyWith( + color: Theme.of(context).extension()!.popupBG), + ) + ], + ); + } + } + + Widget _buildTorStatus(TorSyncStatus status) { + switch (status) { + case TorSyncStatus.unableToSync: + return Text( + "Disconnected", + style: STextStyles.itemSubtitle(context).copyWith( + color: Theme.of(context).extension()!.textSubtitle3), + ); + case TorSyncStatus.synced: + return Text( + "Connected", + style: STextStyles.itemSubtitle(context).copyWith( + color: + Theme.of(context).extension()!.accentColorGreen), + ); + case TorSyncStatus.syncing: + return Text( + "Connecting", + style: STextStyles.itemSubtitle(context).copyWith( + color: Theme.of(context) + .extension()! + .accentColorYellow), + ); + } + } + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final isDesktop = Util.isDesktop; + + return Background( + child: Scaffold( + backgroundColor: Colors.transparent, + appBar: AppBar( + automaticallyImplyLeading: false, + backgroundColor: + Theme.of(context).extension()!.backgroundAppBar, + leading: AppBarBackButton( + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Text( + "Tor settings", + style: STextStyles.navBarTitle(context), + ), + actions: [ + AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + icon: SvgPicture.asset( + Assets.svg.circleQuestion, + ), + onPressed: () { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return const StackDialog( + title: "What is Tor?", + message: + "Short for \"The Onion Router\", is an open-source software that enables internet communication" + " to remain anonymous by routing internet traffic through a series of layered nodes," + " to obscure the origin and destination of data.", + rightButton: SecondaryButton( + label: "Close", + ), + ); + }, + ); + }, + ), + ), + ], + ), + body: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(10.0), + child: _buildTorIcon(_networkStatus), + ), + ], + ), + SizedBox( + height: 30, + ), + RoundedWhiteContainer( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + children: [ + Text( + "Tor status", + style: STextStyles.titleBold12(context), + ), + const Spacer(), + _buildTorStatus(_networkStatus), + ], + ), + ), + ), + SizedBox( + height: 8, + ), + RoundedWhiteContainer( + child: Consumer( + builder: (_, ref, __) { + 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: [ + Row( + children: [ + Text( + "Tor killswitch", + style: STextStyles.titleBold12(context), + ), + const SizedBox(width: 8), + GestureDetector( + onTap: () { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return const StackDialog( + title: "What is Tor killswitch?", + message: + "A security feature that protects your information from accidental exposure by" + " disconnecting your device from the Tor network if your virtual private network (VPN)" + " connection is disrupted or compromised.", + rightButton: SecondaryButton( + label: "Close", + ), + ); + }, + ); + }, + child: SvgPicture.asset( + Assets.svg.circleInfo, + height: 16, + width: 16, + color: Theme.of(context) + .extension()! + .infoItemLabel, + ), + ), + ], + ), + SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: ref.watch( + prefsChangeNotifierProvider + .select((value) => value.torKillswitch), + ), + onValueChanged: (newValue) { + ref + .read(prefsChangeNotifierProvider) + .torKillswitch = newValue; + }, + ), + ), + ], + ), + ), + ); + }, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages_desktop_specific/desktop_menu.dart b/lib/pages_desktop_specific/desktop_menu.dart index 1a0a1f09f..b21a1d724 100644 --- a/lib/pages_desktop_specific/desktop_menu.dart +++ b/lib/pages_desktop_specific/desktop_menu.dart @@ -14,7 +14,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/pages/home_view/sub_widgets/tor_sync_status_changed_event.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_menu_item.dart'; +import 'package:stackwallet/pages_desktop_specific/settings/settings_menu.dart'; import 'package:stackwallet/providers/desktop/current_desktop_menu_item.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/assets.dart'; @@ -57,6 +59,70 @@ class _DesktopMenuState extends ConsumerState { // final _buyDataLoadingService = BuyDataLoadingService(); + Widget _buildTorIcon(TorSyncStatus status) { + switch (status) { + case TorSyncStatus.unableToSync: + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + Assets.svg.tor, + color: Theme.of(context).extension()!.textSubtitle3, + width: 20, + height: 20, + ), + Text( + "\tDisconnected", + style: STextStyles.smallMed12(context).copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle3), + ) + ], + ); + case TorSyncStatus.synced: + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + Assets.svg.tor, + color: + Theme.of(context).extension()!.accentColorGreen, + width: 20, + height: 20, + ), + Text( + "\tConnected", + style: STextStyles.smallMed12(context).copyWith( + color: Theme.of(context) + .extension()! + .accentColorGreen), + ) + ], + ); + case TorSyncStatus.syncing: + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset( + Assets.svg.tor, + color: + Theme.of(context).extension()!.accentColorYellow, + width: 20, + height: 20, + ), + Text( + "\tConnecting", + style: STextStyles.smallMed12(context).copyWith( + color: Theme.of(context) + .extension()! + .accentColorYellow), + ) + ], + ); + } + } + void updateSelectedMenuItem(DesktopMenuItemId idKey) { widget.onSelectionWillChange?.call(idKey); @@ -140,7 +206,22 @@ class _DesktopMenuState extends ConsumerState { ), ), const SizedBox( - height: 60, + height: 5, + ), + MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + onTap: () { + ref.read(currentDesktopMenuItemProvider.state).state = + DesktopMenuItemId.settings; + ref + .watch(selectedSettingsMenuItemStateProvider.state) + .state = 4; + }, + child: _buildTorIcon(TorSyncStatus.unableToSync)), + ), + const SizedBox( + height: 40, ), Expanded( child: AnimatedContainer( diff --git a/lib/pages_desktop_specific/settings/desktop_settings_view.dart b/lib/pages_desktop_specific/settings/desktop_settings_view.dart index 2a8c180f9..6a785fc8a 100644 --- a/lib/pages_desktop_specific/settings/desktop_settings_view.dart +++ b/lib/pages_desktop_specific/settings/desktop_settings_view.dart @@ -19,6 +19,7 @@ import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/langua import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/nodes_settings.dart'; import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/security_settings.dart'; import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/syncing_preferences_settings.dart'; +import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart'; import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/themes/stack_colors.dart'; import 'package:stackwallet/utilities/text_styles.dart'; @@ -56,7 +57,12 @@ class _DesktopSettingsViewState extends ConsumerState { key: Key("settingsLanguageDesktopKey"), onGenerateRoute: RouteGenerator.generateRoute, initialRoute: LanguageOptionSettings.routeName, - ), //language + ), + const Navigator( + key: Key("settingsTorDesktopKey"), + onGenerateRoute: RouteGenerator.generateRoute, + initialRoute: TorSettings.routeName, + ), //tor const Navigator( key: Key("settingsNodesDesktopKey"), onGenerateRoute: RouteGenerator.generateRoute, diff --git a/lib/pages_desktop_specific/settings/settings_menu.dart b/lib/pages_desktop_specific/settings/settings_menu.dart index 4f3175a72..ba2c21781 100644 --- a/lib/pages_desktop_specific/settings/settings_menu.dart +++ b/lib/pages_desktop_specific/settings/settings_menu.dart @@ -32,6 +32,7 @@ class _SettingsMenuState extends ConsumerState { "Security", "Currency", "Language", + "Tor settings", "Nodes", "Syncing preferences", "Appearance", diff --git a/lib/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart b/lib/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart new file mode 100644 index 000000000..e8d4b3f6a --- /dev/null +++ b/lib/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart @@ -0,0 +1,316 @@ +/* + * This file is part of Stack Wallet. + * + * Copyright (c) 2023 Cypher Stack + * All Rights Reserved. + * The code is distributed under GPLv3 license, see LICENSE file for details. + * Generated by Cypher Stack on 2023-05-26 + * + */ + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +import '../../../../providers/global/prefs_provider.dart'; +import '../../../../themes/stack_colors.dart'; +import '../../../../widgets/custom_buttons/draggable_switch_button.dart'; + +class TorSettings extends ConsumerStatefulWidget { + const TorSettings({Key? key}) : super(key: key); + + static const String routeName = "/torDesktopSettings"; + + @override + ConsumerState createState() => _TorSettingsState(); +} + +class _TorSettingsState extends ConsumerState { + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final isDesktop = Util.isDesktop; + + /// todo: redo the padding + return Column( + children: [ + Padding( + padding: const EdgeInsets.only( + right: 30, + ), + child: RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: SvgPicture.asset( + Assets.svg.circleTor, + width: 48, + height: 48, + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: SvgPicture.asset( + Assets.svg.disconnectedButton, + width: 48, + height: 48, + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.all(10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Tor settings", + style: STextStyles.desktopTextSmall(context), + ), + RichText( + textAlign: TextAlign.start, + text: TextSpan( + children: [ + TextSpan( + text: + "\nConnect to the Tor Network with one click.", + style: STextStyles.desktopTextExtraExtraSmall( + context), + ), + TextSpan( + text: "\tWhat is Tor?", + style: STextStyles.richLink(context).copyWith( + fontSize: 14, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return DesktopDialog( + maxWidth: 580, + maxHeight: double.infinity, + child: Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + DesktopDialogCloseButton( + onPressedOverride: () => + Navigator.of(context) + .pop(true), + ), + ], + ), + Padding( + padding: + const EdgeInsets.all(20), + child: Column( + mainAxisSize: + MainAxisSize.max, + children: [ + Text( + "What is Tor?", + style: + STextStyles.desktopH2( + context), + ), + const SizedBox( + height: 20, + ), + Text( + "Short for \"The Onion Router\", is an open-source software that enables internet communication" + " to remain anonymous by routing internet traffic through a series of layered nodes," + " to obscure the origin and destination of data.", + style: STextStyles + .desktopTextMedium( + context) + .copyWith( + color: Theme.of(context) + .extension< + StackColors>()! + .textDark3, + ), + ), + ], + ), + ), + ], + ), + ); + }); + }, + ), + ], + ), + ), + ], + ), + ), + const SizedBox( + height: 10, + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: SecondaryButton( + label: "Disconnect from Tor", + width: 200, + buttonHeight: ButtonHeight.m, + onPressed: () {}, + ), + ), + const SizedBox( + height: 30, + ), + Padding( + padding: const EdgeInsets.all(10.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + RichText( + textAlign: TextAlign.start, + text: TextSpan( + children: [ + TextSpan( + text: "Tor killswitch", + style: STextStyles.desktopTextExtraExtraSmall( + context) + .copyWith( + color: Theme.of(context) + .extension()! + .accentColorDark), + ), + TextSpan( + text: "\nWhat is Tor killswitch?", + style: STextStyles.richLink(context).copyWith( + fontSize: 14, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: true, + builder: (context) { + return DesktopDialog( + maxWidth: 580, + maxHeight: double.infinity, + child: Column( + children: [ + Row( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + DesktopDialogCloseButton( + onPressedOverride: () => + Navigator.of( + context) + .pop(true), + ), + ], + ), + Padding( + padding: + const EdgeInsets.all( + 20), + child: Column( + mainAxisSize: + MainAxisSize.max, + children: [ + Text( + "What is Tor killswitch?", + style: STextStyles + .desktopH2( + context), + ), + const SizedBox( + height: 20, + ), + Text( + "A security feature that protects your information from accidental exposure by" + " disconnecting your device from the Tor network if your virtual private network (VPN)" + " connection is disrupted or compromised.", + style: STextStyles + .desktopTextMedium( + context) + .copyWith( + color: Theme.of( + context) + .extension< + StackColors>()! + .textDark3, + ), + ), + ], + ), + ), + ], + ), + ); + }); + }, + ), + ], + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: SizedBox( + height: 20, + width: 40, + child: DraggableSwitchButton( + isOn: ref.watch( + prefsChangeNotifierProvider + .select((value) => value.torKillswitch), + ), + onValueChanged: (newValue) { + ref + .read(prefsChangeNotifierProvider) + .torKillswitch = newValue; + }, + ), + ), + ), + ], + ), + ), + const SizedBox( + height: 10, + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 344eb7876..38604bc33 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -110,6 +110,7 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/support_vi import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart'; import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/wallet_syncing_options_view.dart'; +import 'package:stackwallet/pages/settings_views/global_settings_view/tor_settings/tor_settings_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_backup_views/wallet_backup_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart'; import 'package:stackwallet/pages/settings_views/wallet_settings_view/wallet_settings_view.dart'; @@ -166,6 +167,7 @@ import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/langua import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/nodes_settings.dart'; import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/security_settings.dart'; import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/syncing_preferences_settings.dart'; +import 'package:stackwallet/pages_desktop_specific/settings/settings_menu/tor_settings/tor_settings.dart'; import 'package:stackwallet/services/coins/manager.dart'; import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart'; import 'package:stackwallet/services/event_bus/events/global/wallet_sync_status_changed_event.dart'; @@ -654,6 +656,18 @@ class RouteGenerator { builder: (_) => const LanguageSettingsView(), settings: RouteSettings(name: settings.name)); + case TorSettingsView.routeName: + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => const TorSettingsView(), + settings: RouteSettings(name: settings.name)); + + case TorSettings.routeName: + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => const TorSettings(), + settings: RouteSettings(name: settings.name)); + case AboutView.routeName: return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart index d1d7a55d8..9880e12c5 100644 --- a/lib/utilities/assets.dart +++ b/lib/utilities/assets.dart @@ -92,6 +92,11 @@ class _SVG { final coinControl = const _COIN_CONTROL(); + String get connectedButton => "assets/svg/connected-button.svg"; + String get connectingButton => "assets/svg/connecting-button.svg"; + String get disconnectedButton => "assets/svg/disconnected-button.svg"; + String get circleTor => "assets/svg/tor-circle.svg"; + String get tor => "assets/svg/tor.svg"; String get monkey => "assets/svg/monkey.svg"; String get circleSliders => "assets/svg/configuration.svg"; String get circlePlus => "assets/svg/plus-circle.svg"; diff --git a/lib/utilities/prefs.dart b/lib/utilities/prefs.dart index d9a2192be..8adb19ce3 100644 --- a/lib/utilities/prefs.dart +++ b/lib/utilities/prefs.dart @@ -44,6 +44,7 @@ class Prefs extends ChangeNotifier { _lastUnlocked = await _getLastUnlocked(); _lastUnlockedTimeout = await _getLastUnlockedTimeout(); _showTestNetCoins = await _getShowTestNetCoins(); + _torKillswitch = await _getTorKillswitch(); _isAutoBackupEnabled = await _getIsAutoBackupEnabled(); _autoBackupLocation = await _getAutoBackupLocation(); _backupFrequencyType = await _getBackupFrequencyType(); @@ -396,6 +397,27 @@ class Prefs extends ChangeNotifier { 0; } + // tor + + bool _torKillswitch = false; + + bool get torKillswitch => _torKillswitch; + + set torKillswitch(bool torKillswitch) { + if (_torKillswitch != showTestNetCoins) { + DB.instance.put( + boxName: DB.boxNamePrefs, key: "torKillswitch", value: torKillswitch); + _torKillswitch = torKillswitch; + notifyListeners(); + } + } + + Future _getTorKillswitch() async { + return await DB.instance.get( + boxName: DB.boxNamePrefs, key: "torKillswitch") as bool? ?? + false; + } + // show testnet coins bool _showTestNetCoins = false; diff --git a/pubspec.yaml b/pubspec.yaml index efcb1298a..f867999f9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -346,6 +346,11 @@ flutter: - assets/svg/send.svg - assets/svg/ordinal.svg - assets/svg/monkey.svg + - assets/svg/tor.svg + - assets/svg/tor-circle.svg + - assets/svg/connected-button.svg + - assets/svg/connecting-button.svg + - assets/svg/disconnected-button.svg # coin control icons - assets/svg/coin_control/