2020-03-30 14:17:18 +00:00
|
|
|
// @flow
|
|
|
|
|
|
|
|
import React, { Component } from 'react';
|
|
|
|
|
|
|
|
import { translate } from '../../../../base/i18n';
|
|
|
|
import Video from '../../../../base/media/components/Video';
|
2020-05-20 10:57:03 +00:00
|
|
|
import { equals } from '../../../../base/redux';
|
2020-03-30 14:17:18 +00:00
|
|
|
import { createLocalVideoTracks } from '../../../functions';
|
|
|
|
|
|
|
|
|
|
|
|
const videoClassName = 'video-preview-video flipVideoX';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The type of the React {@code Component} props of {@link VideoSettingsContent}.
|
|
|
|
*/
|
|
|
|
export type Props = {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The deviceId of the camera device currently being used.
|
|
|
|
*/
|
|
|
|
currentCameraDeviceId: string,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback invoked to change current camera.
|
|
|
|
*/
|
|
|
|
setVideoInputDevice: Function,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked to obtain translated strings.
|
|
|
|
*/
|
|
|
|
t: Function,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback invoked to toggle the settings popup visibility.
|
|
|
|
*/
|
|
|
|
toggleVideoSettings: Function,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* All the camera device ids currently connected.
|
|
|
|
*/
|
|
|
|
videoDeviceIds: string[],
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The type of the React {@code Component} state of {@link VideoSettingsContent}.
|
|
|
|
*/
|
|
|
|
type State = {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* An array of all the jitsiTracks and eventual errors.
|
|
|
|
*/
|
|
|
|
trackData: Object[],
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements a React {@link Component} which displays a list of video
|
|
|
|
* previews to choose from.
|
|
|
|
*
|
2021-11-04 21:10:43 +00:00
|
|
|
* @augments Component
|
2020-03-30 14:17:18 +00:00
|
|
|
*/
|
|
|
|
class VideoSettingsContent extends Component<Props, State> {
|
|
|
|
_componentWasUnmounted: boolean;
|
2021-06-10 12:48:44 +00:00
|
|
|
_videoContentRef: Object;
|
2020-03-30 14:17:18 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initializes a new {@code VideoSettingsContent} instance.
|
|
|
|
*
|
|
|
|
* @param {Object} props - The read-only properties with which the new
|
|
|
|
* instance is to be initialized.
|
|
|
|
*/
|
|
|
|
constructor(props) {
|
|
|
|
super(props);
|
2021-06-10 12:48:44 +00:00
|
|
|
this._onEscClick = this._onEscClick.bind(this);
|
|
|
|
this._videoContentRef = React.createRef();
|
2020-03-30 14:17:18 +00:00
|
|
|
|
|
|
|
this.state = {
|
|
|
|
trackData: new Array(props.videoDeviceIds.length).fill({
|
|
|
|
jitsiTrack: null
|
|
|
|
})
|
|
|
|
};
|
|
|
|
}
|
2021-06-10 12:48:44 +00:00
|
|
|
_onEscClick: (KeyboardEvent) => void;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Click handler for the video entries.
|
|
|
|
*
|
|
|
|
* @param {KeyboardEvent} event - Esc key click to close the popup.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
_onEscClick(event) {
|
|
|
|
if (event.key === 'Escape') {
|
|
|
|
event.preventDefault();
|
|
|
|
event.stopPropagation();
|
|
|
|
this._videoContentRef.current.style.display = 'none';
|
|
|
|
}
|
|
|
|
}
|
2020-03-30 14:17:18 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates and updates the track data.
|
|
|
|
*
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
async _setTracks() {
|
|
|
|
this._disposeTracks(this.state.trackData);
|
|
|
|
|
2021-02-02 00:20:39 +00:00
|
|
|
const trackData = await createLocalVideoTracks(this.props.videoDeviceIds, 5000);
|
2020-03-30 14:17:18 +00:00
|
|
|
|
|
|
|
// In case the component gets unmounted before the tracks are created
|
|
|
|
// avoid a leak by not setting the state
|
|
|
|
if (this._componentWasUnmounted) {
|
|
|
|
this._disposeTracks(trackData);
|
|
|
|
} else {
|
|
|
|
this.setState({
|
|
|
|
trackData
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Destroys all the tracks from trackData object.
|
|
|
|
*
|
|
|
|
* @param {Object[]} trackData - An array of tracks that are to be disposed.
|
|
|
|
* @returns {Promise<void>}
|
|
|
|
*/
|
|
|
|
_disposeTracks(trackData) {
|
|
|
|
trackData.forEach(({ jitsiTrack }) => {
|
|
|
|
jitsiTrack && jitsiTrack.dispose();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the click handler used when selecting the video preview.
|
|
|
|
*
|
|
|
|
* @param {string} deviceId - The id of the camera device.
|
|
|
|
* @returns {Function}
|
|
|
|
*/
|
|
|
|
_onEntryClick(deviceId) {
|
|
|
|
return () => {
|
|
|
|
this.props.setVideoInputDevice(deviceId);
|
|
|
|
this.props.toggleVideoSettings();
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Renders a preview entry.
|
|
|
|
*
|
|
|
|
* @param {Object} data - The track data.
|
|
|
|
* @param {number} index - The index of the entry.
|
|
|
|
* @returns {React$Node}
|
|
|
|
*/
|
|
|
|
_renderPreviewEntry(data, index) {
|
|
|
|
const { error, jitsiTrack, deviceId } = data;
|
|
|
|
const { currentCameraDeviceId, t } = this.props;
|
|
|
|
const isSelected = deviceId === currentCameraDeviceId;
|
|
|
|
const key = `vp-${index}`;
|
|
|
|
const className = 'video-preview-entry';
|
2021-06-10 12:48:44 +00:00
|
|
|
const tabIndex = '0';
|
2020-03-30 14:17:18 +00:00
|
|
|
|
|
|
|
if (error) {
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className = { className }
|
2021-06-10 12:48:44 +00:00
|
|
|
key = { key }
|
|
|
|
tabIndex = { -1 } >
|
2020-03-30 14:17:18 +00:00
|
|
|
<div className = 'video-preview-error'>{t(error)}</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const props: Object = {
|
|
|
|
className,
|
2021-06-10 12:48:44 +00:00
|
|
|
key,
|
|
|
|
tabIndex
|
2020-03-30 14:17:18 +00:00
|
|
|
};
|
2020-03-30 14:44:45 +00:00
|
|
|
const label = jitsiTrack && jitsiTrack.getTrackLabel();
|
2020-03-30 14:17:18 +00:00
|
|
|
|
|
|
|
if (isSelected) {
|
2021-06-10 12:48:44 +00:00
|
|
|
props['aria-checked'] = true;
|
2020-03-30 14:17:18 +00:00
|
|
|
props.className = `${className} video-preview-entry--selected`;
|
|
|
|
} else {
|
|
|
|
props.onClick = this._onEntryClick(deviceId);
|
2021-06-10 12:48:44 +00:00
|
|
|
props.onKeyPress = e => {
|
|
|
|
if (e.key === ' ' || e.key === 'Enter') {
|
|
|
|
e.preventDefault();
|
|
|
|
props.onClick();
|
|
|
|
}
|
|
|
|
};
|
2020-03-30 14:17:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
2021-06-10 12:48:44 +00:00
|
|
|
<div
|
|
|
|
{ ...props }
|
|
|
|
role = 'radio'>
|
2021-02-23 11:09:22 +00:00
|
|
|
<div className = 'video-preview-label'>
|
2021-03-11 09:25:49 +00:00
|
|
|
{label && <div className = 'video-preview-label-container'>
|
|
|
|
<div className = 'video-preview-label-text'>
|
|
|
|
<span>{label}</span></div>
|
|
|
|
</div>}
|
2021-02-23 11:09:22 +00:00
|
|
|
</div>
|
2020-03-30 14:17:18 +00:00
|
|
|
<div className = 'video-preview-overlay' />
|
|
|
|
<Video
|
|
|
|
className = { videoClassName }
|
2020-06-11 20:13:36 +00:00
|
|
|
playsinline = { true }
|
2020-03-30 14:17:18 +00:00
|
|
|
videoTrack = {{ jitsiTrack }} />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements React's {@link Component#componentDidMount}.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
componentDidMount() {
|
|
|
|
this._setTracks();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements React's {@link Component#componentWillUnmount}.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
componentWillUnmount() {
|
|
|
|
this._componentWasUnmounted = true;
|
|
|
|
this._disposeTracks(this.state.trackData);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements React's {@link Component#componentDidUpdate}.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
componentDidUpdate(prevProps) {
|
|
|
|
if (!equals(this.props.videoDeviceIds, prevProps.videoDeviceIds)) {
|
|
|
|
this._setTracks();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Implements React's {@link Component#render}.
|
|
|
|
*
|
|
|
|
* @inheritdoc
|
|
|
|
*/
|
|
|
|
render() {
|
|
|
|
const { trackData } = this.state;
|
|
|
|
|
|
|
|
return (
|
2021-06-10 12:48:44 +00:00
|
|
|
<div
|
|
|
|
aria-labelledby = 'video-settings-button'
|
|
|
|
className = 'video-preview-container'
|
|
|
|
id = 'video-settings-dialog'
|
|
|
|
onKeyDown = { this._onEscClick }
|
|
|
|
ref = { this._videoContentRef }
|
|
|
|
role = 'radiogroup'
|
|
|
|
tabIndex = '-1'>
|
|
|
|
{trackData.map((data, i) => this._renderPreviewEntry(data, i))}
|
2020-03-30 14:17:18 +00:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default translate(VideoSettingsContent);
|