Skip to content

Commit

Permalink
Refactor RendererBridge (#94)
Browse files Browse the repository at this point in the history
* Fix merge conflicts

* Some styling fixes
  • Loading branch information
ewc340 authored Apr 29, 2021
1 parent 61a9ed7 commit b828c65
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 21 deletions.
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

0 comments on commit b828c65

Please sign in to comment.