mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
ipc-refactoring, fixes
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
763189b27d
commit
4543d8e178
@ -265,7 +265,7 @@
|
|||||||
"css-element-queries": "^1.2.3",
|
"css-element-queries": "^1.2.3",
|
||||||
"css-loader": "^3.5.3",
|
"css-loader": "^3.5.3",
|
||||||
"dompurify": "^2.0.11",
|
"dompurify": "^2.0.11",
|
||||||
"electron": "^9.1.0",
|
"electron": "^9.1.2",
|
||||||
"electron-builder": "^22.7.0",
|
"electron-builder": "^22.7.0",
|
||||||
"electron-notarize": "^0.3.0",
|
"electron-notarize": "^0.3.0",
|
||||||
"electron-rebuild": "^1.11.0",
|
"electron-rebuild": "^1.11.0",
|
||||||
|
|||||||
@ -2,20 +2,118 @@
|
|||||||
// https://www.electronjs.org/docs/api/ipc-main
|
// https://www.electronjs.org/docs/api/ipc-main
|
||||||
// https://www.electronjs.org/docs/api/ipc-renderer
|
// https://www.electronjs.org/docs/api/ipc-renderer
|
||||||
|
|
||||||
import { ipcMain, ipcRenderer, WebContents, webContents } from "electron"
|
import { ipcMain, ipcRenderer, IpcRendererEvent, WebContents, webContents } from "electron"
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
|
import { getRandId } from "./utils";
|
||||||
|
|
||||||
export type IpcChannel = string;
|
export type IpcChannel = string;
|
||||||
|
|
||||||
export interface IpcHandleOpts {
|
export enum IpcMode {
|
||||||
timeout?: number;
|
SYNC = "sync",
|
||||||
|
ASYNC = "async",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IpcMessageHandler<T extends any[] = any> {
|
export interface IpcChannelRequest<A extends any[] = any> {
|
||||||
(...args: T): any;
|
msgId: string;
|
||||||
|
args: A;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IpcMessageOpts<A extends any[] = any> {
|
export interface IpcChannelResponse<T extends any[] = any, E = any> {
|
||||||
|
msgId: string;
|
||||||
|
data?: T;
|
||||||
|
error?: E;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IpcChannelInit {
|
||||||
|
channel: IpcChannel; // main <-> renderer communication channel name
|
||||||
|
mode?: IpcMode; // default: "async", use "sync" as last resort: https://www.electronjs.org/docs/api/ipc-renderer#ipcrenderersendsyncchannel-args
|
||||||
|
handle?: (...args: any[]) => any; // main-process message handler
|
||||||
|
autoBind?: boolean; // auto-bind message handler in main-process, default: false
|
||||||
|
timeout?: number; // timeout for waiting response from the sender
|
||||||
|
once?: boolean; // todo: add support
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createIpcChannel({ autoBind = false, mode = IpcMode.ASYNC, timeout = 0, handle, channel }: IpcChannelInit) {
|
||||||
|
channel = `${mode}:${channel}`
|
||||||
|
|
||||||
|
const ipcChannel = {
|
||||||
|
channel: channel,
|
||||||
|
handleInMain: () => {
|
||||||
|
logger.info(`[IPC]: setup channel "${channel}"`);
|
||||||
|
|
||||||
|
ipcMain.on(channel, async (event, req: IpcChannelRequest) => {
|
||||||
|
let resolved = false;
|
||||||
|
let timerId: any;
|
||||||
|
|
||||||
|
function resolve(res: Partial<IpcChannelResponse>) {
|
||||||
|
if (resolved) return;
|
||||||
|
res.msgId = req.msgId; // return back to sender to be able to handle response
|
||||||
|
resolved = true
|
||||||
|
logger.info(`[IPC]: sending response to "${channel}"`, res);
|
||||||
|
if (mode === IpcMode.ASYNC) {
|
||||||
|
event.reply(channel, res);
|
||||||
|
}
|
||||||
|
if (mode === IpcMode.SYNC) {
|
||||||
|
event.returnValue = res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout > 0) {
|
||||||
|
timerId = setTimeout(() => {
|
||||||
|
const timeoutError = new Error(`[IPC]: response timeout in ${timeout}ms`);
|
||||||
|
resolve({ error: timeoutError })
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await handle(...req.args); // todo: maybe exec in separate thread/worker
|
||||||
|
resolve({ data })
|
||||||
|
} catch (error) {
|
||||||
|
resolve({
|
||||||
|
error: String(error)
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
clearTimeout(timerId);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
invokeFromRenderer: async (...args: any[]) => {
|
||||||
|
const req: IpcChannelRequest = {
|
||||||
|
msgId: getRandId({ prefix: "ipc-msg-id" }),
|
||||||
|
args: args,
|
||||||
|
}
|
||||||
|
logger.info(`[IPC]: "${channel}" sending message to main`, req);
|
||||||
|
if (mode === IpcMode.ASYNC) {
|
||||||
|
ipcRenderer.send(channel, req)
|
||||||
|
}
|
||||||
|
if (mode === IpcMode.SYNC) {
|
||||||
|
ipcRenderer.sendSync(channel, req)
|
||||||
|
}
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
ipcRenderer.on(channel, function waitResponseHandler(event: IpcRendererEvent, res: IpcChannelResponse) {
|
||||||
|
if (req.msgId === res.msgId) {
|
||||||
|
const meta = { ...req, ...res };
|
||||||
|
if (res.data) {
|
||||||
|
logger.info(`[IPC]: "${channel}" resolve`, meta);
|
||||||
|
resolve(res.data);
|
||||||
|
}
|
||||||
|
if (res.error) {
|
||||||
|
logger.error(`[IPC]: "${channel}" reject`, meta);
|
||||||
|
reject(res.error);
|
||||||
|
}
|
||||||
|
ipcRenderer.off(channel, waitResponseHandler); // unsubscribe since handled
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if (autoBind && ipcMain) {
|
||||||
|
ipcChannel.handleInMain();
|
||||||
|
}
|
||||||
|
return ipcChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IpcBroadcastParams<A extends any[] = any> {
|
||||||
channel: IpcChannel
|
channel: IpcChannel
|
||||||
webContentId?: number; // sends to single webContents view
|
webContentId?: number; // sends to single webContents view
|
||||||
filter?: (webContent: WebContents) => boolean
|
filter?: (webContent: WebContents) => boolean
|
||||||
@ -23,7 +121,7 @@ export interface IpcMessageOpts<A extends any[] = any> {
|
|||||||
args?: A;
|
args?: A;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function broadcastIpc({ channel, webContentId, filter, args = [] }: IpcMessageOpts) {
|
export function broadcastIpc({ channel, webContentId, filter, args = [] }: IpcBroadcastParams) {
|
||||||
const singleView = webContentId ? webContents.fromId(webContentId) : null;
|
const singleView = webContentId ? webContents.fromId(webContentId) : null;
|
||||||
let views = singleView ? [singleView] : webContents.getAllWebContents();
|
let views = singleView ? [singleView] : webContents.getAllWebContents();
|
||||||
if (filter) {
|
if (filter) {
|
||||||
@ -35,61 +133,3 @@ export function broadcastIpc({ channel, webContentId, filter, args = [] }: IpcMe
|
|||||||
webContent.send(channel, ...[args].flat());
|
webContent.send(channel, ...[args].flat());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: support timeout + merge with sendMessage?
|
|
||||||
export async function invokeIpc<R = any>(channel: IpcChannel, ...args: any[]): Promise<R> {
|
|
||||||
logger.info(`[IPC]: invoke channel "${channel}"`, { args });
|
|
||||||
return ipcRenderer.invoke(channel, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: make isomorphic api
|
|
||||||
export function handleIpc(channel: IpcChannel, handler: IpcMessageHandler, options: IpcHandleOpts = {}) {
|
|
||||||
const { timeout = 0 } = options;
|
|
||||||
logger.info(`[IPC]: setup to handle "${channel}"`);
|
|
||||||
|
|
||||||
ipcMain.handle(channel, async (event, ...args) => {
|
|
||||||
logger.info(`[IPC]: handle "${channel}"`, { args });
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
let timerId;
|
|
||||||
if (timeout) {
|
|
||||||
timerId = setTimeout(() => {
|
|
||||||
const timeoutError = new Error("[IPC]: response timeout");
|
|
||||||
reject(timeoutError);
|
|
||||||
}, timeout);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const result = await handler(...args); // todo: maybe exec in separate thread/worker
|
|
||||||
resolve(result);
|
|
||||||
clearTimeout(timerId);
|
|
||||||
} catch (err) {
|
|
||||||
reject(err);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IpcPairOptions {
|
|
||||||
channel: IpcChannel
|
|
||||||
handle?: IpcMessageHandler
|
|
||||||
autoBind?: boolean;
|
|
||||||
timeout?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: improve api
|
|
||||||
export function createIpcChannel({ channel, autoBind, ...initOpts }: IpcPairOptions) {
|
|
||||||
const bindHandler = (opts: { handler?: IpcMessageHandler, options?: IpcHandleOpts } = {}) => {
|
|
||||||
const handler = opts.handler || initOpts.handle || Function;
|
|
||||||
const options = opts.options || { timeout: initOpts.timeout };
|
|
||||||
handleIpc(channel, handler, options);
|
|
||||||
};
|
|
||||||
if (autoBind) {
|
|
||||||
bindHandler();
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
channel: channel,
|
|
||||||
handleInMain: bindHandler,
|
|
||||||
invokeFromRenderer(...args: any[]) {
|
|
||||||
return invokeIpc(channel, ...args);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -52,7 +52,7 @@ export class ClusterStatus extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async refreshClusterState() {
|
async refreshClusterState() {
|
||||||
return clusterIpc.activate.invokeFromRenderer(this.clusterId);
|
await clusterIpc.activate.invokeFromRenderer(this.clusterId);
|
||||||
}
|
}
|
||||||
|
|
||||||
reconnect = async () => {
|
reconnect = async () => {
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export class ClusterView extends React.Component {
|
|||||||
view.setAttribute("enableremotemodule", "true")
|
view.setAttribute("enableremotemodule", "true")
|
||||||
view.addEventListener("did-finish-load", () => {
|
view.addEventListener("did-finish-load", () => {
|
||||||
console.log('CLUSTER VIEW READY!', cluster)
|
console.log('CLUSTER VIEW READY!', cluster)
|
||||||
view.openDevTools()
|
// view.openDevTools()
|
||||||
});
|
});
|
||||||
view.addEventListener("did-fail-load", event => {
|
view.addEventListener("did-fail-load", event => {
|
||||||
// todo: handle
|
// todo: handle
|
||||||
|
|||||||
@ -4647,10 +4647,10 @@ electron@*:
|
|||||||
"@types/node" "^12.0.12"
|
"@types/node" "^12.0.12"
|
||||||
extract-zip "^1.0.3"
|
extract-zip "^1.0.3"
|
||||||
|
|
||||||
electron@^9.1.0:
|
electron@^9.1.2:
|
||||||
version "9.1.0"
|
version "9.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/electron/-/electron-9.1.0.tgz#ca77600c9e4cd591298c340e013384114d3d8d05"
|
resolved "https://registry.yarnpkg.com/electron/-/electron-9.1.2.tgz#bfa26d6b192ea13abb6f1461371fd731a8358988"
|
||||||
integrity sha512-VRAF8KX1m0py9I9sf0kw1kWfeC87mlscfFcbcRdLBsNJ44/GrJhi3+E8rKbpHUeZNQxsPaVA5Zu5Lxb6dV/scQ==
|
integrity sha512-xEYadr3XqIqJ4ktBPo0lhzPdovv4jLCpiUUGc2M1frUhFhwqXokwhPaTUcE+zfu5+uf/ONDnQApwjzznBsRrgQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@electron/get" "^1.0.1"
|
"@electron/get" "^1.0.1"
|
||||||
"@types/node" "^12.0.12"
|
"@types/node" "^12.0.12"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user