|
@@ -6,6 +6,17 @@ import {
|
6
|
6
|
createApiEvent,
|
7
|
7
|
sendAnalytics
|
8
|
8
|
} from '../../react/features/analytics';
|
|
9
|
+import {
|
|
10
|
+ approveParticipantAudio,
|
|
11
|
+ approveParticipantVideo,
|
|
12
|
+ rejectParticipantAudio,
|
|
13
|
+ rejectParticipantVideo,
|
|
14
|
+ requestDisableAudioModeration,
|
|
15
|
+ requestDisableVideoModeration,
|
|
16
|
+ requestEnableAudioModeration,
|
|
17
|
+ requestEnableVideoModeration
|
|
18
|
+} from '../../react/features/av-moderation/actions';
|
|
19
|
+import { isEnabledFromState } from '../../react/features/av-moderation/functions';
|
9
|
20
|
import {
|
10
|
21
|
getCurrentConference,
|
11
|
22
|
sendTones,
|
|
@@ -25,7 +36,8 @@ import {
|
25
|
36
|
pinParticipant,
|
26
|
37
|
kickParticipant,
|
27
|
38
|
raiseHand,
|
28
|
|
- isParticipantModerator
|
|
39
|
+ isParticipantModerator,
|
|
40
|
+ isLocalParticipantModerator
|
29
|
41
|
} from '../../react/features/base/participants';
|
30
|
42
|
import { updateSettings } from '../../react/features/base/settings';
|
31
|
43
|
import { isToggleCameraEnabled, toggleCamera } from '../../react/features/base/tracks';
|
|
@@ -50,6 +62,7 @@ import {
|
50
|
62
|
resizeLargeVideo
|
51
|
63
|
} from '../../react/features/large-video/actions.web';
|
52
|
64
|
import { toggleLobbyMode } from '../../react/features/lobby/actions';
|
|
65
|
+import { isForceMuted } from '../../react/features/participants-pane/functions';
|
53
|
66
|
import { RECORDING_TYPES } from '../../react/features/recording/constants';
|
54
|
67
|
import { getActiveSession } from '../../react/features/recording/functions';
|
55
|
68
|
import { isScreenAudioSupported } from '../../react/features/screen-share';
|
|
@@ -100,6 +113,20 @@ let videoAvailable = true;
|
100
|
113
|
*/
|
101
|
114
|
function initCommands() {
|
102
|
115
|
commands = {
|
|
116
|
+ 'approve-video': participantId => {
|
|
117
|
+ if (!isLocalParticipantModerator(APP.store.getState())) {
|
|
118
|
+ return;
|
|
119
|
+ }
|
|
120
|
+
|
|
121
|
+ APP.store.dispatch(approveParticipantVideo(participantId));
|
|
122
|
+ },
|
|
123
|
+ 'ask-to-unmute': participantId => {
|
|
124
|
+ if (!isLocalParticipantModerator(APP.store.getState())) {
|
|
125
|
+ return;
|
|
126
|
+ }
|
|
127
|
+
|
|
128
|
+ APP.store.dispatch(approveParticipantAudio(participantId));
|
|
129
|
+ },
|
103
|
130
|
'display-name': displayName => {
|
104
|
131
|
sendAnalytics(createApiEvent('display.name.changed'));
|
105
|
132
|
APP.conference.changeLocalDisplayName(displayName);
|
|
@@ -150,6 +177,15 @@ function initCommands() {
|
150
|
177
|
'proxy-connection-event': event => {
|
151
|
178
|
APP.conference.onProxyConnectionEvent(event);
|
152
|
179
|
},
|
|
180
|
+ 'reject-participant': (participantId, mediaType) => {
|
|
181
|
+ if (!isLocalParticipantModerator(APP.store.getState())) {
|
|
182
|
+ return;
|
|
183
|
+ }
|
|
184
|
+
|
|
185
|
+ const reject = mediaType === MEDIA_TYPE.VIDEO ? rejectParticipantVideo : rejectParticipantAudio;
|
|
186
|
+
|
|
187
|
+ APP.store.dispatch(reject(participantId));
|
|
188
|
+ },
|
153
|
189
|
'resize-large-video': (width, height) => {
|
154
|
190
|
logger.debug('Resize large video command received');
|
155
|
191
|
sendAnalytics(createApiEvent('largevideo.resized'));
|
|
@@ -218,6 +254,24 @@ function initCommands() {
|
218
|
254
|
sendAnalytics(createApiEvent('chat.toggled'));
|
219
|
255
|
APP.store.dispatch(toggleChat());
|
220
|
256
|
},
|
|
257
|
+ 'toggle-moderation': (enabled, mediaType) => {
|
|
258
|
+ const state = APP.store.getState();
|
|
259
|
+
|
|
260
|
+ if (!isLocalParticipantModerator(state)) {
|
|
261
|
+ return;
|
|
262
|
+ }
|
|
263
|
+
|
|
264
|
+ const enable = mediaType === MEDIA_TYPE.VIDEO
|
|
265
|
+ ? requestEnableVideoModeration : requestEnableAudioModeration;
|
|
266
|
+ const disable = mediaType === MEDIA_TYPE.VIDEO
|
|
267
|
+ ? requestDisableVideoModeration : requestDisableAudioModeration;
|
|
268
|
+
|
|
269
|
+ if (enabled) {
|
|
270
|
+ APP.store.dispatch(enable());
|
|
271
|
+ } else {
|
|
272
|
+ APP.store.dispatch(disable());
|
|
273
|
+ }
|
|
274
|
+ },
|
221
|
275
|
'toggle-raise-hand': () => {
|
222
|
276
|
const localParticipant = getLocalParticipant(APP.store.getState());
|
223
|
277
|
|
|
@@ -541,6 +595,22 @@ function initCommands() {
|
541
|
595
|
case 'is-audio-muted':
|
542
|
596
|
callback(APP.conference.isLocalAudioMuted());
|
543
|
597
|
break;
|
|
598
|
+ case 'is-moderation-on': {
|
|
599
|
+ const { mediaType } = request;
|
|
600
|
+ const type = mediaType || MEDIA_TYPE.AUDIO;
|
|
601
|
+
|
|
602
|
+ callback(isEnabledFromState(type, APP.store.getState()));
|
|
603
|
+ break;
|
|
604
|
+ }
|
|
605
|
+ case 'is-participant-force-muted': {
|
|
606
|
+ const state = APP.store.getState();
|
|
607
|
+ const { participantId, mediaType } = request;
|
|
608
|
+ const type = mediaType || MEDIA_TYPE.AUDIO;
|
|
609
|
+ const participant = getParticipantById(state, participantId);
|
|
610
|
+
|
|
611
|
+ callback(isForceMuted(participant, type, state));
|
|
612
|
+ break;
|
|
613
|
+ }
|
544
|
614
|
case 'is-video-muted':
|
545
|
615
|
callback(APP.conference.isLocalVideoMuted());
|
546
|
616
|
break;
|
|
@@ -806,6 +876,51 @@ class API {
|
806
|
876
|
});
|
807
|
877
|
}
|
808
|
878
|
|
|
879
|
+ /**
|
|
880
|
+ * Notify the external application that the moderation status has changed.
|
|
881
|
+ *
|
|
882
|
+ * @param {string} mediaType - Media type for which the moderation changed.
|
|
883
|
+ * @param {boolean} enabled - Whether or not the new moderation status is enabled.
|
|
884
|
+ * @returns {void}
|
|
885
|
+ */
|
|
886
|
+ notifyModerationChanged(mediaType: string, enabled: boolean) {
|
|
887
|
+ this._sendEvent({
|
|
888
|
+ name: 'moderation-status-changed',
|
|
889
|
+ mediaType,
|
|
890
|
+ enabled
|
|
891
|
+ });
|
|
892
|
+ }
|
|
893
|
+
|
|
894
|
+ /**
|
|
895
|
+ * Notify the external application that a participant was approved on moderation.
|
|
896
|
+ *
|
|
897
|
+ * @param {string} participantId - The ID of the participant that got approved.
|
|
898
|
+ * @param {string} mediaType - Media type for which the participant was approved.
|
|
899
|
+ * @returns {void}
|
|
900
|
+ */
|
|
901
|
+ notifyParticipantApproved(participantId: string, mediaType: string) {
|
|
902
|
+ this._sendEvent({
|
|
903
|
+ name: 'moderation-participant-approved',
|
|
904
|
+ id: participantId,
|
|
905
|
+ mediaType
|
|
906
|
+ });
|
|
907
|
+ }
|
|
908
|
+
|
|
909
|
+ /**
|
|
910
|
+ * Notify the external application that a participant was rejected on moderation.
|
|
911
|
+ *
|
|
912
|
+ * @param {string} participantId - The ID of the participant that got rejected.
|
|
913
|
+ * @param {string} mediaType - Media type for which the participant was rejected.
|
|
914
|
+ * @returns {void}
|
|
915
|
+ */
|
|
916
|
+ notifyParticipantRejected(participantId: string, mediaType: string) {
|
|
917
|
+ this._sendEvent({
|
|
918
|
+ name: 'moderation-participant-rejected',
|
|
919
|
+ id: participantId,
|
|
920
|
+ mediaType
|
|
921
|
+ });
|
|
922
|
+ }
|
|
923
|
+
|
809
|
924
|
/**
|
810
|
925
|
* Notify external application that the video quality setting has changed.
|
811
|
926
|
*
|