summaryrefslogtreecommitdiffhomepage
path: root/gui/src/renderer
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2021-01-26 09:19:06 +0100
committerOskar Nyberg <oskar@mullvad.net>2021-01-26 09:19:06 +0100
commit2c5163b9fa05e8b21ed92f6992e3a216e96817fe (patch)
tree32f4f2e706e152626b31ca472e1e564f71340319 /gui/src/renderer
parent2b055cc39c4eee4b8af8bb193143ddd4055b39ac (diff)
parent5e9d51d61ecf3a7f8df10fcfc767213a108274c1 (diff)
downloadmullvadvpn-2c5163b9fa05e8b21ed92f6992e3a216e96817fe.tar.xz
mullvadvpn-2c5163b9fa05e8b21ed92f6992e3a216e96817fe.zip
Merge branch 'sandbox-electron-renderer'
Diffstat (limited to 'gui/src/renderer')
-rw-r--r--gui/src/renderer/app.tsx67
-rw-r--r--gui/src/renderer/components/AdvancedSettings.tsx2
-rw-r--r--gui/src/renderer/components/CustomScrollbars.tsx2
-rw-r--r--gui/src/renderer/components/Focus.tsx3
-rw-r--r--gui/src/renderer/components/HeaderBar.tsx2
-rw-r--r--gui/src/renderer/components/ImageView.tsx3
-rw-r--r--gui/src/renderer/components/NavigationBarStyles.tsx2
-rw-r--r--gui/src/renderer/components/PlatformWindow.tsx2
-rw-r--r--gui/src/renderer/components/Preferences.tsx4
-rw-r--r--gui/src/renderer/containers/SelectLanguagePage.tsx4
-rw-r--r--gui/src/renderer/context.tsx4
-rw-r--r--gui/src/renderer/index.html3
-rw-r--r--gui/src/renderer/lib/ipc-event-channel.ts5
-rw-r--r--gui/src/renderer/lib/load-translations.ts22
-rw-r--r--gui/src/renderer/lib/logging.ts3
-rw-r--r--gui/src/renderer/preload.ts4
-rw-r--r--gui/src/renderer/redux/settings/reducers.ts2
-rw-r--r--gui/src/renderer/redux/store.ts2
18 files changed, 87 insertions, 49 deletions
diff --git a/gui/src/renderer/app.tsx b/gui/src/renderer/app.tsx
index f63e03633a..9ff1459322 100644
--- a/gui/src/renderer/app.tsx
+++ b/gui/src/renderer/app.tsx
@@ -15,14 +15,15 @@ import configureStore from './redux/store';
import userInterfaceActions from './redux/userinterface/actions';
import versionActions from './redux/version/actions';
-import { ICurrentAppVersionInfo } from '../main';
-import { loadTranslations, messages, relayLocations } from '../shared/gettext';
-import { IGuiSettingsState, SYSTEM_PREFERRED_LOCALE_KEY } from '../shared/gui-settings-state';
-import { IpcRendererEventChannel, IRelayListPair } from '../shared/ipc-event-channel';
+import { ICurrentAppVersionInfo } from '../shared/ipc-types';
import { ILinuxSplitTunnelingApplication } from '../shared/application-types';
+import { messages, relayLocations } from '../shared/gettext';
+import { IGuiSettingsState, SYSTEM_PREFERRED_LOCALE_KEY } from '../shared/gui-settings-state';
import log, { ConsoleOutput } from '../shared/logging';
+import { IRelayListPair } from '../shared/ipc-schema';
import consumePromise from '../shared/promise';
import History from './lib/history';
+import { loadTranslations } from './lib/load-translations';
import {
AccountToken,
@@ -45,6 +46,8 @@ import {
import { LogLevel } from '../shared/logging-types';
import IpcOutput from './lib/logging';
+const IpcRendererEventChannel = window.ipc;
+
interface IPreferredLocaleDescriptor {
name: string;
code: string;
@@ -98,20 +101,6 @@ export default class AppRenderer {
log.addOutput(new ConsoleOutput(LogLevel.debug));
log.addOutput(new IpcOutput(LogLevel.debug));
- IpcRendererEventChannel.locale.listen((locale) => {
- // load translations for the new locale
- this.loadTranslations(locale);
-
- // set current locale
- this.setLocale(locale);
-
- // refresh the relay list pair with the new translations
- this.propagateRelayListPairToRedux();
-
- // refresh the location with the new translations
- this.propagateLocationToRedux();
- });
-
IpcRendererEventChannel.windowShape.listen((windowShapeParams) => {
if (typeof windowShapeParams.arrowPosition === 'number') {
this.reduxActions.userInterface.updateWindowArrowPosition(windowShapeParams.arrowPosition);
@@ -186,9 +175,20 @@ export default class AppRenderer {
// Request the initial state from the main process
const initialState = IpcRendererEventChannel.state.get();
- // Load translations
- this.loadTranslations(initialState.locale);
+ window.platform = initialState.platform;
+ window.runningInDevelopment = initialState.runningInDevelopment;
+
this.setLocale(initialState.locale);
+ loadTranslations(
+ messages,
+ initialState.translations.locale,
+ initialState.translations.messages,
+ );
+ loadTranslations(
+ relayLocations,
+ initialState.translations.locale,
+ initialState.translations.relayLocations,
+ );
this.setAccountExpiry(initialState.accountData && initialState.accountData.expiry);
this.handleAccountChange(undefined, initialState.settings.accountToken);
@@ -464,8 +464,23 @@ export default class AppRenderer {
];
}
- public setPreferredLocale(preferredLocale: string) {
- IpcRendererEventChannel.guiSettings.setPreferredLocale(preferredLocale);
+ public async setPreferredLocale(preferredLocale: string): Promise<void> {
+ const translations = await IpcRendererEventChannel.guiSettings.setPreferredLocale(
+ preferredLocale,
+ );
+
+ // set current locale
+ this.setLocale(translations.locale);
+
+ // load translations for new locale
+ loadTranslations(messages, translations.locale, translations.messages);
+ loadTranslations(relayLocations, translations.locale, translations.relayLocations);
+
+ // refresh the relay list pair with the new translations
+ this.propagateRelayListPairToRedux();
+
+ // refresh the location with the new translations
+ this.propagateLocationToRedux();
}
public getPreferredLocaleDisplayName(localeCode: string): string {
@@ -479,12 +494,6 @@ export default class AppRenderer {
this.loginTimer = global.setTimeout(() => this.history.resetWith('/connect'), 1000);
}
- private loadTranslations(locale: string) {
- for (const catalogue of [messages, relayLocations]) {
- loadTranslations(locale, catalogue);
- }
- }
-
private setLocale(locale: string) {
this.locale = locale;
this.reduxActions.userInterface.updateLocale(locale);
@@ -576,7 +585,7 @@ export default class AppRenderer {
}
private async autoConnect() {
- if (process.env.NODE_ENV === 'development') {
+ if (window.runningInDevelopment) {
log.info('Skip autoconnect in development');
} else if (this.autoConnected) {
log.info('Skip autoconnect because it was done before');
diff --git a/gui/src/renderer/components/AdvancedSettings.tsx b/gui/src/renderer/components/AdvancedSettings.tsx
index 6633e35746..7c97f0d844 100644
--- a/gui/src/renderer/components/AdvancedSettings.tsx
+++ b/gui/src/renderer/components/AdvancedSettings.tsx
@@ -429,7 +429,7 @@ export default class AdvancedSettings extends React.Component<IProps, IState> {
<Cell.Icon height={12} width={7} source="icon-chevron" />
</Cell.CellButton>
- {process.platform === 'linux' && (
+ {window.platform === 'linux' && (
<Cell.CellButton onClick={this.props.onViewLinuxSplitTunneling}>
<Cell.Label>
{messages.pgettext('advanced-settings-view', 'Split tunneling')}
diff --git a/gui/src/renderer/components/CustomScrollbars.tsx b/gui/src/renderer/components/CustomScrollbars.tsx
index bc56ffcf3c..87c5b82dce 100644
--- a/gui/src/renderer/components/CustomScrollbars.tsx
+++ b/gui/src/renderer/components/CustomScrollbars.tsx
@@ -47,7 +47,7 @@ interface IScrollbarUpdateContext {
export default class CustomScrollbars extends React.Component<IProps, IState> {
public static defaultProps: IProps = {
// auto-hide on macOS by default
- autoHide: process.platform === 'darwin',
+ autoHide: window.platform === 'darwin',
trackPadding: { x: 2, y: 2 },
};
diff --git a/gui/src/renderer/components/Focus.tsx b/gui/src/renderer/components/Focus.tsx
index 6a2e9c7126..85c2c307e4 100644
--- a/gui/src/renderer/components/Focus.tsx
+++ b/gui/src/renderer/components/Focus.tsx
@@ -1,4 +1,3 @@
-import path from 'path';
import React, { useImperativeHandle, useState } from 'react';
import { useLocation } from 'react-router';
import { sprintf } from 'sprintf-js';
@@ -29,7 +28,7 @@ function Focus(props: IFocusProps, ref: React.Ref<IFocusHandle>) {
ref,
() => ({
resetFocus: () => {
- const pageName = path.basename(location.pathname);
+ const pageName = location.pathname.slice(location.pathname.lastIndexOf('/') + 1);
const titleElement = document.getElementsByTagName('h1')[0];
const titleContent = titleElement?.textContent ?? pageName;
setTitle(titleContent);
diff --git a/gui/src/renderer/components/HeaderBar.tsx b/gui/src/renderer/components/HeaderBar.tsx
index 67b9622b21..bdf7d51924 100644
--- a/gui/src/renderer/components/HeaderBar.tsx
+++ b/gui/src/renderer/components/HeaderBar.tsx
@@ -28,7 +28,7 @@ interface IHeaderBarContainerProps {
const HeaderBarContainer = styled.header({}, (props: IHeaderBarContainerProps) => ({
padding: '12px 16px',
- paddingTop: process.platform === 'darwin' && !props.unpinnedWindow ? '24px' : '12px',
+ paddingTop: window.platform === 'darwin' && !props.unpinnedWindow ? '24px' : '12px',
backgroundColor: headerBarStyleColorMap[props.barStyle ?? HeaderBarStyle.default],
}));
diff --git a/gui/src/renderer/components/ImageView.tsx b/gui/src/renderer/components/ImageView.tsx
index fd6588b1fb..3be04159a2 100644
--- a/gui/src/renderer/components/ImageView.tsx
+++ b/gui/src/renderer/components/ImageView.tsx
@@ -1,4 +1,3 @@
-import path from 'path';
import React, { useMemo } from 'react';
import styled from 'styled-components';
@@ -40,7 +39,7 @@ const ImageMask = styled.div((props: IImageMaskProps) => {
const HiddenImage = styled.img({ visibility: 'hidden' });
export default function ImageView(props: IImageViewProps) {
- const url = path.isAbsolute(props.source)
+ const url = props.source.startsWith('data:')
? props.source
: `../../assets/images/${props.source}.svg`;
diff --git a/gui/src/renderer/components/NavigationBarStyles.tsx b/gui/src/renderer/components/NavigationBarStyles.tsx
index 98c5184ea3..a82256b077 100644
--- a/gui/src/renderer/components/NavigationBarStyles.tsx
+++ b/gui/src/renderer/components/NavigationBarStyles.tsx
@@ -20,7 +20,7 @@ export const StyledNavigationItems = styled.div({
export const StyledNavigationBar = styled.nav((props: { unpinnedWindow: boolean }) => ({
flex: 0,
padding: '12px',
- paddingTop: process.platform === 'darwin' && !props.unpinnedWindow ? '24px' : '12px',
+ paddingTop: window.platform === 'darwin' && !props.unpinnedWindow ? '24px' : '12px',
}));
export const StyledNavigationBarWrapper = styled.div({
diff --git a/gui/src/renderer/components/PlatformWindow.tsx b/gui/src/renderer/components/PlatformWindow.tsx
index 633b7aee95..83aa7b3f7f 100644
--- a/gui/src/renderer/components/PlatformWindow.tsx
+++ b/gui/src/renderer/components/PlatformWindow.tsx
@@ -10,7 +10,7 @@ interface IPlatformWindowProps {
export default styled.div({}, (props: IPlatformWindowProps) => {
let mask: string | undefined;
- if (process.platform === 'darwin' && !props.unpinnedWindow) {
+ if (window.platform === 'darwin' && !props.unpinnedWindow) {
const arrowPositionCss =
props.arrowPosition !== undefined ? `${props.arrowPosition - ARROW_WIDTH * 0.5}px` : '50%';
diff --git a/gui/src/renderer/components/Preferences.tsx b/gui/src/renderer/components/Preferences.tsx
index ab2b423686..d47a7315c6 100644
--- a/gui/src/renderer/components/Preferences.tsx
+++ b/gui/src/renderer/components/Preferences.tsx
@@ -179,8 +179,8 @@ export default class Preferences extends React.Component<IProps> {
</Cell.Footer>
</AriaInputGroup>
- {(process.platform === 'win32' ||
- (process.platform === 'darwin' && process.env.NODE_ENV === 'development')) && (
+ {(window.platform === 'win32' ||
+ (window.platform === 'darwin' && window.runningInDevelopment)) && (
<AriaInputGroup>
<Cell.Container>
<AriaLabel>
diff --git a/gui/src/renderer/containers/SelectLanguagePage.tsx b/gui/src/renderer/containers/SelectLanguagePage.tsx
index eb052fb8a6..5c073c22e1 100644
--- a/gui/src/renderer/containers/SelectLanguagePage.tsx
+++ b/gui/src/renderer/containers/SelectLanguagePage.tsx
@@ -11,8 +11,8 @@ const mapStateToProps = (state: IReduxState) => ({
const mapDispatchToProps = (_dispatch: ReduxDispatch, props: RouteComponentProps & IAppContext) => {
return {
preferredLocalesList: props.app.getPreferredLocaleList(),
- setPreferredLocale(locale: string) {
- props.app.setPreferredLocale(locale);
+ async setPreferredLocale(locale: string) {
+ await props.app.setPreferredLocale(locale);
props.history.goBack();
},
onClose() {
diff --git a/gui/src/renderer/context.tsx b/gui/src/renderer/context.tsx
index 113a29fe71..d4b20daeb1 100644
--- a/gui/src/renderer/context.tsx
+++ b/gui/src/renderer/context.tsx
@@ -6,7 +6,7 @@ export interface IAppContext {
}
export const AppContext = React.createContext<IAppContext | undefined>(undefined);
-if (process.env.NODE_ENV === 'development') {
+if (window.runningInDevelopment) {
AppContext.displayName = 'AppContext';
}
@@ -34,7 +34,7 @@ export default function withAppContext<Props>(BaseComponent: React.ComponentType
);
};
- if (process.env.NODE_ENV === 'development') {
+ if (window.runningInDevelopment) {
wrappedComponent.displayName =
'withAppContext(' + (BaseComponent.displayName || BaseComponent.name) + ')';
}
diff --git a/gui/src/renderer/index.html b/gui/src/renderer/index.html
index 3f2da3d633..043955b3c5 100644
--- a/gui/src/renderer/index.html
+++ b/gui/src/renderer/index.html
@@ -4,10 +4,11 @@
<title>Mullvad VPN</title>
<link rel="stylesheet" href="../../assets/css/style.css" />
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline'">
+ <meta charset="UTF-8">
</head>
<body>
<div id="app"></div>
<script>var exports = {};</script>
- <script src="./index.js"></script>
+ <script src="./bundle.js"></script>
</body>
</html>
diff --git a/gui/src/renderer/lib/ipc-event-channel.ts b/gui/src/renderer/lib/ipc-event-channel.ts
new file mode 100644
index 0000000000..bcb816e918
--- /dev/null
+++ b/gui/src/renderer/lib/ipc-event-channel.ts
@@ -0,0 +1,5 @@
+import { ipcRenderer } from 'electron';
+import { createIpcRenderer } from '../../shared/ipc-helpers';
+import { ipcSchema } from '../../shared/ipc-schema';
+
+export const IpcRendererEventChannel = createIpcRenderer(ipcSchema, ipcRenderer);
diff --git a/gui/src/renderer/lib/load-translations.ts b/gui/src/renderer/lib/load-translations.ts
new file mode 100644
index 0000000000..43a483de25
--- /dev/null
+++ b/gui/src/renderer/lib/load-translations.ts
@@ -0,0 +1,22 @@
+import { GetTextTranslations } from 'gettext-parser';
+import Gettext from 'node-gettext';
+import log from '../../shared/logging';
+
+const SOURCE_LANGUAGE = 'en';
+
+export function loadTranslations(
+ catalogue: Gettext,
+ locale: string,
+ translations?: GetTextTranslations,
+) {
+ if (translations) {
+ catalogue.addTranslations(locale, catalogue.domain, translations);
+ catalogue.setLocale(locale);
+ log.info(`Loaded translations ${locale}/${catalogue.domain}`);
+ } else {
+ // Reset the locale to source language if we couldn't load the catalogue for the requested locale
+ // Add empty translations to suppress some of the warnings produces by node-gettext
+ catalogue.addTranslations(SOURCE_LANGUAGE, catalogue.domain, {});
+ catalogue.setLocale(SOURCE_LANGUAGE);
+ }
+}
diff --git a/gui/src/renderer/lib/logging.ts b/gui/src/renderer/lib/logging.ts
index 433ba20f8f..7c8dc9e542 100644
--- a/gui/src/renderer/lib/logging.ts
+++ b/gui/src/renderer/lib/logging.ts
@@ -1,10 +1,9 @@
-import { IpcRendererEventChannel } from '../../shared/ipc-event-channel';
import { ILogOutput, LogLevel } from '../../shared/logging-types';
export default class IpcOutput implements ILogOutput {
constructor(public level: LogLevel) {}
public write(level: LogLevel, message: string) {
- IpcRendererEventChannel.logging.log({ level: level, message });
+ window.ipc.logging.log({ level: level, message });
}
}
diff --git a/gui/src/renderer/preload.ts b/gui/src/renderer/preload.ts
new file mode 100644
index 0000000000..5c44b71eeb
--- /dev/null
+++ b/gui/src/renderer/preload.ts
@@ -0,0 +1,4 @@
+import { contextBridge } from 'electron';
+import { IpcRendererEventChannel } from './lib/ipc-event-channel';
+
+contextBridge.exposeInMainWorld('ipc', IpcRendererEventChannel);
diff --git a/gui/src/renderer/redux/settings/reducers.ts b/gui/src/renderer/redux/settings/reducers.ts
index 59b4c32b46..592caab687 100644
--- a/gui/src/renderer/redux/settings/reducers.ts
+++ b/gui/src/renderer/redux/settings/reducers.ts
@@ -148,7 +148,7 @@ const initialState: ISettingsReduxState = {
autoConnect: true,
monochromaticIcon: false,
startMinimized: false,
- unpinnedWindow: process.platform !== 'win32' && process.platform !== 'darwin',
+ unpinnedWindow: window.platform !== 'win32' && window.platform !== 'darwin',
},
relaySettings: {
normal: {
diff --git a/gui/src/renderer/redux/store.ts b/gui/src/renderer/redux/store.ts
index 49f864d18b..92d85e4dbe 100644
--- a/gui/src/renderer/redux/store.ts
+++ b/gui/src/renderer/redux/store.ts
@@ -54,7 +54,7 @@ export default function configureStore(initialState?: IReduxState) {
const composeEnhancers: typeof compose = (() => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const reduxCompose = window && (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
- if (process.env.NODE_ENV === 'development' && reduxCompose) {
+ if (window.runningInDevelopment && reduxCompose) {
return reduxCompose({ actionCreators });
}
return compose;