import { Component } from 'react'; import { WithTranslation } from 'react-i18next'; import { createRecordingDialogEvent } from '../../../analytics/AnalyticsEvents'; import { sendAnalytics } from '../../../analytics/functions'; import { IReduxState, IStore } from '../../../app/types'; import ColorSchemeRegistry from '../../../base/color-scheme/ColorSchemeRegistry'; import { _abstractMapStateToProps } from '../../../base/dialog/functions'; import { isJwtFeatureEnabled } from '../../../base/jwt/functions'; import { isLocalParticipantModerator } from '../../../base/participants/functions'; import { authorizeDropbox, updateDropboxToken } from '../../../dropbox/actions'; import { isVpaasMeeting } from '../../../jaas/functions'; import { canAddTranscriber } from '../../../transcribing/functions'; import { RECORDING_TYPES } from '../../constants'; import { supportsLocalRecording } from '../../functions'; /** * The type of the React {@code Component} props of * {@link AbstractStartRecordingDialogContent}. */ export interface IProps extends WithTranslation { /** * Whether the local participant can start transcribing. */ _canStartTranscribing: boolean; /** * Style of the dialogs feature. */ _dialogStyles: any; /** * Whether to hide the storage warning or not. */ _hideStorageWarning: boolean; /** * Whether local recording is available or not. */ _localRecordingAvailable: boolean; /** * Whether local recording is enabled or not. */ _localRecordingEnabled: boolean; /** * Whether we won't notify the other participants about the recording. */ _localRecordingNoNotification: boolean; /** * Whether self local recording is enabled or not. */ _localRecordingSelfEnabled: boolean; /** * Whether to render recording. */ _renderRecording: boolean; /** * The color-schemed stylesheet of this component. */ _styles: any; /** * The redux dispatch function. */ dispatch: IStore['dispatch']; /** * Whether to show file recordings service, even if integrations * are enabled. */ fileRecordingsServiceEnabled: boolean; /** * Whether to show the possibility to share file recording with other people (e.g. Meeting participants), based on * the actual implementation on the backend. */ fileRecordingsServiceSharingEnabled: boolean; /** * If true the content related to the integrations will be shown. */ integrationsEnabled: boolean; /** * true if we have valid oauth token. */ isTokenValid: boolean; /** * true if we are in process of validating the oauth token. */ isValidating: boolean; /** * Whether or not the current meeting is a vpaas one. */ isVpaas: boolean; /** * Whether or not we should only record the local streams. */ localRecordingOnlySelf?: boolean; /** * The function will be called when there are changes related to the * switches. */ onChange: Function; /** * Callback to change the local recording only self setting. */ onLocalRecordingSelfChange?: () => void; /** * Callback to change the audio and video recording setting. */ onRecordAudioAndVideoChange: Function; /** * Callback to be invoked on sharing setting change. */ onSharingSettingChanged: () => void; /** * Callback to change the transcription recording setting. */ onTranscriptionChange: Function; /** * The currently selected recording service of type: RECORDING_TYPES. */ selectedRecordingService: string | null; /** * Boolean to set file recording sharing on or off. */ sharingSetting: boolean; /** * Whether to show the audio and video related content. */ shouldRecordAudioAndVideo: boolean; /** * Whether to show the transcription related content. */ shouldRecordTranscription: boolean; /** * Number of MiB of available space in user's Dropbox account. */ spaceLeft?: number; /** * The display name of the user's Dropbox account. */ userName?: string; } export interface IState { /** * Whether to show the advanced options or not. */ showAdvancedOptions: boolean; } /** * React Component for getting confirmation to start a recording session. * * @augments Component */ class AbstractStartRecordingDialogContent extends Component { /** * Initializes a new {@code AbstractStartRecordingDialogContent} instance. * * @inheritdoc */ constructor(props: IProps) { super(props); // Bind event handler; it bounds once for every instance. this._onSignIn = this._onSignIn.bind(this); this._onSignOut = this._onSignOut.bind(this); this._onDropboxSwitchChange = this._onDropboxSwitchChange.bind(this); this._onRecordingServiceSwitchChange = this._onRecordingServiceSwitchChange.bind(this); this._onLocalRecordingSwitchChange = this._onLocalRecordingSwitchChange.bind(this); this._onTranscriptionSwitchChange = this._onTranscriptionSwitchChange.bind(this); this._onRecordAudioAndVideoSwitchChange = this._onRecordAudioAndVideoSwitchChange.bind(this); this._onToggleShowOptions = this._onToggleShowOptions.bind(this); this.state = { showAdvancedOptions: true }; } /** * Implements the Component's componentDidMount method. * * @inheritdoc */ componentDidMount() { if (!this._shouldRenderNoIntegrationsContent() && !this._shouldRenderIntegrationsContent() && !this._shouldRenderFileSharingContent()) { this._onLocalRecordingSwitchChange(); } } /** * Implements {@code Component#componentDidUpdate}. * * @inheritdoc */ componentDidUpdate(prevProps: IProps) { // Auto sign-out when the use chooses another recording service. if (prevProps.selectedRecordingService === RECORDING_TYPES.DROPBOX && this.props.selectedRecordingService !== RECORDING_TYPES.DROPBOX && this.props.isTokenValid) { this._onSignOut(); } } /** * Returns whether the advanced options should be rendered. * * @returns {boolean} */ _onToggleShowOptions() { this.setState({ showAdvancedOptions: !this.state.showAdvancedOptions }); } /** * Whether the file sharing content should be rendered or not. * * @returns {boolean} */ _shouldRenderFileSharingContent() { const { fileRecordingsServiceEnabled, fileRecordingsServiceSharingEnabled, isVpaas, selectedRecordingService } = this.props; if (!fileRecordingsServiceEnabled || !fileRecordingsServiceSharingEnabled || isVpaas || selectedRecordingService !== RECORDING_TYPES.JITSI_REC_SERVICE) { return false; } return true; } /** * Whether the save transcription content should be rendered or not. * * @returns {boolean} */ _canStartTranscribing() { return this.props._canStartTranscribing; } /** * Whether the no integrations content should be rendered or not. * * @returns {boolean} */ _shouldRenderNoIntegrationsContent() { // show the non integrations part only if fileRecordingsServiceEnabled // is enabled if (!this.props.fileRecordingsServiceEnabled) { return false; } return true; } /** * Whether the integrations content should be rendered or not. * * @returns {boolean} */ _shouldRenderIntegrationsContent() { if (!this.props.integrationsEnabled) { return false; } return true; } /** * Handler for transcription switch change. * * @param {boolean} value - The new value. * @returns {void} */ _onTranscriptionSwitchChange(value: boolean | undefined) { this.props.onTranscriptionChange(value); } /** * Handler for audio and video switch change. * * @param {boolean} value - The new value. * @returns {void} */ _onRecordAudioAndVideoSwitchChange(value: boolean | undefined) { this.props.onRecordAudioAndVideoChange(value); } /** * Handler for onValueChange events from the Switch component. * * @returns {void} */ _onRecordingServiceSwitchChange() { const { onChange, selectedRecordingService } = this.props; // act like group, cannot toggle off if (selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE) { return; } onChange(RECORDING_TYPES.JITSI_REC_SERVICE); } /** * Handler for onValueChange events from the Switch component. * * @returns {void} */ _onDropboxSwitchChange() { const { isTokenValid, onChange, selectedRecordingService } = this.props; // act like group, cannot toggle off if (selectedRecordingService === RECORDING_TYPES.DROPBOX) { return; } onChange(RECORDING_TYPES.DROPBOX); if (!isTokenValid) { this._onSignIn(); } } /** * Handler for onValueChange events from the Switch component. * * @returns {void} */ _onLocalRecordingSwitchChange() { const { _localRecordingAvailable, onChange, selectedRecordingService } = this.props; if (!_localRecordingAvailable) { return; } // act like group, cannot toggle off if (selectedRecordingService === RECORDING_TYPES.LOCAL) { return; } onChange(RECORDING_TYPES.LOCAL); } /** * Sings in a user. * * @returns {void} */ _onSignIn() { sendAnalytics(createRecordingDialogEvent('start', 'signIn.button')); this.props.dispatch(authorizeDropbox()); } /** * Sings out an user from dropbox. * * @returns {void} */ _onSignOut() { sendAnalytics(createRecordingDialogEvent('start', 'signOut.button')); this.props.dispatch(updateDropboxToken()); } } /** * Maps part of the redux state to the props of this component. * * @param {Object} state - The Redux state. * @returns {IProps} */ export function mapStateToProps(state: IReduxState) { const { localRecording, recordingService } = state['features/base/config']; const _localRecordingAvailable = !localRecording?.disable && supportsLocalRecording(); const isModerator = isLocalParticipantModerator(state); return { ..._abstractMapStateToProps(state), isVpaas: isVpaasMeeting(state), _canStartTranscribing: canAddTranscriber(state), _hideStorageWarning: Boolean(recordingService?.hideStorageWarning), _renderRecording: isJwtFeatureEnabled(state, 'recording', isModerator, false), _localRecordingAvailable, _localRecordingEnabled: !localRecording?.disable, _localRecordingSelfEnabled: !localRecording?.disableSelfRecording, _localRecordingNoNotification: !localRecording?.notifyAllParticipants, _styles: ColorSchemeRegistry.get(state, 'StartRecordingDialogContent') }; } export default AbstractStartRecordingDialogContent;