feat: Implement shimmer loading for profile and avatar images across various components.

This commit is contained in:
pradeepkumar
2026-03-14 14:37:19 +05:30
parent 7b5c670159
commit 9acb0063fa
10 changed files with 83 additions and 101 deletions

View File

@@ -61,6 +61,43 @@ interface ProfileCardProps {
resolvedAvatarUrl?: string | null;
}
// Profile image with shimmer loading
function ProfileImage({ src, alt, className }: { src: string | null; alt: string; className?: string }) {
const [status, setStatus] = useState<'loading' | 'loaded' | 'error'>(src ? 'loading' : 'error');
useEffect(() => {
setStatus(src ? 'loading' : 'error');
}, [src]);
return (
<div className={`relative overflow-hidden bg-[#e8e8e8] ${className || ''}`}>
{status === 'loading' && (
<div className="absolute inset-0 shimmer-loading" />
)}
{src && status !== 'error' ? (
/* eslint-disable-next-line @next/next/no-img-element */
<img
src={src}
alt={alt}
className={`w-full h-full object-cover transition-opacity duration-300 ${status === 'loaded' ? 'opacity-100' : 'opacity-0'}`}
onLoad={() => setStatus('loaded')}
onError={() => setStatus('error')}
/>
) : status === 'error' ? (
<div className="w-full h-full flex items-center justify-center">
<Image
src="/assets/icons/user-placeholder-icon.svg"
alt={alt}
width={64}
height={64}
className="opacity-40"
/>
</div>
) : null}
</div>
);
}
function ProfileCard({ profile, resolvedAvatarUrl }: ProfileCardProps) {
const [showFullBio, setShowFullBio] = useState(false);
const [showAllTags, setShowAllTags] = useState(false);
@@ -235,30 +272,11 @@ function ProfileCard({ profile, resolvedAvatarUrl }: ProfileCardProps) {
<div className="bg-white rounded-[20px] p-4 sm:p-5 flex flex-col sm:flex-row gap-4 sm:gap-5 shadow-[0px_10px_20px_rgba(217,217,217,0.5)]">
{/* Profile Image & View Profile Button */}
<div className="flex-shrink-0 flex flex-col items-center">
<div className="relative w-full sm:w-[200px] h-[200px] rounded-[15px] overflow-hidden bg-[#e8e8e8]">
{getProfileImageUrl() ? (
/* eslint-disable-next-line @next/next/no-img-element */
<img
src={getProfileImageUrl()!}
alt={`${profile.firstName} ${profile.lastName}`}
className="w-full h-full object-cover"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.style.display = 'none';
}}
/>
) : (
<div className="w-full h-full flex items-center justify-center">
<Image
src="/assets/icons/user-placeholder-icon.svg"
alt={`${profile.firstName} ${profile.lastName}`}
width={64}
height={64}
className="opacity-40"
/>
</div>
)}
</div>
<ProfileImage
src={getProfileImageUrl()}
alt={`${profile.firstName} ${profile.lastName}`}
className="w-full sm:w-[200px] h-[200px] rounded-[15px]"
/>
<Link href={`/user/profile/${profile.id}`} className="mt-4">
<button className="w-[113px] py-2.5 bg-[#e58625] hover:bg-[#d47720] text-[#00293d] font-fractul font-bold text-[14px] rounded-[15px] transition-colors">
View Profile