Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor RendererBridge #94

Merged
merged 2 commits into from
Apr 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions main/MenuTemplate/DebugMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ const DebugMenu: MenuItemConstructorOptions = {
{
label: 'Toggle DevTools',
click() {
if (RendererBridge.registeredWindow) {
RendererBridge.registeredWindow.webContents.toggleDevTools();
}
RendererBridge.toggleWindowDevtools('main');
},
accelerator: 'CommandOrControl+alt+I',
},
Expand Down Expand Up @@ -57,9 +55,7 @@ const DebugMenu: MenuItemConstructorOptions = {
label: 'Reload',
accelerator: 'CommandOrControl+R',
click() {
if (RendererBridge.registeredWindow) {
RendererBridge.registeredWindow.reload();
}
RendererBridge.reloadWindow('main');
},
},

Expand Down
4 changes: 1 addition & 3 deletions main/MenuTemplate/HelpMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ const HelpMenu: MenuItemConstructorOptions = {
{
label: 'Interactive Tutorial',
click() {
if (RendererBridge.registeredWindow) {
RendererBridge.registeredWindow.webContents.send('start-interactive-tour');
}
RendererBridge.dispatch('main', 'start-interactive-tour');
},
accelerator: 'CommandOrControl+T',
},
Expand Down
76 changes: 67 additions & 9 deletions main/RendererBridge.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,81 @@
/**
* RendererBridge connects the main process to the renderer's Redux flow.
* Maintains a real-time copy of the renderer's Redux state in the main process, and
* allows the main process to dispatch redux actions to the renderer.
* RendererBridge connects the main process to children renderer processes and
* allows the main process to dispatch data to the renderer processes.
*/

import _ from 'lodash';
import { BrowserWindow } from "electron";

type SingleOrArray<T> = T | T[];

class RendererBridge {
registeredWindow: BrowserWindow | null = null;
private registeredWindows: Record<string, BrowserWindow | null> = {};

hasRegisteredWindow = (key: string) => this.registeredWindows[key] ?? false;

registerWindow = (electronWindow: BrowserWindow) => {
this.registeredWindow = electronWindow;
/** Registers the window in the RendererBridge */
registerWindow = (key: string, electronWindow: BrowserWindow) => {
this.registeredWindows[key] = electronWindow;
};

reduxDispatch = (action: any) => {
if (this.registeredWindow) {
this.registeredWindow.webContents.send('dispatch', action);
/** Unregisters the window from the RendererBridge */
unregisterWindow = (key: string) => {
delete this.registeredWindows[key];
}

/**
* Reloads the window specified by its key in the RendererBridge
* If key specified doesn't exist in the RendererBridge, then nothing will happen.
*/
reloadWindow = (key: string) => {
const registeredWindow = this.registeredWindows[key];
registeredWindow?.reload();
}

/**
* Toggles the window DevTools for window specified by key
* If key specified doesn't exist in the RendererBridge, then nothing will happen.
*/
toggleWindowDevtools = (key: string) => {
const registeredWindow = this.registeredWindows[key];
registeredWindow?.webContents.toggleDevTools();
}

/**
* From windows specified by `windowKeys`, dispatches `data` to `channel`.
* If `windowKeys` is undefined, will send to all registered windows.
* If `windowKeys` is a `string`, then the data will only be dispatched to that registered window.
*/
dispatch = (windowKeys: SingleOrArray<string> | 'all', channel: string, ...data: any[]) => {
if (windowKeys === 'all') {
windowKeys = Object.keys(this.registeredWindows);
} else if (typeof windowKeys === 'string') {
windowKeys = [windowKeys]
}

try {
for (const key of windowKeys) {
const registeredWindow = this.registeredWindows[key];

// Special case for dispatching Redux since we can only dispatch one action at a time
if (channel === 'reduxDispatch') {
data = data[0];
}

registeredWindow?.webContents.send(channel, data);
}
} catch (e) {
console.log(`[RendererBridge] dispatch caught error:`, e)
}
}

/**
* More particular usage of `dispatch`. Use this method if windows you are sending to
* have a Redux store. The default window for this method will have key `'main'` which will dispatch
* Redux actions to the main window to avoid large refactors from the current usage of this method.
*/
reduxDispatch = (action: any, windowKeys: SingleOrArray<string> | 'all' | 'main' = 'main') => {
this.dispatch(windowKeys, 'reduxDispatch', action);
};
};

Expand Down
2 changes: 1 addition & 1 deletion main/main-process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ app.on('ready', () => {
});

// Binding for the main process to inject into Redux workflow
RendererBridge.registerWindow(mainWindow);
RendererBridge.registerWindow('main', mainWindow);

mainWindow.maximize();
mainWindow.loadURL(`file://${__dirname}/../static/index.html`);
Expand Down
4 changes: 2 additions & 2 deletions renderer/utils/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,10 +340,10 @@ function runtimeReceiver() {
emitter(action);
};
// Suscribe listener to dispatches from main process.
ipcRenderer.on('dispatch', listener);
ipcRenderer.on('reduxDispatch', listener);
// Return an unsuscribe function.
return () => {
ipcRenderer.removeListener('dispatch', listener);
ipcRenderer.removeListener('reduxDispatch', listener);
};
});
}
Expand Down