浏览代码

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

j8
Lyubomir Marinov 8 年前
父节点
当前提交
d17cc9fa86

+ 8
- 250
app.js 查看文件

@@ -1,6 +1,4 @@
1
-/* global $, config, getRoomName, loggingConfig, JitsiMeetJS */
2 1
 /* application specific logic */
3
-const logger = require("jitsi-meet-logger").getLogger(__filename);
4 2
 
5 3
 import "babel-polyfill";
6 4
 import "jquery";
@@ -18,104 +16,13 @@ import 'aui-experimental-css';
18 16
 
19 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 19
 import UI from "./modules/UI/UI";
31 20
 import settings from "./modules/settings/Settings";
32 21
 import conference from './conference';
33
-import ConferenceUrl from './modules/URL/ConferenceUrl';
34 22
 import API from './modules/API/API';
35 23
 
36
-import UIEvents from './service/UI/UIEvents';
37
-import getTokenData from "./modules/tokendata/TokenData";
38 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 26
 const APP = {
120 27
     // Used by do_external_connect.js if we receive the attach data after
121 28
     // connect was already executed. status property can be "initialized",
@@ -152,164 +59,15 @@ const APP = {
152 59
      */
153 60
     ConferenceUrl : null,
154 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 73
 module.exports = APP;

+ 1
- 14
modules/UI/UI.js 查看文件

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

+ 0
- 30
modules/UI/welcome_page/WelcomePage.js 查看文件

@@ -1,30 +0,0 @@
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 查看文件

@@ -32,13 +32,13 @@
32 32
     "jws": "*",
33 33
     "lib-jitsi-meet": "jitsi/lib-jitsi-meet",
34 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 37
     "react-native": "0.39.2",
38 38
     "react-native-prompt": "^1.0.0",
39 39
     "react-native-vector-icons": "^3.0.0",
40 40
     "react-native-webrtc": "jitsi/react-native-webrtc",
41
-    "react-redux": "^5.0.1",
41
+    "react-redux": "^5.0.2",
42 42
     "react-router": "^3.0.0",
43 43
     "react-router-redux": "^4.0.7",
44 44
     "redux": "^3.5.2",
@@ -60,7 +60,7 @@
60 60
     "babel-preset-stage-1": "^6.16.0",
61 61
     "clean-css": "*",
62 62
     "css-loader": "*",
63
-    "eslint": "^3.12.2",
63
+    "eslint": "^3.13.1",
64 64
     "eslint-plugin-jsdoc": "*",
65 65
     "eslint-plugin-react": "*",
66 66
     "eslint-plugin-react-native": "^2.2.1",

+ 16
- 13
react/features/app/actions.js 查看文件

@@ -1,23 +1,26 @@
1 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 6
 import {
16 7
     _getRoomAndDomainFromUrlString,
17
-    _getRouteToRender
8
+    _getRouteToRender,
9
+    init
18 10
 } from './functions';
19 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 25
  * Triggers an in-app navigation to a different route. Allows navigation to be
23 26
  * abstracted between the mobile and web versions.

+ 50
- 31
react/features/app/components/App.web.js 查看文件

@@ -1,15 +1,12 @@
1 1
 import React from 'react';
2 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 4
 import { push, syncHistoryWithStore } from 'react-router-redux';
9 5
 
10 6
 import { getDomain } from '../../base/connection';
11 7
 import { RouteRegistry } from '../../base/navigator';
12 8
 
9
+import { appInit } from '../actions';
13 10
 import { AbstractApp } from './AbstractApp';
14 11
 
15 12
 /**
@@ -42,30 +39,18 @@ export class App extends AbstractApp {
42 39
         this.history = syncHistoryWithStore(browserHistory, props.store);
43 40
 
44 41
         // Bind event handlers so they are only bound once for every instance.
45
-        this._onRouteEnter = this._onRouteEnter.bind(this);
46 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,19 +60,14 @@ export class App extends AbstractApp {
75 60
      * @returns {ReactElement}
76 61
      */
77 62
     render() {
78
-        const routes = RouteRegistry.getRoutes();
79
-
80 63
         return (
81 64
             <Provider store = { this.props.store }>
82 65
                 <Router
83 66
                     createElement = { this._routerCreateElement }
84 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 71
                 </Router>
92 72
             </Provider>
93 73
         );
@@ -118,10 +98,18 @@ export class App extends AbstractApp {
118 98
      * Invoked by react-router to notify this App that a Route is about to be
119 99
      * rendered.
120 100
      *
101
+     * @param {Route} route - The Route that is about to be rendered.
121 102
      * @private
122 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 113
         // XXX The following is mandatory. Otherwise, moving back & forward
126 114
         // through the browser's history could leave this App on the Conference
127 115
         // page without a room name.
@@ -140,6 +128,37 @@ export class App extends AbstractApp {
140 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 163
      * Create a ReactElement from the specified component and props on behalf of
145 164
      * the associated Router.

react/features/app/functions.js → react/features/app/functions.native.js 查看文件

@@ -64,8 +64,8 @@ export function _getRoomAndDomainFromUrlString(url) {
64 64
 
65 65
             url
66 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 70
             /* eslint-enable no-param-reassign, prefer-template */
71 71
         }

+ 84
- 0
react/features/app/functions.web.js 查看文件

@@ -0,0 +1,84 @@
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 查看文件


+ 94
- 0
react/features/base/connection/actions.web.js 查看文件

@@ -0,0 +1,94 @@
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 查看文件

@@ -40,6 +40,12 @@ export function initLib() {
40 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 49
         return JitsiMeetJS.init(config)
44 50
             .then(() => dispatch({ type: LIB_INITIALIZED }))
45 51
             .catch(error => {

+ 7
- 0
react/features/base/lib-jitsi-meet/functions.js 查看文件

@@ -8,6 +8,13 @@ import { loadScript } from '../../base/util';
8 8
  * @returns {Promise<Object>}
9 9
  */
10 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 18
     return loadScript(new URL(path, host).toString())
12 19
         .then(() => {
13 20
             const config = window.config;

+ 43
- 2
react/features/conference/components/Conference.web.js 查看文件

@@ -1,5 +1,9 @@
1
-/* global interfaceConfig, APP */
1
+/* global $, APP, interfaceConfig */
2
+
2 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 9
  * For legacy reasons, inline style for display none.
@@ -12,7 +16,42 @@ const DISPLAY_NONE_STYLE = {
12 16
 /**
13 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 57
      * Initializes Conference component instance.
@@ -220,3 +259,5 @@ export default class Conference extends Component {
220 259
         return null;
221 260
     }
222 261
 }
262
+
263
+export default reactReduxConnect()(Conference);

+ 115
- 0
react/features/conference/route.js 查看文件

@@ -1,11 +1,126 @@
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 7
 import { RouteRegistry } from '../base/navigator';
2 8
 
3 9
 import { Conference } from './components';
4 10
 
11
+const logger = require('jitsi-meet-logger').getLogger(__filename);
12
+
5 13
 /**
6 14
  * Register route for Conference (page).
7 15
  */
8 16
 RouteRegistry.register({
9 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 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 查看文件

@@ -1,10 +1,8 @@
1
-/* global APP, interfaceConfig */
1
+/* global $, APP, interfaceConfig */
2 2
 
3 3
 import React from 'react';
4 4
 import { connect } from 'react-redux';
5 5
 
6
-import { Conference } from '../../conference';
7
-
8 6
 import { AbstractWelcomePage, mapStateToProps } from './AbstractWelcomePage';
9 7
 
10 8
 /**
@@ -52,6 +50,9 @@ class WelcomePage extends AbstractWelcomePage {
52 50
         if (this.state.generateRoomnames) {
53 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,12 +62,6 @@ class WelcomePage extends AbstractWelcomePage {
61 62
      * @returns {ReactElement|null}
62 63
      */
63 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 65
         return (
71 66
             <div>
72 67
                 <div id = 'welcome_page'>
@@ -77,7 +72,6 @@ class WelcomePage extends AbstractWelcomePage {
77 72
                         this._renderMain()
78 73
                     }
79 74
                 </div>
80
-                <Conference />
81 75
             </div>
82 76
         );
83 77
     }
@@ -132,19 +126,6 @@ class WelcomePage extends AbstractWelcomePage {
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 130
      * Handles 'keydown' event to initiate joining the room when the
150 131
      * 'Enter/Return' button is pressed.

+ 21
- 0
react/features/welcome/route.js 查看文件

@@ -1,4 +1,7 @@
1
+/* global APP */
2
+
1 3
 import { RouteRegistry } from '../base/navigator';
4
+import { generateRoomWithoutSeparator } from '../base/util';
2 5
 
3 6
 import { WelcomePage } from './components';
4 7
 
@@ -7,5 +10,23 @@ import { WelcomePage } from './components';
7 10
  */
8 11
 RouteRegistry.register({
9 12
     component: WelcomePage,
13
+    onEnter,
10 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 查看文件

@@ -61,7 +61,9 @@ class Root extends Component {
61 61
 
62 62
                 // XXX Start with an empty URL if getting the initial URL fails;
63 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 查看文件

@@ -1,19 +1,17 @@
1
+/* global APP */
2
+
1 3
 import React from 'react';
2 4
 import ReactDOM from 'react-dom';
3 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 7
 import { compose, createStore } from 'redux';
9 8
 import Thunk from 'redux-thunk';
10 9
 
11 10
 import config from './config';
12 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 16
 // Create combined reducer from all reducers in registry + routerReducer from
19 17
 // 'react-router-redux' module (stores location updates from history).
@@ -45,10 +43,33 @@ if (typeof window === 'object'
45 43
 // Create Redux store with our reducer and middleware.
46 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
+});

正在加载...
取消
保存