From aef01ea262a4740e3620af0b78d48fa71b32589a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 22 Oct 2021 18:50:32 +0200 Subject: [PATCH 1/4] Change audio/video playback to stop playback when out of view MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/features/video/index.js | 32 +++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/app/soapbox/features/video/index.js b/app/soapbox/features/video/index.js index 69c16ee47..6de8bb87e 100644 --- a/app/soapbox/features/video/index.js +++ b/app/soapbox/features/video/index.js @@ -139,8 +139,8 @@ class Video extends React.PureComponent { revealed: this.props.visible !== undefined ? this.props.visible : (this.props.displayMedia !== 'hide_all' && !this.props.sensitive || this.props.displayMedia === 'show_all'), }; - // hard coded in components.scss - // any way to get ::before values programatically? + // Hard-coded in components.scss + // Any way to get ::before values programatically? volWidth = 50; volOffset = 70; volHandleOffset = v => { @@ -263,9 +263,9 @@ class Video extends React.PureComponent { togglePlay = () => { if (this.state.paused) { - this.video.play(); + this.setState({ paused: false }, () => this.video.play()); } else { - this.video.pause(); + this.setState({ paused: true }, () => this.video.pause()); } } @@ -282,9 +282,13 @@ class Video extends React.PureComponent { document.addEventListener('webkitfullscreenchange', this.handleFullscreenChange, true); document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true); document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true); + + window.addEventListener('scroll', this.handleScroll); } componentWillUnmount() { + window.removeEventListener('scroll', this.handleScroll); + document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true); document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true); document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true); @@ -303,6 +307,19 @@ class Video extends React.PureComponent { } } + handleScroll = throttle(() => { + if (!this.video) { + return; + } + + const { top, height } = this.video.getBoundingClientRect(); + const inView = (top <= (window.innerHeight || document.documentElement.clientHeight)) && (top + height >= 0); + + if (!this.state.paused && !inView) { + this.setState({ paused: true }, () => this.video.pause()); + } + }, 150, { trailing: true }) + handleFullscreenChange = () => { this.setState({ fullscreen: isFullscreen() }); } @@ -316,8 +333,11 @@ class Video extends React.PureComponent { } toggleMute = () => { - this.video.muted = !this.video.muted; - this.setState({ muted: this.video.muted }); + const muted = !this.video.muted; + + this.setState({ muted }, () => { + this.video.muted = muted; + }); } toggleReveal = () => { From a338e7c98fb0962a2c815a3b0ecab73192a1890f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 22 Oct 2021 18:59:15 +0200 Subject: [PATCH 2/4] Add tooltips to audio/video player buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/features/video/index.js | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/app/soapbox/features/video/index.js b/app/soapbox/features/video/index.js index 6de8bb87e..8dde3b2d7 100644 --- a/app/soapbox/features/video/index.js +++ b/app/soapbox/features/video/index.js @@ -139,15 +139,6 @@ class Video extends React.PureComponent { revealed: this.props.visible !== undefined ? this.props.visible : (this.props.displayMedia !== 'hide_all' && !this.props.sensitive || this.props.displayMedia === 'show_all'), }; - // Hard-coded in components.scss - // Any way to get ::before values programatically? - volWidth = 50; - volOffset = 70; - volHandleOffset = v => { - const offset = v * this.volWidth + this.volOffset; - return (offset > 110) ? 110 : offset; - } - setPlayerRef = c => { this.player = c; @@ -212,16 +203,17 @@ class Video extends React.PureComponent { } handleMouseVolSlide = throttle(e => { - const rect = this.volume.getBoundingClientRect(); - const x = (e.clientX - rect.left) / this.volWidth; //x position within the element. + const { x } = getPointerPosition(this.volume, e); if(!isNaN(x)) { let slideamt = x; + if(x > 1) { slideamt = 1; } else if(x < 0) { slideamt = 0; } + this.video.volume = slideamt; this.setState({ volume: slideamt }); } @@ -489,8 +481,8 @@ class Video extends React.PureComponent {
- - + +
@@ -513,10 +505,10 @@ class Video extends React.PureComponent {
- {(sensitive && !onCloseVideo) && } - {(!fullscreen && onOpenVideo) && } - {onCloseVideo && } - + {(sensitive && !onCloseVideo) && } + {(!fullscreen && onOpenVideo) && } + {onCloseVideo && } +
From a1c4066077f6ffc2ef303040eb5eab0b8b92b495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 22 Oct 2021 19:03:14 +0200 Subject: [PATCH 3/4] Fix video not reacting to window resizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/features/video/index.js | 31 +++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/app/soapbox/features/video/index.js b/app/soapbox/features/video/index.js index 8dde3b2d7..80abd05f0 100644 --- a/app/soapbox/features/video/index.js +++ b/app/soapbox/features/video/index.js @@ -3,7 +3,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { fromJS, is } from 'immutable'; -import { throttle } from 'lodash'; +import { throttle, debounce } from 'lodash'; import classNames from 'classnames'; import { isFullscreen, requestFullscreen, exitFullscreen } from '../ui/util/fullscreen'; import Icon from 'soapbox/components/icon'; @@ -142,14 +142,23 @@ class Video extends React.PureComponent { setPlayerRef = c => { this.player = c; - if (c) { - if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth); - this.setState({ - containerWidth: c.offsetWidth, - }); + if (this.player) { + this._setDimensions(); } } + _setDimensions() { + const width = this.player.offsetWidth; + + if (this.props.cacheWidth) { + this.props.cacheWidth(width); + } + + this.setState({ + containerWidth: width, + }); + } + setVideoRef = c => { this.video = c; @@ -276,10 +285,12 @@ class Video extends React.PureComponent { document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true); window.addEventListener('scroll', this.handleScroll); + window.addEventListener('resize', this.handleResize, { passive: true }); } componentWillUnmount() { window.removeEventListener('scroll', this.handleScroll); + window.removeEventListener('resize', this.handleResize); document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true); document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true); @@ -299,6 +310,14 @@ class Video extends React.PureComponent { } } + handleResize = debounce(() => { + if (this.player) { + this._setDimensions(); + } + }, 250, { + trailing: true, + }); + handleScroll = throttle(() => { if (!this.video) { return; From a2abb0bad7ac323d351f7768e9e1f677b71406c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?marcin=20miko=C5=82ajczak?= Date: Fri, 22 Oct 2021 19:55:09 +0200 Subject: [PATCH 4/4] Add hotkeys for video control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: marcin mikołajczak --- app/soapbox/features/video/index.js | 78 +++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/app/soapbox/features/video/index.js b/app/soapbox/features/video/index.js index 80abd05f0..9fc3831bb 100644 --- a/app/soapbox/features/video/index.js +++ b/app/soapbox/features/video/index.js @@ -262,6 +262,81 @@ class Video extends React.PureComponent { } }, 60); + 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()); @@ -450,6 +525,7 @@ class Video extends React.PureComponent { onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onClick={this.handleClickRoot} + onKeyDown={this.handleKeyDown} tabIndex={0} >