From 7dca91a50a57573db57f49c767a8b7f10463333c Mon Sep 17 00:00:00 2001 From: Robert Pintilii Date: Wed, 22 Jun 2022 10:52:22 +0100 Subject: [PATCH] fix(local-recording) Add notification config and style fixes (#11728) Add analytics --- config.js | 9 ++++-- css/_recording.scss | 19 ++++++++++-- lang/main.json | 1 + react/features/base/config/configWhitelist.js | 2 +- .../features/base/participants/middleware.js | 30 ++++++++++-------- .../Recording/StartRecordingDialogContent.js | 31 +++++++++++++++---- react/features/recording/middleware.js | 13 ++++++-- 7 files changed, 78 insertions(+), 27 deletions(-) diff --git a/config.js b/config.js index 0c6a537f79..0d706ad6bd 100644 --- a/config.js +++ b/config.js @@ -295,8 +295,13 @@ var config = { // Whether to enable live streaming or not. // liveStreamingEnabled: false, - // Whether to enable local recording or not. - // enableLocalRecording: false, + // Local recording configuration. + // localRecording: { + // // Whether to enable local recording or not. + // enable: false, + // // Whether to notify all participants when a participant is recording locally. + // notifyAllParticipants: false + // }, // Transcription (in interface_config, // subtitles and buttons can be configured) diff --git a/css/_recording.scss b/css/_recording.scss index 1478c2797a..9674f31cd1 100644 --- a/css/_recording.scss +++ b/css/_recording.scss @@ -28,8 +28,21 @@ } .local-recording-warning { - margin-top: 4px; + margin-top: 8px; display: block; + font-size: 14px; + line-height: 20px; + padding: 8px 16px; + + &.text { + color: #fff; + background-color: #3D3D3D; + } + + &.notification { + color: #040404; + background-color: #F8AE1A; + } } .recording-switch-disabled { @@ -46,7 +59,7 @@ border-radius: 4px; height: 40px; justify-content: center; - width: 56px; + width: 42px; } .cloud-content-recording-icon-container { @@ -58,7 +71,7 @@ } .jitsi-recording-header { - margin-bottom: 32px; + margin-bottom: 16px; } .jitsi-content-recording-icon-container-with-switch { diff --git a/lang/main.json b/lang/main.json index e03743dc28..b1c8fe9acb 100644 --- a/lang/main.json +++ b/lang/main.json @@ -894,6 +894,7 @@ "limitNotificationDescriptionWeb": "Due to high demand your recording will be limited to {{limit}} min. For unlimited recordings try {{app}}.", "linkGenerated": "We have generated a link to your recording.", "live": "LIVE", + "localRecordingNoNotificationWarning": "The recording will not be announced to other participants. You will need to let them know that the meeting is recorded.", "localRecordingWarning": "Make sure you select the current tab in order to use the right video and audio. The recording is currently limited to 1GB, which is around 100 minutes.", "loggedIn": "Logged in as {{userName}}", "off": "Recording stopped", diff --git a/react/features/base/config/configWhitelist.js b/react/features/base/config/configWhitelist.js index 5e824bb82f..9927764dc5 100644 --- a/react/features/base/config/configWhitelist.js +++ b/react/features/base/config/configWhitelist.js @@ -143,7 +143,6 @@ export default [ 'enableLayerSuspension', 'enableLipSync', 'enableLobbyChat', - 'enableLocalRecording', 'enableOpusRed', 'enableRemb', 'enableSaveLogs', @@ -185,6 +184,7 @@ export default [ 'ignoreStartMuted', 'inviteAppName', 'liveStreamingEnabled', + 'localRecording', 'localSubject', 'maxFullResolutionParticipants', 'mouseMoveCallbackInterval', diff --git a/react/features/base/participants/middleware.js b/react/features/base/participants/middleware.js index 7db444e1a8..b4e29e010e 100644 --- a/react/features/base/participants/middleware.js +++ b/react/features/base/participants/middleware.js @@ -178,20 +178,24 @@ MiddlewareRegistry.register(store => next => action => { } case SET_LOCAL_PARTICIPANT_RECORDING_STATUS: { + const state = store.getState(); const { recording } = action; - const localId = getLocalParticipant(store.getState())?.id; - - store.dispatch(participantUpdated({ - // XXX Only the local participant is allowed to update without - // stating the JitsiConference instance (i.e. participant property - // `conference` for a remote participant) because the local - // participant is uniquely identified by the very fact that there is - // only one local participant. - - id: localId, - local: true, - localRecording: recording - })); + const localId = getLocalParticipant(state)?.id; + const { localRecording } = state['features/base/config']; + + if (localRecording.notifyAllParticipants) { + store.dispatch(participantUpdated({ + // XXX Only the local participant is allowed to update without + // stating the JitsiConference instance (i.e. participant property + // `conference` for a remote participant) because the local + // participant is uniquely identified by the very fact that there is + // only one local participant. + + id: localId, + local: true, + localRecording: recording + })); + } break; } diff --git a/react/features/recording/components/Recording/StartRecordingDialogContent.js b/react/features/recording/components/Recording/StartRecordingDialogContent.js index 3eeecc4512..8a0bacd513 100644 --- a/react/features/recording/components/Recording/StartRecordingDialogContent.js +++ b/react/features/recording/components/Recording/StartRecordingDialogContent.js @@ -49,6 +49,11 @@ type Props = { */ _localRecordingEnabled: boolean, + /** + * Whether we won't notify the other participants about the recording. + */ + _localRecordingNoNotification: boolean, + /** * The color-schemed stylesheet of this component. */ @@ -442,7 +447,8 @@ class StartRecordingDialogContent extends Component { return ( @@ -609,7 +615,14 @@ class StartRecordingDialogContent extends Component { * @returns {React$Component} */ _renderLocalRecordingContent() { - const { _styles: styles, isValidating, t, _dialogStyles, selectedRecordingService } = this.props; + const { + _styles: styles, + isValidating, + t, + _dialogStyles, + selectedRecordingService, + _localRecordingNoNotification + } = this.props; if (!this._localRecordingAvailable) { return null; @@ -645,9 +658,14 @@ class StartRecordingDialogContent extends Component { === RECORDING_TYPES.LOCAL } /> {selectedRecordingService === RECORDING_TYPES.LOCAL - && - {t('recording.localRecordingWarning')} - + && <> + + {t('recording.localRecordingWarning')} + + {_localRecordingNoNotification && + {t('recording.localRecordingNoNotificationWarning')} + } + } @@ -689,7 +707,8 @@ function _mapStateToProps(state) { return { ..._abstractMapStateToProps(state), isVpaas: isVpaasMeeting(state), - _localRecordingEnabled: state['features/base/config'].enableLocalRecording, + _localRecordingEnabled: state['features/base/config'].localRecording.enable, + _localRecordingNoNotification: !state['features/base/config'].localRecording.notifyAllParticipants, _styles: ColorSchemeRegistry.get(state, 'StartRecordingDialogContent') }; } diff --git a/react/features/recording/middleware.js b/react/features/recording/middleware.js index 49bfb1bbe4..138d8f0cf0 100644 --- a/react/features/recording/middleware.js +++ b/react/features/recording/middleware.js @@ -132,6 +132,8 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action => } case START_LOCAL_RECORDING: { + const { localRecording } = getState()['features/base/config']; + try { await LocalRecordingManager.startLocalRecording({ dispatch, getState }); @@ -140,9 +142,12 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action => titleKey: 'dialog.recording' }; - dispatch(playSound(RECORDING_ON_SOUND_ID)); + if (localRecording.notifyAllParticipants) { + dispatch(playSound(RECORDING_ON_SOUND_ID)); + } dispatch(showNotification(props, NOTIFICATION_TIMEOUT_TYPE.MEDIUM)); dispatch(updateLocalRecordingStatus(true)); + sendAnalytics(createRecordingEvent('started', 'local')); } catch (err) { logger.error('Capture failed', err); @@ -158,10 +163,14 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => async action => } case STOP_LOCAL_RECORDING: { + const { localRecording } = getState()['features/base/config']; + if (LocalRecordingManager.isRecordingLocally()) { LocalRecordingManager.stopLocalRecording(); - dispatch(playSound(RECORDING_OFF_SOUND_ID)); dispatch(updateLocalRecordingStatus(false)); + if (localRecording.notifyAllParticipants) { + dispatch(playSound(RECORDING_OFF_SOUND_ID)); + } } break; }