1+ import * as React from 'react'
12import Icon , { type IconType } from '../icon'
23import * as Styles from '@/styles'
34import type { Props , AvatarSize } from '.'
@@ -24,28 +25,54 @@ const Avatar = (p: Props) => {
2425
2526 const scaledAvatarRatio = props . size / AVATAR_SIZE
2627 const avatarScaledWidth = props . crop ?. scaledWidth ? props . crop . scaledWidth * scaledAvatarRatio : null
28+
29+ // Stable style object to prevent img re-render
30+ const imgOpacity = props . opacity === undefined || props . opacity === 1
31+ ? props . blocked
32+ ? 0.1
33+ : 1
34+ : props . opacity
35+ const imgStyle = React . useMemo (
36+ ( ) => ( imgOpacity !== 1 ? { opacity : imgOpacity } : undefined ) ,
37+ [ imgOpacity ]
38+ )
39+
2740 return (
2841 < div
2942 className = { Styles . classNames ( 'avatar' , avatarSizeClasName ) }
3043 onClick = { props . onClick }
3144 style = { Styles . collapseStyles ( [ props . style , props . onClick && styles . clickable ] ) as React . CSSProperties }
3245 >
33- { ! props . skipBackground && (
34- < div className = { Styles . classNames ( 'avatar-background' , avatarSizeClasName ) } />
35- ) }
46+ { /* Inner wrapper clips avatar image content, outer container allows follow icons to extend */ }
47+ < div className = { Styles . classNames ( 'avatar-inner' , avatarSizeClasName ) } >
48+ { ! props . skipBackground && (
49+ < div className = "avatar-background" />
50+ ) }
3651 { ! ! props . blocked && ! ! avatarSizeToPoopIconType ( props . size ) && (
3752 < div
38- className = { Styles . classNames ( ' avatar-user-image' , avatarSizeClasName ) }
53+ className = " avatar-user-image"
3954 style = { styles . poopContainer }
4055 >
4156 { /* ts messes up here without the || 'icon-poop-32' even though it
4257 can't happen due to the !!avatarSizeToPoopIconType() check above */ }
4358 < Icon type = { avatarSizeToPoopIconType ( props . size ) || 'icon-poop-32' } />
4459 </ div >
4560 ) }
46- { ! ! props . url && props . crop === undefined && (
61+ { ! ! props . src && props . crop === undefined && (
62+ < img
63+ key = { props . src }
64+ src = { props . src }
65+ srcSet = { props . size <= 32 ? undefined : props . srcset || undefined }
66+ decoding = "async"
67+ className = "avatar-user-image"
68+ style = { imgStyle as React . CSSProperties }
69+ alt = ""
70+ draggable = { false }
71+ />
72+ ) }
73+ { ! ! props . url && ! props . src && props . crop === undefined && (
4774 < div
48- className = { Styles . classNames ( ' avatar-user-image' , avatarSizeClasName ) }
75+ className = " avatar-user-image"
4976 style = { {
5077 backgroundImage : props . url ,
5178 opacity :
@@ -58,9 +85,8 @@ const Avatar = (p: Props) => {
5885 />
5986 ) }
6087 { ! ! props . url && props . crop ?. offsetLeft !== undefined && props . crop . offsetTop !== undefined && (
61- < img
62- loading = "lazy"
63- className = { Styles . classNames ( 'avatar-user-image' , avatarSizeClasName ) }
88+ < div
89+ className = "avatar-user-image"
6490 style = { {
6591 backgroundImage : props . url ,
6692 backgroundPositionX : props . crop . offsetLeft * scaledAvatarRatio ,
@@ -95,6 +121,7 @@ const Avatar = (p: Props) => {
95121 ) }
96122 />
97123 ) }
124+ </ div >
98125 { props . followIconType && < Icon type = { props . followIconType } style = { props . followIconStyle } /> }
99126 { props . editable && < Icon type = "iconfont-edit" style = { props . isTeam ? styles . editTeam : styles . edit } /> }
100127 { props . children }
0 commit comments