diff --git a/native/media/loadable-image.react.js b/native/media/loadable-image.react.js new file mode 100644 --- /dev/null +++ b/native/media/loadable-image.react.js @@ -0,0 +1,71 @@ +// @flow + +import { Image } from 'expo-image'; +import * as React from 'react'; +import { View, StyleSheet, ActivityIndicator } from 'react-native'; +import type { ImageSource } from 'react-native/Libraries/Image/ImageSource'; + +import type { ImageStyle } from '../types/styles.js'; + +type Props = { + +source: ?ImageSource, + +onLoad: () => void, + +spinnerColor: string, + +style: ImageStyle, + +invisibleLoad: boolean, +}; +function LoadableImage(props: Props): React.Node { + const { source, onLoad: onLoadProp } = props; + + const [loaded, setLoaded] = React.useState(false); + + const onLoad = React.useCallback(() => { + setLoaded(true); + onLoadProp && onLoadProp(); + }, [onLoadProp]); + + const invisibleStyle = React.useMemo( + () => [props.style, styles.invisible], + [props.style], + ); + + if (!loaded && props.invisibleLoad) { + return ; + } + + let spinner; + if (!loaded) { + spinner = ( + + + + ); + } + + return ( + + {spinner} + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + invisible: { + opacity: 0, + }, + spinnerContainer: { + alignItems: 'center', + bottom: 0, + justifyContent: 'center', + left: 0, + position: 'absolute', + right: 0, + top: 0, + }, +}); + +export default LoadableImage; diff --git a/native/media/remote-image.react.js b/native/media/remote-image.react.js --- a/native/media/remote-image.react.js +++ b/native/media/remote-image.react.js @@ -1,11 +1,10 @@ // @flow -import { Image } from 'expo-image'; import * as React from 'react'; -import { View, StyleSheet, ActivityIndicator } from 'react-native'; import { type ConnectionStatus } from 'lib/types/socket-types.js'; +import LoadableImage from './loadable-image.react.js'; import { useSelector } from '../redux/redux-utils.js'; import type { ImageStyle } from '../types/styles.js'; @@ -22,17 +21,16 @@ }; type State = { +attempt: number, - +loaded: boolean, }; class RemoteImage extends React.PureComponent { + loaded: boolean = false; state: State = { attempt: 0, - loaded: false, }; - componentDidUpdate(prevProps: Props, prevState: State) { + componentDidUpdate(prevProps: Props) { if ( - !this.state.loaded && + !this.loaded && this.props.connectionStatus === 'connected' && prevProps.connectionStatus !== 'connected' ) { @@ -40,73 +38,30 @@ attempt: otherPrevState.attempt + 1, })); } - if (this.state.loaded && !prevState.loaded) { - this.props.onLoad && this.props.onLoad(this.props.uri); - } } render() { - const source = { uri: this.props.uri }; - if (this.state.loaded) { - return ( - - ); - } - - if (this.props.invisibleLoad) { - return ( - - ); - } + const { style, spinnerColor, invisibleLoad, uri } = this.props; + const source = { uri }; return ( - - - - - - + ); } onLoad = () => { - this.setState({ loaded: true }); + this.loaded = true; + this.props.onLoad && this.props.onLoad(this.props.uri); }; } -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - invisible: { - opacity: 0, - }, - spinnerContainer: { - alignItems: 'center', - bottom: 0, - justifyContent: 'center', - left: 0, - position: 'absolute', - right: 0, - top: 0, - }, -}); - const ConnectedRemoteImage: React.ComponentType = React.memo(function ConnectedRemoteImage(props: BaseProps) { const connectionStatus = useSelector(state => state.connection.status);