-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.ts
141 lines (120 loc) · 4.74 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import { IpcRenderer, IpcMain, BrowserWindow } from 'electron';
import IPCError from './ipc_error';
import IPCResponse from './ipc_response';
const PROCESS_TYPE_MAIN: boolean = process.type === 'browser';
const PROCESS_TYPE_RENDERER: boolean = process.type === 'renderer';
export default class IPeeSee {
private ipc: IpcRenderer | IpcMain;
private window: BrowserWindow;
constructor(ipc: IpcRenderer | IpcMain, window?: BrowserWindow) {
this.ipc = ipc;
this.window = window;
}
/**
* @param {string} channel - A channel we want to send a message to
* @param {*} [data] - Data you want to send
* @param {{ window?: BrowserWindow; timeout?: number; reply?: boolean }} [options]
* @param browserWindow - A window you want to send the message to. If your application has only
* one window, you can pass its reference to the constructor and ignore this argument further on.
* Moreover, this parameter has higher precedence then the argument passed to the constructor, so
* feel safe to use it in conjunction if you maybe have multiple windows but mostly communication
* to only one of them.
* @param timeout - An optional timeout you can specify how long you want to wait for the reply
* before removing the listener
* @reply - If you want to send a message without waiting for reply, set this to false
* @returns {Promise<{}>}
* @memberof IPeeSee
*/
public send(
channel: string,
data?: any,
options?: { window?: BrowserWindow; timeout?: number; reply?: boolean }
): Promise<{}> {
return new Promise((resolve, reject) => {
const opts = options ? options : {};
const window = opts.window || this.window;
const replyChannel = this.__addReplySuffix(channel);
let timeoutId = null;
const shouldReply =
typeof opts.reply !== 'boolean' || (typeof opts.reply === 'boolean' && opts.reply);
if (shouldReply) {
if (opts && opts.timeout) {
const timeoutDuration = opts.timeout * 1000;
timeoutId = setTimeout(() => {
this.__removeAllListeners(replyChannel);
resolve(
new IPCResponse(
408,
`Timed out after ${timeoutDuration * 0.001}s on channel ${replyChannel}.`
)
);
}, timeoutDuration);
}
this.ipc.on(replyChannel, (_event: any, response: { data: {}; error }) => {
clearTimeout(timeoutId);
this.__removeAllListeners(replyChannel);
if (response && Object.prototype.hasOwnProperty.call(response, 'error')) {
reject(new IPCError(response.error));
} else if (!response) {
resolve(
new IPCResponse(
204,
`Got an undefined reply from ${replyChannel}. This means that you are listening for a reply but not returning anything from ${replyChannel}. You should probably remove this listener and send the message one way, without waiting for the reply.`
)
);
} else {
resolve(Object.assign({}, response, new IPCResponse(200)));
}
});
}
if (PROCESS_TYPE_RENDERER) {
const ipc = this.ipc as IpcRenderer;
ipc.send(channel, data);
}
if (PROCESS_TYPE_MAIN && window && window.webContents) {
window.webContents.on('did-finish-load', () => {
window.webContents.send(channel, data);
});
}
});
}
/**
* Adds a listener that replies to a given channel
* @param {string} channel - A channel to listen on
* @param {(data: any) => any} cb - To reply back, it's mandatory to return from this callback
* @returns {() => void}
* @memberof IPeeSee
*/
public reply(channel: string, cb: (data: any) => any): () => void {
const listener = async (event, data) => {
const response = await cb(data);
if (PROCESS_TYPE_MAIN) {
this.replyRenderer(event, channel, response);
}
if (PROCESS_TYPE_RENDERER) {
this.__replyMain(channel, response);
}
};
this.ipc.on(channel, listener);
return () => {
this.ipc.removeListener(channel, listener);
};
}
private replyRenderer(event, channel, data) {
const window = BrowserWindow.fromWebContents(event.sender);
const isWindowsDestroyed = window && window.isDestroyed();
if (!isWindowsDestroyed) {
event.sender.send(this.__addReplySuffix(channel), data);
}
}
private __replyMain(channel: string, data) {
const ipc = this.ipc as IpcRenderer;
ipc.send(this.__addReplySuffix(channel), data);
}
private __addReplySuffix(channelName: string) {
return `${channelName}-reply`;
}
private __removeAllListeners(channelName: string) {
this.ipc.removeAllListeners(channelName);
}
}