Pārlūkot izejas kodu

ref(stage-filmstrip) Refactor as new layout

Fixes screensharing selection issues. Now when there’s a screen share we just use the old VERTICAL_FILMSTRIP_VIEW layout
Add THUMBAIL_TYPE to determine how to display thumbnails
master
robertpin 3 gadus atpakaļ
vecāks
revīzija
0abefa87aa
27 mainītis faili ar 448 papildinājumiem un 351 dzēšanām
  1. 138
    136
      css/filmstrip/_vertical_filmstrip.scss
  2. 9
    4
      css/filmstrip/_vertical_filmstrip_overrides.scss
  3. 2
    2
      modules/UI/videolayout/VideoContainer.js
  4. 2
    2
      react/features/base/participants/functions.js
  5. 7
    14
      react/features/conference/components/web/Conference.js
  6. 9
    9
      react/features/display-name/components/web/DisplayName.js
  7. 8
    1
      react/features/filmstrip/actionTypes.ts
  8. 22
    14
      react/features/filmstrip/actions.web.js
  9. 22
    19
      react/features/filmstrip/components/web/Filmstrip.js
  10. 4
    8
      react/features/filmstrip/components/web/MainFilmstrip.js
  11. 1
    2
      react/features/filmstrip/components/web/StageFilmstrip.js
  12. 9
    11
      react/features/filmstrip/components/web/StatusIndicators.js
  13. 44
    41
      react/features/filmstrip/components/web/Thumbnail.js
  14. 13
    12
      react/features/filmstrip/components/web/ThumbnailBottomIndicators.js
  15. 17
    16
      react/features/filmstrip/components/web/ThumbnailTopIndicators.js
  16. 10
    10
      react/features/filmstrip/components/web/VideoMenuTriggerButton.js
  17. 15
    7
      react/features/filmstrip/constants.js
  18. 2
    2
      react/features/filmstrip/functions.native.js
  19. 52
    16
      react/features/filmstrip/functions.web.js
  20. 19
    6
      react/features/filmstrip/middleware.web.js
  21. 8
    1
      react/features/filmstrip/reducer.js
  22. 9
    3
      react/features/filmstrip/subscriber.web.js
  23. 7
    2
      react/features/large-video/actions.any.js
  24. 2
    1
      react/features/video-layout/constants.js
  25. 5
    0
      react/features/video-layout/functions.any.js
  26. 6
    6
      react/features/video-menu/components/web/LocalVideoMenuTriggerButton.js
  27. 6
    6
      react/features/video-menu/components/web/RemoteVideoMenuTriggerButton.js

+ 138
- 136
css/filmstrip/_vertical_filmstrip.scss Parādīt failu

@@ -1,175 +1,177 @@
1
-.vertical-filmstrip span:not(.tile-view) .filmstrip {
2
-    &.hide-videos {
3
-        .remote-videos {
4
-            & > div {
5
-                opacity: 0;
6
-                pointer-events: none;
1
+.vertical-filmstrip, .stage-filmstrip {
2
+    span:not(.tile-view) .filmstrip {
3
+        &.hide-videos {
4
+            .remote-videos {
5
+                & > div {
6
+                    opacity: 0;
7
+                    pointer-events: none;
8
+                }
7 9
             }
8 10
         }
9
-    }
10
-
11
-    /*
12
-     * Firefox sets flex items to min-height: auto and min-width: auto,
13
-     * preventing flex children from shrinking like they do on other browsers.
14
-     * Setting min-height and min-width 0 is a workaround for the issue so
15
-     * Firefox behaves like other browsers.
16
-     * https://bugzilla.mozilla.org/show_bug.cgi?id=1043520
17
-     */
18
-    @mixin minHWAutoFix() {
19
-        min-height: 0;
20
-        min-width: 0;
21
-    }
22 11
 
23
-    @extend %align-right;
24
-    align-items: flex-end;
25
-    bottom: 0;
26
-    box-sizing: border-box;
27
-    display: flex;
28
-    flex-direction: column-reverse;
29
-    height: 100%;
30
-    width: 100%;
31
-    padding: 0;
32
-    /**
33
-     * fixed positioning is necessary for remote menus and tooltips to pop
34
-     * out of the scrolling filmstrip. AtlasKit dialogs and tooltips use
35
-     * a library called popper which will position its elements fixed if
36
-     * any parent is also fixed.
37
-     */
38
-    position: fixed;
39
-    top: 0;
40
-    right: 0;
41
-    z-index: $filmstripVideosZ;
42
-
43
-    &.no-vertical-padding {
44
-        padding: 0;
45
-    }
46
-
47
-    /**
48
-     * Hide videos by making them slight to the right.
49
-     */
50
-    .filmstrip__videos {
51
-        @extend %align-right;
52
-        bottom: 0;
53
-        padding: 0;
54
-        position:relative;
55
-        right: 0;
56
-        width: auto;
57
-
58
-        /**
59
-         * An id selector is used to match id specificity with existing
60
-         * filmstrip styles.
12
+        /*
13
+         * Firefox sets flex items to min-height: auto and min-width: auto,
14
+         * preventing flex children from shrinking like they do on other browsers.
15
+         * Setting min-height and min-width 0 is a workaround for the issue so
16
+         * Firefox behaves like other browsers.
17
+         * https://bugzilla.mozilla.org/show_bug.cgi?id=1043520
61 18
          */
62
-        &#remoteVideos {
63
-            border: $thumbnailsBorder solid transparent;
64
-            padding-left: 0;
65
-            border-left: 0;
66
-            width: 100%;
67
-            height: 100%;
68
-            justify-content: center;
19
+        @mixin minHWAutoFix() {
20
+            min-height: 0;
21
+            min-width: 0;
69 22
         }
70
-    }
71 23
 
72
-    /**
73
-     * Re-styles the local Video to better fit vertical filmstrip layout.
74
-     */
75
-    #filmstripLocalVideo {
76
-        align-self: initial;
77
-        margin-bottom: 5px;
24
+        @extend %align-right;
25
+        align-items: flex-end;
26
+        bottom: 0;
27
+        box-sizing: border-box;
78 28
         display: flex;
79 29
         flex-direction: column-reverse;
80
-        height: auto;
81
-        justify-content: flex-start;
30
+        height: 100%;
82 31
         width: 100%;
32
+        padding: 0;
33
+        /**
34
+         * fixed positioning is necessary for remote menus and tooltips to pop
35
+         * out of the scrolling filmstrip. AtlasKit dialogs and tooltips use
36
+         * a library called popper which will position its elements fixed if
37
+         * any parent is also fixed.
38
+         */
39
+        position: fixed;
40
+        top: 0;
41
+        right: 0;
42
+        z-index: $filmstripVideosZ;
83 43
 
84
-        #filmstripLocalVideoThumbnail {
85
-            width: calc(100% - 15px);
44
+        &.no-vertical-padding {
45
+            padding: 0;
46
+        }
86 47
 
87
-            .videocontainer {
88
-                height: 0px;
48
+        /**
49
+         * Hide videos by making them slight to the right.
50
+         */
51
+        .filmstrip__videos {
52
+            @extend %align-right;
53
+            bottom: 0;
54
+            padding: 0;
55
+            position:relative;
56
+            right: 0;
57
+            width: auto;
58
+
59
+            /**
60
+             * An id selector is used to match id specificity with existing
61
+             * filmstrip styles.
62
+             */
63
+            &#remoteVideos {
64
+                border: $thumbnailsBorder solid transparent;
65
+                padding-left: 0;
66
+                border-left: 0;
89 67
                 width: 100%;
68
+                height: 100%;
69
+                justify-content: center;
90 70
             }
91 71
         }
92
-    }
93 72
 
94
-    #filmstripLocalScreenShare {
95
-        align-self: initial;
96
-        margin-bottom: 5px;
97
-        display: flex;
98
-        flex-direction: column-reverse;
99
-        height: auto;
100
-        justify-content: flex-start;
101
-        width: 100%;
73
+        /**
74
+         * Re-styles the local Video to better fit vertical filmstrip layout.
75
+         */
76
+        #filmstripLocalVideo {
77
+            align-self: initial;
78
+            margin-bottom: 5px;
79
+            display: flex;
80
+            flex-direction: column-reverse;
81
+            height: auto;
82
+            justify-content: flex-start;
83
+            width: 100%;
102 84
 
103
-        #filmstripLocalScreenShareThumbnail {
104
-            width: calc(100% - 15px);
85
+            #filmstripLocalVideoThumbnail {
86
+                width: calc(100% - 15px);
105 87
 
106
-            .videocontainer {
107
-                height: 0px;
108
-                width: 100%;
88
+                .videocontainer {
89
+                    height: 0px;
90
+                    width: 100%;
91
+                }
109 92
             }
110 93
         }
111
-    }
112 94
 
113
-    /**
114
-     * Remove unnecssary padding that is normally used to prevent horizontal
115
-     * filmstrip from overlapping the left edge of the screen.
116
-     */
117
-    #filmstripLocalVideo,
118
-    #filmstripLocalScreenShare,
119
-    .remote-videos {
120
-        padding: 0;
121
-    }
95
+        #filmstripLocalScreenShare {
96
+            align-self: initial;
97
+            margin-bottom: 5px;
98
+            display: flex;
99
+            flex-direction: column-reverse;
100
+            height: auto;
101
+            justify-content: flex-start;
102
+            width: 100%;
122 103
 
123
-    #remoteVideos {
124
-        @include minHWAutoFix();
104
+            #filmstripLocalScreenShareThumbnail {
105
+                width: calc(100% - 15px);
125 106
 
126
-        flex-direction: column;
127
-        flex-grow: 1;
128
-    }
107
+                .videocontainer {
108
+                    height: 0px;
109
+                    width: 100%;
110
+                }
111
+            }
112
+        }
129 113
 
130
-    .resizable-filmstrip #remoteVideos .videocontainer {
131
-        border-left: 0;
132
-        margin: 0;
133
-    }
114
+        /**
115
+         * Remove unnecssary padding that is normally used to prevent horizontal
116
+         * filmstrip from overlapping the left edge of the screen.
117
+         */
118
+        #filmstripLocalVideo,
119
+        #filmstripLocalScreenShare,
120
+        .remote-videos {
121
+            padding: 0;
122
+        }
134 123
 
135
-    &.reduce-height {
136
-        height: calc(100% - calc(#{$newToolbarSizeWithPadding} + #{$scrollHeight}));
137
-    }
124
+        #remoteVideos {
125
+            @include minHWAutoFix();
138 126
 
139
-    .filmstrip__videos.vertical-view-grid#remoteVideos {
140
-        align-items: 'center';
141
-        border: 0px;
142
-        padding-right: 7px;
127
+            flex-direction: column;
128
+            flex-grow: 1;
129
+        }
143 130
 
144
-        &.has-scroll {
145
-            padding-right: 0px;
131
+        .resizable-filmstrip #remoteVideos .videocontainer {
132
+            border-left: 0;
133
+            margin: 0;
146 134
         }
147 135
 
148
-        .remote-videos > div {
149
-            left: 0px; // fixes an issue on FF - the div is aligned to the right by default for some reason
136
+        &.reduce-height {
137
+            height: calc(100% - calc(#{$newToolbarSizeWithPadding} + #{$scrollHeight}));
150 138
         }
151 139
 
152
-        .videocontainer {
140
+        .filmstrip__videos.vertical-view-grid#remoteVideos {
141
+            align-items: 'center';
153 142
             border: 0px;
154
-            margin: 2px;
155
-        }
156
-    }
143
+            padding-right: 7px;
157 144
 
158
-    .remote-videos {
159
-        display: flex;
160
-        overscroll-behavior: contain;
145
+            &.has-scroll {
146
+                padding-right: 0px;
147
+            }
161 148
 
162
-        &.height-transition {
163
-            transition: height .3s ease-in;
164
-        }
149
+            .remote-videos > div {
150
+                left: 0px; // fixes an issue on FF - the div is aligned to the right by default for some reason
151
+            }
165 152
 
166
-        & > div {
167
-            position: absolute;
168
-            transition: opacity 1s;
153
+            .videocontainer {
154
+                border: 0px;
155
+                margin: 2px;
156
+            }
169 157
         }
170 158
 
171
-        &.is-not-overflowing > div {
172
-            bottom: 0px;
159
+        .remote-videos {
160
+            display: flex;
161
+            overscroll-behavior: contain;
162
+
163
+            &.height-transition {
164
+                transition: height .3s ease-in;
165
+            }
166
+
167
+            & > div {
168
+                position: absolute;
169
+                transition: opacity 1s;
170
+            }
171
+
172
+            &.is-not-overflowing > div {
173
+                bottom: 0px;
174
+            }
173 175
         }
174 176
     }
175 177
 }

+ 9
- 4
css/filmstrip/_vertical_filmstrip_overrides.scss Parādīt failu

@@ -3,14 +3,17 @@
3 3
  * clashing with the filmstrip.
4 4
  */
5 5
 .vertical-filmstrip #etherpad,
6
-.vertical-filmstrip #sharedvideo {
6
+.stage-filmstrip #etherpad,
7
+.vertical-filmstrip #sharedvideo,
8
+.stage-filmstrip #sharedvideo {
7 9
     text-align: left;
8 10
 }
9 11
 
10 12
 /**
11 13
  * Overrides for small videos in vertical filmstrip mode.
12 14
  */
13
-.vertical-filmstrip .filmstrip__videos .videocontainer {
15
+.vertical-filmstrip .filmstrip__videos .videocontainer,
16
+.stage-filmstrip .filmstrip__videos .videocontainer {
14 17
     .self-view-mobile-portrait video {
15 18
         object-fit: contain;
16 19
     }
@@ -27,7 +30,8 @@
27 30
  * The class opening is for when the filmstrip is transitioning from hidden
28 31
  * to visible.
29 32
  */
30
-.vertical-filmstrip .large-video-labels {
33
+.vertical-filmstrip .large-video-labels,
34
+.stage-filmstrip .large-video-labels {
31 35
     &.with-filmstrip {
32 36
         right: 150px;
33 37
     }
@@ -47,6 +51,7 @@
47 51
  * Overrides for self view when in portrait mode on mobile.
48 52
  * This is done in order to keep the aspect ratio.
49 53
  */
50
-.vertical-filmstrip .self-view-mobile-portrait #localVideo_container {
54
+.vertical-filmstrip .self-view-mobile-portrait #localVideo_container,
55
+.stage-filmstrip .self-view-mobile-portrait #localVideo_container {
51 56
     object-fit: contain;
52 57
 }

+ 2
- 2
modules/UI/videolayout/VideoContainer.js Parādīt failu

@@ -6,7 +6,7 @@ import ReactDOM from 'react-dom';
6 6
 
7 7
 import { browser } from '../../../react/features/base/lib-jitsi-meet';
8 8
 import { isTestModeEnabled } from '../../../react/features/base/testing';
9
-import { FILMSTRIP_BREAKPOINT, shouldDisplayStageFilmstrip } from '../../../react/features/filmstrip';
9
+import { FILMSTRIP_BREAKPOINT } from '../../../react/features/filmstrip';
10 10
 import { ORIENTATION, LargeVideoBackground, updateLastLargeVideoMediaEvent } from '../../../react/features/large-video';
11 11
 import { LAYOUTS, getCurrentLayout } from '../../../react/features/video-layout';
12 12
 /* eslint-enable no-unused-vars */
@@ -414,7 +414,7 @@ export class VideoContainer extends LargeContainer {
414 414
 
415 415
         const verticalFilmstripWidth = state['features/filmstrip'].width?.current;
416 416
 
417
-        if (currentLayout === LAYOUTS.TILE_VIEW || shouldDisplayStageFilmstrip(state)) {
417
+        if (currentLayout === LAYOUTS.TILE_VIEW || currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW) {
418 418
             // We don't need to resize the large video since it won't be displayed and we'll resize when returning back
419 419
             // to stage view.
420 420
             return;

+ 2
- 2
react/features/base/participants/functions.js Parādīt failu

@@ -4,7 +4,7 @@ import { getGravatarURL } from '@jitsi/js-utils/avatar';
4 4
 import type { Store } from 'redux';
5 5
 
6 6
 import { i18next } from '../../base/i18n';
7
-import { isStageFilmstripEnabled } from '../../filmstrip/functions';
7
+import { isStageFilmstripAvailable } from '../../filmstrip/functions';
8 8
 import { GRAVATAR_BASE_URL, isCORSAvatarURL } from '../avatar';
9 9
 import { getSourceNameSignalingFeatureFlag } from '../config';
10 10
 import { JitsiParticipantConnectionStatus } from '../lib-jitsi-meet';
@@ -372,7 +372,7 @@ export function getRemoteParticipantsSorted(stateful: Object | Function) {
372 372
 export function getPinnedParticipant(stateful: Object | Function) {
373 373
     const state = toState(stateful);
374 374
     const { pinnedParticipant } = state['features/base/participants'];
375
-    const stageFilmstrip = isStageFilmstripEnabled(state);
375
+    const stageFilmstrip = isStageFilmstripAvailable(state);
376 376
 
377 377
     if (stageFilmstrip) {
378 378
         const { activeParticipants } = state['features/filmstrip'];

+ 7
- 14
react/features/conference/components/web/Conference.js Parādīt failu

@@ -1,6 +1,5 @@
1 1
 // @flow
2 2
 
3
-import clsx from 'clsx';
4 3
 import _ from 'lodash';
5 4
 import React from 'react';
6 5
 
@@ -12,7 +11,7 @@ import { translate } from '../../../base/i18n';
12 11
 import { connect as reactReduxConnect } from '../../../base/redux';
13 12
 import { setColorAlpha } from '../../../base/util';
14 13
 import { Chat } from '../../../chat';
15
-import { MainFilmstrip, StageFilmstrip, shouldDisplayStageFilmstrip } from '../../../filmstrip';
14
+import { MainFilmstrip, StageFilmstrip } from '../../../filmstrip';
16 15
 import { CalleeInfoContainer } from '../../../invite';
17 16
 import { LargeVideo } from '../../../large-video';
18 17
 import { LobbyScreen } from '../../../lobby';
@@ -59,7 +58,8 @@ const FULL_SCREEN_EVENTS = [
59 58
 export const LAYOUT_CLASSNAMES = {
60 59
     [LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'horizontal-filmstrip',
61 60
     [LAYOUTS.TILE_VIEW]: 'tile-view',
62
-    [LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'vertical-filmstrip'
61
+    [LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'vertical-filmstrip',
62
+    [LAYOUTS.STAGE_FILMSTRIP_VIEW]: 'stage-filmstrip'
63 63
 };
64 64
 
65 65
 /**
@@ -103,11 +103,6 @@ type Props = AbstractProps & {
103 103
      */
104 104
     _showPrejoin: boolean,
105 105
 
106
-    /**
107
-     * Whether or not the stage filmstrip should be displayed.
108
-     */
109
-    _showStageFilmstrip: boolean,
110
-
111 106
     dispatch: Function,
112 107
     t: Function
113 108
 }
@@ -220,8 +215,7 @@ class Conference extends AbstractConference<Props, *> {
220 215
             _notificationsVisible,
221 216
             _overflowDrawer,
222 217
             _showLobby,
223
-            _showPrejoin,
224
-            _showStageFilmstrip
218
+            _showPrejoin
225 219
         } = this.props;
226 220
 
227 221
         return (
@@ -233,7 +227,7 @@ class Conference extends AbstractConference<Props, *> {
233 227
                 ref = { this._setBackground }>
234 228
                 <Chat />
235 229
                 <div
236
-                    className = { clsx(_layoutClassName, _showStageFilmstrip && 'stage-filmstrip') }
230
+                    className = { _layoutClassName }
237 231
                     id = 'videoconference_page'
238 232
                     onMouseMove = { isMobileBrowser() ? undefined : this._onShowToolbar }>
239 233
                     <ConferenceInfo />
@@ -242,7 +236,7 @@ class Conference extends AbstractConference<Props, *> {
242 236
                         id = 'videospace'
243 237
                         onTouchStart = { this._onVidespaceTouchStart }>
244 238
                         <LargeVideo />
245
-                        {_showStageFilmstrip && <StageFilmstrip />}
239
+                        <StageFilmstrip />
246 240
                         <MainFilmstrip />
247 241
                     </div>
248 242
 
@@ -402,8 +396,7 @@ function _mapStateToProps(state) {
402 396
         _overflowDrawer: overflowDrawer,
403 397
         _roomName: getConferenceNameForTitle(state),
404 398
         _showLobby: getIsLobbyVisible(state),
405
-        _showPrejoin: isPrejoinPageVisible(state),
406
-        _showStageFilmstrip: shouldDisplayStageFilmstrip(state)
399
+        _showPrejoin: isPrejoinPageVisible(state)
407 400
     };
408 401
 }
409 402
 

+ 9
- 9
react/features/display-name/components/web/DisplayName.js Parādīt failu

@@ -36,11 +36,6 @@ type Props = {
36 36
      */
37 37
     allowEditing: boolean,
38 38
 
39
-    /**
40
-     * The current layout of the filmstrip.
41
-     */
42
-    currentLayout: string,
43
-
44 39
     /**
45 40
      * Invoked to update the participant's display name.
46 41
      */
@@ -70,7 +65,12 @@ type Props = {
70 65
     /**
71 66
      * Invoked to obtain translated strings.
72 67
      */
73
-    t: Function
68
+    t: Function,
69
+
70
+    /**
71
+     * The type of thumbnail.
72
+     */
73
+    thumbnailType: string
74 74
 };
75 75
 
76 76
 /**
@@ -183,11 +183,11 @@ class DisplayName extends Component<Props, State> {
183 183
         const {
184 184
             _nameToDisplay,
185 185
             allowEditing,
186
-            currentLayout,
187 186
             displayNameSuffix,
188 187
             classes,
189 188
             elementID,
190
-            t
189
+            t,
190
+            thumbnailType
191 191
         } = this.props;
192 192
 
193 193
         if (allowEditing && this.state.isEditing) {
@@ -211,7 +211,7 @@ class DisplayName extends Component<Props, State> {
211 211
         return (
212 212
             <Tooltip
213 213
                 content = { appendSuffix(_nameToDisplay, displayNameSuffix) }
214
-                position = { getIndicatorsTooltipPosition(currentLayout) }>
214
+                position = { getIndicatorsTooltipPosition(thumbnailType) }>
215 215
                 <span
216 216
                     className = { `displayname ${classes.displayName}` }
217 217
                     id = { elementID }

+ 8
- 1
react/features/filmstrip/actionTypes.ts Parādīt failu

@@ -171,7 +171,6 @@ export const SET_STAGE_PARTICIPANTS = 'SET_STAGE_PARTICIPANTS';
171 171
  */
172 172
 export const SET_MAX_STAGE_PARTICIPANTS = 'SET_MAX_STAGE_PARTICIPANTS';
173 173
 
174
-
175 174
 /**
176 175
  * The type of Redux action which toggles the pin state of stage participants.
177 176
  * {
@@ -180,3 +179,11 @@ export const SET_MAX_STAGE_PARTICIPANTS = 'SET_MAX_STAGE_PARTICIPANTS';
180 179
  * }
181 180
  */
182 181
 export const TOGGLE_PIN_STAGE_PARTICIPANT = 'TOGGLE_PIN_STAGE_PARTICIPANT';
182
+
183
+/**
184
+ * The type of Redux action which clears the list of stage participants.
185
+ * {
186
+ *     type: CLEAR_STAGE_PARTICIPANTS
187
+ * }
188
+ */
189
+export const CLEAR_STAGE_PARTICIPANTS = 'CLEAR_STAGE_PARTICIPANTS';

+ 22
- 14
react/features/filmstrip/actions.web.js Parādīt failu

@@ -24,7 +24,8 @@ import {
24 24
     SET_VERTICAL_VIEW_DIMENSIONS,
25 25
     SET_VOLUME,
26 26
     SET_MAX_STAGE_PARTICIPANTS,
27
-    TOGGLE_PIN_STAGE_PARTICIPANT
27
+    TOGGLE_PIN_STAGE_PARTICIPANT,
28
+    CLEAR_STAGE_PARTICIPANTS
28 29
 } from './actionTypes';
29 30
 import {
30 31
     HORIZONTAL_FILMSTRIP_MARGIN,
@@ -266,8 +267,6 @@ export function setStageFilmstripViewDimensions() {
266 267
         const state = getState();
267 268
         const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
268 269
         const {
269
-            disableResponsiveTiles,
270
-            disableTileEnlargement,
271 270
             tileView = {}
272 271
         } = state['features/base/config'];
273 272
         const { visible } = state['features/filmstrip'];
@@ -282,17 +281,15 @@ export function setStageFilmstripViewDimensions() {
282 281
             width,
283 282
             columns,
284 283
             rows
285
-        } = disableResponsiveTiles
286
-            ? calculateNonResponsiveTileViewDimensions(state, true)
287
-            : calculateResponsiveTileViewDimensions({
288
-                clientWidth: availableWidth,
289
-                clientHeight,
290
-                disableTileEnlargement,
291
-                maxColumns,
292
-                noHorizontalContainerMargin: verticalWidth > 0,
293
-                numberOfParticipants,
294
-                numberOfVisibleTiles
295
-            });
284
+        } = calculateResponsiveTileViewDimensions({
285
+            clientWidth: availableWidth,
286
+            clientHeight,
287
+            disableTileEnlargement: false,
288
+            maxColumns,
289
+            noHorizontalContainerMargin: verticalWidth > 0,
290
+            numberOfParticipants,
291
+            numberOfVisibleTiles
292
+        });
296 293
         const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height);
297 294
         const hasScroll = clientHeight < thumbnailsTotalHeight;
298 295
         const filmstripWidth
@@ -469,3 +466,14 @@ export function togglePinStageParticipant(participantId) {
469 466
         participantId
470 467
     };
471 468
 }
469
+
470
+/**
471
+ * Clears the stage participants list.
472
+ *
473
+ * @returns {Object}
474
+ */
475
+export function clearStageParticipants() {
476
+    return {
477
+        type: CLEAR_STAGE_PARTICIPANTS
478
+    };
479
+}

+ 22
- 19
react/features/filmstrip/components/web/Filmstrip.js Parādīt failu

@@ -20,7 +20,7 @@ import { connect } from '../../../base/redux';
20 20
 import { shouldHideSelfView } from '../../../base/settings/functions.any';
21 21
 import { showToolbox } from '../../../toolbox/actions.web';
22 22
 import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.web';
23
-import { LAYOUTS } from '../../../video-layout';
23
+import { getCurrentLayout, LAYOUTS } from '../../../video-layout';
24 24
 import {
25 25
     setFilmstripVisible,
26 26
     setVisibleRemoteParticipants,
@@ -110,7 +110,7 @@ type Props = {
110 110
     /**
111 111
      * The local screen share participant. This prop is behind the sourceNameSignaling feature flag.
112 112
      */
113
-     _localScreenShare: Object,
113
+    _localScreenShare: Object,
114 114
 
115 115
     /**
116 116
      * The maximum width of the vertical filmstrip.
@@ -318,32 +318,29 @@ class Filmstrip extends PureComponent <Props, State> {
318 318
         const { isMouseDown } = this.state;
319 319
         const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
320 320
 
321
-        switch (_currentLayout) {
322
-        case LAYOUTS.VERTICAL_FILMSTRIP_VIEW: {
321
+        if (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && _stageFilmstrip) {
322
+            if (_visible) {
323
+                filmstripStyle.maxWidth = `calc(100% - ${_verticalViewMaxWidth}px)`;
324
+            }
325
+        } else if (_currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
326
+            || (_currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && !_stageFilmstrip)) {
323 327
             filmstripStyle.maxWidth = _verticalViewMaxWidth;
324 328
             if (!_visible) {
325 329
                 filmstripStyle.right = `-${filmstripStyle.maxWidth}px`;
326 330
             }
327
-            break;
328
-        }
329
-        case LAYOUTS.TILE_VIEW: {
330
-            if (_stageFilmstrip && _visible) {
331
-                filmstripStyle.maxWidth = `calc(100% - ${_verticalViewMaxWidth}px)`;
332
-            }
333
-            break;
334
-        }
335 331
         }
336 332
 
337 333
         let toolbar = null;
338 334
 
339
-        if (!this.props._iAmRecorder && this.props._isFilmstripButtonEnabled && _currentLayout !== LAYOUTS.TILE_VIEW) {
335
+        if (!this.props._iAmRecorder && this.props._isFilmstripButtonEnabled
336
+            && _currentLayout !== LAYOUTS.TILE_VIEW && !_stageFilmstrip) {
340 337
             toolbar = this._renderToggleButton();
341 338
         }
342 339
 
343 340
         const filmstrip = (<>
344 341
             <div
345 342
                 className = { clsx(this.props._videosClassName,
346
-                    !tileViewActive && !_resizableFilmstrip && 'filmstrip-hover',
343
+                    !tileViewActive && !_stageFilmstrip && !_resizableFilmstrip && 'filmstrip-hover',
347 344
                     _verticalViewGrid && 'vertical-view-grid') }
348 345
                 id = 'remoteVideos'>
349 346
                 {!_disableSelfView && !_verticalViewGrid && (
@@ -351,7 +348,7 @@ class Filmstrip extends PureComponent <Props, State> {
351 348
                         className = 'filmstrip__videos'
352 349
                         id = 'filmstripLocalVideo'>
353 350
                         {
354
-                            !tileViewActive && <div id = 'filmstripLocalVideoThumbnail'>
351
+                            !tileViewActive && !_stageFilmstrip && <div id = 'filmstripLocalVideoThumbnail'>
355 352
                                 <Thumbnail
356 353
                                     key = 'local' />
357 354
                             </div>
@@ -364,7 +361,7 @@ class Filmstrip extends PureComponent <Props, State> {
364 361
                         id = 'filmstripLocalScreenShare'>
365 362
                         <div id = 'filmstripLocalScreenShareThumbnail'>
366 363
                             {
367
-                                !tileViewActive && <Thumbnail
364
+                                !tileViewActive && !_stageFilmstrip && <Thumbnail
368 365
                                     key = 'localScreenShare'
369 366
                                     participantID = { _localScreenShare.id } />
370 367
 
@@ -604,6 +601,7 @@ class Filmstrip extends PureComponent <Props, State> {
604 601
             _filmstripHeight,
605 602
             _filmstripWidth,
606 603
             _hasScroll,
604
+            _isVerticalFilmstrip,
607 605
             _remoteParticipantsLength,
608 606
             _resizableFilmstrip,
609 607
             _rows,
@@ -619,7 +617,7 @@ class Filmstrip extends PureComponent <Props, State> {
619 617
             return null;
620 618
         }
621 619
 
622
-        if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid) {
620
+        if (_currentLayout === LAYOUTS.TILE_VIEW || _verticalViewGrid || _stageFilmstrip) {
623 621
             return (
624 622
                 <FixedSizeGrid
625 623
                     className = 'filmstrip__videos remote-videos'
@@ -669,7 +667,7 @@ class Filmstrip extends PureComponent <Props, State> {
669 667
                 props.className += ' is-not-overflowing';
670 668
             }
671 669
 
672
-        } else if (_currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW) {
670
+        } else if (_isVerticalFilmstrip) {
673 671
             const itemSize = _thumbnailHeight + TILE_VERTICAL_MARGIN;
674 672
             const isNotOverflowing = !_hasScroll;
675 673
 
@@ -820,15 +818,20 @@ function _mapStateToProps(state, ownProps) {
820 818
         shouldReduceHeight ? 'reduce-height' : ''
821 819
     } ${shiftRight ? 'shift-right' : ''} ${collapseTileView ? 'collapse' : ''} ${visible ? '' : 'hidden'}`.trim();
822 820
 
821
+    const _currentLayout = getCurrentLayout(state);
822
+    const _isVerticalFilmstrip = _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
823
+        || (!ownProps._stageFilmstrip && _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW);
824
+
823 825
     return {
824 826
         _className: className,
825 827
         _chatOpen: state['features/chat'].isOpen,
828
+        _currentLayout,
826 829
         _disableSelfView: disableSelfView,
827 830
         _hasScroll,
828 831
         _iAmRecorder: Boolean(iAmRecorder),
829 832
         _isFilmstripButtonEnabled: isButtonEnabled('filmstrip', state),
830 833
         _isToolboxVisible: isToolboxVisible(state),
831
-        _isVerticalFilmstrip: ownProps._currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW,
834
+        _isVerticalFilmstrip,
832 835
         _localScreenShare: getSourceNameSignalingFeatureFlag(state) && localScreenShare,
833 836
         _maxFilmstripWidth: clientWidth - MIN_STAGE_VIEW_WIDTH,
834 837
         _thumbnailsReordered: enableThumbnailReordering,

+ 4
- 8
react/features/filmstrip/components/web/MainFilmstrip.js Parādīt failu

@@ -17,11 +17,6 @@ import Filmstrip from './Filmstrip';
17 17
 
18 18
 type Props = {
19 19
 
20
-    /**
21
-     * The current layout of the filmstrip.
22
-     */
23
-    _currentLayout: string,
24
-
25 20
     /**
26 21
      * The number of columns in tile view.
27 22
      */
@@ -143,7 +138,8 @@ function _mapStateToProps(state) {
143 138
         && clientWidth <= ASPECT_RATIO_BREAKPOINT;
144 139
 
145 140
     const shouldReduceHeight = reduceHeight && (
146
-        isMobileBrowser() || _currentLayout !== LAYOUTS.VERTICAL_FILMSTRIP_VIEW);
141
+        isMobileBrowser() || (_currentLayout !== LAYOUTS.VERTICAL_FILMSTRIP_VIEW
142
+            && _currentLayout !== LAYOUTS.STAGE_FILMSTRIP_VIEW));
147 143
 
148 144
     let _thumbnailSize, remoteFilmstripHeight, remoteFilmstripWidth;
149 145
 
@@ -154,7 +150,8 @@ function _mapStateToProps(state) {
154 150
         remoteFilmstripHeight = filmstripHeight - (collapseTileView && filmstripPadding > 0 ? filmstripPadding : 0);
155 151
         remoteFilmstripWidth = filmstripWidth;
156 152
         break;
157
-    case LAYOUTS.VERTICAL_FILMSTRIP_VIEW: {
153
+    case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
154
+    case LAYOUTS.STAGE_FILMSTRIP_VIEW: {
158 155
         const {
159 156
             remote,
160 157
             remoteVideosContainer,
@@ -189,7 +186,6 @@ function _mapStateToProps(state) {
189 186
 
190 187
     return {
191 188
         _columns: gridDimensions.columns,
192
-        _currentLayout,
193 189
         _filmstripHeight: remoteFilmstripHeight,
194 190
         _filmstripWidth: remoteFilmstripWidth,
195 191
         _hasScroll,

+ 1
- 2
react/features/filmstrip/components/web/StageFilmstrip.js Parādīt failu

@@ -92,11 +92,10 @@ type Props = {
92 92
     _visible: boolean
93 93
 };
94 94
 
95
-const StageFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW && (
95
+const StageFilmstrip = (props: Props) => props._currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW && (
96 96
     <span className = { LAYOUT_CLASSNAMES[LAYOUTS.TILE_VIEW] }>
97 97
         <Filmstrip
98 98
             { ...props }
99
-            _currentLayout = { LAYOUTS.TILE_VIEW }
100 99
             _stageFilmstrip = { true } />
101 100
     </span>
102 101
 );

+ 9
- 11
react/features/filmstrip/components/web/StatusIndicators.js Parādīt failu

@@ -6,7 +6,6 @@ import { MEDIA_TYPE } from '../../../base/media';
6 6
 import { getParticipantByIdOrUndefined, PARTICIPANT_ROLE } from '../../../base/participants';
7 7
 import { connect } from '../../../base/redux';
8 8
 import { getTrackByMediaTypeAndParticipant, isLocalTrackMuted, isRemoteTrackMuted } from '../../../base/tracks';
9
-import { getCurrentLayout } from '../../../video-layout';
10 9
 import { getIndicatorsTooltipPosition } from '../../functions.web';
11 10
 
12 11
 import AudioMutedIndicator from './AudioMutedIndicator';
@@ -20,11 +19,6 @@ declare var interfaceConfig: Object;
20 19
  */
21 20
 type Props = {
22 21
 
23
-    /**
24
-     * The current layout of the filmstrip.
25
-     */
26
-    _currentLayout: string,
27
-
28 22
     /**
29 23
      * Indicates if the audio muted indicator should be visible or not.
30 24
      */
@@ -43,7 +37,12 @@ type Props = {
43 37
     /**
44 38
      * The ID of the participant for which the status bar is rendered.
45 39
      */
46
-    participantID: String
40
+    participantID: String,
41
+
42
+    /**
43
+     * The type of thumbnail.
44
+     */
45
+    thumbnailType: string
47 46
 };
48 47
 
49 48
 /**
@@ -60,12 +59,12 @@ class StatusIndicators extends Component<Props> {
60 59
      */
61 60
     render() {
62 61
         const {
63
-            _currentLayout,
64 62
             _showAudioMutedIndicator,
65 63
             _showModeratorIndicator,
66
-            _showScreenShareIndicator
64
+            _showScreenShareIndicator,
65
+            thumbnailType
67 66
         } = this.props;
68
-        const tooltipPosition = getIndicatorsTooltipPosition(_currentLayout);
67
+        const tooltipPosition = getIndicatorsTooltipPosition(thumbnailType);
69 68
 
70 69
         return (
71 70
             <>
@@ -111,7 +110,6 @@ function _mapStateToProps(state, ownProps) {
111 110
     const { disableModeratorIndicator } = state['features/base/config'];
112 111
 
113 112
     return {
114
-        _currentLayout: getCurrentLayout(state),
115 113
         _showAudioMutedIndicator: isAudioMuted && audio,
116 114
         _showModeratorIndicator:
117 115
             !disableModeratorIndicator && participant && participant.role === PARTICIPANT_ROLE.MODERATOR && moderator,

+ 44
- 41
react/features/filmstrip/components/web/Thumbnail.js Parādīt failu

@@ -36,6 +36,7 @@ import {
36 36
     DISPLAY_MODE_TO_CLASS_NAME,
37 37
     DISPLAY_VIDEO,
38 38
     SHOW_TOOLBAR_CONTEXT_MENU_AFTER,
39
+    THUMBNAIL_TYPE,
39 40
     VIDEO_TEST_EVENTS
40 41
 } from '../../constants';
41 42
 import {
@@ -44,9 +45,9 @@ import {
44 45
     getDisplayModeInput,
45 46
     isVideoPlayable,
46 47
     showGridInVerticalView,
47
-    isStageFilmstripEnabled,
48
-    shouldDisplayStageFilmstrip
48
+    isStageFilmstripAvailable
49 49
 } from '../../functions';
50
+import { getThumbnailTypeFromLayout } from '../../functions.web';
50 51
 
51 52
 import FakeScreenShareParticipant from './FakeScreenShareParticipant';
52 53
 import ThumbnailAudioIndicator from './ThumbnailAudioIndicator';
@@ -91,11 +92,6 @@ export type Props = {|
91 92
      */
92 93
     _audioTrack: ?Object,
93 94
 
94
-    /**
95
-     * The current layout of the filmstrip.
96
-     */
97
-    _currentLayout: string,
98
-
99 95
     /**
100 96
      * Indicates whether the local video flip feature is disabled or not.
101 97
      */
@@ -189,9 +185,9 @@ export type Props = {|
189 185
     _raisedHand: boolean,
190 186
 
191 187
     /**
192
-     * Whether or not the stage filmstrip is disabled.
188
+     * Whether or not the current layout is stage filmstrip layout.
193 189
      */
194
-    _stageFilmstripDisabled: boolean,
190
+    _stageFilmstripLayout: boolean,
195 191
 
196 192
     /**
197 193
      * Whether or not the participants are displayed on stage.
@@ -200,6 +196,11 @@ export type Props = {|
200 196
      */
201 197
     _stageParticipantsVisible: boolean,
202 198
 
199
+    /**
200
+     * The type of thumbnail to display.
201
+     */
202
+    _thumbnailType: string,
203
+
203 204
     /**
204 205
      * The video object position for the participant.
205 206
      */
@@ -447,15 +448,15 @@ class Thumbnail extends Component<Props, State> {
447 448
      */
448 449
     _maybeSendScreenSharingIssueEvents(input) {
449 450
         const {
450
-            _currentLayout,
451 451
             _isAudioOnly,
452
-            _isScreenSharing
452
+            _isScreenSharing,
453
+            _thumbnailType
453 454
         } = this.props;
454 455
         const { displayMode } = this.state;
455
-        const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
456
+        const isTileType = _thumbnailType === THUMBNAIL_TYPE.TILE;
456 457
 
457 458
         if (!(DISPLAY_VIDEO === displayMode)
458
-            && tileViewActive
459
+            && isTileType
459 460
             && _isScreenSharing
460 461
             && !_isAudioOnly) {
461 462
             sendAnalytics(createScreenSharingIssueEvent({
@@ -530,9 +531,9 @@ class Thumbnail extends Component<Props, State> {
530 531
      * @returns {void}
531 532
      */
532 533
     _hidePopover() {
533
-        const { _currentLayout } = this.props;
534
+        const { _thumbnailType } = this.props;
534 535
 
535
-        if (_currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW) {
536
+        if (_thumbnailType === THUMBNAIL_TYPE.VERTICAL) {
536 537
             this.setState({
537 538
                 isHovered: false
538 539
             });
@@ -550,13 +551,13 @@ class Thumbnail extends Component<Props, State> {
550 551
     _getStyles(): Object {
551 552
         const { canPlayEventReceived } = this.state;
552 553
         const {
553
-            _currentLayout,
554 554
             _disableTileEnlargement,
555 555
             _height,
556 556
             _isFakeScreenShareParticipant,
557 557
             _isHidden,
558 558
             _isScreenSharing,
559 559
             _participant,
560
+            _thumbnailType,
560 561
             _videoObjectPosition,
561 562
             _videoTrack,
562 563
             _width,
@@ -564,7 +565,7 @@ class Thumbnail extends Component<Props, State> {
564 565
             style
565 566
         } = this.props;
566 567
 
567
-        const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
568
+        const isTileType = _thumbnailType === THUMBNAIL_TYPE.TILE;
568 569
         const jitsiVideoTrack = _videoTrack?.jitsiTrack;
569 570
         const track = jitsiVideoTrack?.track;
570 571
         const isPortraitVideo = ((track && track.getSettings()?.aspectRatio) || 1) < 1;
@@ -587,7 +588,7 @@ class Thumbnail extends Component<Props, State> {
587 588
         }
588 589
 
589 590
         let videoStyles = null;
590
-        const doNotStretchVideo = (isPortraitVideo && tileViewActive)
591
+        const doNotStretchVideo = (isPortraitVideo && isTileType)
591 592
             || _disableTileEnlargement
592 593
             || _isScreenSharing;
593 594
 
@@ -636,13 +637,13 @@ class Thumbnail extends Component<Props, State> {
636 637
      * @returns {void}
637 638
      */
638 639
     _onClick() {
639
-        const { _participant, dispatch, _stageFilmstripDisabled } = this.props;
640
+        const { _participant, dispatch, _stageFilmstripLayout } = this.props;
640 641
         const { id, pinned } = _participant;
641 642
 
642
-        if (_stageFilmstripDisabled) {
643
-            dispatch(pinParticipant(pinned ? null : id));
644
-        } else {
643
+        if (_stageFilmstripLayout) {
645 644
             dispatch(togglePinStageParticipant(id));
645
+        } else {
646
+            dispatch(pinParticipant(pinned ? null : id));
646 647
         }
647 648
     }
648 649
 
@@ -790,8 +791,8 @@ class Thumbnail extends Component<Props, State> {
790 791
         const {
791 792
             _isDominantSpeakerDisabled,
792 793
             _participant,
793
-            _currentLayout,
794 794
             _raisedHand,
795
+            _thumbnailType,
795 796
             classes
796 797
         } = this.props;
797 798
 
@@ -804,7 +805,7 @@ class Thumbnail extends Component<Props, State> {
804 805
         if (!_isDominantSpeakerDisabled && _participant?.dominantSpeaker) {
805 806
             className += ` ${classes.activeSpeaker} dominant-speaker`;
806 807
         }
807
-        if (_currentLayout !== LAYOUTS.TILE_VIEW && _participant?.pinned) {
808
+        if (_thumbnailType !== THUMBNAIL_TYPE.TILE && _participant?.pinned) {
808 809
             className += ' videoContainerFocused';
809 810
         }
810 811
 
@@ -902,16 +903,16 @@ class Thumbnail extends Component<Props, State> {
902 903
     _renderParticipant(local = false) {
903 904
         const {
904 905
             _audioTrack,
905
-            _currentLayout,
906 906
             _disableLocalVideoFlip,
907
+            _gifSrc,
907 908
             _isMobile,
908 909
             _isMobilePortrait,
909 910
             _isScreenSharing,
910 911
             _isTestModeEnabled,
911 912
             _localFlipX,
912 913
             _participant,
914
+            _thumbnailType,
913 915
             _videoTrack,
914
-            _gifSrc,
915 916
             classes,
916 917
             stageFilmstrip
917 918
         } = this.props;
@@ -975,28 +976,28 @@ class Thumbnail extends Component<Props, State> {
975 976
                 <div
976 977
                     className = { clsx(classes.indicatorsContainer,
977 978
                         classes.indicatorsTopContainer,
978
-                        _currentLayout === LAYOUTS.TILE_VIEW && 'tile-view-mode'
979
+                        _thumbnailType === THUMBNAIL_TYPE.TILE && 'tile-view-mode'
979 980
                     ) }>
980 981
                     <ThumbnailTopIndicators
981
-                        currentLayout = { _currentLayout }
982 982
                         hidePopover = { this._hidePopover }
983 983
                         indicatorsClassName = { classes.indicatorsBackground }
984 984
                         isHovered = { isHovered }
985 985
                         local = { local }
986 986
                         participantId = { id }
987 987
                         popoverVisible = { popoverVisible }
988
-                        showPopover = { this._showPopover } />
988
+                        showPopover = { this._showPopover }
989
+                        thumbnailType = { _thumbnailType } />
989 990
                 </div>
990 991
                 <div
991 992
                     className = { clsx(classes.indicatorsContainer,
992 993
                         classes.indicatorsBottomContainer,
993
-                        _currentLayout === LAYOUTS.TILE_VIEW && 'tile-view-mode'
994
+                        _thumbnailType === THUMBNAIL_TYPE.TILE && 'tile-view-mode'
994 995
                     ) }>
995 996
                     <ThumbnailBottomIndicators
996 997
                         className = { classes.indicatorsBackground }
997
-                        currentLayout = { _currentLayout }
998 998
                         local = { local }
999
-                        participantId = { id } />
999
+                        participantId = { id }
1000
+                        thumbnailType = { _thumbnailType } />
1000 1001
                 </div>
1001 1002
                 {!_gifSrc && this._renderAvatar(styles.avatar) }
1002 1003
                 { !local && (
@@ -1103,7 +1104,7 @@ function _mapStateToProps(state, ownProps): Object {
1103 1104
     }
1104 1105
     const _audioTrack = isLocal
1105 1106
         ? getLocalAudioTrack(tracks) : getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, participantID);
1106
-    const _currentLayout = stageFilmstrip ? LAYOUTS.TILE_VIEW : getCurrentLayout(state);
1107
+    const _currentLayout = getCurrentLayout(state);
1107 1108
     let size = {};
1108 1109
     let _isMobilePortrait = false;
1109 1110
     const {
@@ -1116,10 +1117,12 @@ function _mapStateToProps(state, ownProps): Object {
1116 1117
     const { localFlipX } = state['features/base/settings'];
1117 1118
     const _isMobile = isMobileBrowser();
1118 1119
     const activeParticipants = getActiveParticipantsIds(state);
1120
+    const tileType = getThumbnailTypeFromLayout(_currentLayout, stageFilmstrip);
1121
+
1119 1122
 
1120
-    switch (_currentLayout) {
1121
-    case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
1122
-    case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW: {
1123
+    switch (tileType) {
1124
+    case THUMBNAIL_TYPE.VERTICAL:
1125
+    case THUMBNAIL_TYPE.HORIZONTAL: {
1123 1126
         const {
1124 1127
             horizontalViewDimensions = {
1125 1128
                 local: {},
@@ -1133,7 +1136,7 @@ function _mapStateToProps(state, ownProps): Object {
1133 1136
         } = state['features/filmstrip'];
1134 1137
         const _verticalViewGrid = showGridInVerticalView(state);
1135 1138
         const { local, remote }
1136
-            = _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW
1139
+            = tileType === THUMBNAIL_TYPE.VERTICAL
1137 1140
                 ? verticalViewDimensions : horizontalViewDimensions;
1138 1141
         const { width, height } = (isLocal ? local : remote) ?? {};
1139 1142
 
@@ -1155,7 +1158,7 @@ function _mapStateToProps(state, ownProps): Object {
1155 1158
 
1156 1159
         break;
1157 1160
     }
1158
-    case LAYOUTS.TILE_VIEW: {
1161
+    case THUMBNAIL_TYPE.TILE: {
1159 1162
         const { thumbnailSize } = state['features/filmstrip'].tileViewDimensions;
1160 1163
         const {
1161 1164
             stageFilmstripDimensions = {
@@ -1186,7 +1189,6 @@ function _mapStateToProps(state, ownProps): Object {
1186 1189
 
1187 1190
     return {
1188 1191
         _audioTrack,
1189
-        _currentLayout,
1190 1192
         _defaultLocalDisplayName: defaultLocalDisplayName,
1191 1193
         _disableLocalVideoFlip: Boolean(disableLocalVideoFlip),
1192 1194
         _disableTileEnlargement: Boolean(disableTileEnlargement),
@@ -1204,8 +1206,9 @@ function _mapStateToProps(state, ownProps): Object {
1204 1206
         _localFlipX: Boolean(localFlipX),
1205 1207
         _participant: participant,
1206 1208
         _raisedHand: hasRaisedHand(participant),
1207
-        _stageFilmstripDisabled: !isStageFilmstripEnabled(state),
1208
-        _stageParticipantsVisible: shouldDisplayStageFilmstrip(state, 1),
1209
+        _stageFilmstripLayout: isStageFilmstripAvailable(state),
1210
+        _stageParticipantsVisible: _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW,
1211
+        _thumbnailType: tileType,
1209 1212
         _videoObjectPosition: getVideoObjectPosition(state, participant?.id),
1210 1213
         _videoTrack,
1211 1214
         ...size,

+ 13
- 12
react/features/filmstrip/components/web/ThumbnailBottomIndicators.js Parādīt failu

@@ -6,7 +6,7 @@ import { useSelector } from 'react-redux';
6 6
 
7 7
 import { isDisplayNameVisible, isNameReadOnly } from '../../../base/config/functions.any';
8 8
 import DisplayName from '../../../display-name/components/web/DisplayName';
9
-import { LAYOUTS } from '../../../video-layout';
9
+import { THUMBNAIL_TYPE } from '../../constants';
10 10
 
11 11
 import StatusIndicators from './StatusIndicators';
12 12
 
@@ -14,11 +14,6 @@ declare var interfaceConfig: Object;
14 14
 
15 15
 type Props = {
16 16
 
17
-    /**
18
-     * The current layout of the filmstrip.
19
-     */
20
-    currentLayout: string,
21
-
22 17
     /**
23 18
      * Class name for indicators container.
24 19
      */
@@ -37,7 +32,12 @@ type Props = {
37 32
     /**
38 33
      * Whether or not to show the status indicators.
39 34
      */
40
-    showStatusIndicators: string
35
+    showStatusIndicators: string,
36
+
37
+    /**
38
+     * The type of thumbnail.
39
+     */
40
+    thumbnailType: string
41 41
 }
42 42
 
43 43
 const useStyles = makeStyles(() => {
@@ -61,10 +61,10 @@ const useStyles = makeStyles(() => {
61 61
 
62 62
 const ThumbnailBottomIndicators = ({
63 63
     className,
64
-    currentLayout,
65 64
     local,
66 65
     participantId,
67
-    showStatusIndicators = true
66
+    showStatusIndicators = true,
67
+    thumbnailType
68 68
 }: Props) => {
69 69
     const styles = useStyles();
70 70
     const _allowEditing = !useSelector(isNameReadOnly);
@@ -77,17 +77,18 @@ const ThumbnailBottomIndicators = ({
77 77
                 audio = { true }
78 78
                 moderator = { true }
79 79
                 participantID = { participantId }
80
-                screenshare = { currentLayout === LAYOUTS.TILE_VIEW } />
80
+                screenshare = { thumbnailType === THUMBNAIL_TYPE.TILE }
81
+                thumbnailType = { thumbnailType } />
81 82
         }
82 83
         {
83 84
             _showDisplayName && (
84 85
                 <span className = { styles.nameContainer }>
85 86
                     <DisplayName
86 87
                         allowEditing = { local ? _allowEditing : false }
87
-                        currentLayout = { currentLayout }
88 88
                         displayNameSuffix = { local ? _defaultLocalDisplayName : '' }
89 89
                         elementID = { local ? 'localDisplayName' : `participant_${participantId}_name` }
90
-                        participantID = { participantId } />
90
+                        participantID = { participantId }
91
+                        thumbnailType = { thumbnailType } />
91 92
                 </span>
92 93
             )
93 94
         }

+ 17
- 16
react/features/filmstrip/components/web/ThumbnailTopIndicators.js Parādīt failu

@@ -8,8 +8,7 @@ import { useSelector } from 'react-redux';
8 8
 import { getSourceNameSignalingFeatureFlag } from '../../../base/config';
9 9
 import { isMobileBrowser } from '../../../base/environment/utils';
10 10
 import ConnectionIndicator from '../../../connection-indicator/components/web/ConnectionIndicator';
11
-import { LAYOUTS } from '../../../video-layout';
12
-import { STATS_POPOVER_POSITION } from '../../constants';
11
+import { STATS_POPOVER_POSITION, THUMBNAIL_TYPE } from '../../constants';
13 12
 import { getIndicatorsTooltipPosition } from '../../functions.web';
14 13
 
15 14
 import PinnedIndicator from './PinnedIndicator';
@@ -21,11 +20,6 @@ declare var interfaceConfig: Object;
21 20
 
22 21
 type Props = {
23 22
 
24
-    /**
25
-     * The current layout of the filmstrip.
26
-     */
27
-    currentLayout: string,
28
-
29 23
     /**
30 24
      * Hide popover callback.
31 25
      */
@@ -64,7 +58,12 @@ type Props = {
64 58
     /**
65 59
      * Show popover callback.
66 60
      */
67
-    showPopover: Function
61
+    showPopover: Function,
62
+
63
+    /**
64
+     * The type of thumbnail.
65
+     */
66
+    thumbnailType: string
68 67
 }
69 68
 
70 69
 const useStyles = makeStyles(() => {
@@ -80,7 +79,6 @@ const useStyles = makeStyles(() => {
80 79
 });
81 80
 
82 81
 const ThumbnailTopIndicators = ({
83
-    currentLayout,
84 82
     hidePopover,
85 83
     indicatorsClassName,
86 84
     isFakeScreenShareParticipant,
@@ -88,7 +86,8 @@ const ThumbnailTopIndicators = ({
88 86
     local,
89 87
     participantId,
90 88
     popoverVisible,
91
-    showPopover
89
+    showPopover,
90
+    thumbnailType
92 91
 }: Props) => {
93 92
     const styles = useStyles();
94 93
 
@@ -111,32 +110,34 @@ const ThumbnailTopIndicators = ({
111 110
                         enableStatsDisplay = { true }
112 111
                         iconSize = { _indicatorIconSize }
113 112
                         participantId = { participantId }
114
-                        statsPopoverPosition = { STATS_POPOVER_POSITION[currentLayout] } />
113
+                        statsPopoverPosition = { STATS_POPOVER_POSITION[thumbnailType] } />
115 114
                 }
116 115
             </div>
117 116
         );
118 117
     }
119 118
 
119
+    const tooltipPosition = getIndicatorsTooltipPosition(thumbnailType);
120
+
120 121
     return (
121 122
         <>
122 123
             <div className = { styles.container }>
123 124
                 <PinnedIndicator
124 125
                     iconSize = { _indicatorIconSize }
125 126
                     participantId = { participantId }
126
-                    tooltipPosition = { getIndicatorsTooltipPosition(currentLayout) } />
127
+                    tooltipPosition = { tooltipPosition } />
127 128
                 {!_connectionIndicatorDisabled
128 129
                     && <ConnectionIndicator
129 130
                         alwaysVisible = { showConnectionIndicator }
130 131
                         enableStatsDisplay = { true }
131 132
                         iconSize = { _indicatorIconSize }
132 133
                         participantId = { participantId }
133
-                        statsPopoverPosition = { STATS_POPOVER_POSITION[currentLayout] } />
134
+                        statsPopoverPosition = { STATS_POPOVER_POSITION[thumbnailType] } />
134 135
                 }
135 136
                 <RaisedHandIndicator
136 137
                     iconSize = { _indicatorIconSize }
137 138
                     participantId = { participantId }
138
-                    tooltipPosition = { getIndicatorsTooltipPosition(currentLayout) } />
139
-                {currentLayout !== LAYOUTS.TILE_VIEW && (
139
+                    tooltipPosition = { tooltipPosition } />
140
+                {thumbnailType !== THUMBNAIL_TYPE.TILE && (
140 141
                     <div className = { clsx(indicatorsClassName, 'top-indicators') }>
141 142
                         <StatusIndicators
142 143
                             participantID = { participantId }
@@ -146,12 +147,12 @@ const ThumbnailTopIndicators = ({
146 147
             </div>
147 148
             <div className = { styles.container }>
148 149
                 <VideoMenuTriggerButton
149
-                    currentLayout = { currentLayout }
150 150
                     hidePopover = { hidePopover }
151 151
                     local = { local }
152 152
                     participantId = { participantId }
153 153
                     popoverVisible = { popoverVisible }
154 154
                     showPopover = { showPopover }
155
+                    thumbnailType = { thumbnailType }
155 156
                     visible = { isHovered } />
156 157
             </div>
157 158
         </>);

+ 10
- 10
react/features/filmstrip/components/web/VideoMenuTriggerButton.js Parādīt failu

@@ -6,11 +6,6 @@ import { LocalVideoMenuTriggerButton, RemoteVideoMenuTriggerButton } from '../..
6 6
 
7 7
 type Props = {
8 8
 
9
-    /**
10
-     * The current layout of the filmstrip.
11
-     */
12
-    currentLayout: string,
13
-
14 9
     /**
15 10
      * Hide popover callback.
16 11
      */
@@ -36,6 +31,11 @@ type Props = {
36 31
      */
37 32
     showPopover: Function,
38 33
 
34
+    /**
35
+     * The type of thumbnail.
36
+     */
37
+    thumbnailType: string,
38
+
39 39
     /**
40 40
      * Whether or not the component is visible.
41 41
      */
@@ -44,33 +44,33 @@ type Props = {
44 44
 
45 45
 // eslint-disable-next-line no-confusing-arrow
46 46
 const VideoMenuTriggerButton = ({
47
-    currentLayout,
48 47
     hidePopover,
49 48
     local,
50 49
     participantId,
51 50
     popoverVisible,
52 51
     showPopover,
52
+    thumbnailType,
53 53
     visible
54 54
 }: Props) => local
55 55
     ? (
56 56
         <span id = 'localvideomenu'>
57 57
             <LocalVideoMenuTriggerButton
58 58
                 buttonVisible = { visible }
59
-                currentLayout = { currentLayout }
60 59
                 hidePopover = { hidePopover }
61 60
                 popoverVisible = { popoverVisible }
62
-                showPopover = { showPopover } />
61
+                showPopover = { showPopover }
62
+                thumbnailType = { thumbnailType } />
63 63
         </span>
64 64
     )
65 65
     : (
66 66
         <span id = 'remotevideomenu'>
67 67
             <RemoteVideoMenuTriggerButton
68 68
                 buttonVisible = { visible }
69
-                currentLayout = { currentLayout }
70 69
                 hidePopover = { hidePopover }
71 70
                 participantID = { participantId }
72 71
                 popoverVisible = { popoverVisible }
73
-                showPopover = { showPopover } />
72
+                showPopover = { showPopover }
73
+                thumbnailType = { thumbnailType } />
74 74
         </span>
75 75
     );
76 76
 

+ 15
- 7
react/features/filmstrip/constants.js Parādīt failu

@@ -1,7 +1,6 @@
1 1
 // @flow
2 2
 
3 3
 import { BoxModel } from '../base/styles';
4
-import { LAYOUTS } from '../video-layout/constants';
5 4
 
6 5
 /**
7 6
  * The size (height and width) of the small (not tile view) thumbnails.
@@ -228,22 +227,31 @@ export const SHOW_TOOLBAR_CONTEXT_MENU_AFTER = 600;
228 227
  */
229 228
 export const TILE_MARGIN = 10;
230 229
 
230
+/**
231
+ * The types of thumbnails for filmstrip.
232
+ */
233
+export const THUMBNAIL_TYPE = {
234
+    TILE: 'TILE',
235
+    VERTICAL: 'VERTICAL',
236
+    HORIZONTAL: 'HORIZONTAL'
237
+};
238
+
231 239
 /**
232 240
  * The popover position for the connection stats table.
233 241
  */
234 242
 export const STATS_POPOVER_POSITION = {
235
-    [LAYOUTS.TILE_VIEW]: 'right-start',
236
-    [LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'left-start',
237
-    [LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'top-end'
243
+    [THUMBNAIL_TYPE.TILE]: 'right-start',
244
+    [THUMBNAIL_TYPE.VERTICAL]: 'left-start',
245
+    [THUMBNAIL_TYPE.HORIZONTAL]: 'top-end'
238 246
 };
239 247
 
240 248
 /**
241 249
  * The tooltip position for the indicators on the thumbnail.
242 250
  */
243 251
 export const INDICATORS_TOOLTIP_POSITION = {
244
-    [LAYOUTS.TILE_VIEW]: 'right',
245
-    [LAYOUTS.VERTICAL_FILMSTRIP_VIEW]: 'left',
246
-    [LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW]: 'top'
252
+    [THUMBNAIL_TYPE.TILE]: 'right',
253
+    [THUMBNAIL_TYPE.VERTICAL]: 'left',
254
+    [THUMBNAIL_TYPE.HORIZONTAL]: 'top'
247 255
 };
248 256
 
249 257
 /**

+ 2
- 2
react/features/filmstrip/functions.native.js Parādīt failu

@@ -103,11 +103,11 @@ export function isReorderingEnabled(state) {
103 103
 }
104 104
 
105 105
 /**
106
- * Whether the stage filmstrip is disabled or not.
106
+ * Whether the stage filmstrip is available or not.
107 107
  *
108 108
  * @param {Object} state - Redux state.
109 109
  * @returns {boolean}
110 110
  */
111
-export function isStageFilmstripEnabled() {
111
+export function isStageFilmstripAvailable() {
112 112
     return false;
113 113
 }

+ 52
- 16
react/features/filmstrip/functions.web.js Parādīt failu

@@ -35,6 +35,7 @@ import {
35 35
     INDICATORS_TOOLTIP_POSITION,
36 36
     SCROLL_SIZE,
37 37
     SQUARE_TILE_ASPECT_RATIO,
38
+    THUMBNAIL_TYPE,
38 39
     TILE_ASPECT_RATIO,
39 40
     TILE_HORIZONTAL_MARGIN,
40 41
     TILE_MIN_HEIGHT_LARGE,
@@ -243,18 +244,16 @@ export function getNumberOfPartipantsForTileView(state) {
243 244
  * disabled.
244 245
  *
245 246
  * @param {Object} state - The redux store state.
246
- * @param {boolean} stageFilmstrip - Whether the dimensions should be calculated for the stage filmstrip.
247 247
  * @returns {Object} - The dimensions.
248 248
  */
249
-export function calculateNonResponsiveTileViewDimensions(state, stageFilmstrip = false) {
249
+export function calculateNonResponsiveTileViewDimensions(state) {
250 250
     const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
251 251
     const { disableTileEnlargement } = state['features/base/config'];
252
-    const { columns: c, minVisibleRows, rows: r } = getNotResponsiveTileViewGridDimensions(state, stageFilmstrip);
253
-    const filmstripWidth = getVerticalViewMaxWidth(state);
252
+    const { columns: c, minVisibleRows, rows: r } = getNotResponsiveTileViewGridDimensions(state);
254 253
     const size = calculateThumbnailSizeForTileView({
255 254
         columns: c,
256 255
         minVisibleRows,
257
-        clientWidth: clientWidth - (stageFilmstrip ? filmstripWidth : 0),
256
+        clientWidth,
258 257
         clientHeight,
259 258
         disableTileEnlargement,
260 259
         disableResponsiveTiles: true
@@ -515,6 +514,7 @@ export function computeDisplayModeFromInput(input: Object) {
515 514
         canPlayEventReceived,
516 515
         isRemoteParticipant,
517 516
         stageParticipantsVisible,
517
+        stageFilmstrip,
518 518
         tileViewActive
519 519
     } = input;
520 520
     const adjustedIsVideoPlayable = input.isVideoPlayable && (!isRemoteParticipant || canPlayEventReceived);
@@ -524,7 +524,7 @@ export function computeDisplayModeFromInput(input: Object) {
524 524
     }
525 525
 
526 526
     if (!tileViewActive && ((isScreenSharing && isRemoteParticipant)
527
-        || (stageParticipantsVisible && isActiveParticipant))) {
527
+        || (stageParticipantsVisible && isActiveParticipant && !stageFilmstrip))) {
528 528
         return DISPLAY_AVATAR;
529 529
     } else if (isCurrentlyOnLargeVideo && !tileViewActive) {
530 530
         // Display name is always and only displayed when user is on the stage
@@ -556,7 +556,8 @@ export function getDisplayModeInput(props: Object, state: Object) {
556 556
         _isVideoPlayable,
557 557
         _participant,
558 558
         _stageParticipantsVisible,
559
-        _videoTrack
559
+        _videoTrack,
560
+        stageFilmstrip
560 561
     } = props;
561 562
     const tileViewActive = _currentLayout === LAYOUTS.TILE_VIEW;
562 563
     const { canPlayEventReceived } = state;
@@ -574,6 +575,7 @@ export function getDisplayModeInput(props: Object, state: Object) {
574 575
         isScreenSharing: _isScreenSharing,
575 576
         isFakeScreenShareParticipant: _isFakeScreenShareParticipant,
576 577
         stageParticipantsVisible: _stageParticipantsVisible,
578
+        stageFilmstrip,
577 579
         videoStreamMuted: _videoTrack ? _videoTrack.muted : 'no stream'
578 580
     };
579 581
 }
@@ -581,11 +583,11 @@ export function getDisplayModeInput(props: Object, state: Object) {
581 583
 /**
582 584
  * Gets the tooltip position for the thumbnail indicators.
583 585
  *
584
- * @param {string} currentLayout - The current layout of the app.
586
+ * @param {string} thumbnailType - The current thumbnail type.
585 587
  * @returns {string}
586 588
  */
587
-export function getIndicatorsTooltipPosition(currentLayout: string) {
588
-    return INDICATORS_TOOLTIP_POSITION[currentLayout] || 'top';
589
+export function getIndicatorsTooltipPosition(thumbnailType: string) {
590
+    return INDICATORS_TOOLTIP_POSITION[thumbnailType] || 'top';
589 591
 }
590 592
 
591 593
 /**
@@ -599,7 +601,7 @@ export function isFilmstripResizable(state: Object) {
599 601
     const _currentLayout = getCurrentLayout(state);
600 602
 
601 603
     return !filmstrip?.disableResizable && !isMobileBrowser()
602
-        && _currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW;
604
+        && (_currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW || _currentLayout === LAYOUTS.STAGE_FILMSTRIP_VIEW);
603 605
 }
604 606
 
605 607
 /**
@@ -665,7 +667,8 @@ export function isFilmstripScrollVisible(state) {
665 667
     case LAYOUTS.TILE_VIEW:
666 668
         ({ hasScroll = false } = state['features/filmstrip'].tileViewDimensions);
667 669
         break;
668
-    case LAYOUTS.VERTICAL_FILMSTRIP_VIEW: {
670
+    case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
671
+    case LAYOUTS.STAGE_FILMSTRIP_VIEW: {
669 672
         ({ hasScroll = false } = state['features/filmstrip'].verticalViewDimensions);
670 673
         break;
671 674
     }
@@ -703,21 +706,30 @@ export function getPinnedActiveParticipants(state) {
703 706
 }
704 707
 
705 708
 /**
706
- * Get whether or not the stage filmstrip should be displayed.
709
+ * Get whether or not the stage filmstrip is available (enabled & can be used).
707 710
  *
708 711
  * @param {Object} state - Redux state.
709 712
  * @param {number} minParticipantCount - The min number of participants for the stage filmstrip
710 713
  * to be displayed.
711 714
  * @returns {boolean}
712 715
  */
713
-export function shouldDisplayStageFilmstrip(state, minParticipantCount = 2) {
716
+export function isStageFilmstripAvailable(state, minParticipantCount = 0) {
714 717
     const { activeParticipants } = state['features/filmstrip'];
715 718
     const { remoteScreenShares } = state['features/video-layout'];
716
-    const currentLayout = getCurrentLayout(state);
717 719
     const sharedVideo = isSharingStatus(state['features/shared-video']?.status);
718 720
 
719 721
     return isStageFilmstripEnabled(state) && remoteScreenShares.length === 0 && !sharedVideo
720
-        && activeParticipants.length >= minParticipantCount && currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW;
722
+        && activeParticipants.length >= minParticipantCount;
723
+}
724
+
725
+/**
726
+ * Get whether or not the stage filmstrip should be displayed.
727
+ *
728
+ * @param {Object} state - Redux state.
729
+ * @returns {boolean}
730
+ */
731
+export function shouldDisplayStageFilmstrip(state) {
732
+    return isStageFilmstripAvailable(state, 2);
721 733
 }
722 734
 
723 735
 /**
@@ -731,3 +743,27 @@ export function isStageFilmstripEnabled(state) {
731 743
 
732 744
     return !(filmstrip?.disableStageFilmstrip ?? true) && interfaceConfig.VERTICAL_FILMSTRIP;
733 745
 }
746
+
747
+/**
748
+ * Gets the thumbnail type by filmstrip type.
749
+ *
750
+ * @param {string} currentLayout - Current app layout.
751
+ * @param {boolean} isStageFilmstrip - Whether the filmstrip is stage filmstrip or not.
752
+ * @returns {string}
753
+ */
754
+export function getThumbnailTypeFromLayout(currentLayout, isStageFilmstrip = false) {
755
+    switch (currentLayout) {
756
+    case LAYOUTS.TILE_VIEW:
757
+        return THUMBNAIL_TYPE.TILE;
758
+    case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
759
+        return THUMBNAIL_TYPE.VERTICAL;
760
+    case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW:
761
+        return THUMBNAIL_TYPE.HORIZONTAL;
762
+    case LAYOUTS.STAGE_FILMSTRIP_VIEW:
763
+        if (isStageFilmstrip) {
764
+            return THUMBNAIL_TYPE.TILE;
765
+        }
766
+
767
+        return THUMBNAIL_TYPE.VERTICAL;
768
+    }
769
+}

+ 19
- 6
react/features/filmstrip/middleware.web.js Parādīt failu

@@ -22,6 +22,7 @@ import {
22 22
 
23 23
 import {
24 24
     ADD_STAGE_PARTICIPANT,
25
+    CLEAR_STAGE_PARTICIPANTS,
25 26
     REMOVE_STAGE_PARTICIPANT,
26 27
     SET_MAX_STAGE_PARTICIPANTS,
27 28
     SET_USER_FILMSTRIP_WIDTH,
@@ -42,10 +43,12 @@ import {
42 43
 import {
43 44
     isFilmstripResizable,
44 45
     updateRemoteParticipants,
45
-    updateRemoteParticipantsOnLeave
46
+    updateRemoteParticipantsOnLeave,
47
+    getActiveParticipantsIds,
48
+    getPinnedActiveParticipants,
49
+    isStageFilmstripAvailable
46 50
 } from './functions';
47 51
 import './subscriber';
48
-import { getActiveParticipantsIds, getPinnedActiveParticipants, isStageFilmstripEnabled } from './functions.web';
49 52
 
50 53
 /**
51 54
  * Map of timers.
@@ -202,15 +205,15 @@ MiddlewareRegistry.register(store => next => action => {
202 205
     case DOMINANT_SPEAKER_CHANGED: {
203 206
         const { id } = action.participant;
204 207
         const state = store.getState();
205
-        const stageFilmstrip = isStageFilmstripEnabled(state);
206
-        const currentLayout = getCurrentLayout(state);
208
+        const stageFilmstrip = isStageFilmstripAvailable(state);
207 209
         const local = getLocalParticipant(state);
210
+        const currentLayout = getCurrentLayout(state);
208 211
 
209
-        if (id === local.id) {
212
+        if (id === local.id || currentLayout === LAYOUTS.TILE_VIEW) {
210 213
             break;
211 214
         }
212 215
 
213
-        if (stageFilmstrip && currentLayout === LAYOUTS.VERTICAL_FILMSTRIP_VIEW) {
216
+        if (stageFilmstrip) {
214 217
             const isPinned = getPinnedActiveParticipants(state).some(p => p.participantId === id);
215 218
 
216 219
             store.dispatch(addStageParticipant(id, Boolean(isPinned)));
@@ -276,7 +279,17 @@ MiddlewareRegistry.register(store => next => action => {
276 279
         } else {
277 280
             dispatch(addStageParticipant(participantId, true));
278 281
         }
282
+        break;
283
+    }
284
+    case CLEAR_STAGE_PARTICIPANTS: {
285
+        const activeParticipants = getActiveParticipantsIds(store.getState());
286
+
287
+        activeParticipants.forEach(pId => {
288
+            const tid = timers.get(pId);
279 289
 
290
+            clearTimeout(tid);
291
+            timers.delete(pId);
292
+        });
280 293
     }
281 294
     }
282 295
 

+ 8
- 1
react/features/filmstrip/reducer.js Parādīt failu

@@ -18,7 +18,8 @@ import {
18 18
     SET_VERTICAL_VIEW_DIMENSIONS,
19 19
     SET_VISIBLE_REMOTE_PARTICIPANTS,
20 20
     SET_VOLUME,
21
-    SET_MAX_STAGE_PARTICIPANTS
21
+    SET_MAX_STAGE_PARTICIPANTS,
22
+    CLEAR_STAGE_PARTICIPANTS
22 23
 } from './actionTypes';
23 24
 
24 25
 const DEFAULT_STATE = {
@@ -273,6 +274,12 @@ ReducerRegistry.register(
273 274
                 maxStageParticipants: action.maxParticipants
274 275
             };
275 276
         }
277
+        case CLEAR_STAGE_PARTICIPANTS: {
278
+            return {
279
+                ...state,
280
+                activeParticipants: []
281
+            };
282
+        }
276 283
         }
277 284
 
278 285
         return state;

+ 9
- 3
react/features/filmstrip/subscriber.web.js Parādīt failu

@@ -1,7 +1,7 @@
1 1
 // @flow
2 2
 
3 3
 import { isMobileBrowser } from '../base/environment/utils';
4
-import { getParticipantCountWithFake } from '../base/participants';
4
+import { getParticipantCountWithFake, pinParticipant } from '../base/participants';
5 5
 import { StateListenerRegistry } from '../base/redux';
6 6
 import { clientResized } from '../base/responsive-ui';
7 7
 import { shouldHideSelfView } from '../base/settings';
@@ -17,6 +17,7 @@ import {
17 17
     setTileViewDimensions,
18 18
     setVerticalViewDimensions
19 19
 } from './actions';
20
+import { clearStageParticipants } from './actions.web';
20 21
 import {
21 22
     ASPECT_RATIO_BREAKPOINT,
22 23
     DISPLAY_DRAWER_THRESHOLD
@@ -24,7 +25,6 @@ import {
24 25
 import {
25 26
     isFilmstripResizable,
26 27
     isFilmstripScrollVisible,
27
-    shouldDisplayStageFilmstrip,
28 28
     updateRemoteParticipants
29 29
 } from './functions';
30 30
 
@@ -74,6 +74,12 @@ StateListenerRegistry.register(
74 74
             break;
75 75
         case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
76 76
             store.dispatch(setVerticalViewDimensions());
77
+            if (store.getState()['features/filmstrip'].activeParticipants.length > 1) {
78
+                store.dispatch(clearStageParticipants());
79
+            }
80
+            break;
81
+        case LAYOUTS.STAGE_FILMSTRIP_VIEW:
82
+            store.dispatch(pinParticipant(null));
77 83
             break;
78 84
         }
79 85
     }, {
@@ -177,7 +183,7 @@ StateListenerRegistry.register(
177 183
         };
178 184
     },
179 185
     /* listener */(_, store) => {
180
-        if (shouldDisplayStageFilmstrip(store.getState())) {
186
+        if (getCurrentLayout(store.getState()) === LAYOUTS.STAGE_FILMSTRIP_VIEW) {
181 187
             store.dispatch(setStageFilmstripViewDimensions());
182 188
         }
183 189
     }, {

+ 7
- 2
react/features/large-video/actions.any.js Parādīt failu

@@ -9,7 +9,8 @@ import {
9 9
     getPinnedParticipant,
10 10
     getRemoteParticipants
11 11
 } from '../base/participants';
12
-import { isStageFilmstripEnabled } from '../filmstrip/functions';
12
+import { isStageFilmstripAvailable } from '../filmstrip/functions';
13
+import { shouldDisplayStageFilmstrip } from '../filmstrip/functions.web';
13 14
 
14 15
 import {
15 16
     SELECT_LARGE_VIDEO_PARTICIPANT,
@@ -30,6 +31,10 @@ export function selectParticipantInLargeVideo(participant: ?string) {
30 31
     return (dispatch: Dispatch<any>, getState: Function) => {
31 32
         const state = getState();
32 33
 
34
+        if (shouldDisplayStageFilmstrip(state)) {
35
+            return;
36
+        }
37
+
33 38
         // Keep Etherpad open.
34 39
         if (state['features/etherpad'].editing) {
35 40
             return;
@@ -103,7 +108,7 @@ function _electLastVisibleRemoteVideo(tracks) {
103 108
  * @returns {(string|undefined)}
104 109
  */
105 110
 function _electParticipantInLargeVideo(state) {
106
-    const stageFilmstrip = isStageFilmstripEnabled(state);
111
+    const stageFilmstrip = isStageFilmstripAvailable(state);
107 112
     let participant;
108 113
 
109 114
     if (!stageFilmstrip) {

+ 2
- 1
react/features/video-layout/constants.js Parādīt failu

@@ -6,5 +6,6 @@
6 6
 export const LAYOUTS = {
7 7
     HORIZONTAL_FILMSTRIP_VIEW: 'horizontal-filmstrip-view',
8 8
     TILE_VIEW: 'tile-view',
9
-    VERTICAL_FILMSTRIP_VIEW: 'vertical-filmstrip-view'
9
+    VERTICAL_FILMSTRIP_VIEW: 'vertical-filmstrip-view',
10
+    STAGE_FILMSTRIP_VIEW: 'stage-filmstrip-view'
10 11
 };

+ 5
- 0
react/features/video-layout/functions.any.js Parādīt failu

@@ -7,6 +7,7 @@ import {
7 7
     getParticipantCount,
8 8
     pinParticipant
9 9
 } from '../base/participants';
10
+import { shouldDisplayStageFilmstrip } from '../filmstrip/functions.web';
10 11
 import { isVideoPlaying } from '../shared-video/functions';
11 12
 import { VIDEO_QUALITY_LEVELS } from '../video-quality/constants';
12 13
 
@@ -40,6 +41,10 @@ export function getCurrentLayout(state: Object) {
40 41
     if (shouldDisplayTileView(state)) {
41 42
         return LAYOUTS.TILE_VIEW;
42 43
     } else if (interfaceConfig.VERTICAL_FILMSTRIP) {
44
+        if (shouldDisplayStageFilmstrip(state)) {
45
+            return LAYOUTS.STAGE_FILMSTRIP_VIEW;
46
+        }
47
+
43 48
         return LAYOUTS.VERTICAL_FILMSTRIP_VIEW;
44 49
     }
45 50
 

+ 6
- 6
react/features/video-menu/components/web/LocalVideoMenuTriggerButton.js Parādīt failu

@@ -18,8 +18,8 @@ import { setParticipantContextMenuOpen } from '../../../base/responsive-ui/actio
18 18
 import { getHideSelfView } from '../../../base/settings';
19 19
 import { getLocalVideoTrack } from '../../../base/tracks';
20 20
 import ConnectionIndicatorContent from '../../../connection-indicator/components/web/ConnectionIndicatorContent';
21
+import { THUMBNAIL_TYPE } from '../../../filmstrip';
21 22
 import { isStageFilmstripEnabled } from '../../../filmstrip/functions.web';
22
-import { LAYOUTS } from '../../../video-layout';
23 23
 import { renderConnectionStatus } from '../../actions.web';
24 24
 
25 25
 import ConnectionStatusButton from './ConnectionStatusButton';
@@ -274,7 +274,7 @@ class LocalVideoMenuTriggerButton extends Component<Props> {
274 274
  * @returns {Props}
275 275
  */
276 276
 function _mapStateToProps(state, ownProps) {
277
-    const { currentLayout } = ownProps;
277
+    const { thumbnailType } = ownProps;
278 278
     const localParticipant = getLocalParticipant(state);
279 279
     const { disableLocalVideoFlip, disableSelfViewSettings } = state['features/base/config'];
280 280
     const videoTrack = getLocalVideoTrack(state['features/base/tracks']);
@@ -284,14 +284,14 @@ function _mapStateToProps(state, ownProps) {
284 284
 
285 285
     let _menuPosition;
286 286
 
287
-    switch (currentLayout) {
288
-    case LAYOUTS.TILE_VIEW:
287
+    switch (thumbnailType) {
288
+    case THUMBNAIL_TYPE.TILE:
289 289
         _menuPosition = 'left-start';
290 290
         break;
291
-    case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
291
+    case THUMBNAIL_TYPE.VERTICAL:
292 292
         _menuPosition = 'left-start';
293 293
         break;
294
-    case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW:
294
+    case THUMBNAIL_TYPE.HORIZONTAL:
295 295
         _menuPosition = 'top-start';
296 296
         break;
297 297
     default:

+ 6
- 6
react/features/video-menu/components/web/RemoteVideoMenuTriggerButton.js Parādīt failu

@@ -14,7 +14,7 @@ import { getParticipantById } from '../../../base/participants';
14 14
 import { Popover } from '../../../base/popover';
15 15
 import { connect } from '../../../base/redux';
16 16
 import { setParticipantContextMenuOpen } from '../../../base/responsive-ui/actions';
17
-import { LAYOUTS } from '../../../video-layout';
17
+import { THUMBNAIL_TYPE } from '../../../filmstrip';
18 18
 import { renderConnectionStatus } from '../../actions.web';
19 19
 
20 20
 import ParticipantContextMenu from './ParticipantContextMenu';
@@ -265,7 +265,7 @@ class RemoteVideoMenuTriggerButton extends Component<Props> {
265 265
  * @returns {Props}
266 266
  */
267 267
 function _mapStateToProps(state, ownProps) {
268
-    const { participantID, currentLayout } = ownProps;
268
+    const { participantID, thumbnailType } = ownProps;
269 269
     let _remoteControlState = null;
270 270
     const participant = getParticipantById(state, participantID);
271 271
     const _participantDisplayName = participant?.name;
@@ -291,14 +291,14 @@ function _mapStateToProps(state, ownProps) {
291 291
 
292 292
     let _menuPosition;
293 293
 
294
-    switch (currentLayout) {
295
-    case LAYOUTS.TILE_VIEW:
294
+    switch (thumbnailType) {
295
+    case THUMBNAIL_TYPE.TILE:
296 296
         _menuPosition = 'left-start';
297 297
         break;
298
-    case LAYOUTS.VERTICAL_FILMSTRIP_VIEW:
298
+    case THUMBNAIL_TYPE.VERTICAL:
299 299
         _menuPosition = 'left-end';
300 300
         break;
301
-    case LAYOUTS.HORIZONTAL_FILMSTRIP_VIEW:
301
+    case THUMBNAIL_TYPE.HORIZONTAL:
302 302
         _menuPosition = 'top';
303 303
         break;
304 304
     default:

Notiek ielāde…
Atcelt
Saglabāt