mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 20:38:52 +00:00
Co-authored-by: Hongtao Lye <codert.sn@gmail.com> Co-authored-by: liuyi <forehalo@gmail.com> Co-authored-by: LongYinan <lynweklm@gmail.com> Co-authored-by: X1a0t <405028157@qq.com> Co-authored-by: JimmFly <yangjinfei001@gmail.com> Co-authored-by: Peng Xiao <pengxiao@outlook.com> Co-authored-by: xiaodong zuo <53252747+zuoxiaodong0815@users.noreply.github.com> Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com> Co-authored-by: Qi <474021214@qq.com> Co-authored-by: danielchim <kahungchim@gmail.com>
118 lines
3.0 KiB
TypeScript
118 lines
3.0 KiB
TypeScript
import path from 'node:path';
|
|
|
|
import type {
|
|
HelperToMain,
|
|
MainToHelper,
|
|
} from '@toeverything/infra/preload/electron';
|
|
import { type _AsyncVersionOf, AsyncCall } from 'async-call-rpc';
|
|
import {
|
|
app,
|
|
dialog,
|
|
MessageChannelMain,
|
|
shell,
|
|
type UtilityProcess,
|
|
utilityProcess,
|
|
type WebContents,
|
|
} from 'electron';
|
|
|
|
import { MessageEventChannel } from '../shared/utils';
|
|
import { logger } from './logger';
|
|
|
|
const HELPER_PROCESS_PATH = path.join(__dirname, './helper.js');
|
|
|
|
function pickAndBind<T extends object, U extends keyof T>(
|
|
obj: T,
|
|
keys: U[]
|
|
): { [K in U]: T[K] } {
|
|
return keys.reduce((acc, key) => {
|
|
const prop = obj[key];
|
|
acc[key] = typeof prop === 'function' ? prop.bind(obj) : prop;
|
|
return acc;
|
|
}, {} as any);
|
|
}
|
|
|
|
class HelperProcessManager {
|
|
ready: Promise<void>;
|
|
#process: UtilityProcess;
|
|
|
|
// a rpc server for the main process -> helper process
|
|
rpc?: _AsyncVersionOf<HelperToMain>;
|
|
|
|
static _instance: HelperProcessManager | null = null;
|
|
|
|
static get instance() {
|
|
if (!this._instance) {
|
|
this._instance = new HelperProcessManager();
|
|
}
|
|
return this._instance;
|
|
}
|
|
|
|
private constructor() {
|
|
const helperProcess = utilityProcess.fork(HELPER_PROCESS_PATH);
|
|
this.#process = helperProcess;
|
|
this.ready = new Promise((resolve, reject) => {
|
|
helperProcess.once('spawn', () => {
|
|
try {
|
|
this.#connectMain();
|
|
resolve();
|
|
} catch (err) {
|
|
logger.error('[helper] connectMain error', err);
|
|
reject(err);
|
|
}
|
|
});
|
|
});
|
|
|
|
app.on('before-quit', () => {
|
|
this.#process.kill();
|
|
});
|
|
}
|
|
|
|
// bridge renderer <-> helper process
|
|
connectRenderer(renderer: WebContents) {
|
|
// connect to the helper process
|
|
const { port1: helperPort, port2: rendererPort } = new MessageChannelMain();
|
|
this.#process.postMessage({ channel: 'renderer-connect' }, [helperPort]);
|
|
renderer.postMessage('helper-connection', null, [rendererPort]);
|
|
|
|
return () => {
|
|
helperPort.close();
|
|
rendererPort.close();
|
|
};
|
|
}
|
|
|
|
// bridge main <-> helper process
|
|
// also set up the RPC to the helper process
|
|
#connectMain() {
|
|
const dialogMethods = pickAndBind(dialog, [
|
|
'showOpenDialog',
|
|
'showSaveDialog',
|
|
]);
|
|
const shellMethods = pickAndBind(shell, [
|
|
'openExternal',
|
|
'showItemInFolder',
|
|
]);
|
|
const appMethods = pickAndBind(app, ['getPath']);
|
|
|
|
const mainToHelperServer: MainToHelper = {
|
|
...dialogMethods,
|
|
...shellMethods,
|
|
...appMethods,
|
|
};
|
|
|
|
this.rpc = AsyncCall<HelperToMain>(mainToHelperServer, {
|
|
strict: {
|
|
// the channel is shared for other purposes as well so that we do not want to
|
|
// restrict to only JSONRPC messages
|
|
unknownMessage: false,
|
|
},
|
|
channel: new MessageEventChannel(this.#process),
|
|
});
|
|
}
|
|
}
|
|
|
|
export async function ensureHelperProcess() {
|
|
const helperProcessManager = HelperProcessManager.instance;
|
|
await helperProcessManager.ready;
|
|
return helperProcessManager;
|
|
}
|