import React from 'react';
import PropTypes from 'prop-types';
-import { defineMessages, injectIntl } from 'react-intl';
+import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import { formatTime } from 'flavours/glitch/features/video';
import Icon from 'flavours/glitch/components/icon';
import classNames from 'classnames';
import { getPointerPosition, fileNameFromURL } from 'flavours/glitch/features/video';
import { debounce } from 'lodash';
import Visualizer from './visualizer';
+import { displayMedia, useBlurhash } from 'flavours/glitch/util/initial_state';
+import Blurhash from 'flavours/glitch/components/blurhash';
+import { is } from 'immutable';
const messages = defineMessages({
play: { id: 'video.play', defaultMessage: 'Play' },
mute: { id: 'video.mute', defaultMessage: 'Mute sound' },
unmute: { id: 'video.unmute', defaultMessage: 'Unmute sound' },
download: { id: 'video.download', defaultMessage: 'Download file' },
+ hide: { id: 'audio.hide', defaultMessage: 'Hide audio' },
});
const TICK_SIZE = 10;
duration: PropTypes.number,
width: PropTypes.number,
height: PropTypes.number,
+ sensitive: PropTypes.bool,
editable: PropTypes.bool,
fullscreen: PropTypes.bool,
intl: PropTypes.object.isRequired,
+ blurhash: PropTypes.string,
cacheWidth: PropTypes.func,
+ visible: PropTypes.bool,
+ onToggleVisibility: PropTypes.func,
backgroundColor: PropTypes.string,
foregroundColor: PropTypes.string,
accentColor: PropTypes.string,
muted: false,
volume: 0.5,
dragging: false,
+ revealed: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
};
constructor (props) {
backgroundColor: this.props.backgroundColor,
foregroundColor: this.props.foregroundColor,
accentColor: this.props.accentColor,
+ sensitive: this.props.sensitive,
+ visible: this.props.visible,
};
}
}
}
+ componentWillReceiveProps (nextProps) {
+ if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
+ this.setState({ revealed: nextProps.visible });
+ }
+ }
+
componentWillUnmount () {
window.removeEventListener('scroll', this.handleScroll);
window.removeEventListener('resize', this.handleResize);
});
}
+ toggleReveal = () => {
+ if (this.props.onToggleVisibility) {
+ this.props.onToggleVisibility();
+ } else {
+ this.setState({ revealed: !this.state.revealed });
+ }
+ }
+
handleVolumeMouseDown = e => {
document.addEventListener('mousemove', this.handleMouseVolSlide, true);
document.addEventListener('mouseup', this.handleVolumeMouseUp, true);
}
render () {
- const { src, intl, alt, editable, autoPlay } = this.props;
- const { paused, muted, volume, currentTime, duration, buffer, dragging } = this.state;
+ const { src, intl, alt, editable, autoPlay, sensitive, blurhash } = this.props;
+ const { paused, muted, volume, currentTime, duration, buffer, dragging, revealed } = this.state;
const progress = Math.min((currentTime / duration) * 100, 100);
+ let warning;
+ if (sensitive) {
+ warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
+ } else {
+ warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
+ }
+
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} tabIndex='0' onKeyDown={this.handleKeyDown}>
- <audio
+ <div className={classNames('audio-player', { editable, inactive: !revealed })} 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}>
+
+ <Blurhash
+ hash={blurhash}
+ className={classNames('media-gallery__preview', {
+ 'media-gallery__preview--hidden': revealed,
+ })}
+ dummy={!useBlurhash}
+ />
+
+ {(revealed || editable) && <audio
src={src}
ref={this.setAudioRef}
preload={autoPlay ? 'auto' : 'none'}
onProgress={this.handleProgress}
onLoadedData={this.handleLoadedData}
crossOrigin='anonymous'
- />
+ />}
<canvas
role='button'
aria-label={alt}
/>
- <img
+ <div className={classNames('spoiler-button', { 'spoiler-button--hidden': revealed || editable })}>
+ <button type='button' className='spoiler-button__overlay' onClick={this.toggleReveal}>
+ <span className='spoiler-button__overlay__label'>{warning}</span>
+ </button>
+ </div>
+
+ {(revealed || editable) && <img
src={this.props.poster}
alt=''
width={(this._getRadius() - TICK_SIZE) * 2}
height={(this._getRadius() - TICK_SIZE) * 2}
style={{ position: 'absolute', left: this._getCX(), top: this._getCY(), transform: 'translate(-50%, -50%)', borderRadius: '50%', pointerEvents: 'none' }}
- />
+ />}
<div className='video-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}>
<div className='video-player__seek__buffer' style={{ width: `${buffer}%` }} />
</div>
<div className='video-player__buttons right'>
+ {!editable && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>}
<a title={intl.formatMessage(messages.download)} aria-label={intl.formatMessage(messages.download)} className='video-player__download__icon player-button' href={this.props.src} download>
<Icon id={'download'} fixedWidth />
</a>