Browse Source

[RN] base/media is intent, base/tracks is reality

j8
Lyubo Marinov 8 years ago
parent
commit
85a168d51b

+ 8
- 4
react/features/base/conference/actions.js View File

@@ -62,13 +62,17 @@ function _addConferenceListeners(conference, dispatch) {
62 62
 
63 63
     // Dispatches into features/base/media follow:
64 64
 
65
-    // FIXME: This is needed because when Jicofo tells us to start muted
66
-    // lib-jitsi-meet does the actual muting. Perhaps this should be refactored
67
-    // so applications are hinted to start muted, but lib-jitsi-meet doesn't
68
-    // take action.
69 65
     conference.on(
70 66
         JitsiConferenceEvents.STARTED_MUTED,
71 67
         () => {
68
+            // XXX Jicofo tells lib-jitsi-meet to start with audio and/or video
69
+            // muted i.e. Jicofo expresses an intent. Lib-jitsi-meet has turned
70
+            // Jicofo's intent into reality by actually muting the respective
71
+            // tracks. The reality is expressed in base/tracks already so what
72
+            // is left is to express Jicofo's intent in base/media.
73
+            // TODO Maybe the app needs to learn about Jicofo's intent and
74
+            // transfer that intent to lib-jitsi-meet instead of lib-jitsi-meet
75
+            // acting on Jicofo's intent without the app's knowledge.
72 76
             dispatch(setAudioMuted(Boolean(conference.startAudioMuted)));
73 77
             dispatch(setVideoMuted(Boolean(conference.startVideoMuted)));
74 78
         });

+ 39
- 39
react/features/base/conference/middleware.js View File

@@ -32,7 +32,7 @@ import {
32 32
 /**
33 33
  * Implements the middleware of the feature base/conference.
34 34
  *
35
- * @param {Store} store - Redux store.
35
+ * @param {Store} store - The redux store.
36 36
  * @returns {Function}
37 37
  */
38 38
 MiddlewareRegistry.register(store => next => action => {
@@ -40,13 +40,13 @@ MiddlewareRegistry.register(store => next => action => {
40 40
     case CONNECTION_ESTABLISHED:
41 41
         return _connectionEstablished(store, next, action);
42 42
 
43
-    case CONFERENCE_JOINED:
44
-        return _conferenceJoined(store, next, action);
45
-
46 43
     case CONFERENCE_FAILED:
47 44
     case CONFERENCE_LEFT:
48 45
         return _conferenceFailedOrLeft(store, next, action);
49 46
 
47
+    case CONFERENCE_JOINED:
48
+        return _conferenceJoined(store, next, action);
49
+
50 50
     case PIN_PARTICIPANT:
51 51
         return _pinParticipant(store, next, action);
52 52
 
@@ -66,13 +66,13 @@ MiddlewareRegistry.register(store => next => action => {
66 66
 
67 67
 /**
68 68
  * Notifies the feature base/conference that the action CONNECTION_ESTABLISHED
69
- * is being dispatched within a specific Redux store.
69
+ * is being dispatched within a specific redux store.
70 70
  *
71
- * @param {Store} store - The Redux store in which the specified action is being
71
+ * @param {Store} store - The redux store in which the specified action is being
72 72
  * dispatched.
73
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
73
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
74 74
  * specified action to the specified store.
75
- * @param {Action} action - The Redux action CONNECTION_ESTABLISHED which is
75
+ * @param {Action} action - The redux action CONNECTION_ESTABLISHED which is
76 76
  * being dispatched in the specified store.
77 77
  * @private
78 78
  * @returns {Object} The new state that is the result of the reduction of the
@@ -91,37 +91,37 @@ function _connectionEstablished(store, next, action) {
91 91
 }
92 92
 
93 93
 /**
94
- * Does extra sync up on properties that may need to be updated, after
95
- * the conference failed or was left.
94
+ * Does extra sync up on properties that may need to be updated after the
95
+ * conference failed or was left.
96 96
  *
97
- * @param {Store} store - The Redux store in which the specified action is being
97
+ * @param {Store} store - The redux store in which the specified action is being
98 98
  * dispatched.
99
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
99
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
100 100
  * specified action to the specified store.
101
- * @param {Action} action - The Redux action {@link CONFERENCE_FAILED} or
101
+ * @param {Action} action - The redux action {@link CONFERENCE_FAILED} or
102 102
  * {@link CONFERENCE_LEFT} which is being dispatched in the specified store.
103 103
  * @private
104 104
  * @returns {Object} The new state that is the result of the reduction of the
105 105
  * specified action.
106 106
  */
107
-function _conferenceFailedOrLeft(store, next, action) {
107
+function _conferenceFailedOrLeft({ dispatch, getState }, next, action) {
108 108
     const result = next(action);
109
-    const { audioOnly } = store.getState()['features/base/conference'];
110 109
 
111
-    audioOnly && store.dispatch(setAudioOnly(false));
110
+    getState()['features/base/conference'].audioOnly
111
+        && dispatch(setAudioOnly(false));
112 112
 
113 113
     return result;
114 114
 }
115 115
 
116 116
 /**
117
- * Does extra sync up on properties that may need to be updated, after
118
- * the conference was joined.
117
+ * Does extra sync up on properties that may need to be updated after the
118
+ * conference was joined.
119 119
  *
120
- * @param {Store} store - The Redux store in which the specified action is being
120
+ * @param {Store} store - The redux store in which the specified action is being
121 121
  * dispatched.
122
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
122
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
123 123
  * specified action to the specified store.
124
- * @param {Action} action - The Redux action CONFERENCE_JOINED which is being
124
+ * @param {Action} action - The redux action CONFERENCE_JOINED which is being
125 125
  * dispatched in the specified store.
126 126
  * @private
127 127
  * @returns {Object} The new state that is the result of the reduction of the
@@ -144,14 +144,14 @@ function _conferenceJoined(store, next, action) {
144 144
 
145 145
 /**
146 146
  * Notifies the feature base/conference that the action PIN_PARTICIPANT is being
147
- * dispatched within a specific Redux store. Pins the specified remote
147
+ * dispatched within a specific redux store. Pins the specified remote
148 148
  * participant in the associated conference, ignores the local participant.
149 149
  *
150
- * @param {Store} store - The Redux store in which the specified action is being
150
+ * @param {Store} store - The redux store in which the specified action is being
151 151
  * dispatched.
152
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
152
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
153 153
  * specified action to the specified store.
154
- * @param {Action} action - The Redux action PIN_PARTICIPANT which is being
154
+ * @param {Action} action - The redux action PIN_PARTICIPANT which is being
155 155
  * dispatched in the specified store.
156 156
  * @private
157 157
  * @returns {Object} The new state that is the result of the reduction of the
@@ -195,26 +195,26 @@ function _pinParticipant(store, next, action) {
195 195
  * Sets the audio-only flag for the current conference. When audio-only is set,
196 196
  * local video is muted and last N is set to 0 to avoid receiving remote video.
197 197
  *
198
- * @param {Store} store - The Redux store in which the specified action is being
198
+ * @param {Store} store - The redux store in which the specified action is being
199 199
  * dispatched.
200
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
200
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
201 201
  * specified action to the specified store.
202
- * @param {Action} action - The Redux action SET_AUDIO_ONLY which is being
202
+ * @param {Action} action - The redux action SET_AUDIO_ONLY which is being
203 203
  * dispatched in the specified store.
204 204
  * @private
205 205
  * @returns {Object} The new state that is the result of the reduction of the
206 206
  * specified action.
207 207
  */
208
-function _setAudioOnly({ dispatch }, next, action) {
208
+function _setAudioOnly({ dispatch, getState }, next, action) {
209 209
     const result = next(action);
210 210
 
211
-    const { audioOnly } = action;
211
+    const { audioOnly } = getState()['features/base/conference'];
212 212
 
213 213
     // Set lastN to 0 in case audio-only is desired; leave it as undefined,
214 214
     // otherwise, and the default lastN value will be chosen automatically.
215 215
     dispatch(setLastN(audioOnly ? 0 : undefined));
216 216
 
217
-    // Mute the local video.
217
+    // Mute/unmute the local video.
218 218
     dispatch(setVideoMuted(audioOnly, VIDEO_MUTISM_AUTHORITY.AUDIO_ONLY));
219 219
 
220 220
     if (typeof APP !== 'undefined') {
@@ -229,11 +229,11 @@ function _setAudioOnly({ dispatch }, next, action) {
229 229
 /**
230 230
  * Sets the last N (value) of the video channel in the conference.
231 231
  *
232
- * @param {Store} store - The Redux store in which the specified action is being
232
+ * @param {Store} store - The redux store in which the specified action is being
233 233
  * dispatched.
234
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
234
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
235 235
  * specified action to the specified store.
236
- * @param {Action} action - The Redux action SET_LASTN which is being dispatched
236
+ * @param {Action} action - The redux action SET_LASTN which is being dispatched
237 237
  * in the specified store.
238 238
  * @private
239 239
  * @returns {Object} The new state that is the result of the reduction of the
@@ -257,7 +257,7 @@ function _setLastN(store, next, action) {
257 257
  * Synchronizes local tracks from state with local tracks in JitsiConference
258 258
  * instance.
259 259
  *
260
- * @param {Store} store - Redux store.
260
+ * @param {Store} store - The redux store.
261 261
  * @param {Object} action - Action object.
262 262
  * @private
263 263
  * @returns {Promise}
@@ -284,13 +284,13 @@ function _syncConferenceLocalTracksWithState(store, action) {
284 284
 
285 285
 /**
286 286
  * Notifies the feature base/conference that the action TRACK_ADDED
287
- * or TRACK_REMOVED is being dispatched within a specific Redux store.
287
+ * or TRACK_REMOVED is being dispatched within a specific redux store.
288 288
  *
289
- * @param {Store} store - The Redux store in which the specified action is being
289
+ * @param {Store} store - The redux store in which the specified action is being
290 290
  * dispatched.
291
- * @param {Dispatch} next - The Redux dispatch function to dispatch the
291
+ * @param {Dispatch} next - The redux dispatch function to dispatch the
292 292
  * specified action to the specified store.
293
- * @param {Action} action - The Redux action TRACK_ADDED or TRACK_REMOVED which
293
+ * @param {Action} action - The redux action TRACK_ADDED or TRACK_REMOVED which
294 294
  * is being dispatched in the specified store.
295 295
  * @private
296 296
  * @returns {Object} The new state that is the result of the reduction of the

+ 6
- 7
react/features/base/media/middleware.js View File

@@ -72,16 +72,15 @@ function _setRoom({ dispatch, getState }, next, action) {
72 72
         && (videoMuted = config.startWithVideoMuted);
73 73
 
74 74
     // Apply startWithAudioMuted and startWithVideoMuted.
75
-    const { audio, video } = state['features/base/media'];
76
-
77 75
     audioMuted = Boolean(audioMuted);
78 76
     videoMuted = Boolean(videoMuted);
79 77
 
80
-    (audio.muted !== audioMuted) && dispatch(setAudioMuted(audioMuted));
81
-    (video.facingMode !== CAMERA_FACING_MODE.USER)
82
-        && dispatch(setCameraFacingMode(CAMERA_FACING_MODE.USER));
83
-    (Boolean(video.muted) !== videoMuted)
84
-        && dispatch(setVideoMuted(videoMuted));
78
+    // Unconditionally express the desires/expectations/intents of the app and
79
+    // the user i.e. the state of base/media. Eventually, practice/reality i.e.
80
+    // the state of base/tracks will or will not agree with the desires.
81
+    dispatch(setAudioMuted(audioMuted));
82
+    dispatch(setCameraFacingMode(CAMERA_FACING_MODE.USER));
83
+    dispatch(setVideoMuted(videoMuted));
85 84
 
86 85
     return next(action);
87 86
 }

+ 30
- 17
react/features/base/tracks/actions.js View File

@@ -87,19 +87,21 @@ export function destroyLocalTracks() {
87 87
  */
88 88
 export function replaceLocalTrack(oldTrack, newTrack, conference) {
89 89
     return (dispatch, getState) => {
90
-        const currentConference = conference
91
-            || getState()['features/base/conference'].conference;
90
+        conference
92 91
 
93
-        return currentConference.replaceTrack(oldTrack, newTrack)
92
+            // eslint-disable-next-line no-param-reassign
93
+            || (conference = getState()['features/base/conference'].conference);
94
+
95
+        return conference.replaceTrack(oldTrack, newTrack)
94 96
             .then(() => {
95
-                // We call dispose after doing the replace because
96
-                //  dispose will try and do a new o/a after the
97
-                //  track removes itself.  Doing it after means
98
-                //  the JitsiLocalTrack::conference member is already
99
-                //  cleared, so it won't try and do the o/a
100
-                const disposePromise = oldTrack
101
-                    ? dispatch(_disposeAndRemoveTracks([ oldTrack ]))
102
-                    : Promise.resolve();
97
+                // We call dispose after doing the replace because dispose will
98
+                // try and do a new o/a after the track removes itself. Doing it
99
+                // after means the JitsiLocalTrack.conference is already
100
+                // cleared, so it won't try and do the o/a.
101
+                const disposePromise
102
+                    = oldTrack
103
+                        ? dispatch(_disposeAndRemoveTracks([ oldTrack ]))
104
+                        : Promise.resolve();
103 105
 
104 106
                 return disposePromise
105 107
                     .then(() => {
@@ -113,10 +115,12 @@ export function replaceLocalTrack(oldTrack, newTrack, conference) {
113 115
                             // track's mute state. If this is not done, the
114 116
                             // current mute state of the app will be reflected
115 117
                             // on the track, not vice-versa.
116
-                            const muteAction = newTrack.isVideoTrack()
117
-                                ? setVideoMuted : setAudioMuted;
118
+                            const setMuted
119
+                                = newTrack.isVideoTrack()
120
+                                    ? setVideoMuted
121
+                                    : setAudioMuted;
118 122
 
119
-                            return dispatch(muteAction(newTrack.isMuted()));
123
+                            return dispatch(setMuted(newTrack.isMuted()));
120 124
                         }
121 125
                     })
122 126
                     .then(() => {
@@ -366,17 +370,26 @@ export function setTrackMuted(track, muted) {
366 370
 
367 371
         const f = muted ? 'mute' : 'unmute';
368 372
 
369
-        // FIXME: This operation disregards the authority. It is not a problem
370
-        // (on mobile) at the moment, but it will be once we start not creating
371
-        // tracks early. Refactor this then.
372 373
         return track[f]().catch(error => {
373 374
             console.error(`set track ${f} failed`, error);
374 375
 
376
+            if (navigator.product === 'ReactNative') {
377
+                // Synchronizing the state of base/tracks into the state of
378
+                // base/media is not required in React (and, respectively, React
379
+                // Native) because base/media expresses the app's and the user's
380
+                // desires/expectations/intents and base/tracks expresses
381
+                // practice/reality. Unfortunately, the old Web does not comply
382
+                // and/or does the opposite.
383
+                return;
384
+            }
385
+
375 386
             const setMuted
376 387
                 = track.mediaType === MEDIA_TYPE.AUDIO
377 388
                     ? setAudioMuted
378 389
                     : setVideoMuted;
379 390
 
391
+            // FIXME The following disregards VIDEO_MUTISM_AUTHORITY (in the
392
+            // case of setVideoMuted, of course).
380 393
             dispatch(setMuted(!muted));
381 394
         });
382 395
     };

+ 17
- 15
react/features/base/tracks/middleware.js View File

@@ -100,35 +100,37 @@ MiddlewareRegistry.register(store => next => action => {
100 100
         break;
101 101
 
102 102
     case TRACK_UPDATED:
103
-        // TODO Remove the below calls to APP.UI once components interested in
104
-        // track mute changes are moved into react.
103
+        // TODO Remove the following calls to APP.UI once components interested
104
+        // in track mute changes are moved into React and/or redux.
105 105
         if (typeof APP !== 'undefined') {
106 106
             const { jitsiTrack } = action.track;
107
-            const isMuted = jitsiTrack.isMuted();
107
+            const muted = jitsiTrack.isMuted();
108 108
             const participantID = jitsiTrack.getParticipantId();
109 109
             const isVideoTrack = jitsiTrack.isVideoTrack();
110 110
 
111 111
             if (jitsiTrack.isLocal()) {
112 112
                 if (isVideoTrack) {
113
-                    APP.conference.videoMuted = isMuted;
113
+                    APP.conference.videoMuted = muted;
114 114
                 } else {
115
-                    APP.conference.audioMuted = isMuted;
115
+                    APP.conference.audioMuted = muted;
116 116
                 }
117 117
             }
118 118
 
119 119
             if (isVideoTrack) {
120
-                APP.UI.setVideoMuted(participantID, isMuted);
120
+                APP.UI.setVideoMuted(participantID, muted);
121 121
                 APP.UI.onPeerVideoTypeChanged(
122
-                    participantID, jitsiTrack.videoType);
122
+                    participantID,
123
+                    jitsiTrack.videoType);
123 124
             } else {
124
-                APP.UI.setAudioMuted(participantID, isMuted);
125
+                APP.UI.setAudioMuted(participantID, muted);
125 126
             }
126 127
 
127
-            // XXX This function synchronizes track states with media states.
128
-            // This is not required in React, because media is the source of
129
-            // truth, synchronization should always happen in the media -> track
130
-            // direction. The old web, however, does the opposite, hence the
131
-            // need for this.
128
+            // XXX The following synchronizes the state of base/tracks into the
129
+            // state of base/media. Which is not required in React (and,
130
+            // respectively, React Native) because base/media expresses the
131
+            // app's and the user's desires/expectations/intents and base/tracks
132
+            // expresses practice/reality. Unfortunately, the old Web does not
133
+            // comply and/or does the opposite. Hence, the following:
132 134
             return _trackUpdated(store, next, action);
133 135
         }
134 136
 
@@ -149,8 +151,8 @@ MiddlewareRegistry.register(store => next => action => {
149 151
  * @returns {Track} The local <tt>Track</tt> associated with the specified
150 152
  * <tt>mediaType</tt> in the specified <tt>store</tt>.
151 153
  */
152
-function _getLocalTrack(store, mediaType: MEDIA_TYPE) {
153
-    return getLocalTrack(store.getState()['features/base/tracks'], mediaType);
154
+function _getLocalTrack({ getState }, mediaType: MEDIA_TYPE) {
155
+    return getLocalTrack(getState()['features/base/tracks'], mediaType);
154 156
 }
155 157
 
156 158
 /**

+ 8
- 22
react/features/mobile/audio-mode/middleware.js View File

@@ -16,10 +16,10 @@ import { MiddlewareRegistry } from '../../base/redux';
16 16
  * based on the type of conference. Audio-only conferences don't use the speaker
17 17
  * by default, and video conferences do.
18 18
  *
19
- * @param {Store} store - Redux store.
19
+ * @param {Store} store - The redux store.
20 20
  * @returns {Function}
21 21
  */
22
-MiddlewareRegistry.register(store => next => action => {
22
+MiddlewareRegistry.register(({ getState }) => next => action => {
23 23
     const AudioMode = NativeModules.AudioMode;
24 24
 
25 25
     if (AudioMode) {
@@ -32,34 +32,20 @@ MiddlewareRegistry.register(store => next => action => {
32 32
             mode = AudioMode.DEFAULT;
33 33
             break;
34 34
 
35
-        case CONFERENCE_WILL_JOIN: {
36
-            const { audioOnly } = store.getState()['features/base/conference'];
37
-
38
-            mode = audioOnly ? AudioMode.AUDIO_CALL : AudioMode.VIDEO_CALL;
39
-            break;
40
-        }
41
-
35
+        case CONFERENCE_WILL_JOIN:
42 36
         case SET_AUDIO_ONLY: {
43
-            const { conference } = store.getState()['features/base/conference'];
44
-
45
-            if (conference) {
37
+            if (getState()['features/base/conference'].conference
38
+                    || action.conference) {
46 39
                 mode
47 40
                     = action.audioOnly
48
-                    ? AudioMode.AUDIO_CALL
49
-                    : AudioMode.VIDEO_CALL;
50
-            } else {
51
-                mode = null;
41
+                        ? AudioMode.AUDIO_CALL
42
+                        : AudioMode.VIDEO_CALL;
52 43
             }
53
-
54 44
             break;
55 45
         }
56
-
57
-        default:
58
-            mode = null;
59
-            break;
60 46
         }
61 47
 
62
-        if (mode !== null) {
48
+        if (typeof mode !== 'undefined') {
63 49
             AudioMode.setMode(mode)
64 50
                 .catch(err =>
65 51
                     console.error(

+ 2
- 5
react/features/mobile/background/actions.js View File

@@ -1,10 +1,7 @@
1 1
 import { setLastN } from '../../base/conference';
2 2
 import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../../base/media';
3 3
 
4
-import {
5
-    _SET_APP_STATE_LISTENER,
6
-    APP_STATE_CHANGED
7
-} from './actionTypes';
4
+import { _SET_APP_STATE_LISTENER, APP_STATE_CHANGED } from './actionTypes';
8 5
 
9 6
 /**
10 7
  * Sets the listener to be used with React Native's AppState API.
@@ -41,7 +38,7 @@ export function _setBackgroundVideoMuted(muted: boolean) {
41 38
         // for last N will be chosen automatically.
42 39
         const { audioOnly } = getState()['features/base/conference'];
43 40
 
44
-        !audioOnly && dispatch(setLastN(muted ? 0 : undefined));
41
+        audioOnly || dispatch(setLastN(muted ? 0 : undefined));
45 42
         dispatch(setVideoMuted(muted, VIDEO_MUTISM_AUTHORITY.BACKGROUND));
46 43
     };
47 44
 }

+ 16
- 29
react/features/mobile/full-screen/middleware.js View File

@@ -22,29 +22,31 @@ import { MiddlewareRegistry } from '../../base/redux';
22 22
  * In immersive mode the status and navigation bars are hidden and thus the
23 23
  * entire screen will be covered by our application.
24 24
  *
25
- * @param {Store} store - Redux store.
25
+ * @param {Store} store - The redux store.
26 26
  * @returns {Function}
27 27
  */
28
-MiddlewareRegistry.register(store => next => action => {
28
+MiddlewareRegistry.register(({ getState }) => next => action => {
29
+    const result = next(action);
30
+
29 31
     let fullScreen = null;
30 32
 
31 33
     switch (action.type) {
32
-    case APP_STATE_CHANGED: {
33
-        // Check if we just came back from the background and reenable full
34
+    case APP_STATE_CHANGED:
35
+    case CONFERENCE_WILL_JOIN:
36
+    case HIDE_DIALOG:
37
+    case SET_AUDIO_ONLY: {
38
+        // Check if we just came back from the background and re-enable full
34 39
         // screen mode if necessary.
35
-        if (action.appState === 'active') {
36
-            const { audioOnly, conference }
37
-                = store.getState()['features/base/conference'];
40
+        const { appState } = action;
38 41
 
39
-            fullScreen = conference ? !audioOnly : false;
42
+        if (typeof appState !== 'undefined' && appState !== 'active') {
43
+            break;
40 44
         }
41
-        break;
42
-    }
43 45
 
44
-    case CONFERENCE_WILL_JOIN: {
45
-        const { audioOnly } = store.getState()['features/base/conference'];
46
+        const { audioOnly, conference }
47
+            = getState()['features/base/conference'];
46 48
 
47
-        fullScreen = !audioOnly;
49
+        fullScreen = conference || action.conference ? !audioOnly : false;
48 50
         break;
49 51
     }
50 52
 
@@ -52,21 +54,6 @@ MiddlewareRegistry.register(store => next => action => {
52 54
     case CONFERENCE_LEFT:
53 55
         fullScreen = false;
54 56
         break;
55
-
56
-    case HIDE_DIALOG: {
57
-        const { audioOnly, conference }
58
-            = store.getState()['features/base/conference'];
59
-
60
-        fullScreen = conference ? !audioOnly : false;
61
-        break;
62
-    }
63
-
64
-    case SET_AUDIO_ONLY: {
65
-        const { conference } = store.getState()['features/base/conference'];
66
-
67
-        fullScreen = conference ? !action.audioOnly : false;
68
-        break;
69
-    }
70 57
     }
71 58
 
72 59
     if (fullScreen !== null) {
@@ -75,7 +62,7 @@ MiddlewareRegistry.register(store => next => action => {
75 62
                 console.warn(`Failed to set full screen mode: ${err}`));
76 63
     }
77 64
 
78
-    return next(action);
65
+    return result;
79 66
 });
80 67
 
81 68
 /**

+ 9
- 12
react/features/mobile/proximity/middleware.js View File

@@ -14,32 +14,29 @@ import { MiddlewareRegistry } from '../../base/redux';
14 14
  * the screen and disable touch controls when an object is nearby. The
15 15
  * functionality is  enabled when a conference is in audio-only mode.
16 16
  *
17
- * @param {Store} store - Redux store.
17
+ * @param {Store} store - The redux store.
18 18
  * @returns {Function}
19 19
  */
20
-MiddlewareRegistry.register(store => next => action => {
21
-    switch (action.type) {
22
-    case CONFERENCE_JOINED: {
23
-        const { audioOnly } = store.getState()['features/base/conference'];
24
-
25
-        _setProximityEnabled(audioOnly);
26
-        break;
27
-    }
20
+MiddlewareRegistry.register(({ getState }) => next => action => {
21
+    const result = next(action);
28 22
 
23
+    switch (action.type) {
29 24
     case CONFERENCE_FAILED:
30 25
     case CONFERENCE_LEFT:
31 26
         _setProximityEnabled(false);
32 27
         break;
33 28
 
29
+    case CONFERENCE_JOINED:
34 30
     case SET_AUDIO_ONLY: {
35
-        const { conference } = store.getState()['features/base/conference'];
31
+        const { audioOnly, conference }
32
+            = getState()['features/base/conference'];
36 33
 
37
-        conference && _setProximityEnabled(action.audioOnly);
34
+        conference && _setProximityEnabled(audioOnly);
38 35
         break;
39 36
     }
40 37
     }
41 38
 
42
-    return next(action);
39
+    return result;
43 40
 });
44 41
 
45 42
 /**

+ 55
- 14
react/features/toolbox/components/Toolbox.native.js View File

@@ -3,7 +3,12 @@ import { View } from 'react-native';
3 3
 import { connect } from 'react-redux';
4 4
 
5 5
 import { toggleAudioOnly } from '../../base/conference';
6
-import { MEDIA_TYPE, toggleCameraFacingMode } from '../../base/media';
6
+import {
7
+    MEDIA_TYPE,
8
+    setAudioMuted,
9
+    setVideoMuted,
10
+    toggleCameraFacingMode
11
+} from '../../base/media';
7 12
 import { Container } from '../../base/react';
8 13
 import { ColorPalette } from '../../base/styles';
9 14
 import { beginRoomLockRequest } from '../../room-lock';
@@ -56,11 +61,6 @@ class Toolbox extends Component {
56 61
          */
57 62
         _onShareRoom: React.PropTypes.func,
58 63
 
59
-        /**
60
-         * Handler for toggle audio.
61
-         */
62
-        _onToggleAudio: React.PropTypes.func,
63
-
64 64
         /**
65 65
          * Toggles the audio-only flag of the conference.
66 66
          */
@@ -72,11 +72,6 @@ class Toolbox extends Component {
72 72
          */
73 73
         _onToggleCameraFacingMode: React.PropTypes.func,
74 74
 
75
-        /**
76
-         * Handler for toggling video.
77
-         */
78
-        _onToggleVideo: React.PropTypes.func,
79
-
80 75
         /**
81 76
          * Flag showing whether video is muted.
82 77
          */
@@ -85,9 +80,25 @@ class Toolbox extends Component {
85 80
         /**
86 81
          * Flag showing whether toolbar is visible.
87 82
          */
88
-        _visible: React.PropTypes.bool
83
+        _visible: React.PropTypes.bool,
84
+
85
+        dispatch: React.PropTypes.func
89 86
     };
90 87
 
88
+    /**
89
+     * Initializes a new {@code Toolbox} instance.
90
+     *
91
+     * @param {Object} props - The read-only React {@code Component} props with
92
+     * which the new instance is to be initialized.
93
+     */
94
+    constructor(props) {
95
+        super(props);
96
+
97
+        // Bind event handlers so they are only bound once per instance.
98
+        this._onToggleAudio = this._onToggleAudio.bind(this);
99
+        this._onToggleVideo = this._onToggleVideo.bind(this);
100
+    }
101
+
91 102
     /**
92 103
      * Implements React's {@link Component#render()}.
93 104
      *
@@ -144,6 +155,36 @@ class Toolbox extends Component {
144 155
         };
145 156
     }
146 157
 
158
+    /**
159
+     * Dispatches an action to toggle the mute state of the audio/microphone.
160
+     *
161
+     * @private
162
+     * @returns {void}
163
+     */
164
+    _onToggleAudio() {
165
+        // The user sees the reality i.e. the state of base/tracks and intends
166
+        // to change reality by tapping on the respective button i.e. the user
167
+        // sets the state of base/media. Whether the user's intention will turn
168
+        // into reality is a whole different story which is of no concern to the
169
+        // tapping.
170
+        this.props.dispatch(setAudioMuted(!this.props._audioMuted));
171
+    }
172
+
173
+    /**
174
+     * Dispatches an action to toggle the mute state of the video/camera.
175
+     *
176
+     * @private
177
+     * @returns {void}
178
+     */
179
+    _onToggleVideo() {
180
+        // The user sees the reality i.e. the state of base/tracks and intends
181
+        // to change reality by tapping on the respective button i.e. the user
182
+        // sets the state of base/media. Whether the user's intention will turn
183
+        // into reality is a whole different story which is of no concern to the
184
+        // tapping.
185
+        this.props.dispatch(setVideoMuted(!this.props._videoMuted));
186
+    }
187
+
147 188
     /**
148 189
      * Renders the toolbar which contains the primary buttons such as hangup,
149 190
      * audio and video mute.
@@ -162,7 +203,7 @@ class Toolbox extends Component {
162 203
                 <ToolbarButton
163 204
                     iconName = { audioButtonStyles.iconName }
164 205
                     iconStyle = { audioButtonStyles.iconStyle }
165
-                    onClick = { this.props._onToggleAudio }
206
+                    onClick = { this._onToggleAudio }
166 207
                     style = { audioButtonStyles.style } />
167 208
                 <ToolbarButton
168 209
                     iconName = 'hangup'
@@ -174,7 +215,7 @@ class Toolbox extends Component {
174 215
                     disabled = { this.props._audioOnly }
175 216
                     iconName = { videoButtonStyles.iconName }
176 217
                     iconStyle = { videoButtonStyles.iconStyle }
177
-                    onClick = { this.props._onToggleVideo }
218
+                    onClick = { this._onToggleVideo }
178 219
                     style = { videoButtonStyles.style } />
179 220
             </View>
180 221
         );

+ 5
- 24
react/features/toolbox/functions.native.js View File

@@ -3,7 +3,6 @@
3 3
 import type { Dispatch } from 'redux';
4 4
 
5 5
 import { appNavigate } from '../app';
6
-import { toggleAudioMuted, toggleVideoMuted } from '../base/media';
7 6
 import { getLocalAudioTrack, getLocalVideoTrack } from '../base/tracks';
8 7
 
9 8
 /**
@@ -19,6 +18,11 @@ import { getLocalAudioTrack, getLocalVideoTrack } from '../base/tracks';
19 18
  */
20 19
 export function abstractMapDispatchToProps(dispatch: Dispatch<*>): Object {
21 20
     return {
21
+        // Inject {@code dispatch} into the React Component's props in case it
22
+        // needs to dispatch an action in the redux store without
23
+        // {@code mapDispatchToProps}.
24
+        dispatch,
25
+
22 26
         /**
23 27
          * Dispatches action to leave the current conference.
24 28
          *
@@ -33,29 +37,6 @@ export function abstractMapDispatchToProps(dispatch: Dispatch<*>): Object {
33 37
             // expression of (1) the lack of knowledge & (2) the desire to no
34 38
             // longer have a valid room name to join.
35 39
             dispatch(appNavigate(undefined));
36
-        },
37
-
38
-        /**
39
-         * Dispatches an action to toggle the mute state of the
40
-         * audio/microphone.
41
-         *
42
-         * @private
43
-         * @returns {Object} - Dispatched action.
44
-         * @type {Function}
45
-         */
46
-        _onToggleAudio() {
47
-            dispatch(toggleAudioMuted());
48
-        },
49
-
50
-        /**
51
-         * Dispatches an action to toggle the mute state of the video/camera.
52
-         *
53
-         * @private
54
-         * @returns {Object} - Dispatched action.
55
-         * @type {Function}
56
-         */
57
-        _onToggleVideo() {
58
-            dispatch(toggleVideoMuted());
59 40
         }
60 41
     };
61 42
 }

Loading…
Cancel
Save