summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2019-02-04 14:43:45 +0100
committerAndrej Mihajlov <and@mullvad.net>2019-02-08 15:26:24 +0100
commit43bdfe6f7a64135730479de8fc36d6514e34b288 (patch)
treefefff8f9140857dd74a71292a2cbbfccae23526b
parent04c35b86c414c490e5bf726e15e5bb9b24b23ad5 (diff)
downloadmullvadvpn-43bdfe6f7a64135730479de8fc36d6514e34b288.tar.xz
mullvadvpn-43bdfe6f7a64135730479de8fc36d6514e34b288.zip
Refactor AdvancedSettings
-rw-r--r--gui/packages/desktop/src/renderer/components/AdvancedSettings.tsx197
-rw-r--r--gui/packages/desktop/src/renderer/components/AdvancedSettingsStyles.tsx46
-rw-r--r--gui/packages/desktop/src/renderer/components/Cell.tsx103
-rw-r--r--gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.tsx21
4 files changed, 182 insertions, 185 deletions
diff --git a/gui/packages/desktop/src/renderer/components/AdvancedSettings.tsx b/gui/packages/desktop/src/renderer/components/AdvancedSettings.tsx
index c8a2b67c93..7fdcb609eb 100644
--- a/gui/packages/desktop/src/renderer/components/AdvancedSettings.tsx
+++ b/gui/packages/desktop/src/renderer/components/AdvancedSettings.tsx
@@ -1,10 +1,8 @@
-/* tslint:disable:jsx-no-lambda */
-// TODO: Refactor this file to fix the jsx-no-lambda warnings
-
-import { HeaderTitle, ImageView, SettingsHeader } from '@mullvad/components';
+import { HeaderTitle, SettingsHeader } from '@mullvad/components';
import * as React from 'react';
-import { Button, Component, Text, View } from 'reactxp';
+import { Component, View } from 'reactxp';
import { colors } from '../../config.json';
+import { RelayProtocol } from '../../shared/daemon-rpc-types';
import styles from './AdvancedSettingsStyles';
import * as Cell from './Cell';
import { Container, Layout } from './Layout';
@@ -19,17 +17,34 @@ import Switch from './Switch';
const MIN_MSSFIX_VALUE = 1000;
const MAX_MSSFIX_VALUE = 1450;
+const PROTOCOLS: RelayProtocol[] = ['udp', 'tcp'];
+const UDP_PORTS = [1194, 1195, 1196, 1197, 1300, 1301, 1302];
+const TCP_PORTS = [80, 443];
+
+const PORT_ITEMS: { [key in RelayProtocol]: Array<ISelectorItem<number>> } = {
+ udp: UDP_PORTS.map(mapPortToSelectorItem),
+ tcp: TCP_PORTS.map(mapPortToSelectorItem),
+};
+
+const PROTOCOL_ITEMS: Array<ISelectorItem<RelayProtocol>> = PROTOCOLS.map((value) => ({
+ label: value.toUpperCase(),
+ value,
+}));
+
+function mapPortToSelectorItem(value: number): ISelectorItem<number> {
+ return { label: value.toString(), value };
+}
interface IProps {
enableIpv6: boolean;
blockWhenDisconnected: boolean;
- protocol: string;
+ protocol?: RelayProtocol;
mssfix?: number;
- port: string | number;
+ port?: number;
setEnableIpv6: (value: boolean) => void;
setBlockWhenDisconnected: (value: boolean) => void;
setOpenVpnMssfix: (value: number | undefined) => void;
- onUpdate: (protocol: string, port: string | number) => void;
+ setRelayProtocolAndPort: (protocol?: RelayProtocol, port?: number) => void;
onClose: () => void;
}
@@ -39,7 +54,7 @@ interface IState {
focusOnMssfix: boolean;
}
-export class AdvancedSettings extends Component<IProps, IState> {
+export default class AdvancedSettings extends Component<IProps, IState> {
constructor(props: IProps) {
super(props);
@@ -61,19 +76,9 @@ export class AdvancedSettings extends Component<IProps, IState> {
}
public render() {
- let portSelector = null;
- let protocol = this.props.protocol.toUpperCase();
-
- if (protocol === 'AUTOMATIC') {
- protocol = 'Automatic';
- } else {
- portSelector = this.createPortSelector();
- }
-
const mssfixStyle = this.mssfixIsValid()
? styles.advanced_settings__mssfix_valid_value
: styles.advanced_settings__mssfix_invalid_value;
-
const mssfixValue = this.state.editedMssfix;
return (
@@ -116,16 +121,23 @@ export class AdvancedSettings extends Component<IProps, IState> {
<View style={styles.advanced_settings__content}>
<Selector
title={'Network protocols'}
- values={['Automatic', 'UDP', 'TCP']}
- value={protocol}
- onSelect={(selectedProtocol) => {
- this.props.onUpdate(selectedProtocol, 'Automatic');
- }}
+ values={PROTOCOL_ITEMS}
+ value={this.props.protocol}
+ onSelect={this.onSelectProtocol}
/>
<View style={styles.advanced_settings__cell_spacer} />
- {portSelector}
+ {this.props.protocol ? (
+ <Selector
+ title={`${this.props.protocol.toUpperCase()} port`}
+ values={PORT_ITEMS[this.props.protocol]}
+ value={this.props.port}
+ onSelect={this.onSelectPort}
+ />
+ ) : (
+ undefined
+ )}
</View>
<Cell.Container>
@@ -155,24 +167,13 @@ export class AdvancedSettings extends Component<IProps, IState> {
);
}
- private createPortSelector() {
- const protocol = this.props.protocol.toUpperCase();
- const ports =
- protocol === 'TCP'
- ? ['Automatic', 80, 443]
- : ['Automatic', 1194, 1195, 1196, 1197, 1300, 1301, 1302];
+ private onSelectProtocol = (protocol?: RelayProtocol) => {
+ this.props.setRelayProtocolAndPort(protocol);
+ };
- return (
- <Selector
- title={protocol + ' port'}
- values={ports}
- value={this.props.port}
- onSelect={(port) => {
- this.props.onUpdate(protocol, port);
- }}
- />
- );
- }
+ private onSelectPort = (port?: number) => {
+ this.props.setRelayProtocolAndPort(this.props.protocol, port);
+ };
private onMssfixChange = (mssfixString: string) => {
const mssfix = mssfixString.replace(/[^0-9]/g, '');
@@ -204,82 +205,74 @@ export class AdvancedSettings extends Component<IProps, IState> {
}
}
-interface ISelectorProps<T> {
- title: string;
- values: T[];
+interface ISelectorItem<T> {
+ label: string;
value: T;
- onSelect: (value: T) => void;
}
-interface ISelectorState<T> {
- hoveredButtonValue?: T;
+interface ISelectorProps<T> {
+ title: string;
+ values: Array<ISelectorItem<T>>;
+ value?: T;
+ onSelect: (value?: T) => void;
}
-class Selector<T> extends Component<ISelectorProps<T>, ISelectorState<T>> {
- public state: ISelectorState<T> = {};
-
+class Selector<T> extends Component<ISelectorProps<T>> {
public render() {
return (
- <View>
- <View style={styles.advanced_settings__section_title}>{this.props.title}</View>
-
- {this.props.values.map((value) => this.renderCell(value))}
- </View>
+ <Cell.Section>
+ <Cell.SectionTitle>{this.props.title}</Cell.SectionTitle>
+ <SelectorCell
+ key={'auto'}
+ selected={this.props.value === undefined}
+ onSelect={this.props.onSelect}>
+ {'Automatic'}
+ </SelectorCell>
+ {this.props.values.map((item, i) => (
+ <SelectorCell
+ key={i}
+ value={item.value}
+ selected={item.value === this.props.value}
+ onSelect={this.props.onSelect}>
+ {item.label}
+ </SelectorCell>
+ ))}
+ </Cell.Section>
);
}
+}
- private handleButtonHover = (value?: T) => {
- this.setState({ hoveredButtonValue: value });
- };
-
- private renderCell(value: T) {
- const selected = value === this.props.value;
- if (selected) {
- return this.renderSelectedCell(value);
- } else {
- return this.renderUnselectedCell(value);
- }
- }
+interface ISelectorCell<T> {
+ value?: T;
+ selected: boolean;
+ onSelect: (value?: T) => void;
+ children?: React.ReactText;
+}
- private renderSelectedCell(value: T) {
+class SelectorCell<T> extends Component<ISelectorCell<T>> {
+ public render() {
return (
- <Button
- style={[
- styles.advanced_settings__cell,
- value === this.state.hoveredButtonValue
- ? [styles.advanced_settings__cell_selected_hover]
- : undefined,
- ]}
- onPress={() => this.props.onSelect(value)}
- onHoverStart={() => this.handleButtonHover(value)}
- onHoverEnd={() => this.handleButtonHover(undefined)}
- key={value.toString()}>
- <ImageView
- style={styles.advanced_settings__cell_icon}
+ <Cell.CellButton
+ style={this.props.selected ? styles.advanced_settings__cell_selected_hover : undefined}
+ cellHoverStyle={
+ this.props.selected ? styles.advanced_settings__cell_selected_hover : undefined
+ }
+ onPress={this.onPress}>
+ <Cell.Icon
+ style={this.props.selected ? undefined : styles.advanced_settings__cell_icon_invisible}
source="icon-tick"
+ width={24}
+ height={24}
tintColor={colors.white}
/>
- <Text style={styles.advanced_settings__cell_label}>{value}</Text>
- </Button>
+ <Cell.Label>{this.props.children}</Cell.Label>
+ </Cell.CellButton>
);
}
- private renderUnselectedCell(value: T) {
- return (
- <Button
- style={[
- styles.advanced_settings__cell_dimmed,
- value === this.state.hoveredButtonValue
- ? styles.advanced_settings__cell_hover
- : undefined,
- ]}
- onPress={() => this.props.onSelect(value)}
- onHoverStart={() => this.handleButtonHover(value)}
- onHoverEnd={() => this.handleButtonHover(undefined)}
- key={value.toString()}>
- <View style={styles.advanced_settings__cell_icon} />
- <Text style={styles.advanced_settings__cell_label}>{value}</Text>
- </Button>
- );
- }
+ private onPress = () => {
+ if (!this.props.selected) {
+ this.props.onSelect(this.props.value);
+ }
+ };
}
diff --git a/gui/packages/desktop/src/renderer/components/AdvancedSettingsStyles.tsx b/gui/packages/desktop/src/renderer/components/AdvancedSettingsStyles.tsx
index b864c72ebf..fd8be66c98 100644
--- a/gui/packages/desktop/src/renderer/components/AdvancedSettingsStyles.tsx
+++ b/gui/packages/desktop/src/renderer/components/AdvancedSettingsStyles.tsx
@@ -7,7 +7,6 @@ export default {
flex: 1,
}),
advanced_settings__container: Styles.createViewStyle({
- flexDirection: 'column',
flex: 1,
}),
// plain CSS style
@@ -15,20 +14,7 @@ export default {
flex: 1,
},
advanced_settings__content: Styles.createViewStyle({
- flexDirection: 'column',
flex: 0,
- overflow: 'visible',
- }),
- advanced_settings__cell: Styles.createButtonStyle({
- cursor: 'default',
- backgroundColor: colors.green,
- flexDirection: 'row',
- paddingTop: 14,
- paddingBottom: 14,
- paddingLeft: 24,
- paddingRight: 24,
- marginBottom: 1,
- justifyContent: 'flex-start',
}),
advanced_settings__cell_hover: Styles.createButtonStyle({
backgroundColor: colors.blue80,
@@ -39,36 +25,8 @@ export default {
advanced_settings__cell_spacer: Styles.createViewStyle({
height: 24,
}),
- advanced_settings__cell_icon: Styles.createViewStyle({
- width: 24,
- height: 24,
- marginRight: 8,
- flex: 0,
- }),
- advanced_settings__cell_dimmed: Styles.createButtonStyle({
- cursor: 'default',
- paddingTop: 14,
- paddingBottom: 14,
- paddingLeft: 24,
- paddingRight: 24,
- marginBottom: 1,
- backgroundColor: colors.blue40,
- flexDirection: 'row',
- justifyContent: 'flex-start',
- }),
-
- advanced_settings__section_title: Styles.createTextStyle({
- backgroundColor: colors.blue,
- paddingTop: 14,
- paddingBottom: 14,
- paddingLeft: 24,
- paddingRight: 24,
- marginBottom: 1,
- fontFamily: 'DINPro',
- fontSize: 20,
- fontWeight: '900',
- lineHeight: 26,
- color: colors.white,
+ advanced_settings__cell_icon_invisible: Styles.createViewStyle({
+ opacity: 0,
}),
advanced_settings__cell_label: Styles.createTextStyle({
fontFamily: 'DINPro',
diff --git a/gui/packages/desktop/src/renderer/components/Cell.tsx b/gui/packages/desktop/src/renderer/components/Cell.tsx
index c2dedc9dfc..501e51a0c7 100644
--- a/gui/packages/desktop/src/renderer/components/Cell.tsx
+++ b/gui/packages/desktop/src/renderer/components/Cell.tsx
@@ -4,19 +4,25 @@ import { Button, Component, Styles, Text, TextInput, Types, View } from 'reactxp
import { colors } from '../../config.json';
const styles = {
- cellButton: Styles.createButtonStyle({
- backgroundColor: colors.blue,
- paddingTop: 0,
- paddingBottom: 0,
- paddingLeft: 16,
- paddingRight: 16,
- marginBottom: 1,
- flex: 1,
- flexDirection: 'row',
- alignItems: 'center',
- alignContent: 'center',
- cursor: '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,
+ }),
+ },
cellContainer: Styles.createViewStyle({
backgroundColor: colors.blue,
flexDirection: 'row',
@@ -24,7 +30,6 @@ const styles = {
paddingLeft: 16,
paddingRight: 12,
}),
-
footer: {
container: Styles.createViewStyle({
paddingTop: 8,
@@ -41,7 +46,6 @@ const styles = {
color: colors.white80,
}),
},
-
label: {
container: Styles.createViewStyle({
marginLeft: 8,
@@ -58,7 +62,6 @@ const styles = {
color: colors.white,
}),
},
-
input: {
frame: Styles.createViewStyle({
flexGrow: 0,
@@ -77,14 +80,9 @@ const styles = {
textAlign: 'right',
}),
},
-
- cellHover: Styles.createButtonStyle({
- backgroundColor: colors.blue80,
- }),
icon: Styles.createViewStyle({
marginLeft: 8,
}),
-
subtext: Styles.createTextStyle({
color: colors.white60,
fontFamily: 'Open Sans',
@@ -94,6 +92,17 @@ const styles = {
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 {
@@ -108,6 +117,7 @@ interface IState {
hovered: boolean;
}
+const CellSectionContext = React.createContext<boolean>(false);
const CellHoverContext = React.createContext<boolean>(false);
export class CellButton extends Component<ICellButtonProps, IState> {
@@ -118,15 +128,50 @@ export class CellButton extends Component<ICellButtonProps, IState> {
public render() {
const { children, style, cellHoverStyle, ...otherProps } = this.props;
- const hoverStyle = cellHoverStyle || styles.cellHover;
+ const hoverStyle = cellHoverStyle || styles.cellButton.hover;
+ return (
+ <CellSectionContext.Consumer>
+ {(containedInSection) => (
+ <Button
+ style={[
+ styles.cellButton.base,
+ containedInSection ? styles.cellButton.section : undefined,
+ style,
+ this.state.hovered ? hoverStyle : undefined,
+ ]}
+ onHoverStart={this.onHoverStart}
+ onHoverEnd={this.onHoverEnd}
+ {...otherProps}>
+ <CellHoverContext.Provider value={this.state.hovered}>
+ {children}
+ </CellHoverContext.Provider>
+ </Button>
+ )}
+ </CellSectionContext.Consumer>
+ );
+ }
+}
+
+interface ISectionTitleProps {
+ children?: React.ReactText;
+}
+
+export function SectionTitle(props: ISectionTitleProps) {
+ return <Text style={styles.sectionTitle}>{props.children}</Text>;
+}
+
+interface ISectionProps {
+ children?: React.ReactNode;
+}
+
+export class Section extends Component<ISectionProps> {
+ public render() {
return (
- <Button
- style={[styles.cellButton, style, this.state.hovered ? hoverStyle : undefined]}
- onHoverStart={this.onHoverStart}
- onHoverEnd={this.onHoverEnd}
- {...otherProps}>
- <CellHoverContext.Provider value={this.state.hovered}>{children}</CellHoverContext.Provider>
- </Button>
+ <View>
+ <CellSectionContext.Provider value={true}>
+ {this.props.children}
+ </CellSectionContext.Provider>
+ </View>
);
}
}
diff --git a/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.tsx b/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.tsx
index d70c0a99ee..5fbdd57b25 100644
--- a/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.tsx
+++ b/gui/packages/desktop/src/renderer/containers/AdvancedSettingsPage.tsx
@@ -3,7 +3,7 @@ import log from 'electron-log';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { RelayProtocol } from '../../shared/daemon-rpc-types';
-import { AdvancedSettings } from '../components/AdvancedSettings';
+import AdvancedSettings from '../components/AdvancedSettings';
import RelaySettingsBuilder from '../lib/relay-settings-builder';
import { RelaySettingsRedux } from '../redux/settings/reducers';
@@ -25,8 +25,8 @@ const mapRelaySettingsToProtocolAndPort = (relaySettings: RelaySettingsRedux) =>
if ('normal' in relaySettings) {
const { protocol, port } = relaySettings.normal;
return {
- protocol: protocol === 'any' ? 'Automatic' : protocol,
- port: port === 'any' ? 'Automatic' : port,
+ protocol: protocol === 'any' ? undefined : protocol,
+ port: port === 'any' ? undefined : port,
};
} else if ('customTunnelEndpoint' in relaySettings) {
const { protocol, port } = relaySettings.customTunnelEndpoint;
@@ -42,18 +42,19 @@ const mapDispatchToProps = (dispatch: ReduxDispatch, props: ISharedRouteProps) =
onClose: () => {
history.goBack();
},
- onUpdate: async (protocol: string, port: string | number) => {
+ setRelayProtocolAndPort: async (protocol?: RelayProtocol, port?: number) => {
const relayUpdate = RelaySettingsBuilder.normal()
.tunnel.openvpn((openvpn) => {
- if (protocol === 'Automatic') {
- openvpn.protocol.any();
+ if (protocol) {
+ openvpn.protocol.exact(protocol);
} else {
- openvpn.protocol.exact(protocol.toLowerCase() as RelayProtocol);
+ openvpn.protocol.any();
}
- if (typeof port === 'string' && port === 'Automatic') {
- openvpn.port.any();
- } else if (typeof port === 'number') {
+
+ if (port) {
openvpn.port.exact(port);
+ } else {
+ openvpn.port.any();
}
})
.build();