Merge branch 'master' into translations-update

This commit is contained in:
damencho 2016-10-24 14:40:50 -05:00
commit 547f96c0c1
23 changed files with 462 additions and 163 deletions

23
app.js
View File

@ -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 () {

View File

@ -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:

View File

@ -1,4 +1,4 @@
.recordingSpinner { .recordingSpinner {
display: none; display: none;
vertical-align: text-bottom; vertical-align: top;
} }

View File

@ -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%;

View File

@ -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;

View File

@ -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;
} }

View File

@ -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;

View File

@ -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';

View File

@ -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;
} }

View File

@ -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;
}

View File

@ -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;

View File

@ -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>

View File

@ -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.",

View File

@ -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.
* *

View File

@ -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. */
class GUMOverlayImpl extends Overlay {
/**
* Constructs overlay with guidance how to proceed with gUM prompt.
* @param {string} browser - name of browser for which to construct the * @param {string} browser - name of browser for which to construct the
* guidance overlay. * guidance overlay.
* @override
*/ */
function buildOverlayHtml(browser) { constructor(browser) {
$overlay = $(` super();
<div class='overlay_container'> this.browser = browser;
<div class='overlay overlay_transparent' /> }
<div class='overlay_content'>
/**
* @inheritDoc
*/
_buildOverlayContent() {
return `
<span class="overlay_icon icon-microphone"></span> <span class="overlay_icon icon-microphone"></span>
<span class="overlay_icon icon-camera"></span> <span class="overlay_icon icon-camera"></span>
<span data-i18n='[html]userMedia.${browser}GrantPermissions' <span data-i18n='[html]userMedia.${this.browser}GrantPermissions'
class='overlay_text overlay_text_small'></span> class='overlay_text_small'></span>`;
</div> }
</div>`);
APP.translation.translateElement($overlay);
} }
/**
* 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();
} }
}; };

View File

@ -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

View File

@ -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();
}
}

View File

@ -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();
}
};

View File

@ -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 &&
(Toolbar.isHovered()
|| APP.UI.isRingOverlayVisible() || APP.UI.isRingOverlayVisible()
|| SideContainerToggler.isVisible()) { || SideContainerToggler.isVisible())) {
toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout); toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
} else { } else {
Toolbar.hide(); Toolbar.hide();

View File

@ -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" ';

View File

@ -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);
}
}

View File

@ -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

View File

@ -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.
*/ */