feat: Implement shimmer loading for profile and avatar images across various components.
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user