Skip to content

Commit

Permalink
Clean up Electron API and remove exposed ipcRenderer invoke and on
Browse files Browse the repository at this point in the history
  • Loading branch information
albireox committed Aug 12, 2022
1 parent 04e6500 commit ea0307b
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 83 deletions.
53 changes: 27 additions & 26 deletions src/main/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,27 @@ export interface TronEventReplyIFace {
export default function loadEvents() {
// Add events to ipcMain

// Main
ipcMain.handle('window-open', async (event, name) => {
// Window handling
ipcMain.handle('window:open', async (event, name) => {
createWindow(name);
});

ipcMain.handle('window-close', async (event, name) => {
ipcMain.handle('window:close', async (event, name) => {
let win = windows.get(name)!;
win.close();
});

ipcMain.handle('window-get-size', async (event, name) => {
ipcMain.handle('window:get-size', async (event, name) => {
let win = windows.get(name)!;
return win.getSize();
});

ipcMain.handle('window:set-size', async (event, name, width, height, animate = false) => {
let win = windows.get(name);
if (win !== undefined) win.setSize(width, height, animate);
});

// Show alerts
ipcMain.handle(
'show-message-box',
async (event, options: MessageBoxOptions) => await dialog.showMessageBox(options)
Expand All @@ -52,49 +58,44 @@ export default function loadEvents() {
async (event, title: string, content: string) => await dialog.showErrorBox(title, content)
);

ipcMain.handle('window-set-size', async (event, name, width, height, animate = false) => {
let win = windows.get(name);
if (win !== undefined) win.setSize(width, height, animate);
});

// Store
ipcMain.handle('get-from-store', async (event, key) => {
ipcMain.handle('store:get', async (event, key) => {
if (Array.isArray(key)) return key.map((k) => store.get(k));
return store.get(key);
});

ipcMain.handle('set-in-store', async (event, key, value) => {
ipcMain.handle('store:set', async (event, key, value) => {
return store.set(key, value);
});

// keytar
ipcMain.handle('get-password', async (event, service, account) => {
// keytar passwords
ipcMain.handle('password:get', async (event, service, account) => {
return await keytar.getPassword(service, account);
});

ipcMain.handle('set-password', async (event, service, account, value) => {
ipcMain.handle('password:set', async (event, service, account, value) => {
return await keytar.setPassword(service, account, value);
});

// Tron
ipcMain.handle('tron-connect', async (event, host: string, port: number) => {
ipcMain.handle('tron:connect', async (event, host: string, port: number) => {
return await tron.connect(host, port);
});

ipcMain.handle('tron-authorise', async (event, credentials) => {
ipcMain.handle('tron:authorise', async (event, credentials) => {
return await tron.authorise(credentials);
});

ipcMain.handle('tron-add-streamer-window', async (event, sendAll = false) => {
ipcMain.handle('tron:add-streamer-window', async (event, sendAll = false) => {
tron.addStreamerWindow(event.sender.id, event.sender, sendAll);
});

ipcMain.handle('tron-remove-streamer-window', async (event) => {
ipcMain.handle('tron:remove-streamer-window', async (event) => {
tron.removeStreamerWindow(event.sender.id);
});

ipcMain.handle(
'tron-send-command',
'tron:send-command',
async (event, commandString: string, raise: boolean = false) => {
let command = await tron.sendCommand(commandString).then();
if (command.status === CommandStatus.Failed && raise) {
Expand All @@ -109,22 +110,22 @@ export default function loadEvents() {
}
);

ipcMain.handle('tron-simulate-data', async (event, sender: string, line: string) =>
ipcMain.handle('tron:simulate-data', async (event, sender: string, line: string) =>
tron.parseData(`.${sender} 666 ${sender} d ${line}`)
);

ipcMain.handle(
'tron-register-model-listener',
'tron:register-model-listener',
async (event, keys: string[], listenOn, refresh = true) => {
tron.model.registerListener(keys, event, listenOn, true, refresh);
}
);

ipcMain.handle('tron-remove-model-listener', async (event, listenOn) => {
tron.model.removeListener(listenOn);
ipcMain.handle('tron:remove-model-listener', async (event, channel) => {
tron.model.removeListener(channel);
});

ipcMain.handle('tron-model-getall', async (event) => {
ipcMain.handle('tron:model-getall', async (event) => {
return tron.model.keywords;
});

Expand All @@ -145,7 +146,7 @@ export default function loadEvents() {
}

if (mainWindow) {
mainWindow.webContents.send('tron-status', tron.status);
mainWindow.webContents.send('tron:status', tron.status);
}
}

Expand All @@ -160,7 +161,7 @@ export default function loadEvents() {

nativeTheme.on('updated', () => reportTheme());

ipcMain.handle('theme-use-dark', () => {
ipcMain.handle('theme:use-dark', () => {
return nativeTheme.shouldUseDarkColors;
});
}
2 changes: 1 addition & 1 deletion src/main/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const template: any[] = [
.then((response) => {
if (response.response === 0) {
BrowserWindow.getAllWindows().forEach((win) => {
win.webContents.send('clear-logs');
win.webContents.send('tron:clear-logs');
tron.clear();
});
}
Expand Down
89 changes: 67 additions & 22 deletions src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,42 @@ import { contextBridge, dialog, ipcRenderer } from 'electron';
import log from 'electron-log';
import { TronEventReplyIFace } from './events';
import store from './store';

// TODO: According to https://bit.ly/38aeKXB, we should not expose the ipcRenderer
// directly. Instead, we should expose the channels from events.ts here. We should also
// not expose the ipcRendered invoke, send, on functions.
import { ConnectionStatus, KeywordMap } from './tron';

export interface IElectronAPI {
log: log.LogFunctions;
invoke(arg0: string, ...arg1: any): Promise<any>;
send(arg0: string, ...arg1: any): void;
on(arg0: string, listener: any): void;
window: {
open(name: string): Promise<void>;
close(name: string): Promise<void>;
getSize(name: string): Promise<number[]>;
setSize(name: string, width: number, height: number, animate?: boolean): Promise<void>;
};
store: {
get(key: string | string[]): Promise<any>;
set(key: string, value: any): Promise<any>;
get_sync(key: string): any;
};
password: {
get(service: string, account: string): Promise<any>;
set(service: string, account: string, value: any): Promise<void>;
};
tron: {
send(commandString: string, raise?: boolean): Promise<TronEventReplyIFace>;
simulateTronData(sender: string, line: string): Promise<void>;
registerModelListener(keywords: string[], channel: string, refresh?: boolean): Promise<void>;
removeModelListener(channel: string): Promise<void>;
subscribe(channel: string, callback: (keywords: KeywordMap) => void): Promise<void>;
addStreamerWindow(sendAll?: boolean): Promise<void>;
removeStreamerWindow(): Promise<void>;
connect(host: string, port: number): Promise<ConnectionStatus>;
authorise(credentials: {
program: string;
user: string;
password: string;
}): Promise<[boolean, string | null]>;
onStatus(callback: (status: ConnectionStatus) => void): Promise<void>;
onModelReceivedReply(callback: (replies: string[]) => void): Promise<void>;
onClearLogs(callback: () => void): Promise<void>;
};
openInBrowser(path: string): void;
openInApplication(command: string): Promise<string>;
Expand All @@ -39,28 +57,55 @@ export interface IElectronAPI {
};
}

const API: IElectronAPI = {
const ElectronAPI: IElectronAPI = {
log: log.functions,
invoke: (channel, ...params) => {
return ipcRenderer.invoke(channel, ...params);
},
send: (channel, ...params) => {
return ipcRenderer.send(channel, ...params);
},
on: (channel, listener) => {
ipcRenderer.removeAllListeners(channel);
ipcRenderer.on(channel, (event, ...args) => listener(...args));
window: {
open: async (name) => ipcRenderer.invoke('window:open', name),
close: async (name) => ipcRenderer.invoke('window:close', name),
getSize: async (name) => ipcRenderer.invoke('window:get-size', name),
setSize: async (name, width, height, animate = false) =>
ipcRenderer.invoke('window:set-size', name, width, height, animate)
},
store: {
get: async (key) => ipcRenderer.invoke('get-from-store', key),
set: async (key, value) => ipcRenderer.invoke('set-in-store', key, value),
get: async (key) => ipcRenderer.invoke('store:get', key),
set: async (key, value) => ipcRenderer.invoke('store:set', key, value),
get_sync: (key) => store.get(key)
},
password: {
get: async (service, account) => ipcRenderer.invoke('password:get', service, account),
set: async (service, account, value) =>
ipcRenderer.invoke('password:set', service, account, value)
},
tron: {
send: async (commandString, raise = false) =>
ipcRenderer.invoke('tron-send-command', commandString, raise),
ipcRenderer.invoke('tron:send-command', commandString, raise),
simulateTronData: async (sender, line) => {
ipcRenderer.invoke('tron-simulate-data', sender, line);
ipcRenderer.invoke('tron:simulate-data', sender, line);
},
registerModelListener: async (...args) =>
ipcRenderer.invoke('tron:register-model-listener', ...args),
removeModelListener: async (channel) =>
ipcRenderer.invoke('tron:remove-model-listener', channel),
subscribe: async (channel, callback) => {
ipcRenderer.removeAllListeners(channel);
ipcRenderer.on(channel, (event, keywords) => callback(keywords));
},
addStreamerWindow: async (sendAll = false) =>
ipcRenderer.invoke('tron:add-streamer-window', sendAll),
removeStreamerWindow: async () => ipcRenderer.invoke('tron:remove-streamer-window'),
connect: async (host, port) => ipcRenderer.invoke('tron:connect', host, port),
authorise: async (credentials) => ipcRenderer.invoke('tron:authorise', credentials),
onStatus: async (callback) => {
ipcRenderer.removeAllListeners('tron:status');
ipcRenderer.on('tron:status', (event, status) => callback(status));
},
onModelReceivedReply: async (callback) => {
ipcRenderer.removeAllListeners('tron:model-received-reply');
ipcRenderer.on('tron:model-received-reply', (event, replies) => callback(replies));
},
onClearLogs: async (callback) => {
ipcRenderer.removeAllListeners('tron:clear-logs');
ipcRenderer.on('tron:clear-logs', (event) => callback());
}
},
openInBrowser: (path) => {
Expand All @@ -84,4 +129,4 @@ const API: IElectronAPI = {
}
};

contextBridge.exposeInMainWorld('api', API);
contextBridge.exposeInMainWorld('api', ElectronAPI);
2 changes: 1 addition & 1 deletion src/main/tron/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ export default class TronConnection {
this._subscribedWindows.forEach((webContents, id) => {
if (!windowId || windowId === id) {
try {
webContents.send('tron-model-received-reply', reply);
webContents.send('tron:model-received-reply', reply);
} catch {
log.debug('Failed sending message to listener', id);
log.debug('Purging listener', id);
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/components/commandButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ function createCommandObservable(command: string) {
});
}
return new Observable((subscriber) => {
window.api
.invoke('tron-send-command', command)
window.api.tron
.send(command)
.then((reply: TronEventReplyIFace) => {
if (reply.status === CommandStatus.Done) {
subscriber.complete();
Expand Down
19 changes: 7 additions & 12 deletions src/renderer/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,10 @@ export function useKeywords(keys: string[], channel: any = null, refresh: boolea
const channel = params.current.channel;

// Subscribe to model and listen on channel.
window.api.on(channel, updateKeywords);
window.api.invoke(
'tron-register-model-listener',
Array.from(lowerKeys.keys()),
channel,
refresh
);

const unload = () => window.api.invoke('tron-remove-model-listener', channel);
window.api.tron.subscribe(channel, updateKeywords);
window.api.tron.registerModelListener(Array.from(lowerKeys.keys()), channel, refresh);

const unload = () => window.api.tron.removeModelListener(channel);
window.addEventListener('unload', unload);

// Unsubscribe when component unmounts.
Expand Down Expand Up @@ -101,13 +96,13 @@ export function useListener(onReceived: (reply: Reply[]) => any, sendAll = true)
);

useEffect(() => {
window.api.on('tron-model-received-reply', parseReplies);
window.api.tron.onModelReceivedReply(parseReplies);
}, [parseReplies]);

useEffect(() => {
window.api.invoke('tron-add-streamer-window', params.current.sendAll);
window.api.tron.addStreamerWindow(params.current.sendAll);

const unload = () => window.api.invoke('tron-remove-streamer-window');
const unload = () => window.api.tron.removeStreamerWindow();
window.addEventListener('unload', unload);

return () => {
Expand Down
13 changes: 6 additions & 7 deletions src/renderer/views/connect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export default function ConnectView() {
.get(['user.connection.program', 'user.connection.user', 'user.connection.host'])
.then(async (res: any) => {
let program = res[0];
if (program) return [...res, await window.api.invoke('get-password', 'hub', program)];
if (program) return [...res, await window.api.password.get('hub', program)];
return [...res, ''];
})
.then((res: any) => {
Expand All @@ -116,7 +116,7 @@ export default function ConnectView() {
window.api.store.set('user.connection.program', connectForm.program.toLowerCase());
window.api.store.set('user.connection.user', connectForm.user);
if (connectForm.program)
window.api.invoke('set-password', 'hub', connectForm.program, connectForm.password);
window.api.password.set('hub', connectForm.program, connectForm.password);
};

let handleConnect = async (event: SyntheticEvent) => {
Expand All @@ -126,17 +126,16 @@ export default function ConnectView() {

let port = (await window.api.store.get('connection.port')) || 9877;

const connectionResult = await window.api.invoke('tron-connect', connectForm.host, port);
const connectionResult = await window.api.tron.connect(connectForm.host, port);

switch (connectionResult) {
case ConnectionStatus.Connected:
const [result, err]: [boolean, string | null] = await window.api.invoke(
'tron-authorise',
const [result, err]: [boolean, string | null] = await window.api.tron.authorise(
(({ program, user, password }) => ({ program, user, password }))(connectForm)
);
if (result === true) {
storeCredentials();
window.api.invoke('window-close', NAME);
window.api.window.close(NAME);
} else {
setError(err!);
}
Expand All @@ -148,7 +147,7 @@ export default function ConnectView() {
setError('Connection timed out');
break;
default:
window.api.invoke('window-close', NAME);
window.api.window.close(NAME);
break;
}
setButtonDisabled(false);
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/views/log/message.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ const Messages: React.FC<MessagesProps> = ({ onConfigUpdate }) => {
}, [actors, onConfigUpdate]);

React.useEffect(() => {
window.api.on('clear-logs', () => dispatch({ type: 'clear' }));
window.api.tron.onClearLogs(() => dispatch({ type: 'clear' }));
}, []);

React.useEffect(() => {
Expand Down
Loading

0 comments on commit ea0307b

Please sign in to comment.