jiti-meet/react/features/av-moderation/middleware.js

206 lines
7.1 KiB
JavaScript

// @flow
import { batch } from 'react-redux';
import { getConferenceState } from '../base/conference';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import { MEDIA_TYPE } from '../base/media';
import {
getLocalParticipant,
getParticipantDisplayName,
getRemoteParticipants,
isLocalParticipantModerator,
isParticipantModerator,
PARTICIPANT_UPDATED,
raiseHand
} from '../base/participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import {
hideNotification,
NOTIFICATION_TIMEOUT,
showNotification
} from '../notifications';
import {
DISABLE_MODERATION,
ENABLE_MODERATION,
LOCAL_PARTICIPANT_MODERATION_NOTIFICATION,
REQUEST_DISABLE_MODERATION,
REQUEST_ENABLE_MODERATION
} from './actionTypes';
import {
disableModeration,
dismissPendingParticipant,
dismissPendingAudioParticipant,
enableModeration,
localParticipantApproved,
participantApproved,
participantPendingAudio
} from './actions';
import {
isEnabledFromState,
isParticipantApproved,
isParticipantPending
} from './functions';
const VIDEO_MODERATION_NOTIFICATION_ID = 'video-moderation';
const AUDIO_MODERATION_NOTIFICATION_ID = 'audio-moderation';
const CS_MODERATION_NOTIFICATION_ID = 'video-moderation';
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const { actor, mediaType, type } = action;
switch (type) {
case DISABLE_MODERATION:
case ENABLE_MODERATION: {
// Audio & video moderation are both enabled at the same time.
// Avoid displaying 2 different notifications.
if (mediaType === MEDIA_TYPE.VIDEO) {
const titleKey = type === ENABLE_MODERATION
? 'notify.moderationStartedTitle'
: 'notify.moderationStoppedTitle';
dispatch(showNotification({
descriptionKey: actor ? 'notify.moderationToggleDescription' : undefined,
descriptionArguments: actor ? {
participantDisplayName: getParticipantDisplayName(getState, actor.getId())
} : undefined,
titleKey
}, NOTIFICATION_TIMEOUT));
}
break;
}
case LOCAL_PARTICIPANT_MODERATION_NOTIFICATION: {
let descriptionKey;
let titleKey;
let uid;
switch (action.mediaType) {
case MEDIA_TYPE.AUDIO: {
titleKey = 'notify.moderationInEffectTitle';
descriptionKey = 'notify.moderationInEffectDescription';
uid = AUDIO_MODERATION_NOTIFICATION_ID;
break;
}
case MEDIA_TYPE.VIDEO: {
titleKey = 'notify.moderationInEffectVideoTitle';
descriptionKey = 'notify.moderationInEffectVideoDescription';
uid = VIDEO_MODERATION_NOTIFICATION_ID;
break;
}
case MEDIA_TYPE.PRESENTER: {
titleKey = 'notify.moderationInEffectCSTitle';
descriptionKey = 'notify.moderationInEffectCSDescription';
uid = CS_MODERATION_NOTIFICATION_ID;
break;
}
}
dispatch(showNotification({
customActionNameKey: 'notify.raiseHandAction',
customActionHandler: () => batch(() => {
dispatch(raiseHand(true));
dispatch(hideNotification(uid));
}),
descriptionKey,
sticky: true,
titleKey,
uid
}));
break;
}
case REQUEST_DISABLE_MODERATION: {
const { conference } = getConferenceState(getState());
conference.disableAVModeration(MEDIA_TYPE.AUDIO);
conference.disableAVModeration(MEDIA_TYPE.VIDEO);
break;
}
case REQUEST_ENABLE_MODERATION: {
const { conference } = getConferenceState(getState());
conference.enableAVModeration(MEDIA_TYPE.AUDIO);
conference.enableAVModeration(MEDIA_TYPE.VIDEO);
break;
}
case PARTICIPANT_UPDATED: {
const state = getState();
const audioModerationEnabled = isEnabledFromState(MEDIA_TYPE.AUDIO, state);
const participant = action.participant;
if (participant && audioModerationEnabled) {
if (isLocalParticipantModerator(state)) {
// this is handled only by moderators
if (participant.raisedHand) {
// if participant raises hand show notification
!isParticipantApproved(participant.id, MEDIA_TYPE.AUDIO)(state)
&& dispatch(participantPendingAudio(participant));
} else {
// if participant lowers hand hide notification
isParticipantPending(participant, MEDIA_TYPE.AUDIO)(state)
&& dispatch(dismissPendingAudioParticipant(participant));
}
} else if (participant.id === getLocalParticipant(state).id
&& /* the new role */ isParticipantModerator(participant)) {
// this is the granted moderator case
getRemoteParticipants(state).forEach(p => {
p.raisedHand && !isParticipantApproved(p.id, MEDIA_TYPE.AUDIO)(state)
&& dispatch(participantPendingAudio(p));
});
}
}
break;
}
}
return next(action);
});
/**
* Registers a change handler for state['features/base/conference'].conference to
* set the event listeners needed for the A/V moderation feature to operate.
*/
StateListenerRegistry.register(
state => state['features/base/conference'].conference,
(conference, { dispatch }, previousConference) => {
if (conference && !previousConference) {
// local participant is allowed to unmute
conference.on(JitsiConferenceEvents.AV_MODERATION_APPROVED, ({ mediaType }) => {
dispatch(localParticipantApproved(mediaType));
// Audio & video moderation are both enabled at the same time.
// Avoid displaying 2 different notifications.
if (mediaType === MEDIA_TYPE.VIDEO) {
dispatch(showNotification({
titleKey: 'notify.unmute',
descriptionKey: 'notify.hostAskedUnmute',
sticky: true
}));
}
});
conference.on(JitsiConferenceEvents.AV_MODERATION_CHANGED, ({ enabled, mediaType, actor }) => {
enabled ? dispatch(enableModeration(mediaType, actor)) : dispatch(disableModeration(mediaType, actor));
});
// this is received by moderators
conference.on(
JitsiConferenceEvents.AV_MODERATION_PARTICIPANT_APPROVED,
({ participant, mediaType }) => {
const { _id: id } = participant;
batch(() => {
// store in the whitelist
dispatch(participantApproved(id, mediaType));
// remove from pending list
dispatch(dismissPendingParticipant(id, mediaType));
});
});
}
});