Browse Source

Merge branch 'BeatC-BeatC-moving-conference-init-to-react-1'

master
Lyubomir Marinov 8 years ago
parent
commit
d17cc9fa86

+ 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 react/index.web.js
67
+// for the sake of consistency and ease of understanding. Temporarily though
68
+// because we are at the beginning of introducing React into the Web app, allow
69
+// the execution of the Web app to start from app.js in order to reduce the
70
+// complexity of the beginning step.
71
+require('./react');
314
 
72
 
315
 module.exports = APP;
73
 module.exports = APP;

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

1
 /* global APP, JitsiMeetJS, $, config, interfaceConfig, toastr */
1
 /* global APP, JitsiMeetJS, $, config, interfaceConfig, toastr */
2
+
2
 const logger = require("jitsi-meet-logger").getLogger(__filename);
3
 const logger = require("jitsi-meet-logger").getLogger(__filename);
3
 
4
 
4
 var UI = {};
5
 var UI = {};
396
  */
397
  */
397
 UI.start = function () {
398
 UI.start = function () {
398
     document.title = interfaceConfig.APP_NAME;
399
     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
 
400
 
414
     // Set the defaults for prompt dialogs.
401
     // Set the defaults for prompt dialogs.
415
     $.prompt.setDefaults({persistent: false});
402
     $.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;

+ 4
- 4
package.json View File

32
     "jws": "*",
32
     "jws": "*",
33
     "lib-jitsi-meet": "jitsi/lib-jitsi-meet",
33
     "lib-jitsi-meet": "jitsi/lib-jitsi-meet",
34
     "postis": "^2.2.0",
34
     "postis": "^2.2.0",
35
-    "react": "15.4.1",
36
-    "react-dom": "15.4.1",
35
+    "react": "15.4.2",
36
+    "react-dom": "15.4.2",
37
     "react-native": "0.39.2",
37
     "react-native": "0.39.2",
38
     "react-native-prompt": "^1.0.0",
38
     "react-native-prompt": "^1.0.0",
39
     "react-native-vector-icons": "^3.0.0",
39
     "react-native-vector-icons": "^3.0.0",
40
     "react-native-webrtc": "jitsi/react-native-webrtc",
40
     "react-native-webrtc": "jitsi/react-native-webrtc",
41
-    "react-redux": "^5.0.1",
41
+    "react-redux": "^5.0.2",
42
     "react-router": "^3.0.0",
42
     "react-router": "^3.0.0",
43
     "react-router-redux": "^4.0.7",
43
     "react-router-redux": "^4.0.7",
44
     "redux": "^3.5.2",
44
     "redux": "^3.5.2",
60
     "babel-preset-stage-1": "^6.16.0",
60
     "babel-preset-stage-1": "^6.16.0",
61
     "clean-css": "*",
61
     "clean-css": "*",
62
     "css-loader": "*",
62
     "css-loader": "*",
63
-    "eslint": "^3.12.2",
63
+    "eslint": "^3.13.1",
64
     "eslint-plugin-jsdoc": "*",
64
     "eslint-plugin-jsdoc": "*",
65
     "eslint-plugin-react": "*",
65
     "eslint-plugin-react": "*",
66
     "eslint-plugin-react-native": "^2.2.1",
66
     "eslint-plugin-react-native": "^2.2.1",

+ 16
- 13
react/features/app/actions.js View File

1
 import { setRoom } from '../base/conference';
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';
2
+import { getDomain, setDomain } from '../base/connection';
3
+import { loadConfig, setConfig } from '../base/lib-jitsi-meet';
10
 
4
 
11
-import {
12
-    APP_WILL_MOUNT,
13
-    APP_WILL_UNMOUNT
14
-} from './actionTypes';
5
+import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
15
 import {
6
 import {
16
     _getRoomAndDomainFromUrlString,
7
     _getRoomAndDomainFromUrlString,
17
-    _getRouteToRender
8
+    _getRouteToRender,
9
+    init
18
 } from './functions';
10
 } from './functions';
19
 import './reducer';
11
 import './reducer';
20
 
12
 
13
+/**
14
+ * Temporary solution. Should dispatch actions related to initial settings of
15
+ * the app like setting log levels, reading the config parameters from query
16
+ * string etc.
17
+ *
18
+ * @returns {Function}
19
+ */
20
+export function appInit() {
21
+    return () => init();
22
+}
23
+
21
 /**
24
 /**
22
  * Triggers an in-app navigation to a different route. Allows navigation to be
25
  * Triggers an in-app navigation to a different route. Allows navigation to be
23
  * abstracted between the mobile and web versions.
26
  * abstracted between the mobile and web versions.

+ 50
- 31
react/features/app/components/App.web.js View File

1
 import React from 'react';
1
 import React from 'react';
2
 import { Provider } from 'react-redux';
2
 import { Provider } from 'react-redux';
3
-import {
4
-    browserHistory,
5
-    Route,
6
-    Router
7
-} from 'react-router';
3
+import { browserHistory, Route, Router } from 'react-router';
8
 import { push, syncHistoryWithStore } from 'react-router-redux';
4
 import { push, syncHistoryWithStore } from 'react-router-redux';
9
 
5
 
10
 import { getDomain } from '../../base/connection';
6
 import { getDomain } from '../../base/connection';
11
 import { RouteRegistry } from '../../base/navigator';
7
 import { RouteRegistry } from '../../base/navigator';
12
 
8
 
9
+import { appInit } from '../actions';
13
 import { AbstractApp } from './AbstractApp';
10
 import { AbstractApp } from './AbstractApp';
14
 
11
 
15
 /**
12
 /**
42
         this.history = syncHistoryWithStore(browserHistory, props.store);
39
         this.history = syncHistoryWithStore(browserHistory, props.store);
43
 
40
 
44
         // Bind event handlers so they are only bound once for every instance.
41
         // Bind event handlers so they are only bound once for every instance.
45
-        this._onRouteEnter = this._onRouteEnter.bind(this);
46
         this._routerCreateElement = this._routerCreateElement.bind(this);
42
         this._routerCreateElement = this._routerCreateElement.bind(this);
47
     }
43
     }
48
 
44
 
49
     /**
45
     /**
50
-     * Temporarily, prevents the super from dispatching Redux actions until they
51
-     * are integrated into the Web App.
46
+     * Inits the app before component will mount.
52
      *
47
      *
53
-     * @returns {void}
48
+     * @inheritdoc
54
      */
49
      */
55
-    componentWillMount() {
56
-        // FIXME Do not override the super once the dispatching of Redux actions
57
-        // is integrated into the Web App.
58
-    }
50
+    componentWillMount(...args) {
51
+        super.componentWillMount(...args);
59
 
52
 
60
-    /**
61
-     * Temporarily, prevents the super from dispatching Redux actions until they
62
-     * are integrated into the Web App.
63
-     *
64
-     * @returns {void}
65
-     */
66
-    componentWillUnmount() {
67
-        // FIXME Do not override the super once the dispatching of Redux actions
68
-        // is integrated into the Web App.
53
+        this.props.store.dispatch(appInit());
69
     }
54
     }
70
 
55
 
71
     /**
56
     /**
75
      * @returns {ReactElement}
60
      * @returns {ReactElement}
76
      */
61
      */
77
     render() {
62
     render() {
78
-        const routes = RouteRegistry.getRoutes();
79
-
80
         return (
63
         return (
81
             <Provider store = { this.props.store }>
64
             <Provider store = { this.props.store }>
82
                 <Router
65
                 <Router
83
                     createElement = { this._routerCreateElement }
66
                     createElement = { this._routerCreateElement }
84
                     history = { this.history }>
67
                     history = { this.history }>
85
-                    { routes.map(r =>
86
-                        <Route
87
-                            component = { r.component }
88
-                            key = { r.component }
89
-                            path = { r.path } />
90
-                    ) }
68
+                    {
69
+                        this._renderRoutes()
70
+                    }
91
                 </Router>
71
                 </Router>
92
             </Provider>
72
             </Provider>
93
         );
73
         );
118
      * Invoked by react-router to notify this App that a Route is about to be
98
      * Invoked by react-router to notify this App that a Route is about to be
119
      * rendered.
99
      * rendered.
120
      *
100
      *
101
+     * @param {Route} route - The Route that is about to be rendered.
121
      * @private
102
      * @private
122
      * @returns {void}
103
      * @returns {void}
123
      */
104
      */
124
-    _onRouteEnter() {
105
+    _onRouteEnter(route, ...args) {
106
+        // Notify the route that it is about to be entered.
107
+        const onEnter = route.onEnter;
108
+
109
+        if (typeof onEnter === 'function') {
110
+            onEnter(...args);
111
+        }
112
+
125
         // XXX The following is mandatory. Otherwise, moving back & forward
113
         // XXX The following is mandatory. Otherwise, moving back & forward
126
         // through the browser's history could leave this App on the Conference
114
         // through the browser's history could leave this App on the Conference
127
         // page without a room name.
115
         // page without a room name.
140
         this._openURL(url);
128
         this._openURL(url);
141
     }
129
     }
142
 
130
 
131
+    /**
132
+     * Renders a specific Route (for the purposes of the Router of this App).
133
+     *
134
+     * @param {Object} route - The Route to render.
135
+     * @returns {ReactElement}
136
+     * @private
137
+     */
138
+    _renderRoute(route) {
139
+        const onEnter = (...args) => {
140
+            this._onRouteEnter(route, ...args);
141
+        };
142
+
143
+        return (
144
+            <Route
145
+                component = { route.component }
146
+                key = { route.component }
147
+                onEnter = { onEnter }
148
+                path = { route.path } />
149
+        );
150
+    }
151
+
152
+    /**
153
+     * Renders the Routes of the Router of this App.
154
+     *
155
+     * @returns {Array.<ReactElement>}
156
+     * @private
157
+     */
158
+    _renderRoutes() {
159
+        return RouteRegistry.getRoutes().map(this._renderRoute, this);
160
+    }
161
+
143
     /**
162
     /**
144
      * Create a ReactElement from the specified component and props on behalf of
163
      * Create a ReactElement from the specified component and props on behalf of
145
      * the associated Router.
164
      * the associated Router.

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

64
 
64
 
65
             url
65
             url
66
                 = match[1] /* URL protocol */
66
                 = match[1] /* URL protocol */
67
-                   + '://enso.hipchat.me/'
68
-                   + url.substring(regex.lastIndex);
67
+                    + '://enso.hipchat.me/'
68
+                    + url.substring(regex.lastIndex);
69
 
69
 
70
             /* eslint-enable no-param-reassign, prefer-template */
70
             /* eslint-enable no-param-reassign, prefer-template */
71
         }
71
         }

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

1
+/* global APP, JitsiMeetJS, loggingConfig */
2
+
3
+import URLProcessor from '../../../modules/config/URLProcessor';
4
+import KeyboardShortcut
5
+    from '../../../modules/keyboardshortcut/keyboardshortcut';
6
+import settings from '../../../modules/settings/Settings';
7
+import getTokenData from '../../../modules/tokendata/TokenData';
8
+import JitsiMeetLogStorage from '../../../modules/util/JitsiMeetLogStorage';
9
+
10
+const Logger = require('jitsi-meet-logger');
11
+
12
+export * from './functions.native';
13
+
14
+/**
15
+ * Temporary solution. Later we'll get rid of global APP and set its properties
16
+ * in redux store.
17
+ *
18
+ * @returns {void}
19
+ */
20
+export function init() {
21
+    URLProcessor.setConfigParametersFromUrl();
22
+    _initLogging();
23
+
24
+    APP.keyboardshortcut = KeyboardShortcut;
25
+    APP.tokenData = getTokenData();
26
+    APP.API.init(APP.tokenData.externalAPISettings);
27
+
28
+    APP.translation.init(settings.getLanguage());
29
+}
30
+
31
+/**
32
+ * Adjusts the logging levels.
33
+ *
34
+ * @private
35
+ * @returns {void}
36
+ */
37
+function _configureLoggingLevels() {
38
+    // NOTE The library Logger is separated from the app loggers, so the levels
39
+    // have to be set in two places
40
+
41
+    // Set default logging level
42
+    const defaultLogLevel
43
+        = loggingConfig.defaultLogLevel || JitsiMeetJS.logLevels.TRACE;
44
+
45
+    Logger.setLogLevel(defaultLogLevel);
46
+    JitsiMeetJS.setLogLevel(defaultLogLevel);
47
+
48
+    // NOTE console was used on purpose here to go around the logging and always
49
+    // print the default logging level to the console
50
+    console.info(`Default logging level set to: ${defaultLogLevel}`);
51
+
52
+    // Set log level for each logger
53
+    if (loggingConfig) {
54
+        Object.keys(loggingConfig).forEach(loggerName => {
55
+            if (loggerName !== 'defaultLogLevel') {
56
+                const level = loggingConfig[loggerName];
57
+
58
+                Logger.setLogLevelById(level, loggerName);
59
+                JitsiMeetJS.setLogLevelById(level, loggerName);
60
+            }
61
+        });
62
+    }
63
+}
64
+
65
+/**
66
+ * Initializes logging in the app.
67
+ *
68
+ * @private
69
+ * @returns {void}
70
+ */
71
+function _initLogging() {
72
+    // Adjust logging level
73
+    _configureLoggingLevels();
74
+
75
+    // Create the LogCollector and register it as the global log transport. It
76
+    // is done early to capture as much logs as possible. Captured logs will be
77
+    // cached, before the JitsiMeetLogStorage gets ready (statistics module is
78
+    // initialized).
79
+    if (!APP.logCollector && !loggingConfig.disableLogCollector) {
80
+        APP.logCollector = new Logger.LogCollector(new JitsiMeetLogStorage());
81
+        Logger.addGlobalTransport(APP.logCollector);
82
+        JitsiMeetJS.addGlobalLogTransport(APP.logCollector);
83
+    }
84
+}

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


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

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

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

40
             throw new Error('Cannot initialize lib-jitsi-meet without config');
40
             throw new Error('Cannot initialize lib-jitsi-meet without config');
41
         }
41
         }
