Parcourir la source

core: refactor routing

Unfortunately, as the Jitsi Meet development evolved the routing mechanism
became more complex and thre logic ended up spread across multiple parts of the
codebase, which made it hard to follow and extend.

This change aims to fix that by rewriting the routing logic and centralizing it
in (pretty much) a single place, with no implicit inter-dependencies.

In order to arrive there, however, some extra changes were needed, which were
not caught early enough and are thus part of this change:

- JitsiMeetJS initialization is now synchronous: there is nothing async about
  it, and the only async requirement (Temasys support) was lifted. See [0].
- WebRTC support can be detected early: building on top of the above, WebRTC
  support can now be detected immediately, so take advantage of this to simplify
  how we handle unsupported browsers. See [0].

The new router takes decissions based on the Redux state at the time of
invocation. A route can be represented by either a component or a URl reference,
with the latter taking precedence. On mobile, obviously, there is no concept of
URL reference so routing is based solely on components.

[0]: https://github.com/jitsi/lib-jitsi-meet/pull/779
master
Saúl Ibarra Corretgé il y a 7 ans
Parent
révision
155e02bbfb
38 fichiers modifiés avec 343 ajouts et 912 suppressions
  1. 30
    36
      conference.js
  2. 0
    32
      modules/URL/ConferenceUrl.js
  3. 8
    15
      react/features/app/actions.js
  4. 10
    47
      react/features/app/components/AbstractApp.js
  5. 0
    97
      react/features/app/components/App.web.js
  6. 1
    1
      react/features/app/functions.native.js
  7. 2
    82
      react/features/app/functions.web.js
  8. 0
    22
      react/features/app/getRouteToRender.js
  9. 3
    39
      react/features/app/middleware.js
  10. 109
    0
      react/features/app/router.js
  11. 4
    1
      react/features/base/conference/reducer.js
  12. 2
    14
      react/features/base/connection/actions.web.js
  13. 0
    20
      react/features/base/lib-jitsi-meet/actionTypes.js
  14. 11
    47
      react/features/base/lib-jitsi-meet/actions.js
  15. 0
    5
      react/features/base/lib-jitsi-meet/constants.js
  16. 0
    1
      react/features/base/lib-jitsi-meet/index.js
  17. 2
    41
      react/features/base/lib-jitsi-meet/middleware.js
  18. 2
    17
      react/features/base/lib-jitsi-meet/reducer.js
  19. 0
    119
      react/features/base/react/RouteRegistry.js
  20. 0
    1
      react/features/base/react/index.js
  21. 57
    4
      react/features/conference/components/Conference.native.js
  22. 85
    20
      react/features/conference/components/Conference.web.js
  23. 0
    1
      react/features/conference/index.js
  24. 0
    105
      react/features/conference/route.js
  25. 0
    0
      react/features/deep-linking/components/DeepLinkingDesktopPage.native.js
  26. 0
    0
      react/features/deep-linking/components/DeepLinkingDesktopPage.web.js
  27. 0
    0
      react/features/deep-linking/components/DeepLinkingMobilePage.native.js
  28. 0
    0
      react/features/deep-linking/components/DeepLinkingMobilePage.web.js
  29. 0
    0
      react/features/deep-linking/components/NoMobileApp.native.js
  30. 0
    0
      react/features/deep-linking/components/NoMobileApp.web.js
  31. 1
    4
      react/features/device-selection/actions.js
  32. 2
    3
      react/features/settings/popup.js
  33. 0
    2
      react/features/unsupported-browser/index.js
  34. 0
    54
      react/features/unsupported-browser/middleware.js
  35. 12
    40
      react/features/welcome/components/BlankPage.native.js
  36. 2
    6
      react/features/welcome/functions.js
  37. 0
    1
      react/features/welcome/index.js
  38. 0
    35
      react/features/welcome/route.js

+ 30
- 36
conference.js Voir le fichier

