/** * Can't use logger in this file: * ERROR in Circular dependency detected: src/services/libs/log/index.ts -> src/services/libs/log/rendererTransport.ts -> src/services/wiki/wikiOperations.ts -> src/services/libs/log/index.ts */ import { ipcMain } from 'electron'; import { WikiChannel } from '@/constants/channels'; import { container } from '@services/container'; import serviceIdentifier from '@services/serviceIdentifier'; import type { IViewService } from '@services/view/interface'; import { WindowNames } from '@services/windows/WindowProperties'; /** * Send to main window renderer (preload script) and not wait for response (fire and forget) */ export function sendToMainWindowNoWait(type: WikiChannel, workspaceID: string, messages: string[]): void { const viewService = container.get(serviceIdentifier.View); const browserView = viewService.getView(workspaceID, WindowNames.main); // second param is always a nonce, but in this case we don't need it browserView?.webContents.send(type, undefined, ...messages); } /** * Send to main window renderer (preload script) and wait for response. * * Will throw error when on Windows and App is at background (WebContentsView will disappear and not accessible.) https://github.com/tiddly-gittly/TidGi-Desktop/issues/398 * * @param type The handler on renderer (preload script) side should implement `ipcRenderer.send(WikiChannel.xxx, nonceReceived, result);`, where `result` is usually `string[]` (the default type for `` in function signature) * @returns undefined if main window webContents is not found */ export async function sendToMainWindowAndAwait(type: WikiChannel, workspaceID: string, messages: string[], options?: { timeout?: number }): Promise { const viewService = container.get(serviceIdentifier.View); const browserView = viewService.getView(workspaceID, WindowNames.main); if ((browserView?.webContents) === undefined) { throw new Error(`browserView.webContents is undefined in sendToMainWindowAndAwait ${workspaceID} when running ${type}`); } return await new Promise((resolve, reject) => { const nonce = Math.random(); browserView.webContents.send(type, nonce, ...messages); let timeoutHandle: NodeJS.Timeout; if (options?.timeout !== undefined) { timeoutHandle = setTimeout(() => { reject(new Error(`${type} for ${workspaceID} in sendToMainWindowAndAwait Timeout after ${String(options.timeout)}ms`)); }, options.timeout); } /** * Use nonce to prevent data racing */ const listener = (_event: Electron.IpcMainEvent, nonceReceived: number, value: T, error?: Error): void => { if (nonce === nonceReceived) { clearTimeout(timeoutHandle); ipcMain.removeListener(type, listener); if (error === undefined) { resolve(value); } else { reject(error); } } }; ipcMain.on(type, listener); }); }