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);
|
||||
|
||||
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.translation.init(settings.getLanguage());
|
||||
|
|
138
index.html
138
index.html
|
@ -34,7 +34,7 @@
|
|||
window.removeEventListener(
|
||||
'error', loadErrHandler, true /* capture phase */);
|
||||
}
|
||||
}
|
||||
};
|
||||
window.addEventListener(
|
||||
'error', loadErrHandler, true /* capture phase type of listener */);
|
||||
</script>
|
||||
|
@ -50,141 +50,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">
|
||||
<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">
|
||||
|
|
|
@ -39,7 +39,9 @@
|
|||
"react-native": "0.37.0",
|
||||
"react-native-vector-icons": "^2.0.3",
|
||||
"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",
|
||||
|
|
|
@ -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
|
||||
// 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';
|
||||
|
|
|
@ -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() {
|
||||
return (
|
||||
<View style = { styles.container }>
|
||||
{ this._renderLocalVideo() }
|
||||
{
|
||||
this._renderLocalVideo()
|
||||
}
|
||||
<View style = { styles.roomContainer }>
|
||||
<Text style = { styles.title }>Enter room name</Text>
|
||||
<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