snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
This commit is contained in:
@@ -0,0 +1,305 @@
|
||||
import { Theme } from '@mui/material';
|
||||
import cx from 'classnames';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { WithTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { withStyles } from 'tss-react/mui';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
import { hideDialog } from '../../base/dialog/actions';
|
||||
import { translate } from '../../base/i18n/functions';
|
||||
import { Video } from '../../base/media/components/index';
|
||||
import { equals } from '../../base/redux/functions';
|
||||
import { createLocalTracksF } from '../../base/tracks/functions';
|
||||
import Spinner from '../../base/ui/components/web/Spinner';
|
||||
import { showWarningNotification } from '../../notifications/actions';
|
||||
import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
|
||||
import { toggleBackgroundEffect } from '../actions';
|
||||
import logger from '../logger';
|
||||
import { IVirtualBackground } from '../reducer';
|
||||
/**
|
||||
* The type of the React {@code PureComponent} props of {@link VirtualBackgroundPreview}.
|
||||
*/
|
||||
export interface IProps extends WithTranslation {
|
||||
|
||||
/**
|
||||
* An object containing the CSS classes.
|
||||
*/
|
||||
classes?: Partial<Record<keyof ReturnType<typeof styles>, string>>;
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: IStore['dispatch'];
|
||||
|
||||
/**
|
||||
* Dialog callback that indicates if the background preview was loaded.
|
||||
*/
|
||||
loadedPreview: Function;
|
||||
|
||||
/**
|
||||
* Represents the virtual background set options.
|
||||
*/
|
||||
options: IVirtualBackground;
|
||||
|
||||
/**
|
||||
* The id of the selected video device.
|
||||
*/
|
||||
selectedVideoInputId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link VirtualBackgroundPreview}.
|
||||
*/
|
||||
interface IState {
|
||||
|
||||
/**
|
||||
* Activate the selected device camera only.
|
||||
*/
|
||||
jitsiTrack: Object | null;
|
||||
|
||||
/**
|
||||
* Loader activated on setting virtual background.
|
||||
*/
|
||||
loading: boolean;
|
||||
|
||||
/**
|
||||
* Flag that indicates if the local track was loaded.
|
||||
*/
|
||||
localTrackLoaded: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the styles for the component.
|
||||
*
|
||||
* @param {Object} theme - The current UI theme.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
const styles = (theme: Theme) => {
|
||||
return {
|
||||
virtualBackgroundPreview: {
|
||||
height: 'auto',
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
marginBottom: theme.spacing(3),
|
||||
zIndex: 2,
|
||||
borderRadius: '3px',
|
||||
backgroundColor: theme.palette.virtualBackgroundPreview,
|
||||
position: 'relative' as const
|
||||
},
|
||||
|
||||
previewLoader: {
|
||||
height: '220px',
|
||||
|
||||
'& svg': {
|
||||
position: 'absolute' as const,
|
||||
top: '40%',
|
||||
left: '45%'
|
||||
}
|
||||
},
|
||||
|
||||
previewVideo: {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
objectFit: 'cover' as const
|
||||
},
|
||||
|
||||
error: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: '100%',
|
||||
height: '220px',
|
||||
position: 'relative' as const
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a React {@link PureComponent} which displays the virtual
|
||||
* background preview.
|
||||
*
|
||||
* @augments PureComponent
|
||||
*/
|
||||
class VirtualBackgroundPreview extends PureComponent<IProps, IState> {
|
||||
_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: IProps) {
|
||||
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: any) {
|
||||
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.selectedVideoInputId,
|
||||
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',
|
||||
descriptionKey: 'deviceError.cameraError'
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.LONG)
|
||||
);
|
||||
logger.error('Failed to access camera device. Error on apply background effect.');
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
|
||||
return (
|
||||
<div className = { classes.previewLoader }>
|
||||
<Spinner size = 'large' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a preview entry.
|
||||
*
|
||||
* @param {Object} data - The track data.
|
||||
* @returns {React$Node}
|
||||
*/
|
||||
_renderPreviewEntry(data: Object) {
|
||||
const { t } = this.props;
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
|
||||
if (this.state.loading) {
|
||||
return this._loadVideoPreview();
|
||||
}
|
||||
if (!data) {
|
||||
return (
|
||||
<div className = { classes.error }>{t('deviceSelection.previewUnavailable')}</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Video
|
||||
className = { cx(classes.previewVideo, 'flipVideoX') }
|
||||
id = 'virtual_background_preview'
|
||||
playsinline = { true }
|
||||
videoTrack = {{ jitsiTrack: data }} />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidMount}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
override componentDidMount() {
|
||||
this._setTracks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentWillUnmount}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
override componentWillUnmount() {
|
||||
this._componentWasUnmounted = true;
|
||||
this._stopStream(this.state.jitsiTrack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#componentDidUpdate}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
override async componentDidUpdate(prevProps: IProps) {
|
||||
if (!equals(this.props.selectedVideoInputId, prevProps.selectedVideoInputId)) {
|
||||
this._setTracks();
|
||||
}
|
||||
if (!equals(this.props.options, prevProps.options) && this.state.localTrackLoaded) {
|
||||
this._applyBackgroundEffect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
override render() {
|
||||
const { jitsiTrack } = this.state;
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
|
||||
return (
|
||||
<div className = { classes.virtualBackgroundPreview }>
|
||||
{jitsiTrack
|
||||
? this._renderPreviewEntry(jitsiTrack)
|
||||
: this._loadVideoPreview()
|
||||
}</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(connect()(withStyles(VirtualBackgroundPreview, styles)));
|
||||
Reference in New Issue
Block a user