feat(recording): StartRecordingDialogContent web and native (#12009)
* feat(recording): StartRecordingDialogContent web and native
This commit is contained in:
parent
f07bd4a0d6
commit
e458eed931
|
@ -7,7 +7,7 @@ export interface ButtonProps {
|
|||
/**
|
||||
* Label used for accessibility.
|
||||
*/
|
||||
accessibilityLabel: string;
|
||||
accessibilityLabel?: string;
|
||||
|
||||
/**
|
||||
* Whether or not the button is disabled.
|
||||
|
|
|
@ -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,
|
||||
|
||||
/**
|
||||
* <tt>true</tt> if we have valid oauth token.
|
||||
*/
|
||||
isTokenValid: boolean,
|
||||
|
||||
/**
|
||||
* <tt>true</tt> 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<P extends Props> extends Component<P> {
|
||||
/**
|
||||
* 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;
|
|
@ -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,
|
||||
|
||||
/**
|
||||
* <tt>true</tt> if we have valid oauth token.
|
||||
*/
|
||||
isTokenValid: boolean,
|
||||
|
||||
/**
|
||||
* <tt>true</tt> 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<Props> {
|
||||
_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 (
|
||||
<Container
|
||||
className = 'recording-dialog'
|
||||
style = { styles.container }>
|
||||
{ this._renderNoIntegrationsContent() }
|
||||
{ this._renderFileSharingContent() }
|
||||
{ this._renderUploadToTheCloudInfo() }
|
||||
{ this._renderIntegrationsContent() }
|
||||
{ this._renderLocalRecordingContent() }
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<Container
|
||||
className = 'recording-header'
|
||||
key = 'fileSharingSetting'
|
||||
style = { styles.header }>
|
||||
<Container className = 'recording-icon-container file-sharing-icon-container'>
|
||||
<Image
|
||||
className = 'recording-file-sharing-icon'
|
||||
src = { ICON_USERS }
|
||||
style = { styles.recordingIcon } />
|
||||
</Container>
|
||||
<Text
|
||||
className = 'recording-title'
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ t('recording.fileSharingdescription') }
|
||||
</Text>
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange
|
||||
= { onSharingSettingChanged }
|
||||
style = { styles.switch }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { sharingSetting } />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<Container
|
||||
className = 'recording-info'
|
||||
key = 'cloudUploadInfo'
|
||||
style = { styles.headerInfo }>
|
||||
<Image
|
||||
className = 'recording-info-icon'
|
||||
src = { ICON_INFO }
|
||||
style = { styles.recordingInfoIcon } />
|
||||
<Text
|
||||
className = 'recording-info-title'
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.titleInfo
|
||||
}}>
|
||||
{ t('recording.serviceDescriptionCloudInfo') }
|
||||
</Text>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
? (
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this._onRecordingServiceSwitchChange }
|
||||
style = { styles.switch }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { this.props.selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE } />
|
||||
) : 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 (
|
||||
<Container
|
||||
className = { `recording-header ${jitsiRecordingHeaderClass}` }
|
||||
key = 'noIntegrationSetting'
|
||||
style = { styles.header }>
|
||||
<Container className = { contentRecordingClass }>
|
||||
<Image
|
||||
className = 'content-recording-icon'
|
||||
src = { ICON_CLOUD }
|
||||
style = { styles.recordingIcon } />
|
||||
</Container>
|
||||
<Text
|
||||
className = 'recording-title'
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ label }
|
||||
</Text>
|
||||
{ switchContent }
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = <Container className = 'recording-switch' />;
|
||||
} else if (isTokenValid) {
|
||||
content = this._renderSignOut();
|
||||
switchContent = (
|
||||
<Container className = 'recording-switch'>
|
||||
<Button
|
||||
label = { t('recording.signOut') }
|
||||
onClick = { this._onSignOut }
|
||||
onPress = { this._onSignOut }
|
||||
type = { BUTTON_TYPES.SECONDARY } />
|
||||
</Container>
|
||||
);
|
||||
|
||||
} else {
|
||||
switchContent = (
|
||||
<Container className = 'recording-switch'>
|
||||
<Button
|
||||
label = { t('recording.signIn') }
|
||||
onClick = { this._onSignIn }
|
||||
onPress = { this._onSignIn }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.fileRecordingsServiceEnabled || this._localRecordingAvailable) {
|
||||
switchContent = (
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this._onDropboxSwitchChange }
|
||||
style = { styles.switch }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { this.props.selectedRecordingService
|
||||
=== RECORDING_TYPES.DROPBOX } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Container
|
||||
className = { `recording-header ${this._shouldRenderNoIntegrationsContent()
|
||||
? 'recording-header-line' : ''}` }
|
||||
style = { styles.headerIntegrations }>
|
||||
<Container
|
||||
className = 'recording-icon-container'>
|
||||
<Image
|
||||
className = 'recording-icon'
|
||||
src = { DROPBOX_LOGO }
|
||||
style = { styles.recordingIcon } />
|
||||
</Container>
|
||||
<Text
|
||||
className = 'recording-title'
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ t('recording.authDropboxText') }
|
||||
</Text>
|
||||
{ switchContent }
|
||||
</Container>
|
||||
<Container
|
||||
className = 'authorization-panel'>
|
||||
{ content }
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
_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 (
|
||||
<LoadingIndicator
|
||||
isCompleting = { false }
|
||||
size = 'small' />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<Container>
|
||||
<Container
|
||||
className = 'logged-in-panel'
|
||||
style = { styles.loggedIn }>
|
||||
<Container>
|
||||
<Text
|
||||
style = { [
|
||||
styles.text,
|
||||
styles.recordingText
|
||||
] }>
|
||||
{ t('recording.loggedIn', { userName }) }
|
||||
</Text>
|
||||
</Container>
|
||||
<Container>
|
||||
<Text
|
||||
style = { [
|
||||
styles.text,
|
||||
styles.recordingText
|
||||
] }>
|
||||
{
|
||||
t('recording.availableSpace', {
|
||||
spaceLeft,
|
||||
duration
|
||||
})
|
||||
}
|
||||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
_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 (
|
||||
<>
|
||||
<Container>
|
||||
<Container
|
||||
className = 'recording-header recording-header-line'
|
||||
style = { styles.header }>
|
||||
<Container
|
||||
className = 'recording-icon-container'>
|
||||
<Image
|
||||
className = 'recording-icon'
|
||||
src = { LOCAL_RECORDING }
|
||||
style = { styles.recordingIcon } />
|
||||
</Container>
|
||||
<Text
|
||||
className = 'recording-title'
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ t('recording.saveLocalRecording') }
|
||||
</Text>
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this._onLocalRecordingSwitchChange }
|
||||
style = { styles.switch }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { this.props.selectedRecordingService
|
||||
=== RECORDING_TYPES.LOCAL } />
|
||||
</Container>
|
||||
</Container>
|
||||
{selectedRecordingService === RECORDING_TYPES.LOCAL && (
|
||||
<>
|
||||
{this.props._localRecordingSelfEnabled && (
|
||||
<Container>
|
||||
<Container
|
||||
className = 'recording-header space-top'
|
||||
style = { styles.header }>
|
||||
<Container className = 'recording-icon-container file-sharing-icon-container'>
|
||||
<Image
|
||||
className = 'recording-file-sharing-icon'
|
||||
src = { ICON_USERS }
|
||||
style = { styles.recordingIcon } />
|
||||
</Container>
|
||||
<Text
|
||||
className = 'recording-title'
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{t('recording.onlyRecordSelf')}
|
||||
</Text>
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this.props.onLocalRecordingSelfChange }
|
||||
style = { styles.switch }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { this.props.localRecordingOnlySelf } />
|
||||
</Container>
|
||||
</Container>
|
||||
)}
|
||||
<Text className = 'local-recording-warning text'>
|
||||
{t('recording.localRecordingWarning')}
|
||||
</Text>
|
||||
{_localRecordingNoNotification && !this.props.localRecordingOnlySelf
|
||||
&& <Text className = 'local-recording-warning notification'>
|
||||
{t('recording.localRecordingNoNotificationWarning')}
|
||||
</Text>
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
_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));
|
|
@ -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.
|
||||
|
|
|
@ -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<Props> {
|
||||
/**
|
||||
* Renders the component.
|
||||
*
|
||||
* @protected
|
||||
* @returns {React$Component}
|
||||
*/
|
||||
render() {
|
||||
const { _styles: styles } = this.props;
|
||||
|
||||
return (
|
||||
<View style = { styles.container }>
|
||||
{ this._renderNoIntegrationsContent() }
|
||||
{ this._renderFileSharingContent() }
|
||||
{ this._renderUploadToTheCloudInfo() }
|
||||
{ this._renderIntegrationsContent() }
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
? (
|
||||
<Switch
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this._onRecordingServiceSwitchChange }
|
||||
style = { styles.switch }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE } />
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<View
|
||||
key = 'noIntegrationSetting'
|
||||
style = { styles.header }>
|
||||
<Image
|
||||
source = { ICON_CLOUD }
|
||||
style = { styles.recordingIcon } />
|
||||
<Text
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ t('recording.serviceDescription') }
|
||||
</Text>
|
||||
{ switchContent }
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<View
|
||||
key = 'fileSharingSetting'
|
||||
style = { styles.header }>
|
||||
<Image
|
||||
source = { ICON_USERS }
|
||||
style = { styles.recordingIcon } />
|
||||
<Text
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ t('recording.fileSharingdescription') }
|
||||
</Text>
|
||||
<Switch
|
||||
disabled = { isValidating }
|
||||
// @ts-ignore
|
||||
onValueChange = { onSharingSettingChanged }
|
||||
style = { styles.switch }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { sharingSetting } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<View
|
||||
key = 'cloudUploadInfo'
|
||||
style = { styles.headerInfo }>
|
||||
<Image
|
||||
source = { ICON_INFO }
|
||||
style = { styles.recordingInfoIcon } />
|
||||
<Text
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.titleInfo
|
||||
}}>
|
||||
{ t('recording.serviceDescriptionCloudInfo') }
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a spinner component.
|
||||
*
|
||||
* @returns {React$Component}
|
||||
*/
|
||||
_renderSpinner() {
|
||||
return (
|
||||
<LoadingIndicator
|
||||
isCompleting = { false }
|
||||
size = 'small' />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<View
|
||||
style = { styles.loggedIn }>
|
||||
<Text
|
||||
style = { [
|
||||
styles.text,
|
||||
styles.recordingText
|
||||
] }>
|
||||
{ t('recording.loggedIn', { userName }) }
|
||||
</Text>
|
||||
<Text
|
||||
style = { [
|
||||
styles.text,
|
||||
styles.recordingText
|
||||
] }>
|
||||
{
|
||||
t('recording.availableSpace', {
|
||||
spaceLeft,
|
||||
duration
|
||||
})
|
||||
}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = <View />;
|
||||
} else if (isTokenValid) {
|
||||
content = this._renderSignOut();
|
||||
switchContent = (
|
||||
<Button
|
||||
accessibilityLabel = 'recording.signOut'
|
||||
labelKey = 'recording.signOut'
|
||||
onClick = { this._onSignOut }
|
||||
type = { BUTTON_TYPES.SECONDARY } />
|
||||
);
|
||||
|
||||
} else {
|
||||
switchContent = (
|
||||
<Button
|
||||
accessibilityLabel = 'recording.signIn'
|
||||
labelKey = 'recording.signIn'
|
||||
onClick = { this._onSignIn }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
);
|
||||
}
|
||||
|
||||
if (fileRecordingsServiceEnabled) {
|
||||
switchContent = (
|
||||
<Switch
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this._onDropboxSwitchChange }
|
||||
style = { styles.switch }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { selectedRecordingService
|
||||
=== RECORDING_TYPES.DROPBOX } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View
|
||||
style = { styles.headerIntegrations }>
|
||||
<Image
|
||||
source = { DROPBOX_LOGO }
|
||||
style = { styles.recordingIcon } />
|
||||
<Text
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ t('recording.authDropboxText') }
|
||||
</Text>
|
||||
{ switchContent }
|
||||
</View>
|
||||
<View>
|
||||
{ content }
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(connect(mapStateToProps)(StartRecordingDialogContent));
|
|
@ -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
|
||||
|
|
|
@ -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<Props> {
|
||||
/**
|
||||
* Renders the component.
|
||||
*
|
||||
* @protected
|
||||
* @returns {React$Component}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Container className = 'recording-dialog'>
|
||||
{ this._renderNoIntegrationsContent() }
|
||||
{ this._renderFileSharingContent() }
|
||||
{ this._renderUploadToTheCloudInfo() }
|
||||
{ this._renderIntegrationsContent() }
|
||||
{ this._renderLocalRecordingContent() }
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
? (
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this._onRecordingServiceSwitchChange }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { selectedRecordingService === RECORDING_TYPES.JITSI_REC_SERVICE } />
|
||||
) : 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 (
|
||||
<Container
|
||||
className = { `recording-header ${jitsiRecordingHeaderClass}` }
|
||||
key = 'noIntegrationSetting'>
|
||||
<Container className = { contentRecordingClass }>
|
||||
<Image
|
||||
className = 'content-recording-icon'
|
||||
src = { ICON_CLOUD } />
|
||||
</Container>
|
||||
<Text className = 'recording-title'>
|
||||
{ label }
|
||||
</Text>
|
||||
{ switchContent }
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<Container
|
||||
className = 'recording-header'
|
||||
key = 'fileSharingSetting'>
|
||||
<Container className = 'recording-icon-container file-sharing-icon-container'>
|
||||
<Image
|
||||
className = 'recording-file-sharing-icon'
|
||||
src = { ICON_USERS } />
|
||||
</Container>
|
||||
<Text className = 'recording-title'>
|
||||
{ t('recording.fileSharingdescription') }
|
||||
</Text>
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange = { onSharingSettingChanged }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { sharingSetting } />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<Container
|
||||
className = 'recording-info'
|
||||
key = 'cloudUploadInfo'>
|
||||
<Image
|
||||
className = 'recording-info-icon'
|
||||
src = { ICON_INFO } />
|
||||
<Text className = 'recording-info-title'>
|
||||
{ t('recording.serviceDescriptionCloudInfo') }
|
||||
</Text>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a spinner component.
|
||||
*
|
||||
* @returns {React$Component}
|
||||
*/
|
||||
_renderSpinner() {
|
||||
return (
|
||||
<LoadingIndicator
|
||||
isCompleting = { false }
|
||||
size = 'small' />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<Container>
|
||||
<Container className = 'logged-in-panel'>
|
||||
<Container>
|
||||
<Text>
|
||||
{ t('recording.loggedIn', { userName }) }
|
||||
</Text>
|
||||
</Container>
|
||||
<Container>
|
||||
<Text>
|
||||
{
|
||||
t('recording.availableSpace', {
|
||||
spaceLeft,
|
||||
duration
|
||||
})
|
||||
}
|
||||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = <Container className = 'recording-switch' />;
|
||||
} else if (isTokenValid) {
|
||||
content = this._renderSignOut();
|
||||
switchContent = (
|
||||
<Container className = 'recording-switch'>
|
||||
<Button
|
||||
accessibilityLabel = { t('recording.signOut') }
|
||||
labelKey = 'recording.signOut'
|
||||
onClick = { this._onSignOut }
|
||||
type = { BUTTON_TYPES.SECONDARY } />
|
||||
</Container>
|
||||
);
|
||||
|
||||
} else {
|
||||
switchContent = (
|
||||
<Container className = 'recording-switch'>
|
||||
<Button
|
||||
accessibilityLabel = { t('recording.signIn') }
|
||||
labelKey = 'recording.signIn'
|
||||
onClick = { this._onSignIn }
|
||||
type = { BUTTON_TYPES.PRIMARY } />
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
if (fileRecordingsServiceEnabled || _localRecordingAvailable) {
|
||||
switchContent = (
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this._onDropboxSwitchChange }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { selectedRecordingService
|
||||
=== RECORDING_TYPES.DROPBOX } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Container
|
||||
className = { `recording-header ${this._shouldRenderNoIntegrationsContent()
|
||||
? 'recording-header-line' : ''}` }>
|
||||
<Container
|
||||
className = 'recording-icon-container'>
|
||||
<Image
|
||||
className = 'recording-icon'
|
||||
src = { DROPBOX_LOGO } />
|
||||
</Container>
|
||||
<Text className = 'recording-title'>
|
||||
{ t('recording.authDropboxText') }
|
||||
</Text>
|
||||
{ switchContent }
|
||||
</Container>
|
||||
<Container className = 'authorization-panel'>
|
||||
{ content }
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (
|
||||
<>
|
||||
<Container>
|
||||
<Container
|
||||
className = 'recording-header recording-header-line'>
|
||||
<Container
|
||||
className = 'recording-icon-container'>
|
||||
<Image
|
||||
className = 'recording-icon'
|
||||
src = { LOCAL_RECORDING } />
|
||||
</Container>
|
||||
<Text className = 'recording-title'>
|
||||
{ t('recording.saveLocalRecording') }
|
||||
</Text>
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this._onLocalRecordingSwitchChange }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { selectedRecordingService
|
||||
=== RECORDING_TYPES.LOCAL } />
|
||||
</Container>
|
||||
</Container>
|
||||
{selectedRecordingService === RECORDING_TYPES.LOCAL && (
|
||||
<>
|
||||
{_localRecordingSelfEnabled && (
|
||||
<Container>
|
||||
<Container className = 'recording-header space-top'>
|
||||
<Container className = 'recording-icon-container file-sharing-icon-container'>
|
||||
<Image
|
||||
className = 'recording-file-sharing-icon'
|
||||
src = { ICON_USERS } />
|
||||
</Container>
|
||||
<Text className = 'recording-title'>
|
||||
{t('recording.onlyRecordSelf')}
|
||||
</Text>
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange = { onLocalRecordingSelfChange }
|
||||
trackColor = {{ false: TRACK_COLOR }}
|
||||
value = { localRecordingOnlySelf } />
|
||||
</Container>
|
||||
</Container>
|
||||
)}
|
||||
<Text className = 'local-recording-warning text'>
|
||||
{t('recording.localRecordingWarning')}
|
||||
</Text>
|
||||
{_localRecordingNoNotification && !localRecordingOnlySelf
|
||||
&& <Text className = 'local-recording-warning notification'>
|
||||
{t('recording.localRecordingNoNotificationWarning')}
|
||||
</Text>
|
||||
}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(connect(mapStateToProps)(StartRecordingDialogContent));
|
Loading…
Reference in New Issue