Browse Source

Merge branch 'BeatC-moving-conference-init-to-react-1' of https://github.com/BeatC/jitsi-meet into BeatC-BeatC-moving-conference-init-to-react-1

j8
Lyubomir Marinov 8 years ago
parent
commit
b67994235e

+ 8
- 250
app.js View File

1
-/* global $, config, getRoomName, loggingConfig, JitsiMeetJS */
2
 /* application specific logic */
1
 /* application specific logic */
3
-const logger = require("jitsi-meet-logger").getLogger(__filename);
4
 
2
 
5
 import "babel-polyfill";
3
 import "babel-polyfill";
6
 import "jquery";
4
 import "jquery";
18
 
16
 
19
 window.toastr = require("toastr");
17
 window.toastr = require("toastr");
20
 
18
 
21
-const Logger = require("jitsi-meet-logger");
22
-const LogCollector = Logger.LogCollector;
23
-import JitsiMeetLogStorage from "./modules/util/JitsiMeetLogStorage";
24
-
25
-import URLProcessor from "./modules/config/URLProcessor";
26
-import {
27
-    generateRoomWithoutSeparator
28
-} from './react/features/base/util/roomnameGenerator';
29
-
30
 import UI from "./modules/UI/UI";
19
 import UI from "./modules/UI/UI";
31
 import settings from "./modules/settings/Settings";
20
 import settings from "./modules/settings/Settings";
32
 import conference from './conference';
21
 import conference from './conference';
33
-import ConferenceUrl from './modules/URL/ConferenceUrl';
34
 import API from './modules/API/API';
22
 import API from './modules/API/API';
35
 
23
 
36
-import UIEvents from './service/UI/UIEvents';
37
-import getTokenData from "./modules/tokendata/TokenData";
38
 import translation from "./modules/translation/translation";
24
 import translation from "./modules/translation/translation";
39
 
25
 
40
-const ConferenceEvents = JitsiMeetJS.events.conference;
41
-
42
-/**
43
- * Tries to push history state with the following parameters:
44
- * 'VideoChat', `Room: ${roomName}`, URL. If fail, prints the error and returns
45
- * it.
46
- */
47
-function pushHistoryState(roomName, URL) {
48
-    try {
49
-        window.history.pushState(
50
-            'VideoChat', `Room: ${roomName}`, URL
51
-        );
52
-    } catch (e) {
53
-        logger.warn("Push history state failed with parameters:",
54
-            'VideoChat', `Room: ${roomName}`, URL, e);
55
-        return e;
56
-    }
57
-    return null;
58
-}
59
-
60
-/**
61
- * Replaces current history state(replaces the URL displayed by the browser).
62
- * @param {string} newUrl the URL string which is to be displayed by the browser
63
- * to the user.
64
- */
65
-function replaceHistoryState (newUrl) {
66
-    if (window.history
67
-        && typeof window.history.replaceState === 'function') {
68
-        window.history.replaceState({}, document.title, newUrl);
69
-    }
70
-}
71
-
72
-/**
73
- * Builds and returns the room name.
74
- */
75
-function buildRoomName () {
76
-    let roomName = getRoomName();
77
-
78
-    if(!roomName) {
79
-        let word = generateRoomWithoutSeparator();
80
-        roomName = word.toLowerCase();
81
-        let historyURL = window.location.href + word;
82
-        //Trying to push state with current URL + roomName
83
-        pushHistoryState(word, historyURL);
84
-    }
85
-
86
-    return roomName;
87
-}
88
-
89
-/**
90
- * Adjusts the logging levels.
91
- * @private
92
- */
93
-function configureLoggingLevels () {
94
-    // NOTE The library Logger is separated from the app loggers, so the levels
95
-    // have to be set in two places
96
-
97
-    // Set default logging level
98
-    const defaultLogLevel
99
-        = loggingConfig.defaultLogLevel || JitsiMeetJS.logLevels.TRACE;
100
-    Logger.setLogLevel(defaultLogLevel);
101
-    JitsiMeetJS.setLogLevel(defaultLogLevel);
102
-
103
-    // NOTE console was used on purpose here to go around the logging
104
-    // and always print the default logging level to the console
105
-    console.info("Default logging level set to: " + defaultLogLevel);
106
-
107
-    // Set log level for each logger
108
-    if (loggingConfig) {
109
-        Object.keys(loggingConfig).forEach(function(loggerName) {
110
-            if ('defaultLogLevel' !== loggerName) {
111
-                const level = loggingConfig[loggerName];
112
-                Logger.setLogLevelById(level, loggerName);
113
-                JitsiMeetJS.setLogLevelById(level, loggerName);
114
-            }
115
-        });
116
-    }
117
-}
118
-
119
 const APP = {
26
 const APP = {
120
     // Used by do_external_connect.js if we receive the attach data after
27
     // Used by do_external_connect.js if we receive the attach data after
121
     // connect was already executed. status property can be "initialized",
28
     // connect was already executed. status property can be "initialized",
152
      */
59
      */
153
     ConferenceUrl : null,
60
     ConferenceUrl : null,
154
     connection: null,
61
     connection: null,
155
-    API,
156
-    init () {
157
-        this.initLogging();
158
-        this.keyboardshortcut =
159
-            require("./modules/keyboardshortcut/keyboardshortcut");
160
-        this.configFetch = require("./modules/config/HttpConfigFetch");
161
-        this.tokenData = getTokenData();
162
-    },
163
-    initLogging () {
164
-        // Adjust logging level
165
-        configureLoggingLevels();
166
-        // Create the LogCollector and register it as the global log transport.
167
-        // It is done early to capture as much logs as possible. Captured logs
168
-        // will be cached, before the JitsiMeetLogStorage gets ready (statistics
169
-        // module is initialized).
170
-        if (!this.logCollector && !loggingConfig.disableLogCollector) {
171
-            this.logCollector = new LogCollector(new JitsiMeetLogStorage());
172
-            Logger.addGlobalTransport(this.logCollector);
173
-            JitsiMeetJS.addGlobalLogTransport(this.logCollector);
174
-        }
175
-    }
62
+    API
176
 };
63
 };
177
 
64
 
