labo-components
Version:
220 lines (199 loc) • 7.65 kB
JSX
import PropTypes from 'prop-types';
import FlexPlayerUtil from '../../util/FlexPlayerUtil';
import IDUtil from '../../util/IDUtil';
//example in pure JS: https://blog.teamtreehouse.com/building-custom-controls-for-html5-videos
//use refs for constant time updates (state triggered rerenders are too slow): https://reactjs.org/docs/refs-and-the-dom.html
//FIXME currently the controls are tied directly to the HTML5VideoPlayer, since off-air content only exists for B&G material,
//which is played using that player. Ideally though these controls could/should be tied to the FlexPlayer and should override all existing player's controls
class FlexPlayerControls extends React.Component {
constructor(props) {
super(props);
this.state = {
muted: this.props.api.isMuted(),
paused: !this.props.api.isPaused(),
fullscreen: false
};
this.seekBarRef = React.createRef(); //not using state for this, since media playback updates every second!
this.volumeRef = React.createRef();
this.buttonsRef = React.createRef();
this.overlayRef = React.createRef();
}
componentDidMount = () => {
//FIXME this part is not player agnostic yet. For now this is fine (see comment at the top)
this.props.api.getApi().addEventListener('timeupdate', () => {
this.onPlayProgress();
});
this.props.api.getApi().addEventListener('volumechange', () => {
this.volumeRef.current.value = this.props.api.getVolume();
});
//only add a listener (for the ESC key) if the "fake full-screen" is used
if (
this.props.toggleFullScreen &&
typeof this.props.toggleFullScreen === 'function'
) {
document.addEventListener('keydown', this.handleKey, false);
}
};
componentWillUnmount = () => {
if (
this.props.toggleFullScreen &&
typeof this.props.toggleFullScreen === 'function'
) {
document.removeEventListener('keydown', this.handleKey, false);
}
};
//handles the ESC key for the "fake full screen" mode
handleKey = event => {
if (event.keyCode === 27) {
this.onToggleFullScreen(true);
}
};
onPlayProgress = () => {
if (!this.seekBarRef.current) {
return;
}
//update the seekbar
const value =
(100 / this.props.duration) *
FlexPlayerUtil.timeRelativeToOnAir(
this.props.api.getPosition(),
this.props.mediaObject
);
this.seekBarRef.current.value = value;
//check if the end of the on-air content has been reached
if (
FlexPlayerUtil.isTimeAfterOnAir(
this.props.api.getPosition(),
this.props.mediaObject
)
) {
this.props.api.pause();
}
};
onTogglePlay = () => {
let paused = this.props.api.isPaused();
if (paused) {
this.props.api.play();
} else {
this.props.api.pause();
}
this.setState({
paused: !paused
});
};
onToggleMute = () => {
this.props.api.toggleMute();
this.setState(
{
muted: this.props.api.isMuted()
},
() => {
if (!this.state.muted && this.props.api.getLastVolume() != -1) {
this.props.api.setVolume(this.props.api.getLastVolume());
} else {
this.props.api.setVolume(0);
}
}
);
};
//this function is call directly by the parent (HTML5Player)
setVisible = visible => {
this.seekBarRef.current.style.display = visible ? 'block' : 'none';
this.buttonsRef.current.style.display = visible ? 'flex' : 'none';
this.overlayRef.current.style.display = visible ? 'block' : 'none';
};
onSeek = e => {
const time = this.props.duration * (e.target.value / 100);
FlexPlayerUtil.seekRelativeToOnAir(
this.props.api,
time,
this.props.mediaObject
);
};
onVolumeChange = e => {
this.props.api.setVolume(e.target.value);
};
//FIXME this function is not yet player agnostic (in the else statement) and only works for the HTML5player API
onToggleFullScreen = exitFullScreen => {
if (
this.props.toggleFullScreen &&
typeof this.props.toggleFullScreen === 'function'
) {
this.props.toggleFullScreen(exitFullScreen);
} else {
if (exitFullScreen === true && !document.fullScreen) {
//should never happen, but just to be sure
return;
}
const api = this.props.api.getApi();
if (api.requestFullscreen) {
api.requestFullscreen();
} else if (api.mozRequestFullScreen) {
api.mozRequestFullScreen(); // Firefox
} else if (api.webkitRequestFullscreen) {
api.webkitRequestFullscreen(); // Chrome and Safari
}
}
this.setState({fullscreen : exitFullScreen === true ? false : !this.state.fullscreen})
};
render = () => {
return (
<div className={IDUtil.cssClassName('flex-controls')}>
<div
className="toggle-play-overlay"
onClick={this.onTogglePlay}
ref={this.overlayRef}
>
<button type="button">
<span
className={
'fa' +
(this.state.paused
? ' fa-play'
: ' fa-pause')
}
></span>
</button>
</div>
<div className="buttons" ref={this.buttonsRef}>
<button type="button" onClick={this.onToggleMute}>
<span
className={
'fa' +
(this.state.muted
? ' fa-volume-off'
: ' fa-volume-up')
}
></span>
</button>
<input
className="volume"
type="range"
ref={this.volumeRef}
min="0"
max="1"
step="0.1"
defaultValue="1"
onChange={this.onVolumeChange}
/>
<button type="button" onClick={this.onToggleFullScreen}>
<span className={
'fa' +
(this.state.fullscreen
? ' fa-compress'
: ' fa-expand')
} />
</button>
</div>
<input
className="seekbar"
type="range"
ref={this.seekBarRef}
defaultValue="0"
onChange={this.onSeek}
/>
</div>
);
};
}
export default FlexPlayerControls;