@@ -19,6 +19,9 @@ type AvatarProps = PropsOfComponent<typeof Flex> & {
1919 showLoadingSpinner ?: boolean ;
2020} ;
2121
22+ const SPINNER_DELAY_MS = 150 ;
23+ const SPINNER_MIN_DURATION_MS = 400 ;
24+
2225export const Avatar = ( props : AvatarProps ) => {
2326 const {
2427 size = ( ) => 26 ,
@@ -34,14 +37,42 @@ export const Avatar = (props: AvatarProps) => {
3437 } = props ;
3538 const [ error , setError ] = React . useState ( false ) ;
3639 const [ loaded , setLoaded ] = React . useState ( false ) ;
40+ const [ spinnerVisible , setSpinnerVisible ] = React . useState ( false ) ;
41+ const spinnerShownAtRef = React . useRef < number | null > ( null ) ;
3742
38- // Reset loaded state when imageUrl changes
3943 React . useEffect ( ( ) => {
4044 setLoaded ( false ) ;
4145 setError ( false ) ;
46+ setSpinnerVisible ( false ) ;
47+ spinnerShownAtRef . current = null ;
4248 } , [ imageUrl ] ) ;
4349
44- const isLoading = showLoadingSpinner && imageUrl && ! loaded && ! error ;
50+ React . useEffect ( ( ) => {
51+ if ( ! showLoadingSpinner || ! imageUrl || loaded || error ) {
52+ return ;
53+ }
54+
55+ const timer = setTimeout ( ( ) => {
56+ setSpinnerVisible ( true ) ;
57+ spinnerShownAtRef . current = Date . now ( ) ;
58+ } , SPINNER_DELAY_MS ) ;
59+
60+ return ( ) => clearTimeout ( timer ) ;
61+ } , [ showLoadingSpinner , imageUrl , loaded , error ] ) ;
62+
63+ const handleImageLoad = React . useCallback ( ( ) => {
64+ if ( spinnerShownAtRef . current ) {
65+ const elapsed = Date . now ( ) - spinnerShownAtRef . current ;
66+ const remaining = SPINNER_MIN_DURATION_MS - elapsed ;
67+ if ( remaining > 0 ) {
68+ const timer = setTimeout ( ( ) => setLoaded ( true ) , remaining ) ;
69+ return ( ) => clearTimeout ( timer ) ;
70+ }
71+ }
72+ setLoaded ( true ) ;
73+ } , [ ] ) ;
74+
75+ const isLoading = showLoadingSpinner && spinnerVisible && imageUrl && ! loaded && ! error ;
4576
4677 const ImgOrFallback =
4778 initials && ( ! imageUrl || error ) ? (
@@ -60,7 +91,7 @@ export const Avatar = (props: AvatarProps) => {
6091 transition : 'opacity 0.2s ease-in-out' ,
6192 } }
6293 onError = { ( ) => setError ( true ) }
63- onLoad = { ( ) => setLoaded ( true ) }
94+ onLoad = { handleImageLoad }
6495 size = { imageFetchSize }
6596 />
6697 ) ;
0 commit comments