summaryrefslogtreecommitdiffhomepage
path: root/gui/src
diff options
context:
space:
mode:
authorOskar <oskar@mullvad.net>2024-08-29 21:57:17 +0200
committerOskar <oskar@mullvad.net>2024-08-30 20:04:00 +0200
commit6fd7f455fa2a393f4fa50e54b83c02d5a8743498 (patch)
treef7da2ef09547e3d77d18f3f0b3e5c34a6ef25be3 /gui/src
parentbda71f3d5860ebff96364ca4d8b6d81f3834ff23 (diff)
downloadmullvadvpn-6fd7f455fa2a393f4fa50e54b83c02d5a8743498.tar.xz
mullvadvpn-6fd7f455fa2a393f4fa50e54b83c02d5a8743498.zip
Add Shadowsocks settings
Diffstat (limited to 'gui/src')
-rw-r--r--gui/src/renderer/components/Shadowsocks.tsx137
-rw-r--r--gui/src/renderer/components/WireguardSettings.tsx11
2 files changed, 148 insertions, 0 deletions
diff --git a/gui/src/renderer/components/Shadowsocks.tsx b/gui/src/renderer/components/Shadowsocks.tsx
new file mode 100644
index 0000000000..0614bc44cf
--- /dev/null
+++ b/gui/src/renderer/components/Shadowsocks.tsx
@@ -0,0 +1,137 @@
+import { useCallback } from 'react';
+import { sprintf } from 'sprintf-js';
+import styled from 'styled-components';
+
+import { wrapConstraint } from '../../shared/daemon-rpc-types';
+import { messages } from '../../shared/gettext';
+import { removeNonNumericCharacters } from '../../shared/string-helpers';
+import { useAppContext } from '../context';
+import { useHistory } from '../lib/history';
+import { useSelector } from '../redux/store';
+import { AriaDescription, AriaInputGroup } from './AriaGroup';
+import * as Cell from './cell';
+import { SelectorItem, SelectorWithCustomItem } from './cell/Selector';
+import { BackAction } from './KeyboardNavigation';
+import { Layout, SettingsContainer } from './Layout';
+import {
+ NavigationBar,
+ NavigationContainer,
+ NavigationItems,
+ NavigationScrollbars,
+ TitleBarItem,
+} from './NavigationBar';
+import SettingsHeader, { HeaderTitle } from './SettingsHeader';
+
+const PORTS: Array<SelectorItem<number>> = [];
+const ALLOWED_RANGE = [1, 65000];
+
+const StyledContent = styled.div({
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ marginBottom: '2px',
+});
+
+const StyledSelectorContainer = styled.div({
+ flex: 0,
+});
+
+export default function Shadowsocks() {
+ const { pop } = useHistory();
+
+ return (
+ <BackAction action={pop}>
+ <Layout>
+ <SettingsContainer>
+ <NavigationContainer>
+ <NavigationBar>
+ <NavigationItems>
+ <TitleBarItem>
+ {
+ // TRANSLATORS: Title label in navigation bar
+ messages.pgettext('wireguard-settings-nav', 'Shadowsocks')
+ }
+ </TitleBarItem>
+ </NavigationItems>
+ </NavigationBar>
+
+ <NavigationScrollbars>
+ <SettingsHeader>
+ <HeaderTitle>
+ {messages.pgettext('wireguard-settings-view', 'Shadowsocks')}
+ </HeaderTitle>
+ </SettingsHeader>
+
+ <StyledContent>
+ <Cell.Group>
+ <ShadowsocksPortSelector />
+ </Cell.Group>
+ </StyledContent>
+ </NavigationScrollbars>
+ </NavigationContainer>
+ </SettingsContainer>
+ </Layout>
+ </BackAction>
+ );
+}
+
+function ShadowsocksPortSelector() {
+ const { setObfuscationSettings } = useAppContext();
+ const obfuscationSettings = useSelector((state) => state.settings.obfuscationSettings);
+
+ const port =
+ obfuscationSettings.shadowsocksSettings.port === 'any'
+ ? null
+ : obfuscationSettings.shadowsocksSettings.port.only;
+
+ const setShadowsocksPort = useCallback(
+ async (port: number | null) => {
+ await setObfuscationSettings({
+ ...obfuscationSettings,
+ shadowsocksSettings: {
+ ...obfuscationSettings.shadowsocksSettings,
+ port: wrapConstraint(port),
+ },
+ });
+ },
+ [setObfuscationSettings, obfuscationSettings],
+ );
+
+ const parseValue = useCallback((port: string) => parseInt(port), []);
+
+ const validateValue = useCallback(
+ (value: number) => value >= ALLOWED_RANGE[0] && value <= ALLOWED_RANGE[1],
+ [],
+ );
+
+ return (
+ <AriaInputGroup>
+ <StyledSelectorContainer>
+ <SelectorWithCustomItem
+ // TRANSLATORS: The title for the WireGuard port selector.
+ title={messages.pgettext('wireguard-settings-view', 'Port')}
+ items={PORTS}
+ value={port}
+ onSelect={setShadowsocksPort}
+ inputPlaceholder={messages.pgettext('wireguard-settings-view', 'Port')}
+ automaticValue={null}
+ parseValue={parseValue}
+ modifyValue={removeNonNumericCharacters}
+ validateValue={validateValue}
+ maxLength={`${ALLOWED_RANGE[1]}`.length}
+ />
+ </StyledSelectorContainer>
+ <Cell.CellFooter>
+ <AriaDescription>
+ <Cell.CellFooterText>
+ {sprintf(
+ // TRANSLATORS: Text describing the valid port range for a port selector.
+ messages.pgettext('wireguard-settings-view', 'Valid range: %(min)s - %(max)s'),
+ { min: ALLOWED_RANGE[0], max: ALLOWED_RANGE[1] },
+ )}
+ </Cell.CellFooterText>
+ </AriaDescription>
+ </Cell.CellFooter>
+ </AriaInputGroup>
+ );
+}
diff --git a/gui/src/renderer/components/WireguardSettings.tsx b/gui/src/renderer/components/WireguardSettings.tsx
index f0aa06fb0f..035ecc6af1 100644
--- a/gui/src/renderer/components/WireguardSettings.tsx
+++ b/gui/src/renderer/components/WireguardSettings.tsx
@@ -219,6 +219,17 @@ function ObfuscationSettings() {
const obfuscationTypeItems: SelectorItem<ObfuscationType>[] = useMemo(
() => [
{
+ label: messages.pgettext('wireguard-settings-view', 'Shadowsocks'),
+ subLabel: sprintf(subLabelTemplate, {
+ port: formatPortForSubLabel(obfuscationSettings.shadowsocksSettings.port),
+ }),
+ value: ObfuscationType.shadowsocks,
+ details: {
+ path: RoutePath.shadowsocks,
+ ariaLabel: messages.pgettext('accessibility', 'Shadowsocks settings'),
+ },
+ },
+ {
label: messages.pgettext('wireguard-settings-view', 'UDP-over-TCP'),
subLabel: sprintf(subLabelTemplate, {
port: formatPortForSubLabel(obfuscationSettings.udp2tcpSettings.port),