/** * @file components/friends/AddFriendTab.tsx * @description Tab component allowing users to search for others by username, view live results, and send or track friend requests. */ "use client"; import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; import { ActionButton } from "../ui/ActionButton"; import { UserAvatar } from "../ui/UserAvatar"; import { FriendsViewProps, FriendUser } from "./FriendsView"; /** * Renders the add friend tab containing the live search input, results list, and request management. * * @param {FriendsViewProps} props - The component props. * @param {string} props.currentUserId - The unique identifier of the currently logged-in user. * @param {Friendship[]} props.initialFriendships - Array of existing friendships. * @returns {JSX.Element} The rendered add friend tab container. */ export function AddFriendTab({ currentUserId, initialFriendships, }: FriendsViewProps) { const router = useRouter(); const [addUsername, setAddUsername] = useState(""); const [searchResults, setSearchResults] = useState([]); const [isSearching, setIsSearching] = useState(false); const [addError, setAddError] = useState(""); const [addSuccess, setAddSuccess] = useState(""); const [loadingUsername, setLoadingUsername] = useState(null); // Live search as you type, with debounce useEffect(() => { const query = addUsername.trim(); if (query.length < 2) { setSearchResults([]); return; } const timer = setTimeout(async () => { setIsSearching(true); try { const res = await fetch( `/api/users/search?q=${encodeURIComponent(query)}`, ); if (res.ok) { const data = await res.json(); setSearchResults(data); } } catch (err) { console.error("Search error:", err); } finally { setIsSearching(false); } }, 250); return () => clearTimeout(timer); }, [addUsername]); /** * Checks the friendship status for a specific user ID against the current user. * * @function getFriendshipStatus * @param {string} userId - The unique identifier of the user to check status for. * @returns {string | null} The friendship status string ("PENDING", "ACCEPTED", "BLOCKED") or null if none exists. */ const getFriendshipStatus = (userId: string) => { const friendship = initialFriendships.find( (f) => (f.senderId === userId && f.receiverId === currentUserId) || (f.receiverId === userId && f.senderId === currentUserId), ); return friendship ? friendship.status : null; }; /** * Sends a friend request to a specified username asynchronously. * * @async * @function sendRequestToUser * @param {string} username - The target username to send a friend request to. * @returns {Promise} Resolves when the request completes. */ const sendRequestToUser = async (username: string) => { setAddError(""); setAddSuccess(""); setLoadingUsername(username); try { const res = await fetch("/api/friends", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username }), }); const data = await res.json(); if (!res.ok) { setAddError(data.error || "Failed to send request"); } else { setAddSuccess(`Success! Friend request sent to ${username}.`); setAddUsername(""); setSearchResults([]); router.refresh(); } } catch { setAddError("Internal server error"); } finally { setLoadingUsername(null); } }; return (

Add Friend

You can add friends by searching for their username.

{/* Search field */}
setAddUsername(e.target.value)} placeholder="Type a username..." className="w-full bg-transparent text-sm text-foreground placeholder:text-muted/60 focus:outline-none" />
{/* Errors */} {addError && (

{addError}

)} {addSuccess &&

{addSuccess}

} {/* Search Results */} {addUsername.trim().length >= 2 && (
{isSearching ? (
Searching...
) : searchResults.length === 0 ? (
No users found matching "{addUsername}"
) : (
{searchResults.map((user) => { if (user.id === currentUserId) return null; // Hide your own account const friendshipStatus = getFriendshipStatus(user.id); return (
{user.username}
{/* Status or Add Button */} {!friendshipStatus ? ( sendRequestToUser(user.username)} > {loadingUsername === user.username ? "Sending..." : "Add Friend"} ) : ( {friendshipStatus === "ACCEPTED" ? "Friend" : "Pending"} )}
); })}
)}
)}
); }