Browse Source

Add hotkeys for audio/video control (#15158)

Fix #14515
master
Eugen Rochko 3 years ago
committed by GitHub
parent
commit
04a079e723
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 128 additions and 1 deletions
  1. +50
    -1
      app/javascript/mastodon/features/audio/index.js
  2. +78
    -0
      app/javascript/mastodon/features/video/index.js

+ 50
- 1
app/javascript/mastodon/features/audio/index.js View File

@ -386,13 +386,59 @@ class Audio extends React.PureComponent {
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 () {
const { src, intl, alt, editable, autoPlay } = this.props;
const { paused, muted, volume, currentTime, duration, buffer, dragging } = this.state;
const progress = Math.min((currentTime / duration) * 100, 100);
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
src={src}
ref={this.setAudioRef}
@ -406,12 +452,14 @@ class Audio extends React.PureComponent {
<canvas
role='button'
tabIndex='0'
className='audio-player__canvas'
width={this.state.width}
height={this.state.height}
style={{ width: '100%', position: 'absolute', top: 0, left: 0 }}
ref={this.setCanvasRef}
onClick={this.togglePlay}
onKeyDown={this.handleAudioKeyDown}
title={alt}
aria-label={alt}
/>
@ -432,6 +480,7 @@ class Audio extends React.PureComponent {
className={classNames('video-player__seek__handle', { active: dragging })}
tabIndex='0'
style={{ left: `${progress}%`, backgroundColor: this._getAccentColor() }}
onKeyDown={this.handleAudioKeyDown}
/>
</div>

+ 78
- 0
app/javascript/mastodon/features/video/index.js View File

@ -266,6 +266,81 @@ class Video extends React.PureComponent {
}
}, 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 = () => {
if (this.state.paused) {
this.setState({ paused: false }, () => this.video.play());
@ -484,6 +559,7 @@ class Video extends React.PureComponent {
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
onClick={this.handleClickRoot}
onKeyDown={this.handleKeyDown}
tabIndex={0}
>
<Blurhash
@ -507,6 +583,7 @@ class Video extends React.PureComponent {
height={height}
volume={volume}
onClick={this.togglePlay}
onKeyDown={this.handleVideoKeyDown}
onPlay={this.handlePlay}
onPause={this.handlePause}
onLoadedData={this.handleLoadedData}
@ -529,6 +606,7 @@ class Video extends React.PureComponent {
className={classNames('video-player__seek__handle', { active: dragging })}
tabIndex='0'
style={{ left: `${progress}%` }}
onKeyDown={this.handleVideoKeyDown}
/>
</div>

Loading…
Cancel
Save