Browse Source

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

j8
Lyubo Marinov 8 years ago
parent
commit
9aaf9a484d

+ 1
- 11
react/features/base/conference/actions.js View File

1
-import _ from 'lodash';
2
 import { JitsiConferenceEvents } from '../lib-jitsi-meet';
1
 import { JitsiConferenceEvents } from '../lib-jitsi-meet';
3
 import { setVideoMuted } from '../media';
2
 import { setVideoMuted } from '../media';
4
 import {
3
 import {
255
 
254
 
256
         dispatch(_conferenceWillJoin(room));
255
         dispatch(_conferenceWillJoin(room));
257
 
256
 
258
-        const config = state['features/base/config'];
259
-        const configOverride = {
260
-            p2p: {
261
-                preferH264: true
262
-            }
263
-        };
264
         const conference
257
         const conference
265
             = connection.initJitsiConference(
258
             = connection.initJitsiConference(
266
 
259
 
267
                 // XXX Lib-jitsi-meet does not accept uppercase letters.
260
                 // XXX Lib-jitsi-meet does not accept uppercase letters.
268
                 room.toLowerCase(),
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
         _addConferenceListeners(conference, dispatch);
264
         _addConferenceListeners(conference, dispatch);
275
 
265
 

+ 118
- 20
react/features/base/config/reducer.js View File

1
-import { ReducerRegistry } from '../redux';
1
+/* @flow */
2
+
3
+import _ from 'lodash';
4
+
5
+import { equals, ReducerRegistry, set } from '../redux';
2
 
6
 
3
 import { SET_CONFIG } from './actionTypes';
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
  * application).
15
  * application).
11
  *
16
  *
12
  * @type {Object}
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
     // FIXME The support for audio levels in lib-jitsi-meet polls the statistics
32
     // FIXME The support for audio levels in lib-jitsi-meet polls the statistics
16
     // of WebRTC at a short interval multiple times a second. Unfortunately,
33
     // of WebRTC at a short interval multiple times a second. Unfortunately,
17
     // React Native is slow to fetch these statistics from the native WebRTC
34
     // React Native is slow to fetch these statistics from the native WebRTC
26
     // Fortunately, these pieces of JavaScript currently involve third parties
43
     // Fortunately, these pieces of JavaScript currently involve third parties
27
     // and we can temporarily disable them (until we implement an alternative to
44
     // and we can temporarily disable them (until we implement an alternative to
28
     // async script elements on React Native).
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
 ReducerRegistry.register(
53
 ReducerRegistry.register(
33
     'features/base/config',
54
     'features/base/config',
34
-    (state = INITIAL_STATE, action) => {
55
+    (state = _getInitialState(), action) => {
35
         switch (action.type) {
56
         switch (action.type) {
36
         case SET_CONFIG:
57
         case SET_CONFIG:
37
             return _setConfig(state, action);
58
             return _setConfig(state, action);
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
  * Reduces a specific Redux action SET_CONFIG of the feature
82
  * Reduces a specific Redux action SET_CONFIG of the feature
46
  * base/lib-jitsi-meet.
83
  * base/lib-jitsi-meet.
52
  * reduction of the specified action.
89
  * reduction of the specified action.
53
  */
90
  */
54
 function _setConfig(state, action) {
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
         // downloaded from the Jitsi Meet deployment because the former contains
107
         // downloaded from the Jitsi Meet deployment because the former contains
60
         // values that are mandatory.
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 View File

20
 export function connect() {
20
 export function connect() {
21
     return (dispatch: Dispatch<*>, getState: Function) => {
21
     return (dispatch: Dispatch<*>, getState: Function) => {
22
         const state = getState();
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
         const { issuer, jwt } = state['features/jwt'];
38
         const { issuer, jwt } = state['features/jwt'];
25
         const { room } = state['features/base/conference'];
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
         const connection
46
         const connection
27
             = new JitsiMeetJS.JitsiConnection(
47
             = new JitsiMeetJS.JitsiConnection(
28
                 options.appId,
48
                 options.appId,
29
                 jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
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
         connection.addEventListener(
52
         connection.addEventListener(
43
             JitsiConnectionEvents.CONNECTION_DISCONNECTED,
53
             JitsiConnectionEvents.CONNECTION_DISCONNECTED,

+ 9
- 5
react/features/base/lib-jitsi-meet/native/polyfills-browser.js View File

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

Loading…
Cancel
Save