diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fda44be75df..49dd7a65f7f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,10 @@ Line wrap the file at 100 chars. Th #### Windows - Add experimental support for Windows ARM64. +### Changed +- Move DAITA and multihop to the root settings view along with moving multihop into a dedicated + view with more information. + ## [2024.6-beta1] - 2024-09-26 ### Added diff --git a/gui/assets/images/daita-off-illustration.svg b/gui/assets/images/daita-off-illustration.svg new file mode 100644 index 000000000000..a4686cdf5936 --- /dev/null +++ b/gui/assets/images/daita-off-illustration.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gui/assets/images/daita-on-illustration.svg b/gui/assets/images/daita-on-illustration.svg new file mode 100644 index 000000000000..4321d00cc81f --- /dev/null +++ b/gui/assets/images/daita-on-illustration.svg @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gui/assets/images/multihop-illustration.svg b/gui/assets/images/multihop-illustration.svg new file mode 100644 index 000000000000..4f6909ea380b --- /dev/null +++ b/gui/assets/images/multihop-illustration.svg @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot index aac7d2de6faa..43a4ebfb811c 100644 --- a/gui/locales/messages.pot +++ b/gui/locales/messages.pot @@ -267,7 +267,6 @@ msgstr "" #. Warning shown in dialog to users when they enable setting that increases #. network latency (decreases performance). -#. Warning text in a dialog that is displayed after a setting is toggled. msgid "This setting increases latency. Use only if needed." msgstr "" @@ -1539,6 +1538,10 @@ msgctxt "settings-view" msgid "Enabling this setting allows traffic to specific Apple-owned networks to go outside of the VPN tunnel, allowing services like iMessage and FaceTime to work whilst using Mullvad." msgstr "" +msgctxt "settings-view" +msgid "Multihop" +msgstr "" + msgctxt "settings-view" msgid "Some Apple services have an issue where the network settings set by Mullvad get ignored, this in turn blocks certain apps." msgstr "" @@ -1936,11 +1939,6 @@ msgctxt "vpn-settings-view" msgid "Enable IPv6" msgstr "" -#. The label next to the multihop settings toggle. -msgctxt "vpn-settings-view" -msgid "Enable multihop" -msgstr "" - msgctxt "vpn-settings-view" msgid "Enable to add at least one DNS server." msgstr "" @@ -1954,21 +1952,10 @@ msgctxt "vpn-settings-view" msgid "Gambling" msgstr "" -#. Description for multihop settings toggle. -#. Available placeholders: -#. %(wireguard)s - Will be replaced with the string "WireGuard" -msgctxt "vpn-settings-view" -msgid "Increases anonymity by routing your traffic into one %(wireguard)s server and out another, making it harder to trace." -msgstr "" - msgctxt "vpn-settings-view" msgid "IPv4 is always enabled and the majority of websites and applications use this protocol. We do not recommend enabling IPv6 unless you know you need it." msgstr "" -msgctxt "vpn-settings-view" -msgid "Is automatically enabled with %(daita)s, makes it possible to use %(daita)s with any server by using multihop. This might increase latency." -msgstr "" - msgctxt "vpn-settings-view" msgid "It does this by allowing network communication outside the tunnel to local multicast and broadcast ranges as well as to and from these private IP ranges:" msgstr "" @@ -1989,6 +1976,10 @@ msgctxt "vpn-settings-view" msgid "Lockdown mode" msgstr "" +msgctxt "vpn-settings-view" +msgid "Makes it possible to use %(daita)s with any server and is automatically enabled." +msgstr "" + #. Label for settings that enables malware blocking. msgctxt "vpn-settings-view" msgid "Malware" @@ -2090,7 +2081,7 @@ msgid "UDP-over-TCP" msgstr "" msgctxt "wireguard-settings-view" -msgid "%(daita)s (%(daitaFull)s) hides patterns in your encrypted VPN traffic. If anyone is monitoring your connection, this makes it significantly harder for them to identify what websites you are visiting. It does this by carefully adding network noise and making all network packets the same size." +msgid "%(daita)s (%(daitaFull)s) hides patterns in your encrypted VPN traffic." msgstr "" #. Available placeholders: @@ -2100,13 +2091,21 @@ msgid "%(wireguard)s settings" msgstr "" msgctxt "wireguard-settings-view" -msgid "Hides patterns in your encrypted VPN traffic. Since this increases your total network traffic, be cautious if you have a limited data plan. It can also negatively impact your network speed and battery usage." +msgid "Can only be used with WireGuard. Since this increases your total network traffic, be cautious if you have a limited data plan. It can also negatively impact your network speed." +msgstr "" + +msgctxt "wireguard-settings-view" +msgid "If anyone is monitoring your connection, this makes it significantly harder for them to identify what websites you are visiting." msgstr "" msgctxt "wireguard-settings-view" msgid "IP version" msgstr "" +msgctxt "wireguard-settings-view" +msgid "It does this by carefully adding network noise and making all network packets the same size." +msgstr "" + msgctxt "wireguard-settings-view" msgid "It does this by performing an extra key exchange using a quantum safe algorithm and mixing the result into WireGuard’s regular encryption. This extra step uses approximately 500 kiB of traffic every time a new tunnel is established." msgstr "" @@ -2115,6 +2114,14 @@ msgctxt "wireguard-settings-view" msgid "MTU" msgstr "" +msgctxt "wireguard-settings-view" +msgid "Multihop" +msgstr "" + +msgctxt "wireguard-settings-view" +msgid "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." +msgstr "" + #. Warning text in a dialog that is displayed after a setting is toggled. msgctxt "wireguard-settings-view" msgid "Not all our servers are %(daita)s-enabled. In order to use the internet, you might have to select a new location after disabling, or you can continue using %(daita)s with Smart routing." @@ -2163,6 +2170,10 @@ msgctxt "wireguard-settings-view" msgid "Shadowsocks" msgstr "" +msgctxt "wireguard-settings-view" +msgid "Switch to “%(wireguard)s” or “%(automatic)s” in Settings > %(tunnelProtocol)s to make %(setting)s available." +msgstr "" + msgctxt "wireguard-settings-view" msgid "The automatic setting will randomly choose from the valid port ranges shown below." msgstr "" @@ -2203,6 +2214,9 @@ msgctxt "wireguard-settings-view" msgid "Which TCP port the UDP-over-TCP obfuscation protocol should connect to on the VPN server." msgstr "" +msgid "%s (%s) hides patterns in your encrypted VPN traffic. If anyone is monitoring your connection, this makes it significantly harder for them to identify what websites you are visiting. It does this by carefully adding network noise and making all network packets the same size." +msgstr "" + msgid "%s (added)" msgstr "" diff --git a/gui/src/renderer/components/AppRouter.tsx b/gui/src/renderer/components/AppRouter.tsx index 75b8df936c3b..e1a3d2ab0ffc 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() { + diff --git a/gui/src/renderer/components/DaitaSettings.tsx b/gui/src/renderer/components/DaitaSettings.tsx index 3176192ad10d..b4ee10c94df2 100644 --- a/gui/src/renderer/components/DaitaSettings.tsx +++ b/gui/src/renderer/components/DaitaSettings.tsx @@ -1,4 +1,4 @@ -import { useCallback } from 'react'; +import React, { useCallback } from 'react'; import { sprintf } from 'sprintf-js'; import styled from 'styled-components'; @@ -17,11 +17,11 @@ import { ModalAlert, ModalAlertType, ModalMessage } from './Modal'; import { NavigationBar, NavigationContainer, - NavigationInfoButton, NavigationItems, NavigationScrollbars, TitleBarItem, } from './NavigationBar'; +import PageSlider from './PageSlider'; import SettingsHeader, { HeaderSubTitle, HeaderTitle } from './SettingsHeader'; import { SmallButton, SmallButtonColor } from './SmallButton'; @@ -32,6 +32,23 @@ const StyledContent = styled.div({ marginBottom: '2px', }); +const StyledHeaderSubTitle = styled(HeaderSubTitle)({ + display: 'inline-block', + + '&&:not(:last-child)': { + paddingBottom: '18px', + }, +}); + +const EnableFooter = styled(Cell.CellFooter)({ + paddingBottom: '16px', +}); + +export const StyledIllustration = styled.img({ + width: '100%', + padding: '8px 0 8px', +}); + export default function DaitaSettings() { const { pop } = useHistory(); @@ -43,30 +60,49 @@ export default function DaitaSettings() { {strings.daita} - - - - {sprintf( - messages.pgettext( - 'wireguard-settings-view', - '%(daita)s (%(daitaFull)s) hides patterns in your encrypted VPN traffic. If anyone is monitoring your connection, this makes it significantly harder for them to identify what websites you are visiting. It does this by carefully adding network noise and making all network packets the same size.', - ), - { daita: strings.daita, daitaFull: strings.daitaFull }, - )} - - {strings.daita} - - {messages.pgettext( - 'wireguard-settings-view', - 'Hides patterns in your encrypted VPN traffic. Since this increases your total network traffic, be cautious if you have a limited data plan. It can also negatively impact your network speed and battery usage.', - )} - + + + + {sprintf( + messages.pgettext( + 'wireguard-settings-view', + '%(daita)s (%(daitaFull)s) hides patterns in your encrypted VPN traffic.', + ), + { daita: strings.daita, daitaFull: strings.daitaFull }, + )} + + + {messages.pgettext( + 'wireguard-settings-view', + 'If anyone is monitoring your connection, this makes it significantly harder for them to identify what websites you are visiting.', + )} + + , + + + + {messages.pgettext( + 'wireguard-settings-view', + 'It does this by carefully adding network noise and making all network packets the same size.', + )} + + + {messages.pgettext( + 'wireguard-settings-view', + 'Can only be used with WireGuard. Since this increases your total network traffic, be cautious if you have a limited data plan. It can also negatively impact your network speed.', + )} + + , + ]} + /> @@ -84,6 +120,7 @@ export default function DaitaSettings() { function DaitaToggle() { const { setEnableDaita, setDaitaSmartRouting } = useAppContext(); + const relaySettings = useSelector((state) => state.settings.relaySettings); const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); const smartRouting = useSelector( (state) => state.settings.wireguard.daita?.smartRouting ?? false, @@ -91,6 +128,9 @@ function DaitaToggle() { const [confirmationDialogVisible, showConfirmationDialog, hideConfirmationDialog] = useBoolean(); + const unavailable = + 'normal' in relaySettings ? relaySettings.normal.tunnelProtocol === 'openvpn' : true; + const setDaita = useCallback((value: boolean) => { void setEnableDaita(value); }, []); @@ -111,17 +151,24 @@ function DaitaToggle() { return ( <> - + {messages.gettext('Enable')} - + + {unavailable ? ( + + + {featureUnavailableMessage()} + + + ) : null} - + {messages.gettext('Smart routing')} @@ -129,7 +176,7 @@ function DaitaToggle() { - + @@ -138,7 +185,7 @@ function DaitaToggle() { {sprintf( messages.pgettext( 'vpn-settings-view', - 'Is automatically enabled with %(daita)s, makes it possible to use %(daita)s with any server by using multihop. This might increase latency.', + 'Makes it possible to use %(daita)s with any server and is automatically enabled.', ), { daita: strings.daita }, )} @@ -191,3 +238,16 @@ export function SmartRoutingModalMessage() { ); } + +function featureUnavailableMessage() { + const automatic = messages.gettext('Automatic'); + const tunnelProtocol = messages.pgettext('vpn-settings-view', 'Tunnel protocol'); + + 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: strings.daita }, + ); +} diff --git a/gui/src/renderer/components/MultihopSettings.tsx b/gui/src/renderer/components/MultihopSettings.tsx new file mode 100644 index 000000000000..3075e1d4ed1b --- /dev/null +++ b/gui/src/renderer/components/MultihopSettings.tsx @@ -0,0 +1,133 @@ +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 { useSelector } from '../redux/store'; +import { AriaDescription, AriaInput, AriaInputGroup, AriaLabel } from './AriaGroup'; +import * as Cell from './cell'; +import { StyledIllustration } from './DaitaSettings'; +import { BackAction } from './KeyboardNavigation'; +import { Layout, SettingsContainer } from './Layout'; +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 ( + + + + + + + + {messages.pgettext('wireguard-settings-view', 'Multihop')} + + + + + + + + {messages.pgettext('wireguard-settings-view', 'Multihop')} + + + + {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.', + )} + + + + + + + + + + + + + + ); +} + +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 setMultihop = 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], + ); + + return ( + <> + + + + {messages.gettext('Enable')} + + + + + + {unavailable ? ( + + + {featureUnavailableMessage()} + + + ) : null} + + + ); +} + +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/PageSlider.tsx b/gui/src/renderer/components/PageSlider.tsx new file mode 100644 index 000000000000..56d64954e79e --- /dev/null +++ b/gui/src/renderer/components/PageSlider.tsx @@ -0,0 +1,241 @@ +import { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; + +import { colors } from '../../config.json'; +import { NonEmptyArray } from '../../shared/utils'; +import { useStyledRef } from '../lib/utilityHooks'; +import { Icon } from './cell'; + +const PAGE_GAP = 16; + +const StyledPageSliderContainer = styled.div({ + display: 'flex', + flexDirection: 'column', +}); + +const StyledPageSlider = styled.div({ + whiteSpace: 'nowrap', + overflow: 'scroll hidden', + scrollSnapType: 'x mandatory', + scrollBehavior: 'smooth', + + '&&::-webkit-scrollbar': { + display: 'none', + }, +}); + +const StyledPage = styled.div({ + display: 'inline-block', + width: '100%', + whiteSpace: 'normal', + verticalAlign: 'top', + scrollSnapAlign: 'start', + + '&&:not(:last-child)': { + marginRight: `${PAGE_GAP}px`, + }, +}); + +interface PageSliderProps { + content: NonEmptyArray; +} + +export default function PageSlider(props: PageSliderProps) { + // A state is needed to trigger a rerender. This is needed to update the "disabled" and "$current" + // props of the arrows and page indicators. + const [, setPageNumberState] = useState(0); + const pageContainerRef = useStyledRef(); + + // Calculate the page number based on the scroll position. + const getPageNumber = useCallback(() => { + if (pageContainerRef.current) { + const scrollLeft = pageContainerRef.current.scrollLeft; + const pageWidth = pageContainerRef.current.offsetWidth + PAGE_GAP; + // Clamp it between 0 and props.content.length-1 to make sure it will correspond to a page. + return Math.max(0, Math.min(Math.round(scrollLeft / pageWidth), props.content.length - 1)); + } else { + return 0; + } + }, [pageContainerRef, props.content.length]); + + // These values are only intended to be used for display purposes. Using them when calculating + // next or prev page would increase the risk of race conditions. + const pageNumber = getPageNumber(); + const hasNext = pageNumber < props.content.length - 1; + const hasPrev = pageNumber > 0; + + // Scroll to a specific page. + const goToPage = useCallback( + (page: number) => { + if (pageContainerRef.current) { + const width = pageContainerRef.current.offsetWidth; + pageContainerRef.current.scrollTo({ left: width * page }); + } + }, + [pageContainerRef], + ); + + const next = useCallback(() => goToPage(getPageNumber() + 1), [goToPage, getPageNumber]); + const prev = useCallback(() => goToPage(getPageNumber() - 1), [goToPage, getPageNumber]); + + // Callback that navigates when left and right arrows are pressed. + const handleKeyDown = useCallback( + (event: KeyboardEvent) => { + if (event.key === 'ArrowLeft') { + prev(); + } else if (event.key === 'ArrowRight') { + next(); + } + }, + [next, prev], + ); + + // Trigger a rerender when the page number has changed. This needs to be done to update the + // states of the arrows and page indicators. + const handleScroll = useCallback(() => setPageNumberState(getPageNumber()), []); + + useEffect(() => { + document.addEventListener('keydown', handleKeyDown); + return () => document.removeEventListener('keydown', handleKeyDown); + }, [handleKeyDown]); + + return ( + + + {props.content.map((page, i) => ( + {page} + ))} + + + + ); +} + +const StyledControlsContainer = styled.div({ + display: 'flex', + marginTop: '12px', + alignItems: 'center', +}); + +const StyledControlElement = styled.div({ + flex: '1 0 60px', + display: 'flex', +}); + +const StyledArrows = styled(StyledControlElement)({ + display: 'flex', + justifyContent: 'right', + gap: '12px', +}); + +const StyledPageIndicators = styled(StyledControlElement)({ + display: 'flex', + flexGrow: 2, + justifyContent: 'center', +}); + +const StyledTransparentButton = styled.button({ + border: 'none', + background: 'transparent', + padding: '4px', + margin: 0, +}); + +const StyledPageIndicator = styled.div<{ $current: boolean }>((props) => ({ + width: '8px', + height: '8px', + borderRadius: '50%', + backgroundColor: props.$current ? colors.white80 : colors.white40, + + [`${StyledTransparentButton}:hover &&`]: { + backgroundColor: colors.white80, + }, +})); + +const StyledArrow = styled(Icon)((props) => ({ + backgroundColor: props.disabled ? colors.white20 : props.tintColor, + + [`${StyledTransparentButton}:hover &&`]: { + backgroundColor: props.disabled ? colors.white20 : props.tintHoverColor, + }, +})); + +const StyledLeftArrow = styled(StyledArrow)({ + transform: 'scaleX(-100%)', +}); + +interface ControlsProps { + pageNumber: number; + numberOfPages: number; + hasNext: boolean; + hasPrev: boolean; + next: () => void; + prev: () => void; + goToPage: (page: number) => void; +} + +function Controls(props: ControlsProps) { + return ( + + {/* spacer to make page indicators centered */} + + {[...Array(props.numberOfPages)].map((_, i) => ( + + ))} + + + + + + + + + + + ); +} + +interface PageIndicatorProps { + pageNumber: number; + goToPage: (page: number) => void; + current: boolean; +} + +function PageIndicator(props: PageIndicatorProps) { + const onClick = useCallback(() => { + props.goToPage(props.pageNumber); + }, [props.goToPage, props.pageNumber]); + + return ( + + + + ); +} diff --git a/gui/src/renderer/components/Settings.tsx b/gui/src/renderer/components/Settings.tsx index 98d36fb9fd69..d76ebb242e31 100644 --- a/gui/src/renderer/components/Settings.tsx +++ b/gui/src/renderer/components/Settings.tsx @@ -70,6 +70,8 @@ export default function Support() { <> + + @@ -133,6 +135,42 @@ 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 ( + + {messages.pgettext('settings-view', 'Multihop')} + + {multihop && !unavailable ? messages.gettext('On') : messages.gettext('Off')} + + + ); +} + +function DaitaButton() { + const history = useHistory(); + const navigate = useCallback(() => history.push(RoutePath.daitaSettings), [history]); + const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); + const relaySettings = useSelector((state) => state.settings.relaySettings); + const unavailable = + 'normal' in relaySettings ? relaySettings.normal.tunnelProtocol === 'openvpn' : true; + + return ( + + {strings.daita} + + {daita && !unavailable ? messages.gettext('On') : messages.gettext('Off')} + + + ); +} + 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 8f2329d77f05..9a79111ad7cb 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, @@ -96,18 +94,10 @@ export default function WireguardSettings() { - - - - - - - - @@ -286,99 +276,6 @@ function formatPortForSubLabel(port: Constraint): 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 ( - <> - - - - - { - // TRANSLATORS: The label next to the multihop settings toggle. - messages.pgettext('vpn-settings-view', 'Enable multihop') - } - - - - - - - - - - {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 }, - )} - - - - - - {messages.gettext('Enable anyway')} - , - - {messages.gettext('Back')} - , - ]} - close={hideConfirmationDialog} - /> - - ); -} - function IpVersionSetting() { const relaySettingsUpdater = useRelaySettingsUpdater(); const relaySettings = useSelector((state) => state.settings.relaySettings); @@ -527,19 +424,6 @@ function MtuSetting() { ); } -function DaitaButton() { - const history = useHistory(); - const navigate = useCallback(() => history.push(RoutePath.daitaSettings), [history]); - const daita = useSelector((state) => state.settings.wireguard.daita?.enabled ?? false); - - return ( - - {strings.daita} - {daita ? messages.gettext('On') : messages.gettext('Off')} - - ); -} - function QuantumResistantSetting() { const { setWireguardQuantumResistant } = useAppContext(); const quantumResistant = useSelector((state) => state.settings.wireguard.quantumResistant); diff --git a/gui/src/renderer/lib/routes.ts b/gui/src/renderer/lib/routes.ts index 5204dc666c61..89b50c1fb0e0 100644 --- a/gui/src/renderer/lib/routes.ts +++ b/gui/src/renderer/lib/routes.ts @@ -13,9 +13,10 @@ 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', + daitaSettings = '/settings/daita', udpOverTcp = '/settings/advanced/wireguard/udp-over-tcp', shadowsocks = '/settings/advanced/shadowsocks', openVpnSettings = '/settings/advanced/openvpn', diff --git a/gui/src/shared/utils.ts b/gui/src/shared/utils.ts index 24984e441215..042c56385af5 100644 --- a/gui/src/shared/utils.ts +++ b/gui/src/shared/utils.ts @@ -1,3 +1,5 @@ +export type NonEmptyArray = [T, ...T[]]; + export function hasValue(value: T): value is NonNullable { return value !== undefined && value !== null; }