diff --git a/lang/main.json b/lang/main.json index 08f30b9686dc..cadc3259eb48 100644 --- a/lang/main.json +++ b/lang/main.json @@ -757,7 +757,7 @@ "focusFail": "{{component}} not available - retry in {{ms}} sec", "gifsMenu": "GIPHY", "groupTitle": "Notifications", - "hostAskedUnmute": "The moderator would like you to speak", + "hostAskedUnmute": "The moderator would like you to unmute.", "invalidTenant": "Invalid tenant", "invalidTenantHyphenDescription": "The tenant you are using is invalid (starts or ends with '-').", "invalidTenantLengthDescription": "The tenant you are using is too long.", @@ -826,7 +826,8 @@ "suggestRecordingAction": "Start", "suggestRecordingDescription": "Would you like to start a recording?", "suggestRecordingTitle": "Record this meeting", - "unmute": "Unmute", + "unmute": "Unmute Audio", + "unmuteVideo": "Unmute Video", "videoMutedRemotelyDescription": "You can always turn it on again.", "videoMutedRemotelyTitle": "Your video has been turned off by {{participantDisplayName}}", "videoUnmuteBlockedDescription": "Camera unmute and desktop sharing operation have been temporarily blocked because of system limits.", diff --git a/react/features/av-moderation/middleware.ts b/react/features/av-moderation/middleware.ts index b326bac4f251..8d2b47548cc2 100644 --- a/react/features/av-moderation/middleware.ts +++ b/react/features/av-moderation/middleware.ts @@ -4,6 +4,7 @@ import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app/actionTypes'; import { getConferenceState } from '../base/conference/functions'; import { JitsiConferenceEvents } from '../base/lib-jitsi-meet'; import { MEDIA_TYPE, MediaType } from '../base/media/constants'; +import { isAudioMuted, isVideoMuted } from '../base/media/functions'; import { PARTICIPANT_UPDATED } from '../base/participants/actionTypes'; import { raiseHand } from '../base/participants/actions'; import { @@ -208,24 +209,46 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { */ StateListenerRegistry.register( state => state['features/base/conference'].conference, - (conference, { dispatch }, previousConference) => { + (conference, { dispatch, getState }, previousConference) => { if (conference && !previousConference) { // local participant is allowed to unmute conference.on(JitsiConferenceEvents.AV_MODERATION_APPROVED, ({ mediaType }: { mediaType: MediaType; }) => { dispatch(localParticipantApproved(mediaType)); - // Audio & video moderation are both enabled at the same time. - // Avoid displaying 2 different notifications. - if (mediaType === MEDIA_TYPE.AUDIO) { - dispatch(showNotification({ - titleKey: 'notify.hostAskedUnmute', - sticky: true, - customActionNameKey: [ 'notify.unmute' ], - customActionHandler: [ () => dispatch(muteLocal(false, MEDIA_TYPE.AUDIO)) ], - uid: ASKED_TO_UNMUTE_NOTIFICATION_ID - }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM)); - dispatch(playSound(ASKED_TO_UNMUTE_SOUND_ID)); + const customActionNameKey = []; + const customActionHandler = []; + + if ((mediaType === MEDIA_TYPE.AUDIO || getState()['features/av-moderation'].audioUnmuteApproved) + && isAudioMuted(getState())) { + customActionNameKey.push('notify.unmute'); + customActionHandler.push(() => { + dispatch(muteLocal(false, MEDIA_TYPE.AUDIO)); + dispatch(hideNotification(ASKED_TO_UNMUTE_NOTIFICATION_ID)); + }); } + + if ((mediaType === MEDIA_TYPE.VIDEO || getState()['features/av-moderation'].videoUnmuteApproved) + && isVideoMuted(getState())) { + customActionNameKey.push('notify.unmuteVideo'); + customActionHandler.push(() => { + dispatch(muteLocal(false, MEDIA_TYPE.VIDEO)); + dispatch(hideNotification(ASKED_TO_UNMUTE_NOTIFICATION_ID)); + + // lower hand as there will be no audio and change in dominant speaker to clear it + dispatch(raiseHand(false)); + + }); + } + + dispatch(showNotification({ + titleKey: 'notify.hostAskedUnmute', + sticky: true, + customActionNameKey, + customActionHandler, + uid: ASKED_TO_UNMUTE_NOTIFICATION_ID + }, NOTIFICATION_TIMEOUT_TYPE.STICKY)); + + dispatch(playSound(ASKED_TO_UNMUTE_SOUND_ID)); }); conference.on(JitsiConferenceEvents.AV_MODERATION_REJECTED, ({ mediaType }: { mediaType: MediaType; }) => { diff --git a/react/features/video-menu/actions.any.ts b/react/features/video-menu/actions.any.ts index 27a9a9027c48..36da60b6cc0d 100644 --- a/react/features/video-menu/actions.any.ts +++ b/react/features/video-menu/actions.any.ts @@ -11,7 +11,7 @@ import { shouldShowModeratedNotification } from '../av-moderation/functions'; import { setAudioMuted, setVideoMuted } from '../base/media/actions'; import { MEDIA_TYPE, MediaType, VIDEO_MUTISM_AUTHORITY } from '../base/media/constants'; import { muteRemoteParticipant } from '../base/participants/actions'; -import { getLocalParticipant, getRemoteParticipants } from '../base/participants/functions'; +import { getRemoteParticipants } from '../base/participants/functions'; import { toggleScreensharing } from '../base/tracks/actions'; import { isModerationNotificationDisplayed } from '../notifications/functions'; @@ -36,7 +36,7 @@ export function muteLocal(enable: boolean, mediaType: MediaType, stopScreenShari } // check for A/V Moderation when trying to unmute - if (!enable && shouldShowModeratedNotification(MEDIA_TYPE.AUDIO, getState())) { + if (isAudio && !enable && shouldShowModeratedNotification(MEDIA_TYPE.AUDIO, getState())) { if (!isModerationNotificationDisplayed(MEDIA_TYPE.AUDIO, getState())) { dispatch(showModeratedNotification(MEDIA_TYPE.AUDIO)); }