ソースを参照

Fix the initialization of the (external) API

The counterpart of the external API in the Jitsi Meet Web app uses the
search URL param jwt to heuristically detect that the Web app is very
likely embedded (as an iframe) and, consequently, needs to forcefully
enable itself. It was looking at whether there was a JSON Web Token
(JWT) but that logic got broken when the JWT support was rewritten
because the check started happening before the search URL param jwt was
parsed.
master
Lyubo Marinov 8年前
コミット
320e67baa1

+ 24
- 18
app.js ファイルの表示

@@ -15,15 +15,25 @@ import 'aui-experimental-css';
15 15
 
16 16
 window.toastr = require("toastr");
17 17
 
18
-import UI from "./modules/UI/UI";
19
-import settings from "./modules/settings/Settings";
20 18
 import conference from './conference';
21 19
 import API from './modules/API';
22
-
23
-import translation from "./modules/translation/translation";
20
+import keyboardshortcut from './modules/keyboardshortcut/keyboardshortcut';
24 21
 import remoteControl from "./modules/remotecontrol/RemoteControl";
22
+import settings from "./modules/settings/Settings";
23
+import translation from "./modules/translation/translation";
24
+import UI from "./modules/UI/UI";
25 25
 
26 26
 const APP = {
27
+    API,
28
+    conference,
29
+
30
+    /**
31
+     * After the APP has been initialized provides utility methods for dealing
32
+     * with the conference room URL(address).
33
+     * @type ConferenceUrl
34
+     */
35
+    ConferenceUrl: null,
36
+
27 37
     // Used by do_external_connect.js if we receive the attach data after
28 38
     // connect was already executed. status property can be "initialized",
29 39
     // "ready" or "connecting". We are interested in "ready" status only which
@@ -34,33 +44,29 @@ const APP = {
34 44
         status: "initialized",
35 45
         handler: null
36 46
     },
47
+    connection: null,
48
+
37 49
     // Used for automated performance tests
38 50
     connectionTimes: {
39 51
         "index.loaded": window.indexLoadedTime
40 52
     },
41
-    UI,
42
-    settings,
43
-    conference,
44
-    translation,
53
+    keyboardshortcut,
54
+
45 55
     /**
46 56
      * The log collector which captures JS console logs for this app.
47 57
      * @type {LogCollector}
48 58
      */
49 59
     logCollector: null,
60
+
50 61
     /**
51 62
      * Indicates if the log collector has been started (it will not be started
52 63
      * if the welcome page is displayed).
53 64
      */
54 65
     logCollectorStarted : false,
55
-    /**
56
-     * After the APP has been initialized provides utility methods for dealing
57
-     * with the conference room URL(address).
58
-     * @type ConferenceUrl
59
-     */
60
-    ConferenceUrl : null,
61
-    connection: null,
62
-    API,
63
-    remoteControl
66
+    remoteControl,
67
+    settings,
68
+    translation,
69
+    UI
64 70
 };
65 71
 
66 72
 // TODO The execution of the mobile app starts from react/index.native.js.
