From f898307368c7121b7fb7948a9de3f7964768bc3c Mon Sep 17 00:00:00 2001 From: Gracy Mehndiratta Date: Fri, 12 Jun 2026 17:14:55 +0530 Subject: [PATCH] fix:profile modal on mention --- src/components/ChatPage.tsx | 58 ++++++++++--- src/components/ChatWindow.tsx | 148 ++++++++++++++++++---------------- 2 files changed, 123 insertions(+), 83 deletions(-) diff --git a/src/components/ChatPage.tsx b/src/components/ChatPage.tsx index 501cb52..1a7587a 100644 --- a/src/components/ChatPage.tsx +++ b/src/components/ChatPage.tsx @@ -1960,21 +1960,56 @@ const loadOlderMessages = async (container?: HTMLDivElement | null) => { setIsProfileOpen(true); try { - const profile = await fetchUserProfile(userId); + const token = localStorage.getItem("access_token"); + + // Try the generic profile endpoint with exhaustive field names + const url = `${process.env.NEXT_PUBLIC_API_URL}/api/profile/${userId}`; + const response = await fetch(url, { + headers: { Authorization: `Bearer ${token || ""}` }, + }); + + let profile: any = null; + + if (response.ok) { + profile = await response.json(); + } else { + // Fallback to the existing fetchUserProfile helper + profile = await fetchUserProfile(userId); + } + if (!profile) throw new Error("Profile not found"); + // Exhaustive extraction — covers user/users nesting and flat shapes + const resolvedUsername = + profile.user?.username || + profile.users?.username || + profile.username || + profile.fullname || + profile.name || + fallbackName || + "Unknown User"; + + const resolvedAvatar = + profile.user?.avatar_url || + profile.users?.avatar_url || + profile.avatar_url || + fallbackAvatar || + "/User_profil.png"; + + const resolvedBio = + profile.user?.bio || + profile.users?.bio || + profile.bio || + profile.about || + "No bio yet..."; + setSelectedUser((prev) => { if (!prev || prev.id !== userId) return prev; return { id: userId, - username: - profile.username || - profile.fullname || - fallbackName || - "Unknown User", - avatarUrl: - profile.avatar_url || fallbackAvatar || "/User_profil.png", - about: profile.bio || "No bio yet...", + username: resolvedUsername, + avatarUrl: resolvedAvatar, + about: resolvedBio, roles: Array.isArray(profile.roles) ? profile.roles .map((role: any) => @@ -1984,8 +2019,8 @@ const loadOlderMessages = async (container?: HTMLDivElement | null) => { : [], }; }); - } catch (profileError) { - console.error("Failed to open DM user profile:", profileError); + } catch (error) { + console.error("openUserProfile fetch failed:", error); setSelectedUser((prev) => prev ? { ...prev, about: "No bio available." } : null ); @@ -1993,7 +2028,6 @@ const loadOlderMessages = async (container?: HTMLDivElement | null) => { }, [] ); - // Mark thread as read when user opens a DM useEffect(() => { if (!activeDmId || !currentUser?.id) return; diff --git a/src/components/ChatWindow.tsx b/src/components/ChatWindow.tsx index 4c94a74..e6c1130 100644 --- a/src/components/ChatWindow.tsx +++ b/src/components/ChatWindow.tsx @@ -578,36 +578,13 @@ const openProfile = useCallback( async (userId: string, username?: string, fallbackAvatar?: string) => { if (!userId) return; - let realUserId = userId; - const safeUsername = username || "Unknown"; - let safeAvatar = fallbackAvatar || "/User_profil.png"; + console.log("openProfile called:", { userId, username }); // ← ADD - // Fix for @mentions: If userId doesn't have a hyphen, it's a username, not a UUID! - // We must find their real UUID in the local chat history first. - if (!realUserId.includes("-")) { - const found = messages.find( - (m) => m.username === realUserId || m.senderId === realUserId - ); - if (found && String(found.senderId).includes("-")) { - realUserId = String(found.senderId); - safeAvatar = found.avatarUrl || safeAvatar; - } else { - // If they aren't in chat history, we can't fetch their profile. Exit gracefully. - setSelectedUser({ - id: realUserId, - username: safeUsername, - avatarUrl: safeAvatar, - about: "No bio available.", - roles: [], - }); - setIsProfileOpen(true); - return; - } - } + const safeUsername = username || "Unknown"; + const safeAvatar = fallbackAvatar || "/User_profil.png"; - // 1. Open the modal instantly setSelectedUser({ - id: realUserId, + id: userId, username: safeUsername, avatarUrl: safeAvatar, about: "Loading bio...", @@ -615,78 +592,107 @@ const openProfile = useCallback( }); setIsProfileOpen(true); - // 2. Fetch the rich user details try { const token = localStorage.getItem("access_token"); - if (!token || !serverId) return; + if (!token || !serverId) { + console.log("No token or serverId:", { token: !!token, serverId }); // ← ADD + return; + } + + const url = `${process.env.NEXT_PUBLIC_API_URL}/api/newserver/${serverId}/members/${userId}`; + console.log("Fetching:", url); // ← ADD - const url = `${process.env.NEXT_PUBLIC_API_URL}/api/newserver/${serverId}/members/${realUserId}`; - const response = await fetch(url, { + const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` }, }); - if (!response.ok) throw new Error("Failed to fetch user"); + console.log("Response status:", res.status); // ← ADD - const memberData = await response.json(); + if (!res.ok) throw new Error(`HTTP ${res.status}`); - // Pure fallback, absolutely NO URL modifications! - const freshAvatar = - memberData.user?.avatar_url || - memberData.users?.avatar_url || - memberData.avatar_url; + const data = await res.json(); + console.log("Member data received:", data); // ← ADD setSelectedUser({ - id: realUserId, + id: userId, username: - memberData.user?.username || - memberData.users?.username || + data.user?.username || + data.users?.username || + data.username || safeUsername, - avatarUrl: freshAvatar || safeAvatar, - about: - memberData.user?.bio || - memberData.users?.bio || - memberData.bio || - "No bio yet...", + avatarUrl: + data.user?.avatar_url || + data.users?.avatar_url || + data.avatar_url || + safeAvatar, + about: data.user?.bio || data.users?.bio || data.bio || "No bio yet...", roles: - memberData.roles?.map((role: any) => ({ - id: role.id || role.role_id, - name: role.name, - color: role.color || "#374151", + data.roles?.map((r: any) => ({ + id: r.id || r.role_id, + name: r.name, + color: r.color || "#374151", })) || [], }); - } catch (error) { + } catch (err) { + console.error("openProfile error:", err); setSelectedUser((prev) => prev ? { ...prev, about: "No bio available." } : null ); } }, - [serverId, messages] // Make sure messages is in the dependency array! + [serverId, messages] ); const handleUsernameClick = useCallback( async (userId: string, username: string) => { - const avatarUrl = avatarCacheRef.current[userId]?.url || "/User_profil.png"; - const existingMessage = messages.find( - (msg) => msg.senderId === userId || msg.username === username + // Step 1: Check already-loaded messages for real UUID + const fromMessages = messages.find( + (msg) => + msg.username?.toLowerCase() === username.toLowerCase() && + msg.senderId && + !String(msg.senderId).startsWith("temp-") && + // Make sure senderId is a real UUID, not another username string + String(msg.senderId) !== msg.username ); - let mockMessage: Message; - - if (existingMessage) { - mockMessage = existingMessage; - } else { - mockMessage = { - id: `temp-${userId}`, - content: "", - senderId: userId, - timestamp: new Date().toISOString(), - username, - avatarUrl: avatarCacheRef.current[userId]?.url || "/User_profil.png", - }; + if (fromMessages?.senderId) { + const avatarUrl = + avatarCacheRef.current[String(fromMessages.senderId)]?.url || + fromMessages.avatarUrl || + "/User_profil.png"; + await openProfile(String(fromMessages.senderId), username, avatarUrl); + return; } - await openProfile(userId, username, avatarUrl); + // Step 2: Resolve UUID from server members list + if (serverId) { + try { + const members = await getServerMembers(serverId); + const match = members?.find( + (m: any) => + m?.users?.username?.toLowerCase() === username.toLowerCase() + ); + + if (match) { + const realId = + match.user_id || match.userId || match.users?.id || match.id; + + const avatarUrl = + match.users?.avatar_url || match.avatar_url || "/User_profil.png"; + + if (realId) { + await openProfile(String(realId), username, avatarUrl); + return; + } + } + } catch (err) { + console.error("Failed to resolve UUID from members list:", err); + } + } + + + await openProfile(userId, username, "/User_profil.png"); }, - [openProfile] + [openProfile, messages, serverId] ); const handleRoleMentionClick = useCallback( async (roleName: string) => {