This repository has been archived on 2019-05-14. You can view files and clone it, but cannot push or open issues or pull requests.
mastodon/app/assets/javascripts/components/components/media_gallery.jsx
Eugen Rochko caf5b8e975 Fix #431 - convert gif to webm during upload. Web UI treats them like it did
before. In the API, attachments now can be either image, video or gifv. Gifv
is to be treated like images in terms of behaviour, but are videos by file
type.
2017-03-04 23:02:24 +01:00

246 lines
5.5 KiB
JavaScript

import ImmutablePropTypes from 'react-immutable-proptypes';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import IconButton from './icon_button';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
const messages = defineMessages({
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' }
});
const outerStyle = {
marginTop: '8px',
overflow: 'hidden',
width: '100%',
boxSizing: 'border-box',
position: 'relative'
};
const spoilerStyle = {
textAlign: 'center',
height: '100%',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column'
};
const spoilerSpanStyle = {
display: 'block',
fontSize: '14px',
};
const spoilerSubSpanStyle = {
display: 'block',
fontSize: '11px',
fontWeight: '500'
};
const spoilerButtonStyle = {
position: 'absolute',
top: '6px',
left: '8px',
zIndex: '100'
};
const itemStyle = {
boxSizing: 'border-box',
position: 'relative',
float: 'left',
border: 'none',
display: 'block'
};
const thumbStyle = {
display: 'block',
width: '100%',
height: '100%',
textDecoration: 'none',
backgroundSize: 'cover',
cursor: 'zoom-in'
};
const gifvThumbStyle = {
position: 'relative',
zIndex: '1',
width: '100%',
height: '100%',
objectFit: 'cover',
top: '50%',
transform: 'translateY(-50%)',
cursor: 'zoom-in'
};
const Item = React.createClass({
propTypes: {
attachment: ImmutablePropTypes.map.isRequired,
index: React.PropTypes.number.isRequired,
size: React.PropTypes.number.isRequired,
onClick: React.PropTypes.func.isRequired
},
mixins: [PureRenderMixin],
handleClick (e) {
const { index, onClick } = this.props;
if (e.button === 0) {
e.preventDefault();
onClick(index);
}
e.stopPropagation();
},
render () {
const { attachment, index, size } = this.props;
let width = 50;
let height = 100;
let top = 'auto';
let left = 'auto';
let bottom = 'auto';
let right = 'auto';
if (size === 1) {
width = 100;
}
if (size === 4 || (size === 3 && index > 0)) {
height = 50;
}
if (size === 2) {
if (index === 0) {
right = '2px';
} else {
left = '2px';
}
} else if (size === 3) {
if (index === 0) {
right = '2px';
} else if (index > 0) {
left = '2px';
}
if (index === 1) {
bottom = '2px';
} else if (index > 1) {
top = '2px';
}
} else if (size === 4) {
if (index === 0 || index === 2) {
right = '2px';
}
if (index === 1 || index === 3) {
left = '2px';
}
if (index < 2) {
bottom = '2px';
} else {
top = '2px';
}
}
let thumbnail = '';
if (attachment.get('type') === 'image') {
thumbnail = (
<a
href={attachment.get('remote_url') ? attachment.get('remote_url') : attachment.get('url')}
onClick={this.handleClick}
target='_blank'
style={{ background: `url(${attachment.get('preview_url')}) no-repeat center`, ...thumbStyle }}
/>
);
} else if (attachment.get('type') === 'gifv') {
thumbnail = (
<video
src={attachment.get('url')}
onClick={this.handleClick}
autoPlay={true}
loop={true}
muted={true}
style={gifvThumbStyle}
/>
);
}
return (
<div key={attachment.get('id')} style={{ ...itemStyle, left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
{thumbnail}
</div>
);
}
});
const MediaGallery = React.createClass({
getInitialState () {
return {
visible: !this.props.sensitive
};
},
propTypes: {
sensitive: React.PropTypes.bool,
media: ImmutablePropTypes.list.isRequired,
height: React.PropTypes.number.isRequired,
onOpenMedia: React.PropTypes.func.isRequired,
intl: React.PropTypes.object.isRequired
},
mixins: [PureRenderMixin],
handleOpen (e) {
this.setState({ visible: !this.state.visible });
},
handleClick (index) {
this.props.onOpenMedia(this.props.media, index);
},
render () {
const { media, intl, sensitive } = this.props;
let children;
if (!this.state.visible) {
let warning;
if (sensitive) {
warning = <FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' />;
} else {
warning = <FormattedMessage id='status.media_hidden' defaultMessage='Media hidden' />;
}
children = (
<div style={spoilerStyle} className='media-spoiler' onClick={this.handleOpen}>
<span style={spoilerSpanStyle}>{warning}</span>
<span style={spoilerSubSpanStyle}><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
</div>
);
} else {
const size = media.take(4).size;
children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} />);
}
return (
<div style={{ ...outerStyle, height: `${this.props.height}px` }}>
<div style={spoilerButtonStyle}>
<IconButton title={intl.formatMessage(messages.toggle_visible)} icon={this.state.visible ? 'eye' : 'eye-slash'} onClick={this.handleOpen} />
</div>
{children}
</div>
);
}
});
export default injectIntl(MediaGallery);