@@ -69,6 +75,6 @@ const APP = {
69 75
 // because we are at the beginning of introducing React into the Web app, allow
70 76
 // the execution of the Web app to start from app.js in order to reduce the
71 77
 // complexity of the beginning step.
72
-require('./react');
78
+import './react';
73 79
 
74 80
 module.exports = APP;

+ 12
- 5
modules/API/API.js ファイルの表示

@@ -1,4 +1,5 @@
1 1
 import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
2
+import { parseJWTFromURLParams } from '../../react/features/jwt';
2 3
 import { getJitsiMeetTransport } from '../transport';
3 4
 
4 5
 import { API_ID } from './constants';
@@ -74,7 +75,15 @@ function onDesktopSharingEnabledChanged(enabled = false) {
74 75
  * @returns {boolean}
75 76
  */
76 77
 function shouldBeEnabled() {
77
-    return typeof API_ID === 'number';
78
+    return (
79
+        typeof API_ID === 'number'
80
+
81
+            // XXX Enable the API when a JSON Web Token (JWT) is specified in
82
+            // the location/URL because then it is very likely that the Jitsi
83
+            // Meet (Web) app is being used by an external/wrapping (Web) app
84
+            // and, consequently, the latter will need to communicate with the
85
+            // former. (The described logic is merely a heuristic though.)
86
+            || parseJWTFromURLParams());
78 87
 }
79 88
 
80 89
 /**
@@ -102,12 +111,10 @@ class API {
102 111
      * sends a message to the external application that API is initialized.
103 112
      *
104 113
      * @param {Object} options - Optional parameters.
105
-     * @param {boolean} options.forceEnable - True to forcefully enable the
106
-     * module.
107 114
      * @returns {void}
108 115
      */
109
-    init({ forceEnable } = {}) {
110
-        if (!shouldBeEnabled() && !forceEnable) {
116
+    init() {
117
+        if (!shouldBeEnabled()) {
111 118
             return;
112 119
         }
113 120
 

+ 6
- 12
modules/keyboardshortcut/keyboardshortcut.js ファイルの表示

@@ -1,8 +1,6 @@
1 1
 /* global APP, $, JitsiMeetJS, interfaceConfig */
2 2
 
3
-import {
4
-    toggleDialog
5
-} from '../../react/features/base/dialog';
3
+import { toggleDialog } from '../../react/features/base/dialog';
6 4
 import { SpeakerStats } from '../../react/features/speaker-stats';
7 5
 
8 6
 /**
@@ -17,7 +15,6 @@ let keyboardShortcutDialog = null;
17 15
  * triggered _only_ with a shortcut.
18 16
  */
19 17
 function initGlobalShortcuts() {
20
-
21 18
     KeyboardShortcut.registerShortcut("ESCAPE", null, function() {
22 19
         showKeyboardShortcutsPanel(false);
23 20
     });
@@ -57,19 +54,16 @@ function initGlobalShortcuts() {
57 54
  */
58 55
 function showKeyboardShortcutsPanel(show) {
59 56
     if (show
60
-        && !APP.UI.messageHandler.isDialogOpened()
61
-        && keyboardShortcutDialog === null) {
62
-
57
+            && !APP.UI.messageHandler.isDialogOpened()
58
+            && keyboardShortcutDialog === null) {
63 59
         let msg = $('#keyboard-shortcuts').html();
64 60
         let buttons = { Close: true };
65 61
 
66 62
         keyboardShortcutDialog = APP.UI.messageHandler.openDialog(
67 63
             'keyboardShortcuts.keyboardShortcuts', msg, true, buttons);
68
-    } else {
69
-        if (keyboardShortcutDialog !== null) {
70
-            keyboardShortcutDialog.close();
71
-            keyboardShortcutDialog = null;
72
-        }
64
+    } else if (keyboardShortcutDialog !== null) {
65
+        keyboardShortcutDialog.close();
66
+        keyboardShortcutDialog = null;
73 67
     }
74 68
 }
75 69
 

+ 20
- 23
react/features/app/actions.js ファイルの表示

@@ -4,23 +4,9 @@ import { setConfig } from '../base/config';
4 4
 import { loadConfig } from '../base/lib-jitsi-meet';
5 5
 
6 6
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
7
-import {
8
-    _getRouteToRender,
9
-    _parseURIString,
10
-    init
11
-} from './functions';
7
+import { _getRouteToRender, _parseURIString } from './functions';
12 8
 
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 (dispatch: Dispatch<*>, getState: Function) =>
22
-        init(getState());
23
-}
9
+declare var APP: Object;
24 10
 
25 11
 /**
26 12
  * Triggers an in-app navigation to a specific route. Allows navigation to be
@@ -28,7 +14,7 @@ export function appInit() {
28 14
  *
29 15
  * @param {(string|undefined)} uri - The URI to which to navigate. It may be a
30 16
  * full URL with an HTTP(S) scheme, a full or partial URI with the app-specific
31
- * sheme, or a mere room name.
17
+ * scheme, or a mere room name.
32 18
  * @returns {Function}
33 19
  */
34 20
 export function appNavigate(uri: ?string) {
@@ -180,9 +166,20 @@ function _appNavigateToOptionalLocation(
180 166
  * }}
181 167
  */
182 168
 export function appWillMount(app) {
183
-    return {
184
-        type: APP_WILL_MOUNT,
185
-        app
169
+    return (dispatch: Dispatch<*>) => {
170
+        dispatch({
171
+            type: APP_WILL_MOUNT,
172
+            app
173
+        });
174
+
175
+        // TODO There was a redux action creator appInit which I did not like
176
+        // because we already had the redux action creator appWillMount and,
177
+        // respectively, the redux action APP_WILL_MOUNT. So I set out to remove
178
+        // appInit and managed to move everything it was doing but the
179
+        // following. Which is not extremely bad because we haven't moved the
180
+        // API module into its own feature yet so we're bound to work on that in
181
+        // the future.
182
+        typeof APP === 'object' && APP.API.init();
186 183
     };
187 184
 }
188 185
 
@@ -205,7 +202,7 @@ export function appWillUnmount(app) {
205 202
 /**
206 203
  * Loads config.js from a specific host.
207 204
  *
208
- * @param {Object} location - The loction URI which specifies the host to load
205
+ * @param {Object} location - The location URI which specifies the host to load
209 206
  * the config.js from.
210 207
  * @returns {Promise<Object>}
211 208
  */
@@ -224,9 +221,9 @@ function _loadConfig(location: Object) {
224 221
 }
225 222
 
226 223
 /**
227
- * Navigates to a route in accord with a specific Redux state.
224
+ * Navigates to a route in accord with a specific redux state.
228 225
  *
229
- * @param {Object} state - The Redux state which determines/identifies the route
226
+ * @param {Object} state - The redux state which determines/identifies the route
230 227
  * to navigate to.
231 228
  * @private
232 229
  * @returns {void}

+ 1
- 1
react/features/app/components/AbstractApp.js ファイルの表示

@@ -84,7 +84,7 @@ export class AbstractApp extends Component {
84 84
         // business logic in the React Component (i.e. UI) AbstractApp now.
85 85
         let localParticipant;
86 86
 
87
-        if (typeof APP !== 'undefined') {
87
+        if (typeof APP === 'object') {
88 88
             localParticipant = {
89 89
                 avatarID: APP.settings.getAvatarId(),
90 90
                 avatarURL: APP.settings.getAvatarUrl(),

+ 0
- 12
react/features/app/components/App.web.js ファイルの表示

@@ -1,4 +1,3 @@
1
-import { appInit } from '../actions';
2 1
 import { AbstractApp } from './AbstractApp';
3 2
 import { getLocationContextRoot } from '../functions';
4 3
 
@@ -38,17 +37,6 @@ export class App extends AbstractApp {
38 37
         };
39 38
     }
40 39
 
41
-    /**
42
-     * Inits the app before component will mount.
43
-     *
44
-     * @inheritdoc
45
-     */
46
-    componentWillMount(...args) {
47
-        super.componentWillMount(...args);
48
-
49
-        this._getStore().dispatch(appInit());
50
-    }
51
-
52 40
     /**
53 41
      * Gets a Location object from the window with information about the current
54 42
      * location of the document.

+ 0
- 47
react/features/app/functions.web.js ファイルの表示

@@ -1,9 +1,6 @@
1 1
 /* @flow */
2 2
 
3
-import Logger from 'jitsi-meet-logger';
4
-
5 3
 import { isRoomValid } from '../base/conference';
6
-import JitsiMeetJS from '../base/lib-jitsi-meet';
7 4
 import { Platform, RouteRegistry } from '../base/react';
8 5
 import { Conference } from '../conference';
9 6
 import {
@@ -14,10 +11,6 @@ import {
14 11
 } from '../unsupported-browser';
15 12
 import { WelcomePage } from '../welcome';
16 13
 
17
-import KeyboardShortcut
18
-    from '../../../modules/keyboardshortcut/keyboardshortcut';
19
-import JitsiMeetLogStorage from '../../../modules/util/JitsiMeetLogStorage';
20
-
21 14
 declare var APP: Object;
22 15
 declare var interfaceConfig: Object;
23 16
 declare var loggingConfig: Object;
@@ -106,46 +99,6 @@ export function _getRouteToRender(stateOrGetState: Object | Function) {
106 99
     return route;
107 100
 }
108 101
 
109
-/**
110
- * Temporary solution. Later we'll get rid of global APP and set its properties
111
- * in redux store.
112
- *
113
- * @param {Object} state - Snapshot of current state of redux store.
114
- * @returns {void}
115
- */
116
-export function init(state: Object) {
117
-    _initLogging();
118
-
119
-    APP.keyboardshortcut = KeyboardShortcut;
120
-
121
-    const { jwt } = state['features/jwt'];
122
-
123
-    // Force enable the API if jwt token is passed because most probably
124
-    // jitsi meet is displayed inside of wrapper that will need to communicate
125
-    // with jitsi meet.
126
-    APP.API.init(jwt ? { forceEnable: true } : undefined);
127
-
128
-    APP.translation.init();
129
-}
130
-
131
-/**
132
- * Initializes logging in the app.
133
- *
134
- * @private
135
- * @returns {void}
136
- */
137
-function _initLogging() {
138
-    // Create the LogCollector and register it as the global log transport. It
139
-    // is done early to capture as much logs as possible. Captured logs will be
140
-    // cached, before the JitsiMeetLogStorage gets ready (statistics module is
141
-    // initialized).
142
-    if (!APP.logCollector && !loggingConfig.disableLogCollector) {
143
-        APP.logCollector = new Logger.LogCollector(new JitsiMeetLogStorage());
144
-        Logger.addGlobalTransport(APP.logCollector);
145
-        JitsiMeetJS.addGlobalLogTransport(APP.logCollector);
146
-    }
147
-}
148
-
149 102
 /**
150 103
  * Intercepts route components based on a {@link _INTERCEPT_COMPONENT_RULES}.
151 104
  *

+ 0
- 5
react/features/app/index.js ファイルの表示

@@ -3,9 +3,4 @@ export * from './actionTypes';
3 3
 export * from './components';
4 4
 export * from './functions';
5 5
 
6
-// We need to import the jwt module in order to register the reducer and
7
-// middleware, because the module is not used outside of this feature.
8
-import '../jwt';
9
-
10 6
 import './reducer';
11
-

+ 1
- 1
react/features/base/config/parseURLParams.js ファイルの表示

@@ -3,7 +3,7 @@
3 3
 /**
4 4
  * Parses the parameters from the URL and returns them as a JS object.
5 5
  *
6
- * @param {string} url - URL to parse.
6
+ * @param {string} url - The URL to parse.
7 7
  * @param {boolean} dontParse - If false or undefined some transformations
8 8
  * (for parsing the value as JSON) are going to be executed.
9 9
  * @param {string} source - Values - "hash"/"search" if "search" the parameters

+ 2
- 0
react/features/base/i18n/index.js ファイルの表示

@@ -4,3 +4,5 @@ export * from './functions';
4 4
 // TODO Eventually (e.g. when the non-React Web app is rewritten into React), it
5 5
 // should not be necessary to export i18next.
6 6
 export { default as i18next } from './i18next';
7
+
8
+import './middleware';

+ 48
- 0
react/features/base/i18n/middleware.js ファイルの表示

@@ -0,0 +1,48 @@
1
+/* @flow */
2
+
3
+import { SET_CONFIG } from '../config';
4
+import { MiddlewareRegistry } from '../redux';
5
+
6
+declare var APP: Object;
7
+
8
+/**
9
+ * The redux middleware of the feature base/i18n.
10
+ *
11
+ * @param {Store} store - The redux store.
12
+ * @returns {Function}
13
+ * @private
14
+ */
15
+MiddlewareRegistry.register(store => next => action => {
16
+    switch (action.type) {
17
+    case SET_CONFIG:
18
+        return _setConfig(store, next, action);
19
+    }
20
+
21
+    return next(action);
22
+});
23
+
24
+/**
25
+ * Notifies the feature base/i18n that the action SET_CONFIG is being dispatched
26
+ * within a specific redux store.
27
+ *
28
+ * @param {Store} store - The redux store in which the specified action is being
29
+ * dispatched.
30
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
31
+ * specified action to the specified store.
32
+ * @param {Action} action - The redux action SET_CONFIG which is being
33
+ * dispatched in the specified store.
34
+ * @private
35
+ * @returns {Object} The new state that is the result of the reduction of the
36
+ * specified action.
37
+ */
38
+function _setConfig({ dispatch, getState }, next, action) {
39
+    const oldValue = getState()['features/base/config'];
40
+    const result = next(action);
41
+    const newValue = getState()['features/base/config'];
42
+
43
+    if (oldValue !== newValue && typeof APP === 'object') {
44
+        APP.translation.init();
45
+    }
46
+
47
+    return result;
48
+}

+ 26
- 0
react/features/base/logging/middleware.js ファイルの表示

@@ -6,6 +6,8 @@ import { APP_WILL_MOUNT } from '../../app';
6 6
 import JitsiMeetJS, { LIB_WILL_INIT } from '../lib-jitsi-meet';
7 7
 import { MiddlewareRegistry } from '../redux';
8 8
 
9
+import JitsiMeetLogStorage from '../../../../modules/util/JitsiMeetLogStorage';
10
+
9 11
 import { SET_LOGGING_CONFIG } from './actionTypes';
10 12
 
11 13
 declare var APP: Object;
@@ -60,6 +62,28 @@ function _appWillMount({ getState }, next, action) {
60 62
     return next(action);
61 63
 }
62 64
 
65
+/**
66
+ * Initializes logging in the app.
67
+ *
68
+ * @param {Object} loggingConfig - The configuration with which logging is to be
69
+ * initialized.
70
+ * @private
71
+ * @returns {void}
72
+ */
73
+function _initLogging(loggingConfig) {
74
+    // Create the LogCollector and register it as the global log transport. It
75
+    // is done early to capture as much logs as possible. Captured logs will be
76
+    // cached, before the JitsiMeetLogStorage gets ready (statistics module is
77
+    // initialized).
78
+    if (typeof APP === 'object'
79
+            && !APP.logCollector
80
+            && !loggingConfig.disableLogCollector) {
81
+        APP.logCollector = new Logger.LogCollector(new JitsiMeetLogStorage());
82
+        Logger.addGlobalTransport(APP.logCollector);
83
+        JitsiMeetJS.addGlobalLogTransport(APP.logCollector);
84
+    }
85
+}
86
+
63 87
 /**
64 88
  * Notifies the feature base/logging that the action {@link LIB_WILL_INIT} is
65 89
  * being dispatched within a specific Redux {@code store}.
@@ -102,6 +126,8 @@ function _setLoggingConfig({ getState }, next, action) {
102 126
     if (oldValue !== newValue) {
103 127
         _setLogLevels(Logger, newValue);
104 128
         _setLogLevels(JitsiMeetJS, newValue);
129
+
130
+        _initLogging(newValue);
105 131
     }
106 132
 
107 133
     return result;

+ 16
- 0
react/features/jwt/functions.js ファイルの表示

@@ -0,0 +1,16 @@
1
+/* @flow */
2
+
3
+import { parseURLParams } from '../base/config';
4
+
5
+/**
6
+ * Retrieves the JSON Web Token (JWT), if any, defined by a specific
7
+ * {@link URL}.
8
+ *
9
+ * @param {URL} url - The {@code URL} to parse and retrieve the JSON Web Token
10
+ * (JWT), if any, from.
11
+ * @returns {string} The JSON Web Token (JWT), if any, defined by the specified
12
+ * {@code url}; otherwise, {@code undefined}.
13
+ */
14
+export function parseJWTFromURLParams(url: URL = window.location) {
15
+    return parseURLParams(url, true, 'search').jwt;
16
+}

+ 1
- 0
react/features/jwt/index.js ファイルの表示

@@ -1,4 +1,5 @@
1 1
 export * from './actions';
2
+export * from './functions';
2 3
 
3 4
 import './middleware';
4 5
 import './reducer';

+ 3
- 2
react/features/jwt/middleware.js ファイルの表示

@@ -1,11 +1,12 @@
1 1
 import jwtDecode from 'jwt-decode';
2 2
 
3
-import { parseURLParams, SET_CONFIG } from '../base/config';
3
+import { SET_CONFIG } from '../base/config';
4 4
 import { SET_LOCATION_URL } from '../base/connection';
5 5
 import { MiddlewareRegistry } from '../base/redux';
6 6
 
7 7
 import { setJWT } from './actions';
8 8
 import { SET_JWT } from './actionTypes';
9
+import { parseJWTFromURLParams } from './functions';
9 10
 
10 11
 /**
11 12
  * Middleware to parse token data upon setting a new room URL.
@@ -55,7 +56,7 @@ function _setConfigOrLocationURL({ dispatch, getState }, next, action) {
55 56
     let jwt;
56 57
 
57 58
     if (locationURL) {
58
-        jwt = parseURLParams(locationURL, true, 'search').jwt;
59
+        jwt = parseJWTFromURLParams(locationURL);
59 60
     }
60 61
     dispatch(setJWT(jwt));
61 62
 

読み込み中…
キャンセル
保存