From a4c20469cd89529e1610bf56df1af756c1604b4d Mon Sep 17 00:00:00 2001 From: Andrei Gavrilescu <51706180+andrei-gavrilescu@users.noreply.github.com> Date: Tue, 17 Jun 2025 14:44:00 +0300 Subject: [PATCH] feat(API): expose recording consent to external api (#16141) * expose recording consent to api * Update react/features/recording/actions.web.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- modules/API/API.js | 18 ++++++++ modules/API/external/external_api.js | 2 + react/features/recording/actions.web.tsx | 42 ++++++++++++++++++- .../Recording/web/RecordingConsentDialog.tsx | 35 +++++++--------- 4 files changed, 75 insertions(+), 22 deletions(-) diff --git a/modules/API/API.js b/modules/API/API.js index c015dd1de5..5241856c5a 100755 --- a/modules/API/API.js +++ b/modules/API/API.js @@ -108,6 +108,7 @@ import { } from '../../react/features/participants-pane/actions'; import { getParticipantsPaneOpen, isForceMuted } from '../../react/features/participants-pane/functions'; import { startLocalVideoRecording, stopLocalVideoRecording } from '../../react/features/recording/actions.any'; +import { grantRecordingConsent, grantRecordingConsentAndUnmute } from '../../react/features/recording/actions.web'; import { RECORDING_METADATA_ID, RECORDING_TYPES } from '../../react/features/recording/constants'; import { getActiveSession, supportsLocalRecording } from '../../react/features/recording/functions'; import { startAudioScreenShareFlow, startScreenShareFlow } from '../../react/features/screen-share/actions'; @@ -209,6 +210,10 @@ function initCommands() { } APP.store.dispatch(grantModerator(participantId)); }, + 'grant-recording-consent': unmute => { + unmute ? APP.store.dispatch(grantRecordingConsentAndUnmute()) + : APP.store.dispatch(grantRecordingConsent()); + }, 'display-name': displayName => { sendAnalytics(createApiEvent('display.name.changed')); APP.store.dispatch(updateSettings({ displayName: getNormalizedDisplayName(displayName) })); @@ -1918,6 +1923,19 @@ class API { }); } + /** + * Notify external application (if API is enabled) that the recording consent dialog open state has changed. + * + * @param {boolean} open - True if the dialog is open, false otherwise. + * @returns {void} + */ + notifyRecordingConsentDialogOpen(open) { + this._sendEvent({ + name: 'recording-consent-dialog-open', + open + }); + } + /** * Notify external application of the current meeting requiring a password * to join. diff --git a/modules/API/external/external_api.js b/modules/API/external/external_api.js index e3fd117465..fe7b351efa 100644 --- a/modules/API/external/external_api.js +++ b/modules/API/external/external_api.js @@ -38,6 +38,7 @@ const commands = { endConference: 'end-conference', email: 'email', grantModerator: 'grant-moderator', + grantRecordingConsent: 'grant-recording-consent', hangup: 'video-hangup', hideNotification: 'hide-notification', initiatePrivateChat: 'initiate-private-chat', @@ -151,6 +152,7 @@ const events = { 'proxy-connection-event': 'proxyConnectionEvent', 'raise-hand-updated': 'raiseHandUpdated', 'ready': 'ready', + 'recording-consent-dialog-open': 'recordingConsentDialogOpen', 'recording-link-available': 'recordingLinkAvailable', 'recording-status-changed': 'recordingStatusChanged', 'participant-menu-button-clicked': 'participantMenuButtonClick', diff --git a/react/features/recording/actions.web.tsx b/react/features/recording/actions.web.tsx index 3cbeddc432..cd0c16742f 100644 --- a/react/features/recording/actions.web.tsx +++ b/react/features/recording/actions.web.tsx @@ -1,8 +1,16 @@ import React from 'react'; +import { batch } from 'react-redux'; import { IStore } from '../app/types'; -import { openDialog } from '../base/dialog/actions'; +import { hideDialog, openDialog } from '../base/dialog/actions'; import JitsiMeetJS from '../base/lib-jitsi-meet'; +import { + setAudioMuted, + setAudioUnmutePermissions, + setVideoMuted, + setVideoUnmutePermissions +} from '../base/media/actions'; +import { VIDEO_MUTISM_AUTHORITY } from '../base/media/constants'; import { showNotification } from '../notifications/actions'; import { NOTIFICATION_TIMEOUT_TYPE } from '../notifications/constants'; @@ -12,6 +20,38 @@ import RecordingLimitNotificationDescription from './components/web/RecordingLim export * from './actions.any'; +/** + * Grants recording consent by setting audio and video unmute permissions. + * + * @returns {Function} + */ +export function grantRecordingConsent() { + return (dispatch: IStore['dispatch']) => { + batch(() => { + dispatch(setAudioUnmutePermissions(false, true)); + dispatch(setVideoUnmutePermissions(false, true)); + dispatch(hideDialog()); + }); + }; +} + +/** + * Grants recording consent, unmutes audio/video, and closes the dialog. + * + * @returns {Function} + */ +export function grantRecordingConsentAndUnmute() { + return (dispatch: IStore['dispatch']) => { + batch(() => { + dispatch(setAudioUnmutePermissions(false, true)); + dispatch(setVideoUnmutePermissions(false, true)); + dispatch(setAudioMuted(false, true)); + dispatch(setVideoMuted(false, VIDEO_MUTISM_AUTHORITY.USER, true)); + dispatch(hideDialog()); + }); + }; +} + /** * Signals that a started recording notification should be shown on the * screen for a given period. diff --git a/react/features/recording/components/Recording/web/RecordingConsentDialog.tsx b/react/features/recording/components/Recording/web/RecordingConsentDialog.tsx index 5656e403d3..71eb88745c 100644 --- a/react/features/recording/components/Recording/web/RecordingConsentDialog.tsx +++ b/react/features/recording/components/Recording/web/RecordingConsentDialog.tsx @@ -1,18 +1,12 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { batch, useDispatch, useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { IReduxState } from '../../../../app/types'; -import { hideDialog } from '../../../../base/dialog/actions'; import { translateToHTML } from '../../../../base/i18n/functions'; -import { - setAudioMuted, - setAudioUnmutePermissions, - setVideoMuted, - setVideoUnmutePermissions -} from '../../../../base/media/actions'; -import { VIDEO_MUTISM_AUTHORITY } from '../../../../base/media/constants'; import Dialog from '../../../../base/ui/components/web/Dialog'; +import { grantRecordingConsent, grantRecordingConsentAndUnmute } from '../../../actions.web'; + /** * Component that renders the dialog for explicit consent for recordings. @@ -26,21 +20,20 @@ export default function RecordingConsentDialog() { const { consentLearnMoreLink } = recordings ?? {}; const learnMore = ` (${t('dialog.learnMore')})`; + useEffect(() => { + APP.API.notifyRecordingConsentDialogOpen(true); + + return () => { + APP.API.notifyRecordingConsentDialogOpen(false); + }; + }, []); + const consent = useCallback(() => { - batch(() => { - dispatch(setAudioUnmutePermissions(false, true)); - dispatch(setVideoUnmutePermissions(false, true)); - }); + dispatch(grantRecordingConsent()); }, []); const consentAndUnmute = useCallback(() => { - batch(() => { - dispatch(setAudioUnmutePermissions(false, true)); - dispatch(setVideoUnmutePermissions(false, true)); - dispatch(setAudioMuted(false, true)); - dispatch(setVideoMuted(false, VIDEO_MUTISM_AUTHORITY.USER, true)); - dispatch(hideDialog()); - }); + dispatch(grantRecordingConsentAndUnmute()); }, []); return (