178
-/**
179
- * If JWT token data it will be used for local user settings
180
- */
181
-function setTokenData() {
182
-    let localUser = APP.tokenData.caller;
183
-    if(localUser) {
184
-        APP.settings.setEmail((localUser.getEmail() || "").trim(), true);
185
-        APP.settings.setAvatarUrl((localUser.getAvatarUrl() || "").trim());
186
-        APP.settings.setDisplayName((localUser.getName() || "").trim(), true);
187
-    }
188
-}
189
-
190
-function init() {
191
-    setTokenData();
192
-    // Initialize the conference URL handler
193
-    APP.ConferenceUrl = new ConferenceUrl(window.location);
194
-    // Clean up the URL displayed by the browser
195
-    replaceHistoryState(APP.ConferenceUrl.getInviteUrl());
196
-
197
-    // TODO The execution of the mobile app starts from react/index.native.js.
198
-    // Similarly, the execution of the Web app should start from
199
-    // react/index.web.js for the sake of consistency and ease of understanding.
200
-    // Temporarily though because we are at the beginning of introducing React
201
-    // into the Web app, allow the execution of the Web app to start from app.js
202
-    // in order to reduce the complexity of the beginning step.
203
-    require('./react');
204
-
205
-    const isUIReady = APP.UI.start();
206
-    if (isUIReady) {
207
-        APP.conference.init({roomName: buildRoomName()}).then(() => {
208
-
209
-            if (APP.logCollector) {
210
-                // Start the LogCollector's periodic "store logs" task only if
211
-                // we're in the conference and not on the welcome page. This is
212
-                // determined by the value of "isUIReady" const above.
213
-                APP.logCollector.start();
214
-                APP.logCollectorStarted = true;
215
-                // Make an attempt to flush in case a lot of logs have been
216
-                // cached, before the collector was started.
217
-                APP.logCollector.flush();
218
-
219
-                // This event listener will flush the logs, before
220
-                // the statistics module (CallStats) is stopped.
221
-                //
222
-                // NOTE The LogCollector is not stopped, because this event can
223
-                // be triggered multiple times during single conference
224
-                // (whenever statistics module is stopped). That includes
225
-                // the case when Jicofo terminates the single person left in the
226
-                // room. It will then restart the media session when someone
227
-                // eventually join the room which will start the stats again.
228
-                APP.conference.addConferenceListener(
229
-                    ConferenceEvents.BEFORE_STATISTICS_DISPOSED,
230
-                    () => {
231
-                        if (APP.logCollector) {
232
-                            APP.logCollector.flush();
233
-                        }
234
-                    }
235
-                );
236
-            }
237
-
238
-            APP.UI.initConference();
239
-
240
-            APP.UI.addListener(UIEvents.LANG_CHANGED, language => {
241
-                APP.translation.setLanguage(language);
242
-                APP.settings.setLanguage(language);
243
-            });
244
-
245
-            APP.keyboardshortcut.init();
246
-        }).catch(err => {
247
-            APP.UI.hideRingOverLay();
248
-            APP.API.notifyConferenceLeft(APP.conference.roomName);
249
-            logger.error(err);
250
-        });
251
-    }
252
-}
253
-
254
-/**
255
- * If we have an HTTP endpoint for getting config.json configured we're going to
256
- * read it and override properties from config.js and interfaceConfig.js.
257
- * If there is no endpoint we'll just continue with initialization.
258
- * Keep in mind that if the endpoint has been configured and we fail to obtain
259
- * the config for any reason then the conference won't start and error message
260
- * will be displayed to the user.
261
- */
262
-function obtainConfigAndInit() {
263
-    let roomName = APP.conference.roomName;
264
-
265
-    if (config.configLocation) {
266
-        APP.configFetch.obtainConfig(
267
-            config.configLocation, roomName,
268
-            // Get config result callback
269
-            function(success, error) {
270
-                if (success) {
271
-                    var now = APP.connectionTimes["configuration.fetched"] =
272
-                        window.performance.now();
273
-                    logger.log("(TIME) configuration fetched:\t", now);
274
-                    init();
275
-                } else {
276
-                    // Show obtain config error,
277
-                    // pass the error object for report
278
-                    APP.UI.messageHandler.openReportDialog(
279
-                        null, "dialog.connectError", error);
280
-                }
281
-            });
282
-    } else {
283
-        require("./modules/config/BoshAddressChoice").chooseAddress(
284
-            config, roomName);
285
-
286
-        init();
287
-    }
288
-}
289
-
290
-
291
-$(document).ready(function () {
292
-    var now = APP.connectionTimes["document.ready"] = window.performance.now();
293
-    logger.log("(TIME) document ready:\t", now);
294
-
295
-    URLProcessor.setConfigParametersFromUrl();
296
-
297
-    APP.init();
298
-
299
-    APP.translation.init(settings.getLanguage());
300
-
301
-    APP.API.init(APP.tokenData.externalAPISettings);
302
-
303
-    obtainConfigAndInit();
304
-});
305
-
306
-$(window).bind('beforeunload', function () {
307
-    // Stop the LogCollector
308
-    if (APP.logCollectorStarted) {
309
-        APP.logCollector.stop();
310
-        APP.logCollectorStarted = false;
311
-    }
312
-    APP.API.dispose();
313
-});
65
+// TODO The execution of the mobile app starts from react/index.native.js.
66
+// Similarly, the execution of the Web app should start from
67
+// react/index.web.js for the sake of consistency and ease of understanding.
68
+// Temporarily though because we are at the beginning of introducing React
69
+// into the Web app, allow the execution of the Web app to start from app.js
70
+// in order to reduce the complexity of the beginning step.
71
+require('./react');
314
 
72
 
315
 module.exports = APP;
73
 module.exports = APP;

+ 0
- 14
modules/UI/UI.js View File

396
  */
396
  */
397
 UI.start = function () {
397
 UI.start = function () {
398
     document.title = interfaceConfig.APP_NAME;
398
     document.title = interfaceConfig.APP_NAME;
399
-    var setupWelcomePage = null;
400
-    if(config.enableWelcomePage && window.location.pathname == "/" &&
401
-       Settings.isWelcomePageEnabled()) {
402
-        $("#videoconference_page").hide();
403
-        if (!setupWelcomePage)
404
-            setupWelcomePage = require("./welcome_page/WelcomePage");
405
-        setupWelcomePage();
406
-
407
-        // Return false to indicate that the UI hasn't been fully started and
408
-        // conference ready. We're still waiting for input from the user.
409
-        return false;
410
-    }
411
-
412
-    $("#welcome_page").hide();
413
 
399
 
414
     // Set the defaults for prompt dialogs.
400
     // Set the defaults for prompt dialogs.
415
     $.prompt.setDefaults({persistent: false});
401
     $.prompt.setDefaults({persistent: false});

+ 0
- 30
modules/UI/welcome_page/WelcomePage.js View File

1
-/* global $ */
2
-
3
-function enterRoom() {
4
-    const $enterRoomField = $("#enter_room_field");
5
-
6
-    var val = $enterRoomField.val();
7
-    if(!val) {
8
-        val = $enterRoomField.data("room-name");
9
-    }
10
-    if (val) {
11
-        window.location.pathname = "/" + val;
12
-    }
13
-}
14
-
15
-function setupWelcomePage() {
16
-    // XXX: We left only going to conference page here because transitions via
17
-    // React Router isn't implemented yet.
18
-
19
-    $("#enter_room_button").click(function() {
20
-        enterRoom();
21
-    });
22
-
23
-    $("#enter_room_field").keydown(function (event) {
24
-        if (event.keyCode === 13 /* enter */) {
25
-            enterRoom();
26
-        }
27
-    });
28
-}
29
-
30
-module.exports = setupWelcomePage;

react/features/app/actions.js → react/features/app/actions.native.js View File


+ 173
- 0
react/features/app/actions.web.js View File

1
+import { setRoom } from '../base/conference';
2
+import {
3
+    getDomain,
4
+    setDomain
5
+} from '../base/connection';
6
+import {
7
+    loadConfig,
8
+    setConfig
9
+} from '../base/lib-jitsi-meet';
10
+
11
+import {
12
+    APP_WILL_MOUNT,
13
+    APP_WILL_UNMOUNT
14
+} from './actionTypes';
15
+import {
16
+    _getRoomAndDomainFromUrlString,
17
+    _getRouteToRender,
18
+    init
19
+} from './functions';
20
+import './reducer';
21
+
22
+
23
+/**
24
+ * Triggers an in-app navigation to a different route. Allows navigation to be
25
+ * abstracted between the mobile and web versions.
26
+ *
27
+ * @param {(string|undefined)} urlOrRoom - The URL or room name to which to
28
+ * navigate.
29
+ * @returns {Function}
30
+ */
31
+export function appNavigate(urlOrRoom) {
32
+    return (dispatch, getState) => {
33
+        const oldDomain = getDomain(getState());
34
+
35
+        const { domain, room } = _getRoomAndDomainFromUrlString(urlOrRoom);
36
+
37
+        // TODO Kostiantyn Tsaregradskyi: We should probably detect if user is
38
+        // currently in a conference and ask her if she wants to close the
39
+        // current conference and start a new one with the new room name or
40
+        // domain.
41
+
42
+        if (typeof domain === 'undefined' || oldDomain === domain) {
43
+            // If both domain and room vars became undefined, that means we're
44
+            // actually dealing with just room name and not with URL.
45
+            dispatch(
46
+                _setRoomAndNavigate(
47
+                    typeof room === 'undefined' && typeof domain === 'undefined'
48
+                        ? urlOrRoom
49
+                        : room));
50
+        } else if (oldDomain !== domain) {
51
+            // Update domain without waiting for config to be loaded to prevent
52
+            // race conditions when we will start to load config multiple times.
53
+            dispatch(setDomain(domain));
54
+
55
+            // If domain has changed, we need to load the config of the new
56
+            // domain and set it, and only after that we can navigate to
57
+            // different route.
58
+            loadConfig(`https://${domain}`)
59
+                .then(
60
+                    config => configLoaded(/* err */ undefined, config),
61
+                    err => configLoaded(err, /* config */ undefined));
62
+        }
63
+
64
+        /**
65
+         * Notifies that an attempt to load the config(uration) of domain has
66
+         * completed.
67
+         *
68
+         * @param {string|undefined} err - If the loading has failed, the error
69
+         * detailing the cause of the failure.
70
+         * @param {Object|undefined} config - If the loading has succeeded, the
71
+         * loaded config(uration).
72
+         * @returns {void}
73
+         */
74
+        function configLoaded(err, config) {
75
+            if (err) {
76
+                // XXX The failure could be, for example, because of a
77
+                // certificate-related error. In which case the connection will
78
+                // fail later in Strophe anyway even if we use the default
79
+                // config here.
80
+
81
+                // The function loadConfig will log the err.
82
+                return;
83
+            }
84
+
85
+            // We set room name only here to prevent race conditions on app
86
+            // start to not make app re-render conference page for two times.
87
+            dispatch(setRoom(room));
88
+            dispatch(setConfig(config));
89
+            _navigate(getState());
90
+        }
91
+    };
92
+}
93
+
94
+/**
95
+ * Temporary solution. Should dispatch actions related to
96
+ * initial settings of the app like setting log levels,
97
+ * reading the config parameters from query string etc.
98
+ *
99
+ * @returns {Function}
100
+ */
101
+export function appInit() {
102
+    return () => {
103
+        init();
104
+    };
105
+}
106
+
107
+/**
108
+ * Signals that a specific App will mount (in the terms of React).
109
+ *
110
+ * @param {App} app - The App which will mount.
111
+ * @returns {{
112
+ *     type: APP_WILL_MOUNT,
113
+ *     app: App
114
+ * }}
115
+ */
116
+export function appWillMount(app) {
117
+    return {
118
+        type: APP_WILL_MOUNT,
119
+        app
120
+    };
121
+}
122
+
123
+/**
124
+ * Signals that a specific App will unmount (in the terms of React).
125
+ *
126
+ * @param {App} app - The App which will unmount.
127
+ * @returns {{
128
+ *     type: APP_WILL_UNMOUNT,
129
+ *     app: App
130
+ * }}
131
+ */
132
+export function appWillUnmount(app) {
133
+    return {
134
+        type: APP_WILL_UNMOUNT,
135
+        app
136
+    };
137
+}
138
+
139
+/**
140
+ * Navigates to route corresponding to current room name.
141
+ *
142
+ * @param {Object} state - Redux state.
143
+ * @private
144
+ * @returns {void}
145
+ */
146
+function _navigate(state) {
147
+    const app = state['features/app'].app;
148
+    const routeToRender = _getRouteToRender(state);
149
+
150
+    app._navigate(routeToRender);
151
+}
152
+
153
+/**
154
+ * Sets room and navigates to new route if needed.
155
+ *
156
+ * @param {string} newRoom - New room name.
157
+ * @private
158
+ * @returns {Function}
159
+ */
160
+function _setRoomAndNavigate(newRoom) {
161
+    return (dispatch, getState) => {
162
+        const oldRoom = getState()['features/base/conference'].room;
163
+
164
+        dispatch(setRoom(newRoom));
165
+
166
+        const state = getState();
167
+        const room = state['features/base/conference'].room;
168
+
169
+        if (room !== oldRoom) {
170
+            _navigate(state);
171
+        }
172
+    };
173
+}

+ 1
- 1
react/features/app/components/AbstractApp.js View File

42
          * The URL, if any, with which the app was launched.
42
          * The URL, if any, with which the app was launched.
43
          */
43
          */
44
         url: React.PropTypes.string
44
         url: React.PropTypes.string
45
-    }
45
+    };
46
 
46
 
47
     /**
47
     /**
48
      * Init lib-jitsi-meet and create local participant when component is going
48
      * Init lib-jitsi-meet and create local participant when component is going

+ 46
- 25
react/features/app/components/App.web.js View File

1
+/* global $ */
1
 import React from 'react';
2
 import React from 'react';
2
 import { Provider } from 'react-redux';
3
 import { Provider } from 'react-redux';
