diff options
| author | Oskar Nyberg <oskar@mullvad.net> | 2021-12-21 13:49:21 +0100 |
|---|---|---|
| committer | Oskar Nyberg <oskar@mullvad.net> | 2021-12-21 13:49:21 +0100 |
| commit | 220948c522328928c16faafca5a553c6bce2e95e (patch) | |
| tree | 0838e7b51d3d69b7a0640f3a7e53e1a5cf132d6d | |
| parent | c50902f92e0c4a4887eb9acc8458691d42e7b219 (diff) | |
| parent | e33a9a936e7be501e1da44b94cf5805bb8f3c824 (diff) | |
| download | mullvadvpn-220948c522328928c16faafca5a553c6bce2e95e.tar.xz mullvadvpn-220948c522328928c16faafca5a553c6bce2e95e.zip | |
Merge branch 'add-killswitch-info'
| -rw-r--r-- | CHANGELOG.md | 2 | ||||
| -rw-r--r-- | gui/assets/images/icon-info.svg | 3 | ||||
| -rw-r--r-- | gui/locales/messages.pot | 8 | ||||
| -rw-r--r-- | gui/src/renderer/components/AdvancedSettings.tsx | 2 | ||||
| -rw-r--r-- | gui/src/renderer/components/CustomDnsSettings.tsx | 2 | ||||
| -rw-r--r-- | gui/src/renderer/components/ExpiredAccountErrorView.tsx | 2 | ||||
| -rw-r--r-- | gui/src/renderer/components/Modal.tsx | 5 | ||||
| -rw-r--r-- | gui/src/renderer/components/Preferences.tsx | 495 |
8 files changed, 292 insertions, 227 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 128355bf90..0adac15caf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ Line wrap the file at 100 chars. Th ## [Unreleased] ### Added +- Add information about the always on kill switch in the desktop app. + #### macOS - Add an opt-in feature to leak macOS network check traffic in blocked states to resolve issues with the app blocking internet connectivity after sleep or when connecting to new wireless networks. 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() { |
