Skip to content

Commit

Permalink
IPC周りの呼び出しをdot記法で書けるように (#2240)
Browse files Browse the repository at this point in the history
* 🧑‍💻 IPC周りの呼び出しをdot記法で書けるように

* 🚧 受信側をオブジェクト形式で書くように変更

* 🏷️ invokeからhandleへ飛べなくなっていたので修正

* 🔥 IpcIHDataから不要な型を削除

* 🏷️ ipcRendererInvoke、ipcMainSendの型を少し修正

* 🩹 置換忘れ

* 🧑‍💻 指摘のあった箇所を修正

* Apply suggestions from code review

---------

Co-authored-by: Hiroshiba <hihokaruta@gmail.com>
  • Loading branch information
MT224244 and Hiroshiba committed Aug 29, 2024
1 parent e49a2fc commit b9ea2fa
Show file tree
Hide file tree
Showing 7 changed files with 500 additions and 499 deletions.
41 changes: 27 additions & 14 deletions src/backend/browser/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,32 @@ import {
// TODO: base pathを設定できるようにするか、ビルド時埋め込みにする
const toStaticPath = (fileName: string) => `/${fileName}`;

// FIXME: asを使わないようオーバーロードにした。オーバーロードも使わない書き方にしたい。
function onReceivedIPCMsg<
T extends {
[K in keyof IpcSOData]: (
event: unknown,
...args: IpcSOData[K]["args"]
) => Promise<IpcSOData[K]["return"]> | IpcSOData[K]["return"];
},
>(listeners: T): void;
function onReceivedIPCMsg(listeners: {
[key: string]: (event: unknown, ...args: unknown[]) => unknown;
}) {
// NOTE: もしブラウザ本体からレンダラへのメッセージを実装するならこんな感じ
window.addEventListener(
"message",
({
data,
}: MessageEvent<{
channel: keyof IpcSOData;
args: IpcSOData[keyof IpcSOData]["args"];
}>) => {
listeners[data.channel]?.({}, ...data.args);
},
);
}

/**
* Browser版のSandBox実装
* src/type/preload.tsのSandboxを変更した場合は、interfaceに追従した変更が必要
Expand Down Expand Up @@ -204,20 +230,7 @@ export const api: Sandbox = {
// NOTE: UIの表示状態の制御のためだけなので固定値を返している
return Promise.resolve(true);
},
onReceivedIPCMsg<T extends keyof IpcSOData>(
channel: T,
listener: (_: unknown, ...args: IpcSOData[T]["args"]) => void,
) {
window.addEventListener("message", (event) => {
const data = event.data as {
channel: keyof IpcSOData;
args: IpcSOData[keyof IpcSOData];
};
if (data.channel == channel) {
listener(data.args);
}
});
},
onReceivedIPCMsg,
closeWindow() {
throw new Error(`Not supported on Browser version: closeWindow`);
},
Expand Down
92 changes: 58 additions & 34 deletions src/backend/electron/ipc.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,69 @@
import { ipcMain, IpcMainInvokeEvent, BrowserWindow } from "electron";
import {
BrowserWindow,
ipcMain,
IpcMainInvokeEvent,
IpcRendererEvent,
} from "electron";
import log from "electron-log/main";
import { IpcIHData, IpcSOData } from "@/type/ipc";

export function ipcMainHandle<T extends keyof IpcIHData>(
channel: T,
listener: (
event: IpcMainInvokeEvent,
...args: IpcIHData[T]["args"]
) => IpcIHData[T]["return"] | Promise<IpcIHData[T]["return"]>,
): void;
export function ipcMainHandle(
channel: string,
listener: (event: IpcMainInvokeEvent, ...args: unknown[]) => unknown,
): void {
const errorHandledListener = (
export type IpcRendererInvoke = {
[K in keyof IpcIHData]: (
...args: IpcIHData[K]["args"]
) => Promise<IpcIHData[K]["return"]>;
};

export type IpcMainHandle = {
[K in keyof IpcIHData]: (
event: IpcMainInvokeEvent,
...args: unknown[]
) => {
try {
validateIpcSender(event);
return listener(event, ...args);
} catch (e) {
log.error(e);
}
};
ipcMain.handle(channel, errorHandledListener);
}
...args: IpcIHData[K]["args"]
) => Promise<IpcIHData[K]["return"]> | IpcIHData[K]["return"];
};

export function ipcMainSend<T extends keyof IpcSOData>(
win: BrowserWindow,
channel: T,
...args: IpcSOData[T]["args"]
export type IpcMainSend = {
[K in keyof IpcSOData]: (
win: BrowserWindow,
...args: IpcSOData[K]["args"]
) => void;
};

export type IpcRendererOn = {
[K in keyof IpcSOData]: (
event: IpcRendererEvent,
...args: IpcSOData[K]["args"]
) => Promise<IpcSOData[K]["return"]> | IpcSOData[K]["return"];
};

// FIXME: asを使わないようオーバーロードにした。オーバーロードも使わない書き方にしたい。
export function registerIpcMainHandle<T extends IpcMainHandle>(
listeners: T,
): void;
export function ipcMainSend(
win: BrowserWindow,
channel: string,
...args: unknown[]
): void {
win.webContents.send(channel, ...args);
export function registerIpcMainHandle(listeners: {
[key: string]: (event: IpcMainInvokeEvent, ...args: unknown[]) => unknown;
}) {
Object.entries(listeners).forEach(([channel, listener]) => {
const errorHandledListener: typeof listener = (event, ...args) => {
try {
validateIpcSender(event);
return listener(event, ...args);
} catch (e) {
log.error(e);
}
};
ipcMain.handle(channel, errorHandledListener);
});
}

export const ipcMainSendProxy = new Proxy(
{},
{
get:
(_, channel: string) =>
(win: BrowserWindow, ...args: unknown[]) =>
win.webContents.send(channel, ...args),
},
) as IpcMainSend;

/** IPCメッセージの送信元を確認する */
const validateIpcSender = (event: IpcMainInvokeEvent) => {
let isValid: boolean;
Expand Down
Loading

0 comments on commit b9ea2fa

Please sign in to comment.