summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2020-08-19 10:44:29 +0200
committerOskar Nyberg <oskar@mullvad.net>2020-08-19 10:44:29 +0200
commiteccdb59ddf88eb42736c880331d3034766f3e21b (patch)
tree9ee68713f40c68a52e577407e6187de763ebe1fd
parentdd1689df906ae8f4782f25970aba56bd3e3bf9d5 (diff)
parent00a9d8c9942369f0869294a0473f763bc93e3b06 (diff)
downloadmullvadvpn-eccdb59ddf88eb42736c880331d3034766f3e21b.tar.xz
mullvadvpn-eccdb59ddf88eb42736c880331d3034766f3e21b.zip
Merge branch 'convert-components-from-reactxp' into master
-rw-r--r--gui/src/renderer/components/AdvancedSettings.tsx446
-rw-r--r--gui/src/renderer/components/AdvancedSettingsStyles.tsx60
-rw-r--r--gui/src/renderer/components/ConnectionPanel.tsx91
-rw-r--r--gui/src/renderer/components/ConnectionPanelDisclosure.tsx96
-rw-r--r--gui/src/renderer/components/ErrorBoundary.tsx76
-rw-r--r--gui/src/renderer/components/Launch.tsx69
-rw-r--r--gui/src/renderer/components/Preferences.tsx262
-rw-r--r--gui/src/renderer/components/PreferencesStyles.tsx35
-rw-r--r--gui/src/renderer/components/SelectLanguage.tsx87
-rw-r--r--gui/src/renderer/components/Settings.tsx140
-rw-r--r--gui/src/renderer/components/SettingsStyles.tsx65
11 files changed, 666 insertions, 761 deletions
diff --git a/gui/src/renderer/components/AdvancedSettings.tsx b/gui/src/renderer/components/AdvancedSettings.tsx
index 7d6724b001..40131d58ff 100644
--- a/gui/src/renderer/components/AdvancedSettings.tsx
+++ b/gui/src/renderer/components/AdvancedSettings.tsx
@@ -1,17 +1,22 @@
import * as React from 'react';
-import { Component, Text, View } from 'reactxp';
import { sprintf } from 'sprintf-js';
import { BridgeState, RelayProtocol, TunnelProtocol } from '../../shared/daemon-rpc-types';
import { messages } from '../../shared/gettext';
import { WgKeyState } from '../redux/settings/reducers';
-import styles, {
- InputFrame,
+import {
+ StyledBottomCellGroup,
+ StyledContainer,
+ StyledInputFrame,
StyledNavigationScrollbars,
- TunnelProtocolSelector,
+ StyledNoWireguardKeyError,
+ StyledNoWireguardKeyErrorContainer,
+ StyledSelectorContainer,
+ StyledTunnelProtocolSelector,
+ StyledTunnelProtocolContainer,
} from './AdvancedSettingsStyles';
import * as AppButton from './AppButton';
import * as Cell from './Cell';
-import { Container, Layout } from './Layout';
+import { Layout } from './Layout';
import { ModalAlert, ModalAlertType, ModalContainer, ModalMessage } from './Modal';
import {
BackBarItem,
@@ -70,7 +75,7 @@ interface IState {
showConfirmBlockWhenDisconnectedAlert: boolean;
}
-export default class AdvancedSettings extends Component<IProps, IState> {
+export default class AdvancedSettings extends React.Component<IProps, IState> {
private portItems: { [key in RelayProtocol]: Array<ISelectorItem<OptionalPort>> };
private protocolItems: Array<ISelectorItem<OptionalRelayProtocol>>;
private bridgeStateItems: Array<ISelectorItem<BridgeState>>;
@@ -138,248 +143,229 @@ export default class AdvancedSettings extends Component<IProps, IState> {
return (
<ModalContainer>
<Layout>
- <Container>
- <View style={styles.advanced_settings}>
- <NavigationContainer>
- <NavigationBar>
- <NavigationItems>
- <BackBarItem action={this.props.onClose}>
- {
- // TRANSLATORS: Back button in navigation bar
- messages.pgettext('navigation-bar', 'Settings')
- }
- </BackBarItem>
- <TitleBarItem>
- {
- // TRANSLATORS: Title label in navigation bar
- messages.pgettext('advanced-settings-nav', 'Advanced')
- }
- </TitleBarItem>
- </NavigationItems>
- </NavigationBar>
+ <StyledContainer>
+ <NavigationContainer>
+ <NavigationBar>
+ <NavigationItems>
+ <BackBarItem action={this.props.onClose}>
+ {
+ // TRANSLATORS: Back button in navigation bar
+ messages.pgettext('navigation-bar', 'Settings')
+ }
+ </BackBarItem>
+ <TitleBarItem>
+ {
+ // TRANSLATORS: Title label in navigation bar
+ messages.pgettext('advanced-settings-nav', 'Advanced')
+ }
+ </TitleBarItem>
+ </NavigationItems>
+ </NavigationBar>
- <View style={styles.advanced_settings__container}>
- <StyledNavigationScrollbars>
- <SettingsHeader>
- <HeaderTitle>
- {messages.pgettext('advanced-settings-view', 'Advanced')}
- </HeaderTitle>
- </SettingsHeader>
+ <StyledNavigationScrollbars>
+ <SettingsHeader>
+ <HeaderTitle>
+ {messages.pgettext('advanced-settings-view', 'Advanced')}
+ </HeaderTitle>
+ </SettingsHeader>
- <Cell.Container>
- <Cell.Label>
- {messages.pgettext('advanced-settings-view', 'Enable IPv6')}
- </Cell.Label>
- <Cell.Switch
- isOn={this.props.enableIpv6}
- onChange={this.props.setEnableIpv6}
- />
- </Cell.Container>
- <Cell.Footer>
- <Cell.FooterText>
- {messages.pgettext(
- 'advanced-settings-view',
- 'Enable IPv6 communication through the tunnel.',
- )}
- </Cell.FooterText>
- </Cell.Footer>
+ <Cell.Container>
+ <Cell.Label>
+ {messages.pgettext('advanced-settings-view', 'Enable IPv6')}
+ </Cell.Label>
+ <Cell.Switch isOn={this.props.enableIpv6} onChange={this.props.setEnableIpv6} />
+ </Cell.Container>
+ <Cell.Footer>
+ <Cell.FooterText>
+ {messages.pgettext(
+ 'advanced-settings-view',
+ 'Enable IPv6 communication through the tunnel.',
+ )}
+ </Cell.FooterText>
+ </Cell.Footer>
- <Cell.Container>
- <Cell.Label>
- {messages.pgettext('advanced-settings-view', 'Always require VPN')}
- </Cell.Label>
- <Cell.Switch
- isOn={this.props.blockWhenDisconnected}
- onChange={this.setBlockWhenDisconnected}
- />
- </Cell.Container>
- <Cell.Footer>
- <Cell.FooterText>
+ <Cell.Container>
+ <Cell.Label>
+ {messages.pgettext('advanced-settings-view', 'Always require VPN')}
+ </Cell.Label>
+ <Cell.Switch
+ isOn={this.props.blockWhenDisconnected}
+ onChange={this.setBlockWhenDisconnected}
+ />
+ </Cell.Container>
+ <Cell.Footer>
+ <Cell.FooterText>
+ {messages.pgettext(
+ 'advanced-settings-view',
+ 'If you disconnect or quit the app, this setting will block your internet.',
+ )}
+ </Cell.FooterText>
+ </Cell.Footer>
+
+ <StyledTunnelProtocolContainer>
+ <StyledTunnelProtocolSelector
+ title={messages.pgettext('advanced-settings-view', 'Tunnel protocol')}
+ values={this.tunnelProtocolItems(hasWireguardKey)}
+ value={this.props.tunnelProtocol}
+ onSelect={this.onSelectTunnelProtocol}
+ />
+ {!hasWireguardKey && (
+ <StyledNoWireguardKeyErrorContainer>
+ <StyledNoWireguardKeyError>
{messages.pgettext(
'advanced-settings-view',
- 'If you disconnect or quit the app, this setting will block your internet.',
+ 'To enable WireGuard, generate a key under the "WireGuard key" setting below.',
)}
- </Cell.FooterText>
- </Cell.Footer>
-
- <View
- style={[
- styles.advanced_settings__content,
- styles.advanced_settings__cell_bottom_margin,
- ]}>
- <TunnelProtocolSelector
- title={messages.pgettext('advanced-settings-view', 'Tunnel protocol')}
- values={this.tunnelProtocolItems(hasWireguardKey)}
- value={this.props.tunnelProtocol}
- onSelect={this.onSelectTunnelProtocol}
- />
- {!hasWireguardKey && (
- <Text style={styles.advanced_settings__wg_no_key}>
- {messages.pgettext(
- 'advanced-settings-view',
- 'To enable WireGuard, generate a key under the "WireGuard key" setting below.',
- )}
- </Text>
- )}
- </View>
-
- {this.props.tunnelProtocol !== 'wireguard' ? (
- <View style={styles.advanced_settings__content}>
- <Selector
- title={messages.pgettext(
- 'advanced-settings-view',
- 'OpenVPN transport protocol',
- )}
- values={this.protocolItems}
- value={this.props.openvpn.protocol}
- onSelect={this.onSelectOpenvpnProtocol}
- />
-
- {this.props.openvpn.protocol ? (
- <Selector
- title={sprintf(
- // TRANSLATORS: The title for the port selector section.
- // TRANSLATORS: Available placeholders:
- // TRANSLATORS: %(portType)s - a selected protocol (either TCP or UDP)
- messages.pgettext(
- 'advanced-settings-view',
- 'OpenVPN %(portType)s port',
- ),
- {
- portType: this.props.openvpn.protocol.toUpperCase(),
- },
- )}
- values={this.portItems[this.props.openvpn.protocol]}
- value={this.props.openvpn.port}
- onSelect={this.onSelectOpenVpnPort}
- />
- ) : undefined}
- </View>
- ) : undefined}
-
- {this.props.tunnelProtocol === 'wireguard' ? (
- <View style={styles.advanced_settings__content}>
- <Selector
- // TRANSLATORS: The title for the shadowsocks bridge selector section.
- title={messages.pgettext('advanced-settings-view', 'WireGuard port')}
- values={this.wireguardPortItems}
- value={this.props.wireguard.port}
- onSelect={this.onSelectWireguardPort}
- />
- </View>
- ) : undefined}
+ </StyledNoWireguardKeyError>
+ </StyledNoWireguardKeyErrorContainer>
+ )}
+ </StyledTunnelProtocolContainer>
+ {this.props.tunnelProtocol !== 'wireguard' ? (
+ <StyledSelectorContainer>
<Selector
- title={
- // TRANSLATORS: The title for the shadowsocks bridge selector section.
- messages.pgettext('advanced-settings-view', 'Bridge mode')
- }
- values={this.bridgeStateItems}
- value={this.props.bridgeState}
- onSelect={this.onSelectBridgeState}
+ title={messages.pgettext(
+ 'advanced-settings-view',
+ 'OpenVPN transport protocol',
+ )}
+ values={this.protocolItems}
+ value={this.props.openvpn.protocol}
+ onSelect={this.onSelectOpenvpnProtocol}
/>
- <Cell.Container>
- <Cell.Label>
- {messages.pgettext('advanced-settings-view', 'OpenVPN Mssfix')}
- </Cell.Label>
- <InputFrame>
- <Cell.AutoSizingTextInput
- value={this.props.mssfix ? this.props.mssfix.toString() : ''}
- inputMode={'numeric'}
- maxLength={4}
- placeholder={messages.pgettext('advanced-settings-view', 'Default')}
- onSubmitValue={this.onMssfixSubmit}
- validateValue={AdvancedSettings.mssfixIsValid}
- submitOnBlur={true}
- modifyValue={AdvancedSettings.removeNonNumericCharacters}
- />
- </InputFrame>
- </Cell.Container>
- <Cell.Footer>
- <Cell.FooterText>
- {sprintf(
- // TRANSLATORS: The hint displayed below the Mssfix input field.
+ {this.props.openvpn.protocol ? (
+ <Selector
+ title={sprintf(
+ // TRANSLATORS: The title for the port selector section.
// TRANSLATORS: Available placeholders:
- // TRANSLATORS: %(max)d - the maximum possible mssfix value
- // TRANSLATORS: %(min)d - the minimum possible mssfix value
- messages.pgettext(
- 'advanced-settings-view',
- 'Set OpenVPN MSS value. Valid range: %(min)d - %(max)d.',
- ),
+ // TRANSLATORS: %(portType)s - a selected protocol (either TCP or UDP)
+ messages.pgettext('advanced-settings-view', 'OpenVPN %(portType)s port'),
{
- min: MIN_MSSFIX_VALUE,
- max: MAX_MSSFIX_VALUE,
+ portType: this.props.openvpn.protocol.toUpperCase(),
},
)}
- </Cell.FooterText>
- </Cell.Footer>
+ values={this.portItems[this.props.openvpn.protocol]}
+ value={this.props.openvpn.port}
+ onSelect={this.onSelectOpenVpnPort}
+ />
+ ) : undefined}
+ </StyledSelectorContainer>
+ ) : undefined}
- <Cell.Container>
- <Cell.Label>
- {messages.pgettext('advanced-settings-view', 'WireGuard MTU')}
- </Cell.Label>
- <InputFrame>
- <Cell.AutoSizingTextInput
- value={this.props.wireguardMtu ? this.props.wireguardMtu.toString() : ''}
- inputMode={'numeric'}
- maxLength={4}
- placeholder={messages.pgettext('advanced-settings-view', 'Default')}
- onSubmitValue={this.onWireguardMtuSubmit}
- validateValue={AdvancedSettings.wireguarMtuIsValid}
- submitOnBlur={true}
- modifyValue={AdvancedSettings.removeNonNumericCharacters}
- />
- </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>
+ {this.props.tunnelProtocol === 'wireguard' ? (
+ <StyledSelectorContainer>
+ <Selector
+ // TRANSLATORS: The title for the shadowsocks bridge selector section.
+ title={messages.pgettext('advanced-settings-view', 'WireGuard port')}
+ values={this.wireguardPortItems}
+ value={this.props.wireguard.port}
+ onSelect={this.onSelectWireguardPort}
+ />
+ </StyledSelectorContainer>
+ ) : undefined}
- <View
- style={
- process.platform !== 'linux'
- ? styles.advanced_settings__last_cell_bottom_margin
- : undefined
- }>
- <Cell.CellButton onClick={this.props.onViewWireguardKeys}>
- <Cell.Label>
- {messages.pgettext('advanced-settings-view', 'WireGuard key')}
- </Cell.Label>
- <Cell.Icon height={12} width={7} source="icon-chevron" />
- </Cell.CellButton>
- </View>
+ <Selector
+ title={
+ // TRANSLATORS: The title for the shadowsocks bridge selector section.
+ messages.pgettext('advanced-settings-view', 'Bridge mode')
+ }
+ values={this.bridgeStateItems}
+ value={this.props.bridgeState}
+ onSelect={this.onSelectBridgeState}
+ />
- {process.platform === 'linux' && (
- <View style={styles.advanced_settings__last_cell_bottom_margin}>
- <Cell.CellButton onClick={this.props.onViewLinuxSplitTunneling}>
- <Cell.Label>
- {messages.pgettext('advanced-settings-view', 'Split tunneling')}
- </Cell.Label>
- <Cell.Icon height={12} width={7} source="icon-chevron" />
- </Cell.CellButton>
- </View>
+ <Cell.Container>
+ <Cell.Label>
+ {messages.pgettext('advanced-settings-view', 'OpenVPN Mssfix')}
+ </Cell.Label>
+ <StyledInputFrame>
+ <Cell.AutoSizingTextInput
+ value={this.props.mssfix ? this.props.mssfix.toString() : ''}
+ inputMode={'numeric'}
+ maxLength={4}
+ placeholder={messages.pgettext('advanced-settings-view', 'Default')}
+ onSubmitValue={this.onMssfixSubmit}
+ validateValue={AdvancedSettings.mssfixIsValid}
+ submitOnBlur={true}
+ modifyValue={AdvancedSettings.removeNonNumericCharacters}
+ />
+ </StyledInputFrame>
+ </Cell.Container>
+ <Cell.Footer>
+ <Cell.FooterText>
+ {sprintf(
+ // TRANSLATORS: The hint displayed below the Mssfix input field.
+ // TRANSLATORS: Available placeholders:
+ // TRANSLATORS: %(max)d - the maximum possible mssfix value
+ // TRANSLATORS: %(min)d - the minimum possible mssfix value
+ messages.pgettext(
+ 'advanced-settings-view',
+ 'Set OpenVPN MSS value. Valid range: %(min)d - %(max)d.',
+ ),
+ {
+ min: MIN_MSSFIX_VALUE,
+ max: MAX_MSSFIX_VALUE,
+ },
)}
- </StyledNavigationScrollbars>
- </View>
- </NavigationContainer>
- </View>
- </Container>
+ </Cell.FooterText>
+ </Cell.Footer>
+
+ <Cell.Container>
+ <Cell.Label>
+ {messages.pgettext('advanced-settings-view', 'WireGuard MTU')}
+ </Cell.Label>
+ <StyledInputFrame>
+ <Cell.AutoSizingTextInput
+ value={this.props.wireguardMtu ? this.props.wireguardMtu.toString() : ''}
+ inputMode={'numeric'}
+ maxLength={4}
+ placeholder={messages.pgettext('advanced-settings-view', 'Default')}
+ onSubmitValue={this.onWireguardMtuSubmit}
+ validateValue={AdvancedSettings.wireguarMtuIsValid}
+ submitOnBlur={true}
+ modifyValue={AdvancedSettings.removeNonNumericCharacters}
+ />
+ </StyledInputFrame>
+ </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>
+
+ <StyledBottomCellGroup>
+ <Cell.CellButton onClick={this.props.onViewWireguardKeys}>
+ <Cell.Label>
+ {messages.pgettext('advanced-settings-view', 'WireGuard key')}
+ </Cell.Label>
+ <Cell.Icon height={12} width={7} source="icon-chevron" />
+ </Cell.CellButton>
+
+ {process.platform === 'linux' && (
+ <Cell.CellButton onClick={this.props.onViewLinuxSplitTunneling}>
+ <Cell.Label>
+ {messages.pgettext('advanced-settings-view', 'Split tunneling')}
+ </Cell.Label>
+ <Cell.Icon height={12} width={7} source="icon-chevron" />
+ </Cell.CellButton>
+ )}
+ </StyledBottomCellGroup>
+ </StyledNavigationScrollbars>
+ </NavigationContainer>
+ </StyledContainer>
</Layout>
{this.state.showConfirmBlockWhenDisconnectedAlert &&
diff --git a/gui/src/renderer/components/AdvancedSettingsStyles.tsx b/gui/src/renderer/components/AdvancedSettingsStyles.tsx
index e39a7b4ed7..f9bd6db82b 100644
--- a/gui/src/renderer/components/AdvancedSettingsStyles.tsx
+++ b/gui/src/renderer/components/AdvancedSettingsStyles.tsx
@@ -1,46 +1,46 @@
-import { Styles } from 'reactxp';
import styled from 'styled-components';
import { colors } from '../../config.json';
import * as Cell from './Cell';
+import { Container } from './Layout';
import { NavigationScrollbars } from './NavigationBar';
import Selector from './Selector';
-export const InputFrame = styled(Cell.InputFrame)({
+export const StyledContainer = styled(Container)({
+ backgroundColor: colors.darkBlue,
+});
+
+export const StyledInputFrame = styled(Cell.InputFrame)({
flex: 0,
});
-export const TunnelProtocolSelector = (styled(Selector)({
+export const StyledSelectorContainer = styled.div({
+ flex: 0,
+});
+
+export const StyledTunnelProtocolSelector = (styled(Selector)({
marginBottom: 0,
}) as unknown) as new <T>() => Selector<T>;
+export const StyledTunnelProtocolContainer = styled(StyledSelectorContainer)({
+ marginBottom: '20px',
+});
+
export const StyledNavigationScrollbars = styled(NavigationScrollbars)({
flex: 1,
});
-export default {
- advanced_settings: Styles.createViewStyle({
- backgroundColor: colors.darkBlue,
- flex: 1,
- }),
- advanced_settings__container: Styles.createViewStyle({
- flex: 1,
- }),
- advanced_settings__content: Styles.createViewStyle({
- flex: 0,
- }),
- advanced_settings__cell_bottom_margin: Styles.createViewStyle({
- marginBottom: 20,
- }),
- advanced_settings__last_cell_bottom_margin: Styles.createViewStyle({
- marginBottom: 22,
- }),
- advanced_settings__wg_no_key: Styles.createTextStyle({
- fontFamily: 'Open Sans',
- fontSize: 13,
- fontWeight: '800',
- lineHeight: 20,
- color: colors.red,
- marginTop: 12,
- paddingHorizontal: 22,
- }),
-};
+export const StyledBottomCellGroup = styled.div({
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ marginBottom: '22px',
+});
+
+export const StyledNoWireguardKeyErrorContainer = styled(Cell.Footer)({
+ paddingBottom: 0,
+});
+
+export const StyledNoWireguardKeyError = styled(Cell.FooterText)({
+ fontWeight: 800,
+ color: colors.red,
+});
diff --git a/gui/src/renderer/components/ConnectionPanel.tsx b/gui/src/renderer/components/ConnectionPanel.tsx
index 156a46ff89..248d466706 100644
--- a/gui/src/renderer/components/ConnectionPanel.tsx
+++ b/gui/src/renderer/components/ConnectionPanel.tsx
@@ -1,6 +1,7 @@
import * as React from 'react';
-import { Component, Styles, Text, Types, View } from 'reactxp';
import { sprintf } from 'sprintf-js';
+import styled from 'styled-components';
+import { colors } from '../../config.json';
import {
ProxyType,
proxyTypeToString,
@@ -38,80 +39,76 @@ interface IProps {
bridgeInfo?: IBridgeData;
outAddress?: IOutAddress;
onToggle: () => void;
- style?: Types.ViewStyleRuleSet | Types.ViewStyleRuleSet[];
+ className?: string;
}
-const styles = {
- row: Styles.createViewStyle({
- flexDirection: 'row',
- marginTop: 3,
- }),
- caption: Styles.createTextStyle({
- fontFamily: 'Open Sans',
- fontSize: 13,
- lineHeight: 15,
- fontWeight: '600',
- color: 'rgb(255, 255, 255)',
- flex: 0,
- marginRight: 8,
- }),
- value: Styles.createTextStyle({
- fontFamily: 'Open Sans',
- fontSize: 13,
- lineHeight: 15,
- fontWeight: '600',
- color: 'rgb(255, 255, 255)',
- }),
- header: Styles.createViewStyle({
- flexDirection: 'row',
- alignItems: 'center',
- }),
-};
+const Row = styled.div({
+ display: 'flex',
+ marginTop: '3px',
+});
-export default class ConnectionPanel extends Component<IProps> {
+const Text = styled.span({
+ fontFamily: 'Open Sans',
+ fontSize: '13px',
+ lineHeight: '15px',
+ fontWeight: 600,
+ color: colors.white,
+});
+
+const Caption = styled(Text)({
+ flex: 0,
+ marginRight: '8px',
+});
+
+const Header = styled.div({
+ display: 'flex',
+ alignItems: 'center',
+});
+
+export default class ConnectionPanel extends React.Component<IProps> {
public render() {
const { inAddress, outAddress, bridgeInfo } = this.props;
const entryPoint = bridgeInfo && inAddress ? bridgeInfo : inAddress;
return (
- <View style={this.props.style}>
+ <div className={this.props.className}>
{this.props.hostname && (
- <View style={styles.header}>
+ <Header>
<ConnectionPanelDisclosure pointsUp={this.props.isOpen} onToggle={this.props.onToggle}>
{this.hostnameLine()}
</ConnectionPanelDisclosure>
- </View>
+ </Header>
)}
{this.props.isOpen && this.props.hostname && (
<React.Fragment>
{this.props.inAddress && (
- <View style={styles.row}>
- <Text style={styles.value}>{this.transportLine()}</Text>
- </View>
+ <Row>
+ <Text>{this.transportLine()}</Text>
+ </Row>
)}
{entryPoint && (
- <View style={styles.row}>
- <Text style={styles.caption}>{messages.pgettext('connection-info', 'In')}</Text>
- <Text style={styles.value}>
+ <Row>
+ <Caption>{messages.pgettext('connection-info', 'In')}</Caption>
+ <Text>
{`${entryPoint.ip}:${entryPoint.port} ${entryPoint.protocol.toUpperCase()}`}
</Text>
- </View>
+ </Row>
)}
{outAddress && (outAddress.ipv4 || outAddress.ipv6) && (
- <View style={styles.row}>
- <Text style={styles.caption}>{messages.pgettext('connection-info', 'Out')}</Text>
- <View>
- {outAddress.ipv4 && <Text style={styles.value}>{outAddress.ipv4}</Text>}
- {outAddress.ipv6 && <Text style={styles.value}>{outAddress.ipv6}</Text>}
- </View>
- </View>
+ <Row>
+ <Caption>{messages.pgettext('connection-info', 'Out')}</Caption>
+ <div>
+ {outAddress.ipv4 && <Text>{outAddress.ipv4}</Text>}
+ {outAddress.ipv6 && <Text>{outAddress.ipv6}</Text>}
+ </div>
+ </Row>
)}
</React.Fragment>
)}
- </View>
+ </div>
);
}
diff --git a/gui/src/renderer/components/ConnectionPanelDisclosure.tsx b/gui/src/renderer/components/ConnectionPanelDisclosure.tsx
index 17c3dc9919..ad7c092f9c 100644
--- a/gui/src/renderer/components/ConnectionPanelDisclosure.tsx
+++ b/gui/src/renderer/components/ConnectionPanelDisclosure.tsx
@@ -1,73 +1,47 @@
import * as React from 'react';
-import { Component, Styles, Text, Types, View } from 'reactxp';
+import styled from 'styled-components';
+import { colors } from '../../config.json';
import ImageView from './ImageView';
-const styles = {
- container: Styles.createViewStyle({
- flexDirection: 'row',
- alignItems: 'center',
- }),
- caption: {
- base: Styles.createTextStyle({
- fontFamily: 'Open Sans',
- fontSize: 15,
- fontWeight: '600',
- lineHeight: 20,
- color: 'rgb(255, 255, 255, 0.4)',
- }),
- hovered: Styles.createTextStyle({
- color: 'rgb(255, 255, 255)',
- }),
+const Container = styled.div({
+ display: 'flex',
+ alignItems: 'center',
+});
+
+const Caption = styled.span((props: { open: boolean }) => ({
+ fontFamily: 'Open Sans',
+ fontSize: '15px',
+ fontWeight: 600,
+ lineHeight: '20px',
+ color: props.open ? colors.white : colors.white40,
+ [Container + ':hover &']: {
+ color: colors.white,
+ },
+}));
+
+const Chevron = styled(ImageView)({
+ [Container + ':hover &']: {
+ backgroundColor: colors.white,
},
-};
+});
interface IProps {
pointsUp: boolean;
onToggle?: () => void;
children: React.ReactText;
- style?: Types.ViewStyleRuleSet | Types.ViewStyleRuleSet[];
-}
-
-interface IState {
- isHovered: boolean;
+ className?: string;
}
-export default class ConnectionPanelDisclosure extends Component<IProps, IState> {
- constructor(props: IProps) {
- super(props);
-
- this.state = {
- isHovered: false,
- };
- }
-
- public render() {
- const tintColor = this.state.isHovered ? 'rgb(255, 255, 255)' : 'rgb(255, 255, 255, 0.4)';
- const textHoverStyle =
- this.props.pointsUp || this.state.isHovered ? styles.caption.hovered : undefined;
-
- return (
- <View
- style={[styles.container, this.props.style]}
- onMouseEnter={this.onMouseEnter}
- onMouseLeave={this.onMouseLeave}
- onPress={this.props.onToggle}>
- <Text style={[styles.caption.base, textHoverStyle]}>{this.props.children}</Text>
- <ImageView
- source={this.props.pointsUp ? 'icon-chevron-up' : 'icon-chevron-down'}
- width={24}
- height={24}
- tintColor={tintColor}
- />
- </View>
- );
- }
-
- private onMouseEnter = () => {
- this.setState({ isHovered: true });
- };
-
- private onMouseLeave = () => {
- this.setState({ isHovered: false });
- };
+export default function ConnectionPanelDisclosure(props: IProps) {
+ return (
+ <Container className={props.className} onClick={props.onToggle}>
+ <Caption open={props.pointsUp}>{props.children}</Caption>
+ <Chevron
+ source={props.pointsUp ? 'icon-chevron-up' : 'icon-chevron-down'}
+ width={24}
+ height={24}
+ tintColor={colors.white40}
+ />
+ </Container>
+ );
}
diff --git a/gui/src/renderer/components/ErrorBoundary.tsx b/gui/src/renderer/components/ErrorBoundary.tsx
index 8be0e8b5b8..bd20abcbdc 100644
--- a/gui/src/renderer/components/ErrorBoundary.tsx
+++ b/gui/src/renderer/components/ErrorBoundary.tsx
@@ -1,6 +1,5 @@
import log from 'electron-log';
-import * as React from 'react';
-import { Component, Styles, Text, View } from 'reactxp';
+import React from 'react';
import styled from 'styled-components';
import { colors, links } from '../../config.json';
import { messages } from '../../shared/gettext';
@@ -16,40 +15,41 @@ interface IState {
hasError: boolean;
}
-const styles = {
- container: Styles.createViewStyle({
- flex: 1,
- flexDirection: 'column',
- alignItems: 'center',
- justifyContent: 'center',
- backgroundColor: colors.blue,
- }),
- title: Styles.createTextStyle({
- fontFamily: 'DINPro',
- fontSize: 24,
- fontWeight: '900',
- lineHeight: 30,
- color: colors.white60,
- marginBottom: 4,
- }),
- subtitle: Styles.createTextStyle({
- fontFamily: 'Open Sans',
- fontSize: 14,
- lineHeight: 20,
- color: colors.white40,
- marginHorizontal: 22,
- textAlign: 'center',
- }),
- email: Styles.createTextStyle({
- fontWeight: '900',
- }),
-};
+const StyledContainer = styled(Container)({
+ flex: 1,
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: colors.blue,
+});
+
+const Title = styled.span({
+ fontFamily: 'DINPro',
+ fontSize: '24px',
+ fontWeight: 900,
+ lineHeight: '30px',
+ color: colors.white60,
+ marginBottom: '4px',
+});
+
+const Subtitle = styled.span({
+ fontFamily: 'Open Sans',
+ fontSize: '14px',
+ lineHeight: '20px',
+ color: colors.white40,
+ marginHorizontal: '22px',
+ textAlign: 'center',
+});
+
+const Email = styled.span({
+ fontWeight: 900,
+});
const Logo = styled(ImageView)({
marginBottom: '5px',
});
-export default class ErrorBoundary extends Component<IProps, IState> {
+export default class ErrorBoundary extends React.Component<IProps, IState> {
public state = { hasError: false };
public componentDidCatch(error: Error, info: React.ErrorInfo) {
@@ -71,18 +71,16 @@ export default class ErrorBoundary extends Component<IProps, IState> {
messages
.pgettext('error-boundary-view', 'Something went wrong. Please contact us at %(email)s')
.split('%(email)s', 2);
- reachBackMessage.splice(1, 0, <Text style={styles.email}>{links.supportEmail}</Text>);
+ reachBackMessage.splice(1, 0, <Email>{links.supportEmail}</Email>);
return (
<PlatformWindowContainer>
<Layout>
- <Container>
- <View style={styles.container}>
- <Logo height={106} width={106} source="logo-icon" />
- <Text style={styles.title}>{messages.pgettext('generic', 'MULLVAD VPN')}</Text>
- <Text style={styles.subtitle}>{reachBackMessage}</Text>
- </View>
- </Container>
+ <StyledContainer>
+ <Logo height={106} width={106} source="logo-icon" />
+ <Title>{messages.pgettext('generic', 'MULLVAD VPN')}</Title>
+ <Subtitle>{reachBackMessage}</Subtitle>
+ </StyledContainer>
</Layout>
</PlatformWindowContainer>
);
diff --git a/gui/src/renderer/components/Launch.tsx b/gui/src/renderer/components/Launch.tsx
index 62c561c7b8..9cb7a23d44 100644
--- a/gui/src/renderer/components/Launch.tsx
+++ b/gui/src/renderer/components/Launch.tsx
@@ -1,5 +1,4 @@
-import * as React from 'react';
-import { Styles, Text, View } from 'reactxp';
+import React from 'react';
import styled from 'styled-components';
import { colors } from '../../config.json';
import { messages } from '../../shared/gettext';
@@ -7,31 +6,31 @@ import { HeaderBarSettingsButton } from './HeaderBar';
import ImageView from './ImageView';
import { Container, Header, Layout } from './Layout';
-const styles = {
- container: Styles.createViewStyle({
- flex: 1,
- flexDirection: 'column',
- alignItems: 'center',
- justifyContent: 'center',
- marginTop: -150,
- }),
- title: Styles.createTextStyle({
- fontFamily: 'DINPro',
- fontSize: 24,
- fontWeight: '900',
- lineHeight: 30,
- color: colors.white60,
- marginBottom: 4,
- }),
- subtitle: Styles.createTextStyle({
- fontFamily: 'Open Sans',
- fontSize: 14,
- lineHeight: 20,
- marginHorizontal: 22,
- color: colors.white40,
- textAlign: 'center',
- }),
-};
+const StyledContainer = styled(Container)({
+ flex: 1,
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginTop: '-150px',
+});
+
+const Title = styled.span({
+ fontFamily: 'DINPro',
+ fontSize: '24px',
+ fontWeight: 900,
+ lineHeight: '30px',
+ color: colors.white60,
+ marginBottom: '4px',
+});
+
+const Subtitle = styled.span({
+ fontFamily: 'Open Sans',
+ fontSize: '14px',
+ lineHeight: '20px',
+ marginHorizontal: '22px',
+ color: colors.white40,
+ textAlign: 'center',
+});
const Logo = styled(ImageView)({
marginBottom: '5px',
@@ -43,15 +42,13 @@ export default function Launch() {
<Header>
<HeaderBarSettingsButton />
</Header>
- <Container>
- <View style={styles.container}>
- <Logo height={106} width={106} source="logo-icon" />
- <Text style={styles.title}>{messages.pgettext('generic', 'MULLVAD VPN')}</Text>
- <Text style={styles.subtitle}>
- {messages.pgettext('launch-view', 'Connecting to Mullvad system service...')}
- </Text>
- </View>
- </Container>
+ <StyledContainer>
+ <Logo height={106} width={106} source="logo-icon" />
+ <Title>{messages.pgettext('generic', 'MULLVAD VPN')}</Title>
+ <Subtitle>
+ {messages.pgettext('launch-view', 'Connecting to Mullvad system service...')}
+ </Subtitle>
+ </StyledContainer>
</Layout>
);
}
diff --git a/gui/src/renderer/components/Preferences.tsx b/gui/src/renderer/components/Preferences.tsx
index a7d28e145d..0b56c53221 100644
--- a/gui/src/renderer/components/Preferences.tsx
+++ b/gui/src/renderer/components/Preferences.tsx
@@ -1,8 +1,7 @@
import * as React from 'react';
-import { Component, View } from 'reactxp';
import { messages } from '../../shared/gettext';
import * as Cell from './Cell';
-import { Container, Layout } from './Layout';
+import { Layout } from './Layout';
import {
BackBarItem,
NavigationBar,
@@ -11,7 +10,7 @@ import {
NavigationScrollbars,
TitleBarItem,
} from './NavigationBar';
-import styles from './PreferencesStyles';
+import { StyledContainer, StyledContent, StyledSeparator } from './PreferencesStyles';
import SettingsHeader, { HeaderTitle } from './SettingsHeader';
export interface IProps {
@@ -34,166 +33,151 @@ export interface IProps {
onClose: () => void;
}
-export default class Preferences extends Component<IProps> {
+export default class Preferences extends React.Component<IProps> {
public render() {
return (
<Layout>
- <Container>
- <View style={styles.preferences}>
- <NavigationContainer>
- <NavigationBar>
- <NavigationItems>
- <BackBarItem action={this.props.onClose}>
- {
- // TRANSLATORS: Back button in navigation bar
- messages.pgettext('navigation-bar', 'Settings')
- }
- </BackBarItem>
- <TitleBarItem>
- {
- // TRANSLATORS: Title label in navigation bar
- messages.pgettext('preferences-nav', 'Preferences')
- }
- </TitleBarItem>
- </NavigationItems>
- </NavigationBar>
+ <StyledContainer>
+ <NavigationContainer>
+ <NavigationBar>
+ <NavigationItems>
+ <BackBarItem action={this.props.onClose}>
+ {
+ // TRANSLATORS: Back button in navigation bar
+ messages.pgettext('navigation-bar', 'Settings')
+ }
+ </BackBarItem>
+ <TitleBarItem>
+ {
+ // TRANSLATORS: Title label in navigation bar
+ messages.pgettext('preferences-nav', 'Preferences')
+ }
+ </TitleBarItem>
+ </NavigationItems>
+ </NavigationBar>
- <View style={styles.preferences__container}>
- <NavigationScrollbars>
- <SettingsHeader>
- <HeaderTitle>
- {messages.pgettext('preferences-view', 'Preferences')}
- </HeaderTitle>
- </SettingsHeader>
+ <NavigationScrollbars>
+ <SettingsHeader>
+ <HeaderTitle>{messages.pgettext('preferences-view', 'Preferences')}</HeaderTitle>
+ </SettingsHeader>
- <View style={styles.preferences__content}>
- <Cell.Container>
- <Cell.Label>
- {messages.pgettext('preferences-view', 'Launch app on start-up')}
- </Cell.Label>
- <Cell.Switch isOn={this.props.autoStart} onChange={this.props.setAutoStart} />
- </Cell.Container>
- <View style={styles.preferences__separator} />
+ <StyledContent>
+ <Cell.Container>
+ <Cell.Label>
+ {messages.pgettext('preferences-view', 'Launch app on start-up')}
+ </Cell.Label>
+ <Cell.Switch isOn={this.props.autoStart} onChange={this.props.setAutoStart} />
+ </Cell.Container>
+ <StyledSeparator />
- <Cell.Container>
- <Cell.Label>
- {messages.pgettext('preferences-view', 'Auto-connect')}
- </Cell.Label>
- <Cell.Switch
- isOn={this.props.autoConnect}
- onChange={this.props.setAutoConnect}
- />
- </Cell.Container>
- <Cell.Footer>
- <Cell.FooterText>
- {messages.pgettext(
- 'preferences-view',
- 'Automatically connect to a server when the app launches.',
- )}
- </Cell.FooterText>
- </Cell.Footer>
+ <Cell.Container>
+ <Cell.Label>{messages.pgettext('preferences-view', 'Auto-connect')}</Cell.Label>
+ <Cell.Switch isOn={this.props.autoConnect} onChange={this.props.setAutoConnect} />
+ </Cell.Container>
+ <Cell.Footer>
+ <Cell.FooterText>
+ {messages.pgettext(
+ 'preferences-view',
+ 'Automatically connect to a server when the app launches.',
+ )}
+ </Cell.FooterText>
+ </Cell.Footer>
- <Cell.Container>
- <Cell.Label>
- {messages.pgettext('preferences-view', 'Local network sharing')}
- </Cell.Label>
- <Cell.Switch isOn={this.props.allowLan} onChange={this.props.setAllowLan} />
- </Cell.Container>
- <Cell.Footer>
- <Cell.FooterText>
- {messages.pgettext(
- 'preferences-view',
- 'Allows access to other devices on the same network for sharing, printing etc.',
- )}
- </Cell.FooterText>
- </Cell.Footer>
+ <Cell.Container>
+ <Cell.Label>
+ {messages.pgettext('preferences-view', 'Local network sharing')}
+ </Cell.Label>
+ <Cell.Switch isOn={this.props.allowLan} onChange={this.props.setAllowLan} />
+ </Cell.Container>
+ <Cell.Footer>
+ <Cell.FooterText>
+ {messages.pgettext(
+ 'preferences-view',
+ 'Allows access to other devices on the same network for sharing, printing etc.',
+ )}
+ </Cell.FooterText>
+ </Cell.Footer>
+
+ <Cell.Container>
+ <Cell.Label>{messages.pgettext('preferences-view', 'Notifications')}</Cell.Label>
+ <Cell.Switch
+ isOn={this.props.enableSystemNotifications}
+ onChange={this.props.setEnableSystemNotifications}
+ />
+ </Cell.Container>
+ <Cell.Footer>
+ <Cell.FooterText>
+ {messages.pgettext(
+ 'preferences-view',
+ 'Enable or disable system notifications. The critical notifications will always be displayed.',
+ )}
+ </Cell.FooterText>
+ </Cell.Footer>
+ <Cell.Container>
+ <Cell.Label>
+ {messages.pgettext('preferences-view', 'Monochromatic tray icon')}
+ </Cell.Label>
+ <Cell.Switch
+ isOn={this.props.monochromaticIcon}
+ onChange={this.props.setMonochromaticIcon}
+ />
+ </Cell.Container>
+ <Cell.Footer>
+ <Cell.FooterText>
+ {messages.pgettext(
+ 'preferences-view',
+ 'Use a monochromatic tray icon instead of a colored one.',
+ )}
+ </Cell.FooterText>
+ </Cell.Footer>
+
+ {this.props.enableStartMinimizedToggle ? (
+ <React.Fragment>
<Cell.Container>
<Cell.Label>
- {messages.pgettext('preferences-view', 'Notifications')}
+ {messages.pgettext('preferences-view', 'Start minimized')}
</Cell.Label>
<Cell.Switch
- isOn={this.props.enableSystemNotifications}
- onChange={this.props.setEnableSystemNotifications}
+ isOn={this.props.startMinimized}
+ onChange={this.props.setStartMinimized}
/>
</Cell.Container>
<Cell.Footer>
<Cell.FooterText>
{messages.pgettext(
'preferences-view',
- 'Enable or disable system notifications. The critical notifications will always be displayed.',
+ 'Show only the tray icon when the app starts.',
)}
</Cell.FooterText>
</Cell.Footer>
+ </React.Fragment>
+ ) : undefined}
- <Cell.Container>
- <Cell.Label>
- {messages.pgettext('preferences-view', 'Monochromatic tray icon')}
- </Cell.Label>
- <Cell.Switch
- isOn={this.props.monochromaticIcon}
- onChange={this.props.setMonochromaticIcon}
- />
- </Cell.Container>
- <Cell.Footer>
- <Cell.FooterText>
- {messages.pgettext(
+ <Cell.Container disabled={this.props.isBeta}>
+ <Cell.Label>{messages.pgettext('preferences-view', 'Beta program')}</Cell.Label>
+ <Cell.Switch
+ isOn={this.props.showBetaReleases}
+ onChange={this.props.setShowBetaReleases}
+ />
+ </Cell.Container>
+ <Cell.Footer>
+ <Cell.FooterText>
+ {this.props.isBeta
+ ? messages.pgettext(
+ 'preferences-view',
+ 'This option is unavailable while using a beta version.',
+ )
+ : messages.pgettext(
'preferences-view',
- 'Use a monochromatic tray icon instead of a colored one.',
+ 'Enable to get notified when new beta versions of the app are released.',
)}
- </Cell.FooterText>
- </Cell.Footer>
-
- {this.props.enableStartMinimizedToggle ? (
- <React.Fragment>
- <Cell.Container>
- <Cell.Label>
- {messages.pgettext('preferences-view', 'Start minimized')}
- </Cell.Label>
- <Cell.Switch
- isOn={this.props.startMinimized}
- onChange={this.props.setStartMinimized}
- />
- </Cell.Container>
- <Cell.Footer>
- <Cell.FooterText>
- {messages.pgettext(
- 'preferences-view',
- 'Show only the tray icon when the app starts.',
- )}
- </Cell.FooterText>
- </Cell.Footer>
- </React.Fragment>
- ) : undefined}
-
- <Cell.Container disabled={this.props.isBeta}>
- <Cell.Label>
- {messages.pgettext('preferences-view', 'Beta program')}
- </Cell.Label>
- <Cell.Switch
- isOn={this.props.showBetaReleases}
- onChange={this.props.setShowBetaReleases}
- />
- </Cell.Container>
- <Cell.Footer>
- <Cell.FooterText>
- {this.props.isBeta
- ? messages.pgettext(
- 'preferences-view',
- 'This option is unavailable while using a beta version.',
- )
- : messages.pgettext(
- 'preferences-view',
- 'Enable to get notified when new beta versions of the app are released.',
- )}
- </Cell.FooterText>
- </Cell.Footer>
- </View>
- </NavigationScrollbars>
- </View>
- </NavigationContainer>
- </View>
- </Container>
+ </Cell.FooterText>
+ </Cell.Footer>
+ </StyledContent>
+ </NavigationScrollbars>
+ </NavigationContainer>
+ </StyledContainer>
</Layout>
);
}
diff --git a/gui/src/renderer/components/PreferencesStyles.tsx b/gui/src/renderer/components/PreferencesStyles.tsx
index 73c9691f7f..b9986f2c38 100644
--- a/gui/src/renderer/components/PreferencesStyles.tsx
+++ b/gui/src/renderer/components/PreferencesStyles.tsx
@@ -1,21 +1,18 @@
-import { Styles } from 'reactxp';
+import styled from 'styled-components';
import { colors } from '../../config.json';
+import { Container } from './Layout';
-export default {
- preferences: Styles.createViewStyle({
- backgroundColor: colors.darkBlue,
- flex: 1,
- }),
- preferences__container: Styles.createViewStyle({
- flexDirection: 'column',
- flex: 1,
- }),
- preferences__content: Styles.createViewStyle({
- flexDirection: 'column',
- flex: 1,
- marginBottom: 2,
- }),
- preferences__separator: Styles.createViewStyle({
- height: 1,
- }),
-};
+export const StyledContainer = styled(Container)({
+ backgroundColor: colors.darkBlue,
+});
+
+export const StyledContent = styled.div({
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ marginBottom: '2px',
+});
+
+export const StyledSeparator = styled.div({
+ height: '1px',
+});
diff --git a/gui/src/renderer/components/SelectLanguage.tsx b/gui/src/renderer/components/SelectLanguage.tsx
index 58f4a7b922..30a37a8a1d 100644
--- a/gui/src/renderer/components/SelectLanguage.tsx
+++ b/gui/src/renderer/components/SelectLanguage.tsx
@@ -1,6 +1,5 @@
import * as React from 'react';
import ReactDOM from 'react-dom';
-import { Component, Styles, View } from 'reactxp';
import styled from 'styled-components';
import { colors } from '../../config.json';
import { messages } from '../../shared/gettext';
@@ -28,15 +27,9 @@ interface IState {
source: Array<ISelectorItem<string>>;
}
-const styles = {
- page: Styles.createViewStyle({
- backgroundColor: colors.darkBlue,
- flex: 1,
- }),
- container: Styles.createViewStyle({
- flex: 1,
- }),
-};
+const StyledContainer = styled(Container)({
+ backgroundColor: colors.darkBlue,
+});
const StyledNavigationScrollbars = styled(NavigationScrollbars)({
flex: 1,
@@ -46,7 +39,7 @@ const StyledSelector = (styled(Selector)({
marginBottom: 0,
}) as unknown) as new <T>() => Selector<T>;
-export default class SelectLanguage extends Component<IProps, IState> {
+export default class SelectLanguage extends React.Component<IProps, IState> {
private scrollView = React.createRef<CustomScrollbars>();
private selectedCellRef = React.createRef<SelectorCell<string>>();
@@ -67,45 +60,41 @@ export default class SelectLanguage extends Component<IProps, IState> {
public render() {
return (
<Layout>
- <Container>
- <View style={styles.page}>
- <NavigationContainer>
- <NavigationBar>
- <NavigationItems>
- <BackBarItem action={this.props.onClose}>
- {
- // TRANSLATORS: Back button in navigation bar
- messages.pgettext('navigation-bar', 'Settings')
- }
- </BackBarItem>
- <TitleBarItem>
- {
- // TRANSLATORS: Title label in navigation bar
- messages.pgettext('select-language-nav', 'Select language')
- }
- </TitleBarItem>
- </NavigationItems>
- </NavigationBar>
+ <StyledContainer>
+ <NavigationContainer>
+ <NavigationBar>
+ <NavigationItems>
+ <BackBarItem action={this.props.onClose}>
+ {
+ // TRANSLATORS: Back button in navigation bar
+ messages.pgettext('navigation-bar', 'Settings')
+ }
+ </BackBarItem>
+ <TitleBarItem>
+ {
+ // TRANSLATORS: Title label in navigation bar
+ messages.pgettext('select-language-nav', 'Select language')
+ }
+ </TitleBarItem>
+ </NavigationItems>
+ </NavigationBar>
- <View style={styles.container}>
- <StyledNavigationScrollbars>
- <SettingsHeader>
- <HeaderTitle>
- {messages.pgettext('select-language-nav', 'Select language')}
- </HeaderTitle>
- </SettingsHeader>
- <StyledSelector
- title=""
- values={this.state.source}
- value={this.props.preferredLocale}
- onSelect={this.props.setPreferredLocale}
- selectedCellRef={this.selectedCellRef}
- />
- </StyledNavigationScrollbars>
- </View>
- </NavigationContainer>
- </View>
- </Container>
+ <StyledNavigationScrollbars>
+ <SettingsHeader>
+ <HeaderTitle>
+ {messages.pgettext('select-language-nav', 'Select language')}
+ </HeaderTitle>
+ </SettingsHeader>
+ <StyledSelector
+ title=""
+ values={this.state.source}
+ value={this.props.preferredLocale}
+ onSelect={this.props.setPreferredLocale}
+ selectedCellRef={this.selectedCellRef}
+ />
+ </StyledNavigationScrollbars>
+ </NavigationContainer>
+ </StyledContainer>
</Layout>
);
}
diff --git a/gui/src/renderer/components/Settings.tsx b/gui/src/renderer/components/Settings.tsx
index dd3fef403c..a852d084ea 100644
--- a/gui/src/renderer/components/Settings.tsx
+++ b/gui/src/renderer/components/Settings.tsx
@@ -1,11 +1,9 @@
import * as React from 'react';
-import { Component, Text, View } from 'reactxp';
import { colors, links } from '../../config.json';
import { hasExpired, formatRemainingTime } from '../../shared/account-expiry';
import { messages } from '../../shared/gettext';
-import * as AppButton from './AppButton';
import * as Cell from './Cell';
-import { Container, Layout } from './Layout';
+import { Layout } from './Layout';
import {
CloseBarItem,
NavigationBar,
@@ -14,7 +12,15 @@ import {
TitleBarItem,
} from './NavigationBar';
import SettingsHeader, { HeaderTitle } from './SettingsHeader';
-import styles, { CellIcon, OutOfTimeSubText, StyledNavigationScrollbars } from './SettingsStyles';
+import {
+ StyledCellIcon,
+ StyledCellSpacer,
+ StyledContainer,
+ StyledContent,
+ StyledNavigationScrollbars,
+ StyledOutOfTimeSubText,
+ StyledQuitButton,
+} from './SettingsStyles';
import { LoginState } from '../redux/account/reducers';
@@ -37,47 +43,43 @@ export interface IProps {
onExternalLink: (url: string) => void;
}
-export default class Settings extends Component<IProps> {
+export default class Settings extends React.Component<IProps> {
public render() {
const showLargeTitle = this.props.loginState.type !== 'ok';
return (
<Layout>
- <Container>
- <View style={styles.settings}>
- <NavigationContainer>
- <NavigationBar alwaysDisplayBarTitle={!showLargeTitle}>
- <NavigationItems>
- <CloseBarItem action={this.props.onClose} />
- <TitleBarItem>
- {
- // TRANSLATORS: Title label in navigation bar
- messages.pgettext('navigation-bar', 'Settings')
- }
- </TitleBarItem>
- </NavigationItems>
- </NavigationBar>
+ <StyledContainer>
+ <NavigationContainer>
+ <NavigationBar alwaysDisplayBarTitle={!showLargeTitle}>
+ <NavigationItems>
+ <CloseBarItem action={this.props.onClose} />
+ <TitleBarItem>
+ {
+ // TRANSLATORS: Title label in navigation bar
+ messages.pgettext('navigation-bar', 'Settings')
+ }
+ </TitleBarItem>
+ </NavigationItems>
+ </NavigationBar>
+
+ <StyledNavigationScrollbars>
+ <StyledContent>
+ {showLargeTitle && (
+ <SettingsHeader>
+ <HeaderTitle>{messages.pgettext('navigation-bar', 'Settings')}</HeaderTitle>
+ </SettingsHeader>
+ )}
+
+ {this.renderTopButtons()}
+ {this.renderMiddleButtons()}
+ {this.renderBottomButtons()}
- <View style={styles.container}>
- <StyledNavigationScrollbars>
- <View style={styles.content}>
- {showLargeTitle && (
- <SettingsHeader>
- <HeaderTitle>{messages.pgettext('navigation-bar', 'Settings')}</HeaderTitle>
- </SettingsHeader>
- )}
- <View>
- {this.renderTopButtons()}
- {this.renderMiddleButtons()}
- {this.renderBottomButtons()}
- </View>
- {this.renderQuitButton()}
- </View>
- </StyledNavigationScrollbars>
- </View>
- </NavigationContainer>
- </View>
- </Container>
+ {this.renderQuitButton()}
+ </StyledContent>
+ </StyledNavigationScrollbars>
+ </NavigationContainer>
+ </StyledContainer>
</Layout>
);
}
@@ -87,11 +89,9 @@ export default class Settings extends Component<IProps> {
private renderQuitButton() {
return (
- <View style={styles.quitButtonFooter}>
- <AppButton.RedButton onClick={this.props.onQuit}>
- {messages.pgettext('settings-view', 'Quit app')}
- </AppButton.RedButton>
- </View>
+ <StyledQuitButton onClick={this.props.onQuit}>
+ {messages.pgettext('settings-view', 'Quit app')}
+ </StyledQuitButton>
);
}
@@ -109,21 +109,19 @@ export default class Settings extends Component<IProps> {
const outOfTimeMessage = messages.pgettext('settings-view', 'OUT OF TIME');
return (
- <View>
- <View>
- <Cell.CellButton onClick={this.props.onViewAccount}>
- <Cell.Label>
- {
- // TRANSLATORS: Navigation button to the 'Account' view
- messages.pgettext('settings-view', 'Account')
- }
- </Cell.Label>
- <OutOfTimeSubText isOutOfTime={isOutOfTime}>
- {isOutOfTime ? outOfTimeMessage : formattedExpiry}
- </OutOfTimeSubText>
- <Cell.Icon height={12} width={7} source="icon-chevron" />
- </Cell.CellButton>
- </View>
+ <>
+ <Cell.CellButton onClick={this.props.onViewAccount}>
+ <Cell.Label>
+ {
+ // TRANSLATORS: Navigation button to the 'Account' view
+ messages.pgettext('settings-view', 'Account')
+ }
+ </Cell.Label>
+ <StyledOutOfTimeSubText isOutOfTime={isOutOfTime}>
+ {isOutOfTime ? outOfTimeMessage : formattedExpiry}
+ </StyledOutOfTimeSubText>
+ <Cell.Icon height={12} width={7} source="icon-chevron" />
+ </Cell.CellButton>
<Cell.CellButton onClick={this.props.onViewPreferences}>
<Cell.Label>
@@ -144,8 +142,8 @@ export default class Settings extends Component<IProps> {
</Cell.Label>
<Cell.Icon height={12} width={7} source="icon-chevron" />
</Cell.CellButton>
- <View style={styles.cellSpacer} />
- </View>
+ <StyledCellSpacer />
+ </>
);
}
@@ -167,18 +165,18 @@ export default class Settings extends Component<IProps> {
? inconsistentVersionMessage
: updateAvailableMessage;
- icon = <CellIcon source="icon-alert" tintColor={colors.red} />;
+ icon = <StyledCellIcon source="icon-alert" tintColor={colors.red} />;
footer = (
- <View style={styles.cellFooter}>
- <Text style={styles.cellFooterLabel}>{message}</Text>
- </View>
+ <Cell.Footer>
+ <Cell.FooterText>{message}</Cell.FooterText>
+ </Cell.Footer>
);
} else {
- footer = <View style={styles.cellSpacer} />;
+ footer = <StyledCellSpacer />;
}
return (
- <View>
+ <>
<Cell.CellButton disabled={this.props.isOffline} onClick={this.openDownloadLink}>
{icon}
<Cell.Label>{messages.pgettext('settings-view', 'App version')}</Cell.Label>
@@ -186,13 +184,13 @@ export default class Settings extends Component<IProps> {
<Cell.Icon height={16} width={16} source="icon-extLink" />
</Cell.CellButton>
{footer}
- </View>
+ </>
);
}
private renderBottomButtons() {
return (
- <View>
+ <>
<Cell.CellButton onClick={this.props.onViewSupport}>
<Cell.Label>
{
@@ -214,7 +212,7 @@ export default class Settings extends Component<IProps> {
</Cell.CellButton>
<Cell.CellButton onClick={this.props.onViewSelectLanguage}>
- <CellIcon width={24} height={24} source="icon-language" />
+ <StyledCellIcon width={24} height={24} source="icon-language" />
<Cell.Label>
{
// TRANSLATORS: Navigation button to the 'Language' settings view
@@ -224,7 +222,7 @@ export default class Settings extends Component<IProps> {
<Cell.SubText>{this.props.preferredLocaleDisplayName}</Cell.SubText>
<Cell.Icon height={12} width={7} source="icon-chevron" />
</Cell.CellButton>
- </View>
+ </>
);
}
}
diff --git a/gui/src/renderer/components/SettingsStyles.tsx b/gui/src/renderer/components/SettingsStyles.tsx
index e971273f14..d396faaecc 100644
--- a/gui/src/renderer/components/SettingsStyles.tsx
+++ b/gui/src/renderer/components/SettingsStyles.tsx
@@ -1,14 +1,15 @@
-import { Styles } from 'reactxp';
import styled from 'styled-components';
import { colors } from '../../config.json';
+import * as AppButton from './AppButton';
import * as Cell from './Cell';
+import { Container } from './Layout';
import { NavigationScrollbars } from './NavigationBar';
-export const OutOfTimeSubText = styled(Cell.SubText)((props: { isOutOfTime: boolean }) => ({
+export const StyledOutOfTimeSubText = styled(Cell.SubText)((props: { isOutOfTime: boolean }) => ({
color: props.isOutOfTime ? colors.red : undefined,
}));
-export const CellIcon = styled(Cell.UntintedIcon)({
+export const StyledCellIcon = styled(Cell.UntintedIcon)({
marginRight: '8px',
});
@@ -16,40 +17,24 @@ export const StyledNavigationScrollbars = styled(NavigationScrollbars)({
flex: 1,
});
-export default {
- settings: Styles.createViewStyle({
- backgroundColor: colors.darkBlue,
- flex: 1,
- }),
- container: Styles.createViewStyle({
- flexDirection: 'column',
- flex: 1,
- }),
- content: Styles.createViewStyle({
- flexDirection: 'column',
- flex: 1,
- justifyContent: 'space-between',
- overflow: 'visible',
- }),
- cellSpacer: Styles.createViewStyle({
- height: 20,
- flex: 0,
- }),
- cellFooter: Styles.createViewStyle({
- paddingTop: 6,
- paddingHorizontal: 22,
- paddingBottom: 20,
- }),
- quitButtonFooter: Styles.createViewStyle({
- paddingTop: 20,
- paddingBottom: 22,
- paddingHorizontal: 22,
- }),
- cellFooterLabel: Styles.createTextStyle({
- fontFamily: 'Open Sans',
- fontSize: 13,
- fontWeight: '600',
- lineHeight: 20,
- color: colors.white60,
- }),
-};
+export const StyledContainer = styled(Container)({
+ backgroundColor: colors.darkBlue,
+});
+
+export const StyledContent = styled.div({
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ justifyContent: 'space-between',
+ overflow: 'visible',
+});
+
+export const StyledCellSpacer = styled.div({
+ height: '20px',
+ minHeight: '20px',
+ flex: 0,
+});
+
+export const StyledQuitButton = styled(AppButton.RedButton)({
+ margin: '20px 22px 22px',
+});