소스 검색

[RN] Fix TypeError: undefined is not an object (evaluating 'this.options.p2p.useStunTurn')

j8
Lyubo Marinov 8 년 전
부모
커밋
9aaf9a484d

+ 1
- 11
react/features/base/conference/actions.js 파일 보기

@@ -1,4 +1,3 @@
1
-import _ from 'lodash';
2 1
 import { JitsiConferenceEvents } from '../lib-jitsi-meet';
3 2
 import { setVideoMuted } from '../media';
4 3
 import {
@@ -255,21 +254,12 @@ export function createConference() {
255 254
 
256 255
         dispatch(_conferenceWillJoin(room));
257 256
 
258
-        const config = state['features/base/config'];
259
-        const configOverride = {
260
-            p2p: {
261
-                preferH264: true
262
-            }
263
-        };
264 257
         const conference
265 258
             = connection.initJitsiConference(
266 259
 
267 260
                 // XXX Lib-jitsi-meet does not accept uppercase letters.
268 261
                 room.toLowerCase(),
269
-
270
-                // We use lodash's merge here because it will recursively merge
271
-                // objects allowing partial overrides.
272
-                _.merge({}, config, configOverride));
262
+                state['features/base/config']);
273 263
 
274 264
         _addConferenceListeners(conference, dispatch);
275 265
 

+ 118
- 20
react/features/base/config/reducer.js 파일 보기

@@ -1,17 +1,34 @@
1
-import { ReducerRegistry } from '../redux';
1
+/* @flow */
2
+
3
+import _ from 'lodash';
4
+
5
+import { equals, ReducerRegistry, set } from '../redux';
2 6
 
3 7
 import { SET_CONFIG } from './actionTypes';
4 8
 
5 9
 /**
6
- * The initial state of the feature base/config. The mandatory configuration to
7
- * be passed to JitsiMeetJS#init(). The app will download config.js from the
8
- * Jitsi Meet deployment and take its values into account but the values bellow
9
- * will be enforced (because they are essential to the correct execution of the
10
+ * The initial state of the feature base/config when executing in a
11
+ * non-React Native environment. The mandatory configuration to be passed to
12
+ * JitsiMeetJS#init(). The app will download config.js from the Jitsi Meet
13
+ * deployment and take its values into account but the values bellow will be
14
+ * enforced (because they are essential to the correct execution of the
10 15
  * application).
11 16
  *
12 17
  * @type {Object}
13 18
  */
14
-const INITIAL_STATE = {
19
+const INITIAL_NON_RN_STATE = {
20
+};
21
+
22
+/**
23
+ * The initial state of the feature base/config when executing in a React Native
24
+ * environment. The mandatory configuration to be passed to JitsiMeetJS#init().
25
+ * The app will download config.js from the Jitsi Meet deployment and take its
26
+ * values into account but the values bellow will be enforced (because they are
27
+ * essential to the correct execution of the application).
28
+ *
29
+ * @type {Object}
30
+ */
31
+const INITIAL_RN_STATE = {
15 32
     // FIXME The support for audio levels in lib-jitsi-meet polls the statistics
16 33
     // of WebRTC at a short interval multiple times a second. Unfortunately,
17 34
     // React Native is slow to fetch these statistics from the native WebRTC
@@ -26,12 +43,16 @@ const INITIAL_STATE = {
26 43
     // Fortunately, these pieces of JavaScript currently involve third parties
27 44
     // and we can temporarily disable them (until we implement an alternative to
28 45
     // async script elements on React Native).
29
-    disableThirdPartyRequests: true
46
+    disableThirdPartyRequests: true,
47
+
48
+    p2p: {
49
+        preferH264: true
50
+    }
30 51
 };
31 52
 
32 53
 ReducerRegistry.register(
33 54
     'features/base/config',
34
-    (state = INITIAL_STATE, action) => {
55
+    (state = _getInitialState(), action) => {
35 56
         switch (action.type) {
36 57
         case SET_CONFIG:
37 58
             return _setConfig(state, action);
@@ -41,6 +62,22 @@ ReducerRegistry.register(
41 62
         }
42 63
     });
43 64
 
65
+/**
66
+ * Gets the initial state of the feature base/config. The mandatory
67
+ * configuration to be passed to JitsiMeetJS#init(). The app will download
68
+ * config.js from the Jitsi Meet deployment and take its values into account but
69
+ * the values bellow will be enforced (because they are essential to the correct
70
+ * execution of the application).
71
+ *
72
+ * @returns {Object}
73
+ */
74
+function _getInitialState() {
75
+    return (
76
+        navigator.userAgent.match(/react[ \s-]*native/i)
77
+            ? INITIAL_RN_STATE
78
+            : INITIAL_NON_RN_STATE);
79
+}
80
+
44 81
 /**
45 82
  * Reduces a specific Redux action SET_CONFIG of the feature
46 83
  * base/lib-jitsi-meet.
@@ -52,19 +89,80 @@ ReducerRegistry.register(
52 89
  * reduction of the specified action.
53 90
  */
54 91
 function _setConfig(state, action) {
55
-    return {
56
-        ...action.config,
92
+    let { config } = action;
93
+
94
+    // The mobile app bundles jitsi-meet and lib-jitsi-meet at build time and
95
+    // does not download them at runtime from the deployment on which it will
96
+    // join a conference. The downloading is planned for implementation in the
97
+    // future (later rather than sooner) but is not implemented yet at the time
98
+    // of this writing and, consequently, we must provide legacy support in the
99
+    // meantime.
100
+    config = _translateLegacyConfig(config);
57 101
 
58
-        // The config of INITIAL_STATE is meant to override the config
102
+    const newState = _.merge(
103
+        {},
104
+        config,
105
+
106
+        // The config of _getInitialState() is meant to override the config
59 107
         // downloaded from the Jitsi Meet deployment because the former contains
60 108
         // values that are mandatory.
61
-        //
62
-        // FIXME At the time of this writing the hard-coded overriding values
63
-        // are specific to mobile/React Native but the source code here is
64
-        // executed on Web/React as well. The latter is not a practical problem
65
-        // right now because the rest of the Web/React source code does not read
66
-        // the overridden properties/values, it still relies on the global
67
-        // variable config.
68
-        ...INITIAL_STATE
69
-    };
109
+        _getInitialState()
110
+    );
111
+
112
+    return equals(state, newState) ? state : newState;
113
+}
114
+
115
+/**
116
+ * Constructs a new config {@code Object}, if necessary, out of a specific
117
+ * config {@code Object} which is in the latest format supported by jitsi-meet.
118
+ * Such a translation from an old config format to a new/the latest config
119
+ * format is necessary because the mobile app bundles jitsi-meet and
120
+ * lib-jitsi-meet at build time and does not download them at runtime from the
121
+ * deployment on which it will join a conference.
122
+ *
123
+ * @param {Object} oldValue - The config {@code Object} which may or may not be
124
+ * in the latest form supported by jitsi-meet and from which a new config
125
+ * {@code Object} is to be constructed if necessary.
126
+ * @returns {Object} A config {@code Object} which is in the latest format
127
+ * supported by jitsi-meet.
128
+ */
129
+function _translateLegacyConfig(oldValue: Object) {
130
+    // jitsi/jitsi-meet#3ea2f005787c9f49c48febaeed9dc0340fe0a01b
131
+
132
+    let newValue = oldValue;
133
+
134
+    // At the time of this writing lib-jitsi-meet will rely on config having a
135
+    // property with the name p2p and with a value of type Object.
136
+    if (typeof oldValue.p2p !== 'object') {
137
+        newValue = set(newValue, 'p2p', {});
138
+    }
139
+
140
+    // Translate the old config properties into the new config.p2p properties.
141
+    for (const [ oldKey, newKey ]
142
+            of [
143
+                [ 'backToP2PDelay', 'backToP2PDelay' ],
144
+                [ 'enableP2P', 'enabled' ],
145
+                [ 'p2pStunServers', 'stunServers' ]
146
+            ]) {
147
+        if (oldKey in newValue) {
148
+            const v = newValue[oldKey];
149
+
150
+            // Do not modify oldValue.
151
+            if (newValue === oldValue) {
152
+                newValue = {
153
+                    ...newValue
154
+                };
155
+            }
156
+            delete newValue[oldKey];
157
+
158
+            // Do not modify p2p because it may be from oldValue i.e. do not
159
+            // modify oldValue.
160
+            newValue.p2p = {
161
+                ...newValue.p2p,
162
+                [newKey]: v
163
+            };
164
+        }
165
+    }
166
+
167
+    return newValue;
70 168
 }

+ 22
- 12
react/features/base/connection/actions.native.js 파일 보기

@@ -20,24 +20,34 @@ import {
20 20
 export function connect() {
21 21
     return (dispatch: Dispatch<*>, getState: Function) => {
22 22
         const state = getState();
23
-        const { options } = state['features/base/connection'];
23
+        let { options } = state['features/base/connection'];
24
+
25
+        options = {
26
+            // Lib-jitsi-meet wants the config passed in multiple places and
27
+            // here is the latest one I have discovered.
28
+            ...state['features/base/config'],
29
+
30
+            // TODO It is probable that config should override the options that
31
+            // have been automatically constructed by the app. Unfortunately,
32
+            // config may specify URLs such as bosh at the time of this writing
33
+            // which react-native cannot parse (because they do not have a
34
+            // protocol/scheme).
35
+            ...options
36
+        };
37
+
24 38
         const { issuer, jwt } = state['features/jwt'];
25 39
         const { room } = state['features/base/conference'];
40
+
41
+        // XXX The Jitsi Meet deployments require the room argument to be in
42
+        // lower case at the time of this writing but, unfortunately, they do
43
+        // not ignore case themselves.
44
+        options.bosh += room ? `?room=${room.toLowerCase()}` : '';
45
+
26 46
         const connection
27 47
             = new JitsiMeetJS.JitsiConnection(
28 48
                 options.appId,
29 49
                 jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
30
-                {
31
-                    ...options,
32
-                    bosh:
33
-                        options.bosh
34
-
35
-                            // XXX The Jitsi Meet deployments require the room
36
-                            // argument to be in lower case at the time of this
37
-                            // writing but, unfortunately, they do not ignore
38
-                            // case themselves.
39
-                            + (room ? `?room=${room.toLowerCase()}` : '')
40
-                });
50
+                options);
41 51
 
42 52
         connection.addEventListener(
43 53
             JitsiConnectionEvents.CONNECTION_DISCONNECTED,

+ 9
- 5
react/features/base/lib-jitsi-meet/native/polyfills-browser.js 파일 보기

@@ -263,7 +263,7 @@ function _visitNode(node, callback) {
263 263
         };
264 264
     }
265 265
 
266
-    const navigator = global.navigator;
266
+    const { navigator } = global;
267 267
 
268 268
     if (navigator) {
269 269
         // platform
@@ -372,13 +372,17 @@ function _visitNode(node, callback) {
372 372
     // Required by:
373 373
     // - lib-jitsi-meet
374 374
     // - Strophe
375
-    global.clearTimeout = window.clearTimeout
375
+    global.clearTimeout
376
+        = window.clearTimeout
376 377
         = BackgroundTimer.clearTimeout.bind(BackgroundTimer);
377
-    global.clearInterval = window.clearInterval
378
+    global.clearInterval
379
+        = window.clearInterval
378 380
         = BackgroundTimer.clearInterval.bind(BackgroundTimer);
379
-    global.setInterval = window.setInterval
381
+    global.setInterval
382
+        = window.setInterval
380 383
         = BackgroundTimer.setInterval.bind(BackgroundTimer);
381
-    global.setTimeout = window.setTimeout
384
+    global.setTimeout
385
+        = window.setTimeout
382 386
         = BackgroundTimer.setTimeout.bind(BackgroundTimer);
383 387
 
384 388
 })(global || window || this); // eslint-disable-line no-invalid-this

Loading…
취소
저장