summaryrefslogtreecommitdiffhomepage
path: root/gui
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2021-12-21 13:49:21 +0100
committerOskar Nyberg <oskar@mullvad.net>2021-12-21 13:49:21 +0100
commit220948c522328928c16faafca5a553c6bce2e95e (patch)
tree0838e7b51d3d69b7a0640f3a7e53e1a5cf132d6d /gui
parentc50902f92e0c4a4887eb9acc8458691d42e7b219 (diff)
parente33a9a936e7be501e1da44b94cf5805bb8f3c824 (diff)
downloadmullvadvpn-220948c522328928c16faafca5a553c6bce2e95e.tar.xz
mullvadvpn-220948c522328928c16faafca5a553c6bce2e95e.zip
Merge branch 'add-killswitch-info'
Diffstat (limited to 'gui')
-rw-r--r--gui/assets/images/icon-info.svg3
-rw-r--r--gui/locales/messages.pot8
-rw-r--r--gui/src/renderer/components/AdvancedSettings.tsx2
-rw-r--r--gui/src/renderer/components/CustomDnsSettings.tsx2
-rw-r--r--gui/src/renderer/components/ExpiredAccountErrorView.tsx2
-rw-r--r--gui/src/renderer/components/Modal.tsx5
-rw-r--r--gui/src/renderer/components/Preferences.tsx495
7 files changed, 290 insertions, 227 deletions
diff --git a/gui/assets/images/icon-info.svg b/gui/assets/images/icon-info.svg
new file mode 100644
index 0000000000..cb2a243e78
--- /dev/null
+++ b/gui/assets/images/icon-info.svg
@@ -0,0 +1,3 @@
+<svg data-name="icon - info" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+ <path d="M12 24A12 12 0 0 1 3.515 3.515a12 12 0 1 1 16.97 16.97A11.922 11.922 0 0 1 12 24zm0-15a1.5 1.5 0 0 0-1.5 1.5V18a1.5 1.5 0 1 0 3 0v-7.5A1.5 1.5 0 0 0 12 9zm0-4.5A1.5 1.5 0 1 0 13.5 6 1.5 1.5 0 0 0 12 4.5z" style="fill:#294d73"/>
+</svg>
diff --git a/gui/locales/messages.pot b/gui/locales/messages.pot
index f4d5af9325..56e3de490c 100644
--- a/gui/locales/messages.pot
+++ b/gui/locales/messages.pot
@@ -857,6 +857,10 @@ msgid "Enable to move the app around as a free-standing window."
msgstr ""
msgctxt "preferences-view"
+msgid "Kill switch"
+msgstr ""
+
+msgctxt "preferences-view"
msgid "Launch app on start-up"
msgstr ""
@@ -885,6 +889,10 @@ msgid "Start minimized"
msgstr ""
msgctxt "preferences-view"
+msgid "The app has a built in kill switch that is enabled by default and cannot be disabled. This is to prevent your traffic from leaking outside of the VPN tunnel if your network suddenly stops working or if the tunnel fails for any reason. Mullvad automatically protects your data until your connection is reestablished."
+msgstr ""
+
+msgctxt "preferences-view"
msgid "This option is unavailable while using a beta version."
msgstr ""
diff --git a/gui/src/renderer/components/AdvancedSettings.tsx b/gui/src/renderer/components/AdvancedSettings.tsx
index 42f48b1b87..f511265464 100644
--- a/gui/src/renderer/components/AdvancedSettings.tsx
+++ b/gui/src/renderer/components/AdvancedSettings.tsx
@@ -233,7 +233,7 @@ export default class AdvancedSettings extends React.Component<IProps, IState> {
private renderConfirmBlockWhenDisconnectedAlert = () => {
return (
<ModalAlert
- type={ModalAlertType.info}
+ type={ModalAlertType.caution}
buttons={[
<AppButton.RedButton key="confirm" onClick={this.confirmEnableBlockWhenDisconnected}>
{messages.pgettext('advanced-settings-view', 'Enable anyway')}
diff --git a/gui/src/renderer/components/CustomDnsSettings.tsx b/gui/src/renderer/components/CustomDnsSettings.tsx
index 7379b4f068..98f0c8f1b2 100644
--- a/gui/src/renderer/components/CustomDnsSettings.tsx
+++ b/gui/src/renderer/components/CustomDnsSettings.tsx
@@ -392,7 +392,7 @@ interface IConfirmationDialogProps {
function ConfirmationDialog(props: IConfirmationDialogProps) {
return (
<ModalAlert
- type={ModalAlertType.info}
+ type={ModalAlertType.caution}
buttons={[
<AppButton.RedButton key="confirm" onClick={props.confirm}>
{messages.pgettext('advanced-settings-view', 'Add anyway')}
diff --git a/gui/src/renderer/components/ExpiredAccountErrorView.tsx b/gui/src/renderer/components/ExpiredAccountErrorView.tsx
index 0fedb67fdf..c24c79da45 100644
--- a/gui/src/renderer/components/ExpiredAccountErrorView.tsx
+++ b/gui/src/renderer/components/ExpiredAccountErrorView.tsx
@@ -194,7 +194,7 @@ export default class ExpiredAccountErrorView extends React.Component<
private renderBlockWhenDisconnectedAlert() {
return (
<ModalAlert
- type={ModalAlertType.info}
+ type={ModalAlertType.caution}
buttons={[
<AppButton.BlueButton
key="cancel"
diff --git a/gui/src/renderer/components/Modal.tsx b/gui/src/renderer/components/Modal.tsx
index 49f0793f5d..97cf4dce22 100644
--- a/gui/src/renderer/components/Modal.tsx
+++ b/gui/src/renderer/components/Modal.tsx
@@ -88,6 +88,7 @@ export function ModalContainer(props: IModalContainerProps) {
export enum ModalAlertType {
info = 1,
+ caution,
warning,
}
@@ -196,6 +197,10 @@ class ModalAlertWithContext extends React.Component<IModalAlertProps & IModalCon
let color = '';
switch (type) {
case ModalAlertType.info:
+ source = 'icon-info';
+ color = colors.white;
+ break;
+ case ModalAlertType.caution:
source = 'icon-alert';
color = colors.white;
break;
diff --git a/gui/src/renderer/components/Preferences.tsx b/gui/src/renderer/components/Preferences.tsx
index ebaa2da359..4f66b60d4a 100644
--- a/gui/src/renderer/components/Preferences.tsx
+++ b/gui/src/renderer/components/Preferences.tsx
@@ -1,11 +1,15 @@
import * as React from 'react';
import { sprintf } from 'sprintf-js';
+import { colors } from '../../config.json';
import { IDnsOptions } from '../../shared/daemon-rpc-types';
import { messages } from '../../shared/gettext';
import { formatMarkdown } from '../markdown-formatter';
+import * as AppButton from './AppButton';
import { AriaDescription, AriaInput, AriaInputGroup, AriaLabel } from './AriaGroup';
import * as Cell from './cell';
+import ImageView from './ImageView';
import { Layout } from './Layout';
+import { ModalAlert, ModalAlertType, ModalContainer } from './Modal';
import {
BackBarItem,
NavigationBar,
@@ -40,204 +44,194 @@ export interface IProps {
onClose: () => void;
}
-export default class Preferences extends React.Component<IProps> {
+interface IState {
+ showKillSwitchInfo: boolean;
+}
+
+export default class Preferences extends React.Component<IProps, IState> {
+ public state = { showKillSwitchInfo: false };
+
public render() {
return (
- <Layout>
- <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>
+ <ModalContainer>
+ <Layout>
+ <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>
- <NavigationScrollbars>
- <SettingsHeader>
- <HeaderTitle>{messages.pgettext('preferences-view', 'Preferences')}</HeaderTitle>
- </SettingsHeader>
+ <NavigationScrollbars>
+ <SettingsHeader>
+ <HeaderTitle>{messages.pgettext('preferences-view', 'Preferences')}</HeaderTitle>
+ </SettingsHeader>
- <StyledContent>
- <AriaInputGroup>
- <Cell.Container>
- <AriaLabel>
- <Cell.InputLabel>
- {messages.pgettext('preferences-view', 'Launch app on start-up')}
- </Cell.InputLabel>
- </AriaLabel>
- <AriaInput>
- <Cell.Switch isOn={this.props.autoStart} onChange={this.props.setAutoStart} />
- </AriaInput>
- </Cell.Container>
- </AriaInputGroup>
- <StyledSeparator />
+ <StyledContent>
+ <Cell.CellButton onClick={this.showKillSwitchInfo}>
+ <Cell.InputLabel>
+ {messages.pgettext('preferences-view', 'Kill switch')}
+ </Cell.InputLabel>
+ <ImageView source="icon-info" width={24} tintColor={colors.white} />
+ </Cell.CellButton>
+ <StyledSeparator height={20} />
- <AriaInputGroup>
- <Cell.Container>
- <AriaLabel>
- <Cell.InputLabel>
- {messages.pgettext('preferences-view', 'Auto-connect')}
- </Cell.InputLabel>
- </AriaLabel>
- <AriaInput>
- <Cell.Switch
- isOn={this.props.autoConnect}
- onChange={this.props.setAutoConnect}
- />
- </AriaInput>
- </Cell.Container>
- <Cell.Footer>
- <AriaDescription>
- <Cell.FooterText>
- {messages.pgettext(
- 'preferences-view',
- 'Automatically connect to a server when the app launches.',
- )}
- </Cell.FooterText>
- </AriaDescription>
- </Cell.Footer>
- </AriaInputGroup>
+ <AriaInputGroup>
+ <Cell.Container>
+ <AriaLabel>
+ <Cell.InputLabel>
+ {messages.pgettext('preferences-view', 'Launch app on start-up')}
+ </Cell.InputLabel>
+ </AriaLabel>
+ <AriaInput>
+ <Cell.Switch
+ isOn={this.props.autoStart}
+ onChange={this.props.setAutoStart}
+ />
+ </AriaInput>
+ </Cell.Container>
+ </AriaInputGroup>
+ <StyledSeparator />
- <AriaInputGroup>
- <Cell.Container disabled={this.props.dns.state === 'custom'}>
- <AriaLabel>
- <Cell.InputLabel>
- {messages.pgettext('preferences-view', 'Block ads')}
- </Cell.InputLabel>
- </AriaLabel>
- <AriaInput>
- <Cell.Switch
- isOn={
- this.props.dns.state === 'default' &&
- this.props.dns.defaultOptions.blockAds
- }
- onChange={this.setBlockAds}
- />
- </AriaInput>
- </Cell.Container>
- </AriaInputGroup>
- <StyledSeparator />
- <AriaInputGroup>
- <Cell.Container disabled={this.props.dns.state === 'custom'}>
- <AriaLabel>
- <Cell.InputLabel>
- {messages.pgettext('preferences-view', 'Block trackers')}
- </Cell.InputLabel>
- </AriaLabel>
- <AriaInput>
- <Cell.Switch
- isOn={
- this.props.dns.state === 'default' &&
- this.props.dns.defaultOptions.blockTrackers
- }
- onChange={this.setBlockTrackers}
- />
- </AriaInput>
- </Cell.Container>
- {this.props.dns.state === 'custom' && <CustomDnsEnabledFooter />}
- </AriaInputGroup>
+ <AriaInputGroup>
+ <Cell.Container>
+ <AriaLabel>
+ <Cell.InputLabel>
+ {messages.pgettext('preferences-view', 'Auto-connect')}
+ </Cell.InputLabel>
+ </AriaLabel>
+ <AriaInput>
+ <Cell.Switch
+ isOn={this.props.autoConnect}
+ onChange={this.props.setAutoConnect}
+ />
+ </AriaInput>
+ </Cell.Container>
+ <Cell.Footer>
+ <AriaDescription>
+ <Cell.FooterText>
+ {messages.pgettext(
+ 'preferences-view',
+ 'Automatically connect to a server when the app launches.',
+ )}
+ </Cell.FooterText>
+ </AriaDescription>
+ </Cell.Footer>
+ </AriaInputGroup>
- {this.props.dns.state !== 'custom' && <StyledSeparator height={20} />}
+ <AriaInputGroup>
+ <Cell.Container disabled={this.props.dns.state === 'custom'}>
+ <AriaLabel>
+ <Cell.InputLabel>
+ {messages.pgettext('preferences-view', 'Block ads')}
+ </Cell.InputLabel>
+ </AriaLabel>
+ <AriaInput>
+ <Cell.Switch
+ isOn={
+ this.props.dns.state === 'default' &&
+ this.props.dns.defaultOptions.blockAds
+ }
+ onChange={this.setBlockAds}
+ />
+ </AriaInput>
+ </Cell.Container>
+ </AriaInputGroup>
+ <StyledSeparator />
+ <AriaInputGroup>
+ <Cell.Container disabled={this.props.dns.state === 'custom'}>
+ <AriaLabel>
+ <Cell.InputLabel>
+ {messages.pgettext('preferences-view', 'Block trackers')}
+ </Cell.InputLabel>
+ </AriaLabel>
+ <AriaInput>
+ <Cell.Switch
+ isOn={
+ this.props.dns.state === 'default' &&
+ this.props.dns.defaultOptions.blockTrackers
+ }
+ onChange={this.setBlockTrackers}
+ />
+ </AriaInput>
+ </Cell.Container>
+ {this.props.dns.state === 'custom' && <CustomDnsEnabledFooter />}
+ </AriaInputGroup>
- <AriaInputGroup>
- <Cell.Container>
- <AriaLabel>
- <Cell.InputLabel>
- {messages.pgettext('preferences-view', 'Local network sharing')}
- </Cell.InputLabel>
- </AriaLabel>
- <AriaInput>
- <Cell.Switch isOn={this.props.allowLan} onChange={this.props.setAllowLan} />
- </AriaInput>
- </Cell.Container>
- <Cell.Footer>
- <AriaDescription>
- <Cell.FooterText>
- {messages.pgettext(
- 'preferences-view',
- 'Allows access to other devices on the same network for sharing, printing etc.',
- )}
- </Cell.FooterText>
- </AriaDescription>
- </Cell.Footer>
- </AriaInputGroup>
+ {this.props.dns.state !== 'custom' && <StyledSeparator height={20} />}
- <AriaInputGroup>
- <Cell.Container>
- <AriaLabel>
- <Cell.InputLabel>
- {messages.pgettext('preferences-view', 'Notifications')}
- </Cell.InputLabel>
- </AriaLabel>
- <AriaInput>
- <Cell.Switch
- isOn={this.props.enableSystemNotifications}
- onChange={this.props.setEnableSystemNotifications}
- />
- </AriaInput>
- </Cell.Container>
- <Cell.Footer>
- <AriaDescription>
- <Cell.FooterText>
- {messages.pgettext(
- 'preferences-view',
- 'Enable or disable system notifications. The critical notifications will always be displayed.',
- )}
- </Cell.FooterText>
- </AriaDescription>
- </Cell.Footer>
- </AriaInputGroup>
+ <AriaInputGroup>
+ <Cell.Container>
+ <AriaLabel>
+ <Cell.InputLabel>
+ {messages.pgettext('preferences-view', 'Local network sharing')}
+ </Cell.InputLabel>
+ </AriaLabel>
+ <AriaInput>
+ <Cell.Switch isOn={this.props.allowLan} onChange={this.props.setAllowLan} />
+ </AriaInput>
+ </Cell.Container>
+ <Cell.Footer>
+ <AriaDescription>
+ <Cell.FooterText>
+ {messages.pgettext(
+ 'preferences-view',
+ 'Allows access to other devices on the same network for sharing, printing etc.',
+ )}
+ </Cell.FooterText>
+ </AriaDescription>
+ </Cell.Footer>
+ </AriaInputGroup>
- <AriaInputGroup>
- <Cell.Container>
- <AriaLabel>
- <Cell.InputLabel>
- {messages.pgettext('preferences-view', 'Monochromatic tray icon')}
- </Cell.InputLabel>
- </AriaLabel>
- <AriaInput>
- <Cell.Switch
- isOn={this.props.monochromaticIcon}
- onChange={this.props.setMonochromaticIcon}
- />
- </AriaInput>
- </Cell.Container>
- <Cell.Footer>
- <AriaDescription>
- <Cell.FooterText>
- {messages.pgettext(
- 'preferences-view',
- 'Use a monochromatic tray icon instead of a colored one.',
- )}
- </Cell.FooterText>
- </AriaDescription>
- </Cell.Footer>
- </AriaInputGroup>
+ <AriaInputGroup>
+ <Cell.Container>
+ <AriaLabel>
+ <Cell.InputLabel>
+ {messages.pgettext('preferences-view', 'Notifications')}
+ </Cell.InputLabel>
+ </AriaLabel>
+ <AriaInput>
+ <Cell.Switch
+ isOn={this.props.enableSystemNotifications}
+ onChange={this.props.setEnableSystemNotifications}
+ />
+ </AriaInput>
+ </Cell.Container>
+ <Cell.Footer>
+ <AriaDescription>
+ <Cell.FooterText>
+ {messages.pgettext(
+ 'preferences-view',
+ 'Enable or disable system notifications. The critical notifications will always be displayed.',
+ )}
+ </Cell.FooterText>
+ </AriaDescription>
+ </Cell.Footer>
+ </AriaInputGroup>
- {(window.env.platform === 'win32' ||
- (window.env.platform === 'darwin' && window.env.development)) && (
<AriaInputGroup>
<Cell.Container>
<AriaLabel>
<Cell.InputLabel>
- {messages.pgettext('preferences-view', 'Unpin app from taskbar')}
+ {messages.pgettext('preferences-view', 'Monochromatic tray icon')}
</Cell.InputLabel>
</AriaLabel>
<AriaInput>
<Cell.Switch
- isOn={this.props.unpinnedWindow}
- onChange={this.props.setUnpinnedWindow}
+ isOn={this.props.monochromaticIcon}
+ onChange={this.props.setMonochromaticIcon}
/>
</AriaInput>
</Cell.Container>
@@ -246,27 +240,26 @@ export default class Preferences extends React.Component<IProps> {
<Cell.FooterText>
{messages.pgettext(
'preferences-view',
- 'Enable to move the app around as a free-standing window.',
+ 'Use a monochromatic tray icon instead of a colored one.',
)}
</Cell.FooterText>
</AriaDescription>
</Cell.Footer>
</AriaInputGroup>
- )}
- {this.props.unpinnedWindow && (
- <React.Fragment>
+ {(window.env.platform === 'win32' ||
+ (window.env.platform === 'darwin' && window.env.development)) && (
<AriaInputGroup>
<Cell.Container>
<AriaLabel>
<Cell.InputLabel>
- {messages.pgettext('preferences-view', 'Start minimized')}
+ {messages.pgettext('preferences-view', 'Unpin app from taskbar')}
</Cell.InputLabel>
</AriaLabel>
<AriaInput>
<Cell.Switch
- isOn={this.props.startMinimized}
- onChange={this.props.setStartMinimized}
+ isOn={this.props.unpinnedWindow}
+ onChange={this.props.setUnpinnedWindow}
/>
</AriaInput>
</Cell.Container>
@@ -275,50 +268,96 @@ export default class Preferences extends React.Component<IProps> {
<Cell.FooterText>
{messages.pgettext(
'preferences-view',
- 'Show only the tray icon when the app starts.',
+ 'Enable to move the app around as a free-standing window.',
)}
</Cell.FooterText>
</AriaDescription>
</Cell.Footer>
</AriaInputGroup>
- </React.Fragment>
- )}
+ )}
- <AriaInputGroup>
- <Cell.Container disabled={this.props.isBeta}>
- <AriaLabel>
- <Cell.InputLabel>
- {messages.pgettext('preferences-view', 'Beta program')}
- </Cell.InputLabel>
- </AriaLabel>
- <AriaInput>
- <Cell.Switch
- isOn={this.props.showBetaReleases}
- onChange={this.props.setShowBetaReleases}
- />
- </AriaInput>
- </Cell.Container>
- <Cell.Footer>
- <AriaDescription>
- <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>
- </AriaDescription>
- </Cell.Footer>
- </AriaInputGroup>
- </StyledContent>
- </NavigationScrollbars>
- </NavigationContainer>
- </StyledContainer>
- </Layout>
+ {this.props.unpinnedWindow && (
+ <React.Fragment>
+ <AriaInputGroup>
+ <Cell.Container>
+ <AriaLabel>
+ <Cell.InputLabel>
+ {messages.pgettext('preferences-view', 'Start minimized')}
+ </Cell.InputLabel>
+ </AriaLabel>
+ <AriaInput>
+ <Cell.Switch
+ isOn={this.props.startMinimized}
+ onChange={this.props.setStartMinimized}
+ />
+ </AriaInput>
+ </Cell.Container>
+ <Cell.Footer>
+ <AriaDescription>
+ <Cell.FooterText>
+ {messages.pgettext(
+ 'preferences-view',
+ 'Show only the tray icon when the app starts.',
+ )}
+ </Cell.FooterText>
+ </AriaDescription>
+ </Cell.Footer>
+ </AriaInputGroup>
+ </React.Fragment>
+ )}
+
+ <AriaInputGroup>
+ <Cell.Container disabled={this.props.isBeta}>
+ <AriaLabel>
+ <Cell.InputLabel>
+ {messages.pgettext('preferences-view', 'Beta program')}
+ </Cell.InputLabel>
+ </AriaLabel>
+ <AriaInput>
+ <Cell.Switch
+ isOn={this.props.showBetaReleases}
+ onChange={this.props.setShowBetaReleases}
+ />
+ </AriaInput>
+ </Cell.Container>
+ <Cell.Footer>
+ <AriaDescription>
+ <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>
+ </AriaDescription>
+ </Cell.Footer>
+ </AriaInputGroup>
+ </StyledContent>
+ </NavigationScrollbars>
+ </NavigationContainer>
+ </StyledContainer>
+ </Layout>
+
+ {this.state.showKillSwitchInfo && (
+ <ModalAlert
+ message={messages.pgettext(
+ 'preferences-view',
+ 'The app has a built in kill switch that is enabled by default and cannot be disabled. This is to prevent your traffic from leaking outside of the VPN tunnel if your network suddenly stops working or if the tunnel fails for any reason. Mullvad automatically protects your data until your connection is reestablished.',
+ )}
+ type={ModalAlertType.info}
+ buttons={[
+ <AppButton.BlueButton key="back" onClick={this.hideKillSwitchInfo}>
+ {messages.gettext('Got it!')}
+ </AppButton.BlueButton>,
+ ]}
+ close={this.hideKillSwitchInfo}
+ />
+ )}
+ </ModalContainer>
);
}
@@ -341,6 +380,14 @@ export default class Preferences extends React.Component<IProps> {
},
});
};
+
+ private showKillSwitchInfo = () => {
+ this.setState({ showKillSwitchInfo: true });
+ };
+
+ private hideKillSwitchInfo = () => {
+ this.setState({ showKillSwitchInfo: false });
+ };
}
function CustomDnsEnabledFooter() {