42
 
42
 
43
+        // XXX Temporarily until conference.js is moved to the React app we
44
+        // shouldn't use JitsiMeetJS from the React app.
45
+        if (typeof APP !== 'undefined') {
46
+            return Promise.resolve();
47
+        }
48
+
43
         return JitsiMeetJS.init(config)
49
         return JitsiMeetJS.init(config)
44
             .then(() => dispatch({ type: LIB_INITIALIZED }))
50
             .then(() => dispatch({ type: LIB_INITIALIZED }))
45
             .catch(error => {
51
             .catch(error => {

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

8
  * @returns {Promise<Object>}
8
  * @returns {Promise<Object>}
9
  */
9
  */
10
 export function loadConfig(host, path = '/config.js') {
10
 export function loadConfig(host, path = '/config.js') {
11
+    // Returns config.js file from global scope. We can't use the version that's
12
+    // being used for the React Native app because the old/current Web app uses
13
+    // config from the global scope.
14
+    if (typeof APP !== 'undefined') {
15
+        return Promise.resolve(window.config);
16
+    }
17
+
11
     return loadScript(new URL(path, host).toString())
18
     return loadScript(new URL(path, host).toString())
12
         .then(() => {
19
         .then(() => {
13
             const config = window.config;
20
             const config = window.config;

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

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

+ 115
- 0
react/features/conference/route.js View File

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

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

1
-/* global APP, interfaceConfig */
1
+/* global $, APP, interfaceConfig */
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.

+ 21
- 0
react/features/welcome/route.js View File

1
+/* global APP */
2
+
1
 import { RouteRegistry } from '../base/navigator';
3
 import { RouteRegistry } from '../base/navigator';
4
+import { generateRoomWithoutSeparator } from '../base/util';
2
 
5
 
3
 import { WelcomePage } from './components';
6
 import { WelcomePage } from './components';
4
 
7
 
7
  */
10
  */
8
 RouteRegistry.register({
11
 RouteRegistry.register({
9
     component: WelcomePage,
12
     component: WelcomePage,
13
+    onEnter,
10
     path: '/'
14
     path: '/'
11
 });
15
 });
16
+
17
+/**
18
+ * If the Welcome page/screen is disabled, generates a (random) room (name) so
19
+ * that the Welcome page/screen is skipped and the Conference page/screen is
20
+ * presented instead.
21
+ *
22
+ * @param {Object} nextState - The next Router state.
23
+ * @param {Function} replace - The function to redirect to another path.
24
+ * @returns {void}
25
+ */
26
+function onEnter(nextState, replace) {
27
+    if (typeof APP !== 'undefined' && !APP.settings.isWelcomePageEnabled()) {
28
+        const room = generateRoomWithoutSeparator();
29
+
30
+        replace(`/${room}`);
31
+    }
32
+}

+ 3
- 1
react/index.native.js View File

61
 
61
 
62
                 // XXX Start with an empty URL if getting the initial URL fails;
62
                 // XXX Start with an empty URL if getting the initial URL fails;
63
                 // otherwise, nothing will be rendered.
63
                 // otherwise, nothing will be rendered.
64
-                this.setState({ url: null });
64
+                if (this.state.url !== null) {
65
+                    this.setState({ url: null });
66
+                }
65
             });
67
             });
66
     }
68
     }
67
 
69
 

+ 36
- 15
react/index.web.js View File

1
+/* global APP */
2
+
1
 import React from 'react';
3
 import React from 'react';
2
 import ReactDOM from 'react-dom';
4
 import ReactDOM from 'react-dom';
3
 import { browserHistory } from 'react-router';
5
 import { browserHistory } from 'react-router';
4
-import {
5
-    routerMiddleware,
6
-    routerReducer
7
-} from 'react-router-redux';
6
+import { routerMiddleware, routerReducer } from 'react-router-redux';
8
 import { compose, createStore } from 'redux';
7
 import { compose, createStore } from 'redux';
9
 import Thunk from 'redux-thunk';
8
 import Thunk from 'redux-thunk';
10
 
9
 
11
 import config from './config';
10
 import config from './config';
12
 import { App } from './features/app';
11
 import { App } from './features/app';
13
-import {
14
-    MiddlewareRegistry,
15
-    ReducerRegistry
16
-} from './features/base/redux';
12
+import { MiddlewareRegistry, ReducerRegistry } from './features/base/redux';
13
+
14
+const logger = require('jitsi-meet-logger').getLogger(__filename);
17
 
15
 
18
 // Create combined reducer from all reducers in registry + routerReducer from
16
 // Create combined reducer from all reducers in registry + routerReducer from
19
 // 'react-router-redux' module (stores location updates from history).
17
 // 'react-router-redux' module (stores location updates from history).
45
 // Create Redux store with our reducer and middleware.
43
 // Create Redux store with our reducer and middleware.
46
 const store = createStore(reducer, middleware);
44
 const store = createStore(reducer, middleware);
47
 
45
 
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'));
46
+/**
47
+ * Renders the app when the DOM tree has been loaded.
48
+ */
49
+document.addEventListener('DOMContentLoaded', () => {
50
+    const now = window.performance.now();
51
+
52
+    APP.connectionTimes['document.ready'] = now;
53
+    logger.log('(TIME) document ready:\t', now);
54
+
55
+    // Render the main Component.
56
+    ReactDOM.render(
57
+        <App
58
+            config = { config }
59
+            store = { store }
60
+            url = { window.location.toString() } />,
61
+        document.getElementById('react'));
62
+});
63
+
64
+/**
65
+ * Stops collecting the logs and disposing the API when the user closes the
66
+ * page.
67
+ */
68
+window.addEventListener('beforeunload', () => {
69
+    // Stop the LogCollector
70
+    if (APP.logCollectorStarted) {
71
+        APP.logCollector.stop();
72
+        APP.logCollectorStarted = false;
73
+    }
74
+    APP.API.dispose();
75
+});

Loading…
Cancel
Save