浏览代码

fix(filmstrip): Move thumbnails reordering behind a config.js flag.

enableThumbnailReordering flag (enabled by default) will be used to check if the thumbnails needs to be reodred in the UI.
master
Jaya Allamsetty 3 年前
父节点
当前提交
7827c3d1ad

+ 60
- 35
react/features/base/participants/reducer.js 查看文件

@@ -61,7 +61,7 @@ const DEFAULT_STATE = {
61 61
     pinnedParticipant: undefined,
62 62
     remote: new Map(),
63 63
     sortedRemoteParticipants: new Map(),
64
-    speakersList: []
64
+    speakersList: new Map()
65 65
 };
66 66
 
67 67
 /**
@@ -96,12 +96,28 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
96 96
     case DOMINANT_SPEAKER_CHANGED: {
97 97
         const { participant } = action;
98 98
         const { id, previousSpeakers = [] } = participant;
99
-        const { dominantSpeaker, local } = state;
100
-        const speakersList = [];
99
+        const { dominantSpeaker, local, speakersList } = state;
100
+        const newSpeakers = [ id, ...previousSpeakers ];
101
+        const sortedSpeakersList = Array.from(speakersList);
101 102
 
102 103
         // Update the speakers list.
103
-        id !== local?.id && speakersList.push(id);
104
-        speakersList.push(...previousSpeakers.filter(p => p !== local?.id));
104
+        for (const speaker of newSpeakers) {
105
+            if (!state.speakersList.has(speaker) && speaker !== local?.id) {
106
+                const remoteParticipant = state.remote.get(speaker);
107
+
108
+                remoteParticipant && sortedSpeakersList.push([ speaker, _getDisplayName(remoteParticipant.name) ]);
109
+            }
110
+        }
111
+
112
+        // Also check if any of the existing speakers have been kicked off the list.
113
+        for (const existingSpeaker of sortedSpeakersList.keys()) {
114
+            if (!newSpeakers.find(s => s === existingSpeaker)) {
115
+                sortedSpeakersList.filter(sortedSpeaker => sortedSpeaker[0] !== existingSpeaker);
116
+            }
117
+        }
118
+
119
+        // Keep the remote speaker list sorted alphabetically.
120
+        sortedSpeakersList.sort((a, b) => a[1].localeCompare(b[1]));
105 121
 
106 122
         // Only one dominant speaker is allowed.
107 123
         if (dominantSpeaker) {
@@ -112,7 +128,7 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
112 128
             return {
113 129
                 ...state,
114 130
                 dominantSpeaker: id,
115
-                speakersList
131
+                speakersList: new Map(sortedSpeakersList)
116 132
             };
117 133
         }
118 134
 
@@ -227,8 +243,7 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
227 243
         state.remote.set(id, participant);
228 244
 
229 245
         // Insert the new participant.
230
-        const displayName = name
231
-            ?? (typeof interfaceConfig === 'object' ? interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME : 'Fellow Jitser');
246
+        const displayName = _getDisplayName(name);
232 247
         const sortedRemoteParticipants = Array.from(state.sortedRemoteParticipants);
233 248
 
234 249
         sortedRemoteParticipants.push([ id, displayName ]);
@@ -297,7 +312,7 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
297 312
         }
298 313
 
299 314
         // Remove the participant from the list of speakers.
300
-        state.speakersList = state.speakersList.filter(speaker => speaker !== id);
315
+        state.speakersList.has(id) && state.speakersList.delete(id);
301 316
 
302 317
         if (pinnedParticipant === id) {
303 318
             state.pinnedParticipant = undefined;
@@ -314,6 +329,17 @@ ReducerRegistry.register('features/base/participants', (state = DEFAULT_STATE, a
314 329
     return state;
315 330
 });
316 331
 
332
+/**
333
+ * Returns the participant's display name, default string if display name is not set on the participant.
334
+ *
335
+ * @param {string} name - The display name of the participant.
336
+ * @returns {string}
337
+ */
338
+ function _getDisplayName(name) {
339
+    return name
340
+        ?? (typeof interfaceConfig === 'object' ? interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME : 'Fellow Jitser');
341
+}
342
+
317 343
 /**
318 344
  * Loops trough the participants in the state in order to check if all participants are moderators.
319 345
  *
@@ -335,32 +361,6 @@ function _isEveryoneModerator(state) {
335 361
     return false;
336 362
 }
337 363
 
338
-
339
-/**
340
- * Updates a specific property for a participant.
341
- *
342
- * @param {State} state - The redux state.
343
- * @param {string} id - The ID of the participant.
344
- * @param {string} property - The property to update.
345
- * @param {*} value - The new value.
346
- * @returns {boolean} - True if a participant was updated and false otherwise.
347
- */
348
-function _updateParticipantProperty(state, id, property, value) {
349
-    const { remote, local } = state;
350
-
351
-    if (remote.has(id)) {
352
-        remote.set(id, set(remote.get(id), property, value));
353
-
354
-        return true;
355
-    } else if (local?.id === id) {
356
-        state.local = set(local, property, value);
357
-
358
-        return true;
359
-    }
360
-
361
-    return false;
362
-}
363
-
364 364
 /**
365 365
  * Reducer function for a single participant.
366 366
  *
@@ -458,3 +458,28 @@ function _participantJoined({ participant }) {
458 458
         role: role || PARTICIPANT_ROLE.NONE
459 459
     };
460 460
 }
461
+
462
+/**
463
+ * Updates a specific property for a participant.
464
+ *
465
+ * @param {State} state - The redux state.
466
+ * @param {string} id - The ID of the participant.
467
+ * @param {string} property - The property to update.
468
+ * @param {*} value - The new value.
469
+ * @returns {boolean} - True if a participant was updated and false otherwise.
470
+ */
471
+ function _updateParticipantProperty(state, id, property, value) {
472
+    const { remote, local } = state;
473
+
474
+    if (remote.has(id)) {
475
+        remote.set(id, set(remote.get(id), property, value));
476
+
477
+        return true;
478
+    } else if (local?.id === id) {
479
+        state.local = set(local, property, value);
480
+
481
+        return true;
482
+    }
483
+
484
+    return false;
485
+}

