Merge branch 'BeatC-BeatC-moving-conference-init-to-react-1'
This commit is contained in:
commit
d17cc9fa86
254
app.js
254
app.js
|
@ -1,6 +1,4 @@
|
|||
/* global $, config, getRoomName, loggingConfig, JitsiMeetJS */
|
||||
/* application specific logic */
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
import "babel-polyfill";
|
||||
import "jquery";
|
||||
|
@ -18,104 +16,13 @@ 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 {
|
||||
generateRoomWithoutSeparator
|
||||
} from './react/features/base/util/roomnameGenerator';
|
||||
|
||||
import UI from "./modules/UI/UI";
|
||||
import settings from "./modules/settings/Settings";
|
||||
import conference from './conference';
|
||||
import ConferenceUrl from './modules/URL/ConferenceUrl';
|
||||
import API from './modules/API/API';
|
||||
|
||||
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
|
||||
* it.
|
||||
*/
|
||||
function pushHistoryState(roomName, URL) {
|
||||
try {
|
||||
window.history.pushState(
|
||||
'VideoChat', `Room: ${roomName}`, URL
|
||||
);
|
||||
} catch (e) {
|
||||
logger.warn("Push history state failed with parameters:",
|
||||
'VideoChat', `Room: ${roomName}`, URL, e);
|
||||
return e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces current history state(replaces the URL displayed by the browser).
|
||||
* @param {string} newUrl the URL string which is to be displayed by the browser
|
||||
* to the user.
|
||||
*/
|
||||
function replaceHistoryState (newUrl) {
|
||||
if (window.history
|
||||
&& typeof window.history.replaceState === 'function') {
|
||||
window.history.replaceState({}, document.title, newUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns the room name.
|
||||
*/
|
||||
function buildRoomName () {
|
||||
let roomName = getRoomName();
|
||||
|
||||
if(!roomName) {
|
||||
let word = generateRoomWithoutSeparator();
|
||||
roomName = word.toLowerCase();
|
||||
let historyURL = window.location.href + word;
|
||||
//Trying to push state with current URL + roomName
|
||||
pushHistoryState(word, historyURL);
|
||||
}
|
||||
|
||||
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",
|
||||
|
@ -152,164 +59,15 @@ const APP = {
|
|||
*/
|
||||
ConferenceUrl : null,
|
||||
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);
|
||||
}
|
||||
}
|
||||
API
|
||||
};
|
||||
|
||||
/**
|
||||
* If JWT token data it will be used for local user settings
|
||||
*/
|
||||
function setTokenData() {
|
||||
let localUser = APP.tokenData.caller;
|
||||
if(localUser) {
|
||||
APP.settings.setEmail((localUser.getEmail() || "").trim(), true);
|
||||
APP.settings.setAvatarUrl((localUser.getAvatarUrl() || "").trim());
|
||||
APP.settings.setDisplayName((localUser.getName() || "").trim(), true);
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
setTokenData();
|
||||
// Initialize the conference URL handler
|
||||
APP.ConferenceUrl = new ConferenceUrl(window.location);
|
||||
// Clean up the URL displayed by the browser
|
||||
replaceHistoryState(APP.ConferenceUrl.getInviteUrl());
|
||||
|
||||
// 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.
|
||||
// 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(() => {
|
||||
|
||||
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, language => {
|
||||
APP.translation.setLanguage(language);
|
||||
APP.settings.setLanguage(language);
|
||||
});
|
||||
|
||||
APP.keyboardshortcut.init();
|
||||
}).catch(err => {
|
||||
APP.UI.hideRingOverLay();
|
||||
APP.API.notifyConferenceLeft(APP.conference.roomName);
|
||||
logger.error(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have an HTTP endpoint for getting config.json configured we're going to
|
||||
* read it and override properties from config.js and interfaceConfig.js.
|
||||
* If there is no endpoint we'll just continue with initialization.
|
||||
* Keep in mind that if the endpoint has been configured and we fail to obtain
|
||||
* the config for any reason then the conference won't start and error message
|
||||
* will be displayed to the user.
|
||||
*/
|
||||
function obtainConfigAndInit() {
|
||||
let roomName = APP.conference.roomName;
|
||||
|
||||
if (config.configLocation) {
|
||||
APP.configFetch.obtainConfig(
|
||||
config.configLocation, roomName,
|
||||
// Get config result callback
|
||||
function(success, error) {
|
||||
if (success) {
|
||||
var now = APP.connectionTimes["configuration.fetched"] =
|
||||
window.performance.now();
|
||||
logger.log("(TIME) configuration fetched:\t", now);
|
||||
init();
|
||||
} else {
|
||||
// Show obtain config error,
|
||||
// pass the error object for report
|
||||
APP.UI.messageHandler.openReportDialog(
|
||||
null, "dialog.connectError", error);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
require("./modules/config/BoshAddressChoice").chooseAddress(
|
||||
config, roomName);
|
||||
|
||||
init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function () {
|
||||
var now = APP.connectionTimes["document.ready"] = window.performance.now();
|
||||
logger.log("(TIME) document ready:\t", now);
|
||||
|
||||
URLProcessor.setConfigParametersFromUrl();
|
||||
|
||||
APP.init();
|
||||
|
||||
APP.translation.init(settings.getLanguage());
|
||||
|
||||
APP.API.init(APP.tokenData.externalAPISettings);
|
||||
|
||||
obtainConfigAndInit();
|
||||
});
|
||||
|
||||
$(window).bind('beforeunload', function () {
|
||||
// Stop the LogCollector
|
||||
if (APP.logCollectorStarted) {
|
||||
APP.logCollector.stop();
|
||||
APP.logCollectorStarted = false;
|
||||
}
|
||||
APP.API.dispose();
|
||||
});
|
||||
|
||||
module.exports = APP;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* global APP, JitsiMeetJS, $, config, interfaceConfig, toastr */
|
||||
|
||||
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
||||
|
||||
var UI = {};
|
||||
|
@ -396,20 +397,6 @@ UI.getSharedVideoManager = function () {
|
|||
*/
|
||||
UI.start = function () {
|
||||
document.title = interfaceConfig.APP_NAME;
|
||||
var setupWelcomePage = null;
|
||||
if(config.enableWelcomePage && window.location.pathname == "/" &&
|
||||
Settings.isWelcomePageEnabled()) {
|
||||
$("#videoconference_page").hide();
|
||||
if (!setupWelcomePage)
|
||||
setupWelcomePage = require("./welcome_page/WelcomePage");
|
||||
setupWelcomePage();
|
||||
|
||||
// Return false to indicate that the UI hasn't been fully started and
|
||||
// conference ready. We're still waiting for input from the user.
|
||||
return false;
|
||||
}
|
||||
|
||||
$("#welcome_page").hide();
|
||||
|
||||
// Set the defaults for prompt dialogs.
|
||||
$.prompt.setDefaults({persistent: false});
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
/* global $ */
|
||||
|
||||
function enterRoom() {
|
||||
const $enterRoomField = $("#enter_room_field");
|
||||
|
||||
var val = $enterRoomField.val();
|
||||
if(!val) {
|
||||
val = $enterRoomField.data("room-name");
|
||||
}
|
||||
if (val) {
|
||||
window.location.pathname = "/" + val;
|
||||
}
|
||||
}
|
||||
|
||||
function setupWelcomePage() {
|
||||
// XXX: We left only going to conference page here because transitions via
|
||||
// React Router isn't implemented yet.
|
||||
|
||||
$("#enter_room_button").click(function() {
|
||||
enterRoom();
|
||||
});
|
||||
|
||||
$("#enter_room_field").keydown(function (event) {
|
||||
if (event.keyCode === 13 /* enter */) {
|
||||
enterRoom();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = setupWelcomePage;
|
|
@ -32,13 +32,13 @@
|
|||
"jws": "*",
|
||||
"lib-jitsi-meet": "jitsi/lib-jitsi-meet",
|
||||
"postis": "^2.2.0",
|
||||
"react": "15.4.1",
|
||||
"react-dom": "15.4.1",
|
||||
"react": "15.4.2",
|
||||
"react-dom": "15.4.2",
|
||||
"react-native": "0.39.2",
|
||||
"react-native-prompt": "^1.0.0",
|
||||
"react-native-vector-icons": "^3.0.0",
|
||||
"react-native-webrtc": "jitsi/react-native-webrtc",
|
||||
"react-redux": "^5.0.1",
|
||||
"react-redux": "^5.0.2",
|
||||
"react-router": "^3.0.0",
|
||||
"react-router-redux": "^4.0.7",
|
||||
"redux": "^3.5.2",
|
||||
|
@ -60,7 +60,7 @@
|
|||
"babel-preset-stage-1": "^6.16.0",
|
||||
"clean-css": "*",
|
||||
"css-loader": "*",
|
||||
"eslint": "^3.12.2",
|
||||
"eslint": "^3.13.1",
|
||||
"eslint-plugin-jsdoc": "*",
|
||||
"eslint-plugin-react": "*",
|
||||
"eslint-plugin-react-native": "^2.2.1",
|
||||
|
|
|
@ -1,23 +1,26 @@
|
|||
import { setRoom } from '../base/conference';
|
||||
import {
|
||||
getDomain,
|
||||
setDomain
|
||||
} from '../base/connection';
|
||||
import {
|
||||
loadConfig,
|
||||
setConfig
|
||||
} from '../base/lib-jitsi-meet';
|
||||
import { getDomain, setDomain } from '../base/connection';
|
||||
import { loadConfig, setConfig } from '../base/lib-jitsi-meet';
|
||||
|
||||
import {
|
||||
APP_WILL_MOUNT,
|
||||
APP_WILL_UNMOUNT
|
||||
} from './actionTypes';
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
|
||||
import {
|
||||
_getRoomAndDomainFromUrlString,
|
||||
_getRouteToRender
|
||||
_getRouteToRender,
|
||||
init
|
||||
} from './functions';
|
||||
import './reducer';
|
||||
|
||||
/**
|
||||
* Temporary solution. Should dispatch actions related to initial settings of
|
||||
* the app like setting log levels, reading the config parameters from query
|
||||
* string etc.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function appInit() {
|
||||
return () => init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers an in-app navigation to a different route. Allows navigation to be
|
||||
* abstracted between the mobile and web versions.
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import {
|
||||
browserHistory,
|
||||
Route,
|
||||
Router
|
||||
} from 'react-router';
|
||||
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 { appInit } from '../actions';
|
||||
import { AbstractApp } from './AbstractApp';
|
||||
|
||||
/**
|
||||
|
@ -42,30 +39,18 @@ export class App extends AbstractApp {
|
|||
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.
|
||||
* Inits the app before component will mount.
|
||||
*
|
||||
* @returns {void}
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentWillMount() {
|
||||
// FIXME Do not override the super once the dispatching of Redux actions
|
||||
// is integrated into the Web App.
|
||||
}
|
||||
componentWillMount(...args) {
|
||||
super.componentWillMount(...args);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
this.props.store.dispatch(appInit());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -75,19 +60,14 @@ export class App extends AbstractApp {
|
|||
* @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 } />
|
||||
) }
|
||||
{
|
||||
this._renderRoutes()
|
||||
}
|
||||
</Router>
|
||||
</Provider>
|
||||
);
|
||||
|
@ -118,10 +98,18 @@ export class App extends AbstractApp {
|
|||
* Invoked by react-router to notify this App that a Route is about to be
|
||||
* rendered.
|
||||
*
|
||||
* @param {Route} route - The Route that is about to be rendered.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onRouteEnter() {
|
||||
_onRouteEnter(route, ...args) {
|
||||
// Notify the route that it is about to be entered.
|
||||
const onEnter = route.onEnter;
|
||||
|
||||
if (typeof onEnter === 'function') {
|
||||
onEnter(...args);
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -140,6 +128,37 @@ export class App extends AbstractApp {
|
|||
this._openURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a specific Route (for the purposes of the Router of this App).
|
||||
*
|
||||
* @param {Object} route - The Route to render.
|
||||
* @returns {ReactElement}
|
||||
* @private
|
||||
*/
|
||||
_renderRoute(route) {
|
||||
const onEnter = (...args) => {
|
||||
this._onRouteEnter(route, ...args);
|
||||
};
|
||||
|
||||
return (
|
||||
<Route
|
||||
component = { route.component }
|
||||
key = { route.component }
|
||||
onEnter = { onEnter }
|
||||
path = { route.path } />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the Routes of the Router of this App.
|
||||
*
|
||||
* @returns {Array.<ReactElement>}
|
||||
* @private
|
||||
*/
|
||||
_renderRoutes() {
|
||||
return RouteRegistry.getRoutes().map(this._renderRoute, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ReactElement from the specified component and props on behalf of
|
||||
* the associated Router.
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/* global APP, JitsiMeetJS, loggingConfig */
|
||||
|
||||
import URLProcessor from '../../../modules/config/URLProcessor';
|
||||
import KeyboardShortcut
|
||||
from '../../../modules/keyboardshortcut/keyboardshortcut';
|
||||
import settings from '../../../modules/settings/Settings';
|
||||
import getTokenData from '../../../modules/tokendata/TokenData';
|
||||
import JitsiMeetLogStorage from '../../../modules/util/JitsiMeetLogStorage';
|
||||
|
||||
const Logger = require('jitsi-meet-logger');
|
||||
|
||||
export * from './functions.native';
|
||||
|
||||
/**
|
||||
* Temporary solution. Later we'll get rid of global APP and set its properties
|
||||
* in redux store.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
export function init() {
|
||||
URLProcessor.setConfigParametersFromUrl();
|
||||
_initLogging();
|
||||
|
||||
APP.keyboardshortcut = KeyboardShortcut;
|
||||
APP.tokenData = getTokenData();
|
||||
APP.API.init(APP.tokenData.externalAPISettings);
|
||||
|
||||
APP.translation.init(settings.getLanguage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts the logging levels.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
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(loggerName => {
|
||||
if (loggerName !== 'defaultLogLevel') {
|
||||
const level = loggingConfig[loggerName];
|
||||
|
||||
Logger.setLogLevelById(level, loggerName);
|
||||
JitsiMeetJS.setLogLevelById(level, loggerName);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes logging in the app.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _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 (!APP.logCollector && !loggingConfig.disableLogCollector) {
|
||||
APP.logCollector = new Logger.LogCollector(new JitsiMeetLogStorage());
|
||||
Logger.addGlobalTransport(APP.logCollector);
|
||||
JitsiMeetJS.addGlobalLogTransport(APP.logCollector);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
/* global APP, JitsiMeetJS */
|
||||
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
|
||||
import { SET_DOMAIN } from './actionTypes';
|
||||
import './reducer';
|
||||
|
||||
const JitsiConferenceEvents = JitsiMeetJS.events.conference;
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Opens new connection.
|
||||
*
|
||||
* @returns {Promise<JitsiConnection>}
|
||||
*/
|
||||
export function connect() {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const room = state['features/base/conference'].room;
|
||||
|
||||
// XXX For web based version we use conference initialization logic
|
||||
// from the old app (at the moment of writing).
|
||||
return APP.conference.init({ roomName: room }).then(() => {
|
||||
if (APP.logCollector) {
|
||||
// Start the LogCollector's periodic "store logs" task
|
||||
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(
|
||||
JitsiConferenceEvents.BEFORE_STATISTICS_DISPOSED,
|
||||
() => {
|
||||
if (APP.logCollector) {
|
||||
APP.logCollector.flush();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
APP.UI.initConference();
|
||||
|
||||
APP.UI.addListener(UIEvents.LANG_CHANGED, language => {
|
||||
APP.translation.setLanguage(language);
|
||||
APP.settings.setLanguage(language);
|
||||
});
|
||||
|
||||
APP.keyboardshortcut.init();
|
||||
})
|
||||
.catch(err => {
|
||||
APP.UI.hideRingOverLay();
|
||||
APP.API.notifyConferenceLeft(APP.conference.roomName);
|
||||
logger.error(err);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes connection.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function disconnect() {
|
||||
// XXX For web based version we use conference hanging up logic from the old
|
||||
// app.
|
||||
return () => APP.conference.hangup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets connection domain.
|
||||
*
|
||||
* @param {string} domain - Domain name.
|
||||
* @returns {{
|
||||
* type: SET_DOMAIN,
|
||||
* domain: string
|
||||
* }}
|
||||
*/
|
||||
export function setDomain(domain) {
|
||||
return {
|
||||
type: SET_DOMAIN,
|
||||
domain
|
||||
};
|
||||
}
|
|
@ -40,6 +40,12 @@ export function initLib() {
|
|||
throw new Error('Cannot initialize lib-jitsi-meet without config');
|
||||
}
|
||||
|
||||
// XXX Temporarily until conference.js is moved to the React app we
|
||||
// shouldn't use JitsiMeetJS from the React app.
|
||||
if (typeof APP !== 'undefined') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return JitsiMeetJS.init(config)
|
||||
.then(() => dispatch({ type: LIB_INITIALIZED }))
|
||||
.catch(error => {
|
||||
|
|
|
@ -8,6 +8,13 @@ import { loadScript } from '../../base/util';
|
|||
* @returns {Promise<Object>}
|
||||
*/
|
||||
export function loadConfig(host, path = '/config.js') {
|
||||
// Returns config.js file from global scope. We can't use the version that's
|
||||
// being used for the React Native app because the old/current Web app uses
|
||||
// config from the global scope.
|
||||
if (typeof APP !== 'undefined') {
|
||||
return Promise.resolve(window.config);
|
||||
}
|
||||
|
||||
return loadScript(new URL(path, host).toString())
|
||||
.then(() => {
|
||||
const config = window.config;
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
/* global interfaceConfig, APP */
|
||||
/* global $, APP, interfaceConfig */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { connect as reactReduxConnect } from 'react-redux';
|
||||
|
||||
import { connect, disconnect } from '../../base/connection';
|
||||
|
||||
/**
|
||||
* For legacy reasons, inline style for display none.
|
||||
|
@ -12,7 +16,42 @@ const DISPLAY_NONE_STYLE = {
|
|||
/**
|
||||
* Implements a React Component which renders initial conference layout
|
||||
*/
|
||||
export default class Conference extends Component {
|
||||
class Conference extends Component {
|
||||
|
||||
/**
|
||||
* Conference component's property types.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static propTypes = {
|
||||
dispatch: React.PropTypes.func
|
||||
}
|
||||
|
||||
/**
|
||||
* Until we don't rewrite UI using react components
|
||||
* we use UI.start from old app. Also method translates
|
||||
* component right after it has been mounted.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentDidMount() {
|
||||
APP.UI.start();
|
||||
|
||||
// XXX Temporary solution until we add React translation.
|
||||
APP.translation.translateElement($('#videoconference_page'));
|
||||
|
||||
this.props.dispatch(connect());
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from the conference when component will be
|
||||
* unmounted.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentWillUnmount() {
|
||||
this.props.dispatch(disconnect());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes Conference component instance.
|
||||
|
@ -220,3 +259,5 @@ export default class Conference extends Component {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default reactReduxConnect()(Conference);
|
||||
|
|
|
@ -1,11 +1,126 @@
|
|||
/* global APP, config */
|
||||
|
||||
import BoshAddressChoice from '../../../modules/config/BoshAddressChoice';
|
||||
import HttpConfigFetch from '../../../modules/config/HttpConfigFetch';
|
||||
import ConferenceUrl from '../../../modules/URL/ConferenceUrl';
|
||||
|
||||
import { RouteRegistry } from '../base/navigator';
|
||||
|
||||
import { Conference } from './components';
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Register route for Conference (page).
|
||||
*/
|
||||
RouteRegistry.register({
|
||||
component: Conference,
|
||||
onEnter: () => {
|
||||
// XXX If config or jwt are set by hash or query parameters
|
||||
// Getting raw URL before stripping it.
|
||||
_obtainConfigAndInit();
|
||||
},
|
||||
path: '/:room'
|
||||
});
|
||||
|
||||
/**
|
||||
* Initialization of the app.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _initConference() {
|
||||
_setTokenData();
|
||||
|
||||
// Initialize the conference URL handler
|
||||
APP.ConferenceUrl = new ConferenceUrl(window.location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise wrapper on obtain config method. When HttpConfigFetch will be moved
|
||||
* to React app it's better to use load config instead.
|
||||
*
|
||||
* @param {string} location - URL of the domain.
|
||||
* @param {string} room - Room name.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function _obtainConfig(location, room) {
|
||||
return new Promise((resolve, reject) => {
|
||||
HttpConfigFetch.obtainConfig(location, room, (success, error) => {
|
||||
if (success) {
|
||||
resolve();
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If we have an HTTP endpoint for getting config.json configured we're going to
|
||||
* read it and override properties from config.js and interfaceConfig.js. If
|
||||
* there is no endpoint we'll just continue with initialization. Keep in mind
|
||||
* that if the endpoint has been configured and we fail to obtain the config for
|
||||
* any reason then the conference won't start and error message will be
|
||||
* displayed to the user.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _obtainConfigAndInit() {
|
||||
// Skip initialization if conference is initialized already.
|
||||
if (typeof APP !== 'undefined' && !APP.ConferenceUrl) {
|
||||
const location = config.configLocation;
|
||||
const room = APP.conference.roomName;
|
||||
|
||||
if (location) {
|
||||
_obtainConfig(location, room)
|
||||
.then(() => {
|
||||
_obtainConfigHandler();
|
||||
_initConference();
|
||||
})
|
||||
.catch(err => {
|
||||
// Show obtain config error.
|
||||
APP.UI.messageHandler.openReportDialog(
|
||||
null, 'dialog.connectError', err);
|
||||
});
|
||||
} else {
|
||||
BoshAddressChoice.chooseAddress(config, room);
|
||||
_initConference();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain config handler.
|
||||
*
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function _obtainConfigHandler() {
|
||||
const now = window.performance.now();
|
||||
|
||||
APP.connectionTimes['configuration.fetched'] = now;
|
||||
logger.log('(TIME) configuration fetched:\t', now);
|
||||
}
|
||||
|
||||
/**
|
||||
* If JWT token data it will be used for local user settings.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _setTokenData() {
|
||||
const localUser = APP.tokenData.caller;
|
||||
|
||||
if (localUser) {
|
||||
const email = localUser.getEmail();
|
||||
const avatarUrl = localUser.getAvatarUrl();
|
||||
const name = localUser.getName();
|
||||
|
||||
APP.settings.setEmail((email || '').trim(), true);
|
||||
APP.settings.setAvatarUrl((avatarUrl || '').trim());
|
||||
APP.settings.setDisplayName((name || '').trim(), true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
/* global APP, interfaceConfig */
|
||||
/* global $, APP, interfaceConfig */
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { Conference } from '../../conference';
|
||||
|
||||
import { AbstractWelcomePage, mapStateToProps } from './AbstractWelcomePage';
|
||||
|
||||
/**
|
||||
|
@ -52,6 +50,9 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
if (this.state.generateRoomnames) {
|
||||
this._updateRoomname();
|
||||
}
|
||||
|
||||
// XXX Temporary solution until we add React translation.
|
||||
APP.translation.translateElement($('#welcome_page'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,12 +62,6 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
* @returns {ReactElement|null}
|
||||
*/
|
||||
render() {
|
||||
// FIXME The rendering of Conference bellow is a very quick and dirty
|
||||
// temporary fix for the following issue: when the WelcomePage is
|
||||
// disabled, app.js expects Conference to be rendered already and only
|
||||
// then it builds a room name but the App component expects the room
|
||||
// name to be built already (by looking at the window's location) in
|
||||
// order to choose between WelcomePage and Conference.
|
||||
return (
|
||||
<div>
|
||||
<div id = 'welcome_page'>
|
||||
|
@ -77,7 +72,6 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
this._renderMain()
|
||||
}
|
||||
</div>
|
||||
<Conference />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -132,19 +126,6 @@ class WelcomePage extends AbstractWelcomePage {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the super in order to prevent the dispatching of the Redux
|
||||
* action SET_ROOM.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {null}
|
||||
*/
|
||||
_onJoin() {
|
||||
// Don't call the super implementation and thus prevent the dispatching
|
||||
// of the Redux action SET_ROOM.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles 'keydown' event to initiate joining the room when the
|
||||
* 'Enter/Return' button is pressed.
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
/* global APP */
|
||||
|
||||
import { RouteRegistry } from '../base/navigator';
|
||||
import { generateRoomWithoutSeparator } from '../base/util';
|
||||
|
||||
import { WelcomePage } from './components';
|
||||
|
||||
|
@ -7,5 +10,23 @@ import { WelcomePage } from './components';
|
|||
*/
|
||||
RouteRegistry.register({
|
||||
component: WelcomePage,
|
||||
onEnter,
|
||||
path: '/'
|
||||
});
|
||||
|
||||
/**
|
||||
* If the Welcome page/screen is disabled, generates a (random) room (name) so
|
||||
* that the Welcome page/screen is skipped and the Conference page/screen is
|
||||
* presented instead.
|
||||
*
|
||||
* @param {Object} nextState - The next Router state.
|
||||
* @param {Function} replace - The function to redirect to another path.
|
||||
* @returns {void}
|
||||
*/
|
||||
function onEnter(nextState, replace) {
|
||||
if (typeof APP !== 'undefined' && !APP.settings.isWelcomePageEnabled()) {
|
||||
const room = generateRoomWithoutSeparator();
|
||||
|
||||
replace(`/${room}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,9 @@ class Root extends Component {
|
|||
|
||||
// XXX Start with an empty URL if getting the initial URL fails;
|
||||
// otherwise, nothing will be rendered.
|
||||
if (this.state.url !== null) {
|
||||
this.setState({ url: null });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
/* global APP */
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { browserHistory } from 'react-router';
|
||||
import {
|
||||
routerMiddleware,
|
||||
routerReducer
|
||||
} from 'react-router-redux';
|
||||
import { routerMiddleware, routerReducer } from 'react-router-redux';
|
||||
import { compose, createStore } from 'redux';
|
||||
import Thunk from 'redux-thunk';
|
||||
|
||||
import config from './config';
|
||||
import { App } from './features/app';
|
||||
import {
|
||||
MiddlewareRegistry,
|
||||
ReducerRegistry
|
||||
} from './features/base/redux';
|
||||
import { MiddlewareRegistry, ReducerRegistry } from './features/base/redux';
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
// Create combined reducer from all reducers in registry + routerReducer from
|
||||
// 'react-router-redux' module (stores location updates from history).
|
||||
|
@ -45,6 +43,15 @@ if (typeof window === 'object'
|
|||
// Create Redux store with our reducer and middleware.
|
||||
const store = createStore(reducer, middleware);
|
||||
|
||||
/**
|
||||
* Renders the app when the DOM tree has been loaded.
|
||||
*/
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const now = window.performance.now();
|
||||
|
||||
APP.connectionTimes['document.ready'] = now;
|
||||
logger.log('(TIME) document ready:\t', now);
|
||||
|
||||
// Render the main Component.
|
||||
ReactDOM.render(
|
||||
<App
|
||||
|
@ -52,3 +59,17 @@ ReactDOM.render(
|
|||
store = { store }
|
||||
url = { window.location.toString() } />,
|
||||
document.getElementById('react'));
|
||||
});
|
||||
|
||||
/**
|
||||
* Stops collecting the logs and disposing the API when the user closes the
|
||||
* page.
|
||||
*/
|
||||
window.addEventListener('beforeunload', () => {
|
||||
// Stop the LogCollector
|
||||
if (APP.logCollectorStarted) {
|
||||
APP.logCollector.stop();
|
||||
APP.logCollectorStarted = false;
|
||||
}
|
||||
APP.API.dispose();
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue