]>
cat aescling's git repositories - mastodon.git/blob - app/javascript/mastodon/components/media_gallery.js
1 import React
from 'react';
2 import ImmutablePropTypes
from 'react-immutable-proptypes';
3 import PropTypes
from 'prop-types';
4 import IconButton
from './icon_button';
5 import { defineMessages
, injectIntl
, FormattedMessage
} from 'react-intl';
6 import { isIOS
} from '../is_mobile';
7 import classNames
from 'classnames';
8 import sizeMe
from 'react-sizeme';
10 const messages
= defineMessages({
11 toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
14 class Item
extends React
.PureComponent
{
16 static contextTypes
= {
17 router: PropTypes
.object
,
21 attachment: ImmutablePropTypes
.map
.isRequired
,
22 standalone: PropTypes
.bool
,
23 index: PropTypes
.number
.isRequired
,
24 size: PropTypes
.number
.isRequired
,
25 onClick: PropTypes
.func
.isRequired
,
26 autoPlayGif: PropTypes
.bool
,
29 static defaultProps
= {
36 handleMouseEnter
= (e
) => {
37 if (this.hoverToPlay()) {
42 handleMouseLeave
= (e
) => {
43 if (this.hoverToPlay()) {
45 e
.target
.currentTime
= 0;
50 const { attachment
, autoPlayGif
} = this.props
;
51 return !autoPlayGif
&& attachment
.get('type') === 'gifv';
54 handleClick
= (e
) => {
55 const { index
, onClick
} = this.props
;
57 if (this.context
.router
&& e
.button
=== 0) {
66 const { attachment
, index
, size
, standalone
} = this.props
;
79 if (size
=== 4 || (size
=== 3 && index
> 0)) {
89 } else if (size
=== 3) {
92 } else if (index
> 0) {
98 } else if (index
> 1) {
101 } else if (size
=== 4) {
102 if (index
=== 0 || index
=== 2) {
106 if (index
=== 1 || index
=== 3) {
119 if (attachment
.get('type') === 'image') {
120 const previewUrl
= attachment
.get('preview_url');
121 const previewWidth
= attachment
.getIn(['meta', 'small', 'width']);
123 const originalUrl
= attachment
.get('url');
124 const originalWidth
= attachment
.getIn(['meta', 'original', 'width']);
126 const hasSize
= typeof originalWidth
=== 'number' && typeof previewWidth
=== 'number';
128 const srcSet
= hasSize
? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null;
129 const sizes
= hasSize
? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null;
133 className
='media-gallery__item-thumbnail'
134 href
={attachment
.get('remote_url') || originalUrl
}
135 onClick
={this.handleClick
}
138 <img src
={previewUrl
} srcSet
={srcSet
} sizes
={sizes
} alt
='' />
141 } else if (attachment
.get('type') === 'gifv') {
142 const autoPlay
= !isIOS() && this.props
.autoPlayGif
;
145 <div className
={classNames('media-gallery__gifv', { autoplay: autoPlay
})}>
147 className
='media-gallery__item-gifv-thumbnail'
149 src
={attachment
.get('url')}
150 onClick
={this.handleClick
}
151 onMouseEnter
={this.handleMouseEnter
}
152 onMouseLeave
={this.handleMouseLeave
}
158 <span className
='media-gallery__gifv__label'>GIF
</span
>
163 const style
= standalone
? {} : { left
, top
, right
, bottom
, width: `${width}%`, height: `${height}%` };
166 <div className
={classNames('media-gallery__item', { standalone
})} key
={attachment
.get('id')} style
={style
}>
176 export default class MediaGallery
extends React
.PureComponent
{
179 sensitive: PropTypes
.bool
,
180 standalone: PropTypes
.bool
,
181 media: ImmutablePropTypes
.list
.isRequired
,
182 size: PropTypes
.object
,
183 height: PropTypes
.number
.isRequired
,
184 onOpenMedia: PropTypes
.func
.isRequired
,
185 intl: PropTypes
.object
.isRequired
,
186 autoPlayGif: PropTypes
.bool
,
189 static defaultProps
= {
195 visible: !this.props
.sensitive
,
198 componentWillReceiveProps (nextProps
) {
199 if (nextProps
.sensitive
!== this.props
.sensitive
) {
200 this.setState({ visible: !nextProps
.sensitive
});
205 this.setState({ visible: !this.state
.visible
});
208 handleClick
= (index
) => {
209 this.props
.onOpenMedia(this.props
.media
, index
);
213 const { media
, intl
, sensitive
, height
, standalone
, size
} = this.props
;
217 const standaloneEligible
= standalone
&& size
.width
&& media
.size
=== 1 && media
.getIn([0, 'meta', 'small', 'aspect']);
220 if (standaloneEligible
) {
221 style
.height
= size
.width
/ media
.getIn([0, 'meta', 'small', 'aspect']);
223 style
.height
= height
;
226 if (!this.state
.visible
) {
230 warning
= <FormattedMessage id
='status.sensitive_warning' defaultMessage
='Sensitive content' />;
232 warning
= <FormattedMessage id
='status.media_hidden' defaultMessage
='Media hidden' />;
236 <button className
='media-spoiler' onClick
={this.handleOpen
} style
={style
}>
237 <span className
='media-spoiler__warning'>{warning
}</span
>
238 <span className
='media-spoiler__trigger'><FormattedMessage id
='status.sensitive_toggle' defaultMessage
='Click to view' /></span>
242 const size
= media
.take(4).size
;
244 if (standaloneEligible
) {
245 children
= <Item standalone onClick
={this.handleClick
} attachment
={media
.get(0)} autoPlayGif
={this.props
.autoPlayGif
} />;
247 children
= media
.take(4).map((attachment
, i
) => <Item key
={attachment
.get('id')} onClick
={this.handleClick
} attachment
={attachment
} autoPlayGif
={this.props
.autoPlayGif
} index
={i
} size
={size
} />);
252 <div className
='media-gallery' style
={style
}>
253 <div className
={classNames('spoiler-button', { 'spoiler-button--visible': this.state
.visible
})}>
254 <IconButton title
={intl
.formatMessage(messages
.toggle_visible
)} icon
={this.state
.visible
? 'eye' : 'eye-slash'} overlay onClick
={this.handleOpen
} />
This page took 0.208275 seconds and 4 git commands to generate.