diff --git a/CachedImage.js b/CachedImage.js index daf03c8..c2a9c93 100644 --- a/CachedImage.js +++ b/CachedImage.js @@ -1,232 +1,269 @@ -'use strict'; +"use strict"; -const _ = require('lodash'); -const React = require('react'); -const ReactNative = require('react-native'); +const _ = require("lodash"); +const React = require("react"); +const ReactNative = require("react-native"); -const PropTypes = require('prop-types'); +const PropTypes = require("prop-types"); -const ImageCacheManagerOptionsPropTypes = require('./ImageCacheManagerOptionsPropTypes'); +const ImageCacheManagerOptionsPropTypes = require("./ImageCacheManagerOptionsPropTypes"); const flattenStyle = ReactNative.StyleSheet.flatten; -const ImageCacheManager = require('./ImageCacheManager'); +const ImageCacheManager = require("./ImageCacheManager"); const { - View, - ImageBackground, - ActivityIndicator, - NetInfo, - Platform, - StyleSheet, + View, + ImageBackground, + ActivityIndicator, + + Platform, + StyleSheet, } = ReactNative; +const NetInfo = require("@react-native-community/netinfo"); + const styles = StyleSheet.create({ - image: { - backgroundColor: 'transparent' - }, - loader: { - backgroundColor: 'transparent', - }, - loaderPlaceholder: { - backgroundColor: 'transparent', - alignItems: 'center', - justifyContent: 'center' - } + image: { + backgroundColor: "transparent", + }, + loader: { + backgroundColor: "transparent", + }, + loaderPlaceholder: { + backgroundColor: "transparent", + alignItems: "center", + justifyContent: "center", + }, }); function getImageProps(props) { - return _.omit(props, ['source', 'defaultSource', 'fallbackSource', 'LoadingIndicator', 'activityIndicatorProps', 'style', 'useQueryParamsInCacheKey', 'renderImage', 'resolveHeaders']); + return _.omit(props, [ + "source", + "defaultSource", + "fallbackSource", + "LoadingIndicator", + "activityIndicatorProps", + "style", + "useQueryParamsInCacheKey", + "renderImage", + "resolveHeaders", + ]); } -const CACHED_IMAGE_REF = 'cachedImage'; +const CACHED_IMAGE_REF = "cachedImage"; class CachedImage extends React.Component { - - static propTypes = { - renderImage: PropTypes.func.isRequired, - activityIndicatorProps: PropTypes.object.isRequired, - - // ImageCacheManager options - ...ImageCacheManagerOptionsPropTypes, - }; - - static defaultProps = { - renderImage: props => (), - activityIndicatorProps: {}, + static propTypes = { + renderImage: PropTypes.func.isRequired, + activityIndicatorProps: PropTypes.object.isRequired, + + // ImageCacheManager options + ...ImageCacheManagerOptionsPropTypes, + }; + + static defaultProps = { + renderImage: (props) => ( + + ), + activityIndicatorProps: {}, + }; + + static contextTypes = { + getImageCacheManager: PropTypes.func, + }; + + constructor(props) { + super(props); + this._isMounted = false; + this.state = { + isCacheable: true, + cachedImagePath: null, + networkAvailable: true, }; - static contextTypes = { - getImageCacheManager: PropTypes.func, - }; - - constructor(props) { - super(props); - this._isMounted = false; - this.state = { - isCacheable: true, - cachedImagePath: null, - networkAvailable: true - }; - - this.getImageCacheManagerOptions = this.getImageCacheManagerOptions.bind(this); - this.getImageCacheManager = this.getImageCacheManager.bind(this); - this.safeSetState = this.safeSetState.bind(this); - this.handleConnectivityChange = this.handleConnectivityChange.bind(this); - this.processSource = this.processSource.bind(this); - this.renderLoader = this.renderLoader.bind(this); - } - - componentWillMount() { - this._isMounted = true; - NetInfo.isConnected.addEventListener('connectionChange', this.handleConnectivityChange); - // initial - NetInfo.isConnected.fetch() - .then(isConnected => { - this.safeSetState({ - networkAvailable: isConnected - }); - }); - - this.processSource(this.props.source); - } - - componentWillUnmount() { - this._isMounted = false; - NetInfo.isConnected.removeEventListener('connectionChange', this.handleConnectivityChange); + this.getImageCacheManagerOptions = + this.getImageCacheManagerOptions.bind(this); + this.getImageCacheManager = this.getImageCacheManager.bind(this); + this.safeSetState = this.safeSetState.bind(this); + this.handleConnectivityChange = this.handleConnectivityChange.bind(this); + this.processSource = this.processSource.bind(this); + this.renderLoader = this.renderLoader.bind(this); + } + + componentWillMount() { + this._isMounted = true; + + NetInfo.addEventListener((state) => { + this.handleConnectivityChange(state.isConnected); + }); + + // NetInfo.addEventListener('connectionChange', this.handleConnectivityChange); + + // initial + NetInfo.fetch().then((state) => { + this.safeSetState({ + networkAvailable: state.isConnected, + }); + }); + + this.processSource(this.props.source); + } + + componentWillUnmount() { + this._isMounted = false; + // NetInforemoveEventListener(state => { + // this.handleConnectivityChange(state.isConnected) + // }); + // NetInfo.removeEventListener('connectionChange', this.handleConnectivityChange); + } + + componentWillReceiveProps(nextProps) { + if (!_.isEqual(this.props.source, nextProps.source)) { + this.processSource(nextProps.source); } + } - componentWillReceiveProps(nextProps) { - if (!_.isEqual(this.props.source, nextProps.source)) { - this.processSource(nextProps.source); - } + setNativeProps(nativeProps) { + try { + this.refs[CACHED_IMAGE_REF].setNativeProps(nativeProps); + } catch (e) { + console.error(e); } + } - setNativeProps(nativeProps) { - try { - this.refs[CACHED_IMAGE_REF].setNativeProps(nativeProps); - } catch (e) { - console.error(e); - } - } - - getImageCacheManagerOptions() { - return _.pick(this.props, _.keys(ImageCacheManagerOptionsPropTypes)); - } + getImageCacheManagerOptions() { + return _.pick(this.props, _.keys(ImageCacheManagerOptionsPropTypes)); + } - getImageCacheManager() { - // try to get ImageCacheManager from context - if (this.context && this.context.getImageCacheManager) { - return this.context.getImageCacheManager(); - } - // create a new one if context is not available - const options = this.getImageCacheManagerOptions(); - return ImageCacheManager(options); + getImageCacheManager() { + // try to get ImageCacheManager from context + if (this.context && this.context.getImageCacheManager) { + return this.context.getImageCacheManager(); } - - safeSetState(newState) { - if (!this._isMounted) { - return; - } - return this.setState(newState); + // create a new one if context is not available + const options = this.getImageCacheManagerOptions(); + return ImageCacheManager(options); + } + + safeSetState(newState) { + if (!this._isMounted) { + return; } - - handleConnectivityChange(isConnected) { + return this.setState(newState); + } + + handleConnectivityChange(isConnected) { + this.safeSetState({ + networkAvailable: isConnected, + }); + } + + processSource(source) { + const url = _.get(source, ["uri"], null); + const options = this.getImageCacheManagerOptions(); + const imageCacheManager = this.getImageCacheManager(); + + imageCacheManager + .downloadAndCacheUrl(url, options) + .then((cachedImagePath) => { this.safeSetState({ - networkAvailable: isConnected + cachedImagePath, }); - } + }) + .catch((err) => { + // console.warn(err); + this.safeSetState({ + cachedImagePath: null, + isCacheable: false, + }); + }); + } - processSource(source) { - const url = _.get(source, ['uri'], null); - const options = this.getImageCacheManagerOptions(); - const imageCacheManager = this.getImageCacheManager(); - - imageCacheManager.downloadAndCacheUrl(url, options) - .then(cachedImagePath => { - this.safeSetState({ - cachedImagePath - }); - }) - .catch(err => { - // console.warn(err); - this.safeSetState({ - cachedImagePath: null, - isCacheable: false - }); - }); + render() { + if (this.state.isCacheable && !this.state.cachedImagePath) { + return this.renderLoader(); } - - render() { - if (this.state.isCacheable && !this.state.cachedImagePath) { - return this.renderLoader(); - } - const props = getImageProps(this.props); - const style = this.props.style || styles.image; - const source = (this.state.isCacheable && this.state.cachedImagePath) ? { - uri: 'file://' + this.state.cachedImagePath - } : this.props.source; - if (this.props.fallbackSource && !this.state.cachedImagePath) { - return this.props.renderImage({ - ...props, - key: `${props.key || source.uri}error`, - style, - source: this.props.fallbackSource - }); - } - return this.props.renderImage({ - ...props, - key: props.key || source.uri, - style, - source - }); + const props = getImageProps(this.props); + const style = this.props.style || styles.image; + const source = + this.state.isCacheable && this.state.cachedImagePath + ? { + uri: "file://" + this.state.cachedImagePath, + } + : this.props.source; + if (this.props.fallbackSource && !this.state.cachedImagePath) { + return this.props.renderImage({ + ...props, + key: `${props.key || source.uri}error`, + style, + source: this.props.fallbackSource, + }); } - - renderLoader() { - const imageProps = getImageProps(this.props); - const imageStyle = [this.props.style, styles.loaderPlaceholder]; - - const activityIndicatorProps = _.omit(this.props.activityIndicatorProps, ['style']); - const activityIndicatorStyle = this.props.activityIndicatorProps.style || styles.loader; - - const LoadingIndicator = this.props.loadingIndicator; - - const source = this.props.defaultSource; - - // if the imageStyle has borderRadius it will break the loading image view on android - // so we only show the ActivityIndicator - if (!source || (Platform.OS === 'android' && flattenStyle(imageStyle).borderRadius)) { - if (LoadingIndicator) { - return ( - - - - ); - } - return ( - - ); - } - // otherwise render an image with the defaultSource with the ActivityIndicator on top of it - return this.props.renderImage({ - ...imageProps, - style: imageStyle, - key: source.uri, - source, - children: ( - LoadingIndicator - ? - - - : - ) - }); + return this.props.renderImage({ + ...props, + key: props.key || source.uri, + style, + source, + }); + } + + renderLoader() { + const imageProps = getImageProps(this.props); + const imageStyle = [this.props.style, styles.loaderPlaceholder]; + + const activityIndicatorProps = _.omit(this.props.activityIndicatorProps, [ + "style", + ]); + const activityIndicatorStyle = + this.props.activityIndicatorProps.style || styles.loader; + + const LoadingIndicator = this.props.loadingIndicator; + + const source = this.props.defaultSource; + + // if the imageStyle has borderRadius it will break the loading image view on android + // so we only show the ActivityIndicator + if ( + !source || + (Platform.OS === "android" && flattenStyle(imageStyle).borderRadius) + ) { + if (LoadingIndicator) { + return ( + + + + ); + } + return ( + + ); } - + // otherwise render an image with the defaultSource with the ActivityIndicator on top of it + return this.props.renderImage({ + ...imageProps, + style: imageStyle, + key: source.uri, + source, + children: LoadingIndicator ? ( + + + + ) : ( + + ), + }); + } } module.exports = CachedImage;