Browse Source

ref(no-data-from-source): logic.

master
Hristo Terezov 6 years ago
parent
commit
2861198251

+ 2
- 2
lang/main.json View File

214
         "maxUsersLimitReachedTitle": "Maximum members limit reached",
214
         "maxUsersLimitReachedTitle": "Maximum members limit reached",
215
         "micConstraintFailedError": "Your microphone does not satisfy some of the required constraints.",
215
         "micConstraintFailedError": "Your microphone does not satisfy some of the required constraints.",
216
         "micNotFoundError": "Microphone was not found.",
216
         "micNotFoundError": "Microphone was not found.",
217
-        "micNotSendingData": "We are unable to access your microphone. Please select another device from the settings menu or try to reload the application.",
218
-        "micNotSendingDataTitle": "Unable to access microphone",
217
+        "micNotSendingData": "Go to your computer's settings to unmute your mic and adjust its level",
218
+        "micNotSendingDataTitle": "Your mic is muted by your system settings",
219
         "micPermissionDeniedError": "You have not granted permission to use your microphone. You can still join the conference but others won't hear you. Use the camera button in the address bar to fix this.",
219
         "micPermissionDeniedError": "You have not granted permission to use your microphone. You can still join the conference but others won't hear you. Use the camera button in the address bar to fix this.",
220
         "micUnknownError": "Cannot use microphone for an unknown reason.",
220
         "micUnknownError": "Cannot use microphone for an unknown reason.",
221
         "muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
221
         "muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",

+ 0
- 18
modules/UI/UI.js View File

731
     });
731
     });
732
 };
732
 };
733
 
733
 
734
-/**
735
- * Shows error dialog that informs the user that no data is received from the
736
- * device.
737
- *
738
- * @param {boolean} isAudioTrack - Whether or not the dialog is for an audio
739
- * track error.
740
- * @returns {void}
741
- */
742
-UI.showTrackNotWorkingDialog = function(isAudioTrack) {
743
-    messageHandler.showError({
744
-        descriptionKey: isAudioTrack
745
-            ? 'dialog.micNotSendingData' : 'dialog.cameraNotSendingData',
746
-        titleKey: isAudioTrack
747
-            ? 'dialog.micNotSendingDataTitle'
748
-            : 'dialog.cameraNotSendingDataTitle'
749
-    });
750
-};
751
-
752
 UI.updateDevicesAvailability = function(id, devices) {
734
 UI.updateDevicesAvailability = function(id, devices) {
753
     VideoLayout.setDeviceAvailabilityIcons(id, devices);
735
     VideoLayout.setDeviceAvailabilityIcons(id, devices);
754
 };
736
 };

+ 2
- 2
package-lock.json View File

8946
       }
8946
       }
8947
     },
8947
     },