4
+import { compose } from 'redux';
3
 import {
5
 import {
4
     browserHistory,
6
     browserHistory,
5
     Route,
7
     Route,
11
 import { RouteRegistry } from '../../base/navigator';
13
 import { RouteRegistry } from '../../base/navigator';
12
 
14
 
13
 import { AbstractApp } from './AbstractApp';
15
 import { AbstractApp } from './AbstractApp';
16
+import { appInit } from '../actions';
17
+
14
 
18
 
15
 /**
19
 /**
16
  * Root application component.
20
  * Root application component.
23
      *
27
      *
24
      * @static
28
      * @static
25
      */
29
      */
26
-    static propTypes = AbstractApp.propTypes
30
+    static propTypes = AbstractApp.propTypes;
27
 
31
 
28
     /**
32
     /**
29
      * Initializes a new App instance.
33
      * Initializes a new App instance.
44
         // Bind event handlers so they are only bound once for every instance.
48
         // Bind event handlers so they are only bound once for every instance.
45
         this._onRouteEnter = this._onRouteEnter.bind(this);
49
         this._onRouteEnter = this._onRouteEnter.bind(this);
46
         this._routerCreateElement = this._routerCreateElement.bind(this);
50
         this._routerCreateElement = this._routerCreateElement.bind(this);
51
+        this._getRoute = this._getRoute.bind(this);
52
+        this._getRoutes = this._getRoutes.bind(this);
47
     }
53
     }
48
 
54
 
49
     /**
55
     /**
50
-     * Temporarily, prevents the super from dispatching Redux actions until they
51
-     * are integrated into the Web App.
52
-     *
53
-     * @returns {void}
54
-     */
55
-    componentWillMount() {
56
-        // FIXME Do not override the super once the dispatching of Redux actions
57
-        // is integrated into the Web App.
58
-    }
59
-
60
-    /**
61
-     * Temporarily, prevents the super from dispatching Redux actions until they
62
-     * are integrated into the Web App.
56
+     * Inits the app before component will mount.
63
      *
57
      *
64
-     * @returns {void}
58
+     * @inheritdoc
65
      */
59
      */
66
-    componentWillUnmount() {
67
-        // FIXME Do not override the super once the dispatching of Redux actions
68
-        // is integrated into the Web App.
60
+    componentWillMount(...args) {
61
+        super.componentWillMount(...args);
62
+        this.props.store.dispatch(appInit());
69
     }
63
     }
70
 
64
 
71
     /**
65
     /**
75
      * @returns {ReactElement}
69
      * @returns {ReactElement}
76
      */
70
      */
77
     render() {
71
     render() {
78
-        const routes = RouteRegistry.getRoutes();
79
 
72
 
80
         return (
73
         return (
81
             <Provider store = { this.props.store }>
74
             <Provider store = { this.props.store }>
82
                 <Router
75
                 <Router
83
                     createElement = { this._routerCreateElement }
76
                     createElement = { this._routerCreateElement }
84
                     history = { this.history }>
77
                     history = { this.history }>
85
-                    { routes.map(r =>
86
-                        <Route
87
-                            component = { r.component }
88
-                            key = { r.component }
89
-                            path = { r.path } />
90
-                    ) }
78
+                    { this._getRoutes() }
91
                 </Router>
79
                 </Router>
92
             </Provider>
80
             </Provider>
93
         );
81
         );
114
         return store.dispatch(push(path));
102
         return store.dispatch(push(path));
115
     }
103
     }
116
 
104
 
105
+    /**
106
+     * Returns routes for application.
107
+     *
108
+     * @returns {Array}
109
+     * @private
110
+     */
111
+    _getRoutes() {
112
+        const routes = RouteRegistry.getRoutes();
113
+
114
+        return routes.map(this._getRoute);
115
+    }
116
+
117
+    /**
118
+     * Method returns route for React Router.
119
+     *
120
+     * @param {Object} route - Object that describes route.
121
+     * @returns {ReactElement}
122
+     * @private
123
+     */
124
+    _getRoute(route) {
125
+        const onEnter = route.onEnter || $.noop;
126
+        const handler = compose(this._onRouteEnter, onEnter);
127
+
128
+        return (
129
+            <Route
130
+                component = { route.component }
131
+                key = { route.component }
132
+                onEnter = { handler }
133
+                path = { route.path } />
134
+        );
135
+    }
136
+
117
     /**
137
     /**
118
      * Invoked by react-router to notify this App that a Route is about to be
138
      * Invoked by react-router to notify this App that a Route is about to be
119
      * rendered.
139
      * rendered.
122
      * @returns {void}
142
      * @returns {void}
123
      */
143
      */
124
     _onRouteEnter() {
144
     _onRouteEnter() {
145
+
125
         // XXX The following is mandatory. Otherwise, moving back & forward
146
         // XXX The following is mandatory. Otherwise, moving back & forward
126
         // through the browser's history could leave this App on the Conference
147
         // through the browser's history could leave this App on the Conference
127
         // page without a room name.
148
         // page without a room name.

react/features/app/functions.js → react/features/app/functions.native.js View File


+ 214
- 0
react/features/app/functions.web.js View File

1
+/* global APP, JitsiMeetJS, loggingConfig */
2
+import { isRoomValid } from '../base/conference';
3
+import { RouteRegistry } from '../base/navigator';
4
+import { Conference } from '../conference';
5
+import { WelcomePage } from '../welcome';
6
+
7
+import getTokenData from '../../../modules/tokendata/TokenData';
8
+import settings from '../../../modules/settings/Settings';
9
+
10
+import URLProcessor from '../../../modules/config/URLProcessor';
11
+import JitsiMeetLogStorage from '../../../modules/util/JitsiMeetLogStorage';
12
+
13
+// eslint-disable-next-line max-len
14
+import KeyboardShortcut from '../../../modules/keyboardshortcut/keyboardshortcut';
15
+
16
+const Logger = require('jitsi-meet-logger');
17
+const LogCollector = Logger.LogCollector;
18
+
19
+
20
+/**
21
+ * Gets room name and domain from URL object.
22
+ *
23
+ * @param {URL} url - URL object.
24
+ * @private
25
+ * @returns {{
26
+ *      domain: (string|undefined),
27
+ *      room: (string|undefined)
28
+ *  }}
29
+ */
30
+function _getRoomAndDomainFromUrlObject(url) {
31
+    let domain;
32
+    let room;
33
+
34
+    if (url) {
35
+        domain = url.hostname;
36
+        room = url.pathname.substr(1);
37
+
38
+        // Convert empty string to undefined to simplify checks.
39
+        if (room === '') {
40
+            room = undefined;
41
+        }
42
+        if (domain === '') {
43
+            domain = undefined;
44
+        }
45
+    }
46
+
47
+    return {
48
+        domain,
49
+        room
50
+    };
51
+}
52
+
53
+/**
54
+ * Gets conference room name and connection domain from URL.
55
+ *
56
+ * @param {(string|undefined)} url - URL.
57
+ * @returns {{
58
+ *      domain: (string|undefined),
59
+ *      room: (string|undefined)
60
+ *  }}
61
+ */
62
+export function _getRoomAndDomainFromUrlString(url) {
63
+    // Rewrite the specified URL in order to handle special cases such as
64
+    // hipchat.com and enso.me which do not follow the common pattern of most
65
+    // Jitsi Meet deployments.
66
+    if (typeof url === 'string') {
67
+        // hipchat.com
68
+        let regex = /^(https?):\/\/hipchat.com\/video\/call\//gi;
69
+        let match = regex.exec(url);
70
+
71
+        if (!match) {
72
+            // enso.me
73
+            regex = /^(https?):\/\/enso\.me\/(?:call|meeting)\//gi;
74
+            match = regex.exec(url);
75
+        }
76
+        if (match && match.length > 1) {
77
+            /* eslint-disable no-param-reassign, prefer-template */
78
+
79
+            url
80
+                = match[1] /* URL protocol */
81
+                + '://enso.hipchat.me/'
82
+                + url.substring(regex.lastIndex);
83
+
84
+            /* eslint-enable no-param-reassign, prefer-template */
85
+        }
86
+    }
87
+
88
+    return _getRoomAndDomainFromUrlObject(_urlStringToObject(url));
89
+}
90
+
91
+/**
92
+ * Determines which route is to be rendered in order to depict a specific Redux
93
+ * store.
94
+ *
95
+ * @param {(Object|Function)} stateOrGetState - Redux state or Regux getState()
96
+ * method.
97
+ * @returns {Route}
98
+ */
99
+export function _getRouteToRender(stateOrGetState) {
100
+    const state
101
+        = typeof stateOrGetState === 'function'
102
+        ? stateOrGetState()
103
+        : stateOrGetState;
104
+    const room = state['features/base/conference'].room;
105
+    const component = isRoomValid(room) ? Conference : WelcomePage;
106
+
107
+    return RouteRegistry.getRouteByComponent(component);
108
+}
109
+
110
+/**
111
+ * Parses a string into a URL (object).
112
+ *
113
+ * @param {(string|undefined)} url - The URL to parse.
114
+ * @private
115
+ * @returns {URL}
116
+ */
117
+function _urlStringToObject(url) {
118
+    let urlObj;
119
+
120
+    if (url) {
121
+        try {
122
+            urlObj = new URL(url);
123
+        } catch (ex) {
124
+            // The return value will signal the failure & the logged
125
+            // exception will provide the details to the developers.
126
+            console.log(`${url} seems to be not a valid URL, but it's OK`, ex);
127
+        }
128
+    }
129
+
130
+    return urlObj;
131
+}
132
+
133
+/**
134
+ * Temporary solution. Later we'll get rid of global APP
135
+ * and set its properties in redux store.
136
+ *
137
+ * @returns {void}
138
+ */
139
+export function init() {
140
+    _setConfigParametersFromUrl();
141
+    _initLogging();
142
+
143
+    APP.keyboardshortcut = KeyboardShortcut;
144
+    APP.tokenData = getTokenData();
145
+    APP.API.init(APP.tokenData.externalAPISettings);
146
+
147
+    APP.translation.init(settings.getLanguage());
148
+}
149
+
150
+/**
151
+ * Initializes logging in the app.
152
+ *
153
+ * @private
154
+ * @returns {void}
155
+ */
156
+function _initLogging() {
157
+    // Adjust logging level
158
+    configureLoggingLevels();
159
+
160
+    // Create the LogCollector and register it as the global log transport.
161
+    // It is done early to capture as much logs as possible. Captured logs
162
+    // will be cached, before the JitsiMeetLogStorage gets ready (statistics
163
+    // module is initialized).
164
+    if (!APP.logCollector && !loggingConfig.disableLogCollector) {
165
+        APP.logCollector = new LogCollector(new JitsiMeetLogStorage());
166
+        Logger.addGlobalTransport(APP.logCollector);
167
+        JitsiMeetJS.addGlobalLogTransport(APP.logCollector);
168
+    }
169
+}
170
+
171
+/**
172
+ * Adjusts the logging levels.
173
+ *
174
+ * @private
175
+ * @returns {void}
176
+ */
177
+function configureLoggingLevels() {
178
+    // NOTE The library Logger is separated from
179
+    // the app loggers, so the levels
180
+    // have to be set in two places
181
+
182
+    // Set default logging level
183
+    const defaultLogLevel
184
+        = loggingConfig.defaultLogLevel || JitsiMeetJS.logLevels.TRACE;
185
+
186
+    Logger.setLogLevel(defaultLogLevel);
187
+    JitsiMeetJS.setLogLevel(defaultLogLevel);
188
+
189
+    // NOTE console was used on purpose here to go around the logging
190
+    // and always print the default logging level to the console
191
+    console.info(`Default logging level set to: ${defaultLogLevel}`);
192
+
193
+    // Set log level for each logger
194
+    if (loggingConfig) {
195
+        Object.keys(loggingConfig).forEach(loggerName => {
196
+            if (loggerName !== 'defaultLogLevel') {
197
+                const level = loggingConfig[loggerName];
198
+
199
+                Logger.setLogLevelById(level, loggerName);
200
+                JitsiMeetJS.setLogLevelById(level, loggerName);
201
+            }
202
+        });
203
+    }
204
+}
205
+
206
+/**
207
+ * Sets config parameters from query string.
208
+ *
209
+ * @private
210
+ * @returns {void}
211
+ */
212
+function _setConfigParametersFromUrl() {
213
+    URLProcessor.setConfigParametersFromUrl();
214
+}

react/features/base/connection/actions.js → react/features/base/connection/actions.native.js View File


+ 96
- 0
react/features/base/connection/actions.web.js View File

1
+/* global APP, JitsiMeetJS */
2
+
3
+import {
4
+    SET_DOMAIN
5
+} from './actionTypes';
6
+import './reducer';
7
+import UIEvents from '../../../../service/UI/UIEvents';
8
+
9
+const logger = require('jitsi-meet-logger').getLogger(__filename);
10
+const ConferenceEvents = JitsiMeetJS.events.conference;
11
+
12
+
13
+/**
14
+ * Opens new connection.
15
+ *
16
+ * @returns {Promise<JitsiConnection>}
17
+ */
18
+export function connect() {
19
+    return (dispatch, getState) => {
20
+        const state = getState();
21
+        const room = state['features/base/conference'].room;
22
+
23
+        // XXX For web based version we use conference initialization logic
24
+        // from the old app (at the moment of writing).
25
+        return APP.conference.init({ roomName: room }).then(() => {
26
+            if (APP.logCollector) {
27
+                // Start the LogCollector's periodic "store logs" task
28
+                APP.logCollector.start();
29
+                APP.logCollectorStarted = true;
30
+
31
+                // Make an attempt to flush in case a lot of logs have been
32
+                // cached, before the collector was started.
33
+                APP.logCollector.flush();
34
+
35
+                // This event listener will flush the logs, before
36
+                // the statistics module (CallStats) is stopped.
37
+                //
38
+                // NOTE The LogCollector is not stopped, because this event can
39
+                // be triggered multiple times during single conference
40
+                // (whenever statistics module is stopped). That includes
41
+                // the case when Jicofo terminates the single person left in the
42
+                // room. It will then restart the media session when someone
43
+                // eventually join the room which will start the stats again.
44
+                APP.conference.addConferenceListener(
45
+                    ConferenceEvents.BEFORE_STATISTICS_DISPOSED,
46
+                    () => {
47
+                        if (APP.logCollector) {
48
+                            APP.logCollector.flush();
49
+                        }
50
+                    }
51
+                );
52
+            }
53
+
54
+            APP.UI.initConference();
55
+
56
+            APP.UI.addListener(UIEvents.LANG_CHANGED, language => {
57
+                APP.translation.setLanguage(language);
58
+                APP.settings.setLanguage(language);
59
+            });
60
+
61
+            APP.keyboardshortcut.init();
62
+        })
63
+            .catch(err => {
64
+                APP.UI.hideRingOverLay();
65
+                APP.API.notifyConferenceLeft(APP.conference.roomName);
66
+                logger.error(err);
67
+            });
68
+    };
69
+}
70
+
71
+/**
72
+ * Closes connection.
73
+ *
74
+ * @returns {Function}
75
+ */
76
+export function disconnect() {
77
+    // XXX For web based version we use conference
78
+    // hanging up logic from the old app.
79
+    return () => APP.conference.hangup();
80
+}
81
+
82
+/**
83
+ * Sets connection domain.
84
+ *
85
+ * @param {string} domain - Domain name.
86
+ * @returns {{
87
+ *      type: SET_DOMAIN,
88
+ *      domain: string
89
+ *  }}
90
+ */
91
+export function setDomain(domain) {
92
+    return {
93
+        type: SET_DOMAIN,
94
+        domain
95
+    };
96
+}

react/features/base/lib-jitsi-meet/actions.js → react/features/base/lib-jitsi-meet/actions.native.js View File


+ 61
- 0
react/features/base/lib-jitsi-meet/actions.web.js View File

1
+import {
2
+    LIB_DISPOSED,
3
+    SET_CONFIG
4
+} from './actionTypes';
5
+import './middleware';
6
+import './reducer';
7
+
8
+/**
9
+ * Disposes lib-jitsi-meet.
10
+ *
11
+ * @returns {Function}
12
+ */
13
+export function disposeLib() {
14
+    // XXX We're wrapping it with Promise, because:
15
+    // a) to be better aligned with initLib() method, which is async.
16
+    // b) as currently there is no implementation for it in lib-jitsi-meet, and
17
+    // there is a big chance it will be async.
18
+    // TODO Currently, lib-jitsi-meet doesn't have any functionality to
19
+    // dispose itself.
20
+    return dispatch => {
21
+        dispatch({ type: LIB_DISPOSED });
22
+
23
+        return Promise.resolve();
24
+    };
25
+}
26
+
27
+/**
28
+ * Initializes lib-jitsi-meet with passed configuration.
29
+ *
30
+ * @returns {Function}
31
+ */
32
+export function initLib() {
33
+    return (dispatch, getState) => {
34
+        const config = getState()['features/base/lib-jitsi-meet'].config;
35
+
36
+        if (!config) {
37
+            throw new Error('Cannot initialize lib-jitsi-meet without config');
38
+        }
39
+
40
+        // XXX Temporary solution. Until conference.js hasn't been moved
41
+        // to the react app we shouldn't use JitsiMeetJS from react app.
42
+        return Promise.resolve();
43
+    };
44
+}
45
+
46
+/**
47
+ * Sets config.
48
+ *
49
+ * @param {Object} config - Config object accepted by JitsiMeetJS#init()
50
+ * method.
51
+ * @returns {{
52
+ *      type: SET_CONFIG,
53
+ *      config: Object
54
+ *  }}
55
+ */
56
+export function setConfig(config) {
57
+    return {
58
+        type: SET_CONFIG,
59
+        config
60
+    };
61
+}

react/features/base/lib-jitsi-meet/functions.js → react/features/base/lib-jitsi-meet/functions.native.js View File


+ 10
- 0
react/features/base/lib-jitsi-meet/functions.web.js View File

1
+/**
2
+ * Returns config.js file from global scope.
3
+ * We can't use version that's being used for native app
4
+ * because the old app uses config from global scope.
5
+ *
6
+ * @returns {Promise<Object>}
7
+ */
8
+export function loadConfig() {
9
+    return Promise.resolve(window.config);
10
+}

+ 67
- 0
react/features/conference/actions.js View File

1
+/* global APP, config */
2
+import ConferenceUrl from '../../../modules/URL/ConferenceUrl';
3
+import BoshAddressChoice from '../../../modules/config/BoshAddressChoice';
4
+import { obtainConfig, setTokenData } from './functions';
5
+const logger = require('jitsi-meet-logger').getLogger(__filename);
6
+
7
+/**
8
+ * If we have an HTTP endpoint for getting config.json configured
9
+ * we're going to read it and override properties from config.js and
10
+ * interfaceConfig.js. If there is no endpoint we'll just
11
+ * continue with initialization.
12
+ * Keep in mind that if the endpoint has been configured and we fail
13
+ * to obtain the config for any reason then the conference won't
14
+ * start and error message will be displayed to the user.
15
+ *
16
+ * @returns {Function}
17
+ */
18
+export function obtainConfigAndInit() {
19
+    return () => {
20
+        const room = APP.conference.roomName;
21
+
22
+        if (config.configLocation) {
23
+            const location = config.configLocation;
24
+
25
+            obtainConfig(location, room)
26
+                .then(_obtainConfigHandler)
27
+                .then(_initConference)
28
+                .catch(err => {
29
+                    // Show obtain config error,
30
+                    // pass the error object for report
31
+                    APP.UI.messageHandler.openReportDialog(
32
+                        null, 'dialog.connectError', err);
33
+                });
34
+        } else {
35
+            BoshAddressChoice.chooseAddress(config, room);
36
+            _initConference();
37
+        }
38
+    };
39
+}
40
+
41
+/**
42
+ * Obtain config handler.
43
+ *
44
+ * @returns {Promise}
45
+ * @private
46
+ */
47
+function _obtainConfigHandler() {
48
+    const now = window.performance.now();
49
+
50
+    APP.connectionTimes['configuration.fetched'] = now;
51
+    logger.log('(TIME) configuration fetched:\t', now);
52
+
53
+    return Promise.resolve();
54
+}
55
+
56
+/**
57
+ *  Initialization of the app.
58
+ *
59
+ *  @returns {void}
60
+ *  @private
61
+ */
62
+function _initConference() {
63
+    setTokenData();
64
+
65
+    // Initialize the conference URL handler
66
+    APP.ConferenceUrl = new ConferenceUrl(window.location);
67
+}

+ 44
- 2
react/features/conference/components/Conference.web.js View File

1
-/* global interfaceConfig, APP */
1
+/* global APP, $, interfaceConfig */
2
 import React, { Component } from 'react';
2
 import React, { Component } from 'react';
3
+import { connect as reactReduxConnect } from 'react-redux';
4
+
5
+import {
6
+    connect,
7
+    disconnect
8
+} from '../../base/connection';
3
 
9
 
4
 /**
10
 /**
5
  * For legacy reasons, inline style for display none.
11
  * For legacy reasons, inline style for display none.
12
 /**
18
 /**
13
  * Implements a React Component which renders initial conference layout
19
  * Implements a React Component which renders initial conference layout
14
  */
20
  */
15
-export default class Conference extends Component {
21
+class Conference extends Component {
22
+    /**
23
+     * Until we don't rewrite UI using react components
24
+     * we use UI.start from old app. Also method translates
25
+     * component right after it has been mounted.
26
+     *
27
+     * @inheritdoc
28
+     */
29
+    componentDidMount() {
30
+        APP.UI.start();
31
+
32
+        // XXX Temporary solution until we add React translation.
33
+        APP.translation.translateElement($('#videoconference_page'));
34
+
35
+        this.props.dispatch(connect());
36
+    }
37
+
38
+    /**
39
+     * Disconnect from the conference when component will be
40
+     * unmounted.
41
+     *
42
+     * @inheritdoc
43
+     */
44
+    componentWillUnmount() {
45
+        this.props.dispatch(disconnect());
46
+    }
47
+
48
+    /**
49
+     * Conference component's property types.
50
+     *
51
+     * @static
52
+     */
53
+    static propTypes = {
54
+        dispatch: React.PropTypes.func
55
+    };
16
 
56
 
17
     /**
57
     /**
18
      * Initializes Conference component instance.
58
      * Initializes Conference component instance.
220
         return null;
260
         return null;
221
     }
261
     }
222
 }
262
 }
263
+
264
+export default reactReduxConnect()(Conference);

+ 108
- 0
react/features/conference/functions.js View File

1
+/* global APP, config */
2
+import HttpConfigFetch from '../../../modules/config/HttpConfigFetch';
3
+import ConferenceUrl from '../../../modules/URL/ConferenceUrl';
4
+import BoshAddressChoice from '../../../modules/config/BoshAddressChoice';
5
+const logger = require('jitsi-meet-logger').getLogger(__filename);
6
+
7
+/**
8
+ * If we have an HTTP endpoint for getting config.json configured
9
+ * we're going to read it and override properties from config.js and
10
+ * interfaceConfig.js. If there is no endpoint we'll just
11
+ * continue with initialization.
12
+ * Keep in mind that if the endpoint has been configured and we fail
13
+ * to obtain the config for any reason then the conference won't
14
+ * start and error message will be displayed to the user.
15
+ *
16
+ * @returns {Function}
17
+ */
18
+export function obtainConfigAndInit() {
19
+    // Skip initialization if conference is already initialized
20
+    if (!APP.ConferenceUrl) {
21
+        const room = APP.conference.roomName;
22
+
23
+        if (config.configLocation) {
24
+            const location = config.configLocation;
25
+
26
+            obtainConfig(location, room)
27
+                .then(_obtainConfigHandler)
28
+                .then(_initConference)
29
+                .catch(err => {
30
+                    // Show obtain config error,
31
+                    // pass the error object for report
32
+                    APP.UI.messageHandler.openReportDialog(
33
+                        null, 'dialog.connectError', err);
34
+                });
35
+        } else {
36
+            BoshAddressChoice.chooseAddress(config, room);
37
+            _initConference();
38
+        }
39
+    }
40
+}
41
+
42
+/**
43
+ * Obtain config handler.
44
+ *
45
+ * @returns {Promise}
46
+ * @private
47
+ */
48
+function _obtainConfigHandler() {
49
+    const now = window.performance.now();
50
+
51
+    APP.connectionTimes['configuration.fetched'] = now;
52
+    logger.log('(TIME) configuration fetched:\t', now);
53
+
54
+    return Promise.resolve();
55
+}
56
+
57
+/**
58
+ *  Initialization of the app.
59
+ *
60
+ *  @returns {void}
61
+ *  @private
62
+ */
63
+function _initConference() {
64
+    setTokenData();
65
+
66
+    // Initialize the conference URL handler
67
+    APP.ConferenceUrl = new ConferenceUrl(window.location);
68
+}
69
+
70
+/**
71
+ * Promise wrapper on obtain config method.
72
+ * When HttpConfigFetch will be moved to React app
73
+ * it's better to use load config instead.
74
+ *
75
+ * @param {string} location - URL of the domain.
76
+ * @param {string} room - Room name.
77
+ * @returns {Promise}
78
+ */
79
+export function obtainConfig(location, room) {
80
+    return new Promise((resolve, reject) => {
81
+        HttpConfigFetch.obtainConfig(location, room, (success, error) => {
82
+            if (success) {
83
+                resolve();
84
+            } else {
85
+                reject(error);
86
+            }
87
+        });
88
+    });
89
+}
90
+
91
+/**
92
+ * If JWT token data it will be used for local user settings.
93
+ *
94
+ * @returns {void}
95
+ */
96
+export function setTokenData() {
97
+    const localUser = APP.tokenData.caller;
98
+
99
+    if (localUser) {
100
+        const email = localUser.getEmail();
101
+        const avatarUrl = localUser.getAvatarUrl();
102
+        const name = localUser.getName();
103
+
104
+        APP.settings.setEmail((email || '').trim(), true);
105
+        APP.settings.setAvatarUrl((avatarUrl || '').trim());
106
+        APP.settings.setDisplayName((name || '').trim(), true);
107
+    }
108
+}

+ 7
- 1
react/features/conference/route.js View File

1
 import { RouteRegistry } from '../base/navigator';
1
 import { RouteRegistry } from '../base/navigator';
2
 
2
 
3
 import { Conference } from './components';
3
 import { Conference } from './components';
4
+import { obtainConfigAndInit } from './functions';
4
 
5
 
5
 /**
6
 /**
6
  * Register route for Conference (page).
7
  * Register route for Conference (page).
7
  */
8
  */
8
 RouteRegistry.register({
9
 RouteRegistry.register({
9
     component: Conference,
10
     component: Conference,
10
-    path: '/:room'
11
+    path: '/:room',
12
+    onEnter: () => {
13
+        // XXX: If config or jwt are set by hash or query parameters
14
+        // Getting raw URL before stripping it.
15
+        obtainConfigAndInit();
16
+    }
11
 });
17
 });

+ 14
- 23
react/features/welcome/components/WelcomePage.web.js View File

1
-/* global APP, interfaceConfig */
1
+/* global interfaceConfig, APP, $  */
2
 
2
 
3
 import React from 'react';
3
 import React from 'react';
4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
5
 
5
 
6
-import { Conference } from '../../conference';
7
-
8
 import { AbstractWelcomePage, mapStateToProps } from './AbstractWelcomePage';
6
 import { AbstractWelcomePage, mapStateToProps } from './AbstractWelcomePage';
9
 
7
 
10
 /**
8
 /**
52
         if (this.state.generateRoomnames) {
50
         if (this.state.generateRoomnames) {
53
             this._updateRoomname();
51
             this._updateRoomname();
54
         }
52
         }
53
+
54
+        // XXX Temporary solution until we add React translation.
55
+        APP.translation.translateElement($('#welcome_page'));
55
     }
56
     }
56
 
57
 
57
     /**
58
     /**
61
      * @returns {ReactElement|null}
62
      * @returns {ReactElement|null}
62
      */
63
      */
63
     render() {
64
     render() {
64
-        // FIXME The rendering of Conference bellow is a very quick and dirty
65
-        // temporary fix for the following issue: when the WelcomePage is
66
-        // disabled, app.js expects Conference to be rendered already and only
67
-        // then it builds a room name but the App component expects the room
68
-        // name to be built already (by looking at the window's location) in
69
-        // order to choose between WelcomePage and Conference.
70
         return (
65
         return (
71
             <div>
66
             <div>
72
                 <div id = 'welcome_page'>
67
                 <div id = 'welcome_page'>
77
                         this._renderMain()
72
                         this._renderMain()
78
                     }
73
                     }
79
                 </div>
74
                 </div>
80
-                <Conference />
81
             </div>
75
             </div>
82
         );
76
         );
83
     }
77
     }
132
         });
126
         });
