|
@@ -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
|