1- import React , { useMemo } from 'react' ;
2- import { Pressable , StyleSheet , Text , View } from 'react-native' ;
1+ import React , { useEffect , useMemo , useRef } from 'react' ;
2+ import {
3+ ImageErrorEventData ,
4+ NativeSyntheticEvent ,
5+ Pressable ,
6+ StyleSheet ,
7+ Text ,
8+ View ,
9+ } from 'react-native' ;
310
411import type { Attachment , LocalMessage } from 'stream-chat' ;
512
@@ -35,6 +42,7 @@ import {
3542import { useTheme } from '../../contexts/themeContext/ThemeContext' ;
3643
3744import { useLoadingImage } from '../../hooks/useLoadingImage' ;
45+ import { useStableCallback } from '../../hooks/useStableCallback' ;
3846import { isVideoPlayerAvailable } from '../../native' ;
3947import { primitives } from '../../theme' ;
4048import { FileTypes } from '../../types/types' ;
@@ -66,6 +74,8 @@ export type GalleryPropsWithContext = Pick<ImageGalleryContextValue, 'imageGalle
6674 messageHasOnlyOneImage : boolean ;
6775 } ;
6876
77+ const IMAGE_LOADING_INDICATOR_DELAY_MS = 120 ;
78+
6979const GalleryWithContext = ( props : GalleryPropsWithContext ) => {
7080 const {
7181 additionalPressableProps,
@@ -385,6 +395,7 @@ const GalleryImageThumbnail = ({
385395 setLoadingImage,
386396 setLoadingImageError,
387397 } = useLoadingImage ( ) ;
398+ const loadingIndicatorTimerRef = useRef < ReturnType < typeof setTimeout > | null > ( null ) ;
388399
389400 const {
390401 theme : {
@@ -394,20 +405,49 @@ const GalleryImageThumbnail = ({
394405
395406 const styles = useStyles ( ) ;
396407
408+ const clearLoadingIndicatorTimer = useStableCallback ( ( ) => {
409+ if ( loadingIndicatorTimerRef . current ) {
410+ clearTimeout ( loadingIndicatorTimerRef . current ) ;
411+ loadingIndicatorTimerRef . current = null ;
412+ }
413+ } ) ;
414+
415+ const onLoadStart = useStableCallback ( ( ) => {
416+ clearLoadingIndicatorTimer ( ) ;
417+ setLoadingImageError ( false ) ;
418+ loadingIndicatorTimerRef . current = setTimeout ( ( ) => {
419+ setLoadingImage ( true ) ;
420+ loadingIndicatorTimerRef . current = null ;
421+ } , IMAGE_LOADING_INDICATOR_DELAY_MS ) ;
422+ } ) ;
423+
424+ const onLoadEnd = useStableCallback ( ( ) => {
425+ clearLoadingIndicatorTimer ( ) ;
426+ setLoadingImage ( false ) ;
427+ setLoadingImageError ( false ) ;
428+ } ) ;
429+
430+ const onError = useStableCallback (
431+ ( { nativeEvent : { error } } : NativeSyntheticEvent < ImageErrorEventData > ) => {
432+ clearLoadingIndicatorTimer ( ) ;
433+ console . warn ( error ) ;
434+ setLoadingImage ( false ) ;
435+ setLoadingImageError ( true ) ;
436+ } ,
437+ ) ;
438+
439+ useEffect ( ( ) => clearLoadingIndicatorTimer , [ clearLoadingIndicatorTimer ] ) ;
440+
397441 return (
398442 < View style = { [ styles . image , borderRadius ] } >
399443 { isLoadingImageError ? (
400444 < ImageLoadingFailedIndicator onReloadImage = { onReloadImage } />
401445 ) : (
402446 < >
403447 < GalleryImage
404- onError = { ( { nativeEvent : { error } } ) => {
405- console . warn ( error ) ;
406- setLoadingImage ( false ) ;
407- setLoadingImageError ( true ) ;
408- } }
409- onLoadEnd = { ( ) => setTimeout ( ( ) => setLoadingImage ( false ) , 0 ) }
410- onLoadStart = { ( ) => setLoadingImage ( true ) }
448+ onError = { onError }
449+ onLoadEnd = { onLoadEnd }
450+ onLoadStart = { onLoadStart }
411451 resizeMode = { thumbnail . resizeMode }
412452 style = { gallery . image }
413453 uri = { thumbnail . url }
0 commit comments