From 39137b4dfc145703a796ff5eceb0eb13b1902eff Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Wed, 4 Mar 2026 20:42:46 +0530 Subject: [PATCH] feat: Optimize agent profile avatar loading by resolving all presigned URLs in parallel at the page level and passing them to individual profile cards. --- src/app/(user)/user/profiles/page.tsx | 61 +++++++++++++-------------- src/services/agents.service.ts | 2 + 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/src/app/(user)/user/profiles/page.tsx b/src/app/(user)/user/profiles/page.tsx index a103dd7..ad497c0 100644 --- a/src/app/(user)/user/profiles/page.tsx +++ b/src/app/(user)/user/profiles/page.tsx @@ -58,41 +58,16 @@ function FilterSection({ title, options, selectedOptions, onToggle, showMore }: interface ProfileCardProps { profile: PublicAgentProfile; + resolvedAvatarUrl?: string | null; } -function ProfileCard({ profile }: ProfileCardProps) { +function ProfileCard({ profile, resolvedAvatarUrl }: ProfileCardProps) { const [showFullBio, setShowFullBio] = useState(false); const [showAllTags, setShowAllTags] = useState(false); - const [avatarUrl, setAvatarUrl] = useState(null); const defaultImage = '/assets/demo_images/8f017153a7b4a239a4f0691234ef97dd55092282.jpg'; - - // Fetch presigned URL for avatar if it's an S3 key - useEffect(() => { - const fetchAvatarUrl = async () => { - if (profile.avatar && !profile.avatar.startsWith('http') && !profile.avatar.startsWith('/')) { - 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); - } catch (error) { - console.error('Failed to get avatar URL:', error); - } - } else if (profile.avatar) { - setAvatarUrl(profile.avatar); - } - }; - fetchAvatarUrl(); - }, [profile.avatar, profile]); - const getProfileImageUrl = () => { - if (avatarUrl) return avatarUrl; + if (resolvedAvatarUrl) return resolvedAvatarUrl; if (profile.avatar?.startsWith('/')) return profile.avatar; return defaultImage; }; @@ -405,6 +380,7 @@ function ProfilesPageContent() { // State for API data const [agents, setAgents] = useState([]); + const [avatarUrls, setAvatarUrls] = useState>({}); const [agentTypes, setAgentTypes] = useState([]); const [filterFields, setFilterFields] = useState([]); const [loading, setLoading] = useState(true); @@ -474,10 +450,9 @@ function ProfilesPageContent() { params.agentTypeId = activeTypeId; } - // Add location from URL params + // Add location search (searches city, state, country, and dynamic field values) if (locationFromUrl) { - // Try to set as city first - params.city = locationFromUrl; + params.location = locationFromUrl; } // Add dynamic profile field filters @@ -493,6 +468,28 @@ function ProfilesPageContent() { } const response = await agentsService.searchAgents(params); + + // Resolve all avatar presigned URLs in parallel before rendering + const urlMap: Record = {}; + const avatarPromises = response.data.map(async (agent) => { + if (agent.avatar && !agent.avatar.startsWith('http') && !agent.avatar.startsWith('/')) { + try { + const presignedUrl = await uploadService.getPresignedDownloadUrl(agent.avatar); + const agentWithTimestamp = agent as unknown as { updatedAt?: string }; + const cacheBuster = agentWithTimestamp.updatedAt + ? new Date(agentWithTimestamp.updatedAt).getTime() + : Date.now(); + urlMap[agent.id] = `${presignedUrl}&_t=${cacheBuster}`; + } catch { + // Skip failed URLs, card will show default image + } + } else if (agent.avatar) { + urlMap[agent.id] = agent.avatar; + } + }); + await Promise.all(avatarPromises); + + setAvatarUrls(urlMap); setAgents(response.data); setTotalPages(response.totalPages); setTotalResults(response.total); @@ -703,7 +700,7 @@ function ProfilesPageContent() { {!loading && !error && agents.length > 0 && (
{agents.map((profile) => ( - + ))}
)} diff --git a/src/services/agents.service.ts b/src/services/agents.service.ts index 0e25f17..fedc24b 100644 --- a/src/services/agents.service.ts +++ b/src/services/agents.service.ts @@ -72,6 +72,7 @@ export interface SaveFieldValuesResponse { // Search parameters for public agent search export interface SearchAgentsParams { search?: string; + location?: string; city?: string; state?: string; country?: string; @@ -187,6 +188,7 @@ class AgentsService { const queryParams = new URLSearchParams(); if (params.search) queryParams.set('search', params.search); + if (params.location) queryParams.set('location', params.location); if (params.city) queryParams.set('city', params.city); if (params.state) queryParams.set('state', params.state); if (params.country) queryParams.set('country', params.country);