diff --git a/react/features/base/ui/components/types.ts b/react/features/base/ui/components/types.ts
index 2a69f0cf5..e2a10075b 100644
--- a/react/features/base/ui/components/types.ts
+++ b/react/features/base/ui/components/types.ts
@@ -7,7 +7,7 @@ export interface ButtonProps {
/**
* Label used for accessibility.
*/
- accessibilityLabel: string;
+ accessibilityLabel?: string;
/**
* Whether or not the button is disabled.
diff --git a/react/features/recording/components/Recording/AbstractStartRecordingDialogContent.tsx b/react/features/recording/components/Recording/AbstractStartRecordingDialogContent.tsx
new file mode 100644
index 000000000..e28449757
--- /dev/null
+++ b/react/features/recording/components/Recording/AbstractStartRecordingDialogContent.tsx
@@ -0,0 +1,360 @@
+/* eslint-disable lines-around-comment */
+import { Component } from 'react';
+// @ts-ignore
+import { WithTranslation } from 'react-i18next';
+
+import {
+ createRecordingDialogEvent,
+ sendAnalytics
+ // @ts-ignore
+} from '../../../analytics';
+// @ts-ignore
+import { ColorSchemeRegistry } from '../../../base/color-scheme';
+// @ts-ignore
+import {
+ _abstractMapStateToProps
+ // @ts-ignore
+} from '../../../base/dialog';
+// @ts-ignore
+import { StyleType } from '../../../base/styles';
+// @ts-ignore
+import { authorizeDropbox, updateDropboxToken } from '../../../dropbox';
+// @ts-ignore
+import { isVpaasMeeting } from '../../../jaas/functions';
+// @ts-ignore
+import { RECORDING_TYPES } from '../../constants';
+// @ts-ignore
+import { supportsLocalRecording } from '../../functions';
+
+/**
+ * The type of the React {@code Component} props of
+ * {@link AbstractStartRecordingDialogContent}.
+ */
+export interface Props extends WithTranslation {
+
+ /**
+ * Style of the dialogs feature.
+ */
+ _dialogStyles: StyleType,
+
+ /**
+ * 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,
+
+ /**
+ * The color-schemed stylesheet of this component.
+ */
+ _styles: StyleType,
+
+ /**
+ * The redux dispatch function.
+ */
+ dispatch: Function,
+
+ /**
+ * 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: Function,
+
+ /**
+ * Callback to be invoked on sharing setting change.
+ */
+ onSharingSettingChanged: Function,
+
+ /**
+ * The currently selected recording service of type: RECORDING_TYPES.
+ */
+ selectedRecordingService: string | null,
+
+ /**
+ * Boolean to set file recording sharing on or off.
+ */
+ sharingSetting: boolean,
+
+ /**
+ * Number of MiB of available space in user's Dropbox account.
+ */
+ spaceLeft: number | null,
+
+ /**
+ * The display name of the user's Dropbox account.
+ */
+ userName: string | null
+}
+
+/**
+ * React Component for getting confirmation to start a file recording session.
+ *
+ * @augments Component
+ */
+class AbstractStartRecordingDialogContent
extends Component
{
+ /**
+ * Initializes a new {@code AbstractStartRecordingDialogContent} instance.
+ *
+ * @inheritdoc
+ */
+ constructor(props: P) {
+ 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);
+ }
+
+ /**
+ * Implements the Component's componentDidMount method.
+ *
+ * @inheritdoc
+ */
+ componentDidMount() {
+ if (!this._shouldRenderNoIntegrationsContent()
+ && !this._shouldRenderIntegrationsContent()
+ && !this._shouldRenderFileSharingContent()) {
+ this._onLocalRecordingSwitchChange();
+ }
+ }
+
+ /**
+ * Implements {@code Component#componentDidUpdate}.
+ *
+ * @inheritdoc
+ */
+ componentDidUpdate(prevProps: P) {
+ // 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();
+ }
+ }
+
+ /**
+ * 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 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 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 {Props}
+ */
+export function mapStateToProps(state: any) {
+ const { localRecording, recordingService } = state['features/base/config'];
+ const _localRecordingAvailable
+ = !localRecording?.disable && supportsLocalRecording();
+
+ return {
+ ..._abstractMapStateToProps(state),
+ isVpaas: isVpaasMeeting(state),
+ _hideStorageWarning: recordingService?.hideStorageWarning,
+ _localRecordingAvailable,
+ _localRecordingEnabled: !localRecording?.disable,
+ _localRecordingSelfEnabled: !localRecording?.disableSelfRecording,
+ _localRecordingNoNotification: !localRecording?.notifyAllParticipants,
+ _styles: ColorSchemeRegistry.get(state, 'StartRecordingDialogContent')
+ };
+}
+
+export default AbstractStartRecordingDialogContent;
diff --git a/react/features/recording/components/Recording/StartRecordingDialogContent.js b/react/features/recording/components/Recording/StartRecordingDialogContent.js
deleted file mode 100644
index fc9fe6520..000000000
--- a/react/features/recording/components/Recording/StartRecordingDialogContent.js
+++ /dev/null
@@ -1,781 +0,0 @@
-import React, { Component } from 'react';
-
-import {
- createRecordingDialogEvent,
- sendAnalytics
-} from '../../../analytics';
-import { ColorSchemeRegistry } from '../../../base/color-scheme';
-import {
- _abstractMapStateToProps
-} from '../../../base/dialog';
-import { translate } from '../../../base/i18n';
-import {
- Container,
- Image,
- LoadingIndicator,
- Switch,
- Text
-} from '../../../base/react';
-import { connect } from '../../../base/redux';
-import { StyleType } from '../../../base/styles';
-import { Button } from '../../../base/ui';
-import { BUTTON_TYPES } from '../../../base/ui/constants';
-import { authorizeDropbox, updateDropboxToken } from '../../../dropbox';
-import { isVpaasMeeting } from '../../../jaas/functions';
-import { RECORDING_TYPES } from '../../constants';
-import { getRecordingDurationEstimation, supportsLocalRecording } from '../../functions';
-
-import {
- DROPBOX_LOGO,
- ICON_CLOUD,
- ICON_INFO,
- ICON_USERS,
- LOCAL_RECORDING,
- TRACK_COLOR
-} from './styles';
-
-type Props = {
-
- /**
- * Style of the dialogs feature.
- */
- _dialogStyles: StyleType,
-
- /**
- * Whether to hide the storage warning or not.
- */
- _hideStorageWarning: 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,
-
- /**
- * The color-schemed stylesheet of this component.
- */
- _styles: StyleType,
-
- /**
- * The redux dispatch function.
- */
- dispatch: Function,
-
- /**
- * 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: Function,
-
- /**
- * Callback to be invoked on sharing setting change.
- */
- onSharingSettingChanged: Function,
-
- /**
- * The currently selected recording service of type: RECORDING_TYPES.
- */
- selectedRecordingService: ?string,
-
- /**
- * Boolean to set file recording sharing on or off.
- */
- sharingSetting: boolean,
-
- /**
- * Number of MiB of available space in user's Dropbox account.
- */
- spaceLeft: ?number,
-
- /**
- * The translate function.
- */
- t: Function,
-
- /**
- * The display name of the user's Dropbox account.
- */
- userName: ?string
-};
-
-/**
- * React Component for getting confirmation to start a file recording session.
- *
- * @augments Component
- */
-class StartRecordingDialogContent extends Component {
- _localRecordingAvailable: boolean;
-
- /**
- * Initializes a new {@code StartRecordingDialogContent} instance.
- *
- * @inheritdoc
- */
- constructor(props) {
- super(props);
-
- this._localRecordingAvailable = props._localRecordingEnabled && supportsLocalRecording();
-
- // Bind event handler so it is only bound 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);
- }
-
- /**
- * Implements the Component's componentDidMount method.
- *
- * @inheritdoc
- */
- componentDidMount() {
- if (!this._shouldRenderNoIntegrationsContent()
- && !this._shouldRenderIntegrationsContent()
- && !this._shouldRenderFileSharingContent()) {
- this._onLocalRecordingSwitchChange();
- }
- }
-
- /**
- * Implements {@code Component#componentDidUpdate}.
- *
- * @inheritdoc
- */
- componentDidUpdate(prevProps) {
- // 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();
- }
- }
-
- /**
- * Renders the component.
- *
- * @protected
- * @returns {React$Component}
- */
- render() {
- const { _styles: styles } = this.props;
-
- return (
-
- { this._renderNoIntegrationsContent() }
- { this._renderFileSharingContent() }
- { this._renderUploadToTheCloudInfo() }
- { this._renderIntegrationsContent() }
- { this._renderLocalRecordingContent() }
-
- );
- }
-
- /**
- * 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;
- }
-
- /**
- * Renders the file recording service sharing options, if enabled.
- *
- * @returns {React$Component}
- */
- _renderFileSharingContent() {
- if (!this._shouldRenderFileSharingContent()) {
- return null;
- }
-
- const {
- _dialogStyles,
- _styles: styles,
- isValidating,
- onSharingSettingChanged,
- sharingSetting,
- t
- } = this.props;
-
- return (
-
-
-
-
-
- { t('recording.fileSharingdescription') }
-
-
-
- );
- }
-
- /**
- * Renders the info in case recording is uploaded to the cloud.
- *
- * @returns {React$Component}
- */
- _renderUploadToTheCloudInfo() {
- const {
- _dialogStyles,
- _hideStorageWarning,
- _styles: styles,
- isVpaas,
- selectedRecordingService,
- t
- } = this.props;
-
- if (!(isVpaas && selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE) || _hideStorageWarning) {
- return null;
- }
-
- return (
-
-
-
- { t('recording.serviceDescriptionCloudInfo') }
-
-
- );
- }
-
- /**
- * 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;
- }
-
- /**
- * Renders the content in case no integrations were enabled.
- *
- * @returns {React$Component}
- */
- _renderNoIntegrationsContent() {
- if (!this._shouldRenderNoIntegrationsContent()) {
- return null;
- }
-
- const { _dialogStyles, _styles: styles, isValidating, isVpaas, t } = this.props;
-
- const switchContent
- = this.props.integrationsEnabled || this.props._localRecordingEnabled
- ? (
-
- ) : null;
-
- const label = isVpaas ? t('recording.serviceDescriptionCloud') : t('recording.serviceDescription');
- const jitsiContentRecordingIconContainer
- = this.props.integrationsEnabled || this.props._localRecordingEnabled
- ? 'jitsi-content-recording-icon-container-with-switch'
- : 'jitsi-content-recording-icon-container-without-switch';
- const contentRecordingClass = isVpaas
- ? 'cloud-content-recording-icon-container'
- : jitsiContentRecordingIconContainer;
- const jitsiRecordingHeaderClass = !isVpaas && 'jitsi-recording-header';
-
- return (
-
-
-
-
-
- { label }
-
- { switchContent }
-
- );
- }
-
- /**
- * Whether the integrations content should be rendered or not.
- *
- * @returns {boolean}
- */
- _shouldRenderIntegrationsContent() {
- if (!this.props.integrationsEnabled) {
- return false;
- }
-
- return true;
- }
-
- /**
- * Renders the content in case integrations were enabled.
- *
- * @protected
- * @returns {React$Component}
- */
- _renderIntegrationsContent() {
- if (!this._shouldRenderIntegrationsContent()) {
- return null;
- }
-
- const { _dialogStyles, _styles: styles, isTokenValid, isValidating, t } = this.props;
-
- let content = null;
- let switchContent = null;
-
- if (isValidating) {
- content = this._renderSpinner();
- switchContent = ;
- } else if (isTokenValid) {
- content = this._renderSignOut();
- switchContent = (
-
-
-
- );
-
- } else {
- switchContent = (
-
-
-
- );
- }
-
- if (this.props.fileRecordingsServiceEnabled || this._localRecordingAvailable) {
- switchContent = (
-
- );
- }
-
- return (
-
-
-
-
-
-
- { t('recording.authDropboxText') }
-
- { switchContent }
-
-
- { content }
-
-
- );
- }
-
- _onDropboxSwitchChange: () => void;
- _onRecordingServiceSwitchChange: () => void;
- _onLocalRecordingSwitchChange: () => void;
-
- /**
- * 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 {
- onChange,
- selectedRecordingService
- } = this.props;
-
- if (!this._localRecordingAvailable) {
- return;
- }
-
- // act like group, cannot toggle off
- if (selectedRecordingService
- === RECORDING_TYPES.LOCAL) {
- return;
- }
-
- onChange(RECORDING_TYPES.LOCAL);
- }
-
- /**
- * Renders a spinner component.
- *
- * @returns {React$Component}
- */
- _renderSpinner() {
- return (
-
- );
- }
-
- /**
- * Renders the screen with the account information of a logged in user.
- *
- * @returns {React$Component}
- */
- _renderSignOut() {
- const { _styles: styles, spaceLeft, t, userName } = this.props;
- const duration = getRecordingDurationEstimation(spaceLeft);
-
- return (
-
-
-
-
- { t('recording.loggedIn', { userName }) }
-
-
-
-
- {
- t('recording.availableSpace', {
- spaceLeft,
- duration
- })
- }
-
-
-
-
- );
- }
-
- _renderLocalRecordingContent: () => void;
-
- /**
- * Renders the content for local recordings.
- *
- * @protected
- * @returns {React$Component}
- */
- _renderLocalRecordingContent() {
- const {
- _styles: styles,
- isValidating,
- t,
- _dialogStyles,
- selectedRecordingService,
- _localRecordingNoNotification
- } = this.props;
-
- if (!this._localRecordingAvailable) {
- return null;
- }
-
- return (
- <>
-
-
-
-
-
-
- { t('recording.saveLocalRecording') }
-
-
-
-
- {selectedRecordingService === RECORDING_TYPES.LOCAL && (
- <>
- {this.props._localRecordingSelfEnabled && (
-
-
-
-
-
-
- {t('recording.onlyRecordSelf')}
-
-
-
-
- )}
-
- {t('recording.localRecordingWarning')}
-
- {_localRecordingNoNotification && !this.props.localRecordingOnlySelf
- &&
- {t('recording.localRecordingNoNotificationWarning')}
-
- }
- >
- )}
- >
-
- );
- }
-
- _onSignIn: () => void;
-
- /**
- * Sings in a user.
- *
- * @returns {void}
- */
- _onSignIn() {
- sendAnalytics(createRecordingDialogEvent('start', 'signIn.button'));
- this.props.dispatch(authorizeDropbox());
- }
-
- _onSignOut: () => void;
-
- /**
- * 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 {Props}
- */
-function _mapStateToProps(state) {
- return {
- ..._abstractMapStateToProps(state),
- isVpaas: isVpaasMeeting(state),
- _hideStorageWarning: state['features/base/config'].recordingService?.hideStorageWarning,
- _localRecordingEnabled: !state['features/base/config'].localRecording?.disable,
- _localRecordingSelfEnabled: !state['features/base/config'].localRecording?.disableSelfRecording,
- _localRecordingNoNotification: !state['features/base/config'].localRecording?.notifyAllParticipants,
- _styles: ColorSchemeRegistry.get(state, 'StartRecordingDialogContent')
- };
-}
-
-export default translate(connect(_mapStateToProps)(StartRecordingDialogContent));
diff --git a/react/features/recording/components/Recording/native/StartRecordingDialog.js b/react/features/recording/components/Recording/native/StartRecordingDialog.js
index dcfbe2c5e..5227c53fd 100644
--- a/react/features/recording/components/Recording/native/StartRecordingDialog.js
+++ b/react/features/recording/components/Recording/native/StartRecordingDialog.js
@@ -1,5 +1,3 @@
-// @flow
-
import React from 'react';
import { translate } from '../../../../base/i18n';
@@ -14,9 +12,11 @@ import AbstractStartRecordingDialog, {
type Props,
mapStateToProps
} from '../AbstractStartRecordingDialog';
-import StartRecordingDialogContent from '../StartRecordingDialogContent';
import styles from '../styles.native';
+import StartRecordingDialogContent from './StartRecordingDialogContent';
+
+
/**
* React Component for getting confirmation to start a file recording session in
* progress.
diff --git a/react/features/recording/components/Recording/native/StartRecordingDialogContent.tsx b/react/features/recording/components/Recording/native/StartRecordingDialogContent.tsx
new file mode 100644
index 000000000..8d01c6a29
--- /dev/null
+++ b/react/features/recording/components/Recording/native/StartRecordingDialogContent.tsx
@@ -0,0 +1,314 @@
+/* eslint-disable lines-around-comment */
+import React from 'react';
+import { Image, View } from 'react-native';
+import { Switch, Text } from 'react-native-paper';
+
+import { translate } from '../../../../base/i18n/functions';
+// @ts-ignore
+import { LoadingIndicator } from '../../../../base/react';
+import { connect } from '../../../../base/redux/functions';
+import Button from '../../../../base/ui/components/native/Button';
+import { BUTTON_TYPES } from '../../../../base/ui/constants';
+// @ts-ignore
+import { RECORDING_TYPES } from '../../../constants';
+// @ts-ignore
+import { getRecordingDurationEstimation } from '../../../functions';
+import AbstractStartRecordingDialogContent, {
+ Props,
+ mapStateToProps
+} from '../AbstractStartRecordingDialogContent';
+import {
+ DROPBOX_LOGO,
+ ICON_CLOUD,
+ ICON_INFO,
+ ICON_USERS,
+ TRACK_COLOR
+ // @ts-ignore
+} from '../styles.native';
+
+
+/**
+ * The start recording dialog content for the mobile application.
+ */
+class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
+ /**
+ * Renders the component.
+ *
+ * @protected
+ * @returns {React$Component}
+ */
+ render() {
+ const { _styles: styles } = this.props;
+
+ return (
+
+ { this._renderNoIntegrationsContent() }
+ { this._renderFileSharingContent() }
+ { this._renderUploadToTheCloudInfo() }
+ { this._renderIntegrationsContent() }
+
+ );
+ }
+
+ /**
+ * Renders the content in case no integrations were enabled.
+ *
+ * @returns {React$Component}
+ */
+ _renderNoIntegrationsContent() {
+ const {
+ _dialogStyles,
+ _styles: styles,
+ integrationsEnabled,
+ isValidating,
+ selectedRecordingService,
+ t
+ } = this.props;
+
+ if (!this._shouldRenderNoIntegrationsContent()) {
+ return null;
+ }
+
+ const switchContent
+ = integrationsEnabled
+ ? (
+
+ ) : null;
+
+ return (
+
+
+
+ { t('recording.serviceDescription') }
+
+ { switchContent }
+
+ );
+ }
+
+ /**
+ * Renders the file recording service sharing options, if enabled.
+ *
+ * @returns {React$Component}
+ */
+ _renderFileSharingContent() {
+ if (!this._shouldRenderFileSharingContent()) {
+ return null;
+ }
+
+ const {
+ _dialogStyles,
+ _styles: styles,
+ isValidating,
+ onSharingSettingChanged,
+ sharingSetting,
+ t
+ } = this.props;
+
+ return (
+
+
+
+ { t('recording.fileSharingdescription') }
+
+
+
+ );
+ }
+
+ /**
+ * Renders the info in case recording is uploaded to the cloud.
+ *
+ * @returns {React$Component}
+ */
+ _renderUploadToTheCloudInfo() {
+ const {
+ _dialogStyles,
+ _hideStorageWarning,
+ _styles: styles,
+ isVpaas,
+ selectedRecordingService,
+ t
+ } = this.props;
+
+ if (!(isVpaas && selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE) || _hideStorageWarning) {
+ return null;
+ }
+
+ return (
+
+
+
+ { t('recording.serviceDescriptionCloudInfo') }
+
+
+ );
+ }
+
+ /**
+ * Renders a spinner component.
+ *
+ * @returns {React$Component}
+ */
+ _renderSpinner() {
+ return (
+
+ );
+ }
+
+ /**
+ * Renders the screen with the account information of a logged in user.
+ *
+ * @returns {React$Component}
+ */
+ _renderSignOut() {
+ const { _styles: styles, spaceLeft, t, userName } = this.props;
+ const duration = getRecordingDurationEstimation(spaceLeft);
+
+ return (
+
+
+ { t('recording.loggedIn', { userName }) }
+
+
+ {
+ t('recording.availableSpace', {
+ spaceLeft,
+ duration
+ })
+ }
+
+
+ );
+ }
+
+ /**
+ * Renders the content in case integrations were enabled.
+ *
+ * @protected
+ * @returns {React$Component}
+ */
+ _renderIntegrationsContent() {
+ if (!this._shouldRenderIntegrationsContent()) {
+ return null;
+ }
+
+ const {
+ _dialogStyles,
+ _styles: styles,
+ fileRecordingsServiceEnabled,
+ isTokenValid,
+ isValidating,
+ selectedRecordingService,
+ t
+ } = this.props;
+
+ let content = null;
+ let switchContent = null;
+
+ if (isValidating) {
+ content = this._renderSpinner();
+ switchContent = ;
+ } else if (isTokenValid) {
+ content = this._renderSignOut();
+ switchContent = (
+
+ );
+
+ } else {
+ switchContent = (
+
+ );
+ }
+
+ if (fileRecordingsServiceEnabled) {
+ switchContent = (
+
+ );
+ }
+
+ return (
+
+
+
+
+ { t('recording.authDropboxText') }
+
+ { switchContent }
+
+
+ { content }
+
+
+ );
+ }
+}
+
+export default translate(connect(mapStateToProps)(StartRecordingDialogContent));
diff --git a/react/features/recording/components/Recording/web/StartRecordingDialog.js b/react/features/recording/components/Recording/web/StartRecordingDialog.js
index 2f9135487..2877ad557 100644
--- a/react/features/recording/components/Recording/web/StartRecordingDialog.js
+++ b/react/features/recording/components/Recording/web/StartRecordingDialog.js
@@ -1,5 +1,3 @@
-// @flow
-
import React from 'react';
import { Dialog } from '../../../../base/dialog';
@@ -11,7 +9,9 @@ import { RECORDING_TYPES } from '../../../constants';
import AbstractStartRecordingDialog, {
mapStateToProps as abstractMapStateToProps
} from '../AbstractStartRecordingDialog';
-import StartRecordingDialogContent from '../StartRecordingDialogContent';
+
+import StartRecordingDialogContent from './StartRecordingDialogContent';
+
/**
* React Component for getting confirmation to start a file recording session in
diff --git a/react/features/recording/components/Recording/web/StartRecordingDialogContent.tsx b/react/features/recording/components/Recording/web/StartRecordingDialogContent.tsx
new file mode 100644
index 000000000..bd4930573
--- /dev/null
+++ b/react/features/recording/components/Recording/web/StartRecordingDialogContent.tsx
@@ -0,0 +1,401 @@
+/* eslint-disable lines-around-comment */
+import React from 'react';
+
+import { translate } from '../../../../base/i18n/functions';
+import {
+ Container,
+ Image,
+ LoadingIndicator,
+ Switch,
+ Text
+ // @ts-ignore
+} from '../../../../base/react';
+import { connect } from '../../../../base/redux/functions';
+import Button from '../../../../base/ui/components/web/Button';
+import { BUTTON_TYPES } from '../../../../base/ui/constants';
+// @ts-ignore
+import { RECORDING_TYPES } from '../../../constants';
+// @ts-ignore
+import { getRecordingDurationEstimation } from '../../../functions';
+import AbstractStartRecordingDialogContent, {
+ Props,
+ mapStateToProps
+} from '../AbstractStartRecordingDialogContent';
+import {
+ DROPBOX_LOGO,
+ ICON_CLOUD,
+ ICON_INFO,
+ ICON_USERS,
+ LOCAL_RECORDING,
+ TRACK_COLOR
+ // @ts-ignore
+} from '../styles.web';
+
+
+/**
+ * The start recording dialog content for the mobile application.
+ */
+class StartRecordingDialogContent extends AbstractStartRecordingDialogContent {
+ /**
+ * Renders the component.
+ *
+ * @protected
+ * @returns {React$Component}
+ */
+ render() {
+ return (
+
+ { this._renderNoIntegrationsContent() }
+ { this._renderFileSharingContent() }
+ { this._renderUploadToTheCloudInfo() }
+ { this._renderIntegrationsContent() }
+ { this._renderLocalRecordingContent() }
+
+ );
+ }
+
+ /**
+ * Renders the content in case no integrations were enabled.
+ *
+ * @returns {React$Component}
+ */
+ _renderNoIntegrationsContent() {
+ if (!this._shouldRenderNoIntegrationsContent()) {
+ return null;
+ }
+
+ const {
+ _localRecordingAvailable,
+ integrationsEnabled,
+ isValidating,
+ isVpaas,
+ selectedRecordingService,
+ t
+ } = this.props;
+
+ const switchContent
+ = integrationsEnabled || _localRecordingAvailable
+ ? (
+
+ ) : null;
+
+ const label = isVpaas ? t('recording.serviceDescriptionCloud') : t('recording.serviceDescription');
+ const jitsiContentRecordingIconContainer
+ = integrationsEnabled || _localRecordingAvailable
+ ? 'jitsi-content-recording-icon-container-with-switch'
+ : 'jitsi-content-recording-icon-container-without-switch';
+ const contentRecordingClass = isVpaas
+ ? 'cloud-content-recording-icon-container'
+ : jitsiContentRecordingIconContainer;
+ const jitsiRecordingHeaderClass = !isVpaas && 'jitsi-recording-header';
+
+ return (
+
+
+
+
+
+ { label }
+
+ { switchContent }
+
+ );
+ }
+
+ /**
+ * Renders the file recording service sharing options, if enabled.
+ *
+ * @returns {React$Component}
+ */
+ _renderFileSharingContent() {
+ if (!this._shouldRenderFileSharingContent()) {
+ return null;
+ }
+
+ const {
+ isValidating,
+ onSharingSettingChanged,
+ sharingSetting,
+ t
+ // @ts-ignore
+ } = this.props;
+
+ return (
+
+
+
+
+
+ { t('recording.fileSharingdescription') }
+
+
+
+ );
+ }
+
+ /**
+ * Renders the info in case recording is uploaded to the cloud.
+ *
+ * @returns {React$Component}
+ */
+ _renderUploadToTheCloudInfo() {
+ const {
+ _hideStorageWarning,
+ isVpaas,
+ selectedRecordingService,
+ t
+ } = this.props;
+
+ if (!(isVpaas && selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE) || _hideStorageWarning) {
+ return null;
+ }
+
+ return (
+
+
+
+ { t('recording.serviceDescriptionCloudInfo') }
+
+
+ );
+ }
+
+ /**
+ * Renders a spinner component.
+ *
+ * @returns {React$Component}
+ */
+ _renderSpinner() {
+ return (
+
+ );
+ }
+
+ /**
+ * Renders the screen with the account information of a logged in user.
+ *
+ * @returns {React$Component}
+ */
+ _renderSignOut() {
+ const {
+ spaceLeft,
+ t,
+ userName
+ } = this.props;
+ const duration = getRecordingDurationEstimation(spaceLeft);
+
+ return (
+
+
+
+
+ { t('recording.loggedIn', { userName }) }
+
+
+
+
+ {
+ t('recording.availableSpace', {
+ spaceLeft,
+ duration
+ })
+ }
+
+
+
+
+ );
+ }
+
+ /**
+ * Renders the content in case integrations were enabled.
+ *
+ * @protected
+ * @returns {React$Component}
+ */
+ _renderIntegrationsContent() {
+ if (!this._shouldRenderIntegrationsContent()) {
+ return null;
+ }
+
+ const {
+ _localRecordingAvailable,
+ fileRecordingsServiceEnabled,
+ isTokenValid,
+ isValidating,
+ selectedRecordingService,
+ t
+ } = this.props;
+ let content = null;
+ let switchContent = null;
+
+ if (isValidating) {
+ content = this._renderSpinner();
+ switchContent = ;
+ } else if (isTokenValid) {
+ content = this._renderSignOut();
+ switchContent = (
+
+
+
+ );
+
+ } else {
+ switchContent = (
+
+
+
+ );
+ }
+
+ if (fileRecordingsServiceEnabled || _localRecordingAvailable) {
+ switchContent = (
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+ { t('recording.authDropboxText') }
+
+ { switchContent }
+
+
+ { content }
+
+
+ );
+ }
+
+ /**
+ * Renders the content for local recordings.
+ *
+ * @protected
+ * @returns {React$Component}
+ */
+ _renderLocalRecordingContent() {
+ const {
+ _localRecordingAvailable,
+ _localRecordingNoNotification,
+ _localRecordingSelfEnabled,
+ isValidating,
+ localRecordingOnlySelf,
+ onLocalRecordingSelfChange,
+ t,
+ selectedRecordingService
+ } = this.props;
+
+ if (!_localRecordingAvailable) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+
+
+
+
+ { t('recording.saveLocalRecording') }
+
+
+
+
+ {selectedRecordingService === RECORDING_TYPES.LOCAL && (
+ <>
+ {_localRecordingSelfEnabled && (
+
+
+
+
+
+
+ {t('recording.onlyRecordSelf')}
+
+
+
+
+ )}
+
+ {t('recording.localRecordingWarning')}
+
+ {_localRecordingNoNotification && !localRecordingOnlySelf
+ &&
+ {t('recording.localRecordingNoNotificationWarning')}
+
+ }
+ >
+ )}
+ >
+
+ );
+ }
+}
+
+export default translate(connect(mapStateToProps)(StartRecordingDialogContent));