diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/javascript/mastodon/features/ui/components/image_loader.js | 45 | ||||
-rw-r--r-- | app/javascript/mastodon/features/ui/components/media_modal.js | 4 | ||||
-rw-r--r-- | app/javascript/styles/components.scss | 15 |
3 files changed, 57 insertions, 7 deletions
diff --git a/app/javascript/mastodon/features/ui/components/image_loader.js b/app/javascript/mastodon/features/ui/components/image_loader.js new file mode 100644 index 000000000..af2870517 --- /dev/null +++ b/app/javascript/mastodon/features/ui/components/image_loader.js @@ -0,0 +1,45 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +class ImageLoader extends React.PureComponent { + + static propTypes = { + src: PropTypes.string.isRequired, + } + + state = { + loading: true, + error: false, + } + + componentWillMount() { + this.loadImage(this.props.src); + } + + componentWillReceiveProps(props) { + this.loadImage(props.src); + } + + loadImage(src) { + const image = new Image(); + image.onerror = () => this.setState({loading: false, error: true}); + image.onload = () => this.setState({loading: false, error: false}); + image.src = src; + this.lastSrc = src; + this.setState({loading: true}); + } + + render() { + const { src } = this.props; + const { loading, error } = this.state; + + // TODO: handle image error state + + const imageClass = `image-loader__img ${loading ? 'image-loader__img-loading' : ''}`; + + return <img className={imageClass} src={src} />; // eslint-disable-line jsx-a11y/img-has-alt + } + +} + +export default ImageLoader; diff --git a/app/javascript/mastodon/features/ui/components/media_modal.js b/app/javascript/mastodon/features/ui/components/media_modal.js index a8912841b..effa0aea3 100644 --- a/app/javascript/mastodon/features/ui/components/media_modal.js +++ b/app/javascript/mastodon/features/ui/components/media_modal.js @@ -3,10 +3,10 @@ import LoadingIndicator from '../../../components/loading_indicator'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import ExtendedVideoPlayer from '../../../components/extended_video_player'; -import ImageLoader from 'react-imageloader'; import { defineMessages, injectIntl } from 'react-intl'; import IconButton from '../../../components/icon_button'; import ImmutablePureComponent from 'react-immutable-pure-component'; +import ImageLoader from './image_loader'; const messages = defineMessages({ close: { id: 'lightbox.close', defaultMessage: 'Close' }, @@ -73,7 +73,7 @@ class MediaModal extends ImmutablePureComponent { } if (attachment.get('type') === 'image') { - content = <ImageLoader src={url} imgProps={{ style: { display: 'block' } }} />; + content = <ImageLoader src={url} />; } else if (attachment.get('type') === 'gifv') { content = <ExtendedVideoPlayer src={url} muted={true} controls={false} />; } diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss index 4afbd12cf..e396f04cc 100644 --- a/app/javascript/styles/components.scss +++ b/app/javascript/styles/components.scss @@ -1137,13 +1137,13 @@ } } -.transparent-background, -.imageloader { - background: url('../images/void.png'); +.image-loader__img { + transition: opacity 0.3s linear; + opacity: 1; } -.imageloader { - display: block; +.image-loader__img-loading { + opacity: 0.7; } .navigation-bar { @@ -2852,6 +2852,11 @@ button.icon-button.active i.fa-retweet { max-width: 80vw; max-height: 80vh; } + + img { + display: block; + background: url('../images/void.png') repeat; + } } .media-modal__close { |