Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] Add image loading indicator #8238

Merged
merged 9 commits into from
Apr 3, 2022
37 changes: 36 additions & 1 deletion src/components/ImageView/index.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
View, Image, Pressable,
View, Image, Pressable, ActivityIndicator, StyleSheet,
} from 'react-native';
import styles from '../../styles/styles';
import * as StyleUtils from '../../styles/StyleUtils';
import canUseTouchScreen from '../../libs/canUseTouchscreen';
import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';
import themeColors from '../../styles/themes/default';

const propTypes = {
/** URL to full-sized image */
Expand All @@ -23,7 +24,10 @@ class ImageView extends PureComponent {
this.onContainerPressIn = this.onContainerPressIn.bind(this);
this.onContainerPress = this.onContainerPress.bind(this);
this.onContainerPressOut = this.onContainerPressOut.bind(this);
this.imageLoadingStart = this.imageLoadingStart.bind(this);
this.imageLoadingEnd = this.imageLoadingEnd.bind(this);
this.state = {
isLoading: false,
containerHeight: 0,
containerWidth: 0,
isZoomed: false,
Expand Down Expand Up @@ -227,6 +231,14 @@ class ImageView extends PureComponent {
this.setState(prevState => ({isDragging: prevState.isMouseDown}));
}

imageLoadingStart() {
this.setState({isLoading: true});
}

imageLoadingEnd() {
this.setState({isLoading: false});
}

render() {
if (this.canUseTouchScreen) {
return (
Expand All @@ -240,7 +252,18 @@ class ImageView extends PureComponent {
styles.h100,
]}
resizeMode="contain"
onLoadStart={this.imageLoadingStart}
onLoadEnd={this.imageLoadingEnd}
/>
{this.state.isLoading && (
<View style={{...StyleSheet.absoluteFillObject}}>
<ActivityIndicator
size="large"
color={themeColors.spinner}
style={[styles.flex1]}
/>
</View>
)}
</View>
);
}
Expand Down Expand Up @@ -274,8 +297,20 @@ class ImageView extends PureComponent {
styles.w100,
]}
resizeMode="contain"
onLoadStart={this.imageLoadingStart}
onLoadEnd={this.imageLoadingEnd}
/>
</Pressable>

{this.state.isLoading && (
<View style={{...StyleSheet.absoluteFillObject}}>
<ActivityIndicator
size="large"
color={themeColors.spinner}
style={[styles.flex1]}
/>
</View>
)}
</View>
);
}
Expand Down
35 changes: 34 additions & 1 deletion src/components/ImageView/index.native.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {View, InteractionManager, PanResponder} from 'react-native';
import {
View, InteractionManager, PanResponder, ActivityIndicator, StyleSheet,
} from 'react-native';
import Image from 'react-native-fast-image';
import ImageZoom from 'react-native-image-pan-zoom';
import ImageSize from 'react-native-image-size';
Expand All @@ -9,6 +11,7 @@ import styles from '../../styles/styles';
import * as StyleUtils from '../../styles/StyleUtils';
import variables from '../../styles/variables';
import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions';
import themeColors from '../../styles/themes/default';

/**
* On the native layer, we use a image library to handle zoom functionality
Expand All @@ -25,6 +28,7 @@ class ImageView extends PureComponent {
super(props);

this.state = {
isLoading: false,
thumbnailWidth: 100,
thumbnailHeight: 100,
imageWidth: undefined,
Expand All @@ -43,6 +47,9 @@ class ImageView extends PureComponent {
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: this.updatePanResponderTouches.bind(this),
});

this.imageLoadingStart = this.imageLoadingStart.bind(this);
this.imageLoadingEnd = this.imageLoadingEnd.bind(this);
}

componentDidMount() {
Expand Down Expand Up @@ -100,6 +107,14 @@ class ImageView extends PureComponent {
return false;
}

imageLoadingStart() {
this.setState({isLoading: true});
}

imageLoadingEnd() {
this.setState({isLoading: false});
}

render() {
// Default windowHeight accounts for the modal header height
const windowHeight = this.props.windowHeight - variables.contentHeaderHeight;
Expand All @@ -122,6 +137,13 @@ class ImageView extends PureComponent {
style={StyleUtils.getWidthAndHeightStyle(this.state.thumbnailWidth, this.state.thumbnailHeight)}
resizeMode={Image.resizeMode.contain}
/>
<View style={{...StyleSheet.absoluteFillObject}}>
<ActivityIndicator
size="large"
color={themeColors.spinner}
style={[styles.flex1]}
/>
</View>
</View>
);
}
Expand Down Expand Up @@ -178,6 +200,8 @@ class ImageView extends PureComponent {
]}
source={{uri: this.props.url}}
resizeMode={Image.resizeMode.contain}
onLoadStart={this.imageLoadingStart}
onLoadEnd={this.imageLoadingEnd}
/>
{/**
Create an invisible view on top of the image so we can capture and set the amount of touches before
Expand All @@ -194,6 +218,15 @@ class ImageView extends PureComponent {
]}
/>
</ImageZoom>
{this.state.isLoading && (
<View style={{...StyleSheet.absoluteFillObject}}>
<ActivityIndicator
size="large"
color={themeColors.spinner}
style={[styles.flex1]}
/>
</View>
)}
</View>
);
}
Expand Down
50 changes: 45 additions & 5 deletions src/components/ImageWithSizeCalculation.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import React, {PureComponent} from 'react';
import {Image} from 'react-native';
import {
StyleSheet, View, Image, ActivityIndicator,
} from 'react-native';
import PropTypes from 'prop-types';
import Log from '../libs/Log';
import styles from '../styles/styles';
import makeCancellablePromise from '../libs/MakeCancellablePromise';
import themeColors from '../styles/themes/default';

const propTypes = {
/** Url for image to display */
Expand All @@ -29,6 +32,17 @@ const defaultProps = {
* it can be appropriately resized.
*/
class ImageWithSizeCalculation extends PureComponent {
constructor(props) {
super(props);

this.state = {
isLoading: false,
};

this.imageLoadingStart = this.imageLoadingStart.bind(this);
this.imageLoadingEnd = this.imageLoadingEnd.bind(this);
}

componentDidMount() {
this.calculateImageSize();
}
Expand Down Expand Up @@ -83,17 +97,43 @@ class ImageWithSizeCalculation extends PureComponent {
});
}

imageLoadingStart() {
this.setState({isLoading: true});
}

imageLoadingEnd() {
this.setState({isLoading: false});
}

render() {
return (
<Image
<View
style={[
styles.w100,
styles.h100,
this.props.style,
]}
source={{uri: this.props.url}}
resizeMode="contain"
/>
>
<Image
style={[
styles.w100,
styles.h100,
]}
source={{uri: this.props.url}}
resizeMode="contain"
onLoadStart={this.imageLoadingStart}
onLoadEnd={this.imageLoadingEnd}
/>
{this.state.isLoading && (
<View style={{...StyleSheet.absoluteFillObject}}>
<ActivityIndicator
size="large"
color={themeColors.spinner}
style={[styles.flex1]}
/>
</View>
)}
</View>
);
}
}
Expand Down