forked from treehouse/mastodon
[Glitch] Add hotkeys for audio/video control
Port 04a079e723
to glitch-soc
Signed-off-by: Thibaut Girka <thib@sitedethib.com>
signup-info-prompt
parent
48f0f3ffee
commit
b82aa33dea
|
@ -392,13 +392,59 @@ class Audio extends React.PureComponent {
|
||||||
return this.props.foregroundColor || '#ffffff';
|
return this.props.foregroundColor || '#ffffff';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
seekBy (time) {
|
||||||
|
const currentTime = this.audio.currentTime + time;
|
||||||
|
|
||||||
|
if (!isNaN(currentTime)) {
|
||||||
|
this.setState({ currentTime }, () => {
|
||||||
|
this.audio.currentTime = currentTime;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAudioKeyDown = e => {
|
||||||
|
// On the audio element or the seek bar, we can safely use the space bar
|
||||||
|
// for playback control because there are no buttons to press
|
||||||
|
|
||||||
|
if (e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.togglePlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown = e => {
|
||||||
|
switch(e.key) {
|
||||||
|
case 'k':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.togglePlay();
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.toggleMute();
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.seekBy(-10);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.seekBy(10);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { src, intl, alt, editable, autoPlay } = this.props;
|
const { src, intl, alt, editable, autoPlay } = this.props;
|
||||||
const { paused, muted, volume, currentTime, duration, buffer, dragging } = this.state;
|
const { paused, muted, volume, currentTime, duration, buffer, dragging } = this.state;
|
||||||
const progress = Math.min((currentTime / duration) * 100, 100);
|
const progress = Math.min((currentTime / duration) * 100, 100);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('audio-player', { editable })} ref={this.setPlayerRef} style={{ backgroundColor: this._getBackgroundColor(), color: this._getForegroundColor(), width: '100%', height: this.props.fullscreen ? '100%' : (this.state.height || this.props.height) }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
<div className={classNames('audio-player', { editable })} ref={this.setPlayerRef} style={{ backgroundColor: this._getBackgroundColor(), color: this._getForegroundColor(), width: '100%', height: this.props.fullscreen ? '100%' : (this.state.height || this.props.height) }} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} tabIndex='0' onKeyDown={this.handleKeyDown}>
|
||||||
<audio
|
<audio
|
||||||
src={src}
|
src={src}
|
||||||
ref={this.setAudioRef}
|
ref={this.setAudioRef}
|
||||||
|
@ -412,12 +458,14 @@ class Audio extends React.PureComponent {
|
||||||
|
|
||||||
<canvas
|
<canvas
|
||||||
role='button'
|
role='button'
|
||||||
|
tabIndex='0'
|
||||||
className='audio-player__canvas'
|
className='audio-player__canvas'
|
||||||
width={this.state.width}
|
width={this.state.width}
|
||||||
height={this.state.height}
|
height={this.state.height}
|
||||||
style={{ width: '100%', position: 'absolute', top: 0, left: 0 }}
|
style={{ width: '100%', position: 'absolute', top: 0, left: 0 }}
|
||||||
ref={this.setCanvasRef}
|
ref={this.setCanvasRef}
|
||||||
onClick={this.togglePlay}
|
onClick={this.togglePlay}
|
||||||
|
onKeyDown={this.handleAudioKeyDown}
|
||||||
title={alt}
|
title={alt}
|
||||||
aria-label={alt}
|
aria-label={alt}
|
||||||
/>
|
/>
|
||||||
|
@ -438,6 +486,7 @@ class Audio extends React.PureComponent {
|
||||||
className={classNames('video-player__seek__handle', { active: dragging })}
|
className={classNames('video-player__seek__handle', { active: dragging })}
|
||||||
tabIndex='0'
|
tabIndex='0'
|
||||||
style={{ left: `${progress}%`, backgroundColor: this._getAccentColor() }}
|
style={{ left: `${progress}%`, backgroundColor: this._getAccentColor() }}
|
||||||
|
onKeyDown={this.handleAudioKeyDown}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -279,6 +279,81 @@ class Video extends React.PureComponent {
|
||||||
}
|
}
|
||||||
}, 15);
|
}, 15);
|
||||||
|
|
||||||
|
seekBy (time) {
|
||||||
|
const currentTime = this.video.currentTime + time;
|
||||||
|
|
||||||
|
if (!isNaN(currentTime)) {
|
||||||
|
this.setState({ currentTime }, () => {
|
||||||
|
this.video.currentTime = currentTime;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleVideoKeyDown = e => {
|
||||||
|
// On the video element or the seek bar, we can safely use the space bar
|
||||||
|
// for playback control because there are no buttons to press
|
||||||
|
|
||||||
|
if (e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.togglePlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeyDown = e => {
|
||||||
|
const frameTime = 1 / 25;
|
||||||
|
|
||||||
|
switch(e.key) {
|
||||||
|
case 'k':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.togglePlay();
|
||||||
|
break;
|
||||||
|
case 'm':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.toggleMute();
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.toggleFullscreen();
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.seekBy(-10);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.seekBy(10);
|
||||||
|
break;
|
||||||
|
case ',':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.seekBy(-frameTime);
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.seekBy(frameTime);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are in fullscreen mode, we don't want any hotkeys
|
||||||
|
// interacting with the UI that's not visible
|
||||||
|
|
||||||
|
if (this.state.fullscreen) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
exitFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
togglePlay = () => {
|
togglePlay = () => {
|
||||||
if (this.state.paused) {
|
if (this.state.paused) {
|
||||||
this.setState({ paused: false }, () => this.video.play());
|
this.setState({ paused: false }, () => this.video.play());
|
||||||
|
@ -504,6 +579,7 @@ class Video extends React.PureComponent {
|
||||||
onMouseEnter={this.handleMouseEnter}
|
onMouseEnter={this.handleMouseEnter}
|
||||||
onMouseLeave={this.handleMouseLeave}
|
onMouseLeave={this.handleMouseLeave}
|
||||||
onMouseDown={this.handleMouseDownRoot}
|
onMouseDown={this.handleMouseDownRoot}
|
||||||
|
onKeyDown={this.handleKeyDown}
|
||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<Blurhash
|
<Blurhash
|
||||||
|
@ -528,6 +604,7 @@ class Video extends React.PureComponent {
|
||||||
height={height}
|
height={height}
|
||||||
volume={volume}
|
volume={volume}
|
||||||
onClick={this.togglePlay}
|
onClick={this.togglePlay}
|
||||||
|
onKeyDown={this.handleVideoKeyDown}
|
||||||
onPlay={this.handlePlay}
|
onPlay={this.handlePlay}
|
||||||
onPause={this.handlePause}
|
onPause={this.handlePause}
|
||||||
onLoadedData={this.handleLoadedData}
|
onLoadedData={this.handleLoadedData}
|
||||||
|
@ -550,6 +627,7 @@ class Video extends React.PureComponent {
|
||||||
className={classNames('video-player__seek__handle', { active: dragging })}
|
className={classNames('video-player__seek__handle', { active: dragging })}
|
||||||
tabIndex='0'
|
tabIndex='0'
|
||||||
style={{ left: `${progress}%` }}
|
style={{ left: `${progress}%` }}
|
||||||
|
onKeyDown={this.handleVideoKeyDown}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue