feat(external-api) expose mute remote command and participant muted event (#16768)

This commit is contained in:
Mihaela Dumitru
2025-12-17 14:14:30 +02:00
committed by GitHub
parent a574d5ec79
commit 4dd241712d
4 changed files with 45 additions and 1 deletions

View File

@@ -126,7 +126,7 @@ import { extractYoutubeIdOrURL } from '../../react/features/shared-video/functio
import { setRequestingSubtitles, toggleRequestingSubtitles } from '../../react/features/subtitles/actions'; import { setRequestingSubtitles, toggleRequestingSubtitles } from '../../react/features/subtitles/actions';
import { isAudioMuteButtonDisabled } from '../../react/features/toolbox/functions'; import { isAudioMuteButtonDisabled } from '../../react/features/toolbox/functions';
import { setTileView, toggleTileView } from '../../react/features/video-layout/actions.any'; import { setTileView, toggleTileView } from '../../react/features/video-layout/actions.any';
import { muteAllParticipants } from '../../react/features/video-menu/actions'; import { muteAllParticipants, muteRemote } from '../../react/features/video-menu/actions';
import { setVideoQuality } from '../../react/features/video-quality/actions'; import { setVideoQuality } from '../../react/features/video-quality/actions';
import { toggleBackgroundEffect, toggleBlurredBackgroundEffect } from '../../react/features/virtual-background/actions'; import { toggleBackgroundEffect, toggleBlurredBackgroundEffect } from '../../react/features/virtual-background/actions';
import { VIRTUAL_BACKGROUND_TYPE } from '../../react/features/virtual-background/constants'; import { VIRTUAL_BACKGROUND_TYPE } from '../../react/features/virtual-background/constants';
@@ -239,6 +239,17 @@ function initCommands() {
APP.store.dispatch(muteAllParticipants(exclude, muteMediaType)); APP.store.dispatch(muteAllParticipants(exclude, muteMediaType));
}, },
'mute-remote-participant': (participantId, mediaType) => {
if (!isLocalParticipantModerator(APP.store.getState())) {
logger.error('Missing moderator rights to mute remote participant');
return;
}
const muteMediaType = mediaType ? mediaType : MEDIA_TYPE.AUDIO;
APP.store.dispatch(muteRemote(participantId, muteMediaType));
},
'toggle-lobby': isLobbyEnabled => { 'toggle-lobby': isLobbyEnabled => {
APP.store.dispatch(toggleLobbyMode(isLobbyEnabled)); APP.store.dispatch(toggleLobbyMode(isLobbyEnabled));
}, },
@@ -1401,6 +1412,25 @@ class API {
}); });
} }
/**
* Notify the external application that a participant's mute status changed.
*
* @param {string} participantId - The ID of the participant.
* @param {boolean} isMuted - True if muted, false if unmuted.
* @param {string} mediaType - Media type that was muted ('audio', 'video', or 'desktop').
* @param {boolean} isSelfMuted - True if participant muted themselves, false if muted by moderator.
* @returns {void}
*/
notifyParticipantMuted(participantId, isMuted, mediaType, isSelfMuted = true) {
this._sendEvent({
name: 'participant-muted',
id: participantId,
isMuted,
mediaType,
isSelfMuted
});
}
/** /**
* Notify the external app that a notification has been triggered. * Notify the external app that a notification has been triggered.
* *

View File

@@ -47,6 +47,7 @@ const commands = {
localSubject: 'local-subject', localSubject: 'local-subject',
kickParticipant: 'kick-participant', kickParticipant: 'kick-participant',
muteEveryone: 'mute-everyone', muteEveryone: 'mute-everyone',
muteRemoteParticipant: 'mute-remote-participant',
overwriteConfig: 'overwrite-config', overwriteConfig: 'overwrite-config',
overwriteNames: 'overwrite-names', overwriteNames: 'overwrite-names',
password: 'password', password: 'password',
@@ -150,6 +151,7 @@ const events = {
'participant-joined': 'participantJoined', 'participant-joined': 'participantJoined',
'participant-kicked-out': 'participantKickedOut', 'participant-kicked-out': 'participantKickedOut',
'participant-left': 'participantLeft', 'participant-left': 'participantLeft',
'participant-muted': 'participantMuted',
'participant-role-changed': 'participantRoleChanged', 'participant-role-changed': 'participantRoleChanged',
'participants-pane-toggled': 'participantsPaneToggled', 'participants-pane-toggled': 'participantsPaneToggled',
'password-required': 'passwordRequired', 'password-required': 'passwordRequired',

View File

@@ -143,6 +143,13 @@ MiddlewareRegistry.register(store => next => action => {
if (typeof action.track?.muted !== 'undefined' && participantID && !local) { if (typeof action.track?.muted !== 'undefined' && participantID && !local) {
logTracksForParticipant(store.getState()['features/base/tracks'], participantID, 'Track updated'); logTracksForParticipant(store.getState()['features/base/tracks'], participantID, 'Track updated');
// Notify external API when remote participant mutes/unmutes themselves
const mediaType = isVideoTrack
? (jitsiTrack.getVideoType() === VIDEO_TYPE.DESKTOP ? 'desktop' : 'video')
: 'audio';
APP.API.notifyParticipantMuted(participantID, action.track.muted, mediaType, true);
} }
return result; return result;

View File

@@ -73,6 +73,11 @@ export function muteRemote(participantId: string, mediaType: MediaType) {
const muteMediaType = mediaType === MEDIA_TYPE.SCREENSHARE ? 'desktop' : mediaType; const muteMediaType = mediaType === MEDIA_TYPE.SCREENSHARE ? 'desktop' : mediaType;
dispatch(muteRemoteParticipant(participantId, muteMediaType)); dispatch(muteRemoteParticipant(participantId, muteMediaType));
// Notify external API that participant was muted by moderator
if (typeof APP !== 'undefined') {
APP.API.notifyParticipantMuted(participantId, true, muteMediaType, false);
}
}; };
} }