feat: Replace hardcoded default profile image with a dynamic placeholder and remove cache-busting from presigned URLs.
This commit is contained in:
@@ -86,8 +86,6 @@ export default function AgentProfileView() {
|
||||
const [connectionStatus, setConnectionStatus] = useState<ConnectionStatus | null>(null);
|
||||
const [connectionRequestId, setConnectionRequestId] = useState<string | null>(null);
|
||||
|
||||
const defaultImage = '/assets/demo_images/8f017153a7b4a239a4f0691234ef97dd55092282.jpg';
|
||||
|
||||
// Helper to check if avatar is an S3 key (not a full URL or local path)
|
||||
const isS3Key = (avatar: string | null | undefined): boolean => {
|
||||
if (!avatar) return false;
|
||||
@@ -149,20 +147,11 @@ export default function AgentProfileView() {
|
||||
if (profile.avatar && isS3Key(profile.avatar)) {
|
||||
try {
|
||||
const presignedUrl = await uploadService.getPresignedDownloadUrl(profile.avatar);
|
||||
// Add cache-busting parameter to force browser to fetch fresh image
|
||||
// This prevents showing old cached image when avatar is updated
|
||||
const profileWithTimestamp = profile as unknown as { updatedAt?: string };
|
||||
const cacheBuster = profileWithTimestamp.updatedAt
|
||||
? new Date(profileWithTimestamp.updatedAt).getTime()
|
||||
: Date.now();
|
||||
const urlWithCacheBuster = `${presignedUrl}&_t=${cacheBuster}`;
|
||||
setAvatarUrl(urlWithCacheBuster);
|
||||
setAvatarUrl(presignedUrl);
|
||||
} catch (avatarErr) {
|
||||
console.error('Failed to get avatar URL:', avatarErr);
|
||||
// Don't fail the whole page, just use default image
|
||||
}
|
||||
} else if (profile.avatar) {
|
||||
// It's already a full URL or local path
|
||||
setAvatarUrl(profile.avatar);
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -213,9 +202,9 @@ export default function AgentProfileView() {
|
||||
};
|
||||
|
||||
const getProfileImageUrl = () => {
|
||||
// If image failed to load, return default
|
||||
// If image failed to load, return null
|
||||
if (imageError) {
|
||||
return defaultImage;
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we have a presigned URL from S3, use it
|
||||
@@ -225,7 +214,7 @@ export default function AgentProfileView() {
|
||||
|
||||
// Check for null, undefined, or empty string
|
||||
if (!agentProfile?.avatar || agentProfile.avatar.trim() === '') {
|
||||
return defaultImage;
|
||||
return null;
|
||||
}
|
||||
|
||||
// For relative paths (local assets), return as-is
|
||||
@@ -233,8 +222,8 @@ export default function AgentProfileView() {
|
||||
return agentProfile.avatar;
|
||||
}
|
||||
|
||||
// Default fallback
|
||||
return defaultImage;
|
||||
// No image available
|
||||
return null;
|
||||
};
|
||||
|
||||
// Format member since date
|
||||
@@ -293,18 +282,26 @@ export default function AgentProfileView() {
|
||||
<div className="w-full lg:w-[280px] flex-shrink-0 space-y-4 flex flex-col items-center lg:items-start">
|
||||
{/* Profile Image */}
|
||||
<div className="relative w-[200px] lg:w-[260px]">
|
||||
<div className="w-[200px] h-[200px] lg:w-[260px] lg:h-[260px] rounded-[15px] overflow-hidden">
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={getProfileImageUrl()}
|
||||
alt="Profile"
|
||||
className="w-full h-full object-cover"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
target.src = defaultImage;
|
||||
setImageError(true);
|
||||
}}
|
||||
/>
|
||||
<div className="w-[200px] h-[200px] lg:w-[260px] lg:h-[260px] rounded-[15px] overflow-hidden bg-[#e8e8e8]">
|
||||
{getProfileImageUrl() ? (
|
||||
/* eslint-disable-next-line @next/next/no-img-element */
|
||||
<img
|
||||
src={getProfileImageUrl()!}
|
||||
alt="Profile"
|
||||
className="w-full h-full object-cover"
|
||||
onError={() => setImageError(true)}
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<Image
|
||||
src="/assets/icons/user-placeholder-icon.svg"
|
||||
alt="Profile"
|
||||
width={80}
|
||||
height={80}
|
||||
className="opacity-40"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{/* Gradient Overlay */}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/50 via-black/20 to-transparent pointer-events-none" />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user