diff options
| author | Oskar <oskar@mullvad.net> | 2024-09-23 17:53:41 +0200 |
|---|---|---|
| committer | Oskar <oskar@mullvad.net> | 2024-09-30 09:20:20 +0200 |
| commit | 2e6ed4dbc6fa5a5aae6d57dbfedd901f83679f75 (patch) | |
| tree | 04789a223a57329c46a684e12f149bff9825da89 /gui | |
| parent | 25ddd914ff6136bc6c2b68855fd2627adab3b410 (diff) | |
| download | mullvadvpn-2e6ed4dbc6fa5a5aae6d57dbfedd901f83679f75.tar.xz mullvadvpn-2e6ed4dbc6fa5a5aae6d57dbfedd901f83679f75.zip | |
Move multihop to top level settings along with a dedicated view
Diffstat (limited to 'gui')
| -rw-r--r-- | gui/src/renderer/components/AppRouter.tsx | 2 | ||||
| -rw-r--r-- | gui/src/renderer/components/MultihopSettings.tsx | 169 | ||||
| -rw-r--r-- | gui/src/renderer/components/Settings.tsx | 19 | ||||
| -rw-r--r-- | gui/src/renderer/components/WireguardSettings.tsx | 101 | ||||
| -rw-r--r-- | gui/src/renderer/lib/routes.ts | 1 |
5 files changed, 192 insertions, 100 deletions
diff --git a/gui/src/renderer/components/AppRouter.tsx b/gui/src/renderer/components/AppRouter.tsx index 75b8df936c..e1a3d2ab0f 100644 --- a/gui/src/renderer/components/AppRouter.tsx +++ b/gui/src/renderer/components/AppRouter.tsx @@ -24,6 +24,7 @@ import Filter from './Filter'; import Focus, { IFocusHandle } from './Focus'; import Launch from './Launch'; import MainView from './main-view/MainView'; +import MultihopSettings from './MultihopSettings'; import OpenVpnSettings from './OpenVpnSettings'; import ProblemReport from './ProblemReport'; import SelectLanguage from './SelectLanguage'; @@ -84,6 +85,7 @@ export default function AppRouter() { <Route exact path={RoutePath.settings} component={Settings} /> <Route exact path={RoutePath.selectLanguage} component={SelectLanguage} /> <Route exact path={RoutePath.userInterfaceSettings} component={UserInterfaceSettings} /> + <Route exact path={RoutePath.multihopSettings} component={MultihopSettings} /> <Route exact path={RoutePath.vpnSettings} component={VpnSettings} /> <Route exact path={RoutePath.wireguardSettings} component={WireguardSettings} /> <Route exact path={RoutePath.daitaSettings} component={DaitaSettings} /> diff --git a/gui/src/renderer/components/MultihopSettings.tsx b/gui/src/renderer/components/MultihopSettings.tsx new file mode 100644 index 0000000000..b2290b71d5 --- /dev/null +++ b/gui/src/renderer/components/MultihopSettings.tsx @@ -0,0 +1,169 @@ +import { useCallback } from 'react'; +import { sprintf } from 'sprintf-js'; +import styled from 'styled-components'; + +import { strings } from '../../config.json'; +import { messages } from '../../shared/gettext'; +import log from '../../shared/logging'; +import { useRelaySettingsUpdater } from '../lib/constraint-updater'; +import { useHistory } from '../lib/history'; +import { useBoolean } from '../lib/utilityHooks'; +import { useSelector } from '../redux/store'; +import * as AppButton from './AppButton'; +import { AriaDescription, AriaInput, AriaInputGroup, AriaLabel } from './AriaGroup'; +import * as Cell from './cell'; +import { BackAction } from './KeyboardNavigation'; +import { Layout, SettingsContainer } from './Layout'; +import { ModalAlert, ModalAlertType } from './Modal'; +import { + NavigationBar, + NavigationContainer, + NavigationItems, + NavigationScrollbars, + TitleBarItem, +} from './NavigationBar'; +import SettingsHeader, { HeaderSubTitle, HeaderTitle } from './SettingsHeader'; + +const StyledContent = styled.div({ + display: 'flex', + flexDirection: 'column', + flex: 1, + marginBottom: '2px', +}); + +export default function MultihopSettings() { + const { pop } = useHistory(); + + return ( + <BackAction action={pop}> + <Layout> + <SettingsContainer> + <NavigationContainer> + <NavigationBar> + <NavigationItems> + <TitleBarItem> + {messages.pgettext('wireguard-settings-view', 'Multihop')} + </TitleBarItem> + </NavigationItems> + </NavigationBar> + + <NavigationScrollbars> + <SettingsHeader> + <HeaderTitle> + {messages.pgettext('wireguard-settings-view', 'Multihop')} + </HeaderTitle> + <HeaderSubTitle> + {messages.pgettext( + 'wireguard-settings-view', + 'Multihop routes your traffic into one WireGuard server and out another, making it harder to trace. This results in increased latency but increases anonymity online.', + )} + </HeaderSubTitle> + </SettingsHeader> + + <StyledContent> + <Cell.Group> + <MultihopSetting /> + </Cell.Group> + </StyledContent> + </NavigationScrollbars> + </NavigationContainer> + </SettingsContainer> + </Layout> + </BackAction> + ); +} + +function MultihopSetting() { + const relaySettings = useSelector((state) => state.settings.relaySettings); + const relaySettingsUpdater = useRelaySettingsUpdater(); + + const multihop = 'normal' in relaySettings ? relaySettings.normal.wireguard.useMultihop : false; + const unavailable = + 'normal' in relaySettings ? relaySettings.normal.tunnelProtocol === 'openvpn' : true; + + const [confirmationDialogVisible, showConfirmationDialog, hideConfirmationDialog] = useBoolean(); + + const setMultihopImpl = useCallback( + async (enabled: boolean) => { + try { + await relaySettingsUpdater((settings) => { + settings.wireguardConstraints.useMultihop = enabled; + return settings; + }); + } catch (e) { + const error = e as Error; + log.error('Failed to update WireGuard multihop settings', error.message); + } + }, + [relaySettingsUpdater], + ); + + const setMultihop = useCallback( + async (newValue: boolean) => { + if (newValue) { + showConfirmationDialog(); + } else { + await setMultihopImpl(false); + } + }, + [setMultihopImpl], + ); + + const confirmMultihop = useCallback(async () => { + await setMultihopImpl(true); + hideConfirmationDialog(); + }, [setMultihopImpl]); + + return ( + <> + <AriaInputGroup> + <Cell.Container disabled={unavailable}> + <AriaLabel> + <Cell.InputLabel>{messages.gettext('Enable')}</Cell.InputLabel> + </AriaLabel> + <AriaInput> + <Cell.Switch isOn={multihop && !unavailable} onChange={setMultihop} /> + </AriaInput> + </Cell.Container> + {unavailable ? ( + <Cell.CellFooter> + <AriaDescription> + <Cell.CellFooterText>{featureUnavailableMessage()}</Cell.CellFooterText> + </AriaDescription> + </Cell.CellFooter> + ) : null} + </AriaInputGroup> + <ModalAlert + isOpen={confirmationDialogVisible} + type={ModalAlertType.caution} + message={ + // TRANSLATORS: Warning text in a dialog that is displayed after a setting is toggled. + messages.gettext('This setting increases latency. Use only if needed.') + } + buttons={[ + <AppButton.RedButton key="confirm" onClick={confirmMultihop}> + {messages.gettext('Enable anyway')} + </AppButton.RedButton>, + <AppButton.BlueButton key="back" onClick={hideConfirmationDialog}> + {messages.gettext('Back')} + </AppButton.BlueButton>, + ]} + close={hideConfirmationDialog} + /> + </> + ); +} + +function featureUnavailableMessage() { + const automatic = messages.gettext('Automatic'); + const tunnelProtocol = messages.pgettext('vpn-settings-view', 'Tunnel protocol'); + const multihop = messages.pgettext('wireguard-settings-view', 'Multihop'); + + return sprintf( + messages.pgettext( + 'wireguard-settings-view', + 'Switch to “%(wireguard)s” or “%(automatic)s” in Settings > %(tunnelProtocol)s to make %(setting)s available.', + ), + { wireguard: strings.wireguard, automatic, tunnelProtocol, setting: multihop }, + ); +} diff --git a/gui/src/renderer/components/Settings.tsx b/gui/src/renderer/components/Settings.tsx index 98d36fb9fd..2280998d3a 100644 --- a/gui/src/renderer/components/Settings.tsx +++ b/gui/src/renderer/components/Settings.tsx @@ -70,6 +70,7 @@ export default function Support() { <> <Cell.Group> <UserInterfaceSettingsButton /> + <MultihopButton /> <VpnSettingsButton /> </Cell.Group> @@ -133,6 +134,24 @@ function UserInterfaceSettingsButton() { ); } +function MultihopButton() { + const history = useHistory(); + const navigate = useCallback(() => history.push(RoutePath.multihopSettings), [history]); + const relaySettings = useSelector((state) => state.settings.relaySettings); + const multihop = 'normal' in relaySettings ? relaySettings.normal.wireguard.useMultihop : false; + const unavailable = + 'normal' in relaySettings ? relaySettings.normal.tunnelProtocol === 'openvpn' : true; + + return ( + <Cell.CellNavigationButton onClick={navigate}> + <Cell.Label>{messages.pgettext('settings-view', 'Multihop')}</Cell.Label> + <Cell.SubText> + {multihop && !unavailable ? messages.gettext('On') : messages.gettext('Off')} + </Cell.SubText> + </Cell.CellNavigationButton> + ); +} + function VpnSettingsButton() { const history = useHistory(); const navigate = useCallback(() => history.push(RoutePath.vpnSettings), [history]); diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx index 8f2329d77f..51817c2029 100644 --- a/gui/src/renderer/components/WireguardSettings.tsx +++ b/gui/src/renderer/components/WireguardSettings.tsx @@ -16,15 +16,13 @@ import { useAppContext } from '../context'; import { useRelaySettingsUpdater } from '../lib/constraint-updater'; import { useHistory } from '../lib/history'; import { RoutePath } from '../lib/routes'; -import { useBoolean } from '../lib/utilityHooks'; import { useSelector } from '../redux/store'; -import * as AppButton from './AppButton'; import { AriaDescription, AriaInput, AriaInputGroup, AriaLabel } from './AriaGroup'; import * as Cell from './cell'; import Selector, { SelectorItem, SelectorWithCustomItem } from './cell/Selector'; import { BackAction } from './KeyboardNavigation'; import { Layout, SettingsContainer } from './Layout'; -import { ModalAlert, ModalAlertType, ModalMessage } from './Modal'; +import { ModalMessage } from './Modal'; import { NavigationBar, NavigationContainer, @@ -105,10 +103,6 @@ export default function WireguardSettings() { </Cell.Group> <Cell.Group> - <MultihopSetting /> - </Cell.Group> - - <Cell.Group> <IpVersionSetting /> </Cell.Group> @@ -286,99 +280,6 @@ function formatPortForSubLabel(port: Constraint<number>): string { return port === 'any' ? messages.gettext('Automatic') : `${port.only}`; } -function MultihopSetting() { - const relaySettings = useSelector((state) => state.settings.relaySettings); - const relaySettingsUpdater = useRelaySettingsUpdater(); - - const multihop = 'normal' in relaySettings ? relaySettings.normal.wireguard.useMultihop : false; - - const [confirmationDialogVisible, showConfirmationDialog, hideConfirmationDialog] = useBoolean(); - - const setMultihopImpl = useCallback( - async (enabled: boolean) => { - try { - await relaySettingsUpdater((settings) => { - settings.wireguardConstraints.useMultihop = enabled; - return settings; - }); - } catch (e) { - const error = e as Error; - log.error('Failed to update WireGuard multihop settings', error.message); - } - }, - [relaySettingsUpdater], - ); - - const setMultihop = useCallback( - async (newValue: boolean) => { - if (newValue) { - showConfirmationDialog(); - } else { - await setMultihopImpl(false); - } - }, - [setMultihopImpl], - ); - - const confirmMultihop = useCallback(async () => { - await setMultihopImpl(true); - hideConfirmationDialog(); - }, [setMultihopImpl]); - - return ( - <> - <AriaInputGroup> - <Cell.Container> - <AriaLabel> - <Cell.InputLabel> - { - // TRANSLATORS: The label next to the multihop settings toggle. - messages.pgettext('vpn-settings-view', 'Enable multihop') - } - </Cell.InputLabel> - </AriaLabel> - <AriaInput> - <Cell.Switch isOn={multihop} onChange={setMultihop} /> - </AriaInput> - </Cell.Container> - <Cell.CellFooter> - <AriaDescription> - <Cell.CellFooterText> - {sprintf( - // TRANSLATORS: Description for multihop settings toggle. - // TRANSLATORS: Available placeholders: - // TRANSLATORS: %(wireguard)s - Will be replaced with the string "WireGuard" - messages.pgettext( - 'vpn-settings-view', - 'Increases anonymity by routing your traffic into one %(wireguard)s server and out another, making it harder to trace.', - ), - { wireguard: strings.wireguard }, - )} - </Cell.CellFooterText> - </AriaDescription> - </Cell.CellFooter> - </AriaInputGroup> - <ModalAlert - isOpen={confirmationDialogVisible} - type={ModalAlertType.caution} - message={ - // TRANSLATORS: Warning text in a dialog that is displayed after a setting is toggled. - messages.gettext('This setting increases latency. Use only if needed.') - } - buttons={[ - <AppButton.RedButton key="confirm" onClick={confirmMultihop}> - {messages.gettext('Enable anyway')} - </AppButton.RedButton>, - <AppButton.BlueButton key="back" onClick={hideConfirmationDialog}> - {messages.gettext('Back')} - </AppButton.BlueButton>, - ]} - close={hideConfirmationDialog} - /> - </> - ); -} - function IpVersionSetting() { const relaySettingsUpdater = useRelaySettingsUpdater(); const relaySettings = useSelector((state) => state.settings.relaySettings); diff --git a/gui/src/renderer/lib/routes.ts b/gui/src/renderer/lib/routes.ts index 5204dc666c..68e8357b6e 100644 --- a/gui/src/renderer/lib/routes.ts +++ b/gui/src/renderer/lib/routes.ts @@ -13,6 +13,7 @@ export enum RoutePath { selectLanguage = '/settings/language', account = '/account', userInterfaceSettings = '/settings/interface', + multihopSettings = '/settings/multihop', vpnSettings = '/settings/vpn', wireguardSettings = '/settings/advanced/wireguard', daitaSettings = '/settings/advanced/wireguard/daita', |