+ 1
- 1
react/features/filmstrip/actions.web.js 查看文件

@@ -132,7 +132,7 @@ export function clickOnVideo(n: number) {
132 132
         const state = getState();
133 133
         const { id: localId } = getLocalParticipant(state);
134 134
 
135
-        // Use the reordered list of participants.
135
+        // Use the list that correctly represents the current order of the participants as visible in the UI.
136 136
         const { remoteParticipants } = state['features/filmstrip'];
137 137
         const participants = [ localId, ...remoteParticipants ];
138 138
         const { id, pinned } = getParticipantById(state, participants[n]);

+ 47
- 10
react/features/filmstrip/components/web/Filmstrip.js 查看文件

@@ -94,6 +94,11 @@ type Props = {
94 94
      */
95 95
     _thumbnailWidth: number,
96 96
 
97
+    /**
98
+     * Flag that indicates whether the thumbnails will be reordered.
99
+     */
100
+    _thumbnailsReordered: Boolean,
101
+
97 102
     /**
98 103
      * Additional CSS class names to add to the container of all the thumbnails.
99 104
      */
@@ -222,6 +227,33 @@ class Filmstrip extends PureComponent <Props> {
222 227
         );
223 228
     }
224 229
 
230
+    /**
231
+     * Calculates the start and stop indices based on whether the thumbnails need to be reordered in the filmstrip.
232
+     *
233
+     * @param {number} startIndex - The start index.
234
+     * @param {number} stopIndex - The stop index.
235
+     * @returns {Object}
236
+     */
237
+    _calculateIndices(startIndex, stopIndex) {
238
+        const { _currentLayout, _thumbnailsReordered } = this.props;
239
+        let start = startIndex;
240
+        let stop = stopIndex;
241
+
242
+        if (_thumbnailsReordered) {
243
+            // In tile view, the start index needs to be offset by 1 because the first thumbnail is that of the local
244
+            // endpoint. The remote participants start from index 1.
245
+            if (_currentLayout === LAYOUTS.TILE_VIEW) {
246
+                start = startIndex > 0 ? startIndex - 1 : 0;
247
+                stop = stopIndex - 1;
248
+            }
249
+        }
250
+
251
+        return {
252
+            startIndex: start,
253
+            stopIndex: stop
254
+        };
255
+    }
256
+
225 257
     _onTabIn: () => void;
226 258
 
227 259
     /**
@@ -262,18 +294,22 @@ class Filmstrip extends PureComponent <Props> {
262 294
      * @returns {string} - The key.
263 295
      */
264 296
     _gridItemKey({ columnIndex, rowIndex }) {
265
-        const { _columns, _remoteParticipants, _remoteParticipantsLength } = this.props;
297
+        const { _columns, _remoteParticipants, _remoteParticipantsLength, _thumbnailsReordered } = this.props;
266 298
         const index = (rowIndex * _columns) + columnIndex;
267 299
 
300
+        // When the thumbnails are reordered, local participant is inserted at index 0.
301
+        const localIndex = _thumbnailsReordered ? 0 : _remoteParticipantsLength;
302
+        const remoteIndex = _thumbnailsReordered ? index - 1 : index;
303
+
268 304
         if (index > _remoteParticipantsLength) {
269 305
             return `empty-${index}`;
270 306
         }
271 307
 
272
-        if (index === 0) {
308
+        if (index === localIndex) {
273 309
             return 'local';
274 310
         }
275 311
 
276
-        return _remoteParticipants[index - 1];
312
+        return _remoteParticipants[remoteIndex];
277 313
     }
278 314
 
279 315
     _onListItemsRendered: Object => void;
@@ -286,8 +322,9 @@ class Filmstrip extends PureComponent <Props> {
286 322
      */
287 323
     _onListItemsRendered({ visibleStartIndex, visibleStopIndex }) {
288 324
         const { dispatch } = this.props;
325
+        const { startIndex, stopIndex } = this._calculateIndices(visibleStartIndex, visibleStopIndex);
289 326
 
290
-        dispatch(setVisibleRemoteParticipants(visibleStartIndex, visibleStopIndex + 1));
327
+        dispatch(setVisibleRemoteParticipants(startIndex, stopIndex));
291 328
     }
292 329
 
293 330
     _onGridItemsRendered: Object => void;
@@ -305,13 +342,11 @@ class Filmstrip extends PureComponent <Props> {
305 342
         visibleRowStopIndex
306 343
     }) {
307 344
         const { _columns, dispatch } = this.props;
308
-        let startIndex = (visibleRowStartIndex * _columns) + visibleColumnStartIndex;
309
-        const endIndex = (visibleRowStopIndex * _columns) + visibleColumnStopIndex;
345
+        const start = (visibleRowStartIndex * _columns) + visibleColumnStartIndex;
346
+        const stop = (visibleRowStopIndex * _columns) + visibleColumnStopIndex;
347
+        const { startIndex, stopIndex } = this._calculateIndices(start, stop);
310 348
 
311
-        // In tile view, the start index needs to be offset by 1 because the first participant is the local
312
-        // participant.
313
-        startIndex = startIndex > 0 ? startIndex - 1 : 0;
314
-        dispatch(setVisibleRemoteParticipants(startIndex, endIndex));
349
+        dispatch(setVisibleRemoteParticipants(startIndex, stopIndex));
315 350
     }
316 351
 
317 352
     /**
@@ -493,6 +528,7 @@ class Filmstrip extends PureComponent <Props> {
493 528
  */
494 529
 function _mapStateToProps(state) {
495 530
     const toolbarButtons = getToolbarButtons(state);
531
+    const { enableThumbnailReordering = true } = state['features/base/config'];
496 532
     const { visible, remoteParticipants } = state['features/filmstrip'];
497 533
     const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
498 534
     const remoteVideosVisible = shouldRemoteVideosBeVisible(state);
@@ -565,6 +601,7 @@ function _mapStateToProps(state) {
565 601
         _rows: gridDimensions.rows,
566 602
         _thumbnailWidth: _thumbnailSize?.width,
567 603
         _thumbnailHeight: _thumbnailSize?.height,
604
+        _thumbnailsReordered: enableThumbnailReordering,
568 605
         _videosClassName: videosClassName,
569 606
         _visible: visible,
570 607
         _isToolboxVisible: isToolboxVisible(state)

+ 7
- 3
react/features/filmstrip/components/web/ThumbnailWrapper.js 查看文件

@@ -104,6 +104,7 @@ function _mapStateToProps(state, ownProps) {
104 104
     const _currentLayout = getCurrentLayout(state);
105 105
     const { remoteParticipants } = state['features/filmstrip'];
106 106
     const remoteParticipantsLength = remoteParticipants.length;
107
+    const { enableThumbnailReordering = true } = state['features/base/config'];
107 108
 
108 109
     if (_currentLayout === LAYOUTS.TILE_VIEW) {
109 110
         const { columnIndex, rowIndex } = ownProps;
@@ -126,8 +127,11 @@ function _mapStateToProps(state, ownProps) {
126 127
             return {};
127 128
         }
128 129
 
129
-        // Make the local participant as the first thumbnail (top left corner) in tile view.
130
-        if (index === 0) {
130
+        // When the thumbnails are reordered, local participant is inserted at index 0.
131
+        const localIndex = enableThumbnailReordering ? 0 : remoteParticipantsLength;
132
+        const remoteIndex = enableThumbnailReordering ? index - 1 : index;
133
+
134
+        if (index === localIndex) {
131 135
             return {
132 136
                 _participantID: 'local',
133 137
                 _horizontalOffset: horizontalOffset
@@ -135,7 +139,7 @@ function _mapStateToProps(state, ownProps) {
135 139
         }
136 140
 
137 141
         return {
138
-            _participantID: remoteParticipants[index - 1],
142
+            _participantID: remoteParticipants[remoteIndex],
139 143
             _horizontalOffset: horizontalOffset
140 144
         };
141 145
     }

+ 28
- 7
react/features/filmstrip/functions.any.js 查看文件

@@ -6,31 +6,52 @@ import { setRemoteParticipants } from './actions';
6 6
  * Computes the reorderd list of the remote participants.
7 7
  *
8 8
  * @param {*} store - The redux store.
9
+ * @param {string} participantId - The endpoint id of the participant that joined the call.
9 10
  * @returns {void}
10 11
  * @private
11 12
  */
12
-export function updateRemoteParticipants(store: Object) {
13
+ export function updateRemoteParticipants(store: Object, participantId: ?number) {
13 14
     const state = store.getState();
15
+    const { enableThumbnailReordering = true } = state['features/base/config'];
16
+    let reorderedParticipants = [];
17
+
18
+    if (!enableThumbnailReordering) {
19
+        if (participantId) {
20
+            const { remoteParticipants } = state['features/filmstrip'];
21
+
22
+            reorderedParticipants = [ ...remoteParticipants, participantId ];
23
+            store.dispatch(setRemoteParticipants(reorderedParticipants));
24
+        }
25
+
26
+        return;
27
+    }
28
+
14 29
     const { fakeParticipants, sortedRemoteParticipants, speakersList } = state['features/base/participants'];
15 30
     const { remoteScreenShares } = state['features/video-layout'];
16 31
     const screenShares = (remoteScreenShares || []).slice();
17
-    let speakers = (speakersList || []).slice();
32
+    const speakers = new Map(speakersList);
18 33
     const remoteParticipants = new Map(sortedRemoteParticipants);
19 34
     const sharedVideos = fakeParticipants ? Array.from(fakeParticipants.keys()) : [];
20 35
 
21 36
     for (const screenshare of screenShares) {
22 37
         remoteParticipants.delete(screenshare);
23
-        speakers = speakers.filter(speaker => speaker !== screenshare);
38
+        speakers.delete(screenshare);
24 39
     }
25 40
     for (const sharedVideo of sharedVideos) {
26 41
         remoteParticipants.delete(sharedVideo);
27
-        speakers = speakers.filter(speaker => speaker !== sharedVideo);
42
+        speakers.delete(sharedVideo);
28 43
     }
29
-    for (const speaker of speakers) {
44
+    for (const speaker of speakers.keys()) {
30 45
         remoteParticipants.delete(speaker);
31 46
     }
32
-    const reorderedParticipants
33
-        = [ ...screenShares.reverse(), ...sharedVideos, ...speakers, ...Array.from(remoteParticipants.keys()) ];
47
+
48
+    // Always update the order of the thumnails.
49
+    reorderedParticipants = [
50
+        ...screenShares.reverse(),
51
+        ...sharedVideos,
52
+        ...Array.from(speakers.keys()),
53
+        ...Array.from(remoteParticipants.keys())
54
+    ];
34 55
 
35 56
     store.dispatch(setRemoteParticipants(reorderedParticipants));
36 57
 }

+ 1
- 1
react/features/filmstrip/middleware.web.js 查看文件

@@ -47,7 +47,7 @@ MiddlewareRegistry.register(store => next => action => {
47 47
         break;
48 48
     }
49 49
     case PARTICIPANT_JOINED: {
50
-        updateRemoteParticipants(store);
50
+        updateRemoteParticipants(store, action.participant?.id);
51 51
         break;
52 52
     }
53 53
     case PARTICIPANT_LEFT: {

+ 2
- 1
react/features/filmstrip/reducer.js 查看文件

@@ -155,7 +155,8 @@ ReducerRegistry.register(
155 155
                 ...state,
156 156
                 visibleParticipantsStartIndex: action.startIndex,
157 157
                 visibleParticipantsEndIndex: action.endIndex,
158
-                visibleRemoteParticipants: new Set(state.remoteParticipants.slice(action.startIndex, action.endIndex))
158
+                visibleRemoteParticipants:
159
+                    new Set(state.remoteParticipants.slice(action.startIndex, action.endIndex + 1))
159 160
             };
160 161
         }
161 162
         case PARTICIPANT_LEFT: {

+ 1
- 19
react/features/filmstrip/subscriber.any.js 查看文件

@@ -17,22 +17,4 @@ StateListenerRegistry.register(
17 17
  */
18 18
 StateListenerRegistry.register(
19 19
     /* selector */ state => state['features/base/participants'].dominantSpeaker,
20
-    /* listener */ (dominantSpeaker, store) => _reorderDominantSpeakers(store));
21
-
22
-/**
23
- * Private helper function that reorders the remote participants based on dominant speaker changes.
24
- *
25
- * @param {*} store - The redux store.
26
- * @returns {void}
27
- * @private
28
- */
29
-function _reorderDominantSpeakers(store) {
30
-    const state = store.getState();
31
-    const { dominantSpeaker, local } = state['features/base/participants'];
32
-    const { visibleRemoteParticipants } = state['features/filmstrip'];
33
-
34
-    // Reorder the participants if the new dominant speaker is currently not visible.
35
-    if (dominantSpeaker !== local?.id && !visibleRemoteParticipants.has(dominantSpeaker)) {
36
-        updateRemoteParticipants(store);
37
-    }
38
-}
20
+    /* listener */ (dominantSpeaker, store) => updateRemoteParticipants(store));

正在加载...
取消
保存