Przeglądaj źródła

feat(large-video/web) Add screen share placeholder (#11971)

* feat(large-video/web) new ScreenSharePlaceholder component
factor2
apetrus20 3 lat temu
rodzic
commit
52ce9a86ed
No account linked to committer's email address

+ 4
- 0
lang/main.json Wyświetl plik

@@ -518,6 +518,10 @@
518 518
         "toggleShortcuts": "Show or hide keyboard shortcuts",
519 519
         "videoMute": "Start or stop your camera"
520 520
     },
521
+    "largeVideo": {
522
+        "screenIsShared": "You are sharing your screen",
523
+        "showMeWhatImSharing": "Show me what I'm sharing"
524
+    },
521 525
     "liveStreaming": {
522 526
         "busy": "We're working on freeing streaming resources. Please try again in a few minutes.",
523 527
         "busyTitle": "All streamers are currently busy",

+ 17
- 12
modules/UI/videolayout/VideoContainer.js Wyświetl plik

@@ -480,7 +480,7 @@ export class VideoContainer extends LargeContainer {
480 480
      */
481 481
     setStream(userID, stream, videoType) {
482 482
         this.userId = userID;
483
-        if (this.stream === stream) {
483
+        if (this.stream === stream && !stream?.forceStreamToReattach) {
484 484
             // Handles the use case for the remote participants when the
485 485
             // videoType is received with delay after turning on/off the
486 486
             // desktop sharing.
@@ -492,8 +492,12 @@ export class VideoContainer extends LargeContainer {
492 492
             return;
493 493
         }
494 494
 
495
+        if (stream?.forceStreamToReattach) {
496
+            delete stream.forceStreamToReattach;
497
+        }
498
+
495 499
         // detach old stream
496
-        if (this.stream) {
500
+        if (this.stream && this.$video[0]) {
497 501
             this.stream.detach(this.$video[0]);
498 502
         }
499 503
 
@@ -504,19 +508,20 @@ export class VideoContainer extends LargeContainer {
504 508
             return;
505 509
         }
506 510
 
507
-        stream.attach(this.$video[0]);
511
+        if (this.$video[0]) {
512
+            stream.attach(this.$video[0]);
508 513
 
509
-        // Ensure large video gets play() called on it when a new stream is attached to it. This is necessary in the
510
-        // case of Safari as autoplay doesn't kick-in automatically on Safari 15 and newer versions.
511
-        browser.isWebKitBased() && this.$video[0].play();
514
+            // Ensure large video gets play() called on it when a new stream is attached to it. This is necessary in the
515
+            // case of Safari as autoplay doesn't kick-in automatically on Safari 15 and newer versions.
516
+            browser.isWebKitBased() && this.$video[0].play();
512 517
 
513
-        const flipX = stream.isLocal() && this.localFlipX && !this.isScreenSharing();
514
-
515
-        this.$video.css({
516
-            transform: flipX ? 'scaleX(-1)' : 'none'
517
-        });
518
+            const flipX = stream.isLocal() && this.localFlipX && !this.isScreenSharing();
518 519
 
519
-        this._updateBackground();
520
+            this.$video.css({
521
+                transform: flipX ? 'scaleX(-1)' : 'none'
522
+            });
523
+            this._updateBackground();
524
+        }
520 525
     }
521 526
 
522 527
     /**

+ 6
- 2
modules/UI/videolayout/VideoLayout.js Wyświetl plik

@@ -177,7 +177,7 @@ const VideoLayout = {
177 177
         return largeVideo && largeVideo.id === id;
178 178
     },
179 179
 
180
-    updateLargeVideo(id, forceUpdate) {
180
+    updateLargeVideo(id, forceUpdate, forceStreamToReattach = false) {
181 181
         if (!largeVideo) {
182 182
             return;
183 183
         }
@@ -198,6 +198,10 @@ const VideoLayout = {
198 198
 
199 199
         const videoStream = videoTrack?.jitsiTrack;
200 200
 
201
+        if (videoStream && forceStreamToReattach) {
202
+            videoStream.forceStreamToReattach = forceStreamToReattach;
203
+        }
204
+
201 205
         if (isOnLarge && !forceUpdate
202 206
                 && LargeVideoManager.isVideoContainer(currentContainerType)
203 207
                 && videoStream) {
@@ -330,7 +334,7 @@ const VideoLayout = {
330 334
      */
331 335
     _updateLargeVideoIfDisplayed(participantId, force = false) {
332 336
         if (this.isCurrentlyOnLarge(participantId)) {
333
-            this.updateLargeVideo(participantId, force);
337
+            this.updateLargeVideo(participantId, force, false);
334 338
         }
335 339
     },
336 340
 

+ 11
- 0
react/features/large-video/actionTypes.ts Wyświetl plik

@@ -30,3 +30,14 @@ export const UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION
30 30
  */
31 31
 export const UPDATE_LAST_LARGE_VIDEO_MEDIA_EVENT
32 32
     = 'UPDATE_LAST_LARGE_VIDEO_MEDIA_EVENT';
33
+
34
+/**
35
+ * Action to set the redux store of the current show me what I'm sharing flag value.
36
+ *
37
+ * @returns {{
38
+ *     type: SET_SEE_WHAT_IS_BEING_SHARED,
39
+ *     seeWhatIsBeingShared: boolean
40
+ * }}
41
+ */
42
+export const SET_SEE_WHAT_IS_BEING_SHARED
43
+        = 'SET_SEE_WHAT_IS_BEING_SHARED';

+ 17
- 1
react/features/large-video/actions.web.js Wyświetl plik

@@ -6,7 +6,7 @@ import VideoLayout from '../../../modules/UI/videolayout/VideoLayout';
6 6
 import { MEDIA_TYPE } from '../base/media';
7 7
 import { getTrackByMediaTypeAndParticipant } from '../base/tracks';
8 8
 
9
-import { UPDATE_LAST_LARGE_VIDEO_MEDIA_EVENT } from './actionTypes';
9
+import { UPDATE_LAST_LARGE_VIDEO_MEDIA_EVENT, SET_SEE_WHAT_IS_BEING_SHARED } from './actionTypes';
10 10
 
11 11
 export * from './actions.any';
12 12
 
@@ -101,3 +101,19 @@ export function updateLastLargeVideoMediaEvent(name: String) {
101 101
         name
102 102
     };
103 103
 }
104
+
105
+/**
106
+ * Updates the value used to display what is being shared.
107
+ *
108
+ * @param {boolean} seeWhatIsBeingShared - The current value.
109
+ * @returns {{
110
+ *     type: SET_SEE_WHAT_IS_BEING_SHARED,
111
+ *     seeWhatIsBeingShared: boolean
112
+ * }}
113
+ */
114
+export function setSeeWhatIsBeingShared(seeWhatIsBeingShared: boolean) {
115
+    return {
116
+        type: SET_SEE_WHAT_IS_BEING_SHARED,
117
+        seeWhatIsBeingShared
118
+    };
119
+}

+ 46
- 5
react/features/large-video/components/LargeVideo.web.js Wyświetl plik

@@ -3,15 +3,22 @@
3 3
 import React, { Component } from 'react';
4 4
 
5 5
 import VideoLayout from '../../../../modules/UI/videolayout/VideoLayout';
6
+import { getLocalParticipant } from '../../base/participants';
6 7
 import { Watermarks } from '../../base/react';
7 8
 import { connect } from '../../base/redux';
9
+import { getVideoTrackByParticipant } from '../../base/tracks';
8 10
 import { setColorAlpha } from '../../base/util';
9 11
 import { StageParticipantNameLabel } from '../../display-name';
10 12
 import { FILMSTRIP_BREAKPOINT, isFilmstripResizable } from '../../filmstrip';
11 13
 import { getVerticalViewMaxWidth } from '../../filmstrip/functions.web';
14
+import { getLargeVideoParticipant } from '../../large-video/functions';
12 15
 import { SharedVideo } from '../../shared-video/components/web';
13 16
 import { Captions } from '../../subtitles/';
14 17
 import { setTileView } from '../../video-layout/actions';
18
+import { setSeeWhatIsBeingShared } from '../actions.web';
19
+
20
+import ScreenSharePlaceholder from './ScreenSharePlaceholder.web';
21
+
15 22
 
16 23
 declare var interfaceConfig: Object;
17 24
 
@@ -68,6 +75,21 @@ type Props = {
68 75
      */
69 76
     _visibleFilmstrip: boolean,
70 77
 
78
+    /**
79
+     * The large video participant id.
80
+     */
81
+     _largeVideoParticipantId: string,
82
+
83
+        /**
84
+     * Whether or not the screen sharing is on.
85
+     */
86
+        _isScreenSharing: boolean,
87
+
88
+    /**
89
+     * Whether or not the screen sharing is visible.
90
+     */
91
+     _seeWhatIsBeingShared: boolean,
92
+
71 93
     /**
72 94
      * The Redux dispatch function.
73 95
      */
@@ -109,11 +131,19 @@ class LargeVideo extends Component<Props> {
109 131
      * @inheritdoc
110 132
      */
111 133
     componentDidUpdate(prevProps: Props) {
112
-        const { _visibleFilmstrip } = this.props;
134
+        const { _visibleFilmstrip, _isScreenSharing, _seeWhatIsBeingShared, _largeVideoParticipantId } = this.props;
113 135
 
114 136
         if (prevProps._visibleFilmstrip !== _visibleFilmstrip) {
115 137
             this._updateLayout();
116 138
         }
139
+
140
+        if (prevProps._isScreenSharing !== _isScreenSharing && !_isScreenSharing) {
141
+            this.props.dispatch(setSeeWhatIsBeingShared(false));
142
+        }
143
+
144
+        if (_isScreenSharing && _seeWhatIsBeingShared) {
145
+            VideoLayout.updateLargeVideo(_largeVideoParticipantId, true, true);
146
+        }
117 147
     }
118 148
 
119 149
     /**
@@ -126,7 +156,9 @@ class LargeVideo extends Component<Props> {
126 156
         const {
127 157
             _isChatOpen,
128 158
             _noAutoPlayVideo,
129
-            _showDominantSpeakerBadge
159
+            _showDominantSpeakerBadge,
160
+            _isScreenSharing,
161
+            _seeWhatIsBeingShared
130 162
         } = this.props;
131 163
         const style = this._getCustomSyles();
132 164
         const className = `videocontainer${_isChatOpen ? ' shift-right' : ''}`;
@@ -152,7 +184,6 @@ class LargeVideo extends Component<Props> {
152 184
                 <span id = 'remoteConnectionMessage' />
153 185
                 <div id = 'largeVideoElementsContainer'>
154 186
                     <div id = 'largeVideoBackgroundContainer' />
155
-
156 187
                     {/*
157 188
                       * FIXME: the architecture of elements related to the large
158 189
                       * video and the naming. The background is not part of
@@ -166,11 +197,11 @@ class LargeVideo extends Component<Props> {
166 197
                         onTouchEnd = { this._onDoubleTap }
167 198
                         ref = { this._wrapperRef }
168 199
                         role = 'figure' >
169
-                        <video
200
+                        {_isScreenSharing && !_seeWhatIsBeingShared ? <ScreenSharePlaceholder /> : <video
170 201
                             autoPlay = { !_noAutoPlayVideo }
171 202
                             id = 'largeVideo'
172 203
                             muted = { true }
173
-                            playsInline = { true } /* for Safari on iOS to work */ />
204
+                            playsInline = { true } /* for Safari on iOS to work */ />}
174 205
                     </div>
175 206
                 </div>
176 207
                 { interfaceConfig.DISABLE_TRANSCRIPTION_SUBTITLES
@@ -292,13 +323,23 @@ function _mapStateToProps(state) {
292 323
     const { width: verticalFilmstripWidth, visible } = state['features/filmstrip'];
293 324
     const { hideDominantSpeakerBadge } = state['features/base/config'];
294 325
 
326
+    const tracks = state['features/base/tracks'];
327
+    const localParticipantId = getLocalParticipant(state)?.id;
328
+    const largeVideoParticipant = getLargeVideoParticipant(state);
329
+    const videoTrack = getVideoTrackByParticipant(tracks, largeVideoParticipant);
330
+    const localParticipantisSharingTheScreen = largeVideoParticipant?.id?.includes(localParticipantId);
331
+    const isScreenSharing = localParticipantisSharingTheScreen && videoTrack?.videoType === 'desktop';
332
+
295 333
     return {
296 334
         _backgroundAlpha: state['features/base/config'].backgroundAlpha,
297 335
         _customBackgroundColor: backgroundColor,
298 336
         _customBackgroundImageUrl: backgroundImageUrl,
299 337
         _isChatOpen: isChatOpen,
338
+        _isScreenSharing: isScreenSharing,
339
+        _largeVideoParticipantId: largeVideoParticipant?.id,
300 340
         _noAutoPlayVideo: testingConfig?.noAutoPlayVideo,
301 341
         _resizableFilmstrip: isFilmstripResizable(state),
342
+        _seeWhatIsBeingShared: state['features/large-video'].seeWhatIsBeingShared,
302 343
         _showDominantSpeakerBadge: !hideDominantSpeakerBadge,
303 344
         _verticalFilmstripWidth: verticalFilmstripWidth.current,
304 345
         _verticalViewMaxWidth: getVerticalViewMaxWidth(state),

+ 99
- 0
react/features/large-video/components/ScreenSharePlaceholder.web.tsx Wyświetl plik

@@ -0,0 +1,99 @@
1
+/* eslint-disable lines-around-comment */
2
+import { makeStyles, createStyles } from '@material-ui/core';
3
+import React, { useCallback } from 'react';
4
+import { useStore } from 'react-redux';
5
+
6
+// @ts-ignore
7
+import { translate } from '../../base/i18n';
8
+import { Theme } from '../../base/ui/types';
9
+// @ts-ignore
10
+import { setSeeWhatIsBeingShared } from '../actions.web';
11
+
12
+const useStyles = makeStyles((theme: Theme) => createStyles({
13
+    overlayContainer: {
14
+        width: '100%',
15
+        height: '100%',
16
+        backgroundColor: theme.palette.ui02,
17
+        display: 'flex',
18
+        justifyContent: 'center',
19
+        alignItems: 'center',
20
+        position: 'absolute'
21
+    },
22
+    content: {
23
+        display: 'flex',
24
+        flexDirection: 'column',
25
+        alignItems: 'center',
26
+        justifyContent: 'center'
27
+    },
28
+    laptop: {
29
+        width: '88px',
30
+        height: '56px',
31
+        boxSizing: 'border-box',
32
+        border: '3px solid',
33
+        borderColor: theme.palette.text01,
34
+        borderRadius: '6px'
35
+    },
36
+    laptopStand: {
37
+        width: '40px',
38
+        height: '4px',
39
+        backgroundColor: theme.palette.text01,
40
+        boxSizing: 'border-box',
41
+        borderRadius: '6px',
42
+        marginTop: '4px'
43
+    },
44
+    sharingMessage: {
45
+        fontStyle: 'normal',
46
+        fontWeight: 600,
47
+        fontSize: '20px',
48
+        lineHeight: '28px',
49
+        marginTop: '24px',
50
+        letterSpacing: '-0.012em',
51
+        color: theme.palette.text01
52
+    },
53
+    showSharing: {
54
+        fontStyle: 'normal',
55
+        fontWeight: 600,
56
+        fontSize: '14px',
57
+        lineHeight: '20px',
58
+        height: '20px',
59
+        marginTop: '16px',
60
+        color: theme.palette.link01,
61
+        cursor: 'pointer',
62
+
63
+        '&:hover': {
64
+            color: theme.palette.link01Hover
65
+        }
66
+    }
67
+}));
68
+
69
+/**
70
+ * Component that displays a placehoder for when the screen is shared.
71
+ * * @param {Function} t - Function which translate strings.
72
+ *
73
+ * @returns {ReactElement}
74
+ */
75
+const ScreenSharePlaceholder: React.FC<{ t: Function }> = ({ t }) => {
76
+    const classes = useStyles();
77
+    const store = useStore();
78
+
79
+
80
+    const updateShowMeWhatImSharing = useCallback(() => {
81
+        store.dispatch(setSeeWhatIsBeingShared(true));
82
+    }, []);
83
+
84
+    return (
85
+        <div className = { classes.overlayContainer }>
86
+            <div className = { classes.content }>
87
+                <div className = { classes.laptop } />
88
+                <div className = { classes.laptopStand } />
89
+                <span className = { classes.sharingMessage }>{ t('largeVideo.screenIsShared') }</span>
90
+                <span
91
+                    className = { classes.showSharing }
92
+                    onClick = { updateShowMeWhatImSharing }
93
+                    role = 'button'>{ t('largeVideo.showMeWhatImSharing') }</span>
94
+            </div>
95
+        </div>
96
+    );
97
+};
98
+
99
+export default translate(ScreenSharePlaceholder);

+ 9
- 1
react/features/large-video/reducer.js Wyświetl plik

@@ -5,7 +5,9 @@ import { ReducerRegistry } from '../base/redux';
5 5
 
6 6
 import {
7 7
     SELECT_LARGE_VIDEO_PARTICIPANT,
8
-    UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION, UPDATE_LAST_LARGE_VIDEO_MEDIA_EVENT
8
+    UPDATE_KNOWN_LARGE_VIDEO_RESOLUTION,
9
+    UPDATE_LAST_LARGE_VIDEO_MEDIA_EVENT,
10
+    SET_SEE_WHAT_IS_BEING_SHARED
9 11
 } from './actionTypes';
10 12
 
11 13
 ReducerRegistry.register('features/large-video', (state = {}, action) => {
@@ -43,6 +45,12 @@ ReducerRegistry.register('features/large-video', (state = {}, action) => {
43 45
             lastMediaEvent: action.name
44 46
         };
45 47
 
48
+    case SET_SEE_WHAT_IS_BEING_SHARED:
49
+        return {
50
+            ...state,
51
+            seeWhatIsBeingShared: action.seeWhatIsBeingShared
52
+        };
53
+
46 54
     }
47 55
 
48 56
     return state;

Ładowanie…
Anuluj
Zapisz