]>
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 { is
} from 'immutable';
5 import IconButton
from './icon_button';
6 import { defineMessages
, injectIntl
, FormattedMessage
} from 'react-intl';
7 import { isIOS
} from '../is_mobile';
8 import classNames
from 'classnames';
9 import sizeMe
from 'react-sizeme';
11 const messages
= defineMessages({
12 toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
15 class Item
extends React
.PureComponent
{
17 static contextTypes
= {
18 router: PropTypes
.object
,
22 attachment: ImmutablePropTypes
.map
.isRequired
,
23 standalone: PropTypes
.bool
,
24 index: PropTypes
.number
.isRequired
,
25 size: PropTypes
.number
.isRequired
,
26 onClick: PropTypes
.func
.isRequired
,
27 autoPlayGif: PropTypes
.bool
,
30 static defaultProps
= {
37 handleMouseEnter
= (e
) => {
38 if (this.hoverToPlay()) {
43 handleMouseLeave
= (e
) => {
44 if (this.hoverToPlay()) {
46 e
.target
.currentTime
= 0;
51 const { attachment
, autoPlayGif
} = this.props
;
52 return !autoPlayGif
&& attachment
.get('type') === 'gifv';
55 handleClick
= (e
) => {
56 const { index
, onClick
} = this.props
;
58 if (this.context
.router
&& e
.button
=== 0) {
67 const { attachment
, index
, size
, standalone
} = this.props
;
80 if (size
=== 4 || (size
=== 3 && index
> 0)) {
90 } else if (size
=== 3) {
93 } else if (index
> 0) {
99 } else if (index
> 1) {
102 } else if (size
=== 4) {
103 if (index
=== 0 || index
=== 2) {
107 if (index
=== 1 || index
=== 3) {
120 if (attachment
.get('type') === 'image') {
121 const previewUrl
= attachment
.get('preview_url');
122 const previewWidth
= attachment
.getIn(['meta', 'small', 'width']);
124 const originalUrl
= attachment
.get('url');
125 const originalWidth
= attachment
.getIn(['meta', 'original', 'width']);
127 const hasSize
= typeof originalWidth
=== 'number' && typeof previewWidth
=== 'number';
129 const srcSet
= hasSize
? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null;
130 const sizes
= hasSize
? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null;
134 className
='media-gallery__item-thumbnail'
135 href
={attachment
.get('remote_url') || originalUrl
}
136 onClick
={this.handleClick
}
139 <img src
={previewUrl
} srcSet
={srcSet
} sizes
={sizes
} alt
='' />
142 } else if (attachment
.get('type') === 'gifv') {
143 const autoPlay
= !isIOS() && this.props
.autoPlayGif
;
146 <div className
={classNames('media-gallery__gifv', { autoplay: autoPlay
})}>
148 className
='media-gallery__item-gifv-thumbnail'
150 src
={attachment
.get('url')}
151 onClick
={this.handleClick
}
152 onMouseEnter
={this.handleMouseEnter
}
153 onMouseLeave
={this.handleMouseLeave
}
159 <span className
='media-gallery__gifv__label'>GIF
</span
>
165 <div className
={classNames('media-gallery__item', { standalone
})} key
={attachment
.get('id')} style
={{ left: left
, top: top
, right: right
, bottom: bottom
, width: `${width}%`, height: `${height}%` }}>
175 export default class MediaGallery
extends React
.PureComponent
{
178 sensitive: PropTypes
.bool
,
179 standalone: PropTypes
.bool
,
180 media: ImmutablePropTypes
.list
.isRequired
,
181 size: PropTypes
.object
,
182 height: PropTypes
.number
.isRequired
,
183 onOpenMedia: PropTypes
.func
.isRequired
,
184 intl: PropTypes
.object
.isRequired
,
185 autoPlayGif: PropTypes
.bool
,
188 static defaultProps
= {
194 visible: !this.props
.sensitive
,
197 componentWillReceiveProps (nextProps
) {
198 if (!is(nextProps
.media
, this.props
.media
)) {
199 this.setState({ visible: !nextProps
.sensitive
});
204 this.setState({ visible: !this.state
.visible
});
207 handleClick
= (index
) => {
208 this.props
.onOpenMedia(this.props
.media
, index
);
212 const { media
, intl
, sensitive
, height
, standalone
, size
} = this.props
;
216 const standaloneEligible
= standalone
&& size
.width
&& media
.size
=== 1 && media
.getIn([0, 'meta', 'small', 'aspect']);
219 if (standaloneEligible
) {
220 style
.height
= size
.width
/ media
.getIn([0, 'meta', 'small', 'aspect']);
222 style
.height
= height
;
225 if (!this.state
.visible
) {
229 warning
= <FormattedMessage id
='status.sensitive_warning' defaultMessage
='Sensitive content' />;
231 warning
= <FormattedMessage id
='status.media_hidden' defaultMessage
='Media hidden' />;
235 <button className
='media-spoiler' onClick
={this.handleOpen
} style
={style
}>
236 <span className
='media-spoiler__warning'>{warning
}</span
>
237 <span className
='media-spoiler__trigger'><FormattedMessage id
='status.sensitive_toggle' defaultMessage
='Click to view' /></span>
241 const size
= media
.take(4).size
;
243 if (standaloneEligible
) {
244 children
= <Item standalone onClick
={this.handleClick
} attachment
={media
.get(0)} autoPlayGif
={this.props
.autoPlayGif
} />;
246 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
} />);
251 <div className
='media-gallery' style
={style
}>
252 <div className
={classNames('spoiler-button', { 'spoiler-button--visible': this.state
.visible
})}>
253 <IconButton title
={intl
.formatMessage(messages
.toggle_visible
)} icon
={this.state
.visible
? 'eye' : 'eye-slash'} overlay onClick
={this.handleOpen
} />
This page took 0.103344 seconds and 4 git commands to generate.