@@ -2320,45 +2320,39 @@ export default {
2320 2320
      * @private
2321 2321
      */
2322 2322
     _initDeviceList() {
2323
-        JitsiMeetJS.mediaDevices.isDeviceListAvailable()
2324
-            .then(isDeviceListAvailable => {
2325
-                if (isDeviceListAvailable
2326
-                        && JitsiMeetJS.mediaDevices.isDeviceChangeAvailable()) {
2327
-                    JitsiMeetJS.mediaDevices.enumerateDevices(devices => {
2328
-                        // Ugly way to synchronize real device IDs with local
2329
-                        // storage and settings menu. This is a workaround until
2330
-                        // getConstraints() method will be implemented
2331
-                        // in browsers.
2332
-                        const { dispatch } = APP.store;
2333
-
2334
-                        if (this.localAudio) {
2335
-                            dispatch(updateSettings({
2336
-                                micDeviceId: this.localAudio.getDeviceId()
2337
-                            }));
2338
-                        }
2339
-
2340
-                        if (this.localVideo) {
2341
-                            dispatch(updateSettings({
2342
-                                cameraDeviceId: this.localVideo.getDeviceId()
2343
-                            }));
2344
-                        }
2345
-
2346
-                        mediaDeviceHelper.setCurrentMediaDevices(devices);
2347
-                        APP.UI.onAvailableDevicesChanged(devices);
2348
-                        APP.store.dispatch(updateDeviceList(devices));
2349
-                    });
2323
+        if (JitsiMeetJS.mediaDevices.isDeviceListAvailable()
2324
+                && JitsiMeetJS.mediaDevices.isDeviceChangeAvailable()) {
2325
+            JitsiMeetJS.mediaDevices.enumerateDevices(devices => {
2326
+                // Ugly way to synchronize real device IDs with local
2327
+                // storage and settings menu. This is a workaround until
2328
+                // getConstraints() method will be implemented
2329
+                // in browsers.
2330
+                const { dispatch } = APP.store;
2331
+
2332
+                if (this.localAudio) {
2333
+                    dispatch(updateSettings({
2334
+                        micDeviceId: this.localAudio.getDeviceId()
2335
+                    }));
2336
+                }
2350 2337
 
2351
-                    this.deviceChangeListener = devices =>
2352
-                        window.setTimeout(
2353
-                            () => this._onDeviceListChanged(devices), 0);
2354
-                    JitsiMeetJS.mediaDevices.addEventListener(
2355
-                        JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
2356
-                        this.deviceChangeListener);
2338
+                if (this.localVideo) {
2339
+                    dispatch(updateSettings({
2340
+                        cameraDeviceId: this.localVideo.getDeviceId()
2341
+                    }));
2357 2342
                 }
2358
-            })
2359
-            .catch(error => {
2360
-                logger.warn(`Error getting device list: ${error}`);
2343
+
2344
+                mediaDeviceHelper.setCurrentMediaDevices(devices);
2345
+                APP.UI.onAvailableDevicesChanged(devices);
2346
+                APP.store.dispatch(updateDeviceList(devices));
2361 2347
             });
2348
+
2349
+            this.deviceChangeListener = devices =>
2350
+                window.setTimeout(
2351
+                    () => this._onDeviceListChanged(devices), 0);
2352
+            JitsiMeetJS.mediaDevices.addEventListener(
2353
+                JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
2354
+                this.deviceChangeListener);
2355
+        }
2362 2356
     },
2363 2357
 
2364 2358
     /**

+ 0
- 32
modules/URL/ConferenceUrl.js Voir le fichier

@@ -1,32 +0,0 @@
1
-const logger = require('jitsi-meet-logger').getLogger(__filename);
2
-
3
-/**
4
- * The modules stores information about the URL used to start the conference and
5
- * provides utility methods for dealing with conference URL and reloads.
6
- */
7
-export default class ConferenceUrl {
8
-    /**
9
-     * Initializes the module.
10
-     *
11
-     * @param location an object which stores provides the info about conference
12
-     * URL(would be 'window.location' for the Web app). The params below are
13
-     * described based on the following example URL:
14
-     *
15
-     * https://example.com:8888/SomeConference1245?opt=1#somehash
16
-     *
17
-     * @param location.href full URL with all parameters, would be the whole URL
18
-     * from the example string above.
19
-     * @param location.host the host part of the URL, 'example.com' from
20
-     * the sample URL above.
21
-     * @param location.pathname the path part of the URL, would be
22
-     * '/SomeConference1245' from the example above.
23
-     * @param location.protocol the protocol part of the URL, would be 'https:'
24
-     * from the sample URL.
25
-     */
26
-    constructor(location) {
27
-        logger.info(`Stored original conference URL: ${location.href}`);
28
-        logger.info(
29
-                `Conference URL for invites: ${location.protocol}//${
30
-                    location.host}${location.pathname}`);
31
-    }
32
-}

+ 8
- 15
react/features/app/actions.js Voir le fichier

@@ -77,32 +77,25 @@ function _appNavigateToMandatoryLocation(
77 77
      * @returns {void}
78 78
      */
79 79
     function loadConfigSettled(error, config) {
80
-        let promise;
81
-
82 80
         // Due to the asynchronous nature of the loading, the specified config
83 81
         // may or may not be required by the time the notification arrives.
84 82
         // If we receive the config for a location we are no longer interested
85 83
         // in, "ignore" it - deliver it to the external API, for example, but do
86 84
         // not proceed with the appNavigate procedure/process.
87 85
         if (getState()['features/base/config'].locationURL === locationURL) {
88
-            promise = dispatch(setLocationURL(locationURL));
86
+            dispatch(setLocationURL(locationURL));
87
+            dispatch(setConfig(config));
89 88
         } else {
90 89
             // eslint-disable-next-line no-param-reassign
91 90
             error || (error = new Error('Config no longer needed!'));
92
-            promise = Promise.resolve();
93
-        }
94 91
 
95
-        return promise.then(() => {
96
-            if (error) {
97
-                // XXX The failure could be, for example, because of a
98
-                // certificate-related error. In which case the connection will
99
-                // fail later in Strophe anyway.
100
-                dispatch(loadConfigError(error, locationURL));
101
-                throw error;
102
-            }
92
+            // XXX The failure could be, for example, because of a
93
+            // certificate-related error. In which case the connection will
94
+            // fail later in Strophe anyway.
95
+            dispatch(loadConfigError(error, locationURL));
103 96
 
104
-            return dispatch(setConfig(config));
105
-        });
97
+            throw error;
98
+        }
106 99
     }
107 100
 }
108 101
 

+ 10
- 47
react/features/app/components/AbstractApp.js Voir le fichier

@@ -1,5 +1,6 @@
1 1
 /* global APP */
2 2
 
3
+import _ from 'lodash';
3 4
 import PropTypes from 'prop-types';
4 5
 import React, { Component, Fragment } from 'react';
5 6
 import { I18nextProvider } from 'react-i18next';
@@ -9,7 +10,6 @@ import Thunk from 'redux-thunk';
9 10
 
10 11
 import { i18next } from '../../base/i18n';
11 12
 import { localParticipantLeft } from '../../base/participants';
12
-import { RouteRegistry } from '../../base/react';
13 13
 import {
14 14
     MiddlewareRegistry,
15 15
     ReducerRegistry,
@@ -19,7 +19,6 @@ import { SoundCollection } from '../../base/sounds';
19 19
 import { PersistenceRegistry } from '../../base/storage';
20 20
 import { toURLString } from '../../base/util';
21 21
 import { OverlayContainer } from '../../overlay';
22
-import { BlankPage } from '../../welcome';
23 22
 
24 23
 import { appNavigate, appWillMount, appWillUnmount } from '../actions';
25 24
 
@@ -84,7 +83,7 @@ export class AbstractApp extends Component {
84 83
              *
85 84
              * @type {Route}
86 85
              */
87
-            route: undefined,
86
+            route: {},
88 87
 
89 88
             /**
90 89
              * The state of the »possible« async initialization of
@@ -114,7 +113,6 @@ export class AbstractApp extends Component {
114 113
                 .catch(() => { /* AbstractApp should always initialize! */ })
115 114
                 .then(() =>
116 115
                     this.setState({
117
-                        route: undefined,
118 116
                         store: this._maybeCreateStore(props)
119 117
                     }));
120 118
     }
@@ -242,7 +240,7 @@ export class AbstractApp extends Component {
242 240
      */
243 241
     render() {
244 242
         const { appAsyncInitialized, route } = this.state;
245
-        const component = (route && route.component) || BlankPage;
243
+        const { component } = route;
246 244
 
247 245
         if (appAsyncInitialized && component) {
248 246
             return (
@@ -421,33 +419,16 @@ export class AbstractApp extends Component {
421 419
      * @returns {Promise}
422 420
      */
423 421
     _navigate(route) {
424
-        if (RouteRegistry.areRoutesEqual(this.state.route, route)) {
422
+        if (_.isEqual(route, this.state.route)) {
425 423
             return Promise.resolve();
426 424
         }
427 425
 
428
-        let nextState = {
429
-            route
430
-        };
426
+        if (route.href) {
427
+            // This navigation requires loading a new URL in the browser.
428
+            window.location.href = route.href;
431 429
 
432
-        // The Web App was using react-router so it utilized react-router's
433
-        // onEnter. During the removal of react-router, modifications were
434
-        // minimized by preserving the onEnter interface:
435
-        // (1) Router would provide its nextState to the Route's onEnter. As the
436
-        // role of Router is now this AbstractApp and we use redux, provide the
437
-        // redux store instead.
438
-        // (2) A replace function would be provided to the Route in case it
439
-        // chose to redirect to another path.
440
-        route && this._onRouteEnter(route, this._getStore(), pathname => {
441
-            if (pathname) {
442
-                this._openURL(pathname);
443
-
444
-                // Do not proceed with the route because it chose to redirect to
445
-                // another path.
446
-                nextState = undefined;
447
-            } else {
448
-                nextState.route = undefined;
449
-            }
450
-        });
430
+            return Promise.resolve();
431
+        }
451 432
 
452 433
         // XXX React's setState is asynchronous which means that the value of
453 434
         // this.state.route above may not even be correct. If the check is
@@ -455,28 +436,10 @@ export class AbstractApp extends Component {
455 436
         // expected route. In order to mitigate the problem, _navigate was
456 437
         // changed to return a Promise.
457 438
         return new Promise(resolve => {
458
-            if (nextState) {
459
-                this.setState(nextState, resolve);
460
-            } else {
461
-                resolve();
462
-            }
439
+            this.setState({ route }, resolve);
463 440
         });
464 441
     }
465 442
 
466
-    /**
467
-     * Notifies this {@code App} that a specific Route is about to be rendered.
468
-     *
469
-     * @param {Route} route - The Route that is about to be rendered.
470
-     * @private
471
-     * @returns {void}
472
-     */
473
-    _onRouteEnter(route, ...args) {
474
-        // Notify the route that it is about to be entered.
475
-        const { onEnter } = route;
476
-
477
-        typeof onEnter === 'function' && onEnter(...args);
478
-    }
479
-
480 443
     /**
481 444
      * Navigates this {@code AbstractApp} to (i.e. opens) a specific URL.
482 445
      *

+ 0
- 97
react/features/app/components/App.web.js Voir le fichier

@@ -2,7 +2,6 @@ import { AtlasKitThemeProvider } from '@atlaskit/theme';
2 2
 import React from 'react';
3 3
 
4 4
 import '../../base/responsive-ui';
5
-import { getLocationContextRoot } from '../../base/util';
6 5
 import '../../chat';
7 6
 import '../../room-lock';
8 7
 import '../../video-layout';
@@ -22,27 +21,6 @@ export class App extends AbstractApp {
22 21
      */
23 22
     static propTypes = AbstractApp.propTypes;
24 23
 
25
-    /**
26
-     * Initializes a new App instance.
27
-     *
28
-     * @param {Object} props - The read-only React Component props with which
29
-     * the new instance is to be initialized.
30
-     */
31
-    constructor(props) {
32
-        super(props);
33
-
34
-        this.state = {
35
-            ...this.state,
36
-
37
-            /**
38
-             * The context root of window.location i.e. this Web App.
39
-             *
40
-             * @type {string}
41
-             */
42
-            windowLocationContextRoot: this._getWindowLocationContextRoot()
43
-        };
44
-    }
45
-
46 24
     /**
47 25
      * Overrides the parent method to inject {@link AtlasKitThemeProvider} as
48 26
      * the top most component.
@@ -66,79 +44,4 @@ export class App extends AbstractApp {
66 44
     getWindowLocation() {
67 45
         return window.location;
68 46
     }
69
-
70
-    /**
71
-     * Gets the context root of this Web App from window.location.
72
-     *
73
-     * @private
74
-     * @returns {string} The context root of window.location i.e. this Web App.
75
-     */
76
-    _getWindowLocationContextRoot() {
77
-        return getLocationContextRoot(this.getWindowLocation());
78
-    }
79
-
80
-    /**
81
-     * Navigates to a specific Route (via platform-specific means).
82
-     *
83
-     * @param {Route} route - The Route to which to navigate.
84
-     * @protected
85
-     * @returns {void}
86
-     */
87
-    _navigate(route) {
88
-        let path;
89
-
90
-        if (route) {
91
-            path = route.path;
92
-
93
-            const store = this._getStore();
94
-
95
-            // The syntax :room bellow is defined by react-router. It "matches a
96
-            // URL segment up to the next /, ?, or #. The matched string is
97
-            // called a param."
98
-            path
99
-                = path.replace(
100
-                    /:room/g,
101
-                    store.getState()['features/base/conference'].room);
102
-            path = this._routePath2WindowLocationPathname(path);
103
-        }
104
-
105
-        // Navigate to the specified Route.
106
-        const windowLocation = this.getWindowLocation();
107
-        let promise;
108
-
109
-        if (!route || windowLocation.pathname === path) {
110
-            // The browser is at the specified path already and what remains is
111
-            // to make this App instance aware of the route to be rendered at
112
-            // the current location.
113
-
114
-            // XXX Refer to the super's _navigate for an explanation why a
115
-            // Promise is returned.
116
-            promise = super._navigate(route);
117
-        } else {
118
-            // The browser must go to the specified location. Once the specified
119
-            // location becomes current, the App will be made aware of the route
120
-            // to be rendered at it.
121
-            windowLocation.pathname = path;
122
-        }
123
-
124
-        return promise || Promise.resolve();
125
-    }
126
-
127
-    /**
128
-     * Converts a specific Route path to a window.location.pathname.
129
-     *
130
-     * @param {string} path - A Route path to be converted to/represeted as a
131
-     * window.location.pathname.
132
-     * @private
133
-     * @returns {string} A window.location.pathname-compatible representation of
134
-     * the specified Route path.
135
-     */
136
-    _routePath2WindowLocationPathname(path) {
137
-        let pathname = this.state.windowLocationContextRoot;
138
-
139
-        pathname.endsWith('/') || (pathname += '/');
140
-        pathname += path.startsWith('/') ? path.substring(1) : path;
141
-
142
-        return pathname;
143
-    }
144 47
 }

+ 1
- 1
react/features/app/functions.native.js Voir le fichier

@@ -3,7 +3,7 @@
3 3
 import { NativeModules } from 'react-native';
4 4
 
5 5
 export * from './functions.any';
6
-export * from './getRouteToRender';
6
+export * from './router';
7 7
 
8 8
 /**
9 9
  * Returns application name.

+ 2
- 82
react/features/app/functions.web.js Voir le fichier

@@ -1,89 +1,9 @@
1 1
 // @flow
2 2
 
3
-import { toState } from '../base/redux';
4
-import { getDeepLinkingPage } from '../deep-linking';
5
-import { UnsupportedDesktopBrowser } from '../unsupported-browser';
6
-
7
-import {
8
-    // eslint-disable-next-line camelcase
9
-    _getRouteToRender as _super_getRouteToRender
10
-} from './getRouteToRender';
11
-
12
-declare var APP: Object;
13
-declare var interfaceConfig: Object;
14
-declare var loggingConfig: Object;
15
-
16
-/**
17
- * Array of rules defining whether we should {@link _interceptComponent} to
18
- * render.
19
- *
20
- * @private
21
- * @param {Object} state - Object containing current redux state.
22
- * @returns {Promise<ReactElement>|void}
23
- * @type {Function[]}
24
- */
25
-const _INTERCEPT_COMPONENT_RULES = [
26
-    getDeepLinkingPage,
27
-    state => {
28
-        const { webRTCReady } = state['features/base/lib-jitsi-meet'];
29
-
30
-        if (webRTCReady === false) {
31
-            return Promise.resolve(UnsupportedDesktopBrowser);
32
-        }
33
-
34
-        return Promise.resolve();
35
-    }
36
-];
37
-
38 3
 export * from './functions.any';
4
+export * from './router';
39 5
 
40
-/**
41
- * Determines which route is to be rendered in order to depict a specific redux
42
- * store.
43
- *
44
- * @param {(Object|Function)} stateOrGetState - The redux state or
45
- * {@link getState} function.
46
- * @returns {Promise<Route>}
47
- */
48
-export function _getRouteToRender(stateOrGetState: Object | Function): Object {
49
-    const route = _super_getRouteToRender(stateOrGetState);
50
-
51
-    // Intercepts route components if any of component interceptor rules is
52
-    // satisfied.
53
-    return _interceptComponent(stateOrGetState, route.component).then(
54
-        (component: React$Element<*>) => {
55
-            route.component = component;
56
-
57
-            return route;
58
-        }, () => Promise.resolve(route));
59
-}
60
-
61
-/**
62
- * Intercepts route components based on a {@link _INTERCEPT_COMPONENT_RULES}.
63
- *
64
- * @param {Object|Function} stateOrGetState - The redux state or
65
- * {@link getState} function.
66
- * @param {ReactElement} component - Current route component to render.
67
- * @private
68
- * @returns {Promise<ReactElement>} If any of the pre-defined rules is
69
- * satisfied, returns intercepted component.
70
- */
71
-function _interceptComponent(
72
-        stateOrGetState: Object | Function,
73
-        component: React$Element<*>) {
74
-    const state = toState(stateOrGetState);
75
-
76
-    const promises = [];
77
-
78
-    _INTERCEPT_COMPONENT_RULES.forEach(rule => {
79
-        promises.push(rule(state));
80
-    });
81
-
82
-    return Promise.all(promises).then(
83
-        results =>
84
-            results.find(result => typeof result !== 'undefined') || component,
85
-        () => Promise.resolve(component));
86
-}
6
+declare var interfaceConfig: Object;
87 7
 
88 8
 /**
89 9
  * Returns application name.

+ 0
- 22
react/features/app/getRouteToRender.js Voir le fichier

@@ -1,22 +0,0 @@
1
-/* @flow */
2
-
3
-import { isRoomValid } from '../base/conference';
4
-import { RouteRegistry } from '../base/react';
5
-import { toState } from '../base/redux';
6
-import { Conference } from '../conference';
7
-import { WelcomePage } from '../welcome';
8
-
9
-/**
10
- * Determines which route is to be rendered in order to depict a specific Redux
11
- * store.
12
- *
13
- * @param {(Object|Function)} stateOrGetState - Redux state or Regux getState()
14
- * method.
15
- * @returns {Route}
16
- */
17
-export function _getRouteToRender(stateOrGetState: Object | Function) {
18
-    const { room } = toState(stateOrGetState)['features/base/conference'];
19
-    const component = isRoomValid(room) ? Conference : WelcomePage;
20
-
21
-    return RouteRegistry.getRouteByComponent(component);
22
-}

+ 3
- 39
react/features/app/middleware.js Voir le fichier

@@ -3,21 +3,17 @@
3 3
 import { SET_ROOM } from '../base/conference';
4 4
 import {
5 5
     CONNECTION_ESTABLISHED,
6
-    getURLWithoutParams,
7
-    SET_LOCATION_URL
6
+    getURLWithoutParams
8 7
 } from '../base/connection';
9 8
 import { MiddlewareRegistry } from '../base/redux';
10 9
 
11
-import { _getRouteToRender } from './functions';
10
+import { getRouteToRender } from './router';
12 11
 
13 12
 MiddlewareRegistry.register(store => next => action => {
14 13
     switch (action.type) {
15 14
     case CONNECTION_ESTABLISHED:
16 15
         return _connectionEstablished(store, next, action);
17 16
 
18
-    case SET_LOCATION_URL:
19
-        return _setLocationURL(store, next, action);
20
-
21 17
     case SET_ROOM:
22 18
         return _setRoom(store, next, action);
23 19
     }
@@ -77,40 +73,8 @@ function _connectionEstablished(store, next, action) {
77 73
 function _navigate({ getState }) {
78 74
     const state = getState();
79 75
     const { app } = state['features/app'];
80
-    const routeToRender = _getRouteToRender(state);
81
-
82
-    // XXX Web changed _getRouteToRender to return Promsie instead of Route.
83
-    // Unfortunately, the commit left mobile to return Route.
84
-    let routeToRenderPromise;
85
-
86
-    if (routeToRender && typeof routeToRender.then === 'function') {
87
-        routeToRenderPromise = routeToRender;
88
-    }
89
-    if (!routeToRenderPromise) {
90
-        routeToRenderPromise = Promise.resolve(routeToRender);
91
-    }
92 76
 
93
-    routeToRenderPromise.then(app._navigate.bind(app));
94
-}
95
-
96
-/**
97
- * Notifies the feature app that the action {@link SET_LOCATION_URL} is being
98
- * dispatched within a specific redux {@code store}.
99
- *
100
- * @param {Store} store - The redux store in which the specified {@code action}
101
- * is being dispatched.
102
- * @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
103
- * specified {@code action} to the specified {@code store}.
104
- * @param {Action} action - The redux action, {@code SET_LOCATION_URL}, which is
105
- * being dispatched in the specified {@code store}.
106
- * @private
107
- * @returns {Object} The new state that is the result of the reduction of the
108
- * specified {@code action}.
109
- */
110
-function _setLocationURL({ getState }, next, action) {
111
-    return (
112
-        getState()['features/app'].app._navigate(undefined)
113
-            .then(() => next(action)));
77
+    getRouteToRender(state).then(route => app._navigate(route));
114 78
 }
115 79
 
116 80
 /**

+ 109
- 0
react/features/app/router.js Voir le fichier

@@ -0,0 +1,109 @@
1
+// @flow
2
+import type { Component } from 'react';
3
+
4
+import { isRoomValid } from '../base/conference';
5
+import JitsiMeetJS from '../base/lib-jitsi-meet';
6
+import { Platform } from '../base/react';
7
+import { toState } from '../base/redux';
8
+import { Conference } from '../conference';
9
+import { getDeepLinkingPage } from '../deep-linking';
10
+import { UnsupportedDesktopBrowser } from '../unsupported-browser';
11
+import {
12
+    BlankPage,
13
+    WelcomePage,
14
+    generateRoomWithoutSeparator,
15
+    isWelcomePageAppEnabled,
16
+    isWelcomePageUserEnabled
17
+} from '../welcome';
18
+
19
+declare var interfaceConfig: Object;
20
+
21
+/**
22
+ * Object describing application route.
23
+ *
24
+ * @typedef {Object} Route
25
+ * @property {Component} component - React Component constructor.
26
+ * @property {string|undefined} href - New location, in case navigation involves
27
+ * a location change.
28
+ */
29
+export type Route = {
30
+    component: Class<Component<*>>,
31
+    href: ?string
32
+};
33
+
34
+/**
35
+ * Determines which route is to be rendered in order to depict a specific Redux
36
+ * store.
37
+ *
38
+ * @param {(Object|Function)} stateful - Redux state or Regux getState()
39
+ * method.
40
+ * @returns {Promise<Route>}
41
+ */
42
+export function getRouteToRender(stateful: Object | Function): Promise<Route> {
43
+    const state = toState(stateful);
44
+    const { room } = state['features/base/conference'];
45
+    const isMobileApp = navigator.product === 'ReactNative';
46
+    const isMobileBrowser
47
+        = !isMobileApp && (Platform.OS === 'android' || Platform.OS === 'ios');
48
+    const route: Route = {
49
+        component: BlankPage,
50
+        href: undefined
51
+    };
52
+
53
+    return new Promise(resolve => {
54
+        // First check if the current endpoint supports WebRTC. We are
55
+        // intentionally not performing the check for mobile browsers because:
56
+        //   - the welcome page is mobile ready
57
+        //   - if the URL points to a conference, getDeepLinkingPage will take
58
+        //     care of it
59
+        if (!isMobileBrowser && !JitsiMeetJS.isWebRtcSupported()) {
60
+            route.component = UnsupportedDesktopBrowser;
61
+            resolve(route);
62
+
63
+            return;
64
+        }
65
+
66
+        if (isRoomValid(room)) {
67
+            if (isMobileApp) {
68
+                route.component = Conference;
69
+                resolve(route);
70
+            } else {
71
+                // Update the location if it doesn't match. This happens when a
72
+                // room is joined from the welcome page. The reason for doing
73
+                // this instead of using the history API is that we want to
74
+                // load the config.js which takes the room into account.
75
+                const { locationURL } = state['features/base/connection'];
76
+
77
+                // eslint-disable-next-line no-negated-condition
78
+                if (window.location.href !== locationURL.href) {
79
+                    route.href = locationURL.href;
80
+                    resolve(route);
81
+                } else {
82
+                    // Maybe show deep-linking, otherwise go to Conference.
83
+                    getDeepLinkingPage(state).then(component => {
84
+                        route.component = component || Conference;
85
+                        resolve(route);
86
+                    });
87
+                }
88
+            }
89
+
90
+            return;
91
+        }
92
+
93
+        if (!isWelcomePageUserEnabled(state)) {
94
+            // Web: if the welcome page is disabled, go directly to a
95
+            // random room.
96
+
97
+            let href = window.location.href;
98
+
99
+            href.endsWith('/') || (href += '/');
100
+            route.href = href + generateRoomWithoutSeparator();
101
+        } else if (isWelcomePageAppEnabled(state)) {
102
+            // Mobile: only go to the welcome page if enabled.
103
+
104
+            route.component = WelcomePage;
105
+        }
106
+
107
+        resolve(route);
108
+    });
109
+}

+ 4
- 1
react/features/base/conference/reducer.js Voir le fichier

@@ -1,6 +1,6 @@
1 1
 // @flow
2 2
 
3
-import { CONNECTION_WILL_CONNECT } from '../connection';
3
+import { CONNECTION_WILL_CONNECT, SET_LOCATION_URL } from '../connection';
4 4
 import { JitsiConferenceErrors } from '../lib-jitsi-meet';
5 5
 import { assign, ReducerRegistry, set } from '../redux';
6 6
 import { LOCKED_LOCALLY, LOCKED_REMOTELY } from '../../room-lock';
@@ -66,6 +66,9 @@ ReducerRegistry.register('features/base/conference', (state = {}, action) => {
66 66
     case SET_FOLLOW_ME:
67 67
         return set(state, 'followMeEnabled', action.enabled);
68 68
 
69
+    case SET_LOCATION_URL:
70
+        return set(state, 'room', undefined);
71
+
69 72
     case SET_PASSWORD:
70 73
         return _setPassword(state, action);
71 74
 

+ 2
- 14
react/features/base/connection/actions.web.js Voir le fichier

@@ -2,8 +2,6 @@
2 2
 
3 3
 import type { Dispatch } from 'redux';
4 4
 
5
-import { libInitError, WEBRTC_NOT_SUPPORTED } from '../lib-jitsi-meet';
6
-
7 5
 declare var APP: Object;
8 6
 declare var config: Object;
9 7
 
@@ -26,24 +24,14 @@ export function connect() {
26 24
 
27 25
         // XXX Lib-jitsi-meet does not accept uppercase letters.
28 26
         const room = state['features/base/conference'].room.toLowerCase();
29
-        const { initPromise } = state['features/base/lib-jitsi-meet'];
30 27
 
31 28
         // XXX For web based version we use conference initialization logic
32 29
         // from the old app (at the moment of writing).
33
-        return initPromise.then(() => APP.conference.init({
30
+        return APP.conference.init({
34 31
             roomName: room
35
-        })).catch(error => {
32
+        }).catch(error => {
36 33
             APP.API.notifyConferenceLeft(APP.conference.roomName);
37 34
             logger.error(error);
38
-
39
-            // TODO The following are in fact Errors raised by
40
-            // JitsiMeetJS.init() which should be taken care of in
41
-            // features/base/lib-jitsi-meet but we are not there yet on the
42
-            // Web at the time of this writing.
43
-            switch (error.name) {
44
-            case WEBRTC_NOT_SUPPORTED:
45
-                dispatch(libInitError(error));
46
-            }
47 35
         });
48 36
     };
49 37
 }

+ 0
- 20
react/features/base/lib-jitsi-meet/actionTypes.js Voir le fichier

@@ -27,16 +27,6 @@ export const LIB_DID_INIT = Symbol('LIB_DID_INIT');
27 27
  */
28 28
 export const LIB_INIT_ERROR = Symbol('LIB_INIT_ERROR');
29 29
 
30
-/**
31
- * Action to dispatch the promise returned by JitsiMeetJS.init.
32
- *
33
- * {
34
- *     type: LIB_INIT_PROMISE_CREATED,
35
- *     initPromise: Promise
36
- * }
37
- */
38
-export const LIB_INIT_PROMISE_CREATED = Symbol('LIB_INIT_PROMISE_CREATED');
39
-
40 30
 /**
41 31
  * The type of Redux action which signals that {@link JitsiMeetJS} will be
42 32
  * disposed.
@@ -56,13 +46,3 @@ export const LIB_WILL_DISPOSE = Symbol('LIB_WILL_DISPOSE');
56 46
  * }
57 47
  */
58 48
 export const LIB_WILL_INIT = Symbol('LIB_WILL_INIT');
59
-
60
-/**
61
- * The type of Redux action which indicates whether WebRTC is ready.
62
- *
63
- * {
64
- *     type: SET_WEBRTC_READY,
65
- *     webRTCReady: boolean | Promise
66
- * }
67
- */
68
-export const SET_WEBRTC_READY = Symbol('SET_WEBRTC_READY');

+ 11
- 47
react/features/base/lib-jitsi-meet/actions.js Voir le fichier

@@ -7,10 +7,8 @@ import {
7 7
     LIB_DID_DISPOSE,
8 8
     LIB_DID_INIT,
9 9
     LIB_INIT_ERROR,
10
-    LIB_INIT_PROMISE_CREATED,
11 10
     LIB_WILL_DISPOSE,
12
-    LIB_WILL_INIT,
13
-    SET_WEBRTC_READY
11
+    LIB_WILL_INIT
14 12
 } from './actionTypes';
15 13
 import { isAnalyticsEnabled } from './functions';
16 14
 
@@ -38,7 +36,7 @@ export function disposeLib() {
38 36
  * @returns {Function}
39 37
  */
40 38
 export function initLib() {
41
-    return (dispatch: Dispatch<*>, getState: Function): Promise<void> => {
39
+    return (dispatch: Dispatch<*>, getState: Function): void => {
42 40
         const config = getState()['features/base/config'];
43 41
 
44 42
         if (!config) {
@@ -47,30 +45,15 @@ export function initLib() {
47 45
 
48 46
         dispatch({ type: LIB_WILL_INIT });
49 47
 
50
-        const initPromise = JitsiMeetJS.init({
51
-            enableAnalyticsLogging: isAnalyticsEnabled(getState),
52
-            ...config
53
-        });
54
-
55
-        dispatch({
56
-            type: LIB_INIT_PROMISE_CREATED,
57
-            initPromise
58
-        });
59
-
60
-        return (
61
-            initPromise
62
-                .then(() => dispatch({ type: LIB_DID_INIT }))
63
-                .catch(error => {
64
-                    // TODO: See the comment in the connect action in
65
-                    // base/connection/actions.web.js.
66
-                    if (typeof APP === 'undefined') {
67
-                        dispatch(libInitError(error));
68
-                    }
69
-
70
-                    // TODO Handle LIB_INIT_ERROR error somewhere instead.
71
-                    console.error('lib-jitsi-meet failed to init:', error);
72
-                    throw error;
73
-                }));
48
+        try {
49
+            JitsiMeetJS.init({
50
+                enableAnalyticsLogging: isAnalyticsEnabled(getState),
51
+                ...config
52
+            });
53
+            dispatch({ type: LIB_DID_INIT });
54
+        } catch (error) {
55
+            dispatch(libInitError(error));
56
+        }
74 57
     };
75 58
 }
76 59
 
@@ -89,22 +72,3 @@ export function libInitError(error: Error) {
89 72
         error
90 73
     };
91 74
 }
92
-
93
-/**
94
- * Sets the indicator which determines whether WebRTC is ready.
95
- *
96
- * @param {boolean} webRTCReady - The indicator which determines
97
- * whether WebRTC is ready.
98
- * @returns {Function}
99
- */
100
-export function setWebRTCReady(webRTCReady: boolean) {
101
-    return (dispatch: Function, getState: Function) => {
102
-        if (getState()['features/base/lib-jitsi-meet'].webRTCReady
103
-                !== webRTCReady) {
104
-            dispatch({
105
-                type: SET_WEBRTC_READY,
106
-                webRTCReady
107
-            });
108
-        }
109
-    };
110
-}

+ 0
- 5
react/features/base/lib-jitsi-meet/constants.js Voir le fichier

@@ -1,5 +0,0 @@
1
-/**
2
- * The name of the Error thrown by {@link JitsiMeetJS.init()} which indicates
3
- * that WebRTC is not supported by the execution environment.
4
- */
5
-export const WEBRTC_NOT_SUPPORTED = 'WEBRTC_NOT_SUPPORTED';

+ 0
- 1
react/features/base/lib-jitsi-meet/index.js Voir le fichier

@@ -24,7 +24,6 @@ export const JitsiTrackEvents = JitsiMeetJS.events.track;
24 24
 
25 25
 export * from './actions';
26 26
 export * from './actionTypes';
27
-export * from './constants';
28 27
 export * from './functions';
29 28
 
30 29
 import './middleware';

+ 2
- 41
react/features/base/lib-jitsi-meet/middleware.js Voir le fichier

@@ -6,9 +6,8 @@ import { PARTICIPANT_LEFT } from '../participants';
6 6
 import { MiddlewareRegistry } from '../redux';
7 7
 
8 8
 import JitsiMeetJS from './_';
9
-import { disposeLib, initLib, setWebRTCReady } from './actions';
10
-import { LIB_DID_INIT, LIB_INIT_ERROR, LIB_WILL_INIT } from './actionTypes';
11
-import { WEBRTC_NOT_SUPPORTED } from './constants';
9
+import { disposeLib, initLib } from './actions';
10
+import { LIB_WILL_INIT } from './actionTypes';
12 11
 
13 12
 declare var APP: Object;
14 13
 
@@ -31,18 +30,6 @@ MiddlewareRegistry.register(store => next => action => {
31 30
             _setErrorHandlers();
32 31
         }
33 32
         break;
34
-    case LIB_DID_INIT:
35
-        // FIXME: The web version doesn't need this action during initialization
36
-        // because it is still using the old logic from conference.js. We still
37
-        // have to reactify the old logic from conference.js and then maybe
38
-        // we'll need this action for web too.
39
-        if (typeof APP === 'undefined') {
40
-            store.dispatch(setWebRTCReady(true));
41
-        }
42
-        break;
43
-
44
-    case LIB_INIT_ERROR:
45
-        return _libInitError(store, next, action);
46 33
 
47 34
     case PARTICIPANT_LEFT:
48 35
         action.participant.local && store.dispatch(disposeLib());
@@ -55,32 +42,6 @@ MiddlewareRegistry.register(store => next => action => {
55 42
     return next(action);
56 43
 });
57 44
 
58
-/**
59
- * Notifies the feature base/lib-jitsi-meet that the action LIB_INIT_ERROR is
60
- * being dispatched within a specific Redux store.
61
- *
62
- * @param {Store} store - The Redux store in which the specified action is being
63
- * dispatched.
64
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
65
- * specified action to the specified store.
66
- * @param {Action} action - The Redux action LIB_INIT_ERROR which is being
67
- * dispatched in the specified store.
68
- * @private
69
- * @returns {Object} The new state that is the result of the reduction of the
70
- * specified action.
71
- */
72
-function _libInitError(store, next, action) {
73
-    const nextState = next(action);
74
-
75
-    const { error } = action;
76
-
77
-    if (error && error.name === WEBRTC_NOT_SUPPORTED) {
78
-        store.dispatch(setWebRTCReady(false));
79
-    }
80
-
81
-    return nextState;
82
-}
83
-
84 45
 /**
85 46
  * Notifies the feature base/lib-jitsi-meet that the action SET_CONFIG is being
86 47
  * dispatched within a specific Redux store.

+ 2
- 17
react/features/base/lib-jitsi-meet/reducer.js Voir le fichier

@@ -5,9 +5,7 @@ import { ReducerRegistry } from '../redux';
5 5
 import {
6 6
     LIB_DID_DISPOSE,
7 7
     LIB_DID_INIT,
8
-    LIB_INIT_ERROR,
9
-    LIB_INIT_PROMISE_CREATED,
10
-    SET_WEBRTC_READY
8
+    LIB_INIT_ERROR
11 9
 } from './actionTypes';
12 10
 
13 11
 /**
@@ -35,20 +33,7 @@ ReducerRegistry.register(
35 33
             return {
36 34
                 ...state,
37 35
                 initError: action.error,
38
-                initialized: false,
39
-                initPromise: undefined
40
-            };
41
-
42
-        case LIB_INIT_PROMISE_CREATED:
43
-            return {
44
-                ...state,
45
-                initPromise: action.initPromise
46
-            };
47
-
48
-        case SET_WEBRTC_READY:
49
-            return {
50
-                ...state,
51
-                webRTCReady: action.webRTCReady
36
+                initialized: false
52 37
             };
53 38
 
54 39
         default:

+ 0
- 119
react/features/base/react/RouteRegistry.js Voir le fichier

@@ -1,119 +0,0 @@
1
-/* @flow */
2
-
3
-import { Component } from 'react';
4
-
5
-/**
6
- * Object describing application route.
7
- *
8
- * @typedef {Object} Route
9
- * @property {Component} component - React Component constructor.
10
- * @property {string} path - URL route, required for web routing.
11
- */
12
-type Route = {
13
-    component: Class<Component<*>>, // eslint-disable-line no-undef
14
-    path: string
15
-};
16
-
17
-/**
18
- * A registry for Navigator routes, allowing features to register themselves
19
- * without needing to create additional inter-feature dependencies.
20
- */
21
-class RouteRegistry {
22
-    _elements: Array<Route>;
23
-
24
-    /**
25
-     * Initializes a new RouteRegistry instance.
26
-     */
27
-    constructor() {
28
-        /**
29
-         * The set of registered routes.
30
-         *
31
-         * @private
32
-         * @type {Route[]}
33
-         */
34
-        this._elements = [];
35
-    }
36
-
37
-    /**
38
-     * Determines whether two specific Routes are equal i.e. they describe one
39
-     * and the same abstract route.
40
-     *
41
-     * @param {Object} a - The Route to compare to b.
42
-     * @param {Object} b - The Route to compare to a.
43
-     * @returns {boolean} True if the specified a and b describe one and the
44
-     * same abstract route; otherwise, false.
45
-     */
46
-    areRoutesEqual(a: Route, b: Route) {
47
-        if (a === b) { // reflexive
48
-            return true;
49
-        }
50
-        if (!a) {
51
-            return !b;
52
-        }
53
-        if (!b) {
54
-            return !a;
55
-        }
56
-
57
-        const aKeys = Object.keys(a);
58
-        const bKeys = Object.keys(b);
59
-
60
-        return (
61
-            aKeys.length === bKeys.length /* symmetric */
62
-                && aKeys.every(key => a[key] === b[key]));
63
-    }
64
-
65
-    /**
66
-     * Returns all registered routes.
67
-     *
68
-     * @returns {Route[]}
69
-     */
70
-    getRoutes() {
71
-        // We use the destructuring operator to 'clone' the route object to
72
-        // prevent modifications from outside (e.g. React Native's Navigator
73
-        // extends it with additional properties).
74
-        return this._elements.map(r => {
75
-            return { ...r };
76
-        });
77
-    }
78
-
79
-    /* eslint-disable no-undef */
80
-
81
-    /**
82
-     * Returns registered route by name if any.
83
-     *
84
-     * @param {Component} component - The React Component (class) of the route
85
-     * to retrieve.
86
-     * @returns {Route|null}
87
-     */
88
-    getRouteByComponent(component: Class<Component<*>>) {
89
-
90
-        /* eslint-enable no-undef */
91
-
92
-        const route = this._elements.find(r => r.component === component);
93
-
94
-        // We use destructuring operator to 'clone' route object to prevent
95
-        // modifications from outside (e.g. React Native's Navigator extends
96
-        // it with some additional properties).
97
-        return route ? { ...route } : null;
98
-    }
99
-
100
-    /**
101
-     * Adds a route to this registry.
102
-     *
103
-     * @param {Route} route - Route definition object.
104
-     * @returns {void}
105
-     */
106
-    register(route: Route) {
107
-        if (this._elements.includes(route)) {
108
-            throw new Error(
109
-                `Route ${String(route.component)} is registered already!`);
110
-        }
111
-
112
-        this._elements.push(route);
113
-    }
114
-}
115
-
116
-/**
117
- * The public singleton instance of the RouteRegistry class.
118
- */
119
-export default new RouteRegistry();

+ 0
- 1
react/features/base/react/index.js Voir le fichier

@@ -1,3 +1,2 @@
1 1
 export * from './components';
2 2
 export { default as Platform } from './Platform';
3
-export { default as RouteRegistry } from './RouteRegistry';

+ 57
- 4
react/features/conference/components/Conference.native.js Voir le fichier

@@ -37,6 +37,13 @@ type Props = {
37 37
      */
38 38
     _connecting: boolean,
39 39
 
40
+    /**
41
+     * Current conference's full URL.
42
+     *
43
+     * @private
44
+     */
45
+    _locationURL: URL,
46
+
40 47
     /**
41 48
      * The handler which dispatches the (redux) action connect.
42 49
      *
@@ -77,6 +84,13 @@ type Props = {
77 84
      */
78 85
     _reducedUI: boolean,
79 86
 
87
+    /**
88
+     * The current conference room name.
89
+     *
90
+     * @private
91
+     */
92
+    _room: string,
93
+
80 94
     /**
81 95
      * The handler which dispatches the (redux) action setToolboxVisible to
82 96
      * show/hide the Toolbox.
@@ -163,15 +177,30 @@ class Conference extends Component<Props> {
163 177
      * participant count.
164 178
      *
165 179
      * @inheritdoc
166
-     * @param {Object} nextProps - The read-only React {@code Component} props
180
+     * @param {Props} nextProps - The read-only React {@code Component} props
167 181
      * that this instance will receive.
168 182
      * @returns {void}
169 183
      */
170
-    componentWillReceiveProps({ _participantCount: newParticipantCount }) {
184
+    componentWillReceiveProps(nextProps: Props) {
171 185
         const {
186
+            _locationURL: oldLocationURL,
187
+            _onConnect,
188
+            _onDisconnect,
172 189
             _participantCount: oldParticipantCount,
190
+            _room: oldRoom,
173 191
             _setToolboxVisible
174 192
         } = this.props;
193
+        const {
194
+            _locationURL: newLocationURL,
195
+            _participantCount: newParticipantCount,
196
+            _room: newRoom
197
+        } = nextProps;
198
+
199
+        // If the location URL changes we need to reconnect.
200
+        oldLocationURL !== newLocationURL && _onDisconnect();
201
+
202
+        // Start the connection process when there is a (valid) room.
203
+        oldRoom !== newRoom && newRoom && _onConnect();
175 204
 
176 205
         if (oldParticipantCount === 1) {
177 206
             newParticipantCount > 1 && _setToolboxVisible(false);
@@ -389,15 +418,23 @@ function _mapDispatchToProps(dispatch) {
389 418
  * @private
390 419
  * @returns {{
391 420
  *     _connecting: boolean,
421
+ *     _locationURL: URL,
392 422
  *     _participantCount: number,
393 423
  *     _reducedUI: boolean,
424
+ *     _room: string,
394 425
  *     _toolboxVisible: boolean,
395 426
  *     _toolboxAlwaysVisible: boolean
396 427
  * }}
397 428
  */
398 429
 function _mapStateToProps(state) {
399
-    const { connecting, connection } = state['features/base/connection'];
400
-    const { conference, joining, leaving } = state['features/base/conference'];
430
+    const { connecting, connection, locationURL }
431
+        = state['features/base/connection'];
432
+    const {
433
+        conference,
434
+        joining,
435
+        leaving,
436
+        room
437
+    } = state['features/base/conference'];
401 438
     const { reducedUI } = state['features/base/responsive-ui'];
402 439
     const { alwaysVisible, visible } = state['features/toolbox'];
403 440
 
@@ -425,6 +462,14 @@ function _mapStateToProps(state) {
425 462
          */
426 463
         _connecting: Boolean(connecting_),
427 464
 
465
+        /**
466
+         * Current conference's full URL.
467
+         *
468
+         * @private
469
+         * @type {URL}
470
+         */
471
+        _locationURL: locationURL,
472
+
428 473
         /**
429 474
          * The number of participants in the conference.
430 475
          *
@@ -442,6 +487,14 @@ function _mapStateToProps(state) {
442 487
          */
443 488
         _reducedUI: reducedUI,
444 489
 
490
+        /**
491
+         * The current conference room name.
492
+         *
493
+         * @private
494
+         * @type {string}
495
+         */
496
+        _room: room,
497
+
445 498
         /**
446 499
          * The indicator which determines whether the Toolbox is visible.
447 500
          *

+ 85
- 20
react/features/conference/components/Conference.web.js Voir le fichier

@@ -4,6 +4,7 @@ import _ from 'lodash';
4 4
 import React, { Component } from 'react';
5 5
 import { connect as reactReduxConnect } from 'react-redux';
6 6
 
7
+import { obtainConfig } from '../../base/config';
7 8
 import { connect, disconnect } from '../../base/connection';
8 9
 import { DialogContainer } from '../../base/dialog';
9 10
 import { translate } from '../../base/i18n';
@@ -22,8 +23,29 @@ import {
22 23
 import { maybeShowSuboptimalExperienceNotification } from '../functions';
23 24
 
24 25
 declare var APP: Object;
26
+declare var config: Object;
25 27
 declare var interfaceConfig: Object;
26 28
 
29
+const logger = require('jitsi-meet-logger').getLogger(__filename);
30
+
31
+/**
32
+ * Promise wrapper on obtain config method. When HttpConfigFetch will be moved
33
+ * to React app it's better to use load config instead.
34
+ *
35
+ * @param {string} location - URL of the domain from which the config is to be
36
+ * obtained.
37
+ * @param {string} room - Room name.
38
+ * @private
39
+ * @returns {Promise}
40
+ */
41
+function _obtainConfig(location: string, room: string) {
42
+    return new Promise((resolve, reject) =>
43
+        obtainConfig(location, room, (success, error) => {
44
+            success ? resolve() : reject(error);
45
+        })
46
+    );
47
+}
48
+
27 49
 /**
28 50
  * DOM events for when full screen mode has changed. Different browsers need
29 51
  * different vendor prefixes.
@@ -47,6 +69,11 @@ type Props = {
47 69
      */
48 70
     _iAmRecorder: boolean,
49 71
 
72
+    /**
73
+     * Conference room name.
74
+     */
75
+    _room: string,
76
+
50 77
     dispatch: Function,
51 78
     t: Function
52 79
 }
@@ -84,29 +111,35 @@ class Conference extends Component<Props> {
84 111
     }
85 112
 
86 113
     /**
87
-     * Until we don't rewrite UI using react components
88
-     * we use UI.start from old app. Also method translates
89
-     * component right after it has been mounted.
114
+     * Start the connection and get the UI ready for the conference.
90 115
      *
91 116
      * @inheritdoc
92 117
      */
93 118
     componentDidMount() {
94
-        APP.UI.start();
95
-
96
-        APP.UI.registerListeners();
97
-        APP.UI.bindEvents();
98
-
99
-        FULL_SCREEN_EVENTS.forEach(name =>
100
-            document.addEventListener(name, this._onFullScreenChange));
101
-
102
-        const { dispatch, t } = this.props;
103
-
104
-        dispatch(connect());
105
-
106
-        maybeShowSuboptimalExperienceNotification(dispatch, t);
107
-
108
-        interfaceConfig.filmStripOnly
109
-            && dispatch(setToolboxAlwaysVisible(true));
119
+        const { configLocation } = config;
120
+
121
+        if (configLocation) {
122
+            _obtainConfig(configLocation, this.props._room)
123
+                .then(() => {
124
+                    const now = window.performance.now();
125
+
126
+                    APP.connectionTimes['configuration.fetched'] = now;
127
+                    logger.log('(TIME) configuration fetched:\t', now);
128
+
129
+                    this._start();
130
+                })
131
+                .catch(err => {
132
+                    logger.log(err);
133
+
134
+                    // Show obtain config error.
135
+                    APP.UI.messageHandler.showError({
136
+                        titleKey: 'connection.CONNFAIL',
137
+                        descriptionKey: 'dialog.connectError'
138
+                    });
139
+                });
140
+        } else {
141
+            this._start();
142
+        }
110 143
     }
111 144
 
112 145
     /**
@@ -185,6 +218,32 @@ class Conference extends Component<Props> {
185 218
     _onShowToolbar() {
186 219
         this.props.dispatch(showToolbox());
187 220
     }
221
+
222
+    /**
223
+     * Until we don't rewrite UI using react components
224
+     * we use UI.start from old app. Also method translates
225
+     * component right after it has been mounted.
226
+     *
227
+     * @inheritdoc
228
+     */
229
+    _start() {
230
+        APP.UI.start();
231
+
232
+        APP.UI.registerListeners();
233
+        APP.UI.bindEvents();
234
+
235
+        FULL_SCREEN_EVENTS.forEach(name =>
236
+            document.addEventListener(name, this._onFullScreenChange));
237
+
238
+        const { dispatch, t } = this.props;
239
+
240
+        dispatch(connect());
241
+
242
+        maybeShowSuboptimalExperienceNotification(dispatch, t);
243
+
244
+        interfaceConfig.filmStripOnly
245
+            && dispatch(setToolboxAlwaysVisible(true));
246
+    }
188 247
 }
189 248
 
190 249
 /**
@@ -198,6 +257,7 @@ class Conference extends Component<Props> {
198 257
  * }}
199 258
  */
200 259
 function _mapStateToProps(state) {
260
+    const { room } = state['features/base/conference'];
201 261
     const { iAmRecorder } = state['features/base/config'];
202 262
 
203 263
     return {
@@ -206,7 +266,12 @@ function _mapStateToProps(state) {
206 266
          *
207 267
          * @private
208 268
          */
209
-        _iAmRecorder: iAmRecorder
269
+        _iAmRecorder: iAmRecorder,
270
+
271
+        /**
272
+         * Conference room name.
273
+         */
274
+        _room: room
210 275
     };
211 276
 }
212 277
 

+ 0
- 1
react/features/conference/index.js Voir le fichier

@@ -1,4 +1,3 @@
1 1
 export * from './components';
2 2
 
3 3
 import './middleware';
4
-import './route';

+ 0
- 105
react/features/conference/route.js Voir le fichier

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

+ 0
- 0
react/features/deep-linking/components/DeepLinkingDesktopPage.native.js Voir le fichier


react/features/deep-linking/components/DeepLinkingDesktopPage.js → react/features/deep-linking/components/DeepLinkingDesktopPage.web.js Voir le fichier


+ 0
- 0
react/features/deep-linking/components/DeepLinkingMobilePage.native.js Voir le fichier


react/features/deep-linking/components/DeepLinkingMobilePage.js → react/features/deep-linking/components/DeepLinkingMobilePage.web.js Voir le fichier


+ 0
- 0
react/features/deep-linking/components/NoMobileApp.native.js Voir le fichier


react/features/deep-linking/components/NoMobileApp.js → react/features/deep-linking/components/NoMobileApp.web.js Voir le fichier


+ 1
- 4
react/features/device-selection/actions.js Voir le fichier

@@ -94,10 +94,7 @@ function _processRequest(dispatch, getState, request, responseCallback) { // esl
94 94
 
95 95
         switch (request.name) {
96 96
         case 'isDeviceListAvailable':
97
-            JitsiMeetJS.mediaDevices.isDeviceListAvailable()
98
-                .then(isDeviceListAvailable =>
99
-                    responseCallback(isDeviceListAvailable))
100
-                .catch(e => responseCallback(null, e));
97
+            responseCallback(JitsiMeetJS.mediaDevices.isDeviceListAvailable());
101 98
             break;
102 99
         case 'isDeviceChangeAvailable':
103 100
             responseCallback(

+ 2
- 3
react/features/settings/popup.js Voir le fichier

@@ -8,9 +8,8 @@ import DeviceSelectionPopup from './DeviceSelectionPopup';
8 8
 let deviceSelectionPopup;
9 9
 
10 10
 window.init = i18next => {
11
-    JitsiMeetJS.init({}).then(() => {
12
-        deviceSelectionPopup = new DeviceSelectionPopup(i18next);
13
-    });
11
+    JitsiMeetJS.init();
12
+    deviceSelectionPopup = new DeviceSelectionPopup(i18next);
14 13
 };
15 14
 
16 15
 window.addEventListener('beforeunload', () => deviceSelectionPopup.close());

+ 0
- 2
react/features/unsupported-browser/index.js Voir le fichier

@@ -1,3 +1 @@
1 1
 export * from './components';
2
-
3
-import './middleware';

+ 0
- 54
react/features/unsupported-browser/middleware.js Voir le fichier

@@ -1,54 +0,0 @@
1
-// @flow
2
-
3
-import { appNavigate } from '../app';
4
-import { SET_WEBRTC_READY } from '../base/lib-jitsi-meet';
5
-import { MiddlewareRegistry } from '../base/redux';
6
-
7
-/**
8
- * Middleware that dispatches appNavigate when WebRTC readiness changes.
9
- *
10
- * @param {Store} store - The Redux store.
11
- * @returns {Function}
12
- * @private
13
- */
14
-MiddlewareRegistry.register(store => next => action => {
15
-    switch (action.type) {
16
-    case SET_WEBRTC_READY:
17
-        return _setWebRTCReady(store, next, action);
18
-    }
19
-
20
-    return next(action);
21
-});
22
-
23
-/**
24
- * Notifies the feature unsupported-browser that the action SET_WEBRTC_READY is
25
- * being dispatched within a specific Redux store.
26
- *
27
- * @param {Store} store - The Redux store in which the specified action is being
28
- * dispatched.
29
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
30
- * specified action to the specified store.
31
- * @param {Action} action - The Redux action SET_WEBRTC_READY which is being
32
- * dispatched in the specified store.
33
- * @private
34
- * @returns {Object} The new state that is the result of the reduction of the
35
- * specified action.
36
- */
37
-function _setWebRTCReady({ dispatch, getState }, next, action) {
38
-    const result = next(action);
39
-
40
-    // FIXME The feature unsupported-browser needs to notify the app that it may
41
-    // need to render a different Component at its current location because the
42
-    // execution enviroment has changed. The current location is not necessarily
43
-    // available through window.location (e.g. on mobile) but the following
44
-    // works at the time of this writing.
45
-    const windowLocation = getState()['features/app'].app.getWindowLocation();
46
-
47
-    if (windowLocation) {
48
-        const { href } = windowLocation;
49
-
50
-        href && dispatch(appNavigate(href));
51
-    }
52
-
53
-    return result;
54
-}

+ 12
- 40
react/features/welcome/components/BlankPage.native.js Voir le fichier

@@ -1,38 +1,27 @@
1
-/* @flow */
1
+// @flow
2 2
 
3
-import PropTypes from 'prop-types';
4 3
 import React, { Component } from 'react';
5 4
 import { connect } from 'react-redux';
5
+import type { Dispatch } from 'redux';
6 6
 
7 7
 import { destroyLocalTracks } from '../../base/tracks';
8 8
 import { NetworkActivityIndicator } from '../../mobile/network-activity';
9 9
 
10
-import { isWelcomePageAppEnabled } from '../functions';
11 10
 import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
12 11
 
12
+/**
13
+ * {@code BlankPage} React {@code Component}'s prop types.
14
+ */
15
+type Props = {
16
+    dispatch: Dispatch<*>
17
+}
18
+
13 19
 /**
14 20
  * The React {@code Component} displayed by {@code AbstractApp} when it has no
15 21
  * {@code Route} to render. Renders a progress indicator when there are ongoing
16 22
  * network requests.
17 23
  */
18
-class BlankPage extends Component<*> {
19
-    /**
20
-     * {@code BlankPage} React {@code Component}'s prop types.
21
-     *
22
-     * @static
23
-     */
24
-    static propTypes = {
25
-        /**
26
-         * The indicator which determines whether {@code WelcomePage} is (to
27
-         * be) rendered.
28
-         *
29
-         * @private
30
-         */
31
-        _welcomePageEnabled: PropTypes.bool,
32
-
33
-        dispatch: PropTypes.func
34
-    };
35
-
24
+class BlankPage extends Component<Props> {
36 25
     /**
37 26
      * Destroys the local tracks (if any) since no media is desired when this
38 27
      * component is rendered.
@@ -41,8 +30,7 @@ class BlankPage extends Component<*> {
41 30
      * @returns {void}
42 31
      */
43 32
     componentWillMount() {
44
-        this.props._welcomePageEnabled
45
-            || this.props.dispatch(destroyLocalTracks());
33
+        this.props.dispatch(destroyLocalTracks());
46 34
     }
47 35
 
48 36
     /**
@@ -60,20 +48,4 @@ class BlankPage extends Component<*> {
60 48
     }
61 49
 }
62 50
 
63
-/**
64
- * Maps (parts of) the redux state to the React {@code Component} props of
65
- * {@code BlankPage}.
66
- *
67
- * @param {Object} state - The redux state.
68
- * @private
69
- * @returns {{
70
- *     _welcomePageEnabled: boolean
71
- * }}
72
- */
73
-function _mapStateToProps(state) {
74
-    return {
75
-        _welcomePageEnabled: isWelcomePageAppEnabled(state)
76
-    };
77
-}
78
-
79
-export default connect(_mapStateToProps)(BlankPage);
51
+export default connect()(BlankPage);

+ 2
- 6
react/features/welcome/functions.js Voir le fichier

@@ -19,8 +19,6 @@ export * from './roomnameGenerator';
19 19
  * {@code true}; otherwise, {@code false}.
20 20
  */
21 21
 export function isWelcomePageAppEnabled(stateful: Function | Object) {
22
-    let b;
23
-
24 22
     if (navigator.product === 'ReactNative') {
25 23
         // We introduced the welcomePageEnabled prop on App in Jitsi Meet SDK
26 24
         // for Android and iOS. There isn't a strong reason not to introduce it
@@ -29,12 +27,10 @@ export function isWelcomePageAppEnabled(stateful: Function | Object) {
29 27
         // - Enabling/disabling the Welcome page on Web historically
30 28
         // automatically redirects to a random room and that does not make sense
31 29
         // on mobile (right now).
32
-        b = Boolean(getAppProp(stateful, 'welcomePageEnabled'));
33
-    } else {
34
-        b = true;
30
+        return Boolean(getAppProp(stateful, 'welcomePageEnabled'));
35 31
     }
36 32
 
37
-    return b;
33
+    return true;
38 34
 }
39 35
 
40 36
 /**

+ 0
- 1
react/features/welcome/index.js Voir le fichier

@@ -2,4 +2,3 @@ export * from './components';
2 2
 export * from './functions';
3 3
 
4 4
 import './reducer';
5
-import './route';

+ 0
- 35
react/features/welcome/route.js Voir le fichier

@@ -1,35 +0,0 @@
1
-/* @flow */
2
-
3
-import { RouteRegistry } from '../base/react';
4
-
5
-import { WelcomePage } from './components';
6
-import {
7
-    generateRoomWithoutSeparator,
8
-    isWelcomePageAppEnabled,
9
-    isWelcomePageUserEnabled
10
-} from './functions';
11
-
12
-/**
13
- * Register route for {@code WelcomePage}.
14
- */
15
-RouteRegistry.register({
16
-    component: WelcomePage,
17
-    onEnter,
18
-    path: '/'
19
-});
20
-
21
-/**
22
- * Skips the {@code WelcomePage} if it is disabled (by the app or the user).
23
- *
24
- * @param {Object} store - The redux store.
25
- * @param {Function} replace - The function to redirect to another path.
26
- * @returns {void}
27
- */
28
-function onEnter({ getState }, replace) {
29
-    if (isWelcomePageAppEnabled(getState)) {
30
-        isWelcomePageUserEnabled(getState)
31
-            || replace(`/${generateRoomWithoutSeparator()}`);
32
-    } else {
33
-        replace(undefined);
34
-    }
35
-}

Chargement…
Annuler
Enregistrer