mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-10 16:50:21 +00:00
Compare commits
78 Commits
1503
...
move_loggi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf83b4142a | ||
|
|
75b3bc6190 | ||
|
|
6c716bcbb1 | ||
|
|
44beed6216 | ||
|
|
2352811c20 | ||
|
|
c1df375af4 | ||
|
|
e8c631de01 | ||
|
|
bdc67201e2 | ||
|
|
0db33bb45c | ||
|
|
9f26270a98 | ||
|
|
df721cbd2e | ||
|
|
8745efb81f | ||
|
|
e56f1a9ded | ||
|
|
52fbb8f02c | ||
|
|
a1639b67a5 | ||
|
|
3475ad4674 | ||
|
|
685d117a91 | ||
|
|
e51f791ab0 | ||
|
|
0f2ba1cefe | ||
|
|
1f457dfca5 | ||
|
|
841050953f | ||
|
|
c6296821c0 | ||
|
|
7c8ca45d9a | ||
|
|
142f6e4518 | ||
|
|
f72e7ffbc2 | ||
|
|
111b6e1c27 | ||
|
|
4add2d0590 | ||
|
|
cb0f7417b6 | ||
|
|
06eb7c6109 | ||
|
|
4d0701cfda | ||
|
|
86bce1e5f6 | ||
|
|
8da0d3a1f1 | ||
|
|
f32438b219 | ||
|
|
0238a10a4b | ||
|
|
6669a96fd8 | ||
|
|
09406bfbfc | ||
|
|
51da40e90c | ||
|
|
52847bd28d | ||
|
|
835d3c6a25 | ||
|
|
00e6e98a61 | ||
|
|
6d90adcdf6 | ||
|
|
06d2fb0aca | ||
|
|
c3428e8213 | ||
|
|
57b0736ebb | ||
|
|
6211566c0c | ||
|
|
48bb427f71 | ||
|
|
56f15356c7 | ||
|
|
8e6fd0ca95 | ||
|
|
b5dfc2a520 | ||
|
|
4900fe020d | ||
|
|
94bd6bc330 | ||
|
|
36bcc6831b | ||
|
|
76c89845a8 | ||
|
|
b58f1cdd16 | ||
|
|
141c64cd00 | ||
|
|
51b802da84 | ||
|
|
71c27f308c | ||
|
|
75b9adf01b | ||
|
|
a079914603 | ||
|
|
4d3ca4a85a | ||
|
|
afa1d5423c | ||
|
|
766eb94c7d | ||
|
|
ad6e793615 | ||
|
|
d900d3c3fd | ||
|
|
7f315ef105 | ||
|
|
dc0a7e7628 | ||
|
|
6d1f42bf30 | ||
|
|
c26b144f0d | ||
|
|
752f4dd5de | ||
|
|
693ebbea9d | ||
|
|
b24a54aab2 | ||
|
|
00aee89709 | ||
|
|
a5f243e18d | ||
|
|
d05ff9b4ee | ||
|
|
a67087b6de | ||
|
|
e2ea16ea3f | ||
|
|
3fc02bf6c2 | ||
|
|
c41c219206 |
@@ -8,3 +8,6 @@ indent_size = 4
|
||||
indent_style = space
|
||||
max_line_length = 80
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
build/
|
||||
debian/
|
||||
libs/
|
||||
node_modules/
|
||||
|
||||
2
404.html
2
404.html
@@ -6,7 +6,7 @@
|
||||
<body>
|
||||
<div class="error_page">
|
||||
<h2>404 Not Found</h2>
|
||||
<p class="error_page__message">You can create new conversation <a href="/">here</a></p>
|
||||
<p class="error_page__message">You can create new conversation <a class="link" href="/">here</a></p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -27,5 +27,7 @@
|
||||
action + '.' + data.browserName, label, value);
|
||||
};
|
||||
|
||||
ctx.Analytics = Analytics;
|
||||
if(typeof ctx.analyticsHandlers === "undefined")
|
||||
ctx.analyticsHandlers = [];
|
||||
ctx.analyticsHandlers.push(Analytics);
|
||||
}(window));
|
||||
|
||||
124
app.js
124
app.js
@@ -1,5 +1,6 @@
|
||||
/* global $, config, getRoomName */
|
||||
/* global $, config, getRoomName, loggingConfig, JitsiMeetJS */
|
||||
/* application specific logic */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import "babel-polyfill";
|
||||
import "jquery";
|
||||
@@ -18,6 +19,10 @@ import 'aui-experimental-css';
|
||||
|
||||
window.toastr = require("toastr");
|
||||
|
||||
const Logger = require("jitsi-meet-logger");
|
||||
const LogCollector = Logger.LogCollector;
|
||||
import JitsiMeetLogStorage from "./modules/util/JitsiMeetLogStorage";
|
||||
|
||||
import URLProcessor from "./modules/config/URLProcessor";
|
||||
import RoomnameGenerator from './modules/util/RoomnameGenerator';
|
||||
|
||||
@@ -31,6 +36,8 @@ import UIEvents from './service/UI/UIEvents';
|
||||
import getTokenData from "./modules/tokendata/TokenData";
|
||||
import translation from "./modules/translation/translation";
|
||||
|
||||
const ConferenceEvents = JitsiMeetJS.events.conference;
|
||||
|
||||
/**
|
||||
* Tries to push history state with the following parameters:
|
||||
* 'VideoChat', `Room: ${roomName}`, URL. If fail, prints the error and returns
|
||||
@@ -42,7 +49,7 @@ function pushHistoryState(roomName, URL) {
|
||||
'VideoChat', `Room: ${roomName}`, URL
|
||||
);
|
||||
} catch (e) {
|
||||
console.warn("Push history state failed with parameters:",
|
||||
logger.warn("Push history state failed with parameters:",
|
||||
'VideoChat', `Room: ${roomName}`, URL, e);
|
||||
return e;
|
||||
}
|
||||
@@ -78,6 +85,36 @@ function buildRoomName () {
|
||||
return roomName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the logging levels.
|
||||
* @private
|
||||
*/
|
||||
function configureLoggingLevels () {
|
||||
// NOTE The library Logger is separated from the app loggers, so the levels
|
||||
// have to be set in two places
|
||||
|
||||
// Set default logging level
|
||||
const defaultLogLevel
|
||||
= loggingConfig.defaultLogLevel || JitsiMeetJS.logLevels.TRACE;
|
||||
Logger.setLogLevel(defaultLogLevel);
|
||||
JitsiMeetJS.setLogLevel(defaultLogLevel);
|
||||
|
||||
// NOTE console was used on purpose here to go around the logging
|
||||
// and always print the default logging level to the console
|
||||
console.info("Default logging level set to: " + defaultLogLevel);
|
||||
|
||||
// Set log level for each logger
|
||||
if (loggingConfig) {
|
||||
Object.keys(loggingConfig).forEach(function(loggerName) {
|
||||
if ('defaultLogLevel' !== loggerName) {
|
||||
const level = loggingConfig[loggerName];
|
||||
Logger.setLogLevelById(level, loggerName);
|
||||
JitsiMeetJS.setLogLevelById(level, loggerName);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const APP = {
|
||||
// Used by do_external_connect.js if we receive the attach data after
|
||||
// connect was already executed. status property can be "initialized",
|
||||
@@ -97,6 +134,16 @@ const APP = {
|
||||
settings,
|
||||
conference,
|
||||
translation,
|
||||
/**
|
||||
* The log collector which captures JS console logs for this app.
|
||||
* @type {LogCollector}
|
||||
*/
|
||||
logCollector: null,
|
||||
/**
|
||||
* Indicates if the log collector has been started (it will not be started
|
||||
* if the welcome page is displayed).
|
||||
*/
|
||||
logCollectorStarted : false,
|
||||
/**
|
||||
* After the APP has been initialized provides utility methods for dealing
|
||||
* with the conference room URL(address).
|
||||
@@ -106,10 +153,24 @@ const APP = {
|
||||
connection: null,
|
||||
API,
|
||||
init () {
|
||||
this.initLogging();
|
||||
this.keyboardshortcut =
|
||||
require("./modules/keyboardshortcut/keyboardshortcut");
|
||||
this.configFetch = require("./modules/config/HttpConfigFetch");
|
||||
this.tokenData = getTokenData();
|
||||
},
|
||||
initLogging () {
|
||||
// Adjust logging level
|
||||
configureLoggingLevels();
|
||||
// Create the LogCollector and register it as the global log transport.
|
||||
// It is done early to capture as much logs as possible. Captured logs
|
||||
// will be cached, before the JitsiMeetLogStorage gets ready (statistics
|
||||
// module is initialized).
|
||||
if (!this.logCollector && !loggingConfig.disableLogCollector) {
|
||||
this.logCollector = new LogCollector(new JitsiMeetLogStorage());
|
||||
Logger.addGlobalTransport(this.logCollector);
|
||||
JitsiMeetJS.addGlobalLogTransport(this.logCollector);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -131,21 +192,60 @@ function init() {
|
||||
APP.ConferenceUrl = new ConferenceUrl(window.location);
|
||||
// Clean up the URL displayed by the browser
|
||||
replaceHistoryState(APP.ConferenceUrl.getInviteUrl());
|
||||
var isUIReady = APP.UI.start();
|
||||
|
||||
// TODO The execution of the mobile app starts from react/index.native.js.
|
||||
// Similarly, the execution of the Web app should start from
|
||||
// react/index.web.js for the sake of consistency and ease of understanding.
|
||||
// Temporarily though because we are at the beginning of introducing React
|
||||
// into the Web app, allow the execution of the Web app to start from app.js
|
||||
// in order to reduce the complexity of the beginning step.
|
||||
require('./react');
|
||||
|
||||
const isUIReady = APP.UI.start();
|
||||
if (isUIReady) {
|
||||
APP.conference.init({roomName: buildRoomName()}).then(function () {
|
||||
APP.conference.init({roomName: buildRoomName()}).then(() => {
|
||||
|
||||
if (APP.logCollector) {
|
||||
// Start the LogCollector's periodic "store logs" task only if
|
||||
// we're in the conference and not on the welcome page. This is
|
||||
// determined by the value of "isUIReady" const above.
|
||||
APP.logCollector.start();
|
||||
APP.logCollectorStarted = true;
|
||||
// Make an attempt to flush in case a lot of logs have been
|
||||
// cached, before the collector was started.
|
||||
APP.logCollector.flush();
|
||||
|
||||
// This event listener will flush the logs, before
|
||||
// the statistics module (CallStats) is stopped.
|
||||
//
|
||||
// NOTE The LogCollector is not stopped, because this event can
|
||||
// be triggered multiple times during single conference
|
||||
// (whenever statistics module is stopped). That includes
|
||||
// the case when Jicofo terminates the single person left in the
|
||||
// room. It will then restart the media session when someone
|
||||
// eventually join the room which will start the stats again.
|
||||
APP.conference.addConferenceListener(
|
||||
ConferenceEvents.BEFORE_STATISTICS_DISPOSED,
|
||||
() => {
|
||||
if (APP.logCollector) {
|
||||
APP.logCollector.flush();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
APP.UI.initConference();
|
||||
|
||||
APP.UI.addListener(UIEvents.LANG_CHANGED, function (language) {
|
||||
APP.UI.addListener(UIEvents.LANG_CHANGED, language => {
|
||||
APP.translation.setLanguage(language);
|
||||
APP.settings.setLanguage(language);
|
||||
});
|
||||
|
||||
APP.keyboardshortcut.init();
|
||||
}).catch(function (err) {
|
||||
}).catch(err => {
|
||||
APP.UI.hideRingOverLay();
|
||||
APP.API.notifyConferenceLeft(APP.conference.roomName);
|
||||
console.error(err);
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -169,7 +269,7 @@ function obtainConfigAndInit() {
|
||||
if (success) {
|
||||
var now = APP.connectionTimes["configuration.fetched"] =
|
||||
window.performance.now();
|
||||
console.log("(TIME) configuration fetched:\t", now);
|
||||
logger.log("(TIME) configuration fetched:\t", now);
|
||||
init();
|
||||
} else {
|
||||
// Show obtain config error,
|
||||
@@ -189,9 +289,10 @@ function obtainConfigAndInit() {
|
||||
|
||||
$(document).ready(function () {
|
||||
var now = APP.connectionTimes["document.ready"] = window.performance.now();
|
||||
console.log("(TIME) document ready:\t", now);
|
||||
logger.log("(TIME) document ready:\t", now);
|
||||
|
||||
URLProcessor.setConfigParametersFromUrl();
|
||||
|
||||
APP.init();
|
||||
|
||||
APP.translation.init(settings.getLanguage());
|
||||
@@ -202,6 +303,11 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
$(window).bind('beforeunload', function () {
|
||||
// Stop the LogCollector
|
||||
if (APP.logCollectorStarted) {
|
||||
APP.logCollector.stop();
|
||||
APP.logCollectorStarted = false;
|
||||
}
|
||||
APP.API.dispose();
|
||||
});
|
||||
|
||||
|
||||
146
conference.js
146
conference.js
@@ -1,4 +1,6 @@
|
||||
/* global $, APP, JitsiMeetJS, config, interfaceConfig */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import {openConnection} from './connection';
|
||||
import Invite from './modules/UI/invite/Invite';
|
||||
import ContactList from './modules/UI/side_pannels/contactlist/ContactList';
|
||||
@@ -13,6 +15,8 @@ import {reportError} from './modules/util/helpers';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
import UIUtil from './modules/UI/util/UIUtil';
|
||||
|
||||
import analytics from './modules/analytics/analytics';
|
||||
|
||||
const ConnectionEvents = JitsiMeetJS.events.connection;
|
||||
const ConnectionErrors = JitsiMeetJS.errors.connection;
|
||||
|
||||
@@ -164,7 +168,7 @@ function muteLocalMedia(localMedia, muted, localMediaTypeString) {
|
||||
const method = muted ? 'mute' : 'unmute';
|
||||
|
||||
localMedia[method]().catch(reason => {
|
||||
console.warn(`${localMediaTypeString} ${method} was rejected:`, reason);
|
||||
logger.warn(`${localMediaTypeString} ${method} was rejected:`, reason);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -252,7 +256,7 @@ function createLocalTracks (options, checkForPermissionPrompt) {
|
||||
});
|
||||
return tracks;
|
||||
}).catch(function (err) {
|
||||
console.error(
|
||||
logger.error(
|
||||
'failed to create local tracks', options.devices, err);
|
||||
return Promise.reject(err);
|
||||
});
|
||||
@@ -292,9 +296,10 @@ function changeLocalDisplayName(nickname = '') {
|
||||
}
|
||||
|
||||
class ConferenceConnector {
|
||||
constructor(resolve, reject) {
|
||||
constructor(resolve, reject, invite) {
|
||||
this._resolve = resolve;
|
||||
this._reject = reject;
|
||||
this._invite = invite;
|
||||
this.reconnectTimeout = null;
|
||||
room.on(ConferenceEvents.CONFERENCE_JOINED,
|
||||
this._handleConferenceJoined.bind(this));
|
||||
@@ -308,7 +313,7 @@ class ConferenceConnector {
|
||||
this._reject(err);
|
||||
}
|
||||
_onConferenceFailed(err, ...params) {
|
||||
console.error('CONFERENCE FAILED:', err, ...params);
|
||||
logger.error('CONFERENCE FAILED:', err, ...params);
|
||||
APP.UI.hideRingOverLay();
|
||||
switch (err) {
|
||||
// room is locked by the password
|
||||
@@ -338,7 +343,8 @@ class ConferenceConnector {
|
||||
}, 5000);
|
||||
|
||||
// notify user that auth is required
|
||||
AuthHandler.requireAuth(room, this.invite.getRoomLocker().password);
|
||||
AuthHandler.requireAuth(
|
||||
room, this._invite.getRoomLocker().password);
|
||||
break;
|
||||
|
||||
case ConferenceErrors.RESERVATION_ERROR:
|
||||
@@ -381,7 +387,8 @@ class ConferenceConnector {
|
||||
// the app. Both the errors above are unrecoverable from the library
|
||||
// perspective.
|
||||
room.leave().then(() => connection.disconnect());
|
||||
APP.UI.showPageReloadOverlay();
|
||||
APP.UI.showPageReloadOverlay(
|
||||
false /* not a network type of failure */, err);
|
||||
break;
|
||||
|
||||
case ConferenceErrors.CONFERENCE_MAX_USERS:
|
||||
@@ -396,7 +403,7 @@ class ConferenceConnector {
|
||||
}
|
||||
}
|
||||
_onConferenceError(err, ...params) {
|
||||
console.error('CONFERENCE Error:', err, params);
|
||||
logger.error('CONFERENCE Error:', err, params);
|
||||
switch (err) {
|
||||
case ConferenceErrors.CHAT_ERROR:
|
||||
{
|
||||
@@ -405,7 +412,7 @@ class ConferenceConnector {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.error("Unknown error.");
|
||||
logger.error("Unknown error.", err);
|
||||
}
|
||||
}
|
||||
_unsubscribe() {
|
||||
@@ -438,26 +445,6 @@ function disconnect() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set permanent properties to analytics.
|
||||
* NOTE: Has to be used after JitsiMeetJS.init. Otherwise analytics will be
|
||||
* null.
|
||||
*/
|
||||
function setAnalyticsPermanentProperties() {
|
||||
let permanentProperties = {
|
||||
userAgent: navigator.userAgent,
|
||||
roomName: APP.conference.roomName
|
||||
};
|
||||
let {server, group} = APP.tokenData;
|
||||
if(server) {
|
||||
permanentProperties.server = server;
|
||||
}
|
||||
if(group) {
|
||||
permanentProperties.group = group;
|
||||
}
|
||||
JitsiMeetJS.analytics.addPermanentProperties(permanentProperties);
|
||||
}
|
||||
|
||||
export default {
|
||||
isModerator: false,
|
||||
audioMuted: false,
|
||||
@@ -480,8 +467,6 @@ export default {
|
||||
*/
|
||||
init(options) {
|
||||
this.roomName = options.roomName;
|
||||
JitsiMeetJS.setLogLevel(JitsiMeetJS.logLevels.TRACE);
|
||||
|
||||
// attaches global error handler, if there is already one, respect it
|
||||
if(JitsiMeetJS.getGlobalOnErrorHandler){
|
||||
var oldOnErrorHandler = window.onerror;
|
||||
@@ -504,12 +489,14 @@ export default {
|
||||
};
|
||||
}
|
||||
|
||||
return JitsiMeetJS.init(config)
|
||||
.then(() => {
|
||||
setAnalyticsPermanentProperties();
|
||||
return JitsiMeetJS.init(
|
||||
Object.assign(
|
||||
{enableAnalyticsLogging: analytics.isEnabled()}, config)
|
||||
).then(() => {
|
||||
analytics.init();
|
||||
return createInitialLocalTracksAndConnect(options.roomName);
|
||||
}).then(([tracks, con]) => {
|
||||
console.log('initialized with %s local tracks', tracks.length);
|
||||
logger.log('initialized with %s local tracks', tracks.length);
|
||||
APP.connection = connection = con;
|
||||
this._bindConnectionFailedHandler(con);
|
||||
this._createRoom(tracks);
|
||||
@@ -537,7 +524,8 @@ export default {
|
||||
// XXX The API will take care of disconnecting from the XMPP
|
||||
// server (and, thus, leaving the room) on unload.
|
||||
return new Promise((resolve, reject) => {
|
||||
(new ConferenceConnector(resolve, reject)).connect();
|
||||
(new ConferenceConnector(
|
||||
resolve, reject, this.invite)).connect();
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -558,19 +546,34 @@ export default {
|
||||
*/
|
||||
_bindConnectionFailedHandler (connection) {
|
||||
const handler = function (error, errMsg) {
|
||||
if (ConnectionErrors.OTHER_ERROR === error) {
|
||||
// - item-not-found
|
||||
// - connection dropped(closed by Strophe unexpectedly
|
||||
// possible due too many transport errors)
|
||||
console.error("XMPP connection error: " + errMsg);
|
||||
APP.UI.showPageReloadOverlay();
|
||||
connection.removeEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED, handler);
|
||||
// FIXME it feels like the conference should be stopped
|
||||
// by lib-jitsi-meet
|
||||
if (room)
|
||||
room.leave();
|
||||
/* eslint-disable no-case-declarations */
|
||||
switch (error) {
|
||||
case ConnectionErrors.CONNECTION_DROPPED_ERROR:
|
||||
case ConnectionErrors.OTHER_ERROR:
|
||||
case ConnectionErrors.SERVER_ERROR:
|
||||
|
||||
logger.error("XMPP connection error: " + errMsg);
|
||||
|
||||
// From all of the cases above only CONNECTION_DROPPED_ERROR
|
||||
// is considered a network type of failure
|
||||
const isNetworkFailure
|
||||
= error === ConnectionErrors.CONNECTION_DROPPED_ERROR;
|
||||
|
||||
APP.UI.showPageReloadOverlay(
|
||||
isNetworkFailure,
|
||||
"xmpp-conn-dropped:" + errMsg);
|
||||
|
||||
connection.removeEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED, handler);
|
||||
|
||||
// FIXME it feels like the conference should be stopped
|
||||
// by lib-jitsi-meet
|
||||
if (room)
|
||||
room.leave();
|
||||
|
||||
break;
|
||||
}
|
||||
/* eslint-enable no-case-declarations */
|
||||
};
|
||||
connection.addEventListener(
|
||||
ConnectionEvents.CONNECTION_FAILED, handler);
|
||||
@@ -892,7 +895,7 @@ export default {
|
||||
} else if (track.isVideoTrack()) {
|
||||
return this.useVideoStream(track);
|
||||
} else {
|
||||
console.error(
|
||||
logger.error(
|
||||
"Ignored not an audio nor a video track: ", track);
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -984,11 +987,11 @@ export default {
|
||||
videoSwitchInProgress: false,
|
||||
toggleScreenSharing (shareScreen = !this.isSharingScreen) {
|
||||
if (this.videoSwitchInProgress) {
|
||||
console.warn("Switch in progress.");
|
||||
logger.warn("Switch in progress.");
|
||||
return;
|
||||
}
|
||||
if (!this.isDesktopSharingEnabled) {
|
||||
console.warn("Cannot toggle screen sharing: not supported.");
|
||||
logger.warn("Cannot toggle screen sharing: not supported.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1042,7 +1045,7 @@ export default {
|
||||
this.videoSwitchInProgress = false;
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'conference.sharingDesktop.start');
|
||||
console.log('sharing local desktop');
|
||||
logger.log('sharing local desktop');
|
||||
}).catch((err) => {
|
||||
// close external installation dialog to show the error.
|
||||
if(externalInstallation)
|
||||
@@ -1054,7 +1057,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('failed to share local desktop', err);
|
||||
logger.error('failed to share local desktop', err);
|
||||
|
||||
if (err.name === TrackErrors.FIREFOX_EXTENSION_NEEDED) {
|
||||
APP.UI.showExtensionRequiredDialog(
|
||||
@@ -1091,11 +1094,11 @@ export default {
|
||||
this.videoSwitchInProgress = false;
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'conference.sharingDesktop.stop');
|
||||
console.log('sharing local video');
|
||||
logger.log('sharing local video');
|
||||
}).catch((err) => {
|
||||
this.useVideoStream(null);
|
||||
this.videoSwitchInProgress = false;
|
||||
console.error('failed to share local video', err);
|
||||
logger.error('failed to share local video', err);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -1121,7 +1124,7 @@ export default {
|
||||
if (user.isHidden())
|
||||
return;
|
||||
|
||||
console.log('USER %s connnected', id, user);
|
||||
logger.log('USER %s connnected', id, user);
|
||||
APP.API.notifyUserJoined(id);
|
||||
APP.UI.addUser(user);
|
||||
|
||||
@@ -1129,7 +1132,7 @@ export default {
|
||||
APP.UI.updateUserRole(user);
|
||||
});
|
||||
room.on(ConferenceEvents.USER_LEFT, (id, user) => {
|
||||
console.log('USER %s LEFT', id, user);
|
||||
logger.log('USER %s LEFT', id, user);
|
||||
APP.API.notifyUserLeft(id);
|
||||
APP.UI.removeUser(id, user.getDisplayName());
|
||||
APP.UI.onSharedVideoStop(id);
|
||||
@@ -1138,7 +1141,7 @@ export default {
|
||||
|
||||
room.on(ConferenceEvents.USER_ROLE_CHANGED, (id, role) => {
|
||||
if (this.isLocalId(id)) {
|
||||
console.info(`My role changed, new role: ${role}`);
|
||||
logger.info(`My role changed, new role: ${role}`);
|
||||
if (this.isModerator !== room.isModerator()) {
|
||||
this.isModerator = room.isModerator();
|
||||
APP.UI.updateLocalRole(room.isModerator());
|
||||
@@ -1196,7 +1199,7 @@ export default {
|
||||
{
|
||||
this.audioLevelsMap[id] = lvl;
|
||||
if(config.debugAudioLevels)
|
||||
console.log("AudioLevel:" + id + "/" + lvl);
|
||||
logger.log("AudioLevel:" + id + "/" + lvl);
|
||||
}
|
||||
|
||||
APP.UI.setAudioLevel(id, lvl);
|
||||
@@ -1277,7 +1280,7 @@ export default {
|
||||
});
|
||||
|
||||
room.on(ConferenceEvents.RECORDER_STATE_CHANGED, (status, error) => {
|
||||
console.log("Received recorder status change: ", status, error);
|
||||
logger.log("Received recorder status change: ", status, error);
|
||||
APP.UI.updateRecordingState(status);
|
||||
});
|
||||
|
||||
@@ -1513,7 +1516,7 @@ export default {
|
||||
})
|
||||
.then(([stream]) => {
|
||||
this.useVideoStream(stream);
|
||||
console.log('switched local video device');
|
||||
logger.log('switched local video device');
|
||||
APP.settings.setCameraDeviceId(cameraDeviceId, true);
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -1535,7 +1538,7 @@ export default {
|
||||
})
|
||||
.then(([stream]) => {
|
||||
this.useAudioStream(stream);
|
||||
console.log('switched local audio device');
|
||||
logger.log('switched local audio device');
|
||||
APP.settings.setMicDeviceId(micDeviceId, true);
|
||||
})
|
||||
.catch((err) => {
|
||||
@@ -1551,9 +1554,9 @@ export default {
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'settings.changeDevice.audioOut');
|
||||
APP.settings.setAudioOutputDeviceId(audioOutputDeviceId)
|
||||
.then(() => console.log('changed audio output device'))
|
||||
.then(() => logger.log('changed audio output device'))
|
||||
.catch((err) => {
|
||||
console.warn('Failed to change audio output device. ' +
|
||||
logger.warn('Failed to change audio output device. ' +
|
||||
'Default or previously set audio output device ' +
|
||||
'will be used instead.', err);
|
||||
APP.UI.setSelectedAudioOutputFromSettings();
|
||||
@@ -1749,14 +1752,25 @@ export default {
|
||||
* @param {string} name the event name
|
||||
* @param {int} value the value (it's int because google analytics supports
|
||||
* only int).
|
||||
* @param {string} label short text which provides more info about the event
|
||||
* which allows to distinguish between few event cases of the same name
|
||||
* NOTE: Should be used after conference.init
|
||||
*/
|
||||
logEvent(name, value) {
|
||||
logEvent(name, value, label) {
|
||||
if(JitsiMeetJS.analytics) {
|
||||
JitsiMeetJS.analytics.sendEvent(name, {value});
|
||||
JitsiMeetJS.analytics.sendEvent(name, {value, label});
|
||||
}
|
||||
if(room) {
|
||||
room.sendApplicationLog(JSON.stringify({name, value}));
|
||||
room.sendApplicationLog(JSON.stringify({name, value, label}));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Methods logs an application event given in the JSON format.
|
||||
* @param {string} logJSON an event to be logged in JSON format
|
||||
*/
|
||||
logJSON(logJSON) {
|
||||
if (room) {
|
||||
room.sendApplicationLog(logJSON);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
||||
12
config.js
12
config.js
@@ -26,7 +26,7 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
desktopSharingChromeExtId: 'diibjkoicjeejcmhdnailmkgecihlobk',
|
||||
// The media sources to use when using screen sharing with the Chrome
|
||||
// extension.
|
||||
desktopSharingChromeSources: ['screen', 'window'],
|
||||
desktopSharingChromeSources: ['screen', 'window', 'tab'],
|
||||
// Required version of Chrome extension
|
||||
desktopSharingChromeMinExtVersion: '0.1',
|
||||
|
||||
@@ -78,3 +78,13 @@ var config = { // eslint-disable-line no-unused-vars
|
||||
// edit their profile.
|
||||
enableUserRolesBasedOnToken: false
|
||||
};
|
||||
|
||||
// Logging configuration
|
||||
var loggingConfig = { // eslint-disable-line no-unused-vars
|
||||
//default log level for the app and lib-jitsi-meet
|
||||
defaultLogLevel: 'trace',
|
||||
// Option to disable LogCollector (which stores the logs on CallStats)
|
||||
//disableLogCollector: true,
|
||||
// Logging level adjustments for verbose modules:
|
||||
'modules/xmpp/strophe.util.js': 'log'
|
||||
};
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* global APP, JitsiMeetJS, config */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import jitsiLocalStorage from './modules/util/JitsiLocalStorage';
|
||||
|
||||
@@ -84,7 +86,7 @@ function connect(id, password, roomName) {
|
||||
|
||||
function handleConnectionFailed(err) {
|
||||
unsubscribe();
|
||||
console.error("CONNECTION FAILED:", err);
|
||||
logger.error("CONNECTION FAILED:", err);
|
||||
reject(err);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@include border-radius(3px);
|
||||
padding: 40px 38px 44px;
|
||||
color: #fff;
|
||||
background: lighten(desaturate($defaultBackground, 70%), 20%);
|
||||
background: $inlayColorBg;
|
||||
text-align: center;
|
||||
|
||||
&__title {
|
||||
@@ -12,7 +12,7 @@
|
||||
color: $popoverFontColor;
|
||||
font-size: 21px;
|
||||
letter-spacing: 0.3px;
|
||||
border-bottom: 1px solid $auiBorderColor;
|
||||
border-bottom: 1px solid $inlayBorderColor;
|
||||
}
|
||||
|
||||
&__text {
|
||||
|
||||
@@ -14,13 +14,13 @@
|
||||
/*-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);*/
|
||||
/*box-shadow: 0 5px 10px rgba(0, 0, 0, 0.4);*/
|
||||
white-space: normal;
|
||||
margin-top: -10px;
|
||||
margin-top: -$popoverMenuPadding;
|
||||
|
||||
&__menu-padding {
|
||||
height: 10px;
|
||||
height: $popoverMenuPadding;
|
||||
width: 100px;
|
||||
position: absolute;
|
||||
bottom: -10px;
|
||||
bottom: -$popoverMenuPadding;
|
||||
}
|
||||
|
||||
&__showmore {
|
||||
|
||||
@@ -60,6 +60,10 @@
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
|
||||
.input-control {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Titles and subtitles of inner containers.
|
||||
*/
|
||||
|
||||
@@ -77,7 +77,6 @@ $rateStarLabelColor: #333;
|
||||
$rateStarDefault: #ccc;
|
||||
$rateStarActivity: #165ecc;
|
||||
$rateStarSize: 34px;
|
||||
$feedbackCancelFontColor: #333;
|
||||
|
||||
/**
|
||||
* Notifications
|
||||
@@ -99,7 +98,7 @@ $notificationWidth: 215px;
|
||||
$borderRadius: 3px;
|
||||
$defaultWatermarkLink: '../images/watermark.png';
|
||||
$sidebarWidth: 220px;
|
||||
|
||||
$popoverMenuPadding: 13px;
|
||||
$happySoftwareBackground: transparent;
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
visibility: visible;
|
||||
height: auto;
|
||||
|
||||
h3 {
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: $auiDialogColor;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,20 +62,18 @@
|
||||
text-align: center;
|
||||
padding: 10px 40px 20px 40px;
|
||||
|
||||
.form-control{
|
||||
&__input {
|
||||
background-color: $feedbackInputBg;
|
||||
color: $feedbackInputTextColor;
|
||||
.input-control {
|
||||
background-color: $feedbackInputBg;
|
||||
color: $feedbackInputTextColor;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: $feedbackInputPlaceholderColor;
|
||||
}
|
||||
&::-moz-placeholder { /* Firefox 19+ */
|
||||
color: $feedbackInputPlaceholderColor;
|
||||
}
|
||||
&:-ms-input-placeholder {
|
||||
color: $feedbackInputPlaceholderColor;
|
||||
}
|
||||
&::-webkit-input-placeholder {
|
||||
color: $feedbackInputPlaceholderColor;
|
||||
}
|
||||
&::-moz-placeholder { /* Firefox 19+ */
|
||||
color: $feedbackInputPlaceholderColor;
|
||||
}
|
||||
&:-ms-input-placeholder {
|
||||
color: $feedbackInputPlaceholderColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,5 +13,4 @@
|
||||
#reloadProgressBar {
|
||||
width: 180px;
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -53,6 +53,12 @@ $auiDialogContentBg: $baseLight;
|
||||
$auiBorderColor: #ccc;
|
||||
$dialogTitleFontWeight: 400;
|
||||
|
||||
/**
|
||||
* Inlay colors
|
||||
**/
|
||||
$inlayColorBg: lighten($defaultBackground, 20%);
|
||||
$inlayBorderColor: lighten($auiDialogContentBg, 10%);
|
||||
|
||||
// Main controls
|
||||
$inputBackground: $controlBackground;
|
||||
$inputBorderColor: #ccc;
|
||||
@@ -68,6 +74,9 @@ $linkHoverFontColor: darken(#3572b0, 10%);
|
||||
$dropdownColor: #333;
|
||||
$errorColor: #c61600;
|
||||
|
||||
// Feedback colors
|
||||
$feedbackCancelFontColor: #333;
|
||||
|
||||
// Popover colors
|
||||
$popoverBg: #000;
|
||||
$popoverFontColor: #ffffff;
|
||||
|
||||
2
debian/changelog
vendored
2
debian/changelog
vendored
@@ -1,4 +1,4 @@
|
||||
jitsi-meet (1.0.1-1) unstable; urgency=low
|
||||
jitsi-meet-web (1.0.1-1) unstable; urgency=low
|
||||
|
||||
* Initial release. (Closes: #760485)
|
||||
|
||||
|
||||
26
debian/control
vendored
26
debian/control
vendored
@@ -1,4 +1,4 @@
|
||||
Source: jitsi-meet
|
||||
Source: jitsi-meet-web
|
||||
Section: net
|
||||
Priority: extra
|
||||
Maintainer: Jitsi Team <dev@jitsi.org>
|
||||
@@ -7,10 +7,10 @@ Build-Depends: debhelper (>= 8.0.0)
|
||||
Standards-Version: 3.9.6
|
||||
Homepage: https://jitsi.org/meet
|
||||
|
||||
Package: jitsi-meet
|
||||
Package: jitsi-meet-web
|
||||
Replaces: jitsi-meet (<= 1.0.1525-1)
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, jitsi-videobridge, jitsi-meet-prosody,
|
||||
openjdk-8-jre-headless | nginx
|
||||
Depends: ${misc:Depends}
|
||||
Description: WebRTC JavaScript video conferences
|
||||
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
|
||||
Videobridge to provide high quality, scalable video conferences.
|
||||
@@ -19,9 +19,25 @@ Description: WebRTC JavaScript video conferences
|
||||
forwarding and relaying, configured to work with jetty instance
|
||||
running embedded into Jitsi Videobridge
|
||||
|
||||
Package: jitsi-meet-web-config
|
||||
Architecture: all
|
||||
Depends: openssl, openjdk-8-jre-headless | nginx | apache2,
|
||||
jitsi-meet-web
|
||||
Description: Configuration for web serving of Jitsi Meet
|
||||
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
|
||||
Videobridge to provide high quality, scalable video conferences.
|
||||
.
|
||||
It is a web interface to Jitsi Videobridge for audio and video
|
||||
forwarding and relaying, configured to work with jetty instance
|
||||
running embedded into Jitsi Videobridge or using a webserver Nginx or
|
||||
Apache2.
|
||||
.
|
||||
This package contains configuration for Nginx to be used with
|
||||
Jitsi Meet.
|
||||
|
||||
Package: jitsi-meet-prosody
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, openssl, prosody | prosody-trunk, jitsi-videobridge, jicofo
|
||||
Depends: openssl, prosody | prosody-trunk, jitsi-meet-web
|
||||
Description: Prosody configuration for Jitsi Meet
|
||||
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
|
||||
Videobridge to provide high quality, scalable video conferences.
|
||||
|
||||
4
debian/jitsi-meet-prosody.config
vendored
4
debian/jitsi-meet-prosody.config
vendored
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
# Source debconf library.
|
||||
. /usr/share/debconf/confmodule
|
||||
62
debian/jitsi-meet-prosody.postinst
vendored
62
debian/jitsi-meet-prosody.postinst
vendored
@@ -21,13 +21,49 @@ set -e
|
||||
case "$1" in
|
||||
configure)
|
||||
|
||||
. /etc/jitsi/videobridge/config
|
||||
|
||||
. /etc/jitsi/jicofo/config
|
||||
|
||||
# loading debconf
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
# try to get host from jitsi-videobridge
|
||||
db_get jitsi-videobridge/jvb-hostname
|
||||
if [ -z "$RET" ] ; then
|
||||
# server hostname
|
||||
db_set jitsi-videobridge/jvb-hostname "localhost"
|
||||
db_input critical jitsi-videobridge/jvb-hostname || true
|
||||
db_go
|
||||
fi
|
||||
JVB_HOSTNAME="$RET"
|
||||
|
||||
db_get jitsi-videobridge/jvbsecret
|
||||
if [ -z "$RET" ] ; then
|
||||
db_input critical jitsi-videobridge/jvbsecret || true
|
||||
db_go
|
||||
fi
|
||||
JVB_SECRET="$RET"
|
||||
|
||||
db_get jicofo/jicofo-authuser
|
||||
if [ -z "$RET" ] ; then
|
||||
db_input critical jicofo/jicofo-authuser || true
|
||||
db_go
|
||||
fi
|
||||
JICOFO_AUTH_USER="$RET"
|
||||
|
||||
db_get jicofo/jicofo-authpassword
|
||||
if [ -z "$RET" ] ; then
|
||||
db_input critical jicofo/jicofo-authpassword || true
|
||||
db_go
|
||||
fi
|
||||
JICOFO_AUTH_PASSWORD="$RET"
|
||||
|
||||
db_get jicofo/jicofosecret
|
||||
if [ -z "$RET" ] ; then
|
||||
db_input critical jicofo/jicofosecret || true
|
||||
db_go
|
||||
fi
|
||||
JICOFO_SECRET="$RET"
|
||||
|
||||
JICOFO_AUTH_DOMAIN="auth.$JVB_HOSTNAME"
|
||||
|
||||
# detect dpkg-reconfigure, just delete old links
|
||||
db_get jitsi-meet-prosody/jvb-hostname
|
||||
JVB_HOSTNAME_OLD=$RET
|
||||
@@ -38,7 +74,7 @@ case "$1" in
|
||||
fi
|
||||
|
||||
# stores the hostname so we will reuse it later, like in purge
|
||||
db_set jitsi-meet-prosody/jvb-hostname $JVB_HOSTNAME
|
||||
db_set jitsi-meet-prosody/jvb-hostname "$JVB_HOSTNAME"
|
||||
|
||||
# and we're done with debconf
|
||||
db_stop
|
||||
@@ -68,21 +104,21 @@ case "$1" in
|
||||
fi
|
||||
fi
|
||||
# UPGRADE to server side focus check if focus is configured
|
||||
if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "VirtualHost \"auth.$JVB_HOSTNAME\"" $PROSODY_HOST_CONFIG; then
|
||||
echo -e "\nVirtualHost \"auth.$JVB_HOSTNAME\"" >> $PROSODY_HOST_CONFIG
|
||||
if [ -f $PROSODY_HOST_CONFIG ] && ! grep -q "VirtualHost \"$JICOFO_AUTH_DOMAIN\"" $PROSODY_HOST_CONFIG; then
|
||||
echo -e "\nVirtualHost \"$JICOFO_AUTH_DOMAIN\"" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " authentication = \"internal_plain\"\n" >> $PROSODY_HOST_CONFIG
|
||||
sed -i "s/Component \"conference.$JVB_HOSTNAME\" \"muc\"/Component \"conference.$JVB_HOSTNAME\" \"muc\"\nadmins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\" }\n/g" $PROSODY_HOST_CONFIG
|
||||
sed -i "s/Component \"conference.$JVB_HOSTNAME\" \"muc\"/Component \"conference.$JVB_HOSTNAME\" \"muc\"\nadmins = { \"$JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN\" }\n/g" $PROSODY_HOST_CONFIG
|
||||
echo -e "Component \"focus.$JVB_HOSTNAME\"" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " component_secret=\"$JICOFO_SECRET\"\n" >> $PROSODY_HOST_CONFIG
|
||||
PROSODY_CREATE_JICOFO_USER="true"
|
||||
# UPGRADE to server side focus on old config(/etc/prosody/prosody.cfg.lua)
|
||||
elif [ ! -f $PROSODY_HOST_CONFIG ] && ! grep -q "VirtualHost \"auth.$JVB_HOSTNAME\"" $PROSODY_CONFIG_OLD; then
|
||||
echo -e "\nVirtualHost \"auth.$JVB_HOSTNAME\"" >> $PROSODY_CONFIG_OLD
|
||||
elif [ ! -f $PROSODY_HOST_CONFIG ] && ! grep -q "VirtualHost \"$JICOFO_AUTH_DOMAIN\"" $PROSODY_CONFIG_OLD; then
|
||||
echo -e "\nVirtualHost \"$JICOFO_AUTH_DOMAIN\"" >> $PROSODY_CONFIG_OLD
|
||||
echo -e " authentication = \"internal_plain\"\n" >> $PROSODY_CONFIG_OLD
|
||||
if ! grep -q "admins = { }" $PROSODY_CONFIG_OLD; then
|
||||
echo -e "admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\" }\n" >> $PROSODY_CONFIG_OLD
|
||||
echo -e "admins = { \"$JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN\" }\n" >> $PROSODY_CONFIG_OLD
|
||||
else
|
||||
sed -i "s/admins = { }/admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\" }\n/g" $PROSODY_CONFIG_OLD
|
||||
sed -i "s/admins = { }/admins = { \"$JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN\" }\n/g" $PROSODY_CONFIG_OLD
|
||||
fi
|
||||
echo -e "Component \"focus.$JVB_HOSTNAME\"" >> $PROSODY_CONFIG_OLD
|
||||
echo -e " component_secret=\"$JICOFO_SECRET\"\n" >> $PROSODY_CONFIG_OLD
|
||||
@@ -109,8 +145,6 @@ case "$1" in
|
||||
|
||||
if [ "$PROSODY_CONFIG_PRESENT" = "false" ]; then
|
||||
invoke-rc.d prosody restart
|
||||
invoke-rc.d jitsi-videobridge restart
|
||||
invoke-rc.d jicofo restart
|
||||
fi
|
||||
;;
|
||||
|
||||
|
||||
3
debian/jitsi-meet-prosody.postrm
vendored
3
debian/jitsi-meet-prosody.postrm
vendored
@@ -36,6 +36,9 @@ case "$1" in
|
||||
rm -f /etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua
|
||||
rm -f /etc/prosody/conf.d/$JVB_HOSTNAME.cfg.lua
|
||||
fi
|
||||
|
||||
# Clear the debconf variable
|
||||
db_purge
|
||||
;;
|
||||
|
||||
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
|
||||
|
||||
26
debian/jitsi-meet-prosody.templates
vendored
26
debian/jitsi-meet-prosody.templates
vendored
@@ -2,3 +2,29 @@ Template: jitsi-meet-prosody/jvb-hostname
|
||||
Type: string
|
||||
_Description: The hostname of the current installation:
|
||||
The value for the hostname that is set in Jitsi Videobridge installation.
|
||||
|
||||
Template: jitsi-videobridge/jvb-hostname
|
||||
Type: string
|
||||
_Description: The hostname of the current installation:
|
||||
The value for the hostname that is set in Jitsi Videobridge installation.
|
||||
|
||||
Template: jitsi-videobridge/jvbsecret
|
||||
Type: password
|
||||
_Description: Jitsi Videobridge Component secret:
|
||||
The secret used by Jitsi Videobridge to connect to xmpp server as component.
|
||||
|
||||
Template: jicofo/jicofo-authuser
|
||||
Type: string
|
||||
Default: focus
|
||||
_Description: Jicofo username:
|
||||
The jicofo needs an authenticated admin user to connect to xmpp server.
|
||||
|
||||
Template: jicofo/jicofo-authpassword
|
||||
Type: password
|
||||
_Description: Jicofo user password:
|
||||
The secret used to connect to xmpp server as jicofo user.
|
||||
|
||||
Template: jicofo/jicofosecret
|
||||
Type: password
|
||||
_Description: Jicofo Component secret:
|
||||
The secret used to connect to xmpp server as component
|
||||
|
||||
17
debian/jitsi-meet-tokens.postinst
vendored
17
debian/jitsi-meet-tokens.postinst
vendored
@@ -21,17 +21,12 @@ set -e
|
||||
case "$1" in
|
||||
configure)
|
||||
|
||||
if [ -f "/etc/jitsi/videobridge/config" ] ; then
|
||||
. /etc/jitsi/videobridge/config
|
||||
fi
|
||||
|
||||
if [ -f "/etc/jitsi/jicofo/config" ] ; then
|
||||
. /etc/jitsi/jicofo/config
|
||||
fi
|
||||
|
||||
# loading debconf
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
db_get jitsi-meet-prosody/jvb-hostname
|
||||
JVB_HOSTNAME="$RET"
|
||||
|
||||
db_get jitsi-meet-tokens/appid
|
||||
if [ "$RET" = "false" ] ; then
|
||||
echo "Application ID is mandatory"
|
||||
@@ -45,14 +40,10 @@ case "$1" in
|
||||
fi
|
||||
APP_SECRET=$RET
|
||||
|
||||
# We can adjust Prosody config only if there is Jvb or Jicofo domain configured
|
||||
PROSODY_HOST_CONFIG="/etc/prosody/conf.avail/$JVB_HOSTNAME.cfg.lua"
|
||||
if [ ! -f "$PROSODY_HOST_CONFIG" ] ; then
|
||||
PROSODY_HOST_CONFIG="/etc/prosody/conf.avail/$JICOFO_HOSTNAME.cfg.lua"
|
||||
fi
|
||||
|
||||
# Store config filename for purge
|
||||
db_set jitsi-meet-prosody/prosody_config $PROSODY_HOST_CONFIG
|
||||
db_set jitsi-meet-prosody/prosody_config "$PROSODY_HOST_CONFIG"
|
||||
|
||||
db_stop
|
||||
|
||||
|
||||
2
debian/jitsi-meet-tokens.postrm
vendored
2
debian/jitsi-meet-tokens.postrm
vendored
@@ -52,6 +52,8 @@ case "$1" in
|
||||
;;
|
||||
|
||||
purge)
|
||||
# Clear the debconf variable
|
||||
db_purge
|
||||
;;
|
||||
|
||||
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# postinst script for jitsi-meet
|
||||
# postinst script for jitsi-meet-web-config
|
||||
#
|
||||
# see: dh_installdeb(1)
|
||||
|
||||
@@ -20,13 +20,19 @@ set -e
|
||||
case "$1" in
|
||||
configure)
|
||||
|
||||
JVB_ETC_CONFIG="/etc/jitsi/videobridge/config"
|
||||
|
||||
. $JVB_ETC_CONFIG
|
||||
|
||||
# loading debconf
|
||||
. /usr/share/debconf/confmodule
|
||||
|
||||
# try to get host from jitsi-videobridge
|
||||
db_get jitsi-videobridge/jvb-hostname
|
||||
if [ -z "$RET" ] ; then
|
||||
# server hostname
|
||||
db_set jitsi-videobridge/jvb-hostname "localhost"
|
||||
db_input critical jitsi-videobridge/jvb-hostname || true
|
||||
db_go
|
||||
fi
|
||||
JVB_HOSTNAME="$RET"
|
||||
|
||||
# detect dpkg-reconfigure
|
||||
RECONFIGURING="false"
|
||||
db_get jitsi-meet/jvb-hostname
|
||||
@@ -49,22 +55,54 @@ case "$1" in
|
||||
if [ "$NGINX_INSTALL_CHECK" = "installed" ] || [ "$NGINX_INSTALL_CHECK" = "unpacked" ] ; then
|
||||
FORCE_NGINX="true"
|
||||
fi
|
||||
APACHE_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'apache2' 2>/dev/null | awk '{print $3}' || true)"
|
||||
if [ "$APACHE_INSTALL_CHECK" = "installed" ] || [ "$APACHE_INSTALL_CHECK" = "unpacked" ] ; then
|
||||
FORCE_APACHE="true"
|
||||
fi
|
||||
|
||||
# SSL for nginx
|
||||
db_get jitsi-meet/cert-choice
|
||||
CERT_CHOICE="$RET"
|
||||
UPLOADED_CERT_CHOICE="A certificate is available and the files are uploaded on the server"
|
||||
# if first time config ask for certs, or if we are reconfiguring
|
||||
if [ -z "$JVB_HOSTNAME_OLD" ] || [ "$RECONFIGURING" = "true" ] ; then
|
||||
# SSL for nginx
|
||||
db_get jitsi-meet/cert-choice
|
||||
CERT_CHOICE="$RET"
|
||||
UPLOADED_CERT_CHOICE="A certificate is available and the files are uploaded on the server"
|
||||
|
||||
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
|
||||
db_set jitsi-meet/cert-path-key "/etc/ssl/$JVB_HOSTNAME.key"
|
||||
db_input critical jitsi-meet/cert-path-key || true
|
||||
db_go
|
||||
db_get jitsi-meet/cert-path-key
|
||||
CERT_KEY="$RET"
|
||||
db_set jitsi-meet/cert-path-crt "/etc/ssl/$JVB_HOSTNAME.crt"
|
||||
db_input critical jitsi-meet/cert-path-crt || true
|
||||
db_go
|
||||
db_get jitsi-meet/cert-path-crt
|
||||
CERT_CRT="$RET"
|
||||
else
|
||||
# create self-signed certs
|
||||
CERT_KEY="/etc/jitsi/meet/$JVB_HOSTNAME.key"
|
||||
CERT_CRT="/etc/jitsi/meet/$JVB_HOSTNAME.crt"
|
||||
HOST="$( (hostname -s; echo localhost) | head -n 1)"
|
||||
DOMAIN="$( (hostname -d; echo localdomain) | head -n 1)"
|
||||
openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj \
|
||||
"/O=$DOMAIN/OU=$HOST/CN=$JVB_HOSTNAME/emailAddress=webmaster@$HOST.$DOMAIN" \
|
||||
-keyout $CERT_KEY \
|
||||
-out $CERT_CRT
|
||||
fi
|
||||
fi
|
||||
|
||||
# jitsi meet
|
||||
JITSI_MEET_CONFIG="/etc/jitsi/meet/$JVB_HOSTNAME-config.js"
|
||||
if [ ! -f $JITSI_MEET_CONFIG ] ; then
|
||||
cp /usr/share/doc/jitsi-meet/config.js $JITSI_MEET_CONFIG
|
||||
cp /usr/share/doc/jitsi-meet-web/config.js $JITSI_MEET_CONFIG
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" $JITSI_MEET_CONFIG
|
||||
fi
|
||||
|
||||
# this is new install let's configure jvb to serve meet
|
||||
if [[ -z $FORCE_NGINX && ( -z $JVB_HOSTNAME_OLD || "$JVB_SERVE" = "true" ) ]] ; then
|
||||
# no-nginx, no-apache installed on machine, this is new install or reconfiguring old one which have jvb_serve set
|
||||
if [[ -z "$FORCE_NGINX" && -z "$FORCE_APACHE" && ( -z "$JVB_HOSTNAME_OLD" || ( "$JVB_SERVE" = "true" && "$RECONFIGURING" = "true" )) ]] ; then
|
||||
|
||||
JVB_ETC_CONFIG="/etc/jitsi/videobridge/config"
|
||||
JVB_CONFIG="/etc/jitsi/videobridge/sip-communicator.properties"
|
||||
|
||||
# this is a reconfigure, lets just delete old links
|
||||
@@ -112,34 +150,19 @@ case "$1" in
|
||||
chmod 755 /etc/authbind/byport/443
|
||||
fi
|
||||
|
||||
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
|
||||
# create jks from uploaded certs
|
||||
openssl pkcs12 -export \
|
||||
-in /etc/ssl/$JVB_HOSTNAME.crt \
|
||||
-inkey /etc/ssl/$JVB_HOSTNAME.key \
|
||||
-passout pass:changeit > /etc/jitsi/videobridge/$JVB_HOSTNAME.p12
|
||||
keytool -importkeystore \
|
||||
-srckeystore /etc/jitsi/videobridge/$JVB_HOSTNAME.p12 \
|
||||
-destkeystore /etc/jitsi/videobridge/$JVB_HOSTNAME.jks \
|
||||
-srcstoretype pkcs12 \
|
||||
-noprompt -storepass changeit -srcstorepass changeit
|
||||
else
|
||||
# create jks from self-signed certs
|
||||
openssl pkcs12 -export \
|
||||
-in /var/lib/prosody/$JVB_HOSTNAME.crt \
|
||||
-inkey /var/lib/prosody/$JVB_HOSTNAME.key \
|
||||
-passout pass:changeit > /etc/jitsi/videobridge/$JVB_HOSTNAME.p12
|
||||
keytool -importkeystore \
|
||||
-srckeystore /etc/jitsi/videobridge/$JVB_HOSTNAME.p12 \
|
||||
-destkeystore /etc/jitsi/videobridge/$JVB_HOSTNAME.jks \
|
||||
-srcstoretype pkcs12 \
|
||||
-noprompt -storepass changeit -srcstorepass changeit
|
||||
fi
|
||||
CERT_P12="/etc/jitsi/videobridge/$JVB_HOSTNAME.p12"
|
||||
CERT_JKS="/etc/jitsi/videobridge/$JVB_HOSTNAME.jks"
|
||||
# create jks from certs
|
||||
openssl pkcs12 -export \
|
||||
-in $CERT_CRT -inkey $CERT_KEY -passout pass:changeit > $CERT_P12
|
||||
keytool -importkeystore -destkeystore $CERT_JKS \
|
||||
-srckeystore $CERT_P12 -srcstoretype pkcs12 \
|
||||
-noprompt -storepass changeit -srcstorepass changeit
|
||||
|
||||
db_set jitsi-meet/jvb-serve "true"
|
||||
|
||||
invoke-rc.d jitsi-videobridge restart
|
||||
elif [[ "$FORCE_NGINX" = "true" || ( -n $JVB_HOSTNAME_OLD && "$JVB_SERVE" = "false" ) ]] ; then
|
||||
elif [[ "$FORCE_NGINX" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
|
||||
# this is a reconfigure, lets just delete old links
|
||||
if [ "$RECONFIGURING" = "true" ] ; then
|
||||
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME_OLD.conf
|
||||
@@ -148,7 +171,7 @@ case "$1" in
|
||||
|
||||
# nginx conf
|
||||
if [ ! -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf ] ; then
|
||||
cp /usr/share/doc/jitsi-meet/jitsi-meet.example /etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
cp /usr/share/doc/jitsi-meet-web/jitsi-meet.example /etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
if [ ! -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf ] ; then
|
||||
ln -s /etc/nginx/sites-available/$JVB_HOSTNAME.conf /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
|
||||
fi
|
||||
@@ -156,28 +179,47 @@ case "$1" in
|
||||
fi
|
||||
|
||||
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
|
||||
db_set jitsi-meet/cert-path-key "/etc/ssl/$JVB_HOSTNAME.key"
|
||||
db_input critical jitsi-meet/cert-path-key || true
|
||||
db_go
|
||||
db_get jitsi-meet/cert-path-key
|
||||
CERT_KEY="$RET"
|
||||
db_set jitsi-meet/cert-path-crt "/etc/ssl/$JVB_HOSTNAME.crt"
|
||||
db_input critical jitsi-meet/cert-path-crt || true
|
||||
db_go
|
||||
db_get jitsi-meet/cert-path-crt
|
||||
CERT_CRT="$RET"
|
||||
# replace self-signed certificate paths with user provided ones
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/ssl_certificate_key\ \/var\/lib\/prosody\/.*key/ssl_certificate_key\ $CERT_KEY_ESC/g" \
|
||||
sed -i "s/ssl_certificate_key\ \/etc\/jitsi\/meet\/.*key/ssl_certificate_key\ $CERT_KEY_ESC/g" \
|
||||
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/ssl_certificate\ \/var\/lib\/prosody\/.*crt/ssl_certificate\ $CERT_CRT_ESC/g" \
|
||||
sed -i "s/ssl_certificate\ \/etc\/jitsi\/meet\/.*crt/ssl_certificate\ $CERT_CRT_ESC/g" \
|
||||
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
fi
|
||||
|
||||
invoke-rc.d nginx reload
|
||||
elif [[ "$FORCE_APACHE" = "true" && ( -z "$JVB_HOSTNAME_OLD" || "$RECONFIGURING" = "true" ) ]] ; then
|
||||
# this is a reconfigure, lets just delete old links
|
||||
if [ "$RECONFIGURING" = "true" ] ; then
|
||||
a2dissite $JVB_HOSTNAME_OLD.conf
|
||||
rm -f /etc/jitsi/meet/$JVB_HOSTNAME_OLD-config.js
|
||||
fi
|
||||
|
||||
# apache2 config
|
||||
if [ ! -f /etc/apache2/sites-available/$JVB_HOSTNAME.conf ] ; then
|
||||
# when creating new config, make sure all needed modules are enabled
|
||||
a2enmod rewrite ssl headers proxy_http include
|
||||
cp /usr/share/doc/jitsi-meet-web/jitsi-meet.example-apache /etc/apache2/sites-available/$JVB_HOSTNAME.conf
|
||||
a2ensite $JVB_HOSTNAME.conf
|
||||
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" /etc/apache2/sites-available/$JVB_HOSTNAME.conf
|
||||
fi
|
||||
|
||||
if [ "$CERT_CHOICE" = "$UPLOADED_CERT_CHOICE" ] ; then
|
||||
# replace self-signed certificate paths with user provided ones
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
|
||||
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/SSLCertificateKeyFile\ \/etc\/jitsi\/meet\/.*key/SSLCertificateKeyFile\ $CERT_KEY_ESC/g" \
|
||||
/etc/apache2/sites-available/$JVB_HOSTNAME.conf
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
|
||||
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
|
||||
sed -i "s/SSLCertificateFile\ \/etc\/jitsi\/meet\/.*crt/SSLCertificateFile\ $CERT_CRT_ESC/g" \
|
||||
/etc/apache2/sites-available/$JVB_HOSTNAME.conf
|
||||
fi
|
||||
|
||||
invoke-rc.d apache2 reload
|
||||
fi
|
||||
|
||||
# and we're done with debconf
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# postrm script for jitsi-meet
|
||||
# postrm script for jitsi-meet-web-config
|
||||
#
|
||||
# see: dh_installdeb(1)
|
||||
|
||||
@@ -27,6 +27,9 @@ case "$1" in
|
||||
if [ -x "/etc/init.d/nginx" ]; then
|
||||
invoke-rc.d nginx reload
|
||||
fi
|
||||
if [ -x "/etc/init.d/apache2" ]; then
|
||||
invoke-rc.d apache2 reload
|
||||
fi
|
||||
;;
|
||||
purge)
|
||||
db_get jitsi-meet/jvb-hostname
|
||||
@@ -35,9 +38,15 @@ case "$1" in
|
||||
rm -f /etc/jitsi/meet/$JVB_HOSTNAME-config.js
|
||||
rm -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf
|
||||
rm -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf
|
||||
rm -f /etc/apache2/sites-available/$JVB_HOSTNAME.conf
|
||||
rm -f /etc/apache2/sites-enabled/$JVB_HOSTNAME.conf
|
||||
rm -f /etc/jitsi/videobridge/$JVB_HOSTNAME.jks
|
||||
rm -f /etc/jitsi/videobridge/$JVB_HOSTNAME.p12
|
||||
rm -f /etc/jitsi/meet/$JVB_HOSTNAME.key
|
||||
rm -f /etc/jitsi/meet/$JVB_HOSTNAME.crt
|
||||
fi
|
||||
# Clear the debconf variable
|
||||
db_purge
|
||||
;;
|
||||
upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
|
||||
;;
|
||||
@@ -30,3 +30,8 @@ Type: boolean
|
||||
Default: false
|
||||
_Description: for internal use
|
||||
for internal use.
|
||||
|
||||
Template: jitsi-videobridge/jvb-hostname
|
||||
Type: string
|
||||
_Description: Hostname:
|
||||
The Jitsi Meet web config package needs the DNS hostname of your instance.
|
||||
@@ -1,4 +1,5 @@
|
||||
README.md
|
||||
doc/debian/jitsi-meet/jitsi-meet.example
|
||||
doc/debian/jitsi-meet/jitsi-meet.example-apache
|
||||
doc/debian/jitsi-meet/README
|
||||
config.js
|
||||
2
debian/po/POTFILES.in
vendored
2
debian/po/POTFILES.in
vendored
@@ -1 +1 @@
|
||||
[type: gettext/rfc822deb] jitsi-meet.templates
|
||||
[type: gettext/rfc822deb] jitsi-meet-web-config.templates
|
||||
|
||||
53
debian/po/templates.pot
vendored
53
debian/po/templates.pot
vendored
@@ -1,10 +1,14 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the jitsi-meet-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: jitsi-meet\n"
|
||||
"Report-Msgid-Bugs-To: jitsi-meet@packages.debian.org\n"
|
||||
"POT-Creation-Date: 2014-09-03 17:26+0200\n"
|
||||
"Project-Id-Version: jitsi-meet-web\n"
|
||||
"Report-Msgid-Bugs-To: jitsi-meet-web@packages.debian.org\n"
|
||||
"POT-Creation-Date: 2016-11-15 22:39+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -15,25 +19,25 @@ msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Choices
|
||||
#: ../jitsi-meet.templates:1001
|
||||
#: ../jitsi-meet-web-config.templates:1001
|
||||
msgid "Self-signed certificate will be generated"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Choices
|
||||
#: ../jitsi-meet.templates:1001
|
||||
#: ../jitsi-meet-web-config.templates:1001
|
||||
msgid "A certificate is available and the files are uploaded on the server"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:1002
|
||||
#: ../jitsi-meet-web-config.templates:1002
|
||||
msgid "SSL certificate for the Jitsi Meet instance"
|
||||
msgstr ""
|
||||
|
||||
#. Type: select
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:1002
|
||||
#: ../jitsi-meet-web-config.templates:1002
|
||||
msgid ""
|
||||
"Jitsi Meet is best to be set up with an SSL certificate. Having no "
|
||||
"certificate, a self-signed one will be generated. Having a certificate "
|
||||
@@ -44,13 +48,13 @@ msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:2001
|
||||
#: ../jitsi-meet-web-config.templates:2001
|
||||
msgid "Full local server path to the SSL key file:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:2001
|
||||
#: ../jitsi-meet-web-config.templates:2001
|
||||
msgid ""
|
||||
"The full path to the SSL key file on the server. If it has not been "
|
||||
"uploaded, now is a good time to do so."
|
||||
@@ -58,13 +62,13 @@ msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:3001
|
||||
#: ../jitsi-meet-web-config.templates:3001
|
||||
msgid "Full local server path to the SSL certificate file:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:3001
|
||||
#: ../jitsi-meet-web-config.templates:3001
|
||||
msgid ""
|
||||
"The full path to the SSL certificate file on the server. If you haven't "
|
||||
"uploaded it, now is a good time to upload it in another console."
|
||||
@@ -72,27 +76,38 @@ msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:4001
|
||||
#: ../jitsi-meet-web-config.templates:4001
|
||||
msgid "The hostname of the current installation:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:4001
|
||||
#: ../jitsi-meet-web-config.templates:4001
|
||||
msgid ""
|
||||
"The value for the hostname that is set in Jitsi Videobridge installation."
|
||||
msgstr ""
|
||||
|
||||
|
||||
#. Type: string
|
||||
#. Type: boolean
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:5001
|
||||
#: ../jitsi-meet-web-config.templates:5001
|
||||
msgid "for internal use"
|
||||
msgstr ""
|
||||
|
||||
#. Type: boolean
|
||||
#. Description
|
||||
#: ../jitsi-meet-web-config.templates:5001
|
||||
msgid "for internal use."
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet.templates:5001
|
||||
msgid ""
|
||||
"Jitsi Videobridge installation can use its internal jetty to serve static meet pages."
|
||||
#: ../jitsi-meet-web-config.templates:6001
|
||||
msgid "Hostname:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../jitsi-meet-web-config.templates:6001
|
||||
msgid ""
|
||||
"The Jitsi Meet web config package needs the DNS hostname of your instance."
|
||||
msgstr ""
|
||||
|
||||
@@ -33,7 +33,7 @@ You can overwrite options set in config.js and interface_config.js. For example,
|
||||
```javascript
|
||||
var configOverwrite = {enableSimulcast: false};
|
||||
var interfaceConfigOverwrite = {filmStripOnly: true};
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement, true, configOverwrite, interfaceConfigOverwrite);
|
||||
var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement, configOverwrite, interfaceConfigOverwrite);
|
||||
```
|
||||
|
||||
Controlling embedded Jitsi Meet Conference
|
||||
@@ -54,7 +54,7 @@ Currently we support the following commands:
|
||||
* **displayName** - sets the display name of the local participant. This command requires one argument -
|
||||
the new display name to be set
|
||||
```
|
||||
api.executeCommand('displayName', ['New Nickname']);
|
||||
api.executeCommand('displayName', 'New Nickname');
|
||||
```
|
||||
* **toggleAudio** - mutes / unmutes the audio for the local participant. No arguments are required.
|
||||
```
|
||||
|
||||
@@ -15,8 +15,8 @@ server {
|
||||
|
||||
add_header Strict-Transport-Security "max-age=31536000";
|
||||
|
||||
ssl_certificate /var/lib/prosody/jitsi-meet.example.com.crt;
|
||||
ssl_certificate_key /var/lib/prosody/jitsi-meet.example.com.key;
|
||||
ssl_certificate /etc/jitsi/meet/jitsi-meet.example.com.crt;
|
||||
ssl_certificate_key /etc/jitsi/meet/jitsi-meet.example.com.key;
|
||||
|
||||
root /usr/share/jitsi-meet;
|
||||
index index.html index.htm;
|
||||
|
||||
43
doc/debian/jitsi-meet/jitsi-meet.example-apache
Normal file
43
doc/debian/jitsi-meet/jitsi-meet.example-apache
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
<VirtualHost *:80>
|
||||
ServerName jitsi-meet.example.com
|
||||
Redirect permanent / https://jitsi-meet.example.com/
|
||||
RewriteEngine On
|
||||
RewriteCond %{HTTPS} off
|
||||
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||
</VirtualHost>
|
||||
|
||||
<VirtualHost *:443>
|
||||
|
||||
ServerName jitsi-meet.example.com
|
||||
|
||||
SSLProtocol TLSv1 TLSv1.1 TLSv1.2
|
||||
SSLEngine on
|
||||
SSLProxyEngine on
|
||||
SSLCertificateFile /etc/jitsi/meet/jitsi-meet.example.com.crt
|
||||
SSLCertificateKeyFile /etc/jitsi/meet/jitsi-meet.example.com.key
|
||||
SSLCipherSuite "EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA256:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:EECDH+aRSA+SHA384:EDH+aRSA+AESGCM:EDH+aRSA+SHA256:EDH+aRSA:EECDH:!aNULL:!eNULL:!MEDIUM:!LOW:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS:!RC4:!SEED"
|
||||
SSLHonorCipherOrder on
|
||||
Header set Strict-Transport-Security "max-age=31536000"
|
||||
|
||||
DocumentRoot "/usr/share/jitsi-meet"
|
||||
<Directory "/usr/share/jitsi-meet">
|
||||
Options Indexes MultiViews Includes FollowSymLinks
|
||||
AddOutputFilter Includes html
|
||||
AllowOverride All
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
Alias "/config.js" "/etc/jitsi/meet/jitsi-meet.example.com-config.js"
|
||||
<Location /config.js>
|
||||
Require all granted
|
||||
</Location>
|
||||
|
||||
ProxyPreserveHost on
|
||||
ProxyPass /http-bind http://localhost:5280/http-bind/
|
||||
ProxyPassReverse /http-bind http://localhost:5280/http-bind/
|
||||
|
||||
RewriteEngine on
|
||||
RewriteRule ^/([a-zA-Z0-9]+)$ /index.html
|
||||
</VirtualHost>
|
||||
139
index.html
139
index.html
@@ -14,6 +14,7 @@
|
||||
"utils.js",
|
||||
"do_external_connect.js",
|
||||
"interface_config.js",
|
||||
"logging_config.js",
|
||||
"lib-jitsi-meet.min.js",
|
||||
"app.bundle.min.js",
|
||||
"all.css"
|
||||
@@ -34,7 +35,7 @@
|
||||
window.removeEventListener(
|
||||
'error', loadErrHandler, true /* capture phase */);
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener(
|
||||
'error', loadErrHandler, true /* capture phase type of listener */);
|
||||
</script>
|
||||
@@ -50,141 +51,7 @@
|
||||
<!--#include virtual="plugin.head.html" -->
|
||||
</head>
|
||||
<body>
|
||||
<div id="welcome_page">
|
||||
<div id="welcome_page_header">
|
||||
<a target="_new">
|
||||
<div class="watermark leftwatermark"></div>
|
||||
</a>
|
||||
<a target="_new">
|
||||
<div class="watermark rightwatermark"></div>
|
||||
</a>
|
||||
<a class="poweredby" href="http://jitsi.org" target="_new" ><span data-i18n="poweredby"></span> jitsi.org</a>
|
||||
|
||||
<div id="enter_room_container">
|
||||
<div id="enter_room_form" >
|
||||
<div id="domain_name"></div>
|
||||
<div id="enter_room">
|
||||
<input id="enter_room_field" type="text" autofocus/>
|
||||
<div class="icon-reload" id="reload_roomname"></div>
|
||||
<input id="enter_room_button" type="button" data-i18n="[value]welcomepage.go" value="GO" />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="brand_header"></div>
|
||||
<input type='checkbox' name='checkbox' id="disable_welcome"/>
|
||||
<label for="disable_welcome" class="disable_welcome_position" data-i18n="welcomepage.disable"></label>
|
||||
<div id="header_text">
|
||||
<!--#include virtual="plugin.header.text.html" -->
|
||||
</div>
|
||||
</div>
|
||||
<div id="welcome_page_main">
|
||||
<div id="features">
|
||||
<div class="feature_row">
|
||||
<div class="feature_holder">
|
||||
<div class="feature_icon" data-i18n="welcomepage.feature1.title" ></div>
|
||||
<div class="feature_description" data-i18n="welcomepage.feature1.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature_holder">
|
||||
<div class="feature_icon" data-i18n="welcomepage.feature2.title" ></div>
|
||||
<div class="feature_description" data-i18n="welcomepage.feature2.content">
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature_holder">
|
||||
<div class="feature_icon" data-i18n="welcomepage.feature3.title" ></div>
|
||||
<div class="feature_description" data-i18n="welcomepage.feature3.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature_holder">
|
||||
<div class="feature_icon" data-i18n="welcomepage.feature4.title" ></div>
|
||||
<div class="feature_description" data-i18n="welcomepage.feature4.content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature_row">
|
||||
<div class="feature_holder">
|
||||
<div class="feature_icon" data-i18n="welcomepage.feature5.title" ></div>
|
||||
<div class="feature_description" data-i18n="welcomepage.feature5.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature_holder">
|
||||
<div class="feature_icon" data-i18n="welcomepage.feature6.title" ></div>
|
||||
<div class="feature_description" data-i18n="welcomepage.feature6.content" data-i18n-options='{ "postProcess": "resolveAppName" }'>
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature_holder">
|
||||
<div class="feature_icon" data-i18n="welcomepage.feature7.title" ></div>
|
||||
<div class="feature_description" data-i18n="welcomepage.feature7.content" data-i18n-options='{ "postProcess": "resolveAppName" }'></div>
|
||||
</div>
|
||||
<div class="feature_holder">
|
||||
<div class="feature_icon" data-i18n="welcomepage.feature8.title" ></div>
|
||||
<div class="feature_description" data-i18n="welcomepage.feature8.content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--#include virtual="plugin.welcomepage.footer.html" -->
|
||||
</div>
|
||||
<div id="videoconference_page">
|
||||
<div id="mainToolbarContainer">
|
||||
<div id="notice" class="notice" style="display: none">
|
||||
<span id="noticeText" class="noticeText"></span>
|
||||
</div>
|
||||
<div id="mainToolbar" class="toolbar"></div>
|
||||
</div>
|
||||
<div id="subject" class="hide"></div>
|
||||
|
||||
<div id="extendedToolbar" class="toolbar">
|
||||
<div id="extendedToolbarButtons"></div>
|
||||
|
||||
<a class="button icon-feedback" id="feedbackButton"></a>
|
||||
|
||||
<div id="sideToolbarContainer"></div>
|
||||
</div>
|
||||
<div id="videospace">
|
||||
<div id="largeVideoContainer" class="videocontainer">
|
||||
<div id="sharedVideo"><div id="sharedVideoIFrame"></div></div>
|
||||
<div id="etherpad"></div>
|
||||
<a target="_new"><div class="watermark leftwatermark"></div></a>
|
||||
<a target="_new"><div class="watermark rightwatermark"></div></a>
|
||||
<a class="poweredby" href="http://jitsi.org" target="_new">
|
||||
<span data-i18n="poweredby"></span> jitsi.org
|
||||
</a>
|
||||
<div id="dominantSpeaker">
|
||||
<div class="dynamic-shadow"></div>
|
||||
<img id="dominantSpeakerAvatar" src=""/>
|
||||
</div>
|
||||
<span id="remoteConnectionMessage"></span>
|
||||
<div id="largeVideoWrapper">
|
||||
<video id="largeVideo" muted="true" autoplay></video>
|
||||
</div>
|
||||
<span id="localConnectionMessage"></span>
|
||||
<span id="videoResolutionLabel" class="video-state-indicator moveToCorner">HD</span>
|
||||
<span id="recordingLabel" class="video-state-indicator centeredVideoLabel">
|
||||
<span id="recordingLabelText"></span>
|
||||
<img id="recordingSpinner" class="recordingSpinner" src="images/spin.svg"></img>
|
||||
</span>
|
||||
</div>
|
||||
<div class="filmstrip">
|
||||
<div class="filmstrip__videos" id="remoteVideos">
|
||||
<span id="localVideoContainer" class="videocontainer videocontainer_small">
|
||||
<div class="videocontainer__background"></div>
|
||||
<span id="localVideoWrapper">
|
||||
<!--<video id="localVideo" autoplay muted></video> - is now per stream generated -->
|
||||
</span>
|
||||
<audio id="localAudio" autoplay muted></audio>
|
||||
<div class="videocontainer__toolbar"></div>
|
||||
<div class="videocontainer__toptoolbar"></div>
|
||||
<div class="videocontainer__hoverOverlay"></div>
|
||||
</span>
|
||||
<audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
|
||||
<audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="react"></div>
|
||||
<div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">
|
||||
<div class="content">
|
||||
<ul id="keyboard-shortcuts-list" class="shortcuts-list">
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* global APP, getConfigParamsFromUrl */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Implements API class that communicates with external api class
|
||||
* and provides interface to access Jitsi Meet features by external
|
||||
@@ -131,13 +133,13 @@ function onSystemMessage(message) {
|
||||
switch (message.type) {
|
||||
case "eventStatus":
|
||||
if(!message.name || !message.value) {
|
||||
console.warn("Unknown system message format", message);
|
||||
logger.warn("Unknown system message format", message);
|
||||
break;
|
||||
}
|
||||
events[message.name] = message.value;
|
||||
break;
|
||||
default:
|
||||
console.warn("Unknown system message type", message);
|
||||
logger.warn("Unknown system message type", message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
10
modules/API/external/external_api.js
vendored
10
modules/API/external/external_api.js
vendored
@@ -1,3 +1,5 @@
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Implements API class that embeds Jitsi Meet in external applications.
|
||||
*/
|
||||
@@ -72,7 +74,7 @@ function sendMessage(postis, object) {
|
||||
*/
|
||||
function changeEventStatus(postis, event, status) {
|
||||
if(!(event in events)) {
|
||||
console.error("Not supported event name.");
|
||||
logger.error("Not supported event name.");
|
||||
return;
|
||||
}
|
||||
sendMessage(postis, {
|
||||
@@ -174,7 +176,7 @@ function JitsiMeetExternalAPI(domain, room_name, width, height, parentNode,
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.executeCommand = function(name, argumentsList) {
|
||||
if(!(name in commands)) {
|
||||
console.error("Not supported command name.");
|
||||
logger.error("Not supported command name.");
|
||||
return;
|
||||
}
|
||||
var argumentsArray = argumentsList;
|
||||
@@ -306,7 +308,7 @@ JitsiMeetExternalAPI.prototype.addEventListeners = function(object) {
|
||||
*/
|
||||
JitsiMeetExternalAPI.prototype.addEventListener = function(event, listener) {
|
||||
if(!(event in events)) {
|
||||
console.error("Not supported event name.");
|
||||
logger.error("Not supported event name.");
|
||||
return;
|
||||
}
|
||||
// We cannot remove listeners from postis that's why we are handling the
|
||||
@@ -328,7 +330,7 @@ JitsiMeetExternalAPI.prototype.addEventListener = function(event, listener) {
|
||||
JitsiMeetExternalAPI.prototype.removeEventListener = function(event) {
|
||||
if(!(event in this.eventHandlers))
|
||||
{
|
||||
console.error("The event " + event + " is not registered.");
|
||||
logger.error("The event " + event + " is not registered.");
|
||||
return;
|
||||
}
|
||||
delete this.eventHandlers[event];
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import UIEvents from '../service/UI/UIEvents';
|
||||
import VideoLayout from './UI/videolayout/VideoLayout';
|
||||
@@ -308,7 +309,7 @@ class FollowMe {
|
||||
|
||||
if (!this._conference.isParticipantModerator(id))
|
||||
{
|
||||
console.warn('Received follow-me command ' +
|
||||
logger.warn('Received follow-me command ' +
|
||||
'not from moderator');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* global APP, JitsiMeetJS, $, config, interfaceConfig, toastr */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
var UI = {};
|
||||
|
||||
import Chat from "./side_pannels/chat/Chat";
|
||||
@@ -443,10 +445,10 @@ UI.start = function () {
|
||||
// Display notice message at the top of the toolbar
|
||||
if (config.noticeMessage) {
|
||||
$('#noticeText').text(config.noticeMessage);
|
||||
UIUtil.showElement('notice');
|
||||
UIUtil.setVisible('notice', true);
|
||||
}
|
||||
} else {
|
||||
UIUtil.hideElement('mainToolbarContainer');
|
||||
UIUtil.setVisible('mainToolbarContainer', false);
|
||||
FilmStrip.setupFilmStripOnly();
|
||||
messageHandler.enableNotifications(false);
|
||||
JitsiPopover.enabled = false;
|
||||
@@ -474,7 +476,9 @@ UI.start = function () {
|
||||
"hideEasing": "linear",
|
||||
"showMethod": "fadeIn",
|
||||
"hideMethod": "fadeOut",
|
||||
"newestOnTop": false
|
||||
"newestOnTop": false,
|
||||
// this is the default toastr close button html, just adds tabIndex
|
||||
"closeHtml": '<button type="button" tabIndex="-1">×</button>'
|
||||
};
|
||||
|
||||
}
|
||||
@@ -501,7 +505,7 @@ UI.addLocalStream = function (track) {
|
||||
VideoLayout.changeLocalVideo(track);
|
||||
break;
|
||||
default:
|
||||
console.error("Unknown stream type: " + track.getType());
|
||||
logger.error("Unknown stream type: " + track.getType());
|
||||
break;
|
||||
}
|
||||
};
|
||||
@@ -539,7 +543,7 @@ UI.initEtherpad = function (name) {
|
||||
if (etherpadManager || !config.etherpad_base || !name) {
|
||||
return;
|
||||
}
|
||||
console.log('Etherpad is enabled');
|
||||
logger.log('Etherpad is enabled');
|
||||
etherpadManager
|
||||
= new EtherpadManager(config.etherpad_base, name, eventEmitter);
|
||||
Toolbar.showEtherpadButton();
|
||||
@@ -737,7 +741,7 @@ UI.connectionIndicatorShowMore = function(id) {
|
||||
|
||||
// FIXME check if someone user this
|
||||
UI.showLoginPopup = function(callback) {
|
||||
console.log('password is required');
|
||||
logger.log('password is required');
|
||||
|
||||
let message = (
|
||||
`<input name="username" type="text"
|
||||
@@ -1088,10 +1092,16 @@ UI.notifyFocusDisconnected = function (focus, retrySec) {
|
||||
/**
|
||||
* Notify the user that the video conferencing service is badly broken and
|
||||
* the page should be reloaded.
|
||||
*
|
||||
* @param {boolean} isNetworkFailure <tt>true</tt> indicates that it's caused by
|
||||
* network related failure or <tt>false</tt> when it's the infrastructure.
|
||||
* @param {string} a label string identifying the reason for the page reload
|
||||
* which will be included in details of the log event.
|
||||
*/
|
||||
UI.showPageReloadOverlay = function () {
|
||||
UI.showPageReloadOverlay = function (isNetworkFailure, reason) {
|
||||
// Reload the page after 10 - 30 seconds
|
||||
PageReloadOverlay.show(10 + RandomUtil.randomInt(0, 20));
|
||||
PageReloadOverlay.show(
|
||||
10 + RandomUtil.randomInt(0, 20), isNetworkFailure, reason);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1103,13 +1113,13 @@ UI.updateAuthInfo = function (isAuthEnabled, login) {
|
||||
let showAuth = isAuthEnabled && UIUtil.isAuthenticationEnabled();
|
||||
let loggedIn = !!login;
|
||||
|
||||
Toolbar.showAuthenticateButton(showAuth);
|
||||
Profile.showAuthenticationButtons(showAuth);
|
||||
|
||||
if (showAuth) {
|
||||
Toolbar.setAuthenticatedIdentity(login);
|
||||
Profile.setAuthenticatedIdentity(login);
|
||||
|
||||
Toolbar.showLoginButton(!loggedIn);
|
||||
Toolbar.showLogoutButton(loggedIn);
|
||||
Profile.showLoginButton(!loggedIn);
|
||||
Profile.showLogoutButton(loggedIn);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1179,7 +1189,7 @@ UI.getLargeVideo = function () {
|
||||
UI.showExtensionRequiredDialog = function (url) {
|
||||
messageHandler.openMessageDialog(
|
||||
"dialog.extensionRequired",
|
||||
"dialog.firefoxExtensionPrompt",
|
||||
"[html]dialog.firefoxExtensionPrompt",
|
||||
{url: url});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* global APP, config, JitsiMeetJS, Promise */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import LoginDialog from './LoginDialog';
|
||||
import UIUtil from '../util/UIUtil';
|
||||
@@ -74,13 +75,13 @@ function redirectToTokenAuthService(roomName) {
|
||||
function initJWTTokenListener(room) {
|
||||
var listener = function (event) {
|
||||
if (externalAuthWindow !== event.source) {
|
||||
console.warn("Ignored message not coming " +
|
||||
logger.warn("Ignored message not coming " +
|
||||
"from external authnetication window");
|
||||
return;
|
||||
}
|
||||
if (event.data && event.data.jwtToken) {
|
||||
config.token = event.data.jwtToken;
|
||||
console.info("Received JWT token:", config.token);
|
||||
logger.info("Received JWT token:", config.token);
|
||||
var roomName = room.getName();
|
||||
openConnection({retry: false, roomName: roomName })
|
||||
.then(function (connection) {
|
||||
@@ -97,10 +98,10 @@ function initJWTTokenListener(room) {
|
||||
// to upgrade user's role
|
||||
room.room.moderator.authenticate()
|
||||
.then(function () {
|
||||
console.info("User role upgrade done !");
|
||||
logger.info("User role upgrade done !");
|
||||
unregister();
|
||||
}).catch(function (err, errCode) {
|
||||
console.error(
|
||||
logger.error(
|
||||
"Authentication failed: ", err, errCode);
|
||||
unregister();
|
||||
}
|
||||
@@ -108,13 +109,13 @@ function initJWTTokenListener(room) {
|
||||
}).catch(function (error, code) {
|
||||
unregister();
|
||||
connection.disconnect();
|
||||
console.error(
|
||||
logger.error(
|
||||
'Authentication failed on the new connection',
|
||||
error, code);
|
||||
});
|
||||
}, function (err) {
|
||||
unregister();
|
||||
console.error("Failed to open new connection", err);
|
||||
logger.error("Failed to open new connection", err);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -161,7 +162,7 @@ function doXmppAuth (room, lockPassword) {
|
||||
}).catch(function (error, code) {
|
||||
connection.disconnect();
|
||||
|
||||
console.error('Auth on the fly failed', error);
|
||||
logger.error('Auth on the fly failed', error);
|
||||
|
||||
loginDialog.displayError(
|
||||
'connection.GET_SESSION_ID_ERROR', {code: code});
|
||||
|
||||
@@ -206,7 +206,7 @@ export default {
|
||||
*/
|
||||
showAuthRequiredDialog: function (roomName, onAuthNow) {
|
||||
var msg = APP.translation.generateTranslationHTML(
|
||||
"dialog.WaitForHostMsg", {room: roomName}
|
||||
"[html]dialog.WaitForHostMsg", {room: roomName}
|
||||
);
|
||||
|
||||
var buttonTxt = APP.translation.generateTranslationHTML(
|
||||
|
||||
@@ -1,4 +1,28 @@
|
||||
/*
|
||||
* Adorable Avatars service used at the end of this file is released under the
|
||||
* terms of the MIT License.
|
||||
*
|
||||
* Copyright (c) 2014 Adorable IO LLC
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
/* global MD5, config, interfaceConfig, APP */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
let users = {};
|
||||
|
||||
@@ -87,7 +111,7 @@ export default {
|
||||
let random = !avatarId || avatarId.indexOf('@') < 0;
|
||||
|
||||
if (!avatarId) {
|
||||
console.warn(
|
||||
logger.warn(
|
||||
`No avatar stored yet for ${userId} - using ID as avatar ID`);
|
||||
avatarId = userId;
|
||||
}
|
||||
@@ -105,8 +129,8 @@ export default {
|
||||
urlSuf = interfaceConfig.RANDOM_AVATAR_URL_SUFFIX;
|
||||
}
|
||||
else {
|
||||
urlPref = 'https://robohash.org/';
|
||||
urlSuf = ".png?size=200x200";
|
||||
urlPref = 'https://api.adorable.io/avatars/200/';
|
||||
urlSuf = ".png";
|
||||
}
|
||||
|
||||
return urlPref + avatarId + urlSuf;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* global JitsiMeetJS, APP */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import InviteDialogView from './InviteDialogView';
|
||||
import createRoomLocker from './RoomLocker';
|
||||
@@ -28,7 +29,7 @@ class Invite {
|
||||
this.conference.on(ConferenceEvents.LOCK_STATE_CHANGED,
|
||||
(locked, error) => {
|
||||
|
||||
console.log("Received channel password lock change: ", locked,
|
||||
logger.log("Received channel password lock change: ", locked,
|
||||
error);
|
||||
|
||||
if (!locked) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* global $, APP, JitsiMeetJS */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Substate for password
|
||||
@@ -312,7 +313,7 @@ export default class InviteDialogView {
|
||||
this.blur();
|
||||
}
|
||||
catch (err) {
|
||||
console.error('error when copy the text');
|
||||
logger.error('error when copy the text');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* global APP, JitsiMeetJS */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import RequirePasswordDialog from './RequirePasswordDialog';
|
||||
|
||||
/**
|
||||
@@ -6,7 +8,7 @@ import RequirePasswordDialog from './RequirePasswordDialog';
|
||||
* because server doesn't support that.
|
||||
*/
|
||||
function notifyPasswordNotSupported () {
|
||||
console.warn('room passwords not supported');
|
||||
logger.warn('room passwords not supported');
|
||||
APP.UI.messageHandler.showError(
|
||||
"dialog.warning", "dialog.passwordNotSupported");
|
||||
}
|
||||
@@ -16,7 +18,7 @@ function notifyPasswordNotSupported () {
|
||||
* @param {Error} err error
|
||||
*/
|
||||
function notifyPasswordFailed(err) {
|
||||
console.warn('setting password failed', err);
|
||||
logger.warn('setting password failed', err);
|
||||
APP.UI.messageHandler.showError(
|
||||
"dialog.lockTitle", "dialog.lockMessage");
|
||||
}
|
||||
@@ -64,7 +66,7 @@ export default function createRoomLocker (room) {
|
||||
if (!password)
|
||||
lockedElsewhere = false;
|
||||
}).catch(function (err) {
|
||||
console.error(err);
|
||||
logger.error(err);
|
||||
if (err === ConferenceErrors.PASSWORD_NOT_SUPPORTED) {
|
||||
notifyPasswordNotSupported();
|
||||
} else {
|
||||
@@ -113,7 +115,7 @@ export default function createRoomLocker (room) {
|
||||
// pass stays between attempts
|
||||
password = null;
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
console.error(reason);
|
||||
logger.error(reason);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
import UIUtil from '../util/UIUtil';
|
||||
import VideoLayout from '../videolayout/VideoLayout';
|
||||
@@ -327,7 +329,7 @@ var Recording = {
|
||||
}).catch(
|
||||
reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
console.error(reason);
|
||||
logger.error(reason);
|
||||
else
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.canceled');
|
||||
@@ -350,7 +352,7 @@ var Recording = {
|
||||
}).catch(
|
||||
reason => {
|
||||
if (reason !== APP.UI.messageHandler.CANCEL)
|
||||
console.error(reason);
|
||||
logger.error(reason);
|
||||
else
|
||||
JitsiMeetJS.analytics.sendEvent(
|
||||
'recording.canceled');
|
||||
@@ -392,11 +394,7 @@ var Recording = {
|
||||
let shouldShow = show && _isRecordingButtonEnabled();
|
||||
let id = 'toolbar_button_record';
|
||||
|
||||
if (shouldShow) {
|
||||
UIUtil.showElement(id);
|
||||
} else {
|
||||
UIUtil.hideElement(id);
|
||||
}
|
||||
UIUtil.setVisible(id, shouldShow);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -480,11 +478,8 @@ var Recording = {
|
||||
|
||||
// Recording spinner
|
||||
let spinnerId = 'recordingSpinner';
|
||||
if(recordingState === Status.RETRYING) {
|
||||
UIUtil.showElement(spinnerId);
|
||||
} else {
|
||||
UIUtil.hideElement(spinnerId);
|
||||
}
|
||||
UIUtil.setVisible(
|
||||
spinnerId, recordingState === Status.RETRYING);
|
||||
},
|
||||
// checks whether recording is enabled and whether we have params
|
||||
// to start automatically recording
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* global $, APP, AJS */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import Overlay from '../overlay/Overlay';
|
||||
|
||||
@@ -31,20 +32,22 @@ class PageReloadOverlayImpl extends Overlay{
|
||||
* @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>`;
|
||||
return `<div class="inlay">
|
||||
<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>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +85,7 @@ class PageReloadOverlayImpl extends Overlay{
|
||||
}
|
||||
}.bind(this), 1000);
|
||||
|
||||
console.info(
|
||||
logger.info(
|
||||
"The conference will be reloaded after "
|
||||
+ this.timeLeft + " seconds.");
|
||||
}
|
||||
@@ -110,8 +113,13 @@ export default {
|
||||
*
|
||||
* @param {number} timeoutSeconds how many seconds before the conference
|
||||
* reload will happen.
|
||||
* @param {boolean} isNetworkFailure <tt>true</tt> indicates that it's
|
||||
* caused by network related failure or <tt>false</tt> when it's
|
||||
* the infrastructure.
|
||||
* @param {string} reason a label string identifying the reason for the page
|
||||
* reload which will be included in details of the log event
|
||||
*/
|
||||
show(timeoutSeconds) {
|
||||
show(timeoutSeconds, isNetworkFailure, reason) {
|
||||
|
||||
if (!overlay) {
|
||||
overlay = new PageReloadOverlayImpl(timeoutSeconds);
|
||||
@@ -121,7 +129,8 @@ export default {
|
||||
// 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');
|
||||
APP.conference.logEvent(
|
||||
'page.reload', undefined /* value */, reason /* label */);
|
||||
}
|
||||
overlay.show();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* global $, APP, YT, onPlayerReady, onPlayerStateChange, onPlayerError,
|
||||
JitsiMeetJS */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import UIUtil from '../util/UIUtil';
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
@@ -75,7 +76,7 @@ export default class SharedVideoManager {
|
||||
JitsiMeetJS.analytics.sendEvent('sharedvideo.started');
|
||||
},
|
||||
err => {
|
||||
console.log('SHARED VIDEO CANCELED', err);
|
||||
logger.log('SHARED VIDEO CANCELED', err);
|
||||
JitsiMeetJS.analytics.sendEvent('sharedvideo.canceled');
|
||||
}
|
||||
);
|
||||
@@ -277,7 +278,7 @@ export default class SharedVideoManager {
|
||||
};
|
||||
|
||||
window.onPlayerError = function(event) {
|
||||
console.error("Error in the player:", event.data);
|
||||
logger.error("Error in the player:", event.data);
|
||||
// store the error player, so we can remove it
|
||||
self.errorInPlayer = event.target;
|
||||
};
|
||||
@@ -313,7 +314,7 @@ export default class SharedVideoManager {
|
||||
&& player.getVolume() != attributes.volume) {
|
||||
|
||||
player.setVolume(attributes.volume);
|
||||
console.info("Player change of volume:" + attributes.volume);
|
||||
logger.info("Player change of volume:" + attributes.volume);
|
||||
this.showSharedVideoMutedPopup(false);
|
||||
}
|
||||
|
||||
@@ -337,7 +338,7 @@ export default class SharedVideoManager {
|
||||
processTime (player, attributes, forceSeek)
|
||||
{
|
||||
if(forceSeek) {
|
||||
console.info("Player seekTo:", attributes.time);
|
||||
logger.info("Player seekTo:", attributes.time);
|
||||
player.seekTo(attributes.time);
|
||||
return;
|
||||
}
|
||||
@@ -349,7 +350,7 @@ export default class SharedVideoManager {
|
||||
// if we drift more than the interval for checking
|
||||
// sync, the interval is in milliseconds
|
||||
if(diff > updateInterval/1000) {
|
||||
console.info("Player seekTo:", attributes.time,
|
||||
logger.info("Player seekTo:", attributes.time,
|
||||
" current time is:", currentPosition, " diff:", diff);
|
||||
player.seekTo(attributes.time);
|
||||
}
|
||||
@@ -669,7 +670,7 @@ SharedVideoThumb.prototype.videoClick = function () {
|
||||
* Removes RemoteVideo from the page.
|
||||
*/
|
||||
SharedVideoThumb.prototype.remove = function () {
|
||||
console.log("Remove shared video thumb", this.id);
|
||||
logger.log("Remove shared video thumb", this.id);
|
||||
|
||||
// Make sure that the large video is updated if are removing its
|
||||
// corresponding small video.
|
||||
@@ -686,7 +687,7 @@ SharedVideoThumb.prototype.remove = function () {
|
||||
*/
|
||||
SharedVideoThumb.prototype.setDisplayName = function(displayName) {
|
||||
if (!this.container) {
|
||||
console.warn( "Unable to set displayName - " + this.videoSpanId +
|
||||
logger.warn( "Unable to set displayName - " + this.videoSpanId +
|
||||
" does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -157,6 +157,17 @@ function resizeChatConversation() {
|
||||
chat.height(window.innerHeight - 15 - msgareaHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Focus input after 400 ms
|
||||
* Found input by id
|
||||
*
|
||||
* @param id {string} input id
|
||||
*/
|
||||
function deferredFocus(id){
|
||||
setTimeout(function (){
|
||||
$(`#${id}`).focus();
|
||||
}, 400);
|
||||
}
|
||||
/**
|
||||
* Chat related user interface.
|
||||
*/
|
||||
@@ -180,6 +191,7 @@ var Chat = {
|
||||
let val = this.value;
|
||||
this.value = '';
|
||||
eventEmitter.emit(UIEvents.NICKNAME_CHANGED, val);
|
||||
deferredFocus('usermsg');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -223,9 +235,9 @@ var Chat = {
|
||||
// if we are in conversation mode focus on the text input
|
||||
// if we are not, focus on the display name input
|
||||
if (APP.settings.getDisplayName())
|
||||
$('#usermsg').focus();
|
||||
deferredFocus('usermsg');
|
||||
else
|
||||
$('#nickinput').focus();
|
||||
deferredFocus('nickinput');
|
||||
});
|
||||
|
||||
addSmileys();
|
||||
@@ -294,18 +306,14 @@ var Chat = {
|
||||
* @param subject the subject
|
||||
*/
|
||||
setSubject (subject) {
|
||||
let toggleFunction;
|
||||
if (subject) {
|
||||
subject = subject.trim();
|
||||
}
|
||||
|
||||
toggleFunction = subject ? UIUtil.showElement : UIUtil.hideElement;
|
||||
toggleFunction = toggleFunction.bind(UIUtil);
|
||||
|
||||
let subjectId = 'subject';
|
||||
let html = linkify(UIUtil.escapeHtml(subject));
|
||||
$(`#${subjectId}`).html(html);
|
||||
toggleFunction(subjectId);
|
||||
UIUtil.setVisible(subjectId, subject && subject.length > 0);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -318,13 +326,6 @@ var Chat = {
|
||||
setChatConversationMode (isConversationMode) {
|
||||
$('#' + CHAT_CONTAINER_ID)
|
||||
.toggleClass('is-conversation-mode', isConversationMode);
|
||||
|
||||
// this is needed when we transition from no conversation mode to
|
||||
// conversation mode. When user enters his nickname and hits enter,
|
||||
// to focus on the write area.
|
||||
if (isConversationMode) {
|
||||
$('#usermsg').focus();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* global $, APP, interfaceConfig */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import Avatar from '../../avatar/Avatar';
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import UIUtil from '../../util/UIUtil';
|
||||
@@ -27,7 +29,7 @@ function updateNumberOfParticipants(delta) {
|
||||
numberOfContacts += delta;
|
||||
|
||||
if (numberOfContacts <= 0) {
|
||||
console.error("Invalid number of participants: " + numberOfContacts);
|
||||
logger.error("Invalid number of participants: " + numberOfContacts);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global $ */
|
||||
/* global $, APP, JitsiMeetJS */
|
||||
import UIUtil from "../../util/UIUtil";
|
||||
import UIEvents from "../../../../service/UI/UIEvents";
|
||||
import Settings from '../../../settings/Settings';
|
||||
@@ -18,15 +18,15 @@ const htmlStr = `
|
||||
<input id="setEmail" type="text" class="input-control"
|
||||
data-i18n="[placeholder]profile.setEmailInput">
|
||||
</div>
|
||||
<div id="authenticationContainer"
|
||||
<div id="profile_auth_container"
|
||||
class="sideToolbarBlock auth_container">
|
||||
<p data-i18n="toolbar.authenticate"></p>
|
||||
<ul>
|
||||
<li id="toolbar_auth_identity"></li>
|
||||
<li id="toolbar_button_login">
|
||||
<li id="profile_auth_identity"></li>
|
||||
<li id="profile_button_login">
|
||||
<a class="authButton" data-i18n="toolbar.login"></a>
|
||||
</li>
|
||||
<li id="toolbar_button_logout">
|
||||
<li id="profile_button_logout">
|
||||
<a class="authButton" data-i18n="toolbar.logout"></a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -68,6 +68,34 @@ export default {
|
||||
updateEmail();
|
||||
}
|
||||
}).focusout(updateEmail);
|
||||
|
||||
// LOGIN
|
||||
function loginClicked () {
|
||||
JitsiMeetJS.analytics.sendEvent('authenticate.login.clicked');
|
||||
emitter.emit(UIEvents.AUTH_CLICKED);
|
||||
}
|
||||
|
||||
$('#profile_button_login').click(loginClicked);
|
||||
|
||||
// LOGOUT
|
||||
function logoutClicked () {
|
||||
let titleKey = "dialog.logoutTitle";
|
||||
let msgKey = "dialog.logoutQuestion";
|
||||
JitsiMeetJS.analytics.sendEvent('authenticate.logout.clicked');
|
||||
// Ask for confirmation
|
||||
APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey: titleKey,
|
||||
msgKey: msgKey,
|
||||
leftButtonKey: "dialog.Yes",
|
||||
submitFunction: function (evt, yes) {
|
||||
if (yes) {
|
||||
emitter.emit(UIEvents.LOGOUT);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('#profile_button_logout').click(logoutClicked);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -92,5 +120,46 @@ export default {
|
||||
*/
|
||||
changeAvatar (avatarUrl) {
|
||||
$('#avatar').attr('src', avatarUrl);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows or hides authentication related buttons
|
||||
* @param {boolean} show <tt>true</tt> to show or <tt>false</tt> to hide
|
||||
*/
|
||||
showAuthenticationButtons (show) {
|
||||
let id = 'profile_auth_container';
|
||||
UIUtil.setVisible(id, show);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows/hides login button.
|
||||
* @param {boolean} show <tt>true</tt> to show or <tt>false</tt> to hide
|
||||
*/
|
||||
showLoginButton (show) {
|
||||
let id = 'profile_button_login';
|
||||
|
||||
UIUtil.setVisible(id, show);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows/hides logout button.
|
||||
* @param {boolean} show <tt>true</tt> to show or <tt>false</tt> to hide
|
||||
*/
|
||||
showLogoutButton (show) {
|
||||
let id = 'profile_button_logout';
|
||||
|
||||
UIUtil.setVisible(id, show);
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays user's authenticated identity name (login).
|
||||
* @param {string} authIdentity identity name to be displayed.
|
||||
*/
|
||||
setAuthenticatedIdentity (authIdentity) {
|
||||
let id = 'profile_auth_identity';
|
||||
|
||||
UIUtil.setVisible(id, !!authIdentity);
|
||||
|
||||
$(`#${id}`).text(authIdentity ? authIdentity : '');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -166,7 +166,7 @@ export default {
|
||||
APP.translation.addLanguageChangedListener(
|
||||
lng => selectInput[0].dataset.i18n = `languages:${lng}`);
|
||||
|
||||
UIUtil.showElement(wrapperId);
|
||||
UIUtil.setVisible(wrapperId, true);
|
||||
}
|
||||
// DEVICES LIST
|
||||
if (UIUtil.isSettingEnabled('devices')) {
|
||||
@@ -181,9 +181,9 @@ export default {
|
||||
});
|
||||
// Only show the subtitle if this isn't the only setting section.
|
||||
if (interfaceConfig.SETTINGS_SECTIONS.length > 1)
|
||||
UIUtil.showElement("deviceOptionsTitle");
|
||||
UIUtil.setVisible("deviceOptionsTitle", true);
|
||||
|
||||
UIUtil.showElement(wrapperId);
|
||||
UIUtil.setVisible(wrapperId, true);
|
||||
}
|
||||
// MODERATOR
|
||||
if (UIUtil.isSettingEnabled('moderator')) {
|
||||
@@ -208,7 +208,7 @@ export default {
|
||||
emitter.emit(UIEvents.FOLLOW_ME_ENABLED, isFollowMeEnabled);
|
||||
});
|
||||
|
||||
UIUtil.showElement(wrapperId);
|
||||
UIUtil.setVisible(wrapperId, true);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -245,15 +245,15 @@ export default {
|
||||
// Only show the subtitle if this isn't the only setting section.
|
||||
if (!$("#moderatorOptionsTitle").is(":visible")
|
||||
&& interfaceConfig.SETTINGS_SECTIONS.length > 1)
|
||||
UIUtil.showElement("moderatorOptionsTitle");
|
||||
UIUtil.setVisible("moderatorOptionsTitle", true);
|
||||
|
||||
UIUtil.showElement("startMutedOptions");
|
||||
UIUtil.setVisible("startMutedOptions", true);
|
||||
} else {
|
||||
// Only show the subtitle if this isn't the only setting section.
|
||||
if ($("#moderatorOptionsTitle").is(":visible"))
|
||||
UIUtil.hideElement("moderatorOptionsTitle");
|
||||
UIUtil.setVisible("moderatorOptionsTitle", false);
|
||||
|
||||
UIUtil.hideElement("startMutedOptions");
|
||||
UIUtil.setVisible("startMutedOptions", false);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -268,11 +268,9 @@ export default {
|
||||
* @param {boolean} show {true} to show those options, {false} to hide them
|
||||
*/
|
||||
showFollowMeOptions (show) {
|
||||
if (show && UIUtil.isSettingEnabled('moderator')) {
|
||||
UIUtil.showElement("followMeOptions");
|
||||
} else {
|
||||
UIUtil.hideElement("followMeOptions");
|
||||
}
|
||||
UIUtil.setVisible(
|
||||
"followMeOptions",
|
||||
show && UIUtil.isSettingEnabled('moderator'));
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -97,26 +97,6 @@ const buttonHandlers = {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.hangup');
|
||||
emitter.emit(UIEvents.HANGUP);
|
||||
},
|
||||
"toolbar_button_login": function () {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.authenticate.login.clicked');
|
||||
emitter.emit(UIEvents.AUTH_CLICKED);
|
||||
},
|
||||
"toolbar_button_logout": function () {
|
||||
let titleKey = "dialog.logoutTitle";
|
||||
let msgKey = "dialog.logoutQuestion";
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.authenticate.logout.clicked');
|
||||
// Ask for confirmation
|
||||
APP.UI.messageHandler.openTwoButtonDialog({
|
||||
titleKey,
|
||||
msgKey,
|
||||
leftButtonKey: "dialog.Yes",
|
||||
submitFunction: function (evt, yes) {
|
||||
if (yes) {
|
||||
emitter.emit(UIEvents.LOGOUT);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
"toolbar_button_raisehand": function () {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.raiseHand.clicked');
|
||||
APP.conference.maybeToggleRaisedHand();
|
||||
@@ -275,7 +255,9 @@ const defaultToolbarButtons = {
|
||||
'recording': {
|
||||
id: 'toolbar_button_record',
|
||||
tooltipKey: 'liveStreaming.buttonTooltip',
|
||||
className: 'button'
|
||||
className: 'button',
|
||||
hidden: true // will be displayed once
|
||||
// the recording functionality is detected
|
||||
},
|
||||
'sharedvideo': {
|
||||
id: 'toolbar_button_sharedvideo',
|
||||
@@ -290,7 +272,9 @@ const defaultToolbarButtons = {
|
||||
'sip': {
|
||||
id: 'toolbar_button_sip',
|
||||
tooltipKey: 'toolbar.sip',
|
||||
className: 'button icon-telephone'
|
||||
className: 'button icon-telephone',
|
||||
hidden: true // will be displayed once
|
||||
// the SIP calls functionality is detected
|
||||
},
|
||||
'dialpad': {
|
||||
id: 'toolbar_button_dialpad',
|
||||
@@ -397,18 +381,6 @@ Toolbar = {
|
||||
isEnabled() {
|
||||
return this.enabled;
|
||||
},
|
||||
/**
|
||||
* Shows or hides authentication button
|
||||
* @param show <tt>true</tt> to show or <tt>false</tt> to hide
|
||||
*/
|
||||
showAuthenticateButton (show) {
|
||||
let id = 'authenticationContainer';
|
||||
if (show) {
|
||||
UIUtil.showElement(id);
|
||||
} else {
|
||||
UIUtil.hideElement(id);
|
||||
}
|
||||
},
|
||||
|
||||
showEtherpadButton () {
|
||||
if (!$('#toolbar_button_etherpad').is(":visible")) {
|
||||
@@ -425,10 +397,8 @@ Toolbar = {
|
||||
if (shouldShow) {
|
||||
let el = document.getElementById(id);
|
||||
UIUtil.setTooltip(el, 'toolbar.sharedvideo', 'right');
|
||||
UIUtil.showElement(id);
|
||||
} else {
|
||||
UIUtil.hideElement(id);
|
||||
}
|
||||
UIUtil.setVisible(id, shouldShow);
|
||||
},
|
||||
|
||||
// checks whether desktop sharing is enabled and whether
|
||||
@@ -446,63 +416,15 @@ Toolbar = {
|
||||
&& UIUtil.isButtonEnabled('sip') && show;
|
||||
let id = 'toolbar_button_sip';
|
||||
|
||||
if (shouldShow) {
|
||||
UIUtil.showElement(id);
|
||||
} else {
|
||||
UIUtil.hideElement(id);
|
||||
}
|
||||
UIUtil.setVisible(id, shouldShow);
|
||||
},
|
||||
|
||||
// Shows or hides the dialpad button
|
||||
showDialPadButton (show) {
|
||||
let shouldShow = UIUtil.isButtonEnabled('dialpad') && show;
|
||||
let id = 'toolbar_button_dialpad';
|
||||
if (shouldShow) {
|
||||
UIUtil.showElement(id);
|
||||
} else {
|
||||
UIUtil.hideElement(id);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays user authenticated identity name(login).
|
||||
* @param authIdentity identity name to be displayed.
|
||||
*/
|
||||
setAuthenticatedIdentity (authIdentity) {
|
||||
let id = 'toolbar_auth_identity';
|
||||
if(authIdentity) {
|
||||
UIUtil.showElement(id);
|
||||
$(`#${id}`).text(authIdentity);
|
||||
} else {
|
||||
UIUtil.hideElement(id);
|
||||
$(`#${id}`).text('');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows/hides login button.
|
||||
* @param show <tt>true</tt> to show
|
||||
*/
|
||||
showLoginButton (show) {
|
||||
let id = 'toolbar_button_login';
|
||||
if (show) {
|
||||
UIUtil.showElement(id);
|
||||
} else {
|
||||
UIUtil.hideElement(id);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows/hides logout button.
|
||||
* @param show <tt>true</tt> to show
|
||||
*/
|
||||
showLogoutButton (show) {
|
||||
let id = 'toolbar_button_logout';
|
||||
if (show) {
|
||||
UIUtil.showElement(id);
|
||||
} else {
|
||||
UIUtil.hideElement(id);
|
||||
}
|
||||
UIUtil.setVisible(id, shouldShow);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -69,7 +69,7 @@ var JitsiPopover = (function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the popover
|
||||
* Hides the popover if not hovered or popover is not shown.
|
||||
*/
|
||||
JitsiPopover.prototype.hide = function () {
|
||||
if(!this.elementIsHovered && !this.popoverIsHovered &&
|
||||
@@ -79,7 +79,7 @@ var JitsiPopover = (function () {
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the popover.
|
||||
* Hides the popover and clears the document elements added by popover.
|
||||
*/
|
||||
JitsiPopover.prototype.forceHide = function () {
|
||||
$(".jitsipopover").remove();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* global $, APP, toastr */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import UIUtil from './UIUtil';
|
||||
import jitsiLocalStorage from '../../util/JitsiLocalStorage';
|
||||
@@ -79,7 +80,7 @@ function dontShowTheDialog(options) {
|
||||
function dontShowAgainSubmitFunctionWrapper(options, submitFunction) {
|
||||
if(isDontShowAgainEnabled(options)) {
|
||||
return (...args) => {
|
||||
console.debug(args, options.buttonValues);
|
||||
logger.debug(args, options.buttonValues);
|
||||
//args[1] is the value associated with the pressed button
|
||||
if(!options.buttonValues || options.buttonValues.length === 0
|
||||
|| options.buttonValues.indexOf(args[1]) !== -1 ) {
|
||||
@@ -127,7 +128,8 @@ var messageHandler = {
|
||||
return null;
|
||||
|
||||
let dialog = $.prompt(
|
||||
APP.translation.generateTranslationHTML(messageKey, i18nOptions), {
|
||||
APP.translation.generateTranslationHTML(messageKey, i18nOptions),
|
||||
{
|
||||
title: this._getFormattedTitleString(titleKey),
|
||||
persistent: false,
|
||||
promptspeed: 0,
|
||||
@@ -289,7 +291,15 @@ var messageHandler = {
|
||||
buttons: buttons,
|
||||
defaultButton: 1,
|
||||
promptspeed: 0,
|
||||
loaded: loadedFunction,
|
||||
loaded: function() {
|
||||
if (loadedFunction) {
|
||||
loadedFunction.apply(this, arguments);
|
||||
}
|
||||
// Hide the close button
|
||||
if (persistent) {
|
||||
$(".jqiclose", this).hide();
|
||||
}
|
||||
},
|
||||
submit: dontShowAgainSubmitFunctionWrapper(
|
||||
dontShowAgain, submitFunction),
|
||||
close: closeFunction,
|
||||
@@ -408,7 +418,7 @@ var messageHandler = {
|
||||
*/
|
||||
openReportDialog: function(titleKey, msgKey, error) {
|
||||
this.openMessageDialog(titleKey, msgKey);
|
||||
console.log(error);
|
||||
logger.log(error);
|
||||
//FIXME send the error to the server
|
||||
},
|
||||
|
||||
@@ -438,7 +448,7 @@ var messageHandler = {
|
||||
* @param messageKey the key from the language file for the text of the
|
||||
* message.
|
||||
* @param messageArguments object with the arguments for the message.
|
||||
* @param options object with language options.
|
||||
* @param options passed to toastr (e.g. timeOut)
|
||||
*/
|
||||
notify: function(displayName, displayNameKey, cls, messageKey,
|
||||
messageArguments, options) {
|
||||
|
||||
@@ -240,11 +240,13 @@ const IndicatorFontSizes = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Shows the element given by id.
|
||||
* Shows / hides the element given by id.
|
||||
*
|
||||
* @param {String} the identifier of the element to show
|
||||
* @param {string|HTMLElement} idOrElement the identifier or the element
|
||||
* to show/hide
|
||||
* @param {boolean} show <tt>true</tt> to show or <tt>false</tt> to hide
|
||||
*/
|
||||
showElement(id) {
|
||||
setVisible(id, visible) {
|
||||
let element;
|
||||
if (id instanceof HTMLElement) {
|
||||
element = id;
|
||||
@@ -256,40 +258,20 @@ const IndicatorFontSizes = {
|
||||
return;
|
||||
}
|
||||
|
||||
if(element.classList.contains('hide')) {
|
||||
if (!visible)
|
||||
element.classList.add('hide');
|
||||
else if (element.classList.contains('hide')) {
|
||||
element.classList.remove('hide');
|
||||
}
|
||||
|
||||
let type = this._getElementDefaultDisplay(element.tagName);
|
||||
let className = SHOW_CLASSES[type];
|
||||
element.classList.add(className);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides the element given by id.
|
||||
*
|
||||
* @param {String} the identifier of the element to hide
|
||||
*/
|
||||
hideElement(id) {
|
||||
let element;
|
||||
if (id instanceof HTMLElement) {
|
||||
element = id;
|
||||
} else {
|
||||
element = document.getElementById(id);
|
||||
if (visible) {
|
||||
element.classList.add(className);
|
||||
}
|
||||
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
let type = this._getElementDefaultDisplay(element.tagName);
|
||||
let className = SHOW_CLASSES[type];
|
||||
|
||||
if(element.classList.contains(className)) {
|
||||
else if (element.classList.contains(className))
|
||||
element.classList.remove(className);
|
||||
}
|
||||
|
||||
element.classList.add('hide');
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -311,12 +293,13 @@ const IndicatorFontSizes = {
|
||||
/**
|
||||
* Shows / hides the element with the given jQuery selector.
|
||||
*
|
||||
* @param {jQuery} selector the jQuery selector of the element to show/hide
|
||||
* @param {jQuery} jquerySelector the jQuery selector of the element to
|
||||
* show / shide
|
||||
* @param {boolean} isVisible
|
||||
*/
|
||||
setVisibility(selector, isVisible) {
|
||||
if (selector && selector.length > 0) {
|
||||
selector.css("visibility", isVisible ? "visible" : "hidden");
|
||||
setVisibleBySelector(jquerySelector, isVisible) {
|
||||
if (jquerySelector && jquerySelector.length > 0) {
|
||||
jquerySelector.css("visibility", isVisible ? "visible" : "hidden");
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -57,9 +57,12 @@ const FilmStrip = {
|
||||
* Attach 'click' listener to "hide filmstrip" button
|
||||
*/
|
||||
registerListeners() {
|
||||
let toggleFilmstripMethod = this.toggleFilmStrip.bind(this);
|
||||
let selector = '#hideVideoToolbar';
|
||||
$('#videospace').on('click', selector, toggleFilmstripMethod);
|
||||
// Important:
|
||||
// Firing the event instead of executing toggleFilmstrip method because
|
||||
// it's important to hide the filmstrip by UI.toggleFilmstrip in order
|
||||
// to correctly resize the video area.
|
||||
$('#hideVideoToolbar').on('click',
|
||||
() => this.eventEmitter.emit(UIEvents.TOGGLE_FILM_STRIP));
|
||||
|
||||
this._registerToggleFilmstripShortcut();
|
||||
},
|
||||
@@ -72,10 +75,11 @@ const FilmStrip = {
|
||||
let shortcut = 'F';
|
||||
let shortcutAttr = 'filmstripPopover';
|
||||
let description = 'keyboardShortcuts.toggleFilmstrip';
|
||||
let handler = () => {
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.filmstrip.toggled');
|
||||
this.eventEmitter.emit(UIEvents.TOGGLE_FILM_STRIP);
|
||||
};
|
||||
// Important:
|
||||
// Firing the event instead of executing toggleFilmstrip method because
|
||||
// it's important to hide the filmstrip by UI.toggleFilmstrip in order
|
||||
// to correctly resize the video area.
|
||||
let handler = () => this.eventEmitter.emit(UIEvents.TOGGLE_FILM_STRIP);
|
||||
|
||||
APP.keyboardshortcut.registerShortcut(
|
||||
shortcut,
|
||||
@@ -110,6 +114,11 @@ const FilmStrip = {
|
||||
* of the film strip. If not specified, the visibility will be flipped
|
||||
* (i.e. toggled); otherwise, the visibility will be set to the specified
|
||||
* value.
|
||||
*
|
||||
* Note:
|
||||
* This method shouldn't be executed directly to hide the filmstrip.
|
||||
* It's important to hide the filmstrip with UI.toggleFilmstrip in order
|
||||
* to correctly resize the video area.
|
||||
*/
|
||||
toggleFilmStrip(visible) {
|
||||
let isVisibleDefined = typeof visible === 'boolean';
|
||||
@@ -118,7 +127,7 @@ const FilmStrip = {
|
||||
} else if (this.isFilmStripVisible() === visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
JitsiMeetJS.analytics.sendEvent('toolbar.filmstrip.toggled');
|
||||
this.filmStrip.toggleClass("hidden");
|
||||
|
||||
if (!visible) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* global $, APP, interfaceConfig */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import Avatar from "../avatar/Avatar";
|
||||
import {createDeferred} from '../../util/helpers';
|
||||
@@ -126,7 +127,7 @@ export default class LargeVideoManager {
|
||||
const { id, stream, videoType, resolve } = this.newStreamData;
|
||||
this.newStreamData = null;
|
||||
|
||||
console.info("hover in %s", id);
|
||||
logger.info("hover in %s", id);
|
||||
this.state = videoType;
|
||||
const container = this.getContainer(this.state);
|
||||
container.setStream(stream, videoType);
|
||||
@@ -333,13 +334,13 @@ export default class LargeVideoManager {
|
||||
}
|
||||
|
||||
let id = 'localConnectionMessage';
|
||||
|
||||
UIUtil.setVisible(id, show);
|
||||
|
||||
if (show) {
|
||||
UIUtil.showElement(id);
|
||||
// Avatar message conflicts with 'videoConnectionMessage',
|
||||
// so it must be hidden
|
||||
this.showRemoteConnectionMessage(false);
|
||||
} else {
|
||||
UIUtil.hideElement(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* global $, config, interfaceConfig, APP, JitsiMeetJS */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import ConnectionIndicator from "./ConnectionIndicator";
|
||||
import UIUtil from "../util/UIUtil";
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
@@ -40,7 +42,7 @@ LocalVideo.prototype.constructor = LocalVideo;
|
||||
*/
|
||||
LocalVideo.prototype.setDisplayName = function(displayName) {
|
||||
if (!this.container) {
|
||||
console.warn(
|
||||
logger.warn(
|
||||
"Unable to set displayName - " + this.videoSpanId +
|
||||
" does not exist");
|
||||
return;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* global $, APP, interfaceConfig */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import ConnectionIndicator from './ConnectionIndicator';
|
||||
|
||||
@@ -105,6 +106,14 @@ RemoteVideo.prototype._initPopupMenu = function (popupMenuElement) {
|
||||
// call the original show, passing its actual this
|
||||
origShowFunc.call(this.popover);
|
||||
}.bind(this);
|
||||
|
||||
// override popover hide method so we can cleanup click handlers
|
||||
let origHideFunc = this.popover.forceHide;
|
||||
this.popover.forceHide = function () {
|
||||
$(document).off("click", '#mutelink_' + this.id);
|
||||
$(document).off("click", '#ejectlink_' + this.id);
|
||||
origHideFunc.call(this.popover);
|
||||
}.bind(this);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -183,7 +192,7 @@ RemoteVideo.prototype._muteHandler = function () {
|
||||
}
|
||||
}).catch(e => {
|
||||
//currently shouldn't be called
|
||||
console.error(e);
|
||||
logger.error(e);
|
||||
});
|
||||
|
||||
this.popover.forceHide();
|
||||
@@ -335,7 +344,7 @@ RemoteVideo.prototype.removeRemoteStreamElement = function (stream) {
|
||||
this.wasVideoPlayed = false;
|
||||
}
|
||||
|
||||
console.info((isVideo ? "Video" : "Audio") +
|
||||
logger.info((isVideo ? "Video" : "Audio") +
|
||||
" removed " + this.id, select);
|
||||
|
||||
// when removing only the video element and we are on stage
|
||||
@@ -400,7 +409,7 @@ RemoteVideo.prototype.updateConnectionStatusIndicator = function (isActive) {
|
||||
}
|
||||
}
|
||||
|
||||
console.debug(this.id + " thumbnail is connection active ? " + isActive);
|
||||
logger.debug(this.id + " thumbnail is connection active ? " + isActive);
|
||||
|
||||
// Update 'mutedWhileDisconnected' flag
|
||||
this._figureOutMutedWhileDisconnected(!isActive);
|
||||
@@ -419,7 +428,7 @@ RemoteVideo.prototype.updateConnectionStatusIndicator = function (isActive) {
|
||||
* Removes RemoteVideo from the page.
|
||||
*/
|
||||
RemoteVideo.prototype.remove = function () {
|
||||
console.log("Remove thumbnail", this.id);
|
||||
logger.log("Remove thumbnail", this.id);
|
||||
this.removeConnectionIndicator();
|
||||
// Make sure that the large video is updated if are removing its
|
||||
// corresponding small video.
|
||||
@@ -578,7 +587,7 @@ RemoteVideo.prototype.hideConnectionIndicator = function () {
|
||||
*/
|
||||
RemoteVideo.prototype.setDisplayName = function(displayName) {
|
||||
if (!this.container) {
|
||||
console.warn( "Unable to set displayName - " + this.videoSpanId +
|
||||
logger.warn( "Unable to set displayName - " + this.videoSpanId +
|
||||
" does not exist");
|
||||
return;
|
||||
}
|
||||
@@ -628,7 +637,7 @@ RemoteVideo.prototype.removeRemoteVideoMenu = function() {
|
||||
RemoteVideo.createContainer = function (spanId) {
|
||||
let container = document.createElement('span');
|
||||
container.id = spanId;
|
||||
container.className = 'videocontainer videocontainer_remote';
|
||||
container.className = 'videocontainer';
|
||||
|
||||
let wrapper = document.createElement('div');
|
||||
wrapper.className = 'videocontainer__background';
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* global $, JitsiMeetJS, interfaceConfig */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import Avatar from "../avatar/Avatar";
|
||||
import UIUtil from "../util/UIUtil";
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
@@ -215,13 +217,10 @@ SmallVideo.prototype.hideIndicator = function () {
|
||||
* @param {boolean} isMuted indicates if the muted element should be shown
|
||||
* or hidden
|
||||
*/
|
||||
SmallVideo.prototype.showAudioIndicator = function(isMuted) {
|
||||
SmallVideo.prototype.showAudioIndicator = function (isMuted) {
|
||||
let mutedIndicator = this.getAudioMutedIndicator();
|
||||
if (isMuted) {
|
||||
UIUtil.showElement(mutedIndicator);
|
||||
} else {
|
||||
UIUtil.hideElement(mutedIndicator);
|
||||
}
|
||||
|
||||
UIUtil.setVisible(mutedIndicator, isMuted);
|
||||
|
||||
this.isAudioMuted = isMuted;
|
||||
};
|
||||
@@ -270,11 +269,8 @@ SmallVideo.prototype.setVideoMutedView = function(isMuted) {
|
||||
this.updateView();
|
||||
|
||||
let element = this.getVideoMutedIndicator();
|
||||
if (isMuted) {
|
||||
UIUtil.showElement(element);
|
||||
} else {
|
||||
UIUtil.hideElement(element);
|
||||
}
|
||||
|
||||
UIUtil.setVisible(element, isMuted);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -503,7 +499,7 @@ SmallVideo.prototype.updateView = function () {
|
||||
// Init avatar
|
||||
this.avatarChanged(Avatar.getAvatarUrl(this.id));
|
||||
} else {
|
||||
console.error("Unable to init avatar - no id", this);
|
||||
logger.error("Unable to init avatar - no id", this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -511,24 +507,24 @@ SmallVideo.prototype.updateView = function () {
|
||||
// Determine whether video, avatar or blackness should be displayed
|
||||
let displayMode = this.selectDisplayMode();
|
||||
// Show/hide video.
|
||||
UIUtil.setVisibility( this.selectVideoElement(),
|
||||
(displayMode === DISPLAY_VIDEO
|
||||
UIUtil.setVisibleBySelector(this.selectVideoElement(),
|
||||
(displayMode === DISPLAY_VIDEO
|
||||
|| displayMode === DISPLAY_VIDEO_WITH_NAME));
|
||||
// Show/hide the avatar.
|
||||
UIUtil.setVisibility( this.$avatar(),
|
||||
(displayMode === DISPLAY_AVATAR
|
||||
UIUtil.setVisibleBySelector(this.$avatar(),
|
||||
(displayMode === DISPLAY_AVATAR
|
||||
|| displayMode === DISPLAY_AVATAR_WITH_NAME));
|
||||
// Show/hide the display name.
|
||||
UIUtil.setVisibility( this.$displayName(),
|
||||
!this.hideDisplayName
|
||||
&& (displayMode === DISPLAY_BLACKNESS_WITH_NAME
|
||||
UIUtil.setVisibleBySelector(this.$displayName(),
|
||||
!this.hideDisplayName
|
||||
&& (displayMode === DISPLAY_BLACKNESS_WITH_NAME
|
||||
|| displayMode === DISPLAY_VIDEO_WITH_NAME
|
||||
|| displayMode === DISPLAY_AVATAR_WITH_NAME));
|
||||
// show hide overlay when there is a video or avatar under
|
||||
// the display name
|
||||
UIUtil.setVisibility( $('#' + this.videoSpanId
|
||||
UIUtil.setVisibleBySelector($('#' + this.videoSpanId
|
||||
+ ' .videocontainer__hoverOverlay'),
|
||||
(displayMode === DISPLAY_AVATAR_WITH_NAME
|
||||
(displayMode === DISPLAY_AVATAR_WITH_NAME
|
||||
|| displayMode === DISPLAY_VIDEO_WITH_NAME));
|
||||
};
|
||||
|
||||
@@ -561,7 +557,7 @@ SmallVideo.prototype.showDominantSpeakerIndicator = function (show) {
|
||||
return;
|
||||
|
||||
if (!this.container) {
|
||||
console.warn( "Unable to set dominant speaker indicator - "
|
||||
logger.warn( "Unable to set dominant speaker indicator - "
|
||||
+ this.videoSpanId + " does not exist");
|
||||
return;
|
||||
}
|
||||
@@ -576,11 +572,7 @@ SmallVideo.prototype.showDominantSpeakerIndicator = function (show) {
|
||||
tooltip: 'speaker'
|
||||
});
|
||||
|
||||
if (show) {
|
||||
UIUtil.showElement(indicatorSpan);
|
||||
} else {
|
||||
UIUtil.hideElement(indicatorSpan);
|
||||
}
|
||||
UIUtil.setVisible(indicatorSpan, show);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -589,7 +581,7 @@ SmallVideo.prototype.showDominantSpeakerIndicator = function (show) {
|
||||
*/
|
||||
SmallVideo.prototype.showRaisedHandIndicator = function (show) {
|
||||
if (!this.container) {
|
||||
console.warn( "Unable to raised hand indication - "
|
||||
logger.warn( "Unable to raised hand indication - "
|
||||
+ this.videoSpanId + " does not exist");
|
||||
return;
|
||||
}
|
||||
@@ -604,11 +596,7 @@ SmallVideo.prototype.showRaisedHandIndicator = function (show) {
|
||||
tooltip: 'raisedHand'
|
||||
});
|
||||
|
||||
if (show) {
|
||||
UIUtil.showElement(indicatorSpan);
|
||||
} else {
|
||||
UIUtil.hideElement(indicatorSpan);
|
||||
}
|
||||
UIUtil.setVisible(indicatorSpan, show);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* global config, APP, $, interfaceConfig */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import FilmStrip from "./FilmStrip";
|
||||
import UIEvents from "../../../service/UI/UIEvents";
|
||||
@@ -261,19 +262,19 @@ var VideoLayout = {
|
||||
if (lastVisible.length) {
|
||||
let id = getPeerContainerResourceId(lastVisible[0]);
|
||||
if (remoteVideos[id]) {
|
||||
console.info("electLastVisibleVideo: " + id);
|
||||
logger.info("electLastVisibleVideo: " + id);
|
||||
return id;
|
||||
}
|
||||
// The RemoteVideo was removed (but the DOM elements may still
|
||||
// exist).
|
||||
}
|
||||
|
||||
console.info("Last visible video no longer exists");
|
||||
logger.info("Last visible video no longer exists");
|
||||
thumbs = FilmStrip.getThumbs().remoteThumbs;
|
||||
if (thumbs.length) {
|
||||
let id = getPeerContainerResourceId(thumbs[0]);
|
||||
if (remoteVideos[id]) {
|
||||
console.info("electLastVisibleVideo: " + id);
|
||||
logger.info("electLastVisibleVideo: " + id);
|
||||
return id;
|
||||
}
|
||||
// The RemoteVideo was removed (but the DOM elements may
|
||||
@@ -281,10 +282,10 @@ var VideoLayout = {
|
||||
}
|
||||
|
||||
// Go with local video
|
||||
console.info("Fallback to local video...");
|
||||
logger.info("Fallback to local video...");
|
||||
|
||||
let id = APP.conference.getMyUserId();
|
||||
console.info("electLastVisibleVideo: " + id);
|
||||
logger.info("electLastVisibleVideo: " + id);
|
||||
|
||||
return id;
|
||||
},
|
||||
@@ -341,14 +342,20 @@ var VideoLayout = {
|
||||
* @param id the identifier of the video thumbnail
|
||||
*/
|
||||
handleVideoThumbClicked (id) {
|
||||
var smallVideo = VideoLayout.getSmallVideo(id);
|
||||
if(pinnedId) {
|
||||
var oldSmallVideo = VideoLayout.getSmallVideo(pinnedId);
|
||||
if (oldSmallVideo && !interfaceConfig.filmStripOnly)
|
||||
if (oldSmallVideo && !interfaceConfig.filmStripOnly) {
|
||||
oldSmallVideo.focus(false);
|
||||
// as no pinned event will be sent for local video
|
||||
// and we will unpin old one, lets signal it
|
||||
// otherwise we will just send the new pinned one
|
||||
if (smallVideo.isLocal)
|
||||
eventEmitter.emit(
|
||||
UIEvents.PINNED_ENDPOINT, oldSmallVideo, false);
|
||||
}
|
||||
}
|
||||
|
||||
var smallVideo = VideoLayout.getSmallVideo(id);
|
||||
|
||||
// Unpin if currently pinned.
|
||||
if (pinnedId === id)
|
||||
{
|
||||
@@ -358,6 +365,12 @@ var VideoLayout = {
|
||||
if(smallVideo && smallVideo.hasVideo()) {
|
||||
this.updateLargeVideo(currentDominantSpeaker);
|
||||
}
|
||||
} else {
|
||||
// if there is no currentDominantSpeaker, it can also be
|
||||
// that local participant is the dominant speaker
|
||||
// we should act as a participant has left and was on large
|
||||
// and we should choose somebody (electLastVisibleVideo)
|
||||
this.updateLargeVideo(this.electLastVisibleVideo());
|
||||
}
|
||||
|
||||
eventEmitter.emit(UIEvents.PINNED_ENDPOINT, smallVideo, false);
|
||||
@@ -426,7 +439,7 @@ var VideoLayout = {
|
||||
// FIXME: what does this do???
|
||||
remoteVideoActive(videoElement, resourceJid) {
|
||||
|
||||
console.info(resourceJid + " video is now active", videoElement);
|
||||
logger.info(resourceJid + " video is now active", videoElement);
|
||||
|
||||
VideoLayout.resizeThumbnails(
|
||||
false, false, function() {$(videoElement).show();});
|
||||
@@ -724,11 +737,11 @@ var VideoLayout = {
|
||||
if (resourceJid &&
|
||||
lastNEndpoints.indexOf(resourceJid) < 0 &&
|
||||
localLastNSet.indexOf(resourceJid) < 0) {
|
||||
console.log("Remove from last N", resourceJid);
|
||||
logger.log("Remove from last N", resourceJid);
|
||||
if (smallVideo)
|
||||
smallVideo.showPeerContainer('hide');
|
||||
else if (!APP.conference.isLocalId(resourceJid))
|
||||
console.error("No remote video for: " + resourceJid);
|
||||
logger.error("No remote video for: " + resourceJid);
|
||||
isReceived = false;
|
||||
} else if (resourceJid &&
|
||||
//TOFIX: smallVideo may be undefined
|
||||
@@ -741,7 +754,7 @@ var VideoLayout = {
|
||||
if (smallVideo)
|
||||
smallVideo.showPeerContainer('avatar');
|
||||
else if (!APP.conference.isLocalId(resourceJid))
|
||||
console.error("No remote video for: " + resourceJid);
|
||||
logger.error("No remote video for: " + resourceJid);
|
||||
isReceived = false;
|
||||
}
|
||||
|
||||
@@ -768,7 +781,7 @@ var VideoLayout = {
|
||||
remoteVideo.showPeerContainer('show');
|
||||
|
||||
if (!remoteVideo.isVisible()) {
|
||||
console.log("Add to last N", resourceJid);
|
||||
logger.log("Add to last N", resourceJid);
|
||||
|
||||
remoteVideo.addRemoteStreamElement(remoteVideo.videoStream);
|
||||
|
||||
@@ -869,23 +882,23 @@ var VideoLayout = {
|
||||
removeParticipantContainer (id) {
|
||||
// Unlock large video
|
||||
if (pinnedId === id) {
|
||||
console.info("Focused video owner has left the conference");
|
||||
logger.info("Focused video owner has left the conference");
|
||||
pinnedId = null;
|
||||
}
|
||||
|
||||
if (currentDominantSpeaker === id) {
|
||||
console.info("Dominant speaker has left the conference");
|
||||
logger.info("Dominant speaker has left the conference");
|
||||
currentDominantSpeaker = null;
|
||||
}
|
||||
|
||||
var remoteVideo = remoteVideos[id];
|
||||
if (remoteVideo) {
|
||||
// Remove remote video
|
||||
console.info("Removing remote video: " + id);
|
||||
logger.info("Removing remote video: " + id);
|
||||
delete remoteVideos[id];
|
||||
remoteVideo.remove();
|
||||
} else {
|
||||
console.warn("No remote video for " + id);
|
||||
logger.warn("No remote video for " + id);
|
||||
}
|
||||
|
||||
VideoLayout.resizeThumbnails();
|
||||
@@ -896,12 +909,12 @@ var VideoLayout = {
|
||||
return;
|
||||
}
|
||||
|
||||
console.info("Peer video type changed: ", id, newVideoType);
|
||||
logger.info("Peer video type changed: ", id, newVideoType);
|
||||
|
||||
var smallVideo;
|
||||
if (APP.conference.isLocalId(id)) {
|
||||
if (!localVideoThumbnail) {
|
||||
console.warn("Local video not ready yet");
|
||||
logger.warn("Local video not ready yet");
|
||||
return;
|
||||
}
|
||||
smallVideo = localVideoThumbnail;
|
||||
@@ -925,7 +938,7 @@ var VideoLayout = {
|
||||
if (remoteVideo) {
|
||||
remoteVideo.connectionIndicator.showMore();
|
||||
} else {
|
||||
console.info("Error - no remote video for id: " + id);
|
||||
logger.info("Error - no remote video for id: " + id);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -982,7 +995,7 @@ var VideoLayout = {
|
||||
if (smallVideo) {
|
||||
smallVideo.avatarChanged(avatarUrl);
|
||||
} else {
|
||||
console.warn(
|
||||
logger.warn(
|
||||
"Missed avatar update - no small video yet for " + id
|
||||
);
|
||||
}
|
||||
@@ -1138,11 +1151,7 @@ var VideoLayout = {
|
||||
updateResolutionLabel(isResolutionHD) {
|
||||
let id = 'videoResolutionLabel';
|
||||
|
||||
if (isResolutionHD) {
|
||||
UIUtil.showElement(id);
|
||||
} else {
|
||||
UIUtil.hideElement(id);
|
||||
}
|
||||
UIUtil.setVisible(id, isResolutionHD);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global console */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import { redirect } from '../util/helpers';
|
||||
|
||||
@@ -45,8 +45,8 @@ export default class ConferenceUrl {
|
||||
*/
|
||||
this.inviteURL
|
||||
= location.protocol + "//" + location.host + location.pathname;
|
||||
console.info("Stored original conference URL: " + this.originalURL);
|
||||
console.info("Conference URL for invites: " + this.inviteURL);
|
||||
logger.info("Stored original conference URL: " + this.originalURL);
|
||||
logger.info("Conference URL for invites: " + this.inviteURL);
|
||||
}
|
||||
/**
|
||||
* Obtains the conference invite URL.
|
||||
@@ -67,7 +67,7 @@ export default class ConferenceUrl {
|
||||
* Reloads the conference using original URL with all of the parameters.
|
||||
*/
|
||||
reload() {
|
||||
console.info("Reloading the conference using URL: " + this.originalURL);
|
||||
logger.info("Reloading the conference using URL: " + this.originalURL);
|
||||
redirect(this.originalURL);
|
||||
}
|
||||
}
|
||||
|
||||
128
modules/analytics/analytics.js
Normal file
128
modules/analytics/analytics.js
Normal file
@@ -0,0 +1,128 @@
|
||||
/* global JitsiMeetJS, config, APP */
|
||||
/**
|
||||
* Load the integration of a third-party analytics API such as Google
|
||||
* Analytics. Since we cannot guarantee the quality of the third-party service
|
||||
* (e.g. their server may take noticeably long time to respond), it is in our
|
||||
* best interest (in the sense that the intergration of the analytics API is
|
||||
* important to us but not enough to allow it to prevent people from joining
|
||||
* a conference) to download the API asynchronously. Additionally, Google
|
||||
* Analytics will download its implementation asynchronously anyway so it makes
|
||||
* sense to append the loading on our side rather than prepend it.
|
||||
* @param {string} url the url to be loaded
|
||||
* @returns {Promise} resolved with no arguments when the script is loaded and
|
||||
* rejected with the error from JitsiMeetJS.ScriptUtil.loadScript method
|
||||
*/
|
||||
function loadScript(url) {
|
||||
return new Promise((resolve, reject) =>
|
||||
JitsiMeetJS.util.ScriptUtil.loadScript(
|
||||
url,
|
||||
/* async */ true,
|
||||
/* prepend */ false,
|
||||
/* relativeURL */ false,
|
||||
/* loadCallback */ () => resolve(),
|
||||
/* errorCallback */ error => reject(error)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the initialization of analytics.
|
||||
*/
|
||||
class Analytics {
|
||||
constructor() {
|
||||
this._scriptURLs = Array.isArray(config.analyticsScriptUrls)
|
||||
? config.analyticsScriptUrls : [];
|
||||
this._enabled = !!this._scriptURLs.length
|
||||
&& !config.disableThirdPartyRequests;
|
||||
window.analyticsHandlers = [];
|
||||
const machineId = JitsiMeetJS.getMachineId();
|
||||
this._handlerConstructorOptions = {
|
||||
product: "lib-jitsi-meet",
|
||||
version: JitsiMeetJS.version,
|
||||
session: machineId,
|
||||
user: "uid-" + machineId
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether analytics is enabled or not.
|
||||
* @returns {boolean} whether analytics is enabled or not.
|
||||
*/
|
||||
isEnabled() {
|
||||
return this._enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load the scripts for the analytics handlers.
|
||||
* @returns {Promise} resolves with the handlers that have been
|
||||
* successfully loaded and rejects if there are no handlers loaded or the
|
||||
* analytics is disabled.
|
||||
*/
|
||||
_loadHandlers() {
|
||||
if(!this.isEnabled()) {
|
||||
return Promise.reject(new Error("Analytics is disabled!"));
|
||||
}
|
||||
let handlersPromises = [];
|
||||
this._scriptURLs.forEach(url =>
|
||||
handlersPromises.push(
|
||||
loadScript(url).then(
|
||||
() => {
|
||||
return {type: "success"};
|
||||
},
|
||||
error => {
|
||||
return {type: "error", error, url};
|
||||
}))
|
||||
);
|
||||
return new Promise((resolve, reject) =>
|
||||
{
|
||||
Promise.all(handlersPromises).then(values => {
|
||||
values.forEach(el => {
|
||||
if(el.type === "error") {
|
||||
console.log("Fialed to load " + el.url);
|
||||
console.error(el.error);
|
||||
}
|
||||
});
|
||||
|
||||
if(window.analyticsHandlers.length === 0) {
|
||||
reject(new Error("No analytics handlers available"));
|
||||
} else {
|
||||
let handlerInstances = [];
|
||||
window.analyticsHandlers.forEach(
|
||||
Handler => handlerInstances.push(
|
||||
new Handler(this._handlerConstructorOptions)));
|
||||
resolve(handlerInstances);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the analytics scripts and inits JitsiMeetJS.analytics by setting
|
||||
* permanent properties and setting the handlers from the loaded scripts.
|
||||
* NOTE: Has to be used after JitsiMeetJS.init. Otherwise analytics will be
|
||||
* null.
|
||||
*/
|
||||
init() {
|
||||
let analytics = JitsiMeetJS.analytics;
|
||||
if(!this.isEnabled() || !analytics)
|
||||
return;
|
||||
|
||||
this._loadHandlers()
|
||||
.then(handlers => {
|
||||
let permanentProperties = {
|
||||
userAgent: navigator.userAgent,
|
||||
roomName: APP.conference.roomName
|
||||
};
|
||||
let {server, group} = APP.tokenData;
|
||||
if(server) {
|
||||
permanentProperties.server = server;
|
||||
}
|
||||
if(group) {
|
||||
permanentProperties.group = group;
|
||||
}
|
||||
analytics.addPermanentProperties(permanentProperties);
|
||||
analytics.setAnalyticsHandlers(handlers);
|
||||
}, error => analytics.dispose() && console.error(error));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export default new Analytics();
|
||||
@@ -1,3 +1,5 @@
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
var JSSHA = require('jssha');
|
||||
|
||||
module.exports = {
|
||||
@@ -22,7 +24,7 @@ module.exports = {
|
||||
var attemptFirstAddress;
|
||||
|
||||
config.bosh = config.boshList[idx];
|
||||
console.log('Setting config.bosh to ' + config.bosh +
|
||||
logger.log('Setting config.bosh to ' + config.bosh +
|
||||
' (idx=' + idx + ')');
|
||||
|
||||
if (config.boshAttemptFirstList &&
|
||||
@@ -34,10 +36,10 @@ module.exports = {
|
||||
|
||||
if (attemptFirstAddress != config.bosh) {
|
||||
config.boshAttemptFirst = attemptFirstAddress;
|
||||
console.log('Setting config.boshAttemptFirst=' +
|
||||
logger.log('Setting config.boshAttemptFirst=' +
|
||||
attemptFirstAddress + ' (idx=' + idx + ')');
|
||||
} else {
|
||||
console.log('Not setting boshAttemptFirst, address matches.');
|
||||
logger.log('Not setting boshAttemptFirst, address matches.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/* global $, config, interfaceConfig */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
var configUtil = require('./Util');
|
||||
|
||||
@@ -16,7 +17,7 @@ var HttpConfig = {
|
||||
* @param complete
|
||||
*/
|
||||
obtainConfig: function (endpoint, roomName, complete) {
|
||||
console.info(
|
||||
logger.info(
|
||||
"Send config request to " + endpoint + " for room: " + roomName);
|
||||
|
||||
|
||||
@@ -28,7 +29,7 @@ var HttpConfig = {
|
||||
data: JSON.stringify({"roomName": roomName}),
|
||||
dataType: 'json',
|
||||
error: function(jqXHR, textStatus, errorThrown) {
|
||||
console.error("Get config error: ", jqXHR, errorThrown);
|
||||
logger.error("Get config error: ", jqXHR, errorThrown);
|
||||
var error = "Get config response status: " + textStatus;
|
||||
complete(false, error);
|
||||
},
|
||||
@@ -39,7 +40,7 @@ var HttpConfig = {
|
||||
complete(true);
|
||||
return;
|
||||
} catch (exception) {
|
||||
console.error("Parse config error: ", exception);
|
||||
logger.error("Parse config error: ", exception);
|
||||
complete(false, exception);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* global config, interfaceConfig, getConfigParamsFromUrl */
|
||||
/* global config, interfaceConfig, loggingConfig, getConfigParamsFromUrl */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
var configUtils = require('./Util');
|
||||
var params = {};
|
||||
|
||||
@@ -25,11 +27,12 @@ var URLProcessor = {
|
||||
// }
|
||||
var configJSON = {
|
||||
config: {},
|
||||
interfaceConfig: {}
|
||||
interfaceConfig: {},
|
||||
loggingConfig: {}
|
||||
};
|
||||
for (var key in params) {
|
||||
if (typeof key !== "string") {
|
||||
console.warn("Invalid config key: ", key);
|
||||
logger.warn("Invalid config key: ", key);
|
||||
continue;
|
||||
}
|
||||
var confObj = null, confKey;
|
||||
@@ -38,13 +41,16 @@ var URLProcessor = {
|
||||
confKey = key.substr("config.".length);
|
||||
|
||||
// prevent passing some parameters which can inject scripts
|
||||
if (confKey === 'analyticsScriptUrl'
|
||||
if (confKey === 'analyticsScriptUrls'
|
||||
|| confKey === 'callStatsCustomScriptUrl')
|
||||
continue;
|
||||
|
||||
} else if (key.indexOf("interfaceConfig.") === 0) {
|
||||
confObj = configJSON.interfaceConfig;
|
||||
confKey = key.substr("interfaceConfig.".length);
|
||||
} else if (key.indexOf("loggingConfig.") === 0) {
|
||||
confObj = configJSON.loggingConfig;
|
||||
confKey = key.substr("loggingConfig.".length);
|
||||
}
|
||||
|
||||
if (!confObj)
|
||||
@@ -52,7 +58,8 @@ var URLProcessor = {
|
||||
|
||||
confObj[confKey] = params[key];
|
||||
}
|
||||
configUtils.overrideConfigJSON(config, interfaceConfig, configJSON);
|
||||
configUtils.overrideConfigJSON(
|
||||
config, interfaceConfig, loggingConfig, configJSON);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
var ConfigUtil = {
|
||||
/**
|
||||
* Method overrides JSON properties in <tt>config</tt> and
|
||||
@@ -5,6 +7,8 @@ var ConfigUtil = {
|
||||
* @param config the config object for which we'll be overriding properties
|
||||
* @param interfaceConfig the interfaceConfig object for which we'll be
|
||||
* overriding properties.
|
||||
* @param loggingConfig the logging config object for which we'll be
|
||||
* overriding properties.
|
||||
* @param newConfig object containing configuration properties. Destination
|
||||
* object is selected based on root property name:
|
||||
* {
|
||||
@@ -12,11 +16,15 @@ var ConfigUtil = {
|
||||
* // config.js properties to be
|
||||
* },
|
||||
* interfaceConfig: {
|
||||
* // interfaceConfig.js properties here
|
||||
* // interface_config.js properties here
|
||||
* },
|
||||
* loggingConfig: {
|
||||
* // logging_config.js properties here
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
overrideConfigJSON: function (config, interfaceConfig, newConfig) {
|
||||
overrideConfigJSON: function (config,
|
||||
interfaceConfig, loggingConfig, newConfig) {
|
||||
var configRoot, key, value, confObj;
|
||||
for (configRoot in newConfig) {
|
||||
confObj = null;
|
||||
@@ -24,6 +32,8 @@ var ConfigUtil = {
|
||||
confObj = config;
|
||||
} else if (configRoot == "interfaceConfig") {
|
||||
confObj = interfaceConfig;
|
||||
} else if (configRoot == "loggingConfig") {
|
||||
confObj = loggingConfig;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
@@ -31,10 +41,10 @@ var ConfigUtil = {
|
||||
for (key in newConfig[configRoot]) {
|
||||
value = newConfig[configRoot][key];
|
||||
if (confObj[key] && typeof confObj[key] !== typeof value) {
|
||||
console.log("Overriding a " + configRoot +
|
||||
logger.log("Overriding a " + configRoot +
|
||||
" property with a property of different type.");
|
||||
}
|
||||
console.info("Overriding " + key + " with: " + value);
|
||||
logger.info("Overriding " + key + " with: " + value);
|
||||
confObj[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/* global JitsiMeetJS */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import UIUtil from '../UI/util/UIUtil';
|
||||
import jitsiLocalStorage from '../util/JitsiLocalStorage';
|
||||
|
||||
@@ -9,11 +11,6 @@ function generateUniqueId() {
|
||||
return _p8() + _p8() + _p8() + _p8();
|
||||
}
|
||||
|
||||
if (!jitsiLocalStorage.getItem("jitsiMeetId")) {
|
||||
jitsiLocalStorage.setItem("jitsiMeetId",generateUniqueId());
|
||||
console.log("generated id", jitsiLocalStorage.getItem("jitsiMeetId"));
|
||||
}
|
||||
|
||||
let avatarUrl = '';
|
||||
|
||||
let email = UIUtil.unescapeHtml(jitsiLocalStorage.getItem("email") || '');
|
||||
@@ -42,7 +39,7 @@ if (audioOutputDeviceId !==
|
||||
JitsiMeetJS.mediaDevices.getAudioOutputDevice()) {
|
||||
JitsiMeetJS.mediaDevices.setAudioOutputDevice(audioOutputDeviceId)
|
||||
.catch((ex) => {
|
||||
console.warn('Failed to set audio output device from local ' +
|
||||
logger.warn('Failed to set audio output device from local ' +
|
||||
'storage. Default audio output device will be used' +
|
||||
'instead.', ex);
|
||||
});
|
||||
|
||||
66
modules/util/JitsiMeetLogStorage.js
Normal file
66
modules/util/JitsiMeetLogStorage.js
Normal file
@@ -0,0 +1,66 @@
|
||||
/* global APP */
|
||||
|
||||
/**
|
||||
* Implements logs storage through the CallStats.
|
||||
*/
|
||||
export default class JitsiMeetLogStorage {
|
||||
|
||||
/**
|
||||
* Creates new <tt>JitsiMeetLogStorage</tt>
|
||||
*/
|
||||
constructor() {
|
||||
/**
|
||||
* Counts each log entry, increases on every batch log entry stored.
|
||||
* @type {number}
|
||||
*/
|
||||
this.counter = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean} <tt>true</tt> when this storage is ready or
|
||||
* <tt>false</tt> otherwise.
|
||||
*/
|
||||
isReady() {
|
||||
return APP.logCollectorStarted && APP.conference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the <tt>LogCollector</tt> to store a series of log lines into
|
||||
* batch.
|
||||
* @param {string|object[]}logEntries an array containing strings
|
||||
* representing log lines or aggregated lines objects.
|
||||
*/
|
||||
storeLogs(logEntries) {
|
||||
|
||||
if (!APP.conference.isCallstatsEnabled()) {
|
||||
// Discard the logs if CallStats is not enabled.
|
||||
return;
|
||||
}
|
||||
|
||||
let logJSON = '{"log' + this.counter + '":"\n';
|
||||
for (let i = 0, len = logEntries.length; i < len; i++) {
|
||||
let logEntry = logEntries[i];
|
||||
if (typeof logEntry === 'object') {
|
||||
// Aggregated message
|
||||
logJSON += '(' + logEntry.count + ') ' + logEntry.text + '\n';
|
||||
} else {
|
||||
// Regular message
|
||||
logJSON += logEntry + '\n';
|
||||
}
|
||||
}
|
||||
logJSON += '"}';
|
||||
|
||||
this.counter += 1;
|
||||
|
||||
// Try catch was used, because there are many variables
|
||||
// on the way that could be uninitialized if the storeLogs
|
||||
// attempt would be made very early (which is unlikely)
|
||||
try {
|
||||
APP.conference.logJSON(logJSON);
|
||||
} catch (error) {
|
||||
// NOTE console is intentional here
|
||||
console.error(
|
||||
"Failed to store the logs: ", logJSON, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Create deferred object.
|
||||
* @returns {{promise, resolve, reject}}
|
||||
@@ -35,7 +37,7 @@ export function redirect (url) {
|
||||
* @param msg {string} [optional] the message printed in addition to the error
|
||||
*/
|
||||
export function reportError (e, msg = "") {
|
||||
console.error(msg, e);
|
||||
logger.error(msg, e);
|
||||
if(window.onerror)
|
||||
window.onerror(msg, null, null,
|
||||
null, e);
|
||||
|
||||
33
package.json
33
package.json
@@ -20,38 +20,44 @@
|
||||
"async": "0.9.0",
|
||||
"autosize": "^1.18.13",
|
||||
"bootstrap": "3.1.1",
|
||||
"events": "*",
|
||||
"i18next": "3.4.4",
|
||||
"i18next-xhr-backend": "1.1.0",
|
||||
"jquery-i18next": "1.1.0",
|
||||
"json-loader": "0.5.4",
|
||||
"jQuery-Impromptu": "trentrichardson/jQuery-Impromptu#v6.0.0",
|
||||
"jitsi-meet-logger": "jitsi/jitsi-meet-logger",
|
||||
"jquery": "~2.1.1",
|
||||
"jquery-contextmenu": "*",
|
||||
"jquery-i18next": "1.1.0",
|
||||
"jQuery-Impromptu": "trentrichardson/jQuery-Impromptu#v6.0.0",
|
||||
"jquery-ui": "1.10.5",
|
||||
"json-loader": "0.5.4",
|
||||
"jssha": "1.5.0",
|
||||
"jws": "*",
|
||||
"lib-jitsi-meet": "jitsi/lib-jitsi-meet",
|
||||
"postis": "^2.2.0",
|
||||
"react": "15.3.2",
|
||||
"react-native": "0.37.0",
|
||||
"react-native-vector-icons": "^2.0.3",
|
||||
"react": "15.4.1",
|
||||
"react-dom": "15.4.1",
|
||||
"react-native": "0.38.0",
|
||||
"react-native-vector-icons": "^3.0.0",
|
||||
"react-native-webrtc": "jitsi/react-native-webrtc",
|
||||
"react-redux": "^4.4.5",
|
||||
"react-redux": "^4.4.6",
|
||||
"react-router": "^3.0.0",
|
||||
"react-router-redux": "^4.0.7",
|
||||
"redux": "^3.5.2",
|
||||
"redux-thunk": "^2.1.0",
|
||||
"retry": "0.6.1",
|
||||
"strophe": "^1.2.2",
|
||||
"strophejs-plugins": "^0.0.6",
|
||||
"strophe": "1.2.4",
|
||||
"strophejs-plugins": "0.0.7",
|
||||
"toastr": "^2.0.3",
|
||||
"url-polyfill": "github/url-polyfill",
|
||||
"xmldom": "^0.1.22"
|
||||
"xmldom": "^0.1.27"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-core": "*",
|
||||
"babel-loader": "*",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-loader": "^6.2.8",
|
||||
"babel-polyfill": "*",
|
||||
"babel-preset-es2015": "6.14.0",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"babel-preset-react": "^6.16.0",
|
||||
"babel-preset-stage-1": "^6.16.0",
|
||||
"clean-css": "*",
|
||||
"css-loader": "*",
|
||||
"eslint": ">=3",
|
||||
@@ -60,6 +66,7 @@
|
||||
"eslint-plugin-react-native": "*",
|
||||
"expose-loader": "*",
|
||||
"file-loader": "*",
|
||||
"haste-resolver-webpack-plugin": "^0.2.2",
|
||||
"imports-loader": "*",
|
||||
"jshint": "2.8.0",
|
||||
"node-sass": "^3.8.0",
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
module.exports = {
|
||||
'env': {
|
||||
'browser': true,
|
||||
'commonjs': true,
|
||||
'es6': true
|
||||
},
|
||||
'extends': '../.eslintrc.js',
|
||||
'parser': 'babel-eslint',
|
||||
'parserOptions': {
|
||||
'ecmaFeatures': {
|
||||
'experimentalObjectRestSpread': true,
|
||||
'jsx': true
|
||||
},
|
||||
'sourceType': 'module'
|
||||
}
|
||||
},
|
||||
'plugins': [
|
||||
'jsdoc',
|
||||
|
||||
@@ -29,6 +29,21 @@ const DEFAULT_CONFIG = {
|
||||
* @abstract
|
||||
*/
|
||||
export class AbstractApp extends Component {
|
||||
/**
|
||||
* AbstractApp component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
config: React.PropTypes.object,
|
||||
store: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* The URL, if any, with which the app was launched.
|
||||
*/
|
||||
url: React.PropTypes.string
|
||||
}
|
||||
|
||||
/**
|
||||
* Init lib-jitsi-meet and create local participant when component is going
|
||||
* to be mounted.
|
||||
@@ -113,18 +128,3 @@ export class AbstractApp extends Component {
|
||||
this.props.store.dispatch(appNavigate(url));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AbstractApp component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
AbstractApp.propTypes = {
|
||||
config: React.PropTypes.object,
|
||||
store: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* The URL, if any, with which the app was launched.
|
||||
*/
|
||||
url: React.PropTypes.string
|
||||
};
|
||||
|
||||
@@ -11,6 +11,13 @@ import { AbstractApp } from './AbstractApp';
|
||||
* @extends AbstractApp
|
||||
*/
|
||||
export class App extends AbstractApp {
|
||||
/**
|
||||
* App component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = AbstractApp.propTypes
|
||||
|
||||
/**
|
||||
* Initializes a new App instance.
|
||||
*
|
||||
@@ -126,10 +133,3 @@ export class App extends AbstractApp {
|
||||
this._openURL(event.url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* App component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
App.propTypes = AbstractApp.propTypes;
|
||||
|
||||
157
react/features/app/components/App.web.js
Normal file
157
react/features/app/components/App.web.js
Normal file
@@ -0,0 +1,157 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import {
|
||||
browserHistory,
|
||||
Route,
|
||||
Router
|
||||
} from 'react-router';
|
||||
import { push, syncHistoryWithStore } from 'react-router-redux';
|
||||
|
||||
import { getDomain } from '../../base/connection';
|
||||
import { RouteRegistry } from '../../base/navigator';
|
||||
|
||||
import { AbstractApp } from './AbstractApp';
|
||||
|
||||
/**
|
||||
* Root application component.
|
||||
*
|
||||
* @extends AbstractApp
|
||||
*/
|
||||
export class App extends AbstractApp {
|
||||
/**
|
||||
* App component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = AbstractApp.propTypes
|
||||
|
||||
/**
|
||||
* Initializes a new App instance.
|
||||
*
|
||||
* @param {Object} props - The read-only React Component props with which
|
||||
* the new instance is to be initialized.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
/**
|
||||
* Create an enhanced history that syncs navigation events with the
|
||||
* store.
|
||||
* @link https://github.com/reactjs/react-router-redux#how-it-works
|
||||
*/
|
||||
this.history = syncHistoryWithStore(browserHistory, props.store);
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onRouteEnter = this._onRouteEnter.bind(this);
|
||||
this._routerCreateElement = this._routerCreateElement.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily, prevents the super from dispatching Redux actions until they
|
||||
* are integrated into the Web App.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
componentWillMount() {
|
||||
// FIXME Do not override the super once the dispatching of Redux actions
|
||||
// is integrated into the Web App.
|
||||
}
|
||||
|
||||
/**
|
||||
* Temporarily, prevents the super from dispatching Redux actions until they
|
||||
* are integrated into the Web App.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
// FIXME Do not override the super once the dispatching of Redux actions
|
||||
// is integrated into the Web App.
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const routes = RouteRegistry.getRoutes();
|
||||
|
||||
return (
|
||||
<Provider store = { this.props.store }>
|
||||
<Router
|
||||
createElement = { this._routerCreateElement }
|
||||
history = { this.history }>
|
||||
{ routes.map(r =>
|
||||
<Route
|
||||
component = { r.component }
|
||||
key = { r.component }
|
||||
path = { r.path } />
|
||||
) }
|
||||
</Router>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to a specific Route (via platform-specific means).
|
||||
*
|
||||
* @param {Route} route - The Route to which to navigate.
|
||||
* @returns {void}
|
||||
*/
|
||||
_navigate(route) {
|
||||
let path = route.path;
|
||||
const store = this.props.store;
|
||||
|
||||
// The syntax :room bellow is defined by react-router. It "matches a URL
|
||||
// segment up to the next /, ?, or #. The matched string is called a
|
||||
// param."
|
||||
path
|
||||
= path.replace(
|
||||
/:room/g,
|
||||
store.getState()['features/base/conference'].room);
|
||||
|
||||
return store.dispatch(push(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by react-router to notify this App that a Route is about to be
|
||||
* rendered.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRouteEnter() {
|
||||
// XXX The following is mandatory. Otherwise, moving back & forward
|
||||
// through the browser's history could leave this App on the Conference
|
||||
// page without a room name.
|
||||
|
||||
// Our Router configuration (at the time of this writing) is such that
|
||||
// each Route corresponds to a single URL. Hence, entering into a Route
|
||||
// is like opening a URL.
|
||||
|
||||
// XXX In order to unify work with URLs in web and native environments,
|
||||
// we will construct URL here with correct domain from config.
|
||||
const currentDomain = getDomain(this.props.store.getState);
|
||||
const url
|
||||
= new URL(window.location.pathname, `https://${currentDomain}`)
|
||||
.toString();
|
||||
|
||||
this._openURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ReactElement from the specified component and props on behalf of
|
||||
* the associated Router.
|
||||
*
|
||||
* @param {Component} component - The component from which the ReactElement
|
||||
* is to be created.
|
||||
* @param {Object} props - The read-only React Component props with which
|
||||
* the ReactElement is to be initialized.
|
||||
* @private
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_routerCreateElement(component, props) {
|
||||
return this._createElement(component, props);
|
||||
}
|
||||
}
|
||||
@@ -1 +1,33 @@
|
||||
export * from './native';
|
||||
import './native';
|
||||
|
||||
// The library lib-jitsi-meet (externally) depends on the libraries jQuery and
|
||||
// Strophe
|
||||
(global => {
|
||||
// jQuery
|
||||
if (typeof global.$ === 'undefined') {
|
||||
const jQuery = require('jquery');
|
||||
|
||||
jQuery(global);
|
||||
global.$ = jQuery;
|
||||
}
|
||||
|
||||
// Strophe
|
||||
if (typeof global.Strophe === 'undefined') {
|
||||
require('strophe');
|
||||
require('strophejs-plugins/disco/strophe.disco');
|
||||
require('strophejs-plugins/caps/strophe.caps.jsonly');
|
||||
}
|
||||
})(global || window || this); // eslint-disable-line no-invalid-this
|
||||
|
||||
// Re-export JitsiMeetJS from the library lib-jitsi-meet to (the other features
|
||||
// of) the project jitsi-meet-react.
|
||||
//
|
||||
// TODO The Web support implemented by the jitsi-meet project explicitly uses
|
||||
// the library lib-jitsi-meet as a binary and keeps it out of the application
|
||||
// bundle. The mobile support implemented by the jitsi-meet-react project did
|
||||
// not get to keeping the lib-jitsi-meet library out of the application bundle
|
||||
// and even used it from source. As an intermediate step, start using the
|
||||
// library lib-jitsi-meet as a binary on mobile at the time of this writing. In
|
||||
// the future, implement not packaging it in the application bundle.
|
||||
import JitsiMeetJS from 'lib-jitsi-meet/lib-jitsi-meet.min';
|
||||
export { JitsiMeetJS as default };
|
||||
|
||||
3
react/features/base/lib-jitsi-meet/_.web.js
Normal file
3
react/features/base/lib-jitsi-meet/_.web.js
Normal file
@@ -0,0 +1,3 @@
|
||||
/* global JitsiMeetJS */
|
||||
|
||||
export default JitsiMeetJS;
|
||||
@@ -1,36 +1,6 @@
|
||||
import './_';
|
||||
|
||||
// The library lib-jitsi-meet (externally) depends on the libraries jQuery and
|
||||
// Strophe
|
||||
(global => {
|
||||
// jQuery
|
||||
if (typeof global.$ === 'undefined') {
|
||||
const jQuery = require('jquery');
|
||||
|
||||
jQuery(global);
|
||||
global.$ = jQuery;
|
||||
}
|
||||
|
||||
// Strophe
|
||||
if (typeof global.Strophe === 'undefined') {
|
||||
require('strophe');
|
||||
require('strophejs-plugins/disco/strophe.disco');
|
||||
require('strophejs-plugins/caps/strophe.caps.jsonly');
|
||||
}
|
||||
})(global || window || this); // eslint-disable-line no-invalid-this
|
||||
|
||||
// Re-export JitsiMeetJS from the library lib-jitsi-meet to (the other features
|
||||
// of) the project jitsi-meet-react.
|
||||
//
|
||||
// TODO The Web support implemented by the jitsi-meet project explicitly uses
|
||||
// the library lib-jitsi-meet as a binary and keeps it out of the application
|
||||
// bundle. The mobile support implemented by the jitsi-meet-react project did
|
||||
// not get to keeping the lib-jitsi-meet library out of the application bundle
|
||||
// and even used it from source. As an intermediate step, start using the
|
||||
// library lib-jitsi-meet as a binary on mobile at the time of this writing. In
|
||||
// the future, implement not packaging it in the application bundle.
|
||||
import JitsiMeetJS from 'lib-jitsi-meet/lib-jitsi-meet.min';
|
||||
|
||||
import JitsiMeetJS from './_';
|
||||
export { JitsiMeetJS as default };
|
||||
|
||||
export * from './actions';
|
||||
|
||||
@@ -11,6 +11,24 @@ import { Video } from './_';
|
||||
* @abstract
|
||||
*/
|
||||
export class AbstractVideoTrack extends Component {
|
||||
/**
|
||||
* AbstractVideoTrack component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
dispatch: React.PropTypes.func,
|
||||
videoTrack: React.PropTypes.object,
|
||||
waitForVideoStarted: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The z-order of the Video of AbstractVideoTrack in the stacking space
|
||||
* of all Videos. For more details, refer to the zOrder property of the
|
||||
* Video class for React Native.
|
||||
*/
|
||||
zOrder: React.PropTypes.number
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new AbstractVideoTrack instance.
|
||||
*
|
||||
@@ -116,24 +134,6 @@ export class AbstractVideoTrack extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AbstractVideoTrack component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
AbstractVideoTrack.propTypes = {
|
||||
dispatch: React.PropTypes.func,
|
||||
videoTrack: React.PropTypes.object,
|
||||
waitForVideoStarted: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The z-order of the Video of AbstractVideoTrack in the stacking space of
|
||||
* all Videos. For more details, refer to the zOrder property of the Video
|
||||
* class for React Native.
|
||||
*/
|
||||
zOrder: React.PropTypes.number
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns null if a specific value is falsy; otherwise, returns the specified
|
||||
* value.
|
||||
|
||||
@@ -5,6 +5,16 @@ import React, { Component } from 'react';
|
||||
* around react-native-webrtc's RTCView.
|
||||
*/
|
||||
export class Audio extends Component {
|
||||
/**
|
||||
* Audio component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
muted: React.PropTypes.bool,
|
||||
stream: React.PropTypes.object
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -18,13 +28,3 @@ export class Audio extends Component {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
Audio.propTypes = {
|
||||
muted: React.PropTypes.bool,
|
||||
stream: React.PropTypes.object
|
||||
};
|
||||
|
||||
@@ -18,6 +18,43 @@ const RTCVIEW_SUPPORTS_MIRROR = Platform.OS === 'android';
|
||||
* around react-native-webrtc's RTCView.
|
||||
*/
|
||||
export class Video extends Component {
|
||||
/**
|
||||
* Video component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
mirror: React.PropTypes.bool,
|
||||
muted: React.PropTypes.bool,
|
||||
onPlaying: React.PropTypes.func,
|
||||
stream: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* Similarly to the CSS property z-index, specifies the z-order of this
|
||||
* Video in the stacking space of all Videos. When Videos overlap,
|
||||
* zOrder determines which one covers the other. A Video with a larger
|
||||
* zOrder generally covers a Video with a lower one.
|
||||
*
|
||||
* Non-overlapping Videos may safely share a z-order (because one does
|
||||
* not have to cover the other).
|
||||
*
|
||||
* The support for zOrder is platform-dependent and/or
|
||||
* implementation-specific. Thus, specifying a value for zOrder is to be
|
||||
* thought of as giving a hint rather than as imposing a requirement.
|
||||
* For example, video renderers such as Video are commonly implemented
|
||||
* using OpenGL and OpenGL views may have different numbers of layers in
|
||||
* their stacking space. Android has three: a layer bellow the window
|
||||
* (aka default), a layer bellow the window again but above the previous
|
||||
* layer (aka media overlay), and above the window. Consequently, it is
|
||||
* advisable to limit the number of utilized layers in the stacking
|
||||
* space to the minimum sufficient for the desired display. For example,
|
||||
* a video call application usually needs a maximum of two zOrder
|
||||
* values: 0 for the remote video(s) which appear in the background, and
|
||||
* 1 for the local video(s) which appear above the remote video(s).
|
||||
*/
|
||||
zOrder: React.PropTypes.number
|
||||
}
|
||||
|
||||
/**
|
||||
* React Component method that executes once component is mounted.
|
||||
*
|
||||
@@ -86,40 +123,3 @@ export class Video extends Component {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Video component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
Video.propTypes = {
|
||||
mirror: React.PropTypes.bool,
|
||||
muted: React.PropTypes.bool,
|
||||
onPlaying: React.PropTypes.func,
|
||||
stream: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* Similarly to the CSS property z-index, specifies the z-order of this
|
||||
* Video in the stacking space of all Videos. When Videos overlap, zOrder
|
||||
* determines which one covers the other. A Video with a larger zOrder
|
||||
* generally covers a Video with a lower one.
|
||||
*
|
||||
* Non-overlapping Videos may safely share a z-order (because one does not
|
||||
* have to cover the other).
|
||||
*
|
||||
* The support for zOrder is platform-dependent and/or
|
||||
* implementation-specific. Thus, specifying a value for zOrder is to be
|
||||
* thought of as giving a hint rather than as imposing a requirement. For
|
||||
* example, video renderers such as Video are commonly implemented using
|
||||
* OpenGL and OpenGL views may have different numbers of layers in their
|
||||
* stacking space. Android has three: a layer bellow the window (aka
|
||||
* default), a layer bellow the window again but above the previous layer
|
||||
* (aka media overlay), and above the window. Consequently, it is advisable
|
||||
* to limit the number of utilized layers in the stacking space to the
|
||||
* minimum sufficient for the desired display. For example, a video call
|
||||
* application usually needs a maximum of two zOrder values: 0 for the
|
||||
* remote video(s) which appear in the background, and 1 for the local
|
||||
* video(s) which appear above the remote video(s).
|
||||
*/
|
||||
zOrder: React.PropTypes.number
|
||||
};
|
||||
|
||||
@@ -11,6 +11,13 @@ import { styles } from './styles';
|
||||
* @extends AbstractVideoTrack
|
||||
*/
|
||||
class VideoTrack extends AbstractVideoTrack {
|
||||
/**
|
||||
* VideoTrack component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = AbstractVideoTrack.propTypes
|
||||
|
||||
/**
|
||||
* Initializes a new VideoTrack instance.
|
||||
*
|
||||
@@ -153,11 +160,4 @@ class VideoTrack extends AbstractVideoTrack {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* VideoTrack component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
VideoTrack.propTypes = AbstractVideoTrack.propTypes;
|
||||
|
||||
export default connect()(VideoTrack);
|
||||
|
||||
@@ -6,6 +6,40 @@ import React, { Component } from 'react';
|
||||
* @extends Component
|
||||
*/
|
||||
export default class AbstractContainer extends Component {
|
||||
/**
|
||||
* AbstractContainer component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
children: React.PropTypes.node,
|
||||
|
||||
/**
|
||||
* The event handler/listener to be invoked when this AbstractContainer
|
||||
* is clicked on Web or pressed on React Native. If onClick is defined
|
||||
* and touchFeedback is undefined, touchFeedback is considered defined
|
||||
* as true.
|
||||
*/
|
||||
onClick: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* The style (as in stylesheet) to be applied to this AbstractContainer.
|
||||
*/
|
||||
style: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* True if this instance is to provide visual feedback when touched;
|
||||
* otherwise, false. If touchFeedback is undefined and onClick is
|
||||
* defined, touchFeedback is considered defined as true.
|
||||
*/
|
||||
touchFeedback: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* True if this AbstractContainer is to be visible or false if this
|
||||
* instance is to be hidden or not rendered at all.
|
||||
*/
|
||||
visible: React.PropTypes.bool
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders this AbstractContainer as a React Component of a specific type.
|
||||
@@ -38,37 +72,3 @@ export default class AbstractContainer extends Component {
|
||||
return React.createElement(type, filteredProps, children);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AbstractContainer component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
AbstractContainer.propTypes = {
|
||||
children: React.PropTypes.node,
|
||||
|
||||
/**
|
||||
* The event handler/listener to be invoked when this AbstractContainer is
|
||||
* clicked on Web or pressed on React Native. If onClick is defined and
|
||||
* touchFeedback is undefined, touchFeedback is considered defined as true.
|
||||
*/
|
||||
onClick: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* The style (as in stylesheet) to be applied to this AbstractContainer.
|
||||
*/
|
||||
style: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* True if this instance is to provide visual feedback when touched;
|
||||
* otherwise, false. If touchFeedback is undefined and onClick is defined,
|
||||
* touchFeedback is considered defined as true.
|
||||
*/
|
||||
touchFeedback: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* True if this AbstractContainer is to be visible or false if this instance
|
||||
* is to be hidden or not rendered at all.
|
||||
*/
|
||||
visible: React.PropTypes.bool
|
||||
};
|
||||
|
||||
@@ -14,6 +14,12 @@ import AbstractContainer from './AbstractContainer';
|
||||
* @extends AbstractContainer
|
||||
*/
|
||||
export class Container extends AbstractContainer {
|
||||
/**
|
||||
* Container component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = AbstractContainer.propTypes
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
@@ -74,10 +80,3 @@ export class Container extends AbstractContainer {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Container component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
Container.propTypes = AbstractContainer.propTypes;
|
||||
|
||||
89
react/features/base/react/components/Link.native.js
Normal file
89
react/features/base/react/components/Link.native.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Linking, Text } from 'react-native';
|
||||
|
||||
/**
|
||||
* Implements a (hyper)link to a URL in the fashion of the HTML anchor element
|
||||
* and its href attribute.
|
||||
*/
|
||||
export class Link extends Component {
|
||||
/**
|
||||
* Link component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* The children to be displayed within this Link.
|
||||
*/
|
||||
children: React.PropTypes.node,
|
||||
|
||||
/**
|
||||
* Notifies that this Link failed to open the URL associated with it.
|
||||
*/
|
||||
onLinkingOpenURLRejected: React.PropTypes.func,
|
||||
|
||||
/**
|
||||
* The CSS style to be applied to this Link for the purposes of display.
|
||||
*/
|
||||
style: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* The URL to be opened when this Link is clicked/pressed.
|
||||
*/
|
||||
url: React.PropTypes.string
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new Link instance.
|
||||
*
|
||||
* @param {Object} props - Component properties.
|
||||
*/
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once for every instance.
|
||||
this._onPress = this._onPress.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Text
|
||||
onPress = { this._onPress }
|
||||
style = { this.props.style }>
|
||||
{
|
||||
this.props.children
|
||||
}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this instance that Linking failed to open the associated URL.
|
||||
*
|
||||
* @param {any} reason - The rejection reason.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onLinkingOpenURLRejected(reason) {
|
||||
const onRejected = this.props.onLinkingOpenURLRejected;
|
||||
|
||||
onRejected && onRejected(reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles press on this Link. Opens the URL associated with this Link.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onPress() {
|
||||
Linking.openURL(this.props.url)
|
||||
.catch(reason => this._onLinkingOpenURLRejected(reason));
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from './Container';
|
||||
export * from './Link';
|
||||
|
||||
15
react/features/base/styles/components/styles/BoxModel.js
Normal file
15
react/features/base/styles/components/styles/BoxModel.js
Normal file
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* The application's default properties related to the CSS box model such as
|
||||
* margins, borders, padding.
|
||||
*/
|
||||
export const BoxModel = {
|
||||
/**
|
||||
* The application's default margin when non-zero margin is necessary.
|
||||
*/
|
||||
margin: 10,
|
||||
|
||||
/**
|
||||
* The application's default padding when non-zero padding is necessary.
|
||||
*/
|
||||
padding: 10
|
||||
};
|
||||
@@ -1,11 +1,26 @@
|
||||
/**
|
||||
* The application color palette.
|
||||
* The application's definition of the default color black.
|
||||
*/
|
||||
const BLACK = '#111111';
|
||||
|
||||
/**
|
||||
* The application's color palette.
|
||||
*/
|
||||
export const ColorPalette = {
|
||||
appBackground: '#111111',
|
||||
/**
|
||||
* The application's background color.
|
||||
*/
|
||||
appBackground: BLACK,
|
||||
|
||||
/**
|
||||
* The application's definition of the default color black. Generally,
|
||||
* expected to be kept in sync with the application's background color for
|
||||
* the sake of consistency.
|
||||
*/
|
||||
black: BLACK,
|
||||
blue: '#17A0DB',
|
||||
buttonUnderlay: '#495258',
|
||||
jitsiBlue: '#17A0DB',
|
||||
jitsiDarkGrey: '#555555',
|
||||
jitsiRed: '#D00000',
|
||||
jitsiToggled: '#495258'
|
||||
darkGrey: '#555555',
|
||||
red: '#D00000',
|
||||
white: 'white'
|
||||
};
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './BoxModel';
|
||||
export * from './ColorPalette';
|
||||
|
||||
@@ -7,6 +7,20 @@ import { styles } from './styles';
|
||||
* Display a participant avatar.
|
||||
*/
|
||||
export default class Avatar extends Component {
|
||||
/**
|
||||
* Avatar component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* The optional style to add to an Avatar in order to customize its base
|
||||
* look (and feel).
|
||||
*/
|
||||
style: React.PropTypes.object,
|
||||
uri: React.PropTypes.string
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -23,18 +37,3 @@ export default class Avatar extends Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
Avatar.propTypes = {
|
||||
|
||||
/**
|
||||
* The optional style to add to an Avatar in order to customize its base
|
||||
* look (and feel).
|
||||
*/
|
||||
style: React.PropTypes.object,
|
||||
uri: React.PropTypes.string
|
||||
};
|
||||
|
||||
@@ -21,6 +21,14 @@ const TOOLBAR_TIMEOUT_MS = 5000;
|
||||
* The conference page of the application.
|
||||
*/
|
||||
class Conference extends Component {
|
||||
/**
|
||||
* Conference component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
dispatch: React.PropTypes.func
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new Conference instance.
|
||||
@@ -125,13 +133,4 @@ class Conference extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Conference component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
Conference.propTypes = {
|
||||
dispatch: React.PropTypes.func
|
||||
};
|
||||
|
||||
export default reactReduxConnect()(Conference);
|
||||
133
react/features/conference/components/Conference.web.js
Normal file
133
react/features/conference/components/Conference.web.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
/**
|
||||
* For legacy reasons, inline style for display none.
|
||||
* @type {{display: string}}
|
||||
*/
|
||||
const DISPLAY_NONE_STYLE = {
|
||||
display: 'none'
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a React Component which renders initial conference layout
|
||||
*/
|
||||
export default class Conference extends Component {
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<div id = 'videoconference_page'>
|
||||
<div id = 'mainToolbarContainer'>
|
||||
<div
|
||||
className = 'notice'
|
||||
id = 'notice'
|
||||
style = { DISPLAY_NONE_STYLE }>
|
||||
<span
|
||||
className = 'noticeText'
|
||||
id = 'noticeText' />
|
||||
</div>
|
||||
<div
|
||||
className = 'toolbar'
|
||||
id = 'mainToolbar' />
|
||||
</div>
|
||||
<div
|
||||
className = 'hide'
|
||||
id = 'subject' />
|
||||
<div
|
||||
className = 'toolbar'
|
||||
id = 'extendedToolbar'>
|
||||
<div id = 'extendedToolbarButtons' />
|
||||
<a
|
||||
className = 'button icon-feedback'
|
||||
id = 'feedbackButton' />
|
||||
<div id = 'sideToolbarContainer' />
|
||||
</div>
|
||||
<div id = 'videospace'>
|
||||
<div
|
||||
className = 'videocontainer'
|
||||
id = 'largeVideoContainer'>
|
||||
<div id = 'sharedVideo'>
|
||||
<div id = 'sharedVideoIFrame' />
|
||||
</div>
|
||||
<div id = 'etherpad' />
|
||||
<a target = '_new'>
|
||||
<div className = 'watermark leftwatermark' />
|
||||
</a>
|
||||
<a target = '_new'>
|
||||
<div className = 'watermark rightwatermark' />
|
||||
</a>
|
||||
<a
|
||||
className = 'poweredby'
|
||||
href = 'http://jitsi.org'
|
||||
target = '_new'>
|
||||
<span data-i18n = 'poweredby' /> jitsi.org
|
||||
</a>
|
||||
<div id = 'dominantSpeaker'>
|
||||
<div className = 'dynamic-shadow' />
|
||||
<img
|
||||
id = 'dominantSpeakerAvatar'
|
||||
src = '' />
|
||||
</div>
|
||||
<span id = 'remoteConnectionMessage' />
|
||||
<div id = 'largeVideoWrapper'>
|
||||
<video
|
||||
autoPlay = { true }
|
||||
id = 'largeVideo'
|
||||
muted = 'true' />
|
||||
</div>
|
||||
<span id = 'localConnectionMessage' />
|
||||
<span
|
||||
className = 'video-state-indicator moveToCorner'
|
||||
id = 'videoResolutionLabel'>HD</span>
|
||||
<span
|
||||
className
|
||||
= 'video-state-indicator centeredVideoLabel'
|
||||
id = 'recordingLabel'>
|
||||
<span id = 'recordingLabelText' />
|
||||
<img
|
||||
className = 'recordingSpinner'
|
||||
id = 'recordingSpinner'
|
||||
src = 'images/spin.svg' />
|
||||
</span>
|
||||
</div>
|
||||
<div className = 'filmstrip'>
|
||||
<div
|
||||
className = 'filmstrip__videos'
|
||||
id = 'remoteVideos'>
|
||||
<span
|
||||
className = 'videocontainer'
|
||||
id = 'localVideoContainer'>
|
||||
<div
|
||||
className = 'videocontainer__background' />
|
||||
<span id = 'localVideoWrapper' />
|
||||
<audio
|
||||
autoPlay = { true }
|
||||
id = 'localAudio'
|
||||
muted = { true } />
|
||||
<div className = 'videocontainer__toolbar' />
|
||||
<div
|
||||
className = 'videocontainer__toptoolbar' />
|
||||
<div
|
||||
className
|
||||
= 'videocontainer__hoverOverlay' />
|
||||
</span>
|
||||
<audio
|
||||
id = 'userJoined'
|
||||
preload = 'auto'
|
||||
src = 'sounds/joined.wav' />
|
||||
<audio
|
||||
id = 'userLeft'
|
||||
preload = 'auto'
|
||||
src = 'sounds/left.wav' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,63 @@ import { styles } from './styles';
|
||||
* @extends Component
|
||||
*/
|
||||
class ParticipantView extends Component {
|
||||
/**
|
||||
* ParticipantView component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
/**
|
||||
* The source (e.g. URI, URL) of the avatar image of the participant
|
||||
* with {@link #participantId}.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_avatar: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* The video Track of the participant with {@link #participantId}.
|
||||
*/
|
||||
_videoTrack: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* The style, if any, of the avatar in addition to the default style.
|
||||
*/
|
||||
avatarStyle: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* The ID of the participant (to be) depicted by ParticipantView.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
participantId: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* True if the avatar of the depicted participant is to be shown should
|
||||
* the avatar be available and the video of the participant is not to be
|
||||
* shown; otherwise, false. If undefined, defaults to true.
|
||||
*/
|
||||
showAvatar: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* True if the video of the depicted participant is to be shown should
|
||||
* the video be available. If undefined, defaults to true.
|
||||
*/
|
||||
showVideo: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The style, if any, to apply to ParticipantView in addition to its
|
||||
* default style.
|
||||
*/
|
||||
style: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* The z-order of the Video of ParticipantView in the stacking space of
|
||||
* all Videos. For more details, refer to the zOrder property of the
|
||||
* Video class for React Native.
|
||||
*/
|
||||
zOrder: React.PropTypes.number
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
@@ -77,65 +134,6 @@ class ParticipantView extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ParticipantView component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
ParticipantView.propTypes = {
|
||||
|
||||
/**
|
||||
* The source (e.g. URI, URL) of the avatar image of the participant with
|
||||
* {@link #participantId}.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_avatar: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* The video Track of the participant with {@link #participantId}.
|
||||
*/
|
||||
_videoTrack: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* The style, if any, of the avatar in addition to the default style.
|
||||
*/
|
||||
avatarStyle: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* The ID of the participant (to be) depicted by ParticipantView.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
participantId: React.PropTypes.string,
|
||||
|
||||
/**
|
||||
* True if the avatar of the depicted participant is to be shown should the
|
||||
* avatar be available and the video of the participant is not to be shown;
|
||||
* otherwise, false. If undefined, defaults to true.
|
||||
*/
|
||||
showAvatar: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* True if the video of the depicted participant is to be shown should the
|
||||
* video be available. If undefined, defaults to true.
|
||||
*/
|
||||
showVideo: React.PropTypes.bool,
|
||||
|
||||
/**
|
||||
* The style, if any, to apply to ParticipantView in addition to its default
|
||||
* style.
|
||||
*/
|
||||
style: React.PropTypes.object,
|
||||
|
||||
/**
|
||||
* The z-order of the Video of ParticipantView in the stacking space of all
|
||||
* Videos. For more details, refer to the zOrder property of the Video class
|
||||
* for React Native.
|
||||
*/
|
||||
zOrder: React.PropTypes.number
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the specified value to a boolean value. If the specified value is
|
||||
* undefined, returns the boolean value of undefinedValue.
|
||||
22
react/features/conference/components/ParticipantView.web.js
Normal file
22
react/features/conference/components/ParticipantView.web.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Component } from 'react';
|
||||
|
||||
/**
|
||||
* Implements a React Component which depicts a specific participant's avatar
|
||||
* and video.
|
||||
*/
|
||||
export default class ParticipantView extends Component {
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement|null}
|
||||
*/
|
||||
render() {
|
||||
// FIXME ParticipantView is supposed to be platform-independent.
|
||||
// Temporarily though, ParticipantView is not in use on Web but has to
|
||||
// exist in order to split App, Conference, and WelcomePage out of
|
||||
// index.html.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user