// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. import PropTypes from 'prop-types'; import React, {PureComponent} from 'react'; import {Animated, ImageBackground, Image, Platform, View, StyleSheet} from 'react-native'; import thumb from '@assets/images/thumb.png'; import RetriableFastImage from '@components/retriable_fast_image'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; const AnimatedImageBackground = Animated.createAnimatedComponent(ImageBackground); const AnimatedFastImage = Animated.createAnimatedComponent(RetriableFastImage); export default class ProgressiveImage extends PureComponent { static propTypes = { id: PropTypes.string, isBackgroundImage: PropTypes.bool, children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf([PropTypes.node])]), defaultSource: PropTypes.oneOfType([PropTypes.string, PropTypes.object, PropTypes.number]), // this should be provided by the component imageUri: PropTypes.string, imageStyle: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]), inViewPort: PropTypes.bool, onError: PropTypes.func, resizeMethod: PropTypes.string, resizeMode: PropTypes.string, style: PropTypes.oneOfType([PropTypes.object, PropTypes.number, PropTypes.array]), theme: PropTypes.object.isRequired, thumbnailUri: PropTypes.string, tintDefaultSource: PropTypes.bool, }; static defaultProps = { style: {}, defaultSource: undefined, resizeMode: 'contain', }; constructor(props) { super(props); this.state = { intensity: new Animated.Value(0), showHighResImage: false, }; } componentDidUpdate(prevProps) { if (prevProps.inViewPort !== this.props.inViewPort && this.props.inViewPort) { this.startLoadingOriginalImage(); } } startLoadingOriginalImage = () => { this.setState({ showHighResImage: true, }); } onLoadImageEnd = () => { Animated.timing(this.state.intensity, { duration: 300, toValue: 100, useNativeDriver: true, }).start(); } render() { const { defaultSource, id, imageStyle, imageUri, isBackgroundImage, onError, resizeMode, resizeMethod, style, theme, thumbnailUri, tintDefaultSource, } = this.props; const {showHighResImage} = this.state; let DefaultComponent; let ImageComponent; if (isBackgroundImage) { DefaultComponent = ImageBackground; ImageComponent = AnimatedImageBackground; } else { DefaultComponent = AnimatedFastImage; ImageComponent = AnimatedFastImage; } const styles = getStyleSheet(theme); if (isBackgroundImage) { return ( {this.props.children} ); } if (defaultSource) { return ( {this.props.children} ); } const opacity = this.state.intensity.interpolate({ inputRange: [20, 100], outputRange: [0.2, 1], }); const defaultOpacity = this.state.intensity.interpolate({ inputRange: [0, 100], outputRange: [0.5, 0], }); const containerStyle = { backgroundColor: changeOpacity(theme.centerChannelColor, defaultOpacity), }; let thumbnail; let image; if (thumbnailUri) { const ImageElement = thumbnailUri.startsWith('data:') ? Image : ImageComponent; thumbnail = ( {this.props.children} ); if (showHighResImage) { image = ( {this.props.children} ); } } else { thumbnail = ( ); image = ( {this.props.children} ); } return ( {thumbnail} {image} ); } } const getStyleSheet = makeStyleSheetFromTheme((theme) => { return { defaultImageContainer: { alignItems: 'center', justifyContent: 'center', backgroundColor: changeOpacity(theme.centerChannelColor, 0.08), }, defaultImageTint: { flex: 1, tintColor: changeOpacity(theme.centerChannelColor, 0.2), }, }; });