refactor: optimize image loading and error handling in UserAvatar component

This commit is contained in:
Danny Avila 2025-10-22 22:28:05 +02:00
parent 9b4c4cafb6
commit 41380d9cb9
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956

View file

@ -1,4 +1,4 @@
import React, { memo, useState } from 'react'; import React, { memo, useState, useMemo } from 'react';
import { UserIcon, useAvatar } from '@librechat/client'; import { UserIcon, useAvatar } from '@librechat/client';
import type { TUser } from 'librechat-data-provider'; import type { TUser } from 'librechat-data-provider';
import type { IconProps } from '~/common'; import type { IconProps } from '~/common';
@ -17,9 +17,15 @@ type UserAvatarProps = {
const UserAvatar = memo(({ size, user, avatarSrc, username, className }: UserAvatarProps) => { const UserAvatar = memo(({ size, user, avatarSrc, username, className }: UserAvatarProps) => {
const [imageError, setImageError] = useState(false); const [imageError, setImageError] = useState(false);
const [imageLoaded, setImageLoaded] = useState(false);
const handleImageError = () => { const handleImageError = () => {
setImageError(true); setImageError(true);
setImageLoaded(false);
};
const handleImageLoad = () => {
setImageLoaded(true);
}; };
const renderDefaultAvatar = () => ( const renderDefaultAvatar = () => (
@ -36,6 +42,10 @@ const UserAvatar = memo(({ size, user, avatarSrc, username, className }: UserAva
</div> </div>
); );
const hasAvatar = useMemo(() => (user?.avatar ?? '') || avatarSrc, [user?.avatar, avatarSrc]);
const showImage = useMemo(() => hasAvatar && !imageError, [hasAvatar, imageError]);
const imageSrc = useMemo(() => (user?.avatar ?? '') || avatarSrc, [user?.avatar, avatarSrc]);
return ( return (
<div <div
title={username} title={username}
@ -45,14 +55,16 @@ const UserAvatar = memo(({ size, user, avatarSrc, username, className }: UserAva
}} }}
className={cn('relative flex items-center justify-center', className ?? '')} className={cn('relative flex items-center justify-center', className ?? '')}
> >
{(!(user?.avatar ?? '') && (!(user?.username ?? '') || user?.username.trim() === '')) || {!showImage || !imageLoaded ? renderDefaultAvatar() : null}
imageError ? ( {showImage && (
renderDefaultAvatar()
) : (
<img <img
style={{
display: imageLoaded ? 'block' : 'none',
}}
className="rounded-full" className="rounded-full"
src={(user?.avatar ?? '') || avatarSrc} src={imageSrc}
alt="avatar" alt="avatar"
onLoad={handleImageLoad}
onError={handleImageError} onError={handleImageError}
/> />
)} )}
@ -69,8 +81,12 @@ const Icon: React.FC<IconProps> = memo((props) => {
const avatarSrc = useAvatar(user); const avatarSrc = useAvatar(user);
const localize = useLocalize(); const localize = useLocalize();
const username = useMemo(
() => user?.name ?? user?.username ?? localize('com_nav_user'),
[user?.name, user?.username, localize],
);
if (isCreatedByUser) { if (isCreatedByUser) {
const username = user?.name ?? user?.username ?? localize('com_nav_user');
return ( return (
<UserAvatar <UserAvatar
size={size} size={size}