133
     }
127
     }
134
 
128
 
135
-    /**
136
-     * Overrides the super in order to prevent the dispatching of the Redux
137
-     * action SET_ROOM.
138
-     *
139
-     * @override
140
-     * @protected
141
-     * @returns {null}
142
-     */
143
-    _onJoin() {
144
-        // Don't call the super implementation and thus prevent the dispatching
145
-        // of the Redux action SET_ROOM.
146
-    }
147
-
148
     /**
129
     /**
149
      * Handles 'keydown' event to initiate joining the room when the
130
      * Handles 'keydown' event to initiate joining the room when the
150
      * 'Enter/Return' button is pressed.
131
      * 'Enter/Return' button is pressed.
359
         return null;
340
         return null;
360
     }
341
     }
361
 
342
 
343
+    /**
344
+    * Handles updating roomname.
345
+    *
346
+    * @private
347
+    * @returns {void}
348
+    */
349
+    _onUpdateRoomname() {
350
+        this._updateRoomname();
351
+    }
352
+
362
     /**
353
     /**
363
      * Renders the main part of this WelcomePage.
354
      * Renders the main part of this WelcomePage.
364
      *
355
      *

+ 22
- 2
react/features/welcome/route.js View File

1
+/* global APP */
1
 import { RouteRegistry } from '../base/navigator';
