Skip to content

Commit 6c6de83

Browse files
Gabriel-Tiberiu Imre-Lucacihristoterezov
authored andcommitted
feat(alwaysontop): Add visibility monitoring for meeting frame
1 parent 1972c3b commit 6c6de83

File tree

4 files changed

+62
-10
lines changed

4 files changed

+62
-10
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,5 @@ typings/
5757
# dotenv environment variables file
5858
.env
5959

60+
build/
61+
.jshint*

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,17 @@ const {
7373
} = require("jitsi-meet-electron-utils");
7474

7575
const api = new JitsiMeetExternalAPI(...);
76-
setupAlwaysOnTopRender(api);
76+
const alwaysOnTop = setupAlwaysOnTopRender(api);
77+
78+
alwaysOnTop.on('will-close', handleAlwaysOnTopClose);
7779
```
7880

81+
`setupAlwaysOnTopRender` return an instance of EventEmitter with the following events:
82+
83+
* _will-close_ - emitted right before the always on top window is going to close
84+
7985
#### WiFi Stats
80-
Provides a function to query for wifi stats on the host computer. Returns information like interface name, addresses, signal quality, noise (not available on all OS).
86+
Provides a function to query for wifi stats on the host computer. Returns information like interface name, addresses, signal quality, noise (not available on all OS).
8187

8288
**WiFi Stats:**
8389

alwaysontop/constants.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
ALWAYSONTOP_WILL_CLOSE: 'will-close'
3+
};

alwaysontop/render.js

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
/* global __dirname */
22
const { ipcRenderer, remote } = require('electron');
33

4+
const { EventEmitter } = require('events');
45
const os = require('os');
56
const path = require('path');
67
const url = require('url');
78

9+
const { ALWAYSONTOP_WILL_CLOSE } = require('./constants');
10+
811
/**
912
* URL for index.html which will be our entry point.
1013
*/
@@ -43,22 +46,25 @@ function getNumberFromLocalStorage(localStorageKey) {
4346
/**
4447
* Implements the always on top functionality for the render process.
4548
*/
46-
class AlwaysOnTop {
49+
class AlwaysOnTop extends EventEmitter {
4750
/**
4851
* Creates new instance.
4952
*
5053
* @param {JitsiIFrameApi} api - the Jitsi Meet iframe api object.
5154
*/
5255
constructor(api) {
56+
super();
5357
this._updateLargeVideoSrc = this._updateLargeVideoSrc.bind(this);
5458
this._openAlwaysOnTopWindow = this._openAlwaysOnTopWindow.bind(this);
5559
this._closeAlwaysOnTopWindow = this._closeAlwaysOnTopWindow.bind(this);
5660
this._onMessageReceived = this._onMessageReceived.bind(this);
5761
this._onConferenceJoined = this._onConferenceJoined.bind(this);
5862
this._onConferenceLeft = this._onConferenceLeft.bind(this);
63+
this._onIntersection = this._onIntersection.bind(this);
5964

6065
this._api = api;
6166
this._jitsiMeetElectronWindow = remote.getCurrentWindow();
67+
this._intersectionObserver = new IntersectionObserver(this._onIntersection);
6268

6369
if (!api) {
6470
throw new Error('Wrong arguments!');
@@ -158,6 +164,7 @@ class AlwaysOnTop {
158164
this._jitsiMeetElectronWindow.on('blur', this._openAlwaysOnTopWindow);
159165
this._jitsiMeetElectronWindow.on('focus', this._closeAlwaysOnTopWindow);
160166
this._jitsiMeetElectronWindow.on('close', this._closeAlwaysOnTopWindow);
167+
this._intersectionObserver.observe(this._api.getIFrame());
161168
}
162169

163170
/**
@@ -166,6 +173,7 @@ class AlwaysOnTop {
166173
* @returns {void}
167174
*/
168175
_onConferenceLeft() {
176+
this._intersectionObserver.unobserve(this._api.getIFrame());
169177
this._jitsiMeetElectronWindow.removeListener(
170178
'blur',
171179
this._openAlwaysOnTopWindow
@@ -181,6 +189,30 @@ class AlwaysOnTop {
181189
this._closeAlwaysOnTopWindow();
182190
}
183191

192+
/**
193+
* Handles intersection events for the instance's IntersectionObserver
194+
*
195+
* @param {IntersectionObserverEntry[]} entries
196+
* @param {IntersectionObserver} observer
197+
*/
198+
_onIntersection(entries) {
199+
const singleEntry = entries.pop();
200+
this._jitsiMeetElectronWindow.removeListener(
201+
'focus',
202+
this._closeAlwaysOnTopWindow
203+
);
204+
205+
if (singleEntry.isIntersecting) {
206+
this._closeAlwaysOnTopWindow();
207+
this._jitsiMeetElectronWindow.on(
208+
'focus',
209+
this._closeAlwaysOnTopWindow
210+
);
211+
} else {
212+
this._openAlwaysOnTopWindow();
213+
}
214+
}
215+
184216
/**
185217
* Handles IPC messages from the main process.
186218
*
@@ -214,6 +246,7 @@ class AlwaysOnTop {
214246
api: this._api,
215247
onload: this._updateLargeVideoSrc,
216248
onbeforeunload: () => {
249+
this.emit(ALWAYSONTOP_WILL_CLOSE);
217250
this._api.removeListener(
218251
'largeVideoChanged',
219252
this._updateLargeVideoSrc
@@ -243,21 +276,29 @@ class AlwaysOnTop {
243276
* @returns {void}
244277
*/
245278
_closeAlwaysOnTopWindow() {
246-
if (this._alwaysOnTopBrowserWindow) {
247-
const position
248-
= this._alwaysOnTopBrowserWindow.getPosition();
249-
this._alwaysOnTopBrowserWindow = undefined;
279+
if (this._alwaysOnTopBrowserWindow && !this._alwaysOnTopBrowserWindow.isDestroyed()) {
280+
const position =
281+
this._alwaysOnTopBrowserWindow.getPosition();
282+
250283
this._position = {
251284
x: position[0],
252285
y: position[1]
253286
};
254287
}
288+
255289
if(this._alwaysOnTopWindow) {
256-
this._alwaysOnTopWindow.close();
257-
this._alwaysOnTopWindow = undefined;
290+
// we need to check the BrowserWindow reference here because
291+
// window.closed is not reliable due to Electron quirkiness
292+
if(this._alwaysOnTopBrowserWindow &&!this._alwaysOnTopBrowserWindow.isDestroyed()) {
293+
this._alwaysOnTopWindow.close();
294+
}
295+
258296
ipcRenderer.removeListener('jitsi-always-on-top',
259297
this._onMessageReceived);
260298
}
299+
300+
this._alwaysOnTopBrowserWindow = undefined;
301+
this._alwaysOnTopWindow = undefined;
261302
}
262303

263304
/**
@@ -292,5 +333,5 @@ class AlwaysOnTop {
292333
* @param {JitsiIFrameApi} api - the Jitsi Meet iframe api object.
293334
*/
294335
module.exports = function setupAlwaysOnTopRender(api) {
295-
new AlwaysOnTop(api);
336+
return new AlwaysOnTop(api);
296337
};

0 commit comments

Comments
 (0)