bigbluebutton-Github/bigbluebutton-html5/imports/ui/components/video-preview/component.jsx
Anton Georgiev ec2349e851
Merge pull request #6341 from BobakOftadeh/fix-modal-structure
Added option to use modal component without header border
2019-01-09 13:53:50 -05:00

275 lines
7.7 KiB
JavaScript

import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { defineMessages, injectIntl, intlShape } from 'react-intl';
import Button from '/imports/ui/components/button/component';
import { notify } from '/imports/ui/services/notification';
import logger from '/imports/startup/client/logger';
import Modal from '/imports/ui/components/modal/simple/component';
import { styles } from './styles';
const VIDEO_CONSTRAINTS = Meteor.settings.public.kurento.cameraConstraints;
const propTypes = {
intl: intlShape.isRequired,
closeModal: PropTypes.func.isRequired,
startSharing: PropTypes.func.isRequired,
changeWebcam: PropTypes.func.isRequired,
};
const intlMessages = defineMessages({
webcamSettingsTitle: {
id: 'app.videoPreview.webcamSettingsTitle',
description: 'Title for the video preview modal',
},
closeLabel: {
id: 'app.videoPreview.closeLabel',
description: 'Close button label',
},
webcamPreviewLabel: {
id: 'app.videoPreview.webcamPreviewLabel',
description: 'Webcam preview label',
},
cameraLabel: {
id: 'app.videoPreview.cameraLabel',
description: 'Camera dropdown label',
},
cancelLabel: {
id: 'app.videoPreview.cancelLabel',
description: 'Cancel button label',
},
startSharingLabel: {
id: 'app.videoPreview.startSharingLabel',
description: 'Start Sharing button label',
},
webcamOptionLabel: {
id: 'app.videoPreview.webcamOptionLabel',
description: 'Default webcam option label',
},
webcamNotFoundLabel: {
id: 'app.videoPreview.webcamNotFoundLabel',
description: 'Webcam not found label',
},
permissionError: {
id: 'app.video.permissionError',
description: 'Error message for webcam permission',
},
NotFoundError: {
id: 'app.video.notFoundError',
description: 'error message when can not get webcam video',
},
NotAllowedError: {
id: 'app.video.notAllowed',
description: 'error message when webcam had permission denied',
},
NotSupportedError: {
id: 'app.video.notSupportedError',
description: 'error message when origin do not have ssl valid',
},
NotReadableError: {
id: 'app.video.notReadableError',
description: 'error message When the webcam is being used by other software',
},
});
class VideoPreview extends Component {
constructor(props) {
super(props);
const {
closeModal,
startSharing,
changeWebcam,
webcamDeviceId,
} = props;
this.handleJoinVideo = this.handleJoinVideo.bind(this);
this.handleProceed = this.handleProceed.bind(this);
this.handleStartSharing = this.handleStartSharing.bind(this);
this.closeModal = closeModal;
this.startSharing = startSharing;
this.changeWebcam = changeWebcam;
this.webcamDeviceId = webcamDeviceId;
this.deviceStream = null;
this.state = {
webcamDeviceId: this.webcamDeviceId,
availableWebcams: null,
isStartSharingDisabled: false,
};
}
stopTracks() {
if (this.deviceStream) {
this.deviceStream.getTracks().forEach((track) => {
track.stop();
});
}
}
handlegUMError(error) {
const {
intl,
} = this.props;
const errorMessage = intlMessages[error.name]
|| intlMessages.permissionError;
notify(intl.formatMessage(errorMessage), 'error', 'video');
logger.error(error);
}
handleSelectWebcam(event) {
const {
intl,
} = this.props;
const webcamValue = event.target.value;
this.setState({ webcamDeviceId: webcamValue });
this.changeWebcam(webcamValue);
VIDEO_CONSTRAINTS.deviceId = webcamValue ? { exact: webcamValue } : undefined;
const constraints = {
video: VIDEO_CONSTRAINTS,
};
this.stopTracks();
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
this.video.srcObject = stream;
this.deviceStream = stream;
}).catch((error) => {
this.handlegUMError(error);
});
}
handleStartSharing() {
const { resolve } = this.props;
this.stopTracks();
this.startSharing();
if (resolve) resolve();
}
handleProceed() {
const { resolve } = this.props;
this.closeModal();
if (resolve) resolve();
}
componentDidMount() {
const { webcamDeviceId } = this.props;
const constraints = {
video: VIDEO_CONSTRAINTS,
};
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
this.video.srcObject = stream;
this.deviceStream = stream;
navigator.mediaDevices.enumerateDevices().then((devices) => {
let isInitialDeviceSet = false;
const webcams = [];
if (webcamDeviceId) {
this.changeWebcam(webcamDeviceId);
this.setState({ webcamDeviceId });
isInitialDeviceSet = true;
}
devices.forEach((device) => {
if (device.kind === 'videoinput') {
webcams.push(device);
if (!isInitialDeviceSet) {
this.changeWebcam(device.deviceId);
this.setState({ webcamDeviceId: device.deviceId });
isInitialDeviceSet = true;
}
}
});
if (webcams.length > 0) {
this.setState({ availableWebcams: webcams });
}
});
}).catch((error) => {
this.setState({ isStartSharingDisabled: true });
this.handlegUMError(error);
});
}
handleJoinVideo() {
const {
joinVideo,
} = this.props;
joinVideo();
}
render() {
const {
intl,
} = this.props;
return (
<Modal
overlayClassName={styles.overlay}
className={styles.modal}
onRequestClose={this.handleProceed}
hideBorder
>
<div className={styles.title}>
{intl.formatMessage(intlMessages.webcamSettingsTitle)}
</div>
<div className={styles.content}>
<div className={styles.col}>
<video
id="preview"
className={styles.preview}
ref={(ref) => { this.video = ref; }}
autoPlay
playsInline
/>
</div>
<div className={styles.options}>
<label className={styles.label}>
{intl.formatMessage(intlMessages.cameraLabel)}
</label>
{this.state.availableWebcams && this.state.availableWebcams.length > 0 ? (
<select
value={this.state.webcamDeviceId}
className={styles.select}
onChange={this.handleSelectWebcam.bind(this)}
>
<option disabled>
{intl.formatMessage(intlMessages.webcamOptionLabel)}
</option>
{this.state.availableWebcams.map((webcam, index) => (
<option key={index} value={webcam.deviceId}>
{webcam.label}
</option>
))}
</select>
) :
<select
className={styles.select}
>
<option disabled>
{intl.formatMessage(intlMessages.webcamNotFoundLabel)}
</option>
</select>}
</div>
</div>
<div className={styles.footer}>
<div className={styles.actions}>
<Button
label={intl.formatMessage(intlMessages.cancelLabel)}
onClick={this.handleProceed}
/>
<Button
color="primary"
label={intl.formatMessage(intlMessages.startSharingLabel)}
onClick={() => this.handleStartSharing()}
disabled={this.state.isStartSharingDisabled}
/>
</div>
</div>
</Modal>
);
}
}
VideoPreview.propTypes = propTypes;
export default injectIntl(VideoPreview);