Updates recording dialog. (#3953)
* Updates recording dialog. * Update config.js doc. * Adds comment and make a check more intuitive. * Changes of using enum for recording types.
This commit is contained in:
parent
f439ad2999
commit
12d0aef686
|
@ -180,6 +180,11 @@ var config = {
|
|||
// redirectURI:
|
||||
// 'https://jitsi-meet.example.com/subfolder/static/oauth.html'
|
||||
// },
|
||||
// When integrations like dropbox are enabled only that will be shown,
|
||||
// by enabling fileRecordingsServiceEnabled, we show both the integrations
|
||||
// and the generic recording service (its configuration and storage type
|
||||
// depends on jibri configuration)
|
||||
// fileRecordingsServiceEnabled: false
|
||||
|
||||
// Whether to enable live streaming or not.
|
||||
// liveStreamingEnabled: false,
|
||||
|
|
|
@ -11,42 +11,37 @@
|
|||
flex: 0;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 32px;
|
||||
|
||||
.recording-title {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.recording-icon-container {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.recording-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.recording-switch {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.authorization-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 10px;
|
||||
margin: 0 40px 10px 40px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
.dropbox-sign-in {
|
||||
align-items: center;
|
||||
border: 1px solid #4285f4;
|
||||
background-color: white;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
padding: 10px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
margin: 10px 0px;
|
||||
color: #4285f4;
|
||||
|
||||
.dropbox-logo {
|
||||
background-color: white;
|
||||
border-radius: 2px;
|
||||
display: inline-block;
|
||||
padding-right: 5px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.logged-in-panel {
|
||||
padding: 10px;
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
|
@ -518,9 +518,10 @@
|
|||
"on": "Recording",
|
||||
"pending": "Preparing to record the meeting...",
|
||||
"rec": "REC",
|
||||
"serviceDescription": "Your recording will be saved by the recording service",
|
||||
"serviceName": "Recording service",
|
||||
"signIn": "sign in",
|
||||
"signOut": "Sign Out",
|
||||
"signIn": "Sign in",
|
||||
"signOut": "Sign out",
|
||||
"startRecordingBody": "Are you sure you would like to start recording?",
|
||||
"unavailable": "Oops! The __serviceName__ is currently unavailable. We're working on resolving the issue. Please try again later.",
|
||||
"unavailableTitle": "Recording unavailable"
|
||||
|
|
|
@ -338,18 +338,40 @@ export function createProfilePanelButtonEvent(buttonName, attributes = {}) {
|
|||
* @param {string} dialogName - The name of the dialog (e.g. 'start' or 'stop').
|
||||
* @param {string} buttonName - The name of the button (e.g. 'confirm' or
|
||||
* 'cancel').
|
||||
* @param {Object} attributes - Attributes to attach to the event.
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createRecordingDialogEvent(dialogName, buttonName) {
|
||||
export function createRecordingDialogEvent(
|
||||
dialogName, buttonName, attributes = {}) {
|
||||
return {
|
||||
action: 'clicked',
|
||||
actionSubject: buttonName,
|
||||
attributes,
|
||||
source: `${dialogName}.recording.dialog`,
|
||||
type: TYPE_UI
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that a specific button on one of the
|
||||
* liveStreaming-related dialogs was clicked.
|
||||
*
|
||||
* @param {string} dialogName - The name of the dialog (e.g. 'start' or 'stop').
|
||||
* @param {string} buttonName - The name of the button (e.g. 'confirm' or
|
||||
* 'cancel').
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createLiveStreamingDialogEvent(dialogName, buttonName) {
|
||||
return {
|
||||
action: 'clicked',
|
||||
actionSubject: buttonName,
|
||||
source: `${dialogName}.liveStreaming.dialog`,
|
||||
type: TYPE_UI
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that an action related to recording has
|
||||
* occured.
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text, TouchableOpacity } from 'react-native';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* React Elements to display within the component.
|
||||
*/
|
||||
children: React$Node | Object,
|
||||
|
||||
/**
|
||||
* Handler called when the user presses the button.
|
||||
*/
|
||||
onValueChange: Function,
|
||||
|
||||
/**
|
||||
* The component's external style
|
||||
*/
|
||||
style: Object
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a button.
|
||||
*/
|
||||
export default class ButtonImpl extends Component<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}, renders the button.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<TouchableOpacity
|
||||
onPress = { this.props.onValueChange } >
|
||||
<Text style = { this.props.style }>
|
||||
{ this.props.children }
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Image } from 'react-native';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link Image}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The URL to be rendered as image.
|
||||
*/
|
||||
src: string,
|
||||
|
||||
/**
|
||||
* The component's external style
|
||||
*/
|
||||
style: Object
|
||||
};
|
||||
|
||||
/**
|
||||
* A component rendering aN IMAGE.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export default class ImageImpl extends Component<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<Image
|
||||
source = { this.props.src }
|
||||
style = { this.props.style } />
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
export { default as AvatarListItem } from './AvatarListItem';
|
||||
export { default as BackButton } from './BackButton';
|
||||
export { default as Button } from './Button';
|
||||
export { default as Container } from './Container';
|
||||
export { default as ForwardButton } from './ForwardButton';
|
||||
export { default as Header } from './Header';
|
||||
export { default as HeaderLabel } from './HeaderLabel';
|
||||
export { default as Image } from './Image';
|
||||
export { default as Link } from './Link';
|
||||
export { default as LoadingIndicator } from './LoadingIndicator';
|
||||
export { default as Modal } from './Modal';
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/* @flow */
|
||||
|
||||
import Button from '@atlaskit/button';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* React Elements to display within the component.
|
||||
*/
|
||||
children: React$Node | Object,
|
||||
|
||||
/**
|
||||
* Handler called when the user presses the button.
|
||||
*/
|
||||
onValueChange: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a button.
|
||||
*/
|
||||
export default class ButtonImpl extends Component<Props> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { onValueChange } = this.props;
|
||||
|
||||
return (
|
||||
<Button
|
||||
appearance = 'primary'
|
||||
onClick = { onValueChange }
|
||||
type = 'button'>
|
||||
{ this.props.children }
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import React, { Component } from 'react';
|
||||
|
||||
/**
|
||||
* Implements a React/Web {@link Component} for displaying image
|
||||
* in order to facilitate cross-platform source code.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export default class Image extends Component {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return React.createElement('img', this.props);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
export { default as Button } from './Button';
|
||||
export { default as Container } from './Container';
|
||||
export { default as Image } from './Image';
|
||||
export { default as LoadingIndicator } from './LoadingIndicator';
|
||||
export { default as MeetingsList } from './MeetingsList';
|
||||
export { default as MultiSelectAutocomplete } from './MultiSelectAutocomplete';
|
||||
|
|
|
@ -44,8 +44,11 @@ export function getSpaceUsage(token: string) {
|
|||
* Returns <tt>true</tt> if the dropbox features is enabled and <tt>false</tt>
|
||||
* otherwise.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isEnabled() {
|
||||
return Dropbox.ENABLED;
|
||||
export function isEnabled(state: Object) {
|
||||
const { dropbox = {} } = state['features/base/config'];
|
||||
|
||||
return Dropbox.ENABLED && typeof dropbox.appKey === 'string';
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import { Component } from 'react';
|
||||
|
||||
import {
|
||||
createRecordingDialogEvent,
|
||||
createLiveStreamingDialogEvent,
|
||||
sendAnalytics
|
||||
} from '../../../analytics';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
|
@ -149,7 +149,7 @@ export default class AbstractStartLiveStreamDialog<P: Props>
|
|||
* @returns {boolean} True is returned to close the modal.
|
||||
*/
|
||||
_onCancel() {
|
||||
sendAnalytics(createRecordingDialogEvent('start', 'cancel.button'));
|
||||
sendAnalytics(createLiveStreamingDialogEvent('start', 'cancel.button'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ export default class AbstractStartLiveStreamDialog<P: Props>
|
|||
}
|
||||
|
||||
sendAnalytics(
|
||||
createRecordingDialogEvent('start', 'confirm.button'));
|
||||
createLiveStreamingDialogEvent('start', 'confirm.button'));
|
||||
|
||||
this.props._conference.startRecording({
|
||||
broadcastId: selectedBroadcastID,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import { Component } from 'react';
|
||||
|
||||
import {
|
||||
createRecordingDialogEvent,
|
||||
createLiveStreamingDialogEvent,
|
||||
sendAnalytics
|
||||
} from '../../../analytics';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
|
@ -61,7 +61,7 @@ export default class AbstractStopLiveStreamDialog extends Component<Props> {
|
|||
* @returns {boolean} True to close the modal.
|
||||
*/
|
||||
_onSubmit() {
|
||||
sendAnalytics(createRecordingDialogEvent('stop', 'confirm.button'));
|
||||
sendAnalytics(createLiveStreamingDialogEvent('stop', 'confirm.button'));
|
||||
|
||||
const { _session } = this.props;
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
getDropboxData,
|
||||
isEnabled as isDropboxEnabled
|
||||
} from '../../../dropbox';
|
||||
import { RECORDING_TYPES } from '../../constants';
|
||||
|
||||
type Props = {
|
||||
|
||||
|
@ -24,6 +25,12 @@ type Props = {
|
|||
*/
|
||||
_appKey: string,
|
||||
|
||||
/**
|
||||
* Whether to show file recordings service, even if integrations
|
||||
* are enabled.
|
||||
*/
|
||||
_fileRecordingsServiceEnabled: boolean,
|
||||
|
||||
/**
|
||||
* If true the dropbox integration is enabled, otherwise - disabled.
|
||||
*/
|
||||
|
@ -165,23 +172,28 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
|
|||
* @returns {boolean} - True (to note that the modal should be closed).
|
||||
*/
|
||||
_onSubmit() {
|
||||
sendAnalytics(
|
||||
createRecordingDialogEvent('start', 'confirm.button')
|
||||
);
|
||||
const { _conference, _isDropboxEnabled, _token } = this.props;
|
||||
let appData;
|
||||
const attributes = {};
|
||||
|
||||
if (_isDropboxEnabled) {
|
||||
if (_isDropboxEnabled && _token) {
|
||||
appData = JSON.stringify({
|
||||
'file_recording_metadata': {
|
||||
'upload_credentials': {
|
||||
'service_name': 'dropbox',
|
||||
'service_name': RECORDING_TYPES.DROPBOX,
|
||||
'token': _token
|
||||
}
|
||||
}
|
||||
});
|
||||
attributes.type = RECORDING_TYPES.DROPBOX;
|
||||
} else {
|
||||
attributes.type = RECORDING_TYPES.JITSI_REC_SERVICE;
|
||||
}
|
||||
|
||||
sendAnalytics(
|
||||
createRecordingDialogEvent('start', 'confirm.button', attributes)
|
||||
);
|
||||
|
||||
_conference.startRecording({
|
||||
mode: JitsiRecordingConstants.mode.FILE,
|
||||
appData
|
||||
|
@ -212,11 +224,15 @@ class AbstractStartRecordingDialog extends Component<Props, State> {
|
|||
* }}
|
||||
*/
|
||||
export function mapStateToProps(state: Object) {
|
||||
const { dropbox = {} } = state['features/base/config'];
|
||||
const {
|
||||
fileRecordingsServiceEnabled = false,
|
||||
dropbox = {}
|
||||
} = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_appKey: dropbox.appKey,
|
||||
_conference: state['features/base/conference'].conference,
|
||||
_fileRecordingsServiceEnabled: fileRecordingsServiceEnabled,
|
||||
_isDropboxEnabled: isDropboxEnabled(state),
|
||||
_token: state['features/dropbox'].token
|
||||
};
|
||||
|
|
|
@ -8,20 +8,27 @@ import {
|
|||
sendAnalytics
|
||||
} from '../../../analytics';
|
||||
import {
|
||||
DialogContent,
|
||||
_abstractMapStateToProps
|
||||
} from '../../../base/dialog';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import {
|
||||
Button,
|
||||
Container,
|
||||
Image,
|
||||
LoadingIndicator,
|
||||
Switch,
|
||||
Text
|
||||
} from '../../../base/react';
|
||||
import { StyleType } from '../../../base/styles';
|
||||
import { ColorPalette, StyleType } from '../../../base/styles';
|
||||
import { authorizeDropbox, updateDropboxToken } from '../../../dropbox';
|
||||
|
||||
import styles from './styles';
|
||||
import {
|
||||
default as styles,
|
||||
DROPBOX_LOGO,
|
||||
JITSI_LOGO
|
||||
} from './styles';
|
||||
|
||||
import { RECORDING_TYPES } from '../../constants';
|
||||
import { getRecordingDurationEstimation } from '../../functions';
|
||||
|
||||
type Props = {
|
||||
|
@ -36,6 +43,12 @@ type Props = {
|
|||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* Whether to show file recordings service, even if integrations
|
||||
* are enabled.
|
||||
*/
|
||||
fileRecordingsServiceEnabled: boolean,
|
||||
|
||||
/**
|
||||
* If true the content related to the integrations will be shown.
|
||||
*/
|
||||
|
@ -67,12 +80,24 @@ type Props = {
|
|||
userName: ?string,
|
||||
};
|
||||
|
||||
/**
|
||||
* State of the component.
|
||||
*/
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* The currently selected recording service of type: RECORDING_TYPES.
|
||||
*/
|
||||
selectedRecordingService: string
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* React Component for getting confirmation to start a file recording session.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class StartRecordingDialogContent extends Component<Props> {
|
||||
class StartRecordingDialogContent extends Component<Props, State> {
|
||||
/**
|
||||
* Initializes a new {@code StartRecordingDialogContent} instance.
|
||||
*
|
||||
|
@ -82,9 +107,18 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
super(props);
|
||||
|
||||
// Bind event handler so it is only bound once for every instance.
|
||||
this._signIn = this._signIn.bind(this);
|
||||
this._signOut = this._signOut.bind(this);
|
||||
this._onSwitchChange = this._onSwitchChange.bind(this);
|
||||
this._onSignIn = this._onSignIn.bind(this);
|
||||
this._onSignOut = this._onSignOut.bind(this);
|
||||
this._onDropboxSwitchChange
|
||||
= this._onDropboxSwitchChange.bind(this);
|
||||
this._onRecordingServiceSwitchChange
|
||||
= this._onRecordingServiceSwitchChange.bind(this);
|
||||
|
||||
// the initial state is jitsi rec service is always selected
|
||||
// if only one type of recording is enabled this state will be ignored
|
||||
this.state = {
|
||||
selectedRecordingService: RECORDING_TYPES.JITSI_REC_SERVICE
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,11 +128,14 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
* @returns {React$Component}
|
||||
*/
|
||||
render() {
|
||||
if (this.props.integrationsEnabled === true) { // explicit true needed
|
||||
return this._renderIntegrationsContent();
|
||||
}
|
||||
|
||||
return this._renderNoIntegrationsContent();
|
||||
return (
|
||||
<Container
|
||||
className = 'recording-dialog'
|
||||
style = { styles.container }>
|
||||
{ this._renderNoIntegrationsContent() }
|
||||
{ this._renderIntegrationsContent() }
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,10 +144,51 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
* @returns {React$Component}
|
||||
*/
|
||||
_renderNoIntegrationsContent() {
|
||||
|
||||
// show the non integrations part only if fileRecordingsServiceEnabled
|
||||
// is enabled or when there are no integrations enabled
|
||||
if (!(this.props.fileRecordingsServiceEnabled
|
||||
|| !this.props.integrationsEnabled)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { _dialogStyles, isValidating, t } = this.props;
|
||||
|
||||
const switchContent
|
||||
= this.props.integrationsEnabled
|
||||
? (
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange
|
||||
= { this._onRecordingServiceSwitchChange }
|
||||
style = { styles.switch }
|
||||
trackColor = {{ false: ColorPalette.lightGrey }}
|
||||
value = {
|
||||
this.state.selectedRecordingService
|
||||
=== RECORDING_TYPES.JITSI_REC_SERVICE } />
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<DialogContent style = { this.props._dialogStyles.text }>
|
||||
{ this.props.t('recording.startRecordingBody') }
|
||||
</DialogContent>
|
||||
<Container
|
||||
className = 'recording-header'
|
||||
style = { styles.header }>
|
||||
<Container className = 'recording-icon-container'>
|
||||
<Image
|
||||
className = 'recording-icon'
|
||||
src = { JITSI_LOGO }
|
||||
style = { styles.recordingIcon } />
|
||||
</Container>
|
||||
<Text
|
||||
className = 'recording-title'
|
||||
style = {{
|
||||
..._dialogStyles.text,
|
||||
...styles.title
|
||||
}}>
|
||||
{ t('recording.serviceDescription') }
|
||||
</Text>
|
||||
{ switchContent }
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -121,27 +199,67 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
* @returns {React$Component}
|
||||
*/
|
||||
_renderIntegrationsContent() {
|
||||
if (!this.props.integrationsEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { _dialogStyles, 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
|
||||
onValueChange = { this._onSignOut }
|
||||
style = { styles.signButton }>
|
||||
{ t('recording.signOut') }
|
||||
</Button>
|
||||
</Container>
|
||||
);
|
||||
|
||||
} else {
|
||||
switchContent = (
|
||||
<Container className = 'recording-switch'>
|
||||
<Button
|
||||
onValueChange = { this._onSignIn }
|
||||
style = { styles.signButton }>
|
||||
{ t('recording.signIn') }
|
||||
</Button>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
// else { // Sign in screen:
|
||||
// We don't need to render any additional information.
|
||||
// }
|
||||
if (this.props.fileRecordingsServiceEnabled) {
|
||||
switchContent = (
|
||||
<Switch
|
||||
className = 'recording-switch'
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this._onDropboxSwitchChange }
|
||||
style = { styles.switch }
|
||||
trackColor = {{ false: ColorPalette.lightGrey }}
|
||||
value = { this.state.selectedRecordingService
|
||||
=== RECORDING_TYPES.DROPBOX } />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Container
|
||||
className = 'recording-dialog'
|
||||
style = { styles.container }>
|
||||
<Container>
|
||||
<Container
|
||||
className = 'recording-header'
|
||||
style = { styles.header }>
|
||||
<Container
|
||||
className = 'recording-icon-container'>
|
||||
<Image
|
||||
className = 'recording-icon'
|
||||
src = { DROPBOX_LOGO }
|
||||
style = { styles.recordingIcon } />
|
||||
</Container>
|
||||
<Text
|
||||
className = 'recording-title'
|
||||
style = {{
|
||||
|
@ -150,11 +268,7 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
}}>
|
||||
{ t('recording.authDropboxText') }
|
||||
</Text>
|
||||
<Switch
|
||||
disabled = { isValidating }
|
||||
onValueChange = { this._onSwitchChange }
|
||||
style = { styles.switch }
|
||||
value = { isTokenValid } />
|
||||
{ switchContent }
|
||||
</Container>
|
||||
<Container
|
||||
className = 'authorization-panel'>
|
||||
|
@ -164,18 +278,49 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
);
|
||||
}
|
||||
|
||||
_onSwitchChange: boolean => void;
|
||||
_onDropboxSwitchChange: boolean => void;
|
||||
_onRecordingServiceSwitchChange: boolean => void;
|
||||
|
||||
/**
|
||||
* Handler for onValueChange events from the Switch component.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onSwitchChange() {
|
||||
_onRecordingServiceSwitchChange() {
|
||||
|
||||
// act like group, cannot toggle off
|
||||
if (this.state.selectedRecordingService
|
||||
=== RECORDING_TYPES.JITSI_REC_SERVICE) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedRecordingService: RECORDING_TYPES.JITSI_REC_SERVICE
|
||||
});
|
||||
|
||||
if (this.props.isTokenValid) {
|
||||
this._signOut();
|
||||
} else {
|
||||
this._signIn();
|
||||
this._onSignOut();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for onValueChange events from the Switch component.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_onDropboxSwitchChange() {
|
||||
// act like group, cannot toggle off
|
||||
if (this.state.selectedRecordingService
|
||||
=== RECORDING_TYPES.DROPBOX) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selectedRecordingService: RECORDING_TYPES.DROPBOX
|
||||
});
|
||||
|
||||
if (!this.props.isTokenValid) {
|
||||
this._onSignIn();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,37 +367,32 @@ class StartRecordingDialogContent extends Component<Props> {
|
|||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
<Container style = { styles.startRecordingText }>
|
||||
<Text style = { styles.text }>
|
||||
{ t('recording.startRecordingBody') }
|
||||
</Text>
|
||||
</Container>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
_signIn: () => {};
|
||||
_onSignIn: () => {};
|
||||
|
||||
/**
|
||||
* Sings in a user.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_signIn() {
|
||||
_onSignIn() {
|
||||
sendAnalytics(
|
||||
createRecordingDialogEvent('start', 'signIn.button')
|
||||
);
|
||||
this.props.dispatch(authorizeDropbox());
|
||||
}
|
||||
|
||||
_signOut: () => {};
|
||||
_onSignOut: () => {};
|
||||
|
||||
/**
|
||||
* Sings out an user from dropbox.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_signOut() {
|
||||
_onSignOut() {
|
||||
sendAnalytics(
|
||||
createRecordingDialogEvent('start', 'signOut.button')
|
||||
);
|
||||
|
|
|
@ -25,13 +25,23 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
|||
*/
|
||||
render() {
|
||||
const { isTokenValid, isValidating, spaceLeft, userName } = this.state;
|
||||
const { _isDropboxEnabled } = this.props;
|
||||
const { _fileRecordingsServiceEnabled, _isDropboxEnabled } = this.props;
|
||||
|
||||
// disable ok button id recording service is shown only, when
|
||||
// validating dropbox token, if that is not enabled we either always
|
||||
// show the ok button or if just dropbox is enabled ok is available
|
||||
// when there is token
|
||||
const isOkDisabled
|
||||
= _fileRecordingsServiceEnabled ? isValidating
|
||||
: _isDropboxEnabled ? !isTokenValid : false;
|
||||
|
||||
return (
|
||||
<CustomSubmitDialog
|
||||
okDisabled = { _isDropboxEnabled && !isTokenValid }
|
||||
okDisabled = { isOkDisabled }
|
||||
onSubmit = { this._onSubmit } >
|
||||
<StartRecordingDialogContent
|
||||
fileRecordingsServiceEnabled
|
||||
= { _fileRecordingsServiceEnabled }
|
||||
integrationsEnabled = { _isDropboxEnabled }
|
||||
isTokenValid = { isTokenValid }
|
||||
isValidating = { isValidating }
|
||||
|
|
|
@ -6,6 +6,12 @@ import { BoxModel, createStyleSheet, ColorPalette } from '../../../base/styles';
|
|||
// the special case(s) of the recording feature bellow.
|
||||
const _PADDING = BoxModel.padding * 1.5;
|
||||
|
||||
export const DROPBOX_LOGO
|
||||
= require('../../../../../images/dropboxLogo_square.png');
|
||||
|
||||
export const JITSI_LOGO
|
||||
= require('../../../../../images/jitsiLogo_square.png');
|
||||
|
||||
/**
|
||||
* The styles of the React {@code Components} of the feature recording.
|
||||
*/
|
||||
|
@ -28,18 +34,29 @@ export default createStyleSheet({
|
|||
paddingBottom: _PADDING
|
||||
},
|
||||
|
||||
startRecordingText: {
|
||||
paddingBottom: _PADDING
|
||||
recordingIcon: {
|
||||
width: 24,
|
||||
height: 24
|
||||
},
|
||||
|
||||
signButton: {
|
||||
backgroundColor: ColorPalette.blue,
|
||||
color: ColorPalette.white,
|
||||
fontSize: 16,
|
||||
borderRadius: 5,
|
||||
padding: BoxModel.padding * 0.5
|
||||
},
|
||||
|
||||
switch: {
|
||||
color: ColorPalette.white,
|
||||
paddingRight: BoxModel.padding
|
||||
color: ColorPalette.white
|
||||
},
|
||||
|
||||
title: {
|
||||
flex: 1,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold'
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'left',
|
||||
paddingLeft: BoxModel.padding
|
||||
},
|
||||
|
||||
text: {
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
// XXX CSS is used on Web, JavaScript styles are use only for mobile. Export an
|
||||
// (empty) object so that styles[*] statements on Web don't trigger errors.
|
||||
export default {};
|
||||
|
||||
export const DROPBOX_LOGO = 'images/dropboxLogo_square.png';
|
||||
|
||||
export const JITSI_LOGO = 'images/jitsiLogo_square.png';
|
||||
|
|
|
@ -25,16 +25,26 @@ class StartRecordingDialog extends AbstractStartRecordingDialog {
|
|||
*/
|
||||
render() {
|
||||
const { isTokenValid, isValidating, spaceLeft, userName } = this.state;
|
||||
const { _isDropboxEnabled } = this.props;
|
||||
const { _fileRecordingsServiceEnabled, _isDropboxEnabled } = this.props;
|
||||
|
||||
// disable ok button id recording service is shown only, when
|
||||
// validating dropbox token, if that is not enabled we either always
|
||||
// show the ok button or if just dropbox is enabled ok is available
|
||||
// when there is token
|
||||
const isOkDisabled
|
||||
= _fileRecordingsServiceEnabled ? isValidating
|
||||
: _isDropboxEnabled ? !isTokenValid : false;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
okDisabled = { !isTokenValid && _isDropboxEnabled }
|
||||
okKey = 'dialog.confirm'
|
||||
okDisabled = { isOkDisabled }
|
||||
okKey = 'dialog.startRecording'
|
||||
onSubmit = { this._onSubmit }
|
||||
titleKey = 'dialog.recording'
|
||||
titleKey = 'dialog.startRecording'
|
||||
width = 'small'>
|
||||
<StartRecordingDialogContent
|
||||
fileRecordingsServiceEnabled
|
||||
= { _fileRecordingsServiceEnabled }
|
||||
integrationsEnabled = { _isDropboxEnabled }
|
||||
isTokenValid = { isTokenValid }
|
||||
isValidating = { isValidating }
|
||||
|
|
|
@ -19,14 +19,13 @@ export const RECORDING_OFF_SOUND_ID = 'RECORDING_OFF_SOUND';
|
|||
export const RECORDING_ON_SOUND_ID = 'RECORDING_ON_SOUND';
|
||||
|
||||
/**
|
||||
* Expected supported recording types. JIBRI is known to support live streaming
|
||||
* whereas JIRECON is for recording.
|
||||
* Expected supported recording types.
|
||||
*
|
||||
* @type {Object}
|
||||
* @enum {string}
|
||||
*/
|
||||
export const RECORDING_TYPES = {
|
||||
JIBRI: 'jibri',
|
||||
JIRECON: 'jirecon'
|
||||
JITSI_REC_SERVICE: 'recording-service',
|
||||
DROPBOX: 'dropbox'
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue