Compare commits
5 Commits
main
...
fix/avatar
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9ef26ddd5 | ||
|
|
c63f2a634c | ||
|
|
39d83b705b | ||
|
|
e5a5931818 | ||
|
|
41380d9cb9 |
@@ -1,4 +1,4 @@
|
||||
import React, { memo, useState } from 'react';
|
||||
import React, { memo, useState, useMemo, useRef, useCallback, useEffect } from 'react';
|
||||
import { UserIcon, useAvatar } from '@librechat/client';
|
||||
import type { TUser } from 'librechat-data-provider';
|
||||
import type { IconProps } from '~/common';
|
||||
@@ -15,26 +15,49 @@ type UserAvatarProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Default avatar component - memoized outside to prevent recreation on every render
|
||||
*/
|
||||
const DefaultAvatar = memo(() => (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: 'rgb(121, 137, 255)',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
boxShadow: 'rgba(240, 246, 252, 0.1) 0px 0px 0px 1px',
|
||||
}}
|
||||
className="relative flex h-9 w-9 items-center justify-center rounded-sm p-1 text-white"
|
||||
>
|
||||
<UserIcon />
|
||||
</div>
|
||||
));
|
||||
|
||||
DefaultAvatar.displayName = 'DefaultAvatar';
|
||||
|
||||
const UserAvatar = memo(({ size, user, avatarSrc, username, className }: UserAvatarProps) => {
|
||||
const [imageError, setImageError] = useState(false);
|
||||
const imageLoadedRef = useRef(false);
|
||||
|
||||
const handleImageError = () => {
|
||||
const imageSrc = useMemo(() => (user?.avatar ?? '') || avatarSrc, [user?.avatar, avatarSrc]);
|
||||
|
||||
/** Reset loaded state and error state if image source changes */
|
||||
useEffect(() => {
|
||||
imageLoadedRef.current = false;
|
||||
setImageError(false);
|
||||
}, [imageSrc]);
|
||||
|
||||
const handleImageError = useCallback(() => {
|
||||
setImageError(true);
|
||||
};
|
||||
imageLoadedRef.current = false;
|
||||
}, []);
|
||||
|
||||
const renderDefaultAvatar = () => (
|
||||
<div
|
||||
style={{
|
||||
backgroundColor: 'rgb(121, 137, 255)',
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
boxShadow: 'rgba(240, 246, 252, 0.1) 0px 0px 0px 1px',
|
||||
}}
|
||||
className="relative flex h-9 w-9 items-center justify-center rounded-sm p-1 text-white"
|
||||
>
|
||||
<UserIcon />
|
||||
</div>
|
||||
);
|
||||
const handleImageLoad = useCallback(() => {
|
||||
imageLoadedRef.current = true;
|
||||
setImageError(false);
|
||||
}, []);
|
||||
|
||||
const hasAvatar = useMemo(() => imageSrc !== '', [imageSrc]);
|
||||
const showImage = useMemo(() => hasAvatar && !imageError, [hasAvatar, imageError]);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -45,14 +68,14 @@ const UserAvatar = memo(({ size, user, avatarSrc, username, className }: UserAva
|
||||
}}
|
||||
className={cn('relative flex items-center justify-center', className ?? '')}
|
||||
>
|
||||
{(!(user?.avatar ?? '') && (!(user?.username ?? '') || user?.username.trim() === '')) ||
|
||||
imageError ? (
|
||||
renderDefaultAvatar()
|
||||
{!showImage ? (
|
||||
<DefaultAvatar />
|
||||
) : (
|
||||
<img
|
||||
className="rounded-full"
|
||||
src={(user?.avatar ?? '') || avatarSrc}
|
||||
src={imageSrc}
|
||||
alt="avatar"
|
||||
onLoad={handleImageLoad}
|
||||
onError={handleImageError}
|
||||
/>
|
||||
)}
|
||||
@@ -69,8 +92,12 @@ const Icon: React.FC<IconProps> = memo((props) => {
|
||||
const avatarSrc = useAvatar(user);
|
||||
const localize = useLocalize();
|
||||
|
||||
const username = useMemo(
|
||||
() => user?.name ?? user?.username ?? localize('com_nav_user'),
|
||||
[user?.name, user?.username, localize],
|
||||
);
|
||||
|
||||
if (isCreatedByUser) {
|
||||
const username = user?.name ?? user?.username ?? localize('com_nav_user');
|
||||
return (
|
||||
<UserAvatar
|
||||
size={size}
|
||||
|
||||
Reference in New Issue
Block a user