@ -7,6 +7,7 @@ import classNames from 'classnames';
import { isFullscreen , requestFullscreen , exitFullscreen } from '../ui/util/fullscreen' ;
import { isFullscreen , requestFullscreen , exitFullscreen } from '../ui/util/fullscreen' ;
import { displayMedia } from '../../initial_state' ;
import { displayMedia } from '../../initial_state' ;
import Icon from 'mastodon/components/icon' ;
import Icon from 'mastodon/components/icon' ;
import { decode } from 'blurhash' ;
const messages = defineMessages ( {
const messages = defineMessages ( {
play : { id : 'video.play' , defaultMessage : 'Play' } ,
play : { id : 'video.play' , defaultMessage : 'Play' } ,
@ -102,6 +103,7 @@ class Video extends React.PureComponent {
inline : PropTypes . bool ,
inline : PropTypes . bool ,
cacheWidth : PropTypes . func ,
cacheWidth : PropTypes . func ,
intl : PropTypes . object . isRequired ,
intl : PropTypes . object . isRequired ,
blurhash : PropTypes . string ,
} ;
} ;
state = {
state = {
@ -139,6 +141,7 @@ class Video extends React.PureComponent {
setVideoRef = c => {
setVideoRef = c => {
this . video = c ;
this . video = c ;
if ( this . video ) {
if ( this . video ) {
this . setState ( { volume : this . video . volume , muted : this . video . muted } ) ;
this . setState ( { volume : this . video . volume , muted : this . video . muted } ) ;
}
}
@ -152,6 +155,10 @@ class Video extends React.PureComponent {
this . volume = c ;
this . volume = c ;
}
}
setCanvasRef = c => {
this . canvas = c ;
}
handleClickRoot = e => e . stopPropagation ( ) ;
handleClickRoot = e => e . stopPropagation ( ) ;
handlePlay = ( ) => {
handlePlay = ( ) => {
@ -170,7 +177,6 @@ class Video extends React.PureComponent {
}
}
handleVolumeMouseDown = e => {
handleVolumeMouseDown = e => {
document . addEventListener ( 'mousemove' , this . handleMouseVolSlide , true ) ;
document . addEventListener ( 'mousemove' , this . handleMouseVolSlide , true ) ;
document . addEventListener ( 'mouseup' , this . handleVolumeMouseUp , true ) ;
document . addEventListener ( 'mouseup' , this . handleVolumeMouseUp , true ) ;
document . addEventListener ( 'touchmove' , this . handleMouseVolSlide , true ) ;
document . addEventListener ( 'touchmove' , this . handleMouseVolSlide , true ) ;
@ -190,7 +196,6 @@ class Video extends React.PureComponent {
}
}
handleMouseVolSlide = throttle ( e => {
handleMouseVolSlide = throttle ( e => {
const rect = this . volume . getBoundingClientRect ( ) ;
const rect = this . volume . getBoundingClientRect ( ) ;
const x = ( e . clientX - rect . left ) / this . volWidth ; //x position within the element.
const x = ( e . clientX - rect . left ) / this . volWidth ; //x position within the element.
@ -261,6 +266,10 @@ class Video extends React.PureComponent {
document . addEventListener ( 'webkitfullscreenchange' , this . handleFullscreenChange , true ) ;
document . addEventListener ( 'webkitfullscreenchange' , this . handleFullscreenChange , true ) ;
document . addEventListener ( 'mozfullscreenchange' , this . handleFullscreenChange , true ) ;
document . addEventListener ( 'mozfullscreenchange' , this . handleFullscreenChange , true ) ;
document . addEventListener ( 'MSFullscreenChange' , this . handleFullscreenChange , true ) ;
document . addEventListener ( 'MSFullscreenChange' , this . handleFullscreenChange , true ) ;
if ( this . props . blurhash ) {
this . _decode ( ) ;
}
}
}
componentWillUnmount ( ) {
componentWillUnmount ( ) {
@ -270,6 +279,24 @@ class Video extends React.PureComponent {
document . removeEventListener ( 'MSFullscreenChange' , this . handleFullscreenChange , true ) ;
document . removeEventListener ( 'MSFullscreenChange' , this . handleFullscreenChange , true ) ;
}
}
componentDidUpdate ( prevProps ) {
if ( prevProps . blurhash !== this . props . blurhash && this . props . blurhash ) {
this . _decode ( ) ;
}
}
_decode ( ) {
const hash = this . props . blurhash ;
const pixels = decode ( hash , 32 , 32 ) ;
if ( pixels ) {
const ctx = this . canvas . getContext ( '2d' ) ;
const imageData = new ImageData ( pixels , 32 , 32 ) ;
ctx . putImageData ( imageData , 0 , 0 ) ;
}
}
handleFullscreenChange = ( ) => {
handleFullscreenChange = ( ) => {
this . setState ( { fullscreen : isFullscreen ( ) } ) ;
this . setState ( { fullscreen : isFullscreen ( ) } ) ;
}
}
@ -314,6 +341,7 @@ class Video extends React.PureComponent {
handleOpenVideo = ( ) => {
handleOpenVideo = ( ) => {
const { src , preview , width , height , alt } = this . props ;
const { src , preview , width , height , alt } = this . props ;
const media = fromJS ( {
const media = fromJS ( {
type : 'video' ,
type : 'video' ,
url : src ,
url : src ,
@ -351,6 +379,7 @@ class Video extends React.PureComponent {
}
}
let preload ;
let preload ;
if ( startTime || fullscreen || dragging ) {
if ( startTime || fullscreen || dragging ) {
preload = 'auto' ;
preload = 'auto' ;
} else if ( detailed ) {
} else if ( detailed ) {
@ -360,6 +389,7 @@ class Video extends React.PureComponent {
}
}
let warning ;
let warning ;
if ( sensitive ) {
if ( sensitive ) {
warning = < FormattedMessage id = 'status.sensitive_warning' defaultMessage = 'Sensitive content' / > ;
warning = < FormattedMessage id = 'status.sensitive_warning' defaultMessage = 'Sensitive content' / > ;
} else {
} else {
@ -377,7 +407,9 @@ class Video extends React.PureComponent {
onClick = { this . handleClickRoot }
onClick = { this . handleClickRoot }
tabIndex = { 0 }
tabIndex = { 0 }
>
>
< video
< canvas width = { 32 } height = { 32 } ref = { this . setCanvasRef } className = { classNames ( 'media-gallery__preview' , { 'media-gallery__preview--hidden' : revealed } ) } / >
{ revealed && < video
ref = { this . setVideoRef }
ref = { this . setVideoRef }
src = { src }
src = { src }
poster = { preview }
poster = { preview }
@ -397,12 +429,13 @@ class Video extends React.PureComponent {
onLoadedData = { this . handleLoadedData }
onLoadedData = { this . handleLoadedData }
onProgress = { this . handleProgress }
onProgress = { this . handleProgress }
onVolumeChange = { this . handleVolumeChange }
onVolumeChange = { this . handleVolumeChange }
/ >
/ > }
< button type = 'button' className = { classNames ( 'video-player__spoiler' , { active : ! revealed } ) } onClick = { this . toggleReveal } >
< div className = { classNames ( 'spoiler-button' , { 'spoiler-button--hidden' : revealed } ) } >
< span className = 'video-player__spoiler__title' > { warning } < / s p a n >
< button type = 'button' className = 'spoiler-button__overlay' onClick = { this . toggleReveal } >
< span className = ' video-player__spoiler__subtitle'> < FormattedMessage id = 'status.sensitive_toggle' defaultMessage = 'Click to view' / > < / s p a n >
< span className = ' spoiler-button__overlay__label'> { warning } < / s p a n >
< / b u t t o n >
< / b u t t o n >
< / d i v >
< div className = { classNames ( 'video-player__controls' , { active : paused || hovered } ) } >
< div className = { classNames ( 'video-player__controls' , { active : paused || hovered } ) } >
< div className = 'video-player__seek' onMouseDown = { this . handleMouseDown } ref = { this . setSeekRef } >
< div className = 'video-player__seek' onMouseDown = { this . handleMouseDown } ref = { this . setSeekRef } >