2
 import { RouteRegistry } from '../base/navigator';
2
-
3
+import { generateRoomWithoutSeparator } from '../base/util';
3
 import { WelcomePage } from './components';
4
 import { WelcomePage } from './components';
4
 
5
 
6
+
7
+/**
8
+ * Function that checks if welcome page is enabled and if it isn't
9
+ * redirects to randomly created conference.
10
+ *
11
+ * @param {Object} nextState - Next router state.
12
+ * @param {Function} replace - Function to redirect to another path.
13
+ * @returns {void}
14
+ */
15
+function onEnter(nextState, replace) {
16
+    if (!APP.settings.isWelcomePageEnabled()) {
17
+        const generatedRoomname = generateRoomWithoutSeparator();
18
+        const normalizedRoomname = generatedRoomname.toLowerCase();
19
+
20
+        replace(`/${normalizedRoomname}`);
21
+    }
22
+}
23
+
5
 /**
24
 /**
6
  * Register route for WelcomePage.
25
  * Register route for WelcomePage.
7
  */
26
  */
8
 RouteRegistry.register({
27
 RouteRegistry.register({
9
     component: WelcomePage,
28
     component: WelcomePage,
10
-    path: '/'
29
+    path: '/',
30
+    onEnter
11
 });
31
 });

+ 33
- 7
react/index.web.js View File

1
+/* global APP */
1
 import React from 'react';
2
 import React from 'react';
2
 import ReactDOM from 'react-dom';
3
 import ReactDOM from 'react-dom';
3
 import { browserHistory } from 'react-router';
4
 import { browserHistory } from 'react-router';
15
     ReducerRegistry
16
     ReducerRegistry
16
 } from './features/base/redux';
17
 } from './features/base/redux';
17
 
18
 
19
+const logger = require('jitsi-meet-logger').getLogger(__filename);
20
+
18
 // Create combined reducer from all reducers in registry + routerReducer from
21
 // Create combined reducer from all reducers in registry + routerReducer from
19
 // 'react-router-redux' module (stores location updates from history).
22
 // 'react-router-redux' module (stores location updates from history).
20
 // @see https://github.com/reactjs/react-router-redux#routerreducer.
23
 // @see https://github.com/reactjs/react-router-redux#routerreducer.
45
 // Create Redux store with our reducer and middleware.
48
 // Create Redux store with our reducer and middleware.
46
 const store = createStore(reducer, middleware);
49
 const store = createStore(reducer, middleware);
47
 
50
 
48
-// Render the main Component.
49
-ReactDOM.render(
50
-    <App
51
-        config = { config }
52
-        store = { store }
53
-        url = { window.location.toString() } />,
54
-    document.getElementById('react'));
51
+/**
52
+ * Render the app when DOM tree has been loaded.
53
+ */
54
+document.addEventListener('DOMContentLoaded', () => {
55
+    const now = window.performance.now();
56
+
57
+    APP.connectionTimes['document.ready'] = now;
58
+    logger.log('(TIME) document ready:\t', now);
59
+
60
+    // Render the main Component.
61
+    ReactDOM.render(
62
+        <App
63
+            config = { config }
64
+            store = { store }
65
+            url = { window.location.toString() } />,
66
+        document.getElementById('react'));
67
+});
68
+
69
+/**
70
+ * Stop collecting the logs and disposing the API when
71
+ * user closes the page.
72
+ */
73
+window.addEventListener('beforeunload', () => {
74
+    // Stop the LogCollector
75
+    if (APP.logCollectorStarted) {
76
+        APP.logCollector.stop();
77
+        APP.logCollectorStarted = false;
78
+    }
79
+    APP.API.dispose();
80
+});

Loading…
Cancel
Save