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);