summaryrefslogtreecommitdiffhomepage
path: root/gui/src/renderer
diff options
context:
space:
mode:
Diffstat (limited to 'gui/src/renderer')
-rw-r--r--gui/src/renderer/app.tsx13
-rw-r--r--gui/src/renderer/components/ErrorView.tsx29
-rw-r--r--gui/src/renderer/components/Launch.tsx64
-rw-r--r--gui/src/renderer/redux/userinterface/actions.ts14
-rw-r--r--gui/src/renderer/redux/userinterface/reducers.ts5
5 files changed, 119 insertions, 6 deletions
diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx
index 87bf91123b..14fa75f9cc 100644
--- a/gui/src/renderer/app.tsx
+++ b/gui/src/renderer/app.tsx
@@ -125,6 +125,10 @@ export default class AppRenderer {
this.setIsPerformingPostUpgrade(isPerformingPostUpgrade);
});
+ IpcRendererEventChannel.daemon.listenDaemonAllowed((daemonAllowed) => {
+ this.reduxActions.userInterface.setDaemonAllowed(daemonAllowed);
+ });
+
IpcRendererEventChannel.account.listen((newAccountData?: IAccountData) => {
this.setAccountExpiry(newAccountData?.expiry);
});
@@ -204,6 +208,10 @@ export default class AppRenderer {
this.setSettings(initialState.settings);
this.setIsPerformingPostUpgrade(initialState.isPerformingPostUpgrade);
+ if (initialState.daemonAllowed !== undefined) {
+ this.reduxActions.userInterface.setDaemonAllowed(initialState.daemonAllowed);
+ }
+
if (initialState.deviceState) {
const deviceState = initialState.deviceState;
this.handleDeviceEvent(
@@ -470,6 +478,10 @@ export default class AppRenderer {
void IpcRendererEventChannel.windowsSplitTunneling.removeApplication(application);
}
+ public async showLaunchDaemonSettings() {
+ await IpcRendererEventChannel.app.showLaunchDaemonSettings();
+ }
+
public async sendProblemReport(
email: string,
message: string,
@@ -617,6 +629,7 @@ export default class AppRenderer {
private onDaemonConnected() {
this.connectedToDaemon = true;
this.reduxActions.userInterface.setConnectedToDaemon(true);
+ this.reduxActions.userInterface.setDaemonAllowed(true);
this.resetNavigation();
}
diff --git a/gui/src/renderer/components/ErrorView.tsx b/gui/src/renderer/components/ErrorView.tsx
index 12ca510396..fead788c24 100644
--- a/gui/src/renderer/components/ErrorView.tsx
+++ b/gui/src/renderer/components/ErrorView.tsx
@@ -1,3 +1,4 @@
+import React from 'react';
import styled from 'styled-components';
import { colors } from '../../config.json';
@@ -10,8 +11,15 @@ const StyledContainer = styled(Container)({
flex: 1,
flexDirection: 'column',
alignItems: 'center',
- justifyContent: 'center',
- marginTop: '-150px',
+ justifyContent: 'end',
+});
+
+const StyledContent = styled.div({
+ display: 'flex',
+ flex: 1,
+ flexDirection: 'column',
+ alignItems: 'center',
+ justifyContent: 'end',
});
const Logo = styled(ImageView)({
@@ -32,8 +40,16 @@ const Subtitle = styled.span({
textAlign: 'center',
});
+const StyledFooterContainer = styled.div({
+ display: 'flex',
+ flexDirection: 'column',
+ justifyContent: 'end',
+ minHeight: '241px',
+});
+
interface ErrorViewProps {
settingsUnavailable?: boolean;
+ footer?: React.ReactNode | React.ReactNode[];
children: React.ReactNode | React.ReactNode[];
}
@@ -42,9 +58,12 @@ export default function ErrorView(props: ErrorViewProps) {
<Layout>
<Header>{!props.settingsUnavailable && <HeaderBarSettingsButton />}</Header>
<StyledContainer>
- <Logo height={106} width={106} source="logo-icon" />
- <Title height={18} source="logo-text" />
- <Subtitle role="alert">{props.children}</Subtitle>
+ <StyledContent>
+ <Logo height={106} width={106} source="logo-icon" />
+ <Title height={18} source="logo-text" />
+ <Subtitle role="alert">{props.children}</Subtitle>
+ </StyledContent>
+ <StyledFooterContainer>{props.footer}</StyledFooterContainer>
</StyledContainer>
</Layout>
);
diff --git a/gui/src/renderer/components/Launch.tsx b/gui/src/renderer/components/Launch.tsx
index a3145b8e16..5f12fcce8f 100644
--- a/gui/src/renderer/components/Launch.tsx
+++ b/gui/src/renderer/components/Launch.tsx
@@ -1,10 +1,72 @@
+import { useCallback } from 'react';
+import styled from 'styled-components';
+
+import { colors } from '../../config.json';
import { messages } from '../../shared/gettext';
+import { useAppContext } from '../context';
+import { useSelector } from '../redux/store';
+import * as AppButton from './AppButton';
+import { measurements, tinyText } from './common-styles';
import ErrorView from './ErrorView';
+import { Footer } from './Layout';
export default function Launch() {
+ const daemonAllowed = useSelector((state) => state.userInterface.daemonAllowed);
+ const footer = <SettingsFooter show={daemonAllowed === false} />;
+
return (
- <ErrorView>
+ <ErrorView footer={footer}>
{messages.pgettext('launch-view', 'Connecting to Mullvad system service...')}
</ErrorView>
);
}
+
+const StyledFooter = styled(Footer)({}, (props: { show: boolean }) => ({
+ backgroundColor: colors.blue,
+ padding: `0 14px ${measurements.viewMargin}`,
+ opacity: props.show ? 1 : 0,
+ transition: 'opacity 250ms ease-in-out',
+}));
+
+const StyledSystemSettingsContainer = styled.div({
+ display: 'flex',
+ flexDirection: 'column',
+ flex: 1,
+ backgroundColor: colors.darkBlue,
+ borderRadius: '8px',
+ margin: 0,
+ padding: '16px',
+});
+
+const StyledLaunchFooterPrompt = styled.span(tinyText, {
+ color: colors.white,
+ margin: `8px 0 ${measurements.buttonVerticalMargin} 0`,
+});
+
+interface ISettingsFooterProps {
+ show: boolean;
+}
+
+function SettingsFooter(props: ISettingsFooterProps) {
+ const { showLaunchDaemonSettings } = useAppContext();
+
+ const openSettings = useCallback(async () => {
+ await showLaunchDaemonSettings();
+ }, []);
+
+ return (
+ <StyledFooter show={props.show}>
+ <StyledSystemSettingsContainer>
+ <StyledLaunchFooterPrompt>
+ {messages.pgettext(
+ 'launch-view',
+ 'Permission for the Mullvad VPN service has been revoked. Please go to System Settings and allow Mullvad VPN under the “Allow in the Background” setting.',
+ )}
+ </StyledLaunchFooterPrompt>
+ <AppButton.BlueButton onClick={openSettings}>
+ {messages.gettext('Go to System Settings')}
+ </AppButton.BlueButton>
+ </StyledSystemSettingsContainer>
+ </StyledFooter>
+ );
+}
diff --git a/gui/src/renderer/redux/userinterface/actions.ts b/gui/src/renderer/redux/userinterface/actions.ts
index 46da6bdc3d..b4b5885370 100644
--- a/gui/src/renderer/redux/userinterface/actions.ts
+++ b/gui/src/renderer/redux/userinterface/actions.ts
@@ -30,6 +30,11 @@ export interface ISetConnectedToDaemon {
connectedToDaemon: boolean;
}
+export interface ISetDaemonAllowed {
+ type: 'SET_DAEMON_ALLOWED';
+ daemonAllowed: boolean;
+}
+
export interface ISetChangelog {
type: 'SET_CHANGELOG';
changelog: IChangelog;
@@ -52,6 +57,7 @@ export type UserInterfaceAction =
| ISetWindowFocusedAction
| ISetMacOsScrollbarVisibility
| ISetConnectedToDaemon
+ | ISetDaemonAllowed
| ISetChangelog
| ISetForceShowChanges
| ISetIsPerformingPostUpgrade;
@@ -99,6 +105,13 @@ function setConnectedToDaemon(connectedToDaemon: boolean): ISetConnectedToDaemon
};
}
+function setDaemonAllowed(daemonAllowed: boolean): ISetDaemonAllowed {
+ return {
+ type: 'SET_DAEMON_ALLOWED',
+ daemonAllowed,
+ };
+}
+
function setChangelog(changelog: IChangelog): ISetChangelog {
return {
type: 'SET_CHANGELOG',
@@ -127,6 +140,7 @@ export default {
setWindowFocused,
setMacOsScrollbarVisibility,
setConnectedToDaemon,
+ setDaemonAllowed,
setChangelog,
setForceShowChanges,
setIsPerformingPostUpgrade,
diff --git a/gui/src/renderer/redux/userinterface/reducers.ts b/gui/src/renderer/redux/userinterface/reducers.ts
index 96d8bc03e6..f9ecc6fdad 100644
--- a/gui/src/renderer/redux/userinterface/reducers.ts
+++ b/gui/src/renderer/redux/userinterface/reducers.ts
@@ -9,6 +9,7 @@ export interface IUserInterfaceReduxState {
windowFocused: boolean;
macOsScrollbarVisibility?: MacOsScrollbarVisibility;
connectedToDaemon: boolean;
+ daemonAllowed?: boolean;
changelog: IChangelog;
forceShowChanges: boolean;
isPerformingPostUpgrade: boolean;
@@ -20,6 +21,7 @@ const initialState: IUserInterfaceReduxState = {
windowFocused: false,
macOsScrollbarVisibility: undefined,
connectedToDaemon: false,
+ daemonAllowed: undefined,
changelog: [],
forceShowChanges: false,
isPerformingPostUpgrade: false,
@@ -48,6 +50,9 @@ export default function (
case 'SET_CONNECTED_TO_DAEMON':
return { ...state, connectedToDaemon: action.connectedToDaemon };
+ case 'SET_DAEMON_ALLOWED':
+ return { ...state, daemonAllowed: action.daemonAllowed };
+
case 'SET_CHANGELOG':
return {
...state,