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;