|
1 | 1 | import { ipcMain } from 'electron';
|
| 2 | +import type { AppAPI } from '../preload'; |
2 | 3 | import { createLogger } from './logger';
|
3 | 4 |
|
4 | 5 | const logger = createLogger('ipc');
|
5 | 6 |
|
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'; |
13 | 19 | };
|
14 | 20 |
|
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 | + }); |
19 | 61 | });
|
20 | 62 | };
|
0 commit comments