瀏覽代碼

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

master
Hristo Terezov 6 年之前
父節點
當前提交
2861198251

+ 2
- 2
lang/main.json 查看文件

@@ -214,8 +214,8 @@
214 214
         "maxUsersLimitReachedTitle": "Maximum members limit reached",
215 215
         "micConstraintFailedError": "Your microphone does not satisfy some of the required constraints.",
216 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 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 220
         "micUnknownError": "Cannot use microphone for an unknown reason.",
221 221
         "muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",

+ 0
- 18
modules/UI/UI.js 查看文件

@@ -731,24 +731,6 @@ UI.showExtensionInlineInstallationDialog = function(callback) {
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 734
 UI.updateDevicesAvailability = function(id, devices) {
753 735
     VideoLayout.setDeviceAvailabilityIcons(id, devices);
754 736
 };

+ 2
- 2
package-lock.json 查看文件

@@ -8946,8 +8946,8 @@
8946 8946
       }
8947 8947
     },
8948 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 8951
       "requires": {
8952 8952
         "@jitsi/sdp-interop": "0.1.14",
8953 8953
         "@jitsi/sdp-simulcast": "0.2.1",

+ 1
- 1
package.json 查看文件

@@ -52,7 +52,7 @@
52 52
     "js-utils": "github:jitsi/js-utils#73a67a7a60d52f8e895f50939c8fcbd1f20fe7b5",
53 53
     "jsrsasign": "8.0.12",
54 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 56
     "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
57 57
     "lodash": "4.17.11",
58 58
     "moment": "2.19.4",

+ 10
- 0
react/features/base/tracks/actionTypes.js 查看文件

@@ -43,6 +43,16 @@ export const TRACK_CREATE_CANCELED = 'TRACK_CREATE_CANCELED';
43 43
  */
44 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 57
  * The type of redux action dispatched when a track has been (locally or
48 58
  * remotely) removed from the conference.

+ 98
- 3
react/features/base/tracks/actions.js 查看文件

@@ -3,6 +3,7 @@ import {
3 3
     sendAnalytics
4 4
 } from '../../analytics';
5 5
 import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
6
+import { showErrorNotification, showNotification } from '../../notifications';
6 7
 import {
7 8
     CAMERA_FACING_MODE,
8 9
     MEDIA_TYPE,
@@ -17,11 +18,12 @@ import {
17 18
     TRACK_ADDED,
18 19
     TRACK_CREATE_CANCELED,
19 20
     TRACK_CREATE_ERROR,
21
+    TRACK_NO_DATA_FROM_SOURCE,
20 22
     TRACK_REMOVED,
21 23
     TRACK_UPDATED,
22 24
     TRACK_WILL_CREATE
23 25
 } from './actionTypes';
24
-import { createLocalTracksF, getLocalTrack, getLocalTracks } from './functions';
26
+import { createLocalTracksF, getLocalTrack, getLocalTracks, getTrackByJitsiTrack } from './functions';
25 27
 
26 28
 const logger = require('jitsi-meet-logger').getLogger(__filename);
27 29
 
@@ -189,6 +191,55 @@ export function destroyLocalTracks() {
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 244
  * Signals that the local participant is ending screensharing or beginning the
194 245
  * screensharing flow.
@@ -288,7 +339,8 @@ export function trackAdded(track) {
288 339
 
289 340
         // participantId
290 341
         const local = track.isLocal();
291
-        let participantId;
342
+        const mediaType = track.getType();
343
+        let isReceivingData, noDataFromSourceNotificationInfo, participantId;
292 344
 
293 345
         if (local) {
294 346
             const participant = getLocalParticipant(getState);
@@ -296,18 +348,40 @@ export function trackAdded(track) {
296 348
             if (participant) {
297 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 370
         } else {
300 371
             participantId = track.getParticipantId();
372
+            isReceivingData = true;
301 373
         }
302 374
 
303 375
         return dispatch({
304 376
             type: TRACK_ADDED,
305 377
             track: {
306 378
                 jitsiTrack: track,
379
+                isReceivingData,
307 380
                 local,
308
-                mediaType: track.getType(),
381
+                mediaType,
309 382
                 mirror: _shouldMirror(track),
310 383
                 muted: track.isMuted(),
384
+                noDataFromSourceNotificationInfo,
311 385
                 participantId,
312 386
                 videoStarted: false,
313 387
                 videoType: track.videoType
@@ -336,6 +410,26 @@ export function trackMutedChanged(track) {
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 434
  * Create an action for when a track has been signaled for removal from the
341 435
  * conference.
@@ -349,6 +443,7 @@ export function trackMutedChanged(track) {
349 443
 export function trackRemoved(track) {
350 444
     track.removeAllListeners(JitsiTrackEvents.TRACK_MUTE_CHANGED);
351 445
     track.removeAllListeners(JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED);
446
+    track.removeAllListeners(JitsiTrackEvents.NO_DATA_FROM_SOURCE);
352 447
 
353 448
     return {
354 449
         type: TRACK_REMOVED,

+ 1
- 17
react/features/base/tracks/functions.js 查看文件

@@ -1,7 +1,6 @@
1 1
 /* global APP */
2 2
 
3
-import JitsiMeetJS, { JitsiTrackErrors, JitsiTrackEvents }
4
-    from '../lib-jitsi-meet';
3
+import JitsiMeetJS, { JitsiTrackErrors } from '../lib-jitsi-meet';
5 4
 import { MEDIA_TYPE } from '../media';
6 5
 import {
7 6
     getUserSelectedCameraDeviceId,
@@ -77,21 +76,6 @@ export function createLocalTracksF(
77 76
                 resolution
78 77
             },
79 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 79
         .catch(err => {
96 80
             logger.error('Failed to create local tracks', options.devices, err);
97 81
 

+ 84
- 2
react/features/base/tracks/middleware.js 查看文件

@@ -9,15 +9,22 @@ import {
9 9
     TOGGLE_CAMERA_FACING_MODE,
10 10
     toggleCameraFacingMode
11 11
 } from '../media';
12
+import { hideNotification } from '../../notifications';
12 13
 import { MiddlewareRegistry } from '../redux';
13 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 21
 import {
17 22
     TOGGLE_SCREENSHARING,
23
+    TRACK_NO_DATA_FROM_SOURCE,
24
+    TRACK_REMOVED,
18 25
     TRACK_UPDATED
19 26
 } from './actionTypes';
20
-import { getLocalTrack, setTrackMuted } from './functions';
27
+import { getLocalTrack, getTrackByJitsiTrack, setTrackMuted } from './functions';
21 28
 
22 29
 declare var APP: Object;
23 30
 
@@ -31,6 +38,17 @@ declare var APP: Object;
31 38
  */
32 39
 MiddlewareRegistry.register(store => next => action => {
33 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 52
     case SET_AUDIO_MUTED:
35 53
         _setMuted(store, action, MEDIA_TYPE.AUDIO);
36 54
         break;
@@ -121,6 +139,53 @@ MiddlewareRegistry.register(store => next => action => {
121 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 190
  * Gets the local track associated with a specific {@code MEDIA_TYPE} in a
126 191
  * specific redux store.
@@ -149,6 +214,23 @@ function _getLocalTrack(
149 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 235
  * Mutes or unmutes a local track with a specific media type.
154 236
  *

+ 17
- 0
react/features/base/tracks/reducer.js 查看文件

@@ -5,6 +5,7 @@ import {
5 5
     TRACK_ADDED,
6 6
     TRACK_CREATE_CANCELED,
7 7
     TRACK_CREATE_ERROR,
8
+    TRACK_NO_DATA_FROM_SOURCE,
8 9
     TRACK_REMOVED,
9 10
     TRACK_UPDATED,
10 11
     TRACK_WILL_CREATE
@@ -75,6 +76,21 @@ function track(state, action) {
75 76
         }
76 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 96
     return state;
@@ -86,6 +102,7 @@ function track(state, action) {
86 102
 ReducerRegistry.register('features/base/tracks', (state = [], action) => {
87 103
     switch (action.type) {
88 104
     case PARTICIPANT_ID_CHANGED:
105
+    case TRACK_NO_DATA_FROM_SOURCE:
89 106
     case TRACK_UPDATED:
90 107
         return state.map(t => track(t, action));
91 108
 

Loading…
取消
儲存