/
ElectronApp.ts
94 lines (84 loc) · 3.77 KB
/
ElectronApp.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Renderer
*/
import { ProcessDetector, PromiseReturnType } from "@itwin/core-bentley";
import { IpcListener, IpcSocketFrontend } from "@itwin/core-common";
import { IpcApp, NativeApp, NativeAppOpts } from "@itwin/core-frontend";
import type { IpcRenderer } from "electron";
import { DialogModuleMethod, electronIpcStrings } from "../common/ElectronIpcInterface";
import { ElectronRpcManager } from "../common/ElectronRpcManager";
import type { ITwinElectronApi } from "../common/ITwinElectronApi";
declare global {
interface Window {
itwinjs: ITwinElectronApi;
}
}
/**
* Frontend Ipc support for Electron apps.
*/
class ElectronIpc implements IpcSocketFrontend {
private _api: ITwinElectronApi | IpcRenderer;
public addListener(channelName: string, listener: IpcListener) {
this._api.addListener(channelName, listener);
return () => this._api.removeListener(channelName, listener);
}
public removeListener(channelName: string, listener: IpcListener) {
this._api.removeListener(channelName, listener);
}
public send(channel: string, ...data: any[]) {
this._api.send(channel, ...data);
}
public async invoke(channel: string, ...args: any[]) {
return this._api.invoke(channel, ...args);
}
constructor() {
// use the methods on window.itwinjs exposed by ElectronPreload.ts, or ipcRenderer directly if running with nodeIntegration=true (**only** for tests).
// Note that `require("electron")` doesn't work with nodeIntegration=false - that's what it stops
// eslint-disable-next-line @typescript-eslint/no-var-requires
this._api = window.itwinjs ?? require("electron").ipcRenderer;
}
}
/** @beta */
export type ElectronAppOpts = NativeAppOpts;
/**
* Frontend of an Electron App.
* @beta
*/
export class ElectronApp {
private static _ipc?: ElectronIpc;
public static get isValid(): boolean { return undefined !== this._ipc; }
/**
* Start the frontend of an Electron application.
* @param opts Options for your ElectronApp
* @note This method must only be called from the frontend of an Electron app (i.e. when [ProcessDetector.isElectronAppFrontend]($bentley) is `true`).
*/
public static async startup(opts?: ElectronAppOpts) {
if (!ProcessDetector.isElectronAppFrontend)
throw new Error("Not running under Electron");
if (!this.isValid) {
this._ipc = new ElectronIpc();
ElectronRpcManager.initializeFrontend(this._ipc, opts?.iModelApp?.rpcInterfaces); // eslint-disable-line deprecation/deprecation
}
await NativeApp.startup(this._ipc!, opts);
}
public static async shutdown() {
this._ipc = undefined;
await NativeApp.shutdown();
ElectronRpcManager.terminateFrontend();
}
/**
* Call an asynchronous method in the [Electron.Dialog](https://www.electronjs.org/docs/api/dialog) interface from a previously initialized ElectronFrontend.
* @param methodName the name of the method to call
* @param args arguments to method
* @deprecated in 3.x. use [[dialogIpc]]
*/
public static async callDialog<T extends DialogModuleMethod>(methodName: T, ...args: Parameters<Electron.Dialog[T]>) {
return IpcApp.callIpcChannel(electronIpcStrings.dialogChannel, "callDialog", methodName, ...args) as PromiseReturnType<Electron.Dialog[T]>;
}
/** Proxy object for calling methods of `Electron.Dialog` */
public static dialogIpc = IpcApp.makeIpcFunctionProxy<Electron.Dialog>(electronIpcStrings.dialogChannel, "callDialog");
}