summaryrefslogtreecommitdiffhomepage
path: root/gui/src/shared
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/shared
parent2b055cc39c4eee4b8af8bb193143ddd4055b39ac (diff)
parent5e9d51d61ecf3a7f8df10fcfc767213a108274c1 (diff)
downloadmullvadvpn-2c5163b9fa05e8b21ed92f6992e3a216e96817fe.tar.xz
mullvadvpn-2c5163b9fa05e8b21ed92f6992e3a216e96817fe.zip
Merge branch 'sandbox-electron-renderer'
Diffstat (limited to 'gui/src/shared')
-rw-r--r--gui/src/shared/gettext.ts59
-rw-r--r--gui/src/shared/ipc-helpers.ts60
-rw-r--r--gui/src/shared/ipc-schema.ts (renamed from gui/src/shared/ipc-event-channel.ts)34
-rw-r--r--gui/src/shared/ipc-types.ts10
-rw-r--r--gui/src/shared/notifications/error.ts4
-rw-r--r--gui/src/shared/notifications/no-valid-key.ts2
6 files changed, 60 insertions, 109 deletions
diff --git a/gui/src/shared/gettext.ts b/gui/src/shared/gettext.ts
index 3283269c70..0416e8b1ce 100644
--- a/gui/src/shared/gettext.ts
+++ b/gui/src/shared/gettext.ts
@@ -1,67 +1,8 @@
-import fs from 'fs';
-import { po } from 'gettext-parser';
import Gettext from 'node-gettext';
-import path from 'path';
import { LocalizationContexts } from './localization-contexts';
import log from './logging';
const SOURCE_LANGUAGE = 'en';
-const LOCALES_DIR = path.resolve(__dirname, '../../locales');
-
-export function loadTranslations(currentLocale: string, catalogue: Gettext) {
- // First look for exact match of the current locale
- const preferredLocales = [];
-
- if (currentLocale !== SOURCE_LANGUAGE) {
- preferredLocales.push(currentLocale);
- }
-
- // In case of region bound locale like en-US, fallback to en.
- const language = Gettext.getLanguageCode(currentLocale);
- if (currentLocale !== language) {
- preferredLocales.push(language);
- }
-
- const domain = catalogue.domain;
- for (const locale of preferredLocales) {
- if (parseTranslation(locale, domain, catalogue)) {
- log.info(`Loaded translations ${locale}/${domain}`);
- catalogue.setLocale(locale);
- return;
- }
- }
-
- // 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, domain, {});
- catalogue.setLocale(SOURCE_LANGUAGE);
-}
-
-function parseTranslation(locale: string, domain: string, catalogue: Gettext): boolean {
- const filename = path.join(LOCALES_DIR, locale, `${domain}.po`);
- let contents: string;
-
- try {
- contents = fs.readFileSync(filename, { encoding: 'utf8' });
- } catch (error) {
- if (error.code !== 'ENOENT') {
- log.error(`Cannot read the gettext file "${filename}": ${error.message}`);
- }
- return false;
- }
-
- let translations: ReturnType<typeof po.parse>;
- try {
- translations = po.parse(contents);
- } catch (error) {
- log.error(`Cannot parse the gettext file "${filename}": ${error.message}`);
- return false;
- }
-
- catalogue.addTranslations(locale, domain, translations);
-
- return true;
-}
function setErrorHandler(catalogue: Gettext) {
catalogue.on('error', (error) => {
diff --git a/gui/src/shared/ipc-helpers.ts b/gui/src/shared/ipc-helpers.ts
index d1a72a2df6..59eee46459 100644
--- a/gui/src/shared/ipc-helpers.ts
+++ b/gui/src/shared/ipc-helpers.ts
@@ -1,4 +1,4 @@
-import { ipcMain, ipcRenderer, WebContents } from 'electron';
+import { IpcMain as EIpcMain, IpcRenderer as EIpcRenderer, WebContents } from 'electron';
import { capitalize } from './string-helpers';
import log from './logging';
@@ -7,26 +7,21 @@ type Sender<T, R> = (arg: T) => R;
type Notifier<T> = (webContents: WebContents, arg: T) => void;
type Listener<T> = (callback: (arg: T) => void) => void;
-interface IpcCall<T, R> {
- direction: 'renderer-to-main' | 'main-to-renderer';
- send: (event: string) => Notifier<T> | Sender<T, R>;
- receive: (event: string) => Listener<T> | Handler<T, R>;
-}
-
-interface MainToRenderer<T> extends IpcCall<T, never> {
+interface MainToRenderer<T> {
direction: 'main-to-renderer';
- send: (event: string) => Notifier<T>;
- receive: (event: string) => Listener<T>;
+ send: (event: string, ipcMain: EIpcMain) => Notifier<T>;
+ receive: (event: string, ipcRenderer: EIpcRenderer) => Listener<T>;
}
-interface RendererToMain<T, R> extends IpcCall<T, R> {
+interface RendererToMain<T, R> {
direction: 'renderer-to-main';
- send: (event: string) => Sender<T, R>;
- receive: (event: string) => Handler<T, R>;
+ send: (event: string, ipcRenderer: EIpcRenderer) => Sender<T, R>;
+ receive: (event: string, ipcMain: EIpcMain) => Handler<T, R>;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
-type AnyIpcCall = IpcCall<any, any>;
+type AnyIpcCall = MainToRenderer<any> | RendererToMain<any, any>;
+
type Schema = Record<string, Record<string, AnyIpcCall>>;
// Renames all IPC calls, e.g. `callName` to either `notifyCallName` or `handleCallName` depending
@@ -66,22 +61,31 @@ type IpcRenderer<S extends Schema> = {
};
// Preforms the transformation of the main event channel in accordance with the above types.
-export function createIpcMain<S extends Schema>(ipc: S): IpcMain<S> {
- return createIpc(ipc, (event, key, spec) => {
+export function createIpcMain<S extends Schema>(schema: S, ipcMain: EIpcMain): IpcMain<S> {
+ return createIpc(schema, (event, key, spec) => {
const capitalizedKey = capitalize(key);
const newKey =
spec.direction === 'main-to-renderer' ? `notify${capitalizedKey}` : `handle${capitalizedKey}`;
- const newValue = spec.direction === 'main-to-renderer' ? spec.send(event) : spec.receive(event);
+ const newValue =
+ spec.direction === 'main-to-renderer'
+ ? spec.send(event, ipcMain)
+ : spec.receive(event, ipcMain);
return [newKey, newValue];
});
}
// Preforms the transformation of the renderer event channel in accordance with the above types.
-export function createIpcRenderer<S extends Schema>(ipc: S): IpcRenderer<S> {
- return createIpc(ipc, (event, key, spec) => {
+export function createIpcRenderer<S extends Schema>(
+ schema: S,
+ ipcRenderer: EIpcRenderer,
+): IpcRenderer<S> {
+ return createIpc(schema, (event, key, spec) => {
const newKey = spec.direction === 'main-to-renderer' ? `listen${capitalize(key)}` : key;
- const newValue = spec.direction === 'main-to-renderer' ? spec.receive(event) : spec.send(event);
+ const newValue =
+ spec.direction === 'main-to-renderer'
+ ? spec.receive(event, ipcRenderer)
+ : spec.send(event, ipcRenderer);
return [newKey, newValue];
});
@@ -105,8 +109,8 @@ function createIpc<S extends Schema, T, R extends IpcMain<S> | IpcRenderer<S>>(
export function send<T>(): RendererToMain<T, void> {
return {
direction: 'renderer-to-main',
- send: (event: string) => (newValue: T) => ipcRenderer.send(event, newValue),
- receive: (event: string) => (handlerFn: (value: T) => void) => {
+ send: (event, ipcRenderer) => (newValue: T) => ipcRenderer.send(event, newValue),
+ receive: (event, ipcMain) => (handlerFn: (value: T) => void) => {
ipcMain.on(event, (_event, newValue: T) => {
handlerFn(newValue);
});
@@ -118,8 +122,8 @@ export function send<T>(): RendererToMain<T, void> {
export function invokeSync<T, R>(): RendererToMain<T, R> {
return {
direction: 'renderer-to-main',
- send: (event: string) => (newValue: T) => ipcRenderer.sendSync(event, newValue),
- receive: (event: string) => (handlerFn: (value: T) => R) => {
+ send: (event, ipcRenderer) => (newValue: T) => ipcRenderer.sendSync(event, newValue),
+ receive: (event, ipcMain) => (handlerFn: (value: T) => R) => {
ipcMain.on(event, (ipcEvent, newValue: T) => {
ipcEvent.returnValue = handlerFn(newValue);
});
@@ -141,13 +145,13 @@ export function notifyRenderer<T>(): MainToRenderer<T> {
return {
direction: 'main-to-renderer',
send: notifyRendererImpl,
- receive: (event: string) => (fn: (value: T) => void) => {
+ receive: (event, ipcRenderer) => (fn: (value: T) => void) => {
ipcRenderer.on(event, (_event, newState: T) => fn(newState));
},
};
}
-function notifyRendererImpl<T>(event: string): Notifier<T> {
+function notifyRendererImpl<T>(event: string, _ipcMain: EIpcMain): Notifier<T> {
return (webContents: WebContents, value: T) => {
if (webContents.isDestroyed()) {
log.error(`sender(${event}): webContents is already destroyed!`);
@@ -159,7 +163,7 @@ function notifyRendererImpl<T>(event: string): Notifier<T> {
type RequestResult<T> = { type: 'success'; value: T } | { type: 'error'; message: string };
-function invokeImpl<T, R>(event: string): Sender<T, Promise<R>> {
+function invokeImpl<T, R>(event: string, ipcRenderer: EIpcRenderer): Sender<T, Promise<R>> {
return async (arg: T): Promise<R> => {
const result: RequestResult<R> = await ipcRenderer.invoke(event, arg);
switch (result.type) {
@@ -171,7 +175,7 @@ function invokeImpl<T, R>(event: string): Sender<T, Promise<R>> {
};
}
-function handle<T, R>(event: string): Handler<T, Promise<R>> {
+function handle<T, R>(event: string, ipcMain: EIpcMain): Handler<T, Promise<R>> {
return (fn: (arg: T) => Promise<R>) => {
ipcMain.handle(event, async (_ipcEvent, arg: T) => {
try {
diff --git a/gui/src/shared/ipc-event-channel.ts b/gui/src/shared/ipc-schema.ts
index 19c11f85ab..a31745c01f 100644
--- a/gui/src/shared/ipc-event-channel.ts
+++ b/gui/src/shared/ipc-schema.ts
@@ -1,6 +1,5 @@
-import { ICurrentAppVersionInfo } from '../main/index';
-import { IWindowShapeParameters } from '../main/window-controller';
-import { ILinuxSplitTunnelingApplication } from '../shared/application-types';
+import { GetTextTranslations } from 'gettext-parser';
+import { ILinuxSplitTunnelingApplication } from './application-types';
import {
AccountToken,
BridgeSettings,
@@ -18,20 +17,20 @@ import {
VoucherResponse,
} from './daemon-rpc-types';
import { IGuiSettingsState } from './gui-settings-state';
-import {
- createIpcMain,
- createIpcRenderer,
- invoke,
- invokeSync,
- notifyRenderer,
- send,
-} from './ipc-helpers';
import { LogLevel } from './logging-types';
interface ILogEntry {
level: LogLevel;
message: string;
}
+import { invoke, invokeSync, notifyRenderer, send } from './ipc-helpers';
+import { ICurrentAppVersionInfo, IWindowShapeParameters } from './ipc-types';
+
+export interface ITranslations {
+ locale: string;
+ messages?: GetTextTranslations;
+ relayLocations?: GetTextTranslations;
+}
export interface IRelayListPair {
relays: IRelayList;
@@ -52,6 +51,9 @@ export interface IAppStateSnapshot {
upgradeVersion: IAppVersionInfo;
guiSettings: IGuiSettingsState;
wireguardPublicKey?: IWireguardPublicKey;
+ translations: ITranslations;
+ platform: NodeJS.Platform;
+ runningInDevelopment: boolean;
}
// The different types of requests are:
@@ -93,13 +95,10 @@ export interface IAppStateSnapshot {
// listenFourth: (fn: (arg: boolean) => void) => void,
// },
// }
-const ipc = {
+export const ipcSchema = {
state: {
get: invokeSync<void, IAppStateSnapshot>(),
},
- locale: {
- '': notifyRenderer<string>(),
- },
windowShape: {
'': notifyRenderer<IWindowShapeParameters>(),
},
@@ -155,7 +154,7 @@ const ipc = {
setAutoConnect: send<boolean>(),
setStartMinimized: send<boolean>(),
setMonochromaticIcon: send<boolean>(),
- setPreferredLocale: send<string>(),
+ setPreferredLocale: invoke<string, ITranslations>(),
setUnpinnedWindow: send<boolean>(),
},
account: {
@@ -192,6 +191,3 @@ const ipc = {
log: send<ILogEntry>(),
},
};
-
-export const IpcMainEventChannel = createIpcMain(ipc);
-export const IpcRendererEventChannel = createIpcRenderer(ipc);
diff --git a/gui/src/shared/ipc-types.ts b/gui/src/shared/ipc-types.ts
new file mode 100644
index 0000000000..7781551ed5
--- /dev/null
+++ b/gui/src/shared/ipc-types.ts
@@ -0,0 +1,10 @@
+export interface ICurrentAppVersionInfo {
+ gui: string;
+ daemon: string;
+ isConsistent: boolean;
+ isBeta: boolean;
+}
+
+export interface IWindowShapeParameters {
+ arrowPosition?: number;
+}
diff --git a/gui/src/shared/notifications/error.ts b/gui/src/shared/notifications/error.ts
index d580bf1f1e..c5f15482dc 100644
--- a/gui/src/shared/notifications/error.ts
+++ b/gui/src/shared/notifications/error.ts
@@ -45,7 +45,7 @@ export class ErrorNotificationProvider
function getMessage(errorDetails: IErrorState, accountExpiry?: string): string {
if (errorDetails.blockFailure) {
if (errorDetails.cause.reason === 'set_firewall_policy_error') {
- switch (process.platform) {
+ switch (process.platform ?? window.platform) {
case 'win32':
return messages.pgettext(
'notifications',
@@ -86,7 +86,7 @@ function getMessage(errorDetails: IErrorState, accountExpiry?: string): string {
'Could not configure IPv6. Disable it in the app or enable it on your device.',
);
case 'set_firewall_policy_error':
- switch (process.platform) {
+ switch (process.platform ?? window.platform) {
case 'win32':
return messages.pgettext(
'notifications',
diff --git a/gui/src/shared/notifications/no-valid-key.ts b/gui/src/shared/notifications/no-valid-key.ts
index d1b90095e2..8da145a1f0 100644
--- a/gui/src/shared/notifications/no-valid-key.ts
+++ b/gui/src/shared/notifications/no-valid-key.ts
@@ -14,7 +14,7 @@ export class NoValidKeyNotificationProvider implements InAppNotificationProvider
public mayDisplay() {
const usingWireGuard =
this.context.tunnelProtocol === 'wireguard' ||
- (this.context.tunnelProtocol === 'any' && process.platform !== 'win32');
+ (this.context.tunnelProtocol === 'any' && (process.platform ?? window.platform) !== 'win32');
const keyInvalid =
this.context.wireGuardKey.type === 'key-not-set' ||
this.context.wireGuardKey.type === 'too-many-keys' ||