diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2020-03-09 20:33:19 +0100 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2020-03-10 17:20:29 +0100 |
| commit | f9aa642563c6c1bc0d6a8db8f0f780348f4edcbd (patch) | |
| tree | c0f97b377b15d8096c8f6ee3b35c37aa3d0c81d8 /gui/src | |
| parent | c2018a0c960990bfdc428dd9c2c3eb3c7e21e1ba (diff) | |
| download | mullvadvpn-f9aa642563c6c1bc0d6a8db8f0f780348f4edcbd.tar.xz mullvadvpn-f9aa642563c6c1bc0d6a8db8f0f780348f4edcbd.zip | |
Add WireGuard MTU setting to gui
Diffstat (limited to 'gui/src')
| -rw-r--r-- | gui/src/renderer/components/AdvancedSettings.tsx | 131 | ||||
| -rw-r--r-- | gui/src/renderer/components/AdvancedSettingsStyles.tsx | 11 | ||||
| -rw-r--r-- | gui/src/renderer/components/Cell.tsx | 230 | ||||
| -rw-r--r-- | gui/src/renderer/components/CellStyles.tsx | 130 | ||||
| -rw-r--r-- | gui/src/renderer/containers/AdvancedSettingsPage.tsx | 9 |
5 files changed, 311 insertions, 200 deletions
diff --git a/gui/src/renderer/components/AdvancedSettings.tsx b/gui/src/renderer/components/AdvancedSettings.tsx index 10757e4d8d..27d4043d7c 100644 --- a/gui/src/renderer/components/AdvancedSettings.tsx +++ b/gui/src/renderer/components/AdvancedSettings.tsx @@ -20,6 +20,8 @@ import SettingsHeader, { HeaderTitle } from './SettingsHeader'; const MIN_MSSFIX_VALUE = 1000; const MAX_MSSFIX_VALUE = 1450; +const MIN_WIREGUARD_MTU_VALUE = 1280; +const MAX_WIREGUARD_MTU_VALUE = 1420; const UDP_PORTS = [1194, 1195, 1196, 1197, 1300, 1301, 1302]; const TCP_PORTS = [80, 443]; const WIREUGARD_UDP_PORTS = [53]; @@ -44,25 +46,21 @@ interface IProps { wireguardKeyState: WgKeyState; wireguard: { port?: number }; mssfix?: number; + wireguardMtu?: number; bridgeState: BridgeState; setBridgeState: (value: BridgeState) => void; setEnableIpv6: (value: boolean) => void; setBlockWhenDisconnected: (value: boolean) => void; setTunnelProtocol: (value: OptionalTunnelProtocol) => void; setOpenVpnMssfix: (value: number | undefined) => void; + setWireguardMtu: (value: number | undefined) => void; setOpenVpnRelayProtocolAndPort: (protocol?: RelayProtocol, port?: number) => void; setWireguardRelayPort: (port?: number) => void; onViewWireguardKeys: () => void; onClose: () => void; } -interface IState { - persistedMssfix?: number; - editedMssfix?: number; - focusOnMssfix: boolean; -} - -export default class AdvancedSettings extends Component<IProps, IState> { +export default class AdvancedSettings extends Component<IProps> { private portItems: { [key in RelayProtocol]: Array<ISelectorItem<OptionalPort>> }; private protocolItems: Array<ISelectorItem<OptionalRelayProtocol>>; private bridgeStateItems: Array<ISelectorItem<BridgeState>>; @@ -118,30 +116,9 @@ export default class AdvancedSettings extends Component<IProps, IState> { value: 'off', }, ]; - - this.state = { - persistedMssfix: props.mssfix, - editedMssfix: props.mssfix, - focusOnMssfix: false, - }; - } - - public componentDidUpdate(_prevProps: IProps, _prevState: IState) { - if (this.props.mssfix !== this.state.persistedMssfix) { - this.setState((state, props) => ({ - ...state, - persistedMssfix: props.mssfix, - editedMssfix: state.focusOnMssfix ? state.editedMssfix : props.mssfix, - })); - } } public render() { - const mssfixStyle = this.mssfixIsValid() - ? styles.advanced_settings__mssfix_valid_value - : styles.advanced_settings__mssfix_invalid_value; - const mssfixValue = this.state.editedMssfix; - const hasWireguardKey = this.props.wireguardKeyState.type === 'key-set'; return ( @@ -298,18 +275,20 @@ export default class AdvancedSettings extends Component<IProps, IState> { /> <Cell.Container> - <Cell.Label>{messages.pgettext('advanced-settings-view', 'Mssfix')}</Cell.Label> - <Cell.InputFrame style={styles.advanced_settings__mssfix_frame}> + <Cell.Label> + {messages.pgettext('advanced-settings-view', 'OpenVPN Mssfix')} + </Cell.Label> + <Cell.InputFrame style={styles.advanced_settings__input_frame}> <Cell.AutoSizingTextInputContainer> <Cell.Input + value={this.props.mssfix ? this.props.mssfix.toString() : ''} keyboardType={'numeric'} maxLength={4} placeholder={messages.pgettext('advanced-settings-view', 'Default')} - value={mssfixValue ? mssfixValue.toString() : ''} - style={[styles.advanced_settings__mssfix_input, mssfixStyle]} - onChangeText={this.onMssfixChange} - onFocus={this.onMssfixFocus} - onBlur={this.onMssfixBlur} + onSubmit={this.onMssfixSubmit} + validateValue={AdvancedSettings.mssfixIsValid} + submitOnBlur={true} + modifyValue={AdvancedSettings.removeNonNumericCharacters} /> </Cell.AutoSizingTextInputContainer> </Cell.InputFrame> @@ -332,6 +311,45 @@ export default class AdvancedSettings extends Component<IProps, IState> { )} </Cell.FooterText> </Cell.Footer> + + <Cell.Container> + <Cell.Label> + {messages.pgettext('advanced-settings-view', 'WireGuard MTU')} + </Cell.Label> + <Cell.InputFrame style={styles.advanced_settings__input_frame}> + <Cell.AutoSizingTextInputContainer> + <Cell.Input + value={this.props.wireguardMtu ? this.props.wireguardMtu.toString() : ''} + keyboardType={'numeric'} + maxLength={4} + placeholder={messages.pgettext('advanced-settings-view', 'Default')} + onSubmit={this.onWireguardMtuSubmit} + validateValue={AdvancedSettings.wireguarMtuIsValid} + submitOnBlur={true} + modifyValue={AdvancedSettings.removeNonNumericCharacters} + /> + </Cell.AutoSizingTextInputContainer> + </Cell.InputFrame> + </Cell.Container> + <Cell.Footer> + <Cell.FooterText> + {sprintf( + // TRANSLATORS: The hint displayed below the WireGuard MTU input field. + // TRANSLATORS: Available placeholders: + // TRANSLATORS: %(max)d - the maximum possible wireguard mtu value + // TRANSLATORS: %(min)d - the minimum possible wireguard mtu value + messages.pgettext( + 'advanced-settings-view', + 'Set WireGuard MTU value. Valid range: %(min)d - %(max)d.', + ), + { + min: MIN_WIREGUARD_MTU_VALUE, + max: MAX_WIREGUARD_MTU_VALUE, + }, + )} + </Cell.FooterText> + </Cell.Footer> + <View style={styles.advanced_settings__wgkeys_cell}> <Cell.CellButton onPress={this.props.onViewWireguardKeys}> <Cell.Label> @@ -394,32 +412,37 @@ export default class AdvancedSettings extends Component<IProps, IState> { this.props.setBridgeState(bridgeState); }; - private onMssfixChange = (mssfixString: string) => { - const mssfix = mssfixString.replace(/[^0-9]/g, ''); - - if (mssfix === '') { - this.setState({ editedMssfix: undefined }); - } else { - this.setState({ editedMssfix: parseInt(mssfix, 10) }); + private onMssfixSubmit = (value: string) => { + const parsedValue = value === '' ? undefined : parseInt(value, 10); + if (AdvancedSettings.mssfixIsValid(value)) { + this.props.setOpenVpnMssfix(parsedValue); } }; - private onMssfixFocus = () => { - this.setState({ focusOnMssfix: true }); - }; + private static removeNonNumericCharacters(value: string) { + return value.replace(/[^0-9]/g, ''); + } - private onMssfixBlur = () => { - this.setState({ focusOnMssfix: false }); + private static mssfixIsValid(mssfix: string): boolean { + const parsedMssFix = mssfix ? parseInt(mssfix) : undefined; + return ( + parsedMssFix === undefined || + (parsedMssFix >= MIN_MSSFIX_VALUE && parsedMssFix <= MAX_MSSFIX_VALUE) + ); + } - if (this.mssfixIsValid()) { - this.props.setOpenVpnMssfix(this.state.editedMssfix); - this.setState((state, _props) => ({ persistedMssfix: state.editedMssfix })); + private onWireguardMtuSubmit = (value: string) => { + const parsedValue = value === '' ? undefined : parseInt(value, 10); + if (AdvancedSettings.wireguarMtuIsValid(value)) { + this.props.setWireguardMtu(parsedValue); } }; - private mssfixIsValid(): boolean { - const mssfix = this.state.editedMssfix; - - return mssfix === undefined || (mssfix >= MIN_MSSFIX_VALUE && mssfix <= MAX_MSSFIX_VALUE); + private static wireguarMtuIsValid(mtu: string): boolean { + const parsedMtu = mtu ? parseInt(mtu) : undefined; + return ( + parsedMtu === undefined || + (parsedMtu >= MIN_WIREGUARD_MTU_VALUE && parsedMtu <= MAX_WIREGUARD_MTU_VALUE) + ); } } diff --git a/gui/src/renderer/components/AdvancedSettingsStyles.tsx b/gui/src/renderer/components/AdvancedSettingsStyles.tsx index acdc9e5d8e..737a3e1acd 100644 --- a/gui/src/renderer/components/AdvancedSettingsStyles.tsx +++ b/gui/src/renderer/components/AdvancedSettingsStyles.tsx @@ -55,18 +55,9 @@ export default { advanced_settings__cell_footer_internet_warning_label: Styles.createTextStyle({ marginTop: 4, }), - advanced_settings__mssfix_input: Styles.createTextInputStyle({ - minWidth: 80, - }), - advanced_settings__mssfix_frame: Styles.createViewStyle({ + advanced_settings__input_frame: Styles.createViewStyle({ flex: 0, }), - advanced_settings__mssfix_valid_value: Styles.createTextStyle({ - color: colors.white, - }), - advanced_settings__mssfix_invalid_value: Styles.createTextStyle({ - color: colors.red, - }), advanced_settings__block_when_disconnected_label: Styles.createTextStyle({ letterSpacing: -0.5, }), diff --git a/gui/src/renderer/components/Cell.tsx b/gui/src/renderer/components/Cell.tsx index ddd5d0ea53..8d884db307 100644 --- a/gui/src/renderer/components/Cell.tsx +++ b/gui/src/renderer/components/Cell.tsx @@ -1,130 +1,10 @@ import * as React from 'react'; import { Button, Component, Styles, Text, TextInput, Types, View } from 'reactxp'; import { colors } from '../../config.json'; +import styles from './CellStyles'; import ImageView from './ImageView'; import { default as SwitchControl } from './Switch'; -const styles = { - cellButton: { - base: Styles.createButtonStyle({ - backgroundColor: colors.blue, - paddingVertical: 0, - paddingHorizontal: 16, - marginBottom: 1, - flex: 1, - flexDirection: 'row', - alignItems: 'center', - alignContent: 'center', - cursor: 'default', - }), - section: Styles.createButtonStyle({ - backgroundColor: colors.blue40, - }), - hover: Styles.createButtonStyle({ - backgroundColor: colors.blue80, - }), - selected: Styles.createViewStyle({ - backgroundColor: colors.green, - }), - }, - cellContainer: Styles.createViewStyle({ - backgroundColor: colors.blue, - flexDirection: 'row', - alignItems: 'center', - paddingLeft: 16, - paddingRight: 12, - }), - footer: { - container: Styles.createViewStyle({ - paddingTop: 8, - paddingRight: 24, - paddingBottom: 24, - paddingLeft: 24, - }), - text: Styles.createTextStyle({ - fontFamily: 'Open Sans', - fontSize: 13, - fontWeight: '600', - lineHeight: 20, - letterSpacing: -0.2, - color: colors.white80, - }), - boldText: Styles.createTextStyle({ - fontWeight: '900', - }), - }, - label: { - container: Styles.createViewStyle({ - marginLeft: 8, - marginTop: 14, - marginBottom: 14, - flex: 1, - }), - text: Styles.createTextStyle({ - fontFamily: 'DINPro', - fontSize: 20, - fontWeight: '900', - lineHeight: 26, - letterSpacing: -0.2, - color: colors.white, - }), - }, - switch: Styles.createViewStyle({ - flex: 0, - }), - input: { - frame: Styles.createViewStyle({ - flexGrow: 0, - backgroundColor: 'rgba(255,255,255,0.1)', - borderRadius: 4, - padding: 4, - }), - text: Styles.createTextInputStyle({ - color: colors.white, - backgroundColor: 'transparent', - fontFamily: 'Open Sans', - fontSize: 20, - fontWeight: '600', - lineHeight: 26, - textAlign: 'right', - padding: 0, - }), - }, - autoSizingInputContainer: { - measuringView: Styles.createViewStyle({ - position: 'absolute', - opacity: 0, - }), - measureText: Styles.createTextStyle({ - width: undefined, - flexBasis: undefined, - }), - }, - icon: Styles.createViewStyle({ - marginLeft: 8, - }), - subtext: Styles.createTextStyle({ - color: colors.white60, - fontFamily: 'Open Sans', - fontSize: 13, - fontWeight: '800', - flex: -1, - textAlign: 'right', - marginLeft: 8, - }), - sectionTitle: Styles.createTextStyle({ - backgroundColor: colors.blue, - paddingVertical: 14, - paddingHorizontal: 24, - marginBottom: 1, - fontFamily: 'DINPro', - fontSize: 20, - fontWeight: '900', - lineHeight: 26, - color: colors.white, - }), -}; - interface ICellButtonProps { children?: React.ReactNode; disabled?: boolean; @@ -272,22 +152,100 @@ export const InputFrame = function CellInputFrame(props: IInputFrameProps) { return <View style={[styles.input.frame, style]}>{children}</View>; }; -export const Input = React.forwardRef(function CellInput( - props: Types.TextInputProps, - ref?: React.Ref<TextInput>, -) { - const { style, ...otherProps } = props; +interface IInputProps extends Types.TextInputProps { + validateValue?: (value: string) => boolean; + modifyValue?: (value: string) => string; + submitOnBlur?: boolean; + onSubmit?: (value: string) => void; +} - return ( - <TextInput - ref={ref} - placeholderTextColor={colors.white60} - autoCorrect={false} - style={[styles.input.text, style]} - {...otherProps} - /> - ); -}); +interface IInputState { + value?: string; + focused: boolean; +} + +export class Input extends Component<IInputProps, IInputState> { + public state = { + value: this.props.value || '', + focused: false, + }; + + public componentDidUpdate(prevProps: IInputProps, _prevState: IInputState) { + if ( + !this.state.focused && + prevProps.value !== this.props.value && + this.props.value !== this.state.value + ) { + this.setState((_state, props) => ({ + value: props.value, + })); + } + } + + public render() { + const { + style, + value: _value, + onChangeText: _onChangeText, + onFocus: _onFocus, + onBlur: _onBlur, + onSubmitEditing: _onSubmitEditing, + ...otherProps + } = this.props; + + const validityStyle = + this.props.validateValue && this.props.validateValue(this.state.value) + ? styles.input.validValue + : styles.input.invalidValue; + + return ( + <TextInput + placeholderTextColor={colors.white60} + autoCorrect={false} + style={[styles.input.text, validityStyle, style]} + onChangeText={this.onChangeText} + onFocus={this.onFocus} + onBlur={this.onBlur} + onSubmitEditing={this.onSubmitEditing} + value={this.state.value} + {...otherProps} + /> + ); + } + + private onChangeText = (value: string) => { + this.setState({ value }); + if (this.props.onChangeText) { + this.props.onChangeText(value); + } + }; + + private onFocus = (e: Types.FocusEvent) => { + this.setState({ focused: true }); + if (this.props.onFocus) { + this.props.onFocus(e); + } + }; + + private onBlur = (e: Types.FocusEvent) => { + this.setState({ focused: false }); + if (this.props.onBlur) { + this.props.onBlur(e); + } + if (this.props.submitOnBlur && this.props.onSubmit) { + this.props.onSubmit(this.state.value); + } + }; + + private onSubmitEditing = () => { + if (this.props.onSubmit) { + this.props.onSubmit(this.state.value); + } + if (this.props.onSubmitEditing) { + this.props.onSubmitEditing(); + } + }; +} interface IAutoSizingTextInputContainerProps { style?: Types.StyleRuleSetRecursive<Types.ViewStyleRuleSet>; diff --git a/gui/src/renderer/components/CellStyles.tsx b/gui/src/renderer/components/CellStyles.tsx new file mode 100644 index 0000000000..453a8d3cca --- /dev/null +++ b/gui/src/renderer/components/CellStyles.tsx @@ -0,0 +1,130 @@ +import { Styles } from 'reactxp'; +import { colors } from '../../config.json'; + +export default { + cellButton: { + base: Styles.createButtonStyle({ + backgroundColor: colors.blue, + paddingVertical: 0, + paddingHorizontal: 16, + marginBottom: 1, + flex: 1, + flexDirection: 'row', + alignItems: 'center', + alignContent: 'center', + cursor: 'default', + }), + section: Styles.createButtonStyle({ + backgroundColor: colors.blue40, + }), + hover: Styles.createButtonStyle({ + backgroundColor: colors.blue80, + }), + selected: Styles.createViewStyle({ + backgroundColor: colors.green, + }), + }, + cellContainer: Styles.createViewStyle({ + backgroundColor: colors.blue, + flexDirection: 'row', + alignItems: 'center', + paddingLeft: 16, + paddingRight: 12, + }), + footer: { + container: Styles.createViewStyle({ + paddingTop: 8, + paddingRight: 24, + paddingBottom: 24, + paddingLeft: 24, + }), + text: Styles.createTextStyle({ + fontFamily: 'Open Sans', + fontSize: 13, + fontWeight: '600', + lineHeight: 20, + letterSpacing: -0.2, + color: colors.white80, + }), + boldText: Styles.createTextStyle({ + fontWeight: '900', + }), + }, + label: { + container: Styles.createViewStyle({ + marginLeft: 8, + marginTop: 14, + marginBottom: 14, + flex: 1, + }), + text: Styles.createTextStyle({ + fontFamily: 'DINPro', + fontSize: 20, + fontWeight: '900', + lineHeight: 26, + letterSpacing: -0.2, + color: colors.white, + }), + }, + switch: Styles.createViewStyle({ + flex: 0, + }), + input: { + frame: Styles.createViewStyle({ + flexGrow: 0, + backgroundColor: 'rgba(255,255,255,0.1)', + borderRadius: 4, + padding: 4, + }), + text: Styles.createTextInputStyle({ + color: colors.white, + backgroundColor: 'transparent', + fontFamily: 'Open Sans', + fontSize: 20, + fontWeight: '600', + lineHeight: 26, + textAlign: 'right', + padding: 0, + minWidth: 80, + }), + validValue: Styles.createTextStyle({ + color: colors.white, + }), + invalidValue: Styles.createTextStyle({ + color: colors.red, + }), + }, + autoSizingInputContainer: { + measuringView: Styles.createViewStyle({ + position: 'absolute', + opacity: 0, + }), + measureText: Styles.createTextStyle({ + width: undefined, + flexBasis: undefined, + }), + }, + icon: Styles.createViewStyle({ + marginLeft: 8, + }), + subtext: Styles.createTextStyle({ + color: colors.white60, + fontFamily: 'Open Sans', + fontSize: 13, + fontWeight: '800', + flex: -1, + textAlign: 'right', + marginLeft: 8, + }), + sectionTitle: Styles.createTextStyle({ + backgroundColor: colors.blue, + paddingVertical: 14, + paddingHorizontal: 24, + marginBottom: 1, + fontFamily: 'DINPro', + fontSize: 20, + fontWeight: '900', + lineHeight: 26, + color: colors.white, + }), +}; diff --git a/gui/src/renderer/containers/AdvancedSettingsPage.tsx b/gui/src/renderer/containers/AdvancedSettingsPage.tsx index b61003bc8f..c9f33f3d63 100644 --- a/gui/src/renderer/containers/AdvancedSettingsPage.tsx +++ b/gui/src/renderer/containers/AdvancedSettingsPage.tsx @@ -18,6 +18,7 @@ const mapStateToProps = (state: IReduxState) => { blockWhenDisconnected: state.settings.blockWhenDisconnected, wireguardKeyState: state.settings.wireguardKeyState, mssfix: state.settings.openVpn.mssfix, + wireguardMtu: state.settings.wireguard.mtu, bridgeState: state.settings.bridgeState, ...protocolAndPort, }; @@ -145,6 +146,14 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: IAppContext) => { log.error('Failed to update mssfix value', e.message); } }, + + setWireguardMtu: async (mtu?: number) => { + try { + await props.app.setWireguardMtu(mtu); + } catch (e) { + log.error('Failed to update mtu value', e.message); + } + }, onViewWireguardKeys: () => history.push('/settings/advanced/wireguard-keys'), }; }; |
