소스 검색

fix(av-moderation) Improve advanced moderation (#10004)

* fix(av-moderation) Improve advanced moderation

Hide moderator label on disasbleModeratorIndicator
 - On disasbleModeratorIndicator config hide moderator label from participants pane

Add Ask to Unmute button to mobile web

* Fix lint error
master
robertpin 3 년 전
부모
커밋
700e809439
No account linked to committer's email address

+ 9
- 0
react/features/participants-pane/components/native/MeetingParticipantItem.js 파일 보기

@@ -28,6 +28,11 @@ type Props = {
28 28
      */
29 29
     _audioMediaState: MediaState,
30 30
 
31
+    /**
32
+     * Whether or not to disable the moderator indicator.
33
+     */
34
+    _disableModeratorIndicator: boolean,
35
+
31 36
     /**
32 37
      * The display name of the participant.
33 38
      */
@@ -131,6 +136,7 @@ class MeetingParticipantItem extends PureComponent<Props> {
131 136
     render() {
132 137
         const {
133 138
             _audioMediaState,
139
+            _disableModeratorIndicator,
134 140
             _displayName,
135 141
             _isModerator,
136 142
             _local,
@@ -142,6 +148,7 @@ class MeetingParticipantItem extends PureComponent<Props> {
142 148
         return (
143 149
             <ParticipantItem
144 150
                 audioMediaState = { _audioMediaState }
151
+                disableModeratorIndicator = { _disableModeratorIndicator }
145 152
                 displayName = { _displayName }
146 153
                 isKnockingParticipant = { false }
147 154
                 isModerator = { _isModerator }
@@ -171,9 +178,11 @@ function mapStateToProps(state, ownProps): Object {
171 178
     const _isVideoMuted = isParticipantVideoMuted(participant, state);
172 179
     const audioMediaState = getParticipantAudioMediaState(participant, _isAudioMuted, state);
173 180
     const videoMediaState = getParticipantVideoMediaState(participant, _isVideoMuted, state);
181
+    const { disableModeratorIndicator } = state['features/base/config'];
174 182
 
175 183
     return {
176 184
         _audioMediaState: audioMediaState,
185
+        _disableModeratorIndicator: disableModeratorIndicator,
177 186
         _displayName: getParticipantDisplayName(state, participant?.id),
178 187
         _isAudioMuted,
179 188
         _isFakeParticipant: Boolean(participant?.isFakeParticipant),

+ 9
- 1
react/features/participants-pane/components/native/ParticipantItem.js 파일 보기

@@ -24,6 +24,11 @@ type Props = {
24 24
      */
25 25
     children?: Node,
26 26
 
27
+    /**
28
+     * Whether or not to disable the moderator indicator.
29
+     */
30
+    disableModeratorIndicator?: boolean,
31
+
27 32
     /**
28 33
      * The name of the participant. Used for showing lobby names.
29 34
      */
@@ -73,6 +78,7 @@ type Props = {
73 78
 function ParticipantItem({
74 79
     children,
75 80
     displayName,
81
+    disableModeratorIndicator,
76 82
     isKnockingParticipant,
77 83
     isModerator,
78 84
     local,
@@ -101,7 +107,9 @@ function ParticipantItem({
101 107
                         </Text>
102 108
                         { local ? <Text style = { styles.isLocal }>({t('chat.you')})</Text> : null }
103 109
                     </View>
104
-                    {isModerator && <Text style = { styles.moderatorLabel }>{t('videothumbnail.moderator')}</Text>}
110
+                    {isModerator && !disableModeratorIndicator
111
+                        && <Text style = { styles.moderatorLabel }>{t('videothumbnail.moderator')}</Text>
112
+                    }
105 113
                 </View>
106 114
                 {
107 115
                     !isKnockingParticipant

+ 43
- 1
react/features/participants-pane/components/web/MeetingParticipantContextMenu.js 파일 보기

@@ -2,6 +2,7 @@
2 2
 import { withStyles } from '@material-ui/core/styles';
3 3
 import React, { Component } from 'react';
4 4
 
5
+import { approveParticipant } from '../../../av-moderation/actions';
5 6
 import { Avatar } from '../../../base/avatar';
6 7
 import { isToolbarButtonEnabled } from '../../../base/config/functions.web';
7 8
 import { openDialog } from '../../../base/dialog';
@@ -12,10 +13,12 @@ import {
12 13
     IconCrown,
13 14
     IconMessage,
14 15
     IconMicDisabled,
16
+    IconMicrophone,
15 17
     IconMuteEveryoneElse,
16 18
     IconShareVideo,
17 19
     IconVideoOff
18 20
 } from '../../../base/icons';
21
+import { MEDIA_TYPE } from '../../../base/media';
19 22
 import {
20 23
     getLocalParticipant,
21 24
     getParticipantByIdOrUndefined,
@@ -31,7 +34,7 @@ import { Drawer, DrawerPortal } from '../../../toolbox/components/web';
31 34
 import { GrantModeratorDialog, KickRemoteParticipantDialog, MuteEveryoneDialog } from '../../../video-menu';
32 35
 import { VolumeSlider } from '../../../video-menu/components/web';
33 36
 import MuteRemoteParticipantsVideoDialog from '../../../video-menu/components/web/MuteRemoteParticipantsVideoDialog';
34
-import { getComputedOuterHeight } from '../../functions';
37
+import { getComputedOuterHeight, isForceMuted } from '../../functions';
35 38
 
36 39
 import {
37 40
     ContextMenu,
@@ -43,6 +46,11 @@ import {
43 46
 
44 47
 type Props = {
45 48
 
49
+    /**
50
+     * Whether or not the participant is audio force muted.
51
+     */
52
+    _isAudioForceMuted: boolean,
53
+
46 54
     /**
47 55
      * True if the local participant is moderator and false otherwise.
48 56
      */
@@ -68,6 +76,11 @@ type Props = {
68 76
      */
69 77
     _isParticipantAudioMuted: boolean,
70 78
 
79
+    /**
80
+     * Whether or not the participant is video force muted.
81
+     */
82
+    _isVideoForceMuted: boolean,
83
+
71 84
     /**
72 85
      * Shared video local participant owner.
73 86
      */
@@ -206,6 +219,7 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
206 219
         this._onSendPrivateMessage = this._onSendPrivateMessage.bind(this);
207 220
         this._position = this._position.bind(this);
208 221
         this._onVolumeChange = this._onVolumeChange.bind(this);
222
+        this._onAskToUnmute = this._onAskToUnmute.bind(this);
209 223
     }
210 224
 
211 225
     _getCurrentParticipantId: () => string;
@@ -344,6 +358,20 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
344 358
         dispatch(setVolume(id, value));
345 359
     }
346 360
 
361
+    _onAskToUnmute: () => void;
362
+
363
+    /**
364
+     * Handles click on ask to unmute.
365
+     *
366
+     * @returns {void}
367
+     */
368
+    _onAskToUnmute() {
369
+        const { _participant, dispatch } = this.props;
370
+        const { id } = _participant;
371
+
372
+        dispatch(approveParticipant(id));
373
+    }
374
+
347 375
     /**
348 376
      * Implements React Component's componentDidMount.
349 377
      *
@@ -373,11 +401,13 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
373 401
      */
374 402
     render() {
375 403
         const {
404
+            _isAudioForceMuted,
376 405
             _isLocalModerator,
377 406
             _isChatButtonEnabled,
378 407
             _isParticipantModerator,
379 408
             _isParticipantVideoMuted,
380 409
             _isParticipantAudioMuted,
410
+            _isVideoForceMuted,
381 411
             _localVideoOwner,
382 412
             _participant,
383 413
             _volume = 1,
@@ -416,6 +446,16 @@ class MeetingParticipantContextMenu extends Component<Props, State> {
416 446
                     {_isLocalModerator && (
417 447
                         <ContextMenuItemGroup>
418 448
                             <>
449
+                                {overflowDrawer && (_isAudioForceMuted || _isVideoForceMuted)
450
+                                        && <ContextMenuItem onClick = { this._onAskToUnmute }>
451
+                                            <ContextMenuIcon src = { IconMicrophone } />
452
+                                            <span>
453
+                                                {t(_isAudioForceMuted
454
+                                                    ? 'participantsPane.actions.askUnmute'
455
+                                                    : 'participantsPane.actions.allowVideo')}
456
+                                            </span>
457
+                                        </ContextMenuItem>
458
+                                }
419 459
                                 {
420 460
                                     !_isParticipantAudioMuted && overflowDrawer
421 461
                                     && <ContextMenuItem onClick = { muteAudio(_participant) }>
@@ -542,11 +582,13 @@ function _mapStateToProps(state, ownProps): Object {
542 582
     const isLocal = participant?.local ?? true;
543 583
 
544 584
     return {
585
+        _isAudioForceMuted: isForceMuted(participant, MEDIA_TYPE.AUDIO, state),
545 586
         _isLocalModerator,
546 587
         _isChatButtonEnabled,
547 588
         _isParticipantModerator,
548 589
         _isParticipantVideoMuted,
549 590
         _isParticipantAudioMuted,
591
+        _isVideoForceMuted: isForceMuted(participant, MEDIA_TYPE.VIDEO, state),
550 592
         _localVideoOwner: Boolean(ownerId === localParticipantId),
551 593
         _participant: participant,
552 594
         _volume: isLocal ? undefined : id ? participantsVolume[id] : undefined

+ 15
- 6
react/features/participants-pane/components/web/MeetingParticipantItem.js 파일 보기

@@ -42,10 +42,9 @@ type Props = {
42 42
     _audioTrack: ?Object,
43 43
 
44 44
     /**
45
-     * Media state for video.
45
+     * Whether or not to disable the moderator indicator.
46 46
      */
47
-    _videoMediaState: MediaState,
48
-
47
+    _disableModeratorIndicator: boolean,
49 48
 
50 49
     /**
51 50
      * The display name of the participant.
@@ -85,6 +84,11 @@ type Props = {
85 84
      */
86 85
     _raisedHand: boolean,
87 86
 
87
+    /**
88
+     * Media state for video.
89
+     */
90
+    _videoMediaState: MediaState,
91
+
88 92
     /**
89 93
      * The translated ask unmute text for the qiuck action buttons.
90 94
      */
@@ -156,7 +160,7 @@ type Props = {
156 160
 function MeetingParticipantItem({
157 161
     _audioMediaState,
158 162
     _audioTrack,
159
-    _videoMediaState,
163
+    _disableModeratorIndicator,
160 164
     _displayName,
161 165
     _local,
162 166
     _localVideoOwner,
@@ -164,6 +168,7 @@ function MeetingParticipantItem({
164 168
     _participantID,
165 169
     _quickActionButtonType,
166 170
     _raisedHand,
171
+    _videoMediaState,
167 172
     askUnmuteText,
168 173
     isHighlighted,
169 174
     muteAudio,
@@ -219,6 +224,7 @@ function MeetingParticipantItem({
219 224
         <ParticipantItem
220 225
             actionsTrigger = { ACTION_TRIGGER.HOVER }
221 226
             audioMediaState = { audioMediaState }
227
+            disableModeratorIndicator = { _disableModeratorIndicator }
222 228
             displayName = { _displayName }
223 229
             isHighlighted = { isHighlighted }
224 230
             isModerator = { isParticipantModerator(_participant) }
@@ -279,17 +285,20 @@ function _mapStateToProps(state, ownProps): Object {
279 285
     const _audioTrack = participantID === localParticipantId
280 286
         ? getLocalAudioTrack(tracks) : getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, participantID);
281 287
 
288
+    const { disableModeratorIndicator } = state['features/base/config'];
289
+
282 290
     return {
283 291
         _audioMediaState,
284 292
         _audioTrack,
285
-        _videoMediaState,
293
+        _disableModeratorIndicator: disableModeratorIndicator,
286 294
         _displayName: getParticipantDisplayName(state, participant?.id),
287 295
         _local: Boolean(participant?.local),
288 296
         _localVideoOwner: Boolean(ownerId === localParticipantId),
289 297
         _participant: participant,
290 298
         _participantID: participant?.id,
291 299
         _quickActionButtonType,
292
-        _raisedHand: Boolean(participant?.raisedHand)
300
+        _raisedHand: Boolean(participant?.raisedHand),
301
+        _videoMediaState
293 302
     };
294 303
 }
295 304
 

+ 13
- 7
react/features/participants-pane/components/web/ParticipantItem.js 파일 보기

@@ -51,6 +51,11 @@ type Props = {
51 51
      */
52 52
     children: Node,
53 53
 
54
+    /**
55
+     * Whether or not to disable the moderator indicator.
56
+     */
57
+    disableModeratorIndicator: boolean,
58
+
54 59
     /**
55 60
      * The name of the participant. Used for showing lobby names.
56 61
      */
@@ -119,20 +124,21 @@ type Props = {
119 124
  * @returns {ReactNode}
120 125
  */
121 126
 function ParticipantItem({
122
-    children,
123
-    isHighlighted,
124
-    isModerator,
125
-    onLeave,
126 127
     actionsTrigger = ACTION_TRIGGER.HOVER,
127 128
     audioMediaState = MEDIA_STATE.NONE,
128
-    videoMediaState = MEDIA_STATE.NONE,
129
+    children,
130
+    disableModeratorIndicator,
129 131
     displayName,
130
-    participantID,
132
+    isHighlighted,
133
+    isModerator,
131 134
     local,
135
+    onLeave,
132 136
     openDrawerForParticipant,
133 137
     overflowDrawer,
138
+    participantID,
134 139
     raisedHand,
135 140
     t,
141
+    videoMediaState = MEDIA_STATE.NONE,
136 142
     youText
137 143
 }: Props) {
138 144
     const ParticipantActions = Actions[actionsTrigger];
@@ -162,7 +168,7 @@ function ParticipantItem({
162 168
                         </ParticipantName>
163 169
                         { local ? <span>&nbsp;({ youText })</span> : null }
164 170
                     </ParticipantNameContainer>
165
-                    {isModerator && <ModeratorLabel>
171
+                    {isModerator && !disableModeratorIndicator && <ModeratorLabel>
166 172
                         {t('videothumbnail.moderator')}
167 173
                     </ModeratorLabel>}
168 174
                 </ParticipantDetailsContainer>

Loading…
취소
저장