feat: init window.affine (#2682)

This commit is contained in:
Himself65 2023-06-06 11:43:34 +08:00 committed by GitHub
parent d00d0bd951
commit 8f6db00402
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 155 additions and 20 deletions

View File

@ -1,5 +1,3 @@
/* eslint-disable @typescript-eslint/no-var-requires */
// NOTE: we will generate preload types from this file
import { ipcRenderer } from 'electron';

View File

@ -0,0 +1,72 @@
import { contextBridge, ipcRenderer } from 'electron';
(async () => {
const affineApis = await import('./affine-apis');
contextBridge.exposeInMainWorld('apis', affineApis.apis);
contextBridge.exposeInMainWorld('events', affineApis.events);
contextBridge.exposeInMainWorld('appInfo', affineApis.appInfo);
// Credit to microsoft/vscode
function validateIPC(channel: string) {
if (!channel || !channel.startsWith('affine:')) {
throw new Error(`Unsupported event IPC channel '${channel}'`);
}
return true;
}
const globals = {
ipcRenderer: {
send(channel: string, ...args: any[]) {
if (validateIPC(channel)) {
ipcRenderer.send(channel, ...args);
}
},
invoke(channel: string, ...args: any[]) {
if (validateIPC(channel)) {
return ipcRenderer.invoke(channel, ...args);
}
},
on(
channel: string,
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
) {
if (validateIPC(channel)) {
ipcRenderer.on(channel, listener);
return this;
}
},
once(
channel: string,
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
) {
if (validateIPC(channel)) {
ipcRenderer.once(channel, listener);
return this;
}
},
removeListener(
channel: string,
listener: (event: Electron.IpcRendererEvent, ...args: any[]) => void
) {
if (validateIPC(channel)) {
ipcRenderer.removeListener(channel, listener);
return this;
}
},
},
};
try {
contextBridge.exposeInMainWorld('affine', globals);
} catch (error) {
console.error(error);
}
})();

View File

@ -1,18 +1 @@
/**
* @module preload
*/
import { contextBridge } from 'electron';
import * as affineApis from './affine-apis';
/**
* The "Main World" is the JavaScript context that your main renderer code runs in.
* By default, the page you load in your renderer executes code in this world.
*
* @see https://www.electronjs.org/docs/api/context-bridge
*/
contextBridge.exposeInMainWorld('apis', affineApis.apis);
contextBridge.exposeInMainWorld('events', affineApis.events);
contextBridge.exposeInMainWorld('appInfo', affineApis.appInfo);
import './bootstrap';

View File

@ -9,3 +9,26 @@ if (config.enablePlugin && !environment.isServer) {
if (!environment.isServer) {
import('@affine/bookmark-block');
}
if (!environment.isDesktop && !environment.isServer) {
// Polyfill Electron
const unimplemented = () => {
throw new Error('AFFiNE Plugin Web will be supported in the future');
};
const affine = {
ipcRenderer: {
invoke: unimplemented,
send: unimplemented,
on: unimplemented,
once: unimplemented,
removeListener: unimplemented,
},
};
Object.freeze(affine);
Object.defineProperty(window, 'affine', {
value: affine,
writable: false,
});
}

View File

@ -0,0 +1,59 @@
import { useCallback, useEffect, useRef } from 'react';
declare global {
interface IPCRenderer {
send(channel: string, ...args: any[]): void;
invoke(channel: string, ...args: any[]): Promise<any>;
on(
channel: string,
listener: (event: unknown, ...args: any[]) => void
): this;
once(
channel: string,
listener: (event: unknown, ...args: any[]) => void
): this;
removeListener(channel: string, listener: (...args: any[]) => void): this;
}
interface Window {
affine: {
ipcRenderer: IPCRenderer;
};
}
}
/**
* Unsafe
*/
export function useAffineAsyncCallback(channel: string) {
return useCallback(
(...args: any[]): Promise<any> => {
return window.affine.ipcRenderer.invoke(channel, ...args);
},
[channel]
);
}
/**
* Unsafe
*/
export function useAffineListener(
channel: string,
listener: (event: unknown, ...args: any[]) => void,
once?: boolean
): void {
const fnRef = useRef<((event: unknown, ...args: any[]) => void) | null>(null);
if (!fnRef.current) {
fnRef.current = listener;
}
useEffect(() => {
if (once) {
window.affine.ipcRenderer.once(channel, fnRef.current!);
} else {
window.affine.ipcRenderer.on(channel, fnRef.current!);
}
return () => {
window.affine.ipcRenderer.removeListener(channel, fnRef.current!);
};
}, [channel, once]);
}