diff --git a/conference.js b/conference.js index dd6016e12..e90754bde 100644 --- a/conference.js +++ b/conference.js @@ -1283,6 +1283,30 @@ export default { // FIXME close }); + room.on(ConferenceEvents.SUSPEND_DETECTED, () => { + // After wake up, we will be in a state where conference is left + // there will be dialog shown to user. + // We do not want video/audio as we show an overlay and after it + // user need to rejoin or close, while waking up we can detect + // camera wakeup as a problem with device. + // We also do not care about device change, which happens + // on resume after suspending PC. + if (this.deviceChangeListener) + JitsiMeetJS.mediaDevices.removeEventListener( + JitsiMeetJS.events.mediaDevices.DEVICE_LIST_CHANGED, + this.deviceChangeListener); + + // stop local video + if (localVideo) + localVideo.dispose(); + // stop local audio + if (localAudio) + localAudio.dispose(); + + // show overlay + APP.UI.showSuspendedOverlay(); + }); + room.on(ConferenceEvents.DTMF_SUPPORT_CHANGED, (isDTMFSupported) => { APP.UI.updateDTMFSupport(isDTMFSupported); }); @@ -1617,11 +1641,12 @@ export default { APP.UI.onAvailableDevicesChanged(devices); }); + this.deviceChangeListener = (devices) => + window.setTimeout( + () => this._onDeviceListChanged(devices), 0); JitsiMeetJS.mediaDevices.addEventListener( JitsiMeetJS.events.mediaDevices.DEVICE_LIST_CHANGED, - (devices) => - window.setTimeout( - () => this._onDeviceListChanged(devices), 0)); + this.deviceChangeListener); } }, /** diff --git a/css/_inlay.scss b/css/_inlay.scss index 5cbd777f3..cc0a8d770 100644 --- a/css/_inlay.scss +++ b/css/_inlay.scss @@ -7,7 +7,7 @@ text-align: center; &__title { - margin: 12px 0; + margin: 17px 0; padding-bottom: 17px; color: $popoverFontColor; font-size: 21px; @@ -26,4 +26,8 @@ margin: 0 10px; font-size: 50px; } + + &__button { + float: none !important; + } } \ No newline at end of file diff --git a/lang/main.json b/lang/main.json index ed791890c..c43795a84 100644 --- a/lang/main.json +++ b/lang/main.json @@ -79,6 +79,10 @@ "policyText": " ", "title": "__app__ needs to use your microphone and camera." }, + "suspendedoverlay": { + "title": "Your video call was interrupted, because this computer went to sleep.", + "rejoinKeyTitle": "Rejoin" + }, "toolbar": { "mute": "Mute / Unmute", "videomute": "Start / Stop camera", diff --git a/modules/UI/UI.js b/modules/UI/UI.js index 925be0de5..9b0208ce9 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -16,6 +16,7 @@ import GumPermissionsOverlay from './gum_overlay/UserMediaPermissionsGuidanceOverlay'; import PageReloadOverlay from './reload_overlay/PageReloadOverlay'; +import SuspendedOverlay from './suspended_overlay/SuspendedOverlay'; import VideoLayout from "./videolayout/VideoLayout"; import FilmStrip from "./videolayout/FilmStrip"; import SettingsMenu from "./side_pannels/settings/SettingsMenu"; @@ -1399,12 +1400,14 @@ UI.hideRingOverLay = function () { /** * Indicates if any the "top" overlays are currently visible. The check includes - * the call overlay, GUM permissions overlay and a page reload overlay. + * the call overlay, suspended overlay, GUM permissions overlay + * and a page reload overlay. * * @returns {*|boolean} {true} if the overlay is visible, {false} otherwise */ UI.isOverlayVisible = function () { return RingOverlay.isVisible() + || SuspendedOverlay.isVisible() || PageReloadOverlay.isVisible() || GumPermissionsOverlay.isVisible(); }; @@ -1427,6 +1430,13 @@ UI.showUserMediaPermissionsGuidanceOverlay = function (browser) { GumPermissionsOverlay.show(browser); }; +/** + * Shows suspended overlay with a button to rejoin conference. + */ +UI.showSuspendedOverlay = function () { + SuspendedOverlay.show(); +}; + /** * Hides browser-specific overlay with guidance how to proceed with gUM prompt. */ diff --git a/modules/UI/suspended_overlay/SuspendedOverlay.js b/modules/UI/suspended_overlay/SuspendedOverlay.js new file mode 100644 index 000000000..063d771ad --- /dev/null +++ b/modules/UI/suspended_overlay/SuspendedOverlay.js @@ -0,0 +1,63 @@ +/* global $, APP */ + +import Overlay from '../overlay/Overlay'; + +/** + * An overlay dialog which is shown when a suspended event is detected. + */ +class SuspendedOverlayImpl extends Overlay{ + /** + * Creates new SuspendedOverlayImpl + */ + constructor() { + super(); + + $(document).on('click', '#rejoin', () => { + APP.ConferenceUrl.reload(); + }); + } + /** + * Constructs overlay body with the message and a button to rejoin. + * @override + */ + _buildOverlayContent() { + return ( + `
+ + +

+ +
`); + } +} + +/** + * Holds the page suspended overlay instance. + * + * {@type SuspendedOverlayImpl} + */ +let overlay; + +export default { + /** + * Checks whether the page suspended overlay has been displayed. + * @return {boolean} true if the page suspended overlay is + * currently visible or false otherwise. + */ + isVisible() { + return overlay && overlay.isVisible(); + }, + /** + * Shows the page suspended overlay. + */ + show() { + + if (!overlay) { + overlay = new SuspendedOverlayImpl(); + } + overlay.show(); + } +};