diff --git a/app.js b/app.js index 684d04ce1b..044c8c0fc8 100644 --- a/app.js +++ b/app.js @@ -63,11 +63,11 @@ const APP = { }; // 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'); module.exports = APP; diff --git a/react/features/app/actions.web.js b/react/features/app/actions.js similarity index 99% rename from react/features/app/actions.web.js rename to react/features/app/actions.js index 2ec370379b..96cd27549c 100644 --- a/react/features/app/actions.web.js +++ b/react/features/app/actions.js @@ -19,6 +19,16 @@ import { } 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 @@ -91,19 +101,6 @@ export function appNavigate(urlOrRoom) { }; } -/** - * 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(); - }; -} - /** * Signals that a specific App will mount (in the terms of React). * diff --git a/react/features/app/actions.native.js b/react/features/app/actions.native.js deleted file mode 100644 index ef0566ddd1..0000000000 --- a/react/features/app/actions.native.js +++ /dev/null @@ -1,158 +0,0 @@ -import { setRoom } from '../base/conference'; -import { - getDomain, - setDomain -} from '../base/connection'; -import { - loadConfig, - setConfig -} from '../base/lib-jitsi-meet'; - -import { - APP_WILL_MOUNT, - APP_WILL_UNMOUNT -} from './actionTypes'; -import { - _getRoomAndDomainFromUrlString, - _getRouteToRender -} from './functions'; -import './reducer'; - -/** - * Triggers an in-app navigation to a different route. Allows navigation to be - * abstracted between the mobile and web versions. - * - * @param {(string|undefined)} urlOrRoom - The URL or room name to which to - * navigate. - * @returns {Function} - */ -export function appNavigate(urlOrRoom) { - return (dispatch, getState) => { - const oldDomain = getDomain(getState()); - - const { domain, room } = _getRoomAndDomainFromUrlString(urlOrRoom); - - // TODO Kostiantyn Tsaregradskyi: We should probably detect if user is - // currently in a conference and ask her if she wants to close the - // current conference and start a new one with the new room name or - // domain. - - if (typeof domain === 'undefined' || oldDomain === domain) { - // If both domain and room vars became undefined, that means we're - // actually dealing with just room name and not with URL. - dispatch( - _setRoomAndNavigate( - typeof room === 'undefined' && typeof domain === 'undefined' - ? urlOrRoom - : room)); - } else if (oldDomain !== domain) { - // Update domain without waiting for config to be loaded to prevent - // race conditions when we will start to load config multiple times. - dispatch(setDomain(domain)); - - // If domain has changed, we need to load the config of the new - // domain and set it, and only after that we can navigate to - // different route. - loadConfig(`https://${domain}`) - .then( - config => configLoaded(/* err */ undefined, config), - err => configLoaded(err, /* config */ undefined)); - } - - /** - * Notifies that an attempt to load the config(uration) of domain has - * completed. - * - * @param {string|undefined} err - If the loading has failed, the error - * detailing the cause of the failure. - * @param {Object|undefined} config - If the loading has succeeded, the - * loaded config(uration). - * @returns {void} - */ - function configLoaded(err, config) { - if (err) { - // XXX The failure could be, for example, because of a - // certificate-related error. In which case the connection will - // fail later in Strophe anyway even if we use the default - // config here. - - // The function loadConfig will log the err. - return; - } - - // We set room name only here to prevent race conditions on app - // start to not make app re-render conference page for two times. - dispatch(setRoom(room)); - dispatch(setConfig(config)); - _navigate(getState()); - } - }; -} - -/** - * Signals that a specific App will mount (in the terms of React). - * - * @param {App} app - The App which will mount. - * @returns {{ - * type: APP_WILL_MOUNT, - * app: App - * }} - */ -export function appWillMount(app) { - return { - type: APP_WILL_MOUNT, - app - }; -} - -/** - * Signals that a specific App will unmount (in the terms of React). - * - * @param {App} app - The App which will unmount. - * @returns {{ - * type: APP_WILL_UNMOUNT, - * app: App - * }} - */ -export function appWillUnmount(app) { - return { - type: APP_WILL_UNMOUNT, - app - }; -} - -/** - * Navigates to route corresponding to current room name. - * - * @param {Object} state - Redux state. - * @private - * @returns {void} - */ -function _navigate(state) { - const app = state['features/app'].app; - const routeToRender = _getRouteToRender(state); - - app._navigate(routeToRender); -} - -/** - * Sets room and navigates to new route if needed. - * - * @param {string} newRoom - New room name. - * @private - * @returns {Function} - */ -function _setRoomAndNavigate(newRoom) { - return (dispatch, getState) => { - const oldRoom = getState()['features/base/conference'].room; - - dispatch(setRoom(newRoom)); - - const state = getState(); - const room = state['features/base/conference'].room; - - if (room !== oldRoom) { - _navigate(state); - } - }; -} diff --git a/react/features/app/components/AbstractApp.js b/react/features/app/components/AbstractApp.js index 7d32adec51..4d766a2a29 100644 --- a/react/features/app/components/AbstractApp.js +++ b/react/features/app/components/AbstractApp.js @@ -42,7 +42,7 @@ export class AbstractApp extends Component { * The URL, if any, with which the app was launched. */ url: React.PropTypes.string - }; + } /** * Init lib-jitsi-meet and create local participant when component is going diff --git a/react/features/app/components/App.web.js b/react/features/app/components/App.web.js index 2528124448..3d8de06a1c 100644 --- a/react/features/app/components/App.web.js +++ b/react/features/app/components/App.web.js @@ -1,20 +1,19 @@ /* global $ */ import React from 'react'; import { Provider } from 'react-redux'; -import { compose } from 'redux'; import { browserHistory, Route, Router } from 'react-router'; import { push, syncHistoryWithStore } from 'react-router-redux'; +import { compose } from 'redux'; import { getDomain } from '../../base/connection'; import { RouteRegistry } from '../../base/navigator'; -import { AbstractApp } from './AbstractApp'; import { appInit } from '../actions'; - +import { AbstractApp } from './AbstractApp'; /** * Root application component. @@ -27,7 +26,7 @@ export class App extends AbstractApp { * * @static */ - static propTypes = AbstractApp.propTypes; + static propTypes = AbstractApp.propTypes /** * Initializes a new App instance. @@ -46,10 +45,10 @@ 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); this._getRoute = this._getRoute.bind(this); this._getRoutes = this._getRoutes.bind(this); + this._onRouteEnter = this._onRouteEnter.bind(this); + this._routerCreateElement = this._routerCreateElement.bind(this); } /** @@ -59,6 +58,7 @@ export class App extends AbstractApp { */ componentWillMount(...args) { super.componentWillMount(...args); + this.props.store.dispatch(appInit()); } @@ -69,7 +69,6 @@ export class App extends AbstractApp { * @returns {ReactElement} */ render() { - return ( + ); } /** @@ -115,23 +113,24 @@ export class App extends AbstractApp { } /** - * Method returns route for React Router. + * Navigates to a specific Route (via platform-specific means). * - * @param {Object} route - Object that describes route. - * @returns {ReactElement} - * @private + * @param {Route} route - The Route to which to navigate. + * @returns {void} */ - _getRoute(route) { - const onEnter = route.onEnter || $.noop; - const handler = compose(this._onRouteEnter, onEnter); + _navigate(route) { + let path = route.path; + const store = this.props.store; - return ( - - ); + // 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)); } /** @@ -142,7 +141,6 @@ export class App extends AbstractApp { * @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. diff --git a/react/features/base/lib-jitsi-meet/actions.native.js b/react/features/base/lib-jitsi-meet/actions.js similarity index 88% rename from react/features/base/lib-jitsi-meet/actions.native.js rename to react/features/base/lib-jitsi-meet/actions.js index aa97ae36de..ddb69594dc 100644 --- a/react/features/base/lib-jitsi-meet/actions.native.js +++ b/react/features/base/lib-jitsi-meet/actions.js @@ -1,3 +1,5 @@ +import React from 'react'; + import JitsiMeetJS from './'; import { LIB_DISPOSED, @@ -40,6 +42,12 @@ export function initLib() { throw new Error('Cannot initialize lib-jitsi-meet without config'); } + if (!React.View) { + // XXX Temporarily until conference.js is moved to the React app we + // shouldn't use JitsiMeetJS from the React app. + return Promise.resolve(); + } + return JitsiMeetJS.init(config) .then(() => dispatch({ type: LIB_INITIALIZED })) .catch(error => { diff --git a/react/features/base/lib-jitsi-meet/actions.web.js b/react/features/base/lib-jitsi-meet/actions.web.js deleted file mode 100644 index 02628ea1ae..0000000000 --- a/react/features/base/lib-jitsi-meet/actions.web.js +++ /dev/null @@ -1,61 +0,0 @@ -import { - LIB_DISPOSED, - SET_CONFIG -} from './actionTypes'; -import './middleware'; -import './reducer'; - -/** - * Disposes lib-jitsi-meet. - * - * @returns {Function} - */ -export function disposeLib() { - // XXX We're wrapping it with Promise, because: - // a) to be better aligned with initLib() method, which is async. - // b) as currently there is no implementation for it in lib-jitsi-meet, and - // there is a big chance it will be async. - // TODO Currently, lib-jitsi-meet doesn't have any functionality to - // dispose itself. - return dispatch => { - dispatch({ type: LIB_DISPOSED }); - - return Promise.resolve(); - }; -} - -/** - * Initializes lib-jitsi-meet with passed configuration. - * - * @returns {Function} - */ -export function initLib() { - return (dispatch, getState) => { - const config = getState()['features/base/lib-jitsi-meet'].config; - - if (!config) { - throw new Error('Cannot initialize lib-jitsi-meet without config'); - } - - // XXX Temporary solution. Until conference.js hasn't been moved - // to the react app we shouldn't use JitsiMeetJS from react app. - return Promise.resolve(); - }; -} - -/** - * Sets config. - * - * @param {Object} config - Config object accepted by JitsiMeetJS#init() - * method. - * @returns {{ - * type: SET_CONFIG, - * config: Object - * }} - */ -export function setConfig(config) { - return { - type: SET_CONFIG, - config - }; -} diff --git a/react/features/base/lib-jitsi-meet/functions.native.js b/react/features/base/lib-jitsi-meet/functions.js similarity index 72% rename from react/features/base/lib-jitsi-meet/functions.native.js rename to react/features/base/lib-jitsi-meet/functions.js index 27438b6035..543d64922f 100644 --- a/react/features/base/lib-jitsi-meet/functions.native.js +++ b/react/features/base/lib-jitsi-meet/functions.js @@ -1,3 +1,5 @@ +import React from 'react'; + import { loadScript } from '../../base/util'; /** @@ -8,6 +10,13 @@ import { loadScript } from '../../base/util'; * @returns {Promise} */ export function loadConfig(host, path = '/config.js') { + if (!React.View) { + // 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. + return Promise.resolve(window.config); + } + return loadScript(new URL(path, host).toString()) .then(() => { const config = window.config; diff --git a/react/features/base/lib-jitsi-meet/functions.web.js b/react/features/base/lib-jitsi-meet/functions.web.js deleted file mode 100644 index 5f0206fa66..0000000000 --- a/react/features/base/lib-jitsi-meet/functions.web.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Returns config.js file from global scope. - * We can't use version that's being used for native app - * because the old app uses config from global scope. - * - * @returns {Promise} - */ -export function loadConfig() { - return Promise.resolve(window.config); -} diff --git a/react/features/conference/components/Conference.web.js b/react/features/conference/components/Conference.web.js index ab8b5a9fe2..f930e342ce 100644 --- a/react/features/conference/components/Conference.web.js +++ b/react/features/conference/components/Conference.web.js @@ -1,4 +1,4 @@ -/* global APP, $, interfaceConfig */ +/* global $, APP, interfaceConfig */ import React, { Component } from 'react'; import { connect as reactReduxConnect } from 'react-redux'; diff --git a/react/features/conference/route.js b/react/features/conference/route.js index 35250efa6b..86555aa88d 100644 --- a/react/features/conference/route.js +++ b/react/features/conference/route.js @@ -8,10 +8,10 @@ import { obtainConfigAndInit } from './functions'; */ RouteRegistry.register({ component: Conference, - path: '/:room', onEnter: () => { // XXX: If config or jwt are set by hash or query parameters // Getting raw URL before stripping it. obtainConfigAndInit(); - } + }, + path: '/:room' }); diff --git a/react/features/welcome/components/WelcomePage.web.js b/react/features/welcome/components/WelcomePage.web.js index 40cadf4516..9b287c8db2 100644 --- a/react/features/welcome/components/WelcomePage.web.js +++ b/react/features/welcome/components/WelcomePage.web.js @@ -1,4 +1,4 @@ -/* global interfaceConfig, APP, $ */ +/* global $, APP, interfaceConfig */ import React from 'react'; import { connect } from 'react-redux'; @@ -340,16 +340,6 @@ class WelcomePage extends AbstractWelcomePage { return null; } - /** - * Handles updating roomname. - * - * @private - * @returns {void} - */ - _onUpdateRoomname() { - this._updateRoomname(); - } - /** * Renders the main part of this WelcomePage. * diff --git a/react/features/welcome/route.js b/react/features/welcome/route.js index ea7f6e5a67..6556fc1445 100644 --- a/react/features/welcome/route.js +++ b/react/features/welcome/route.js @@ -26,6 +26,6 @@ function onEnter(nextState, replace) { */ RouteRegistry.register({ component: WelcomePage, - path: '/', - onEnter + onEnter, + path: '/' });