-
Notifications
You must be signed in to change notification settings - Fork 2.8k
/
ImageWithSizeCalculation.tsx
98 lines (86 loc) · 3.25 KB
/
ImageWithSizeCalculation.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import delay from 'lodash/delay';
import React, {useEffect, useRef, useState} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import Log from '@libs/Log';
import FullscreenLoadingIndicator from './FullscreenLoadingIndicator';
import Image from './Image';
import RESIZE_MODES from './Image/resizeModes';
type OnMeasure = (args: {width: number; height: number}) => void;
type OnLoadNativeEvent = {
nativeEvent: {
width: number;
height: number;
};
};
type ImageWithSizeCalculationProps = {
/** Url for image to display */
url: string;
/** Any additional styles to apply */
style?: StyleProp<ViewStyle>;
/** Callback fired when the image has been measured. */
onMeasure: OnMeasure;
/** Whether the image requires an authToken */
isAuthTokenRequired: boolean;
};
/**
* Preloads an image by getting the size and passing dimensions via callback.
* Image size must be provided by parent via width and height props. Useful for
* performing some calculation on a network image after fetching dimensions so
* it can be appropriately resized.
*/
function ImageWithSizeCalculation({url, style, onMeasure, isAuthTokenRequired}: ImageWithSizeCalculationProps) {
const styles = useThemeStyles();
const isLoadedRef = useRef<boolean | null>(null);
const [isImageCached, setIsImageCached] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const onError = () => {
Log.hmmm('Unable to fetch image to calculate size', {url});
};
const imageLoadedSuccessfully = (event: OnLoadNativeEvent) => {
isLoadedRef.current = true;
onMeasure({
width: event.nativeEvent.width,
height: event.nativeEvent.height,
});
};
/** Delay the loader to detect whether the image is being loaded from the cache or the internet. */
useEffect(() => {
if (isLoadedRef.current ?? !isLoading) {
return;
}
const timeout = delay(() => {
if (!isLoading || isLoadedRef.current) {
return;
}
setIsImageCached(false);
}, 200);
return () => clearTimeout(timeout);
}, [isLoading]);
return (
<View style={[styles.w100, styles.h100, style]}>
<Image
style={[styles.w100, styles.h100]}
source={{uri: url}}
isAuthTokenRequired={isAuthTokenRequired}
resizeMode={RESIZE_MODES.cover}
onLoadStart={() => {
if (isLoadedRef.current ?? isLoading) {
return;
}
setIsLoading(true);
}}
onLoadEnd={() => {
setIsLoading(false);
setIsImageCached(true);
}}
onError={onError}
onLoad={imageLoadedSuccessfully}
/>
{isLoading && !isImageCached && <FullscreenLoadingIndicator style={[styles.opacity1, styles.bgTransparent]} />}
</View>
);
}
ImageWithSizeCalculation.displayName = 'ImageWithSizeCalculation';
export default React.memo(ImageWithSizeCalculation);