Split React components out of index.html
This commit is contained in:
parent
57b0736ebb
commit
c3428e8213
9
app.js
9
app.js
|
@ -192,6 +192,15 @@ $(document).ready(function () {
|
||||||
console.log("(TIME) document ready:\t", now);
|
console.log("(TIME) document ready:\t", now);
|
||||||
|
|
||||||
URLProcessor.setConfigParametersFromUrl();
|
URLProcessor.setConfigParametersFromUrl();
|
||||||
|
|
||||||
|
// 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');
|
||||||
|
|
||||||
APP.init();
|
APP.init();
|
||||||
|
|
||||||
APP.translation.init(settings.getLanguage());
|
APP.translation.init(settings.getLanguage());
|
||||||
|
|
138
index.html
138
index.html
|
@ -34,7 +34,7 @@
|
||||||
window.removeEventListener(
|
window.removeEventListener(
|
||||||
'error', loadErrHandler, true /* capture phase */);
|
'error', loadErrHandler, true /* capture phase */);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
window.addEventListener(
|
window.addEventListener(
|
||||||
'error', loadErrHandler, true /* capture phase type of listener */);
|
'error', loadErrHandler, true /* capture phase type of listener */);
|
||||||
</script>
|
</script>
|
||||||
|
@ -50,141 +50,7 @@
|
||||||
<!--#include virtual="plugin.head.html" -->
|
<!--#include virtual="plugin.head.html" -->
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="welcome_page">
|
<div id="react"></div>
|
||||||
<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">
|
|
||||||
<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="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">
|
<div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<ul id="keyboard-shortcuts-list" class="shortcuts-list">
|
<ul id="keyboard-shortcuts-list" class="shortcuts-list">
|
||||||
|
|
|
@ -39,7 +39,9 @@
|
||||||
"react-native": "0.37.0",
|
"react-native": "0.37.0",
|
||||||
"react-native-vector-icons": "^2.0.3",
|
"react-native-vector-icons": "^2.0.3",
|
||||||
"react-native-webrtc": "jitsi/react-native-webrtc",
|
"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": "^3.5.2",
|
||||||
"redux-thunk": "^2.1.0",
|
"redux-thunk": "^2.1.0",
|
||||||
"retry": "0.6.1",
|
"retry": "0.6.1",
|
||||||
|
|
|
@ -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 {
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* App component's property types.
|
||||||
|
*
|
||||||
|
* @static
|
||||||
|
*/
|
||||||
|
App.propTypes = AbstractApp.propTypes;
|
|
@ -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 };
|
||||||
|
|
|
@ -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
|
// Re-export JitsiMeetJS from the library lib-jitsi-meet to (the other features
|
||||||
// of) the project jitsi-meet-react.
|
// of) the project jitsi-meet-react.
|
||||||
//
|
import JitsiMeetJS from './_';
|
||||||
// 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 };
|
export { JitsiMeetJS as default };
|
||||||
|
|
||||||
export * from './actions';
|
export * from './actions';
|
||||||
|
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,7 +22,9 @@ class WelcomePage extends AbstractWelcomePage {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<View style = { styles.container }>
|
<View style = { styles.container }>
|
||||||
{ this._renderLocalVideo() }
|
{
|
||||||
|
this._renderLocalVideo()
|
||||||
|
}
|
||||||
<View style = { styles.roomContainer }>
|
<View style = { styles.roomContainer }>
|
||||||
<Text style = { styles.title }>Enter room name</Text>
|
<Text style = { styles.title }>Enter room name</Text>
|
||||||
<TextInput
|
<TextInput
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The web container rendering the welcome page.
|
||||||
|
*/
|
||||||
|
export default class WelcomePage extends Component {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements React's {@link Component#render()}.
|
||||||
|
*
|
||||||
|
* @inheritdoc
|
||||||
|
* @returns {ReactElement|null}
|
||||||
|
*/
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div id = 'welcome_page'>
|
||||||
|
{
|
||||||
|
this._renderHeader()
|
||||||
|
}
|
||||||
|
{
|
||||||
|
this._renderMain()
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a feature with a specific index.
|
||||||
|
*
|
||||||
|
* @param {number} index - The index of the feature to render.
|
||||||
|
* @private
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
_renderFeature(index) {
|
||||||
|
return (
|
||||||
|
<div className = 'feature_holder'>
|
||||||
|
<div
|
||||||
|
className = 'feature_icon'
|
||||||
|
data-i18n = { `welcomepage.feature${index}.title` } />
|
||||||
|
<div
|
||||||
|
className = 'feature_description'
|
||||||
|
data-i18n = { `welcomepage.feature${index}.content` }
|
||||||
|
data-i18n-options = { JSON.stringify({
|
||||||
|
postProcess: 'resolveAppName'
|
||||||
|
}) } />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders a row of features.
|
||||||
|
*
|
||||||
|
* @param {number} beginIndex - The inclusive feature index to begin the row
|
||||||
|
* with.
|
||||||
|
* @param {number} endIndex - The exclusive feature index to end the row
|
||||||
|
* with.
|
||||||
|
* @private
|
||||||
|
* @returns {ReactElement}
|
||||||
|
*/
|
||||||
|
_renderFeatureRow(beginIndex, endIndex) {
|
||||||
|
const features = [];
|
||||||
|
|
||||||
|
for (let index = beginIndex; index < endIndex; ++index) {
|
||||||
|
features.push(this._renderFeature(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className = 'feature_row'>
|
||||||
|
{
|
||||||
|
features
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the header part of this WelcomePage.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {ReactElement|null}
|
||||||
|
*/
|
||||||
|
_renderHeader() {
|
||||||
|
return (
|
||||||
|
<div id = 'welcome_page_header'>
|
||||||
|
<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 = 'enter_room_container'>
|
||||||
|
<div id = 'enter_room_form'>
|
||||||
|
<div id = 'domain_name' />
|
||||||
|
<div id = 'enter_room'>
|
||||||
|
<input
|
||||||
|
autoFocus = { true }
|
||||||
|
id = 'enter_room_field'
|
||||||
|
type = 'text' />
|
||||||
|
<div
|
||||||
|
className = 'icon-reload'
|
||||||
|
id = 'reload_roomname' />
|
||||||
|
<input
|
||||||
|
data-i18n = '[value]welcomepage.go'
|
||||||
|
id = 'enter_room_button'
|
||||||
|
type = 'button'
|
||||||
|
value = 'GO' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id = 'brand_header' />
|
||||||
|
<input
|
||||||
|
id = 'disable_welcome'
|
||||||
|
name = 'checkbox'
|
||||||
|
type = 'checkbox' />
|
||||||
|
<label
|
||||||
|
className = 'disable_welcome_position'
|
||||||
|
data-i18n = 'welcomepage.disable'
|
||||||
|
htmlFor = 'disable_welcome' />
|
||||||
|
<div id = 'header_text' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the main part of this WelcomePage.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {ReactElement|null}
|
||||||
|
*/
|
||||||
|
_renderMain() {
|
||||||
|
return (
|
||||||
|
<div id = 'welcome_page_main'>
|
||||||
|
<div id = 'features'>
|
||||||
|
{
|
||||||
|
this._renderFeatureRow(1, 5)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
this._renderFeatureRow(5, 9)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import { browserHistory } from 'react-router';
|
||||||
|
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';
|
||||||
|
|
||||||
|
// Create combined reducer from all reducers in registry + routerReducer from
|
||||||
|
// 'react-router-redux' module (stores location updates from history).
|
||||||
|
// @see https://github.com/reactjs/react-router-redux#routerreducer.
|
||||||
|
const reducer = ReducerRegistry.combineReducers({
|
||||||
|
routing: routerReducer
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply all registered middleware from the MiddlewareRegistry + additional
|
||||||
|
// 3rd party middleware:
|
||||||
|
// - Thunk - allows us to dispatch async actions easily. For more info
|
||||||
|
// @see https://github.com/gaearon/redux-thunk.
|
||||||
|
// - routerMiddleware - middleware from 'react-router-redux' module to track
|
||||||
|
// changes in browser history inside Redux state. For more information
|
||||||
|
// @see https://github.com/reactjs/react-router-redux.
|
||||||
|
let middleware = MiddlewareRegistry.applyMiddleware(
|
||||||
|
Thunk,
|
||||||
|
routerMiddleware(browserHistory));
|
||||||
|
|
||||||
|
// Try to enable Redux DevTools Chrome extension in order to make it available
|
||||||
|
// for the purposes of facilitating development.
|
||||||
|
let devToolsExtension;
|
||||||
|
|
||||||
|
if (typeof window === 'object'
|
||||||
|
&& (devToolsExtension = window.devToolsExtension)) {
|
||||||
|
middleware = compose(middleware, devToolsExtension());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Redux store with our reducer and middleware.
|
||||||
|
const store = createStore(reducer, middleware);
|
||||||
|
|
||||||
|
// Render the main Component.
|
||||||
|
ReactDOM.render(
|
||||||
|
<App
|
||||||
|
config = { config }
|
||||||
|
store = { store }
|
||||||
|
url = { window.location.toString() } />,
|
||||||
|
document.getElementById('react'));
|
Loading…
Reference in New Issue