2020-11-10 20:50:52 +01:00
|
|
|
import { app, BrowserWindow, Event, LoadFileOptions, LoadURLOptions, NewWindowWebContentsEvent } from 'electron';
|
2020-02-08 23:26:57 +01:00
|
|
|
import os from 'os';
|
2020-11-09 18:15:30 +01:00
|
|
|
import path from 'path';
|
|
|
|
import { isDev } from '../../core/env';
|
2021-01-17 19:40:24 +01:00
|
|
|
import { completeContentSecurityPolicy, setWindowCsp } from '../session/session-util';
|
2021-01-06 02:35:46 +01:00
|
|
|
import type { AppWindowInterface } from './app-window-interface';
|
2021-01-07 04:51:07 +01:00
|
|
|
import { WindowClosedError } from './window-closed-error';
|
2020-02-08 23:26:57 +01:00
|
|
|
import BrowserWindowConstructorOptions = Electron.BrowserWindowConstructorOptions;
|
|
|
|
|
2020-11-09 18:15:30 +01:00
|
|
|
let defaultOptions: BrowserWindowConstructorOptions = {
|
2020-02-08 23:26:57 +01:00
|
|
|
width: 1600,
|
|
|
|
height: 900,
|
|
|
|
webPreferences: {
|
2020-11-09 18:15:30 +01:00
|
|
|
enableRemoteModule: false,
|
2020-02-08 23:26:57 +01:00
|
|
|
nodeIntegration: false,
|
2020-11-09 18:15:30 +01:00
|
|
|
contextIsolation: true,
|
|
|
|
devTools: isDev(),
|
2020-02-08 23:26:57 +01:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
switch (os.platform()) {
|
|
|
|
case 'win32':
|
|
|
|
defaultOptions = {
|
|
|
|
...defaultOptions,
|
|
|
|
...{
|
2020-10-31 23:12:00 +01:00
|
|
|
icon: path.resolve(app.getAppPath(), 'resources', 'icon.ico'),
|
2020-02-08 23:26:57 +01:00
|
|
|
},
|
|
|
|
};
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-01-06 02:35:46 +01:00
|
|
|
export abstract class AppWindow implements AppWindowInterface {
|
2020-11-09 18:15:30 +01:00
|
|
|
protected static default = {};
|
|
|
|
|
2020-07-25 02:02:37 +02:00
|
|
|
protected _window: BrowserWindow | null = null;
|
2020-02-08 23:26:57 +01:00
|
|
|
|
2021-01-07 04:51:07 +01:00
|
|
|
protected readonly logger: LoggerInterface;
|
|
|
|
|
2020-11-09 18:15:30 +01:00
|
|
|
protected options: BrowserWindowConstructorOptions;
|
|
|
|
|
|
|
|
protected uri: string;
|
|
|
|
|
|
|
|
protected abstract loadOptions: LoadFileOptions | LoadURLOptions;
|
|
|
|
|
2021-01-17 19:40:24 +01:00
|
|
|
protected constructor(logger: LoggerInterface, uri: string, options: BrowserWindowConstructorOptions = {}) {
|
2021-01-07 04:51:07 +01:00
|
|
|
this.logger = logger;
|
2020-11-09 18:15:30 +01:00
|
|
|
this.options = { ...defaultOptions, ...options };
|
|
|
|
this.uri = uri;
|
2020-02-08 23:26:57 +01:00
|
|
|
}
|
|
|
|
|
2020-07-25 02:02:37 +02:00
|
|
|
public get window(): BrowserWindow | null {
|
2020-02-08 23:26:57 +01:00
|
|
|
return this._window;
|
|
|
|
}
|
|
|
|
|
2021-01-17 19:40:24 +01:00
|
|
|
public getWindow(): BrowserWindow {
|
|
|
|
if (!this._window) {
|
|
|
|
throw new WindowClosedError();
|
|
|
|
}
|
|
|
|
return this._window;
|
|
|
|
}
|
|
|
|
|
2020-02-08 23:26:57 +01:00
|
|
|
public open(): Promise<void> {
|
2020-11-09 18:15:30 +01:00
|
|
|
this._window = new BrowserWindow(this.options);
|
|
|
|
|
2021-01-17 19:40:24 +01:00
|
|
|
setWindowCsp(this._window, completeContentSecurityPolicy(this.getCsp()));
|
2020-11-09 18:15:30 +01:00
|
|
|
|
|
|
|
this._window.on('closed', () => {
|
2021-01-05 02:29:01 +01:00
|
|
|
this.onClosed();
|
2020-11-09 18:15:30 +01:00
|
|
|
this._window = null;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (isDev()) {
|
|
|
|
this._window.webContents.openDevTools();
|
2020-02-08 23:26:57 +01:00
|
|
|
}
|
|
|
|
|
2020-11-09 18:15:30 +01:00
|
|
|
this._window.webContents.on('will-navigate', this.onWillNavigate);
|
|
|
|
this._window.webContents.on('new-window', this.onNewWindow);
|
|
|
|
|
|
|
|
return this.load(this._window);
|
|
|
|
}
|
|
|
|
|
|
|
|
public close(force: boolean = false): void {
|
|
|
|
if (force) {
|
|
|
|
this._window?.destroy();
|
2020-07-25 02:02:37 +02:00
|
|
|
} else {
|
2020-11-09 18:15:30 +01:00
|
|
|
this._window?.close();
|
2020-07-25 02:02:37 +02:00
|
|
|
}
|
2020-02-08 23:26:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public isClosed(): boolean {
|
|
|
|
return !this._window;
|
|
|
|
}
|
|
|
|
|
2021-01-17 19:40:24 +01:00
|
|
|
public askForUserInteraction(): void {
|
|
|
|
if (!this.getWindow().isFocused()) {
|
|
|
|
this.getWindow().on('focus', () => {
|
|
|
|
this.getWindow().flashFrame(false);
|
|
|
|
});
|
|
|
|
this.getWindow().flashFrame(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-06 02:35:46 +01:00
|
|
|
protected getCsp(): Session.ContentSecurityPolicy {
|
2020-11-09 18:15:30 +01:00
|
|
|
return {};
|
|
|
|
}
|
2020-02-08 23:26:57 +01:00
|
|
|
|
2020-11-09 18:15:30 +01:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- it is used in child classes
|
|
|
|
protected onWillNavigate(event: Event, navigationUrl: string): void {
|
|
|
|
event.preventDefault();
|
2020-02-08 23:26:57 +01:00
|
|
|
}
|
2020-11-09 18:15:30 +01:00
|
|
|
|
2020-11-10 20:50:52 +01:00
|
|
|
protected onNewWindow(event: NewWindowWebContentsEvent): void {
|
2020-11-09 18:15:30 +01:00
|
|
|
event.preventDefault();
|
|
|
|
}
|
|
|
|
|
2021-01-05 02:29:01 +01:00
|
|
|
protected onClosed(): void {}
|
|
|
|
|
2021-01-07 04:51:07 +01:00
|
|
|
protected getInnerHtml(selector: string): Promise<string> {
|
|
|
|
return new Promise<string>((resolve) => {
|
2021-01-17 19:40:24 +01:00
|
|
|
this.getWindow()
|
|
|
|
.webContents.executeJavaScript(`document.querySelector('${selector}').innerHTML`)
|
2021-01-07 04:51:07 +01:00
|
|
|
.then((innerHtml) => {
|
|
|
|
resolve(innerHtml);
|
|
|
|
})
|
|
|
|
.catch(() => {
|
|
|
|
void this.logger.warning(`Could not get the inner HTML of an element with the selector '${selector}'.`);
|
|
|
|
resolve('');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-11-09 18:15:30 +01:00
|
|
|
protected abstract load(window: BrowserWindow): Promise<void>;
|
2020-02-08 23:26:57 +01:00
|
|
|
}
|