feat: Improve avatar loading experience by displaying placeholders until images are fully loaded across various components.

This commit is contained in:
pradeepkumar
2026-03-13 22:45:11 +05:30
parent b339dd865c
commit 7b5c670159
11 changed files with 225 additions and 80 deletions

View File

@@ -80,6 +80,44 @@ function formatLastSeen(lastSeenAt: string | null | undefined, isOnline: boolean
return `Last seen ${diffDays}d ago`;
}
// Avatar component with placeholder-until-loaded pattern
function AvatarWithPlaceholder({ src, alt, size }: { src: string; alt: string; size: number }) {
const [loaded, setLoaded] = useState(false);
const placeholder = '/assets/icons/user-placeholder-icon.svg';
const isPlaceholder = !src || src === placeholder;
return (
<>
{(!src || !loaded) && (
<Image
src={placeholder}
alt={alt}
width={size}
height={size}
className="absolute inset-0 w-full h-full object-cover"
/>
)}
{src && !isPlaceholder && (
<img
src={src}
alt={alt}
className={`absolute inset-0 w-full h-full object-cover transition-opacity duration-200 ${loaded ? 'opacity-100' : 'opacity-0'}`}
onLoad={() => setLoaded(true)}
/>
)}
{src && isPlaceholder && (
<Image
src={placeholder}
alt={alt}
width={size}
height={size}
className="absolute inset-0 w-full h-full object-cover"
/>
)}
</>
);
}
// Connected agent with avatar URL
interface ConnectedAgent {
agentProfileId: string;
@@ -589,14 +627,11 @@ export function MessagingPage() {
>
{/* Avatar */}
<div className="relative flex-shrink-0">
<div className="w-[50px] h-[50px] rounded-full overflow-hidden border border-[#00293D]/20">
<Image
<div className="w-[50px] h-[50px] rounded-full overflow-hidden border border-[#00293D]/20 relative">
<AvatarWithPlaceholder
src={getValidAvatarUrl(agent.avatarUrl)}
alt={agent.name}
width={50}
height={50}
className="object-cover rounded-full"
style={{ width: '100%', height: '100%' }}
size={50}
/>
</div>
</div>
@@ -659,14 +694,11 @@ export function MessagingPage() {
{/* Avatar with online indicator */}
<div className="relative flex-shrink-0">
<div className="w-[70px] h-[70px] rounded-full overflow-hidden border border-[#00293D]/20">
<Image
<div className="w-[70px] h-[70px] rounded-full overflow-hidden border border-[#00293D]/20 relative">
<AvatarWithPlaceholder
src={getValidAvatarUrl(conversationAvatarUrls[conversation.id])}
alt={conversation.otherParty?.name || 'User'}
width={70}
height={70}
className="object-cover rounded-full"
style={{ width: '100%', height: '100%' }}
size={70}
/>
</div>
{conversation.otherParty?.isOnline && (