8948
     "lib-jitsi-meet": {
8948
     "lib-jitsi-meet": {
8949
-      "version": "github:jitsi/lib-jitsi-meet#afa08b4ea5e81475aa7dcb1a418111e5a281edff",
8950
-      "from": "github:jitsi/lib-jitsi-meet#afa08b4ea5e81475aa7dcb1a418111e5a281edff",
8949
+      "version": "github:jitsi/lib-jitsi-meet#40446bb948823468acd284a5a70bfd8bd7a086f1",
8950
+      "from": "github:jitsi/lib-jitsi-meet#40446bb948823468acd284a5a70bfd8bd7a086f1",
8951
       "requires": {
8951
       "requires": {
8952
         "@jitsi/sdp-interop": "0.1.14",
8952
         "@jitsi/sdp-interop": "0.1.14",
8953
         "@jitsi/sdp-simulcast": "0.2.1",
8953
         "@jitsi/sdp-simulcast": "0.2.1",

+ 1
- 1
package.json View File

52
     "js-utils": "github:jitsi/js-utils#73a67a7a60d52f8e895f50939c8fcbd1f20fe7b5",
52
     "js-utils": "github:jitsi/js-utils#73a67a7a60d52f8e895f50939c8fcbd1f20fe7b5",
53
     "jsrsasign": "8.0.12",
53
     "jsrsasign": "8.0.12",
54
     "jwt-decode": "2.2.0",
54
     "jwt-decode": "2.2.0",
55
-    "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#afa08b4ea5e81475aa7dcb1a418111e5a281edff",
55
+    "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#40446bb948823468acd284a5a70bfd8bd7a086f1",
56
     "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
56
     "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
57
     "lodash": "4.17.11",
57
     "lodash": "4.17.11",
58
     "moment": "2.19.4",
58
     "moment": "2.19.4",

+ 10
- 0
react/features/base/tracks/actionTypes.js View File

43
  */
43
  */
44
 export const TRACK_CREATE_ERROR = 'TRACK_CREATE_ERROR';
44
 export const TRACK_CREATE_ERROR = 'TRACK_CREATE_ERROR';
45
 
45
 
46
+/**
47
+ * The type of redux action dispatched when a track has triggered no data from source event.
48
+ *
49
+ * {
50
+ *     type: TRACK_NO_DATA_FROM_SOURCE,
51
+ *     track: Track
52
+ * }
53
+ */
54
+export const TRACK_NO_DATA_FROM_SOURCE = 'TRACK_NO_DATA_FROM_SOURCE';
55
+
46
 /**
56
 /**
47
  * The type of redux action dispatched when a track has been (locally or
57
  * The type of redux action dispatched when a track has been (locally or
48
  * remotely) removed from the conference.
58
  * remotely) removed from the conference.

+ 98
- 3
react/features/base/tracks/actions.js View File

3
     sendAnalytics
3
     sendAnalytics
4
 } from '../../analytics';
4
 } from '../../analytics';
5
 import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
5
 import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
6
+import { showErrorNotification, showNotification } from '../../notifications';
6
 import {
7
 import {
7
     CAMERA_FACING_MODE,
8
     CAMERA_FACING_MODE,
8
     MEDIA_TYPE,
9
     MEDIA_TYPE,
17
     TRACK_ADDED,
18
     TRACK_ADDED,
18
     TRACK_CREATE_CANCELED,
19
     TRACK_CREATE_CANCELED,
19
     TRACK_CREATE_ERROR,
20
     TRACK_CREATE_ERROR,
21
+    TRACK_NO_DATA_FROM_SOURCE,
20
     TRACK_REMOVED,
22
     TRACK_REMOVED,
21
     TRACK_UPDATED,
23
     TRACK_UPDATED,
22
     TRACK_WILL_CREATE
24
     TRACK_WILL_CREATE
23
 } from './actionTypes';
25
 } from './actionTypes';
24
-import { createLocalTracksF, getLocalTrack, getLocalTracks } from './functions';
26
+import { createLocalTracksF, getLocalTrack, getLocalTracks, getTrackByJitsiTrack } from './functions';
25
 
27
 
26
 const logger = require('jitsi-meet-logger').getLogger(__filename);
28
 const logger = require('jitsi-meet-logger').getLogger(__filename);
27
 
29
 
189
     };
191
     };
190
 }
192
 }
191
 
193
 
194
+/**
195
+ * Signals that the passed JitsiLocalTrack has triggered a no data from source event.
196
+ *
197
+ * @param {JitsiLocalTrack} track - The track.
198
+ * @returns {{
199
+*     type: TRACK_NO_DATA_FROM_SOURCE,
200
+*     track: Track
201
+* }}
202
+*/
203
+export function noDataFromSource(track) {
204
+    return {
205
+        type: TRACK_NO_DATA_FROM_SOURCE,
206
+        track
207
+    };
208
+}
209
+
210
+/**
211
+ * Displays a no data from source video error if needed.
212
+ *
213
+ * @param {JitsiLocalTrack} jitsiTrack - The track.
214
+ * @returns {Function}
215
+ */
216
+export function showNoDataFromSourceVideoError(jitsiTrack) {
217
+    return (dispatch, getState) => {
218
+        let notificationInfo;
219
+
220
+        const track = getTrackByJitsiTrack(getState()['features/base/tracks'], jitsiTrack);
221
+
222
+        if (!track) {
223
+            return;
224
+        }
225
+
226
+        if (track.isReceivingData) {
227
+            notificationInfo = undefined;
228
+        } else {
229
+            const notificationAction = showErrorNotification({
230
+                descriptionKey: 'dialog.cameraNotSendingData',
231
+                titleKey: 'dialog.cameraNotSendingDataTitle'
232
+            });
233
+
234
+            dispatch(notificationAction);
235
+            notificationInfo = {
236
+                uid: notificationAction.uid
237
+            };
238
+        }
239
+        dispatch(trackNoDataFromSourceNotificationInfoChanged(jitsiTrack, notificationInfo));
240
+    };
241
+}
242
+
192
 /**
243
 /**
193
  * Signals that the local participant is ending screensharing or beginning the
244
  * Signals that the local participant is ending screensharing or beginning the
194
  * screensharing flow.
245
  * screensharing flow.
288
 
339
 
289
         // participantId
340
         // participantId
290
         const local = track.isLocal();
341
         const local = track.isLocal();
291
-        let participantId;
342
+        const mediaType = track.getType();
343
+        let isReceivingData, noDataFromSourceNotificationInfo, participantId;
292
 
344
 
293
         if (local) {
345
         if (local) {
294
             const participant = getLocalParticipant(getState);
346
             const participant = getLocalParticipant(getState);
296
             if (participant) {
348
             if (participant) {
297
                 participantId = participant.id;
349
                 participantId = participant.id;
298
             }
350
             }
351
+
352
+            isReceivingData = track.isReceivingData();
353
+            track.on(JitsiTrackEvents.NO_DATA_FROM_SOURCE, () => dispatch(noDataFromSource({ jitsiTrack: track })));
354
+            if (!isReceivingData) {
355
+                if (mediaType === MEDIA_TYPE.AUDIO) {
356
+                    const notificationAction = showNotification({
357
+                        descriptionKey: 'dialog.micNotSendingData',
358
+                        titleKey: 'dialog.micNotSendingDataTitle'
359
+                    });
360
+
361
+                    dispatch(notificationAction);
362
+                    noDataFromSourceNotificationInfo = { uid: notificationAction.uid };
363
+                } else {
364
+                    const timeout = setTimeout(() => dispatch(showNoDataFromSourceVideoError(track)), 5000);
365
+
366
+                    noDataFromSourceNotificationInfo = { timeout };
367
+                }
368
+
369
+            }
299
         } else {
370
         } else {
300
             participantId = track.getParticipantId();
371
             participantId = track.getParticipantId();
372
+            isReceivingData = true;
301
         }
373
         }
302
 
374
 
303
         return dispatch({
375
         return dispatch({
304
             type: TRACK_ADDED,
376
             type: TRACK_ADDED,
305
             track: {
377
             track: {
306
                 jitsiTrack: track,
378
                 jitsiTrack: track,
379
+                isReceivingData,
307
                 local,
380
                 local,
308
-                mediaType: track.getType(),
381
+                mediaType,
309
                 mirror: _shouldMirror(track),
382
                 mirror: _shouldMirror(track),
310
                 muted: track.isMuted(),
383
                 muted: track.isMuted(),
384
+                noDataFromSourceNotificationInfo,
311
                 participantId,
385
                 participantId,
312
                 videoStarted: false,
386
                 videoStarted: false,
313
                 videoType: track.videoType
387
                 videoType: track.videoType
336
     };
410
     };
337
 }
411
 }
338
 
412
 
413
+/**
414
+ * Create an action for when a track's no data from source notification information changes.
415
+ *
416
+ * @param {JitsiLocalTrack} track - JitsiTrack instance.
417
+ * @param {Object} noDataFromSourceNotificationInfo - Information about no data from source notification.
418
+ * @returns {{
419
+ *     type: TRACK_UPDATED,
420
+ *     track: Track
421
+ * }}
422
+ */
423
+export function trackNoDataFromSourceNotificationInfoChanged(track, noDataFromSourceNotificationInfo) {
424
+    return {
425
+        type: TRACK_UPDATED,
426
+        track: {
427
+            jitsiTrack: track,
428
+            noDataFromSourceNotificationInfo
429
+        }
430
+    };
431
+}
432
+
339
 /**
433
 /**
340
  * Create an action for when a track has been signaled for removal from the
434
  * Create an action for when a track has been signaled for removal from the
341
  * conference.
435
  * conference.
349
 export function trackRemoved(track) {
443
 export function trackRemoved(track) {
350
     track.removeAllListeners(JitsiTrackEvents.TRACK_MUTE_CHANGED);
444
     track.removeAllListeners(JitsiTrackEvents.TRACK_MUTE_CHANGED);
351
     track.removeAllListeners(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED);
445
     track.removeAllListeners(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED);
446
+    track.removeAllListeners(JitsiTrackEvents.NO_DATA_FROM_SOURCE);
352
 
447
 
353
     return {
448
     return {
354
         type: TRACK_REMOVED,
449
         type: TRACK_REMOVED,

+ 1
- 17
react/features/base/tracks/functions.js View File

1
 /* global APP */
1
 /* global APP */
2
 
2
 
3
-import JitsiMeetJS, { JitsiTrackErrors, JitsiTrackEvents }
4
-    from '../lib-jitsi-meet';
3
+import JitsiMeetJS, { JitsiTrackErrors } from '../lib-jitsi-meet';
5
 import { MEDIA_TYPE } from '../media';
4
 import { MEDIA_TYPE } from '../media';
6
 import {
5
 import {
7
     getUserSelectedCameraDeviceId,
6
     getUserSelectedCameraDeviceId,
77
                 resolution
76
                 resolution
78
             },
77
             },
79
             firePermissionPromptIsShownEvent)
78
             firePermissionPromptIsShownEvent)
80
-        .then(tracks => {
81
-            // TODO JitsiTrackEvents.NO_DATA_FROM_SOURCE should probably be
82
-            // dispatched in the redux store here and then
83
-            // APP.UI.showTrackNotWorkingDialog should be in a middleware
84
-            // somewhere else.
85
-            if (typeof APP !== 'undefined') {
86
-                tracks.forEach(track =>
87
-                    track.on(
88
-                        JitsiTrackEvents.NO_DATA_FROM_SOURCE,
89
-                        APP.UI.showTrackNotWorkingDialog.bind(
90
-                            null, track.isAudioTrack())));
91
-            }
92
-
93
-            return tracks;
94
-        })
95
         .catch(err => {
79
         .catch(err => {
96
             logger.error('Failed to create local tracks', options.devices, err);
80
             logger.error('Failed to create local tracks', options.devices, err);
97
 
81
 

+ 84
- 2
react/features/base/tracks/middleware.js View File

9
     TOGGLE_CAMERA_FACING_MODE,
9
     TOGGLE_CAMERA_FACING_MODE,
10
     toggleCameraFacingMode
10
     toggleCameraFacingMode
11
 } from '../media';
11
 } from '../media';
12
+import { hideNotification } from '../../notifications';
12
 import { MiddlewareRegistry } from '../redux';
13
 import { MiddlewareRegistry } from '../redux';
13
 import UIEvents from '../../../../service/UI/UIEvents';
14
 import UIEvents from '../../../../service/UI/UIEvents';
14
 
15
 
15
-import { createLocalTracksA } from './actions';
16
+import {
17
+    createLocalTracksA,
18
+    showNoDataFromSourceVideoError,
19
+    trackNoDataFromSourceNotificationInfoChanged
20
+} from './actions';
16
 import {
21
 import {
17
     TOGGLE_SCREENSHARING,
22
     TOGGLE_SCREENSHARING,
23
+    TRACK_NO_DATA_FROM_SOURCE,
24
+    TRACK_REMOVED,
18
     TRACK_UPDATED
25
     TRACK_UPDATED
19
 } from './actionTypes';
26
 } from './actionTypes';
20
-import { getLocalTrack, setTrackMuted } from './functions';
27
+import { getLocalTrack, getTrackByJitsiTrack, setTrackMuted } from './functions';
21
 
28
 
22
 declare var APP: Object;
29
 declare var APP: Object;
23
 
30
 
31
  */
38
  */
32
 MiddlewareRegistry.register(store => next => action => {
39
 MiddlewareRegistry.register(store => next => action => {
33
     switch (action.type) {
40
     switch (action.type) {
41
+    case TRACK_NO_DATA_FROM_SOURCE: {
42
+        const result = next(action);
43
+
44
+        _handleNoDataFromSourceErrors(store, action);
45
+
46
+        return result;
47
+    }
48
+    case TRACK_REMOVED: {
49
+        _removeNoDataFromSourceNotification(store, action.track);
50
+        break;
51
+    }
34
     case SET_AUDIO_MUTED:
52
     case SET_AUDIO_MUTED:
35
         _setMuted(store, action, MEDIA_TYPE.AUDIO);
53
         _setMuted(store, action, MEDIA_TYPE.AUDIO);
36
         break;
54
         break;
121
     return next(action);
139
     return next(action);
122
 });
140
 });
123
 
141
 
142
+/**
143
+ * Handles no data from source errors.
144
+ *
145
+ * @param {Store} store - The redux store in which the specified action is
146
+ * dispatched.
147
+ * @param {Action} action - The redux action dispatched in the specified store.
148
+ * @private
149
+ * @returns {void}
150
+ */
151
+function _handleNoDataFromSourceErrors(store, action) {
152
+    const { getState, dispatch } = store;
153
+
154
+    const track = getTrackByJitsiTrack(getState()['features/base/tracks'], action.track.jitsiTrack);
155
+
156
+    if (!track || !track.local) {
157
+        return;
158
+    }
159
+
160
+    const { jitsiTrack } = track;
161
+
162
+    if (track.mediaType === MEDIA_TYPE.AUDIO && track.isReceivingData) {
163
+        _removeNoDataFromSourceNotification(store, action.track);
164
+    }
165
+
166
+    if (track.mediaType === MEDIA_TYPE.VIDEO) {
167
+        const { noDataFromSourceNotificationInfo = {} } = track;
168
+
169
+        if (track.isReceivingData) {
170
+            if (noDataFromSourceNotificationInfo.timeout) {
171
+                clearTimeout(noDataFromSourceNotificationInfo.timeout);
172
+                dispatch(trackNoDataFromSourceNotificationInfoChanged(jitsiTrack, undefined));
173
+            }
174
+
175
+            // try to remove the notification if there is one.
176
+            _removeNoDataFromSourceNotification(store, action.track);
177
+        } else {
178
+            if (noDataFromSourceNotificationInfo.timeout) {
179
+                return;
180
+            }
181
+
182
+            const timeout = setTimeout(() => dispatch(showNoDataFromSourceVideoError(jitsiTrack)), 5000);
183
+
184
+            dispatch(trackNoDataFromSourceNotificationInfoChanged(jitsiTrack, { timeout }));
185
+        }
186
+    }
187
+}
188
+
124
 /**
189
 /**
125
  * Gets the local track associated with a specific {@code MEDIA_TYPE} in a
190
  * Gets the local track associated with a specific {@code MEDIA_TYPE} in a
126
  * specific redux store.
191
  * specific redux store.
149
             includePending));
214
             includePending));
150
 }
215
 }
151
 
216
 
217
+/**
218
+ * Removes the no data from source notification associated with the JitsiTrack if displayed.
219
+ *
220
+ * @param {Store} store - The redux store.
221
+ * @param {Track} track - The redux action dispatched in the specified store.
222
+ * @returns {void}
223
+ */
224
+function _removeNoDataFromSourceNotification({ getState, dispatch }, track) {
225
+    const t = getTrackByJitsiTrack(getState()['features/base/tracks'], track.jitsiTrack);
226
+    const { jitsiTrack, noDataFromSourceNotificationInfo = {} } = t || {};
227
+
228
+    if (noDataFromSourceNotificationInfo && noDataFromSourceNotificationInfo.uid) {
229
+        dispatch(hideNotification(noDataFromSourceNotificationInfo.uid));
230
+        dispatch(trackNoDataFromSourceNotificationInfoChanged(jitsiTrack, undefined));
231
+    }
232
+}
233
+
152
 /**
234
 /**
153
  * Mutes or unmutes a local track with a specific media type.
235
  * Mutes or unmutes a local track with a specific media type.
154
  *
236
  *

+ 17
- 0
react/features/base/tracks/reducer.js View File

5
     TRACK_ADDED,
5
     TRACK_ADDED,
6
     TRACK_CREATE_CANCELED,
6
     TRACK_CREATE_CANCELED,
7
     TRACK_CREATE_ERROR,
7
     TRACK_CREATE_ERROR,
8
+    TRACK_NO_DATA_FROM_SOURCE,
8
     TRACK_REMOVED,
9
     TRACK_REMOVED,
9
     TRACK_UPDATED,
10
     TRACK_UPDATED,
10
     TRACK_WILL_CREATE
11
     TRACK_WILL_CREATE
75
         }
76
         }
76
         break;
77
         break;
77
     }
78
     }
79
+    case TRACK_NO_DATA_FROM_SOURCE: {
80
+        const t = action.track;
81
+
82
+        if (state.jitsiTrack === t.jitsiTrack) {
83
+            const isReceivingData = t.jitsiTrack.isReceivingData();
84
+
85
+            if (state.isReceivingData !== isReceivingData) {
86
+                return {
87
+                    ...state,
88
+                    isReceivingData
89
+                };
90
+            }
91
+        }
92
+        break;
93
+    }
78
     }
94
     }
79
 
95
 
80
     return state;
96
     return state;
86
 ReducerRegistry.register('features/base/tracks', (state = [], action) => {
102
 ReducerRegistry.register('features/base/tracks', (state = [], action) => {
87
     switch (action.type) {
103
     switch (action.type) {
88
     case PARTICIPANT_ID_CHANGED:
104
     case PARTICIPANT_ID_CHANGED:
105
+    case TRACK_NO_DATA_FROM_SOURCE:
89
     case TRACK_UPDATED:
106
     case TRACK_UPDATED:
90
         return state.map(t => track(t, action));
107
         return state.map(t => track(t, action));
91
 
108
 

Loading…
Cancel
Save