123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- // @flow
-
- import Spinner from '@atlaskit/spinner';
- import React, { PureComponent } from 'react';
-
- import { hideDialog } from '../../base/dialog';
- import { translate } from '../../base/i18n';
- import { VIDEO_TYPE } from '../../base/media';
- import Video from '../../base/media/components/Video';
- import { connect, equals } from '../../base/redux';
- import { getCurrentCameraDeviceId } from '../../base/settings';
- import { createLocalTracksF } from '../../base/tracks/functions';
- import { showWarningNotification } from '../../notifications/actions';
- import { toggleBackgroundEffect } from '../actions';
- import { VIRTUAL_BACKGROUND_TYPE } from '../constants';
- import { localTrackStopped } from '../functions';
- import logger from '../logger';
-
- const videoClassName = 'video-preview-video';
-
- /**
- * The type of the React {@code PureComponent} props of {@link VirtualBackgroundPreview}.
- */
- export type Props = {
-
- /**
- * The deviceId of the camera device currently being used.
- */
- _currentCameraDeviceId: string,
-
- /**
- * The redux {@code dispatch} function.
- */
- dispatch: Function,
-
- /**
- * Dialog callback that indicates if the background preview was loaded.
- */
- loadedPreview: Function,
-
- /**
- * Represents the virtual background setted options.
- */
- options: Object,
-
- /**
- * Invoked to obtain translated strings.
- */
- t: Function
- };
-
- /**
- * The type of the React {@code Component} state of {@link VirtualBackgroundPreview}.
- */
- type State = {
-
- /**
- * Loader activated on setting virtual background.
- */
- loading: boolean,
-
- /**
- * Flag that indicates if the local track was loaded.
- */
- localTrackLoaded: boolean,
-
- /**
- * Activate the selected device camera only.
- */
- jitsiTrack: Object
- };
-
- /**
- * Implements a React {@link PureComponent} which displays the virtual
- * background preview.
- *
- * @extends PureComponent
- */
- class VirtualBackgroundPreview extends PureComponent<Props, State> {
- _componentWasUnmounted: boolean;
-
- /**
- * Initializes a new {@code VirtualBackgroundPreview} instance.
- *
- * @param {Object} props - The read-only properties with which the new
- * instance is to be initialized.
- */
- constructor(props) {
- super(props);
-
- this.state = {
- loading: false,
- localTrackLoaded: false,
- jitsiTrack: null
- };
- }
-
- /**
- * Destroys the jitsiTrack object.
- *
- * @param {Object} jitsiTrack - The track that needs to be disposed.
- * @returns {Promise<void>}
- */
- _stopStream(jitsiTrack) {
- if (jitsiTrack) {
- jitsiTrack.dispose();
- }
- }
-
- /**
- * Creates and updates the track data.
- *
- * @returns {void}
- */
- async _setTracks() {
- try {
- this.setState({ loading: true });
- const [ jitsiTrack ] = await createLocalTracksF({
- cameraDeviceId: this.props._currentCameraDeviceId,
- devices: [ 'video' ]
- });
-
- this.setState({ localTrackLoaded: true });
-
- // In case the component gets unmounted before the tracks are created
- // avoid a leak by not setting the state
- if (this._componentWasUnmounted) {
- this._stopStream(jitsiTrack);
-
- return;
- }
- this.setState({
- jitsiTrack,
- loading: false
- });
- this.props.loadedPreview(true);
- } catch (error) {
- this.props.dispatch(hideDialog());
- this.props.dispatch(
- showWarningNotification({
- titleKey: 'virtualBackground.backgroundEffectError',
- description: 'Failed to access camera device.'
- })
- );
- logger.error('Failed to access camera device. Error on apply background effect.');
-
- return;
- }
-
- if (this.props.options.backgroundType === VIRTUAL_BACKGROUND_TYPE.DESKTOP_SHARE
- && this.state.localTrackLoaded) {
- this._applyBackgroundEffect();
- }
- }
-
- /**
- * Apply background effect on video preview.
- *
- * @returns {Promise}
- */
- async _applyBackgroundEffect() {
- this.setState({ loading: true });
- this.props.loadedPreview(false);
- await this.props.dispatch(toggleBackgroundEffect(this.props.options, this.state.jitsiTrack));
- this.props.loadedPreview(true);
- this.setState({ loading: false });
- }
-
- /**
- * Apply video preview loader.
- *
- * @returns {Promise}
- */
- _loadVideoPreview() {
- return (
- <div className = 'video-preview-loader'>
- <Spinner
- invertColor = { true }
- isCompleting = { false }
- size = { 'large' } />
- </div>
- );
- }
-
- /**
- * Renders a preview entry.
- *
- * @param {Object} data - The track data.
- * @returns {React$Node}
- */
- _renderPreviewEntry(data) {
- const { t } = this.props;
- const className = 'video-background-preview-entry';
-
- if (this.state.loading) {
- return this._loadVideoPreview();
- }
- if (!data) {
- return (
- <div
- className = { className }
- video-preview-container = { true }>
- <div className = 'video-preview-error'>{t('deviceSelection.previewUnavailable')}</div>
- </div>
- );
- }
- const props: Object = {
- className
- };
-
- return (
- <div { ...props }>
- <Video
- className = { videoClassName }
- playsinline = { true }
- videoTrack = {{ jitsiTrack: data }} />
- </div>
- );
- }
-
- /**
- * Implements React's {@link Component#componentDidMount}.
- *
- * @inheritdoc
- */
- componentDidMount() {
- this._setTracks();
- }
-
- /**
- * Implements React's {@link Component#componentWillUnmount}.
- *
- * @inheritdoc
- */
- componentWillUnmount() {
- this._componentWasUnmounted = true;
- this._stopStream(this.state.jitsiTrack);
- }
-
- /**
- * Implements React's {@link Component#componentDidUpdate}.
- *
- * @inheritdoc
- */
- async componentDidUpdate(prevProps) {
- if (!equals(this.props._currentCameraDeviceId, prevProps._currentCameraDeviceId)) {
- this._setTracks();
- }
- if (!equals(this.props.options, prevProps.options) && this.state.localTrackLoaded) {
- if (prevProps.options.backgroundType === VIRTUAL_BACKGROUND_TYPE.DESKTOP_SHARE) {
- prevProps.options.url.dispose();
- }
- this._applyBackgroundEffect();
- }
- if (this.props.options.url?.videoType === VIDEO_TYPE.DESKTOP) {
- localTrackStopped(this.props.dispatch, this.props.options.url, this.state.jitsiTrack);
- }
- }
-
- /**
- * Implements React's {@link Component#render}.
- *
- * @inheritdoc
- */
- render() {
- const { jitsiTrack } = this.state;
-
- return jitsiTrack
- ? <div className = 'video-preview'>{this._renderPreviewEntry(jitsiTrack)}</div>
- : <div className = 'video-preview-loader'>{this._loadVideoPreview()}</div>
- ;
- }
- }
-
- /**
- * Maps (parts of) the redux state to the associated props for the
- * {@code VirtualBackgroundPreview} component.
- *
- * @param {Object} state - The Redux state.
- * @private
- * @returns {{Props}}
- */
- function _mapStateToProps(state): Object {
- return {
- _currentCameraDeviceId: getCurrentCameraDeviceId(state)
- };
- }
-
- export default translate(connect(_mapStateToProps)(VirtualBackgroundPreview));
|