]>
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
>
164 const style
= standalone
? {} : { left
, top
, right
, bottom
, width: `${width}%`, height: `${height}%` };
167 <div className
={classNames('media-gallery__item', { standalone
})} key
={attachment
.get('id')} style
={style
}>
177 export default class MediaGallery
extends React
.PureComponent
{
180 sensitive: PropTypes
.bool
,
181 standalone: PropTypes
.bool
,
182 media: ImmutablePropTypes
.list
.isRequired
,
183 size: PropTypes
.object
,
184 height: PropTypes
.number
.isRequired
,
185 onOpenMedia: PropTypes
.func
.isRequired
,
186 intl: PropTypes
.object
.isRequired
,
187 autoPlayGif: PropTypes
.bool
,
190 static defaultProps
= {
196 visible: !this.props
.sensitive
,
199 componentWillReceiveProps (nextProps
) {
200 if (!is(nextProps
.media
, this.props
.media
)) {
201 this.setState({ visible: !nextProps
.sensitive
});
206 this.setState({ visible: !this.state
.visible
});
209 handleClick
= (index
) => {
210 this.props
.onOpenMedia(this.props
.media
, index
);
214 const { media
, intl
, sensitive
, height
, standalone
, size
} = this.props
;
218 const standaloneEligible
= standalone
&& size
.width
&& media
.size
=== 1 && media
.getIn([0, 'meta', 'small', 'aspect']);
221 if (standaloneEligible
) {
222 style
.height
= size
.width
/ media
.getIn([0, 'meta', 'small', 'aspect']);
224 style
.height
= height
;
227 if (!this.state
.visible
) {
231 warning
= <FormattedMessage id
='status.sensitive_warning' defaultMessage
='Sensitive content' />;
233 warning
= <FormattedMessage id
='status.media_hidden' defaultMessage
='Media hidden' />;
237 <button className
='media-spoiler' onClick
={this.handleOpen
} style
={style
}>
238 <span className
='media-spoiler__warning'>{warning
}</span
>
239 <span className
='media-spoiler__trigger'><FormattedMessage id
='status.sensitive_toggle' defaultMessage
='Click to view' /></span>
243 const size
= media
.take(4).size
;
245 if (standaloneEligible
) {
246 children
= <Item standalone onClick
={this.handleClick
} attachment
={media
.get(0)} autoPlayGif
={this.props
.autoPlayGif
} />;
248 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
} />);
253 <div className
='media-gallery' style
={style
}>
254 <div className
={classNames('spoiler-button', { 'spoiler-button--visible': this.state
.visible
})}>
255 <IconButton title
={intl
.formatMessage(messages
.toggle_visible
)} icon
={this.state
.visible
? 'eye' : 'eye-slash'} overlay onClick
={this.handleOpen
} />
This page took 0.133729 seconds and 4 git commands to generate.