Merge branch 'master' into translations-update
This commit is contained in:
commit
547f96c0c1
23
app.js
23
app.js
|
@ -24,6 +24,7 @@ import RoomnameGenerator from './modules/util/RoomnameGenerator';
|
||||||
import UI from "./modules/UI/UI";
|
import UI from "./modules/UI/UI";
|
||||||
import settings from "./modules/settings/Settings";
|
import settings from "./modules/settings/Settings";
|
||||||
import conference from './conference';
|
import conference from './conference';
|
||||||
|
import ConferenceUrl from './modules/URL/ConferenceUrl';
|
||||||
import API from './modules/API/API';
|
import API from './modules/API/API';
|
||||||
|
|
||||||
import UIEvents from './service/UI/UIEvents';
|
import UIEvents from './service/UI/UIEvents';
|
||||||
|
@ -47,6 +48,18 @@ function pushHistoryState(roomName, URL) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces current history state(replaces the URL displayed by the browser).
|
||||||
|
* @param {string} newUrl the URL string which is to be displayed by the browser
|
||||||
|
* to the user.
|
||||||
|
*/
|
||||||
|
function replaceHistoryState (newUrl) {
|
||||||
|
if (window.history
|
||||||
|
&& typeof window.history.replaceState === 'function') {
|
||||||
|
window.history.replaceState({}, document.title, newUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds and returns the room name.
|
* Builds and returns the room name.
|
||||||
*/
|
*/
|
||||||
|
@ -82,6 +95,12 @@ const APP = {
|
||||||
UI,
|
UI,
|
||||||
settings,
|
settings,
|
||||||
conference,
|
conference,
|
||||||
|
/**
|
||||||
|
* After the APP has been initialized provides utility methods for dealing
|
||||||
|
* with the conference room URL(address).
|
||||||
|
* @type ConferenceUrl
|
||||||
|
*/
|
||||||
|
ConferenceUrl : null,
|
||||||
connection: null,
|
connection: null,
|
||||||
API,
|
API,
|
||||||
init () {
|
init () {
|
||||||
|
@ -107,6 +126,10 @@ function setTokenData() {
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
setTokenData();
|
setTokenData();
|
||||||
|
// Initialize the conference URL handler
|
||||||
|
APP.ConferenceUrl = new ConferenceUrl(window.location);
|
||||||
|
// Clean up the URL displayed by the browser
|
||||||
|
replaceHistoryState(APP.ConferenceUrl.getInviteUrl());
|
||||||
var isUIReady = APP.UI.start();
|
var isUIReady = APP.UI.start();
|
||||||
if (isUIReady) {
|
if (isUIReady) {
|
||||||
APP.conference.init({roomName: buildRoomName()}).then(function () {
|
APP.conference.init({roomName: buildRoomName()}).then(function () {
|
||||||
|
|
|
@ -325,10 +325,6 @@ class ConferenceConnector {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE:
|
|
||||||
APP.UI.notifyBridgeDown();
|
|
||||||
break;
|
|
||||||
|
|
||||||
// not enough rights to create conference
|
// not enough rights to create conference
|
||||||
case ConferenceErrors.AUTHENTICATION_REQUIRED:
|
case ConferenceErrors.AUTHENTICATION_REQUIRED:
|
||||||
// schedule reconnect to check if someone else created the room
|
// schedule reconnect to check if someone else created the room
|
||||||
|
@ -363,6 +359,10 @@ class ConferenceConnector {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// FIXME FOCUS_DISCONNECTED is confusing event name.
|
||||||
|
// What really happens there is that the library is not ready yet,
|
||||||
|
// because Jicofo is not available, but it is going to give
|
||||||
|
// it another try.
|
||||||
case ConferenceErrors.FOCUS_DISCONNECTED:
|
case ConferenceErrors.FOCUS_DISCONNECTED:
|
||||||
{
|
{
|
||||||
let [focus, retrySec] = params;
|
let [focus, retrySec] = params;
|
||||||
|
@ -371,8 +371,17 @@ class ConferenceConnector {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConferenceErrors.FOCUS_LEFT:
|
case ConferenceErrors.FOCUS_LEFT:
|
||||||
|
case ConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE:
|
||||||
|
// Log the page reload event
|
||||||
|
// FIXME (CallStats - issue) this event will not make it to
|
||||||
|
// the CallStats, because the log queue is not flushed, before
|
||||||
|
// "fabric terminated" is sent to the backed
|
||||||
|
APP.conference.logEvent('page.reload');
|
||||||
|
// FIXME the conference should be stopped by the library and not by
|
||||||
|
// the app. Both the errors above are unrecoverable from the library
|
||||||
|
// perspective.
|
||||||
room.leave().then(() => connection.disconnect());
|
room.leave().then(() => connection.disconnect());
|
||||||
APP.UI.notifyFocusLeft();
|
APP.UI.showPageReloadOverlay();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ConferenceErrors.CONFERENCE_MAX_USERS:
|
case ConferenceErrors.CONFERENCE_MAX_USERS:
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.recordingSpinner {
|
.recordingSpinner {
|
||||||
display: none;
|
display: none;
|
||||||
vertical-align: text-bottom;
|
vertical-align: top;
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
.toolbar {
|
.toolbar {
|
||||||
background-color: rgba(0,0,0,0.5);
|
background-color: $toolbarBackground;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: $toolbarZ;
|
z-index: $toolbarZ;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
|
@ -27,6 +27,7 @@ $defaultBackground: #474747;
|
||||||
$tooltipBg: rgba(0,0,0, 0.7);
|
$tooltipBg: rgba(0,0,0, 0.7);
|
||||||
|
|
||||||
// Toolbar
|
// Toolbar
|
||||||
|
$toolbarBackground: rgba(0, 0, 0, 0.5);
|
||||||
$toolbarSelectBackground: rgba(0, 0, 0, .6);
|
$toolbarSelectBackground: rgba(0, 0, 0, .6);
|
||||||
$toolbarBadgeBackground: #165ECC;
|
$toolbarBadgeBackground: #165ECC;
|
||||||
$toolbarBadgeColor: #FFFFFF;
|
$toolbarBadgeColor: #FFFFFF;
|
||||||
|
@ -46,6 +47,8 @@ $dominantSpeakerBg: #165ecc;
|
||||||
$raiseHandBg: #D6D61E;
|
$raiseHandBg: #D6D61E;
|
||||||
$audioLevelBg: #44A5FF;
|
$audioLevelBg: #44A5FF;
|
||||||
$audioLevelShadow: rgba(9, 36, 77, 0.9);
|
$audioLevelShadow: rgba(9, 36, 77, 0.9);
|
||||||
|
$videoStateIndicatorColor: $defaultColor;
|
||||||
|
$videoStateIndicatorBackground: $toolbarBackground;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Feedback Modal
|
* Feedback Modal
|
||||||
|
@ -84,8 +87,9 @@ $sidebarWidth: 200px;
|
||||||
*/
|
*/
|
||||||
$tooltipsZ: 901;
|
$tooltipsZ: 901;
|
||||||
$toolbarZ: 900;
|
$toolbarZ: 900;
|
||||||
$overlayZ: 800;
|
$overlayZ: 902;
|
||||||
$notificationZ: 1012;
|
$notificationZ: 1012;
|
||||||
|
$ringingZ: 800;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Font Colors TODO: Change colors when general dialogs are implemented.
|
* Font Colors TODO: Change colors when general dialogs are implemented.
|
||||||
|
@ -106,6 +110,6 @@ $linkHoverFontColor: #287ade;
|
||||||
/**
|
/**
|
||||||
* Forms
|
* Forms
|
||||||
*/
|
*/
|
||||||
$inputBg: #505F79;
|
$inputBg: $inputSemiBackground;
|
||||||
$inputBgHover: #505F79;
|
$inputBgHover: $inputSemiBackground;
|
||||||
$inputFontColor: #ECEEF1;
|
$inputFontColor: $defaultDarkFontColor;
|
|
@ -511,7 +511,7 @@
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: auto;
|
width: auto;
|
||||||
z-index: 1011;
|
z-index: 2;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -534,7 +534,7 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
top:50%;
|
top:50%;
|
||||||
z-index: 1011;
|
z-index: 2;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -546,47 +546,43 @@
|
||||||
0px 0px 1px rgba(0,0,0,0.3);
|
0px 0px 1px rgba(0,0,0,0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#videoResolutionLabel {
|
.video-state-indicator {
|
||||||
display: none;
|
background: $videoStateIndicatorBackground;
|
||||||
position: absolute;
|
color: $videoStateIndicatorColor;
|
||||||
top: 5px;
|
font-size: 13px;
|
||||||
right: 5px;
|
line-height: 20px;
|
||||||
background: rgba(0,0,0,.5);
|
text-align: center;
|
||||||
padding: 10px;
|
min-width: 40px;
|
||||||
color: rgba(255,255,255,.5);
|
height: 40px;
|
||||||
z-index: 1011;
|
padding: 10px 5px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
#videoResolutionLabel,
|
||||||
|
.centeredVideoLabel {
|
||||||
|
display: none;
|
||||||
|
z-index: 1011;
|
||||||
}
|
}
|
||||||
|
|
||||||
.centeredVideoLabel {
|
.centeredVideoLabel {
|
||||||
display: none;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 45%;
|
bottom: 45%;
|
||||||
top: auto;
|
|
||||||
right: auto;
|
|
||||||
left: auto;
|
|
||||||
line-height: 28px;
|
|
||||||
height: 28px;
|
|
||||||
width: auto;
|
|
||||||
padding: 5px;
|
|
||||||
margin-right: auto;
|
|
||||||
margin-left: auto;
|
|
||||||
background: rgba(0,0,0,.5);
|
|
||||||
color: #FFF;
|
|
||||||
z-index: 1011;
|
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
-webkit-transition: all 2s 2s linear;
|
-webkit-transition: all 2s 2s linear;
|
||||||
transition: all 2s 2s linear;
|
transition: all 2s 2s linear;
|
||||||
|
|
||||||
|
&.moveToCorner {
|
||||||
|
bottom: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.moveToCorner {
|
.moveToCorner {
|
||||||
top: 5px;
|
position: absolute;
|
||||||
right: 50px; /*leave free space for the HD label*/
|
top: 30px;
|
||||||
margin-right: 0px;
|
right: 30px;
|
||||||
margin-left: auto;
|
|
||||||
background: rgba(0,0,0,.3);
|
|
||||||
color: rgba(255,255,255,.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.hidden {
|
.moveToCorner + .moveToCorner {
|
||||||
|
right: 80px;
|
||||||
}
|
}
|
|
@ -3,6 +3,10 @@ form.aui {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
|
||||||
> a {
|
> a {
|
||||||
|
background-color: $inputBg !important;
|
||||||
|
color: $inputFontColor !important;
|
||||||
|
border-color: $inputBg !important;
|
||||||
|
text-shadow: none !important;
|
||||||
margin: 0 auto !important;
|
margin: 0 auto !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
@ -32,16 +36,6 @@ form.aui {
|
||||||
z-index: 900;
|
z-index: 900;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Dark theme
|
|
||||||
form.aui{
|
|
||||||
//Placeholder
|
|
||||||
.aui-select2-container.input-container-dark {
|
|
||||||
a.select2-choice {
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.aui-dropdown2.aui-style-default.dropdown-dark {
|
.aui-dropdown2.aui-style-default.dropdown-dark {
|
||||||
background-color: $defaultBackground;
|
background-color: $defaultBackground;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
@import 'toastr';
|
@import 'toastr';
|
||||||
@import 'base';
|
@import 'base';
|
||||||
@import 'overlay/overlay';
|
@import 'overlay/overlay';
|
||||||
|
@import 'reload_overlay/reload_overlay';
|
||||||
@import 'modals/dialog';
|
@import 'modals/dialog';
|
||||||
@import 'modals/feedback/feedback';
|
@import 'modals/feedback/feedback';
|
||||||
@import 'videolayout_default';
|
@import 'videolayout_default';
|
||||||
|
|
|
@ -1,48 +1,30 @@
|
||||||
.overlay {
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: $overlayZ;
|
|
||||||
background: #21B9FC; /* Old browsers */
|
|
||||||
opacity: 0.75;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlay_transparent {
|
|
||||||
background: rgba(22, 185, 252, .9);
|
|
||||||
}
|
|
||||||
|
|
||||||
.overlay_container {
|
.overlay_container {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: $overlayZ;
|
z-index: $overlayZ;
|
||||||
|
background: rgba(22, 185, 252, .9);
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay_content {
|
.overlay_content {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: normal;
|
|
||||||
font-size: 20px;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 400px;
|
width: 400px;
|
||||||
height: 250px;
|
height: 250px;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
position:absolute;
|
position: absolute;
|
||||||
margin-top: -125px;
|
margin-top: -125px;
|
||||||
margin-left: -200px;
|
margin-left: -200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.overlay_text_small {
|
.overlay_text_small {
|
||||||
|
display: block;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay_icon {
|
.overlay_icon {
|
||||||
position: relative;
|
|
||||||
z-index: 1013;
|
|
||||||
float: none;
|
|
||||||
font-size: 100px;
|
font-size: 100px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
.reload_overlay_title {
|
||||||
|
display: block;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reload_overlay_msg {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#reloadProgressBar {
|
||||||
|
width: 180px;
|
||||||
|
margin: 5px auto;
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: $overlayZ;
|
z-index: $ringingZ;
|
||||||
background: linear-gradient(transparent, #000);
|
background: linear-gradient(transparent, #000);
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
|
|
||||||
|
|
|
@ -240,8 +240,8 @@
|
||||||
<video id="largeVideo" muted="true" autoplay></video>
|
<video id="largeVideo" muted="true" autoplay></video>
|
||||||
</div>
|
</div>
|
||||||
<span id="localConnectionMessage"></span>
|
<span id="localConnectionMessage"></span>
|
||||||
<span id="videoResolutionLabel">HD</span>
|
<span id="videoResolutionLabel" class="video-state-indicator moveToCorner">HD</span>
|
||||||
<span id="recordingLabel" class="centeredVideoLabel">
|
<span id="recordingLabel" class="video-state-indicator centeredVideoLabel">
|
||||||
<span id="recordingLabelText"></span>
|
<span id="recordingLabelText"></span>
|
||||||
<img id="recordingSpinner" class="recordingSpinner" src="images/spin.svg"></img>
|
<img id="recordingSpinner" class="recordingSpinner" src="images/spin.svg"></img>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -204,8 +204,9 @@
|
||||||
"detectext": "Error when trying to detect desktopsharing extension.",
|
"detectext": "Error when trying to detect desktopsharing extension.",
|
||||||
"failtoinstall": "Failed to install desktop sharing extension",
|
"failtoinstall": "Failed to install desktop sharing extension",
|
||||||
"failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.",
|
"failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.",
|
||||||
"bridgeUnavailable": "Jitsi Videobridge is currently unavailable. Please try again later!",
|
"conferenceReloadTitle": "Unfortunately, something went wrong",
|
||||||
"jicofoUnavailable": "Jicofo is currently unavailable. Please try again later!",
|
"conferenceReloadMsg": "We're trying to fix this",
|
||||||
|
"conferenceReloadTimeLeft": "__seconds__ sec.",
|
||||||
"maxUsersLimitReached": "The limit for maximum number of participants in the conference has been reached. The conference is full. Please try again later!",
|
"maxUsersLimitReached": "The limit for maximum number of participants in the conference has been reached. The conference is full. Please try again later!",
|
||||||
"lockTitle": "Lock failed",
|
"lockTitle": "Lock failed",
|
||||||
"lockMessage": "Failed to lock the conference.",
|
"lockMessage": "Failed to lock the conference.",
|
||||||
|
|
|
@ -14,12 +14,12 @@ import Recording from "./recording/Recording";
|
||||||
import GumPermissionsOverlay
|
import GumPermissionsOverlay
|
||||||
from './gum_overlay/UserMediaPermissionsGuidanceOverlay';
|
from './gum_overlay/UserMediaPermissionsGuidanceOverlay';
|
||||||
|
|
||||||
|
import PageReloadOverlay from './reload_overlay/PageReloadOverlay';
|
||||||
import VideoLayout from "./videolayout/VideoLayout";
|
import VideoLayout from "./videolayout/VideoLayout";
|
||||||
import FilmStrip from "./videolayout/FilmStrip";
|
import FilmStrip from "./videolayout/FilmStrip";
|
||||||
import SettingsMenu from "./side_pannels/settings/SettingsMenu";
|
import SettingsMenu from "./side_pannels/settings/SettingsMenu";
|
||||||
import Profile from "./side_pannels/profile/Profile";
|
import Profile from "./side_pannels/profile/Profile";
|
||||||
import Settings from "./../settings/Settings";
|
import Settings from "./../settings/Settings";
|
||||||
import { reload } from '../util/helpers';
|
|
||||||
import RingOverlay from "./ring_overlay/RingOverlay";
|
import RingOverlay from "./ring_overlay/RingOverlay";
|
||||||
import UIErrors from './UIErrors';
|
import UIErrors from './UIErrors';
|
||||||
|
|
||||||
|
@ -189,13 +189,6 @@ UI.notifyConferenceDestroyed = function (reason) {
|
||||||
"dialog.sessTerminated", reason, true, {}, () => false);
|
"dialog.sessTerminated", reason, true, {}, () => false);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify user that Jitsi Videobridge is not accessible.
|
|
||||||
*/
|
|
||||||
UI.notifyBridgeDown = function () {
|
|
||||||
messageHandler.showError("dialog.error", "dialog.bridgeUnavailable");
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show chat error.
|
* Show chat error.
|
||||||
* @param err the Error
|
* @param err the Error
|
||||||
|
@ -259,19 +252,6 @@ UI.setLocalRaisedHandStatus = (raisedHandStatus) => {
|
||||||
*/
|
*/
|
||||||
UI.initConference = function () {
|
UI.initConference = function () {
|
||||||
let id = APP.conference.getMyUserId();
|
let id = APP.conference.getMyUserId();
|
||||||
|
|
||||||
// Do not include query parameters in the invite URL
|
|
||||||
// "https:" + "//" + "example.com:8888" + "/SomeConference1245"
|
|
||||||
var inviteURL = window.location.protocol + "//" +
|
|
||||||
window.location.host + window.location.pathname;
|
|
||||||
|
|
||||||
this.emitEvent(UIEvents.INVITE_URL_INITIALISED, inviteURL);
|
|
||||||
|
|
||||||
// Clean up the URL displayed by the browser
|
|
||||||
if (window.history && typeof window.history.replaceState === 'function') {
|
|
||||||
window.history.replaceState({}, document.title, inviteURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add myself to the contact list.
|
// Add myself to the contact list.
|
||||||
UI.ContactList.addContact(id, true);
|
UI.ContactList.addContact(id, true);
|
||||||
|
|
||||||
|
@ -1104,22 +1084,11 @@ UI.notifyFocusDisconnected = function (focus, retrySec) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify user that focus left the conference so page should be reloaded.
|
* Notify the user that the video conferencing service is badly broken and
|
||||||
|
* the page should be reloaded.
|
||||||
*/
|
*/
|
||||||
UI.notifyFocusLeft = function () {
|
UI.showPageReloadOverlay = function () {
|
||||||
let msg = APP.translation.generateTranslationHTML(
|
PageReloadOverlay.show(15 /* will reload in 15 seconds */);
|
||||||
'dialog.jicofoUnavailable'
|
|
||||||
);
|
|
||||||
messageHandler.openDialog(
|
|
||||||
'dialog.serviceUnavailable',
|
|
||||||
msg,
|
|
||||||
true, // persistent
|
|
||||||
[{title: 'retry'}],
|
|
||||||
function () {
|
|
||||||
reload();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1447,6 +1416,18 @@ UI.hideRingOverLay = function () {
|
||||||
FilmStrip.toggleFilmStrip(true);
|
FilmStrip.toggleFilmStrip(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if any the "top" overlays are currently visible. The check includes
|
||||||
|
* the call 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()
|
||||||
|
|| PageReloadOverlay.isVisible()
|
||||||
|
|| GumPermissionsOverlay.isVisible();
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the ring overlay is currently visible.
|
* Indicates if the ring overlay is currently visible.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,29 +1,50 @@
|
||||||
/* global $, APP */
|
/* global */
|
||||||
|
|
||||||
let $overlay;
|
import Overlay from '../overlay/Overlay';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal function that constructs overlay with guidance how to proceed with
|
* An overlay with guidance how to proceed with gUM prompt.
|
||||||
* gUM prompt.
|
|
||||||
* @param {string} browser - name of browser for which to construct the
|
|
||||||
* guidance overlay.
|
|
||||||
*/
|
*/
|
||||||
function buildOverlayHtml(browser) {
|
class GUMOverlayImpl extends Overlay {
|
||||||
$overlay = $(`
|
|
||||||
<div class='overlay_container'>
|
|
||||||
<div class='overlay overlay_transparent' />
|
|
||||||
<div class='overlay_content'>
|
|
||||||
<span class="overlay_icon icon-microphone"></span>
|
|
||||||
<span class="overlay_icon icon-camera"></span>
|
|
||||||
<span data-i18n='[html]userMedia.${browser}GrantPermissions'
|
|
||||||
class='overlay_text overlay_text_small'></span>
|
|
||||||
</div>
|
|
||||||
</div>`);
|
|
||||||
|
|
||||||
APP.translation.translateElement($overlay);
|
/**
|
||||||
|
* Constructs overlay with guidance how to proceed with gUM prompt.
|
||||||
|
* @param {string} browser - name of browser for which to construct the
|
||||||
|
* guidance overlay.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
constructor(browser) {
|
||||||
|
super();
|
||||||
|
this.browser = browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
_buildOverlayContent() {
|
||||||
|
return `
|
||||||
|
<span class="overlay_icon icon-microphone"></span>
|
||||||
|
<span class="overlay_icon icon-camera"></span>
|
||||||
|
<span data-i18n='[html]userMedia.${this.browser}GrantPermissions'
|
||||||
|
class='overlay_text_small'></span>`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores GUM overlay instance.
|
||||||
|
* @type {GUMOverlayImpl}
|
||||||
|
*/
|
||||||
|
let overlay;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
/**
|
||||||
|
* Checks whether the overlay is currently visible.
|
||||||
|
* @return {boolean} <tt>true</tt> if the overlay is visible
|
||||||
|
* or <tt>false</tt> otherwise.
|
||||||
|
*/
|
||||||
|
isVisible () {
|
||||||
|
return overlay && overlay.isVisible();
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Shows browser-specific overlay with guidance how to proceed with
|
* Shows browser-specific overlay with guidance how to proceed with
|
||||||
* gUM prompt.
|
* gUM prompt.
|
||||||
|
@ -31,9 +52,10 @@ export default {
|
||||||
* guidance overlay.
|
* guidance overlay.
|
||||||
*/
|
*/
|
||||||
show(browser) {
|
show(browser) {
|
||||||
!$overlay && buildOverlayHtml(browser);
|
if (!overlay) {
|
||||||
|
overlay = new GUMOverlayImpl(browser);
|
||||||
!$overlay.parents('body').length && $overlay.appendTo('body');
|
}
|
||||||
|
overlay.show();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,6 +63,6 @@ export default {
|
||||||
* gUM prompt.
|
* gUM prompt.
|
||||||
*/
|
*/
|
||||||
hide() {
|
hide() {
|
||||||
$overlay && $overlay.detach();
|
overlay && overlay.hide();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,7 @@ const ConferenceEvents = JitsiMeetJS.events.conference;
|
||||||
class Invite {
|
class Invite {
|
||||||
constructor(conference) {
|
constructor(conference) {
|
||||||
this.conference = conference;
|
this.conference = conference;
|
||||||
|
this.inviteUrl = APP.ConferenceUrl.getInviteUrl();
|
||||||
this.createRoomLocker(conference);
|
this.createRoomLocker(conference);
|
||||||
this.registerListeners();
|
this.registerListeners();
|
||||||
}
|
}
|
||||||
|
@ -48,11 +49,6 @@ class Invite {
|
||||||
APP.UI.addListener( UIEvents.INVITE_CLICKED,
|
APP.UI.addListener( UIEvents.INVITE_CLICKED,
|
||||||
() => { this.openLinkDialog(); });
|
() => { this.openLinkDialog(); });
|
||||||
|
|
||||||
APP.UI.addListener( UIEvents.INVITE_URL_INITIALISED,
|
|
||||||
(inviteUrl) => {
|
|
||||||
this.updateInviteUrl(inviteUrl);
|
|
||||||
});
|
|
||||||
|
|
||||||
APP.UI.addListener( UIEvents.PASSWORD_REQUIRED,
|
APP.UI.addListener( UIEvents.PASSWORD_REQUIRED,
|
||||||
() => {
|
() => {
|
||||||
this.setLockedFromElsewhere(true);
|
this.setLockedFromElsewhere(true);
|
||||||
|
@ -172,14 +168,6 @@ class Invite {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the room invite url.
|
|
||||||
*/
|
|
||||||
updateInviteUrl (newInviteUrl) {
|
|
||||||
this.inviteUrl = newInviteUrl;
|
|
||||||
this.updateView();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method for encoding
|
* Helper method for encoding
|
||||||
* Invite URL
|
* Invite URL
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
/* global $, APP */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for overlay components - the components which are displayed on
|
||||||
|
* top of the application with semi-transparent background covering the whole
|
||||||
|
* screen.
|
||||||
|
*/
|
||||||
|
export default class Overlay{
|
||||||
|
/**
|
||||||
|
* Creates new <tt>Overlay</tt> instance.
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {jQuery}
|
||||||
|
*/
|
||||||
|
this.$overlay = null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Template method which should be used by subclasses to provide the overlay
|
||||||
|
* content. The contents provided by this method are later subject to
|
||||||
|
* the translation using {@link APP.translation.translateElement}.
|
||||||
|
* @return {string} HTML representation of the overlay dialog contents.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_buildOverlayContent() {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Constructs the HTML body of the overlay dialog.
|
||||||
|
*/
|
||||||
|
buildOverlayHtml() {
|
||||||
|
|
||||||
|
let overlayContent = this._buildOverlayContent();
|
||||||
|
|
||||||
|
this.$overlay = $(`
|
||||||
|
<div class='overlay_container'>
|
||||||
|
<div class='overlay_content'>
|
||||||
|
${overlayContent}
|
||||||
|
</div>
|
||||||
|
</div>`);
|
||||||
|
|
||||||
|
APP.translation.translateElement(this.$overlay);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks whether the page reload overlay has been displayed.
|
||||||
|
* @return {boolean} <tt>true</tt> if the page reload overlay is currently
|
||||||
|
* visible or <tt>false</tt> otherwise.
|
||||||
|
*/
|
||||||
|
isVisible() {
|
||||||
|
return this.$overlay && this.$overlay.parents('body').length > 0;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Template method called just after the overlay is displayed for the first
|
||||||
|
* time.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_onShow() {
|
||||||
|
// To be overridden by subclasses.
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Shows the overlay dialog adn attaches the underlying HTML representation
|
||||||
|
* to the DOM.
|
||||||
|
*/
|
||||||
|
show() {
|
||||||
|
|
||||||
|
!this.$overlay && this.buildOverlayHtml();
|
||||||
|
|
||||||
|
if (!this.isVisible()) {
|
||||||
|
this.$overlay.appendTo('body');
|
||||||
|
this._onShow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the overlay dialog and detaches it's HTML representation from
|
||||||
|
* the DOM.
|
||||||
|
*/
|
||||||
|
hide() {
|
||||||
|
this.$overlay && this.$overlay.detach();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/* global $, APP, AJS */
|
||||||
|
|
||||||
|
import Overlay from '../overlay/Overlay';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An overlay dialog which is shown before the conference is reloaded. Shows
|
||||||
|
* a warning message and counts down towards the reload.
|
||||||
|
*/
|
||||||
|
class PageReloadOverlayImpl extends Overlay{
|
||||||
|
/**
|
||||||
|
* Creates new <tt>PageReloadOverlayImpl</tt>
|
||||||
|
* @param {number} timeoutSeconds how long the overlay dialog will be
|
||||||
|
* displayed, before the conference will be reloaded.
|
||||||
|
*/
|
||||||
|
constructor(timeoutSeconds) {
|
||||||
|
super();
|
||||||
|
/**
|
||||||
|
* Conference reload counter in seconds.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.timeLeft = timeoutSeconds;
|
||||||
|
/**
|
||||||
|
* Conference reload timeout in seconds.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.timeout = timeoutSeconds;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Constructs overlay body with the warning message and count down towards
|
||||||
|
* the conference reload.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_buildOverlayContent() {
|
||||||
|
return `
|
||||||
|
<span data-i18n='dialog.conferenceReloadTitle'
|
||||||
|
class='reload_overlay_title'></span>
|
||||||
|
<span data-i18n='dialog.conferenceReloadMsg'
|
||||||
|
class='reload_overlay_msg'></span>
|
||||||
|
<div>
|
||||||
|
<div id='reloadProgressBar' class="aui-progress-indicator">
|
||||||
|
<span class="aui-progress-indicator-value"></span>
|
||||||
|
</div>
|
||||||
|
<span id='reloadSecRemaining'
|
||||||
|
data-i18n="dialog.conferenceReloadTimeLeft"
|
||||||
|
class='reload_overlay_msg'>
|
||||||
|
</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the progress indicator position and the label with the time left.
|
||||||
|
*/
|
||||||
|
updateDisplay() {
|
||||||
|
|
||||||
|
APP.translation.translateElement(
|
||||||
|
$("#reloadSecRemaining"), { seconds: this.timeLeft });
|
||||||
|
|
||||||
|
const ratio = (this.timeout - this.timeLeft) / this.timeout;
|
||||||
|
AJS.progressBars.update("#reloadProgressBar", ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the reload countdown with the animation.
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
|
_onShow() {
|
||||||
|
|
||||||
|
// Initialize displays
|
||||||
|
this.updateDisplay();
|
||||||
|
|
||||||
|
var intervalId = window.setInterval(function() {
|
||||||
|
|
||||||
|
if (this.timeLeft >= 1) {
|
||||||
|
this.timeLeft -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateDisplay();
|
||||||
|
|
||||||
|
if (this.timeLeft === 0) {
|
||||||
|
window.clearInterval(intervalId);
|
||||||
|
APP.ConferenceUrl.reload();
|
||||||
|
}
|
||||||
|
}.bind(this), 1000);
|
||||||
|
|
||||||
|
console.info(
|
||||||
|
"The conference will be reloaded after "
|
||||||
|
+ this.timeLeft + " seconds.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the page reload overlay instance.
|
||||||
|
*
|
||||||
|
* {@type PageReloadOverlayImpl}
|
||||||
|
*/
|
||||||
|
let overlay;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* Checks whether the page reload overlay has been displayed.
|
||||||
|
* @return {boolean} <tt>true</tt> if the page reload overlay is currently
|
||||||
|
* visible or <tt>false</tt> otherwise.
|
||||||
|
*/
|
||||||
|
isVisible() {
|
||||||
|
return overlay && overlay.isVisible();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Shows the page reload overlay which will do the conference reload after
|
||||||
|
* the given amount of time.
|
||||||
|
*
|
||||||
|
* @param {number} timeoutSeconds how many seconds before the conference
|
||||||
|
* reload will happen.
|
||||||
|
*/
|
||||||
|
show(timeoutSeconds) {
|
||||||
|
|
||||||
|
if (!overlay) {
|
||||||
|
overlay = new PageReloadOverlayImpl(timeoutSeconds);
|
||||||
|
}
|
||||||
|
overlay.show();
|
||||||
|
}
|
||||||
|
};
|
|
@ -34,9 +34,10 @@ function hideToolbar(force) { // eslint-disable-line no-unused-vars
|
||||||
clearTimeout(toolbarTimeoutObject);
|
clearTimeout(toolbarTimeoutObject);
|
||||||
toolbarTimeoutObject = null;
|
toolbarTimeoutObject = null;
|
||||||
|
|
||||||
if (Toolbar.isHovered()
|
if (force !== true &&
|
||||||
|| APP.UI.isRingOverlayVisible()
|
(Toolbar.isHovered()
|
||||||
|| SideContainerToggler.isVisible()) {
|
|| APP.UI.isRingOverlayVisible()
|
||||||
|
|| SideContainerToggler.isVisible())) {
|
||||||
toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
|
toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
|
||||||
} else {
|
} else {
|
||||||
Toolbar.hide();
|
Toolbar.hide();
|
||||||
|
|
|
@ -326,7 +326,7 @@ var messageHandler = {
|
||||||
messageArguments, options) {
|
messageArguments, options) {
|
||||||
|
|
||||||
// If we're in ringing state we skip all toaster notifications.
|
// If we're in ringing state we skip all toaster notifications.
|
||||||
if(!notificationsEnabled || APP.UI.isRingOverlayVisible())
|
if(!notificationsEnabled || APP.UI.isOverlayVisible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var displayNameSpan = '<span class="nickname" ';
|
var displayNameSpan = '<span class="nickname" ';
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* global console */
|
||||||
|
|
||||||
|
import { redirect } from '../util/helpers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The modules stores information about the URL used to start the conference and
|
||||||
|
* provides utility methods for dealing with conference URL and reloads.
|
||||||
|
*/
|
||||||
|
export default class ConferenceUrl {
|
||||||
|
/**
|
||||||
|
* Initializes the module.
|
||||||
|
*
|
||||||
|
* @param location an object which stores provides the info about conference
|
||||||
|
* URL(would be 'window.location' for the Web app). The params below are
|
||||||
|
* described based on the following example URL:
|
||||||
|
*
|
||||||
|
* https://example.com:8888/SomeConference1245?opt=1#somehash
|
||||||
|
*
|
||||||
|
* @param location.href full URL with all parameters, would be the whole URL
|
||||||
|
* from the example string above.
|
||||||
|
*
|
||||||
|
* @param location.host the host part of the URL, 'example.com' from
|
||||||
|
* the sample URL above.
|
||||||
|
*
|
||||||
|
* @param location.pathname the path part of the URL, would be
|
||||||
|
* '/SomeConference1245' from the example above.
|
||||||
|
*
|
||||||
|
* @param location.protocol the protocol part of the URL, would be 'https:'
|
||||||
|
* from the sample URL.
|
||||||
|
*/
|
||||||
|
constructor(location) {
|
||||||
|
/**
|
||||||
|
* Stores the original conference room URL with all parameters.
|
||||||
|
* Example:
|
||||||
|
* https://example.com:8888/SomeConference1245?jwt=a5sbc2#blablahash
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.originalURL = location.href;
|
||||||
|
/**
|
||||||
|
* A simplified version of the conference URL stripped out of
|
||||||
|
* the parameters which should be used for sending invites.
|
||||||
|
* Example:
|
||||||
|
* https://example.com:8888/SomeConference1245
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
this.inviteURL
|
||||||
|
= location.protocol + "//" + location.host + location.pathname;
|
||||||
|
console.info("Stored original conference URL: " + this.originalURL);
|
||||||
|
console.info("Conference URL for invites: " + this.inviteURL);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Obtains the conference invite URL.
|
||||||
|
* @return {string} the URL pointing o the conference which is mean to be
|
||||||
|
* used to invite new participants.
|
||||||
|
*/
|
||||||
|
getInviteUrl() {
|
||||||
|
return this.inviteURL;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Obtains full conference URL with all original parameters.
|
||||||
|
* @return {string} the original URL used to open the current conference.
|
||||||
|
*/
|
||||||
|
getOriginalUrl() {
|
||||||
|
return this.originalURL;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Reloads the conference using original URL with all of the parameters.
|
||||||
|
*/
|
||||||
|
reload() {
|
||||||
|
console.info("Reloading the conference using URL: " + this.originalURL);
|
||||||
|
redirect(this.originalURL);
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,15 @@ export function reload () {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redirects to new URL.
|
||||||
|
* @param {string} url the URL pointing to the location where the user should
|
||||||
|
* be redirected to.
|
||||||
|
*/
|
||||||
|
export function redirect (url) {
|
||||||
|
window.location.replace(url);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the error and reports it to the global error handler.
|
* Prints the error and reports it to the global error handler.
|
||||||
* @param e {Error} the error
|
* @param e {Error} the error
|
||||||
|
|
|
@ -145,11 +145,6 @@ export default {
|
||||||
*/
|
*/
|
||||||
DISPLAY_NAME_CHANGED: "UI.display_name_changed",
|
DISPLAY_NAME_CHANGED: "UI.display_name_changed",
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates that the invite url has been initialised.
|
|
||||||
*/
|
|
||||||
INVITE_URL_INITIALISED: "UI.invite_url_initialised",
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates that a password is required for the call.
|
* Indicates that a password is required for the call.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue