Bladeren bron

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

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

+ 4
- 0
lang/main.json Bestand weergeven

518
         "toggleShortcuts": "Show or hide keyboard shortcuts",
518
         "toggleShortcuts": "Show or hide keyboard shortcuts",
519
         "videoMute": "Start or stop your camera"
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
     "liveStreaming": {
525
     "liveStreaming": {
522
         "busy": "We're working on freeing streaming resources. Please try again in a few minutes.",
526
         "busy": "We're working on freeing streaming resources. Please try again in a few minutes.",
523
         "busyTitle": "All streamers are currently busy",
527
         "busyTitle": "All streamers are currently busy",

+ 17
- 12
modules/UI/videolayout/VideoContainer.js Bestand weergeven

480
      */
480
      */
481
     setStream(userID, stream, videoType) {
481
     setStream(userID, stream, videoType) {
482
         this.userId = userID;
482
         this.userId = userID;
483
-        if (this.stream === stream) {
483
+        if (this.stream === stream && !stream?.forceStreamToReattach) {
484
             // Handles the use case for the remote participants when the
484
             // Handles the use case for the remote participants when the
485
             // videoType is received with delay after turning on/off the
485
             // videoType is received with delay after turning on/off the
486
             // desktop sharing.
486
             // desktop sharing.
492
             return;
492
             return;
493
         }
493
         }
494
 
494
 
495
+        if (stream?.forceStreamToReattach) {
496
+            delete stream.forceStreamToReattach;
497
+        }
498
+
495
         // detach old stream
499
         // detach old stream
496
-        if (this.stream) {
500
+        if (this.stream && this.$video[0]) {
497
             this.stream.detach(this.$video[0]);
501
             this.stream.detach(this.$video[0]);
498
         }
502
         }
499
 
503
 
504
             return;
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 Bestand weergeven

177
         return largeVideo && largeVideo.id === id;
177
         return largeVideo && largeVideo.id === id;
178
     },
178
     },
179
 
179
 
180
-    updateLargeVideo(id, forceUpdate) {
180
+    updateLargeVideo(id, forceUpdate, forceStreamToReattach = false) {
181
         if (!largeVideo) {
181
         if (!largeVideo) {
182
             return;
182
             return;
183
         }
183
         }
198
 
198
 
199
         const videoStream = videoTrack?.jitsiTrack;
199
         const videoStream = videoTrack?.jitsiTrack;
200
 
200
 
201
+        if (videoStream && forceStreamToReattach) {
202
+            videoStream.forceStreamToReattach = forceStreamToReattach;
203
+        }
204
+
201
         if (isOnLarge && !forceUpdate
205
         if (isOnLarge && !forceUpdate
202
                 && LargeVideoManager.isVideoContainer(currentContainerType)
206
                 && LargeVideoManager.isVideoContainer(currentContainerType)
203
                 && videoStream) {
207
                 && videoStream) {
330
      */
334
      */
331
     _updateLargeVideoIfDisplayed(participantId, force = false) {
335
     _updateLargeVideoIfDisplayed(participantId, force = false) {
332
         if (this.isCurrentlyOnLarge(participantId)) {
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 Bestand weergeven

30
  */
30
  */
31
 export const UPDATE_LAST_LARGE_VIDEO_MEDIA_EVENT
31
 export const UPDATE_LAST_LARGE_VIDEO_MEDIA_EVENT
32
     = 'UPDATE_LAST_LARGE_VIDEO_MEDIA_EVENT';
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 Bestand weergeven

6
 import { MEDIA_TYPE } from '../base/media';
6
 import { MEDIA_TYPE } from '../base/media';
7
 import { getTrackByMediaTypeAndParticipant } from '../base/tracks';
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
 export * from './actions.any';
11
 export * from './actions.any';
12
 
12
 
101
         name
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 Bestand weergeven

3
 import React, { Component } from 'react';
3
 import React, { Component } from 'react';
4
 
4
 
5
 import VideoLayout from '../../../../modules/UI/videolayout/VideoLayout';
5
 import VideoLayout from '../../../../modules/UI/videolayout/VideoLayout';
6
+import { getLocalParticipant } from '../../base/participants';
6
 import { Watermarks } from '../../base/react';
7
 import { Watermarks } from '../../base/react';
7
 import { connect } from '../../base/redux';
8
 import { connect } from '../../base/redux';
9
+import { getVideoTrackByParticipant } from '../../base/tracks';
8
 import { setColorAlpha } from '../../base/util';
10
 import { setColorAlpha } from '../../base/util';
9
 import { StageParticipantNameLabel } from '../../display-name';
11
 import { StageParticipantNameLabel } from '../../display-name';
10
 import { FILMSTRIP_BREAKPOINT, isFilmstripResizable } from '../../filmstrip';
12
 import { FILMSTRIP_BREAKPOINT, isFilmstripResizable } from '../../filmstrip';
11
 import { getVerticalViewMaxWidth } from '../../filmstrip/functions.web';
13
 import { getVerticalViewMaxWidth } from '../../filmstrip/functions.web';
14
+import { getLargeVideoParticipant } from '../../large-video/functions';
12
 import { SharedVideo } from '../../shared-video/components/web';
15
 import { SharedVideo } from '../../shared-video/components/web';
13
 import { Captions } from '../../subtitles/';
16
 import { Captions } from '../../subtitles/';
14
 import { setTileView } from '../../video-layout/actions';
17
 import { setTileView } from '../../video-layout/actions';
18
+import { setSeeWhatIsBeingShared } from '../actions.web';
19
+
20
+import ScreenSharePlaceholder from './ScreenSharePlaceholder.web';
21
+
15
 
22
 
16
 declare var interfaceConfig: Object;
23
 declare var interfaceConfig: Object;
17
 
24
 
68
      */
75
      */
69
     _visibleFilmstrip: boolean,
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
      * The Redux dispatch function.
94
      * The Redux dispatch function.
73
      */
95
      */
109
      * @inheritdoc
131
      * @inheritdoc
110
      */
132
      */
111
     componentDidUpdate(prevProps: Props) {
133
     componentDidUpdate(prevProps: Props) {
112
-        const { _visibleFilmstrip } = this.props;
134
+        const { _visibleFilmstrip, _isScreenSharing, _seeWhatIsBeingShared, _largeVideoParticipantId } = this.props;
113
 
135
 
114
         if (prevProps._visibleFilmstrip !== _visibleFilmstrip) {
136
         if (prevProps._visibleFilmstrip !== _visibleFilmstrip) {
115
             this._updateLayout();
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
         const {
156
         const {
127
             _isChatOpen,
157
             _isChatOpen,
128
             _noAutoPlayVideo,
158
             _noAutoPlayVideo,
129
-            _showDominantSpeakerBadge
159
+            _showDominantSpeakerBadge,
160
+            _isScreenSharing,
161
+            _seeWhatIsBeingShared
130
         } = this.props;
162
         } = this.props;
131
         const style = this._getCustomSyles();
163
         const style = this._getCustomSyles();
132
         const className = `videocontainer${_isChatOpen ? ' shift-right' : ''}`;
164
         const className = `videocontainer${_isChatOpen ? ' shift-right' : ''}`;
152
                 <span id = 'remoteConnectionMessage' />
184
                 <span id = 'remoteConnectionMessage' />
153
                 <div id = 'largeVideoElementsContainer'>
185
                 <div id = 'largeVideoElementsContainer'>
154
                     <div id = 'largeVideoBackgroundContainer' />
186
                     <div id = 'largeVideoBackgroundContainer' />
155
-
156
                     {/*
187
                     {/*
157
                       * FIXME: the architecture of elements related to the large
188
                       * FIXME: the architecture of elements related to the large
158
                       * video and the naming. The background is not part of
189
                       * video and the naming. The background is not part of
166
                         onTouchEnd = { this._onDoubleTap }
197
                         onTouchEnd = { this._onDoubleTap }
167
                         ref = { this._wrapperRef }
198
                         ref = { this._wrapperRef }
168
                         role = 'figure' >
199
                         role = 'figure' >
169
-                        <video
200
+                        {_isScreenSharing && !_seeWhatIsBeingShared ? <ScreenSharePlaceholder /> : <video
170
                             autoPlay = { !_noAutoPlayVideo }
201
                             autoPlay = { !_noAutoPlayVideo }
171
                             id = 'largeVideo'
202
                             id = 'largeVideo'
172
                             muted = { true }
203
                             muted = { true }
173
-                            playsInline = { true } /* for Safari on iOS to work */ />
204
+                            playsInline = { true } /* for Safari on iOS to work */ />}
174
                     </div>
205
                     </div>
175
                 </div>
206
                 </div>
176
                 { interfaceConfig.DISABLE_TRANSCRIPTION_SUBTITLES
207
                 { interfaceConfig.DISABLE_TRANSCRIPTION_SUBTITLES
292
     const { width: verticalFilmstripWidth, visible } = state['features/filmstrip'];
323
     const { width: verticalFilmstripWidth, visible } = state['features/filmstrip'];
293
     const { hideDominantSpeakerBadge } = state['features/base/config'];
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
     return {
333
     return {
296
         _backgroundAlpha: state['features/base/config'].backgroundAlpha,
334
         _backgroundAlpha: state['features/base/config'].backgroundAlpha,
297
         _customBackgroundColor: backgroundColor,
335
         _customBackgroundColor: backgroundColor,
298
         _customBackgroundImageUrl: backgroundImageUrl,
336
         _customBackgroundImageUrl: backgroundImageUrl,
299
         _isChatOpen: isChatOpen,
337
         _isChatOpen: isChatOpen,
338
+        _isScreenSharing: isScreenSharing,
339
+        _largeVideoParticipantId: largeVideoParticipant?.id,
300
         _noAutoPlayVideo: testingConfig?.noAutoPlayVideo,
340
         _noAutoPlayVideo: testingConfig?.noAutoPlayVideo,
301
         _resizableFilmstrip: isFilmstripResizable(state),
341
         _resizableFilmstrip: isFilmstripResizable(state),
342
+        _seeWhatIsBeingShared: state['features/large-video'].seeWhatIsBeingShared,
302
         _showDominantSpeakerBadge: !hideDominantSpeakerBadge,
343
         _showDominantSpeakerBadge: !hideDominantSpeakerBadge,
303
         _verticalFilmstripWidth: verticalFilmstripWidth.current,
344
         _verticalFilmstripWidth: verticalFilmstripWidth.current,
304
         _verticalViewMaxWidth: getVerticalViewMaxWidth(state),
345
         _verticalViewMaxWidth: getVerticalViewMaxWidth(state),

+ 99
- 0
react/features/large-video/components/ScreenSharePlaceholder.web.tsx Bestand weergeven

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 Bestand weergeven

5
 
5
 
6
 import {
6
 import {
7
     SELECT_LARGE_VIDEO_PARTICIPANT,
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
 } from './actionTypes';
11
 } from './actionTypes';
10
 
12
 
11
 ReducerRegistry.register('features/large-video', (state = {}, action) => {
13
 ReducerRegistry.register('features/large-video', (state = {}, action) => {
43
             lastMediaEvent: action.name
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
     return state;
56
     return state;

Laden…
Annuleren
Opslaan