Skip to content

Commit 4343e51

Browse files
committed
feat: stub out ipc handlers
1 parent 704ff5b commit 4343e51

File tree

4 files changed

+77
-17
lines changed

4 files changed

+77
-17
lines changed

electron/main/ipc.ts

+53-11
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,62 @@
11
import { ipcMain } from 'electron';
2+
import type { AppAPI } from '../preload';
23
import { createLogger } from './logger';
34

45
const logger = createLogger('ipc');
56

6-
export const registerIpcHandlers = (): void => {
7-
// TODO rather than register unique ipcMain.handle('word') handlers,
8-
// use a design pattern where we have one ipcMain handler that
9-
// receives a standard payload.
10-
// In here, we then route the payload to the appropriate handler.
11-
// I saw this pattern on the webs somewhere.
12-
registerPingHandler();
7+
interface IpcInvokeHandler<K extends keyof AppAPI> {
8+
(params: Parameters<AppAPI[K]>): ReturnType<AppAPI[K]>;
9+
}
10+
11+
type IpcHandlerRegistry = {
12+
[channel in keyof AppAPI]: IpcInvokeHandler<channel>;
13+
};
14+
15+
const pingIpcInvokeHandler: IpcInvokeHandler<
16+
'ping'
17+
> = async (): Promise<string> => {
18+
return 'pong';
1319
};
1420

15-
const registerPingHandler = (): void => {
16-
ipcMain.handle('ping', async (): Promise<string> => {
17-
logger.info('ping');
18-
return 'pong';
21+
const speakIpcInvokeHandler: IpcInvokeHandler<'speak'> = async (
22+
params
23+
): Promise<void> => {
24+
const [text] = params;
25+
`Hello, ${text}`;
26+
};
27+
28+
const climbIpcInvokeHandler: IpcInvokeHandler<'climb'> = async (
29+
params
30+
): Promise<number> => {
31+
const [data] = params;
32+
return data.height * 2;
33+
};
34+
35+
const ipcHandlerRegistry: IpcHandlerRegistry = {
36+
ping: pingIpcInvokeHandler,
37+
speak: speakIpcInvokeHandler,
38+
climb: climbIpcInvokeHandler,
39+
};
40+
41+
export const registerIpcHandlers = (): void => {
42+
Object.keys(ipcHandlerRegistry).forEach((channel) => {
43+
const handler = ipcHandlerRegistry[channel as keyof AppAPI];
44+
45+
if (!handler) {
46+
logger.error('no handler registered for channel', { channel });
47+
throw new Error(`[IPC:CHANNEL:INVALID] ${channel}`);
48+
}
49+
50+
ipcMain.handle(channel, async (_event, ...params) => {
51+
try {
52+
logger.debug('handling channel request', { channel, params });
53+
const result = await handler(params as any);
54+
logger.debug('handled channel request', { channel, result });
55+
return result;
56+
} catch (error) {
57+
logger.error('error handling channel request', { channel, error });
58+
throw new Error(`[IPC:CHANNEL:ERROR] ${channel}: ${error?.message}`);
59+
}
60+
});
1961
});
2062
};

electron/preload/index.d.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@
33
*/
44
declare const appAPI: {
55
ping: () => Promise<string>;
6+
speak: (text: string) => Promise<void>;
7+
climb: (data: { height: number }) => Promise<number>;
68
};
79
declare global {
8-
type AppAPI = typeof appAPI;
10+
type TypeOfAppAPI = typeof appAPI;
11+
type AppAPI = {
12+
[K in keyof TypeOfAppAPI]: TypeOfAppAPI[K];
13+
};
914
interface Window {
1015
api: AppAPI;
1116
}

electron/preload/index.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,26 @@ import { contextBridge, ipcRenderer } from 'electron';
44
* The index.d.ts file is auto-generated by the build process.
55
*/
66

7-
// Custom APIs for renderer
7+
// Custom APIs for renderer.
8+
// Proxies request to the main process then returns any response.
89
const appAPI = {
910
ping: async (): Promise<string> => {
10-
// Proxies request to the main process then returns any response
1111
return ipcRenderer.invoke('ping');
1212
},
13+
speak: async (text: string): Promise<void> => {
14+
return ipcRenderer.invoke('speak', text);
15+
},
16+
climb: async (data: { height: number }): Promise<number> => {
17+
return ipcRenderer.invoke('climb', data);
18+
},
1319
};
1420

1521
declare global {
16-
type AppAPI = typeof appAPI;
22+
type TypeOfAppAPI = typeof appAPI;
23+
24+
type AppAPI = {
25+
[K in keyof TypeOfAppAPI]: TypeOfAppAPI[K];
26+
};
1727

1828
interface Window {
1929
api: AppAPI;

electron/renderer/pages/home.tsx

+5-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ const HomePage: React.FC = (): ReactNode => {
88
const { logger } = useLogger('page:grid');
99
useEffect(() => {
1010
runInBackground(async () => {
11-
const response = await window.api.ping();
12-
logger.info(response); // pong
11+
logger.info('>> ping', { response: await window.api.ping() });
12+
logger.info('>> speak', { response: await window.api.speak('electron') });
13+
logger.info('>> climb', {
14+
response: await window.api.climb({ height: 2 }),
15+
});
1316
});
1417
}, []);
1518
// --

0 commit comments

Comments
 (0)