/** * @file components/layout/MemberSidebar.tsx * @description Sidebar component fetching and displaying the server member list with responsive overlay support. */ "use client"; import { useEffect, useRef, useState } from "react"; import { useSidebarStore } from "@/lib/stores/useSidebarStore"; import { useActiveServer } from "@/lib/context/ServerContext"; import { MemberList } from "@/components/members/MemberList"; import { X, Loader2 } from "lucide-react"; import { clsx } from "clsx"; import type { User } from "@/db/schema"; /** Props for the MemberHeader component. */ interface MemberHeaderProps { title: string; onClose: () => void; } /** Header element for the member sidebar with title and close button. */ function MemberHeader({ title, onClose }: MemberHeaderProps) { return (
Members
); } /** Collapsible sidebar component displaying members of the currently active server. */ export function MemberSidebar() { const { isMembersOpen, closeMembers } = useSidebarStore(); const { activeServer } = useActiveServer(); const desktopSidebarRef = useRef(null); const [members, setMembers] = useState([]); const [isLoading, setIsLoading] = useState(false); /** Fetches members for the active server and manages loading states. */ useEffect(() => { if (!activeServer?.id) { setMembers([]); return; } const controller = new AbortController(); async function fetchMembers() { try { setIsLoading(true); const res = await fetch(`/api/servers/${activeServer?.id}/members`, { signal: controller.signal, }); if (res.ok) { const data: User[] = await res.json(); setMembers(data); } } catch (err: unknown) { if (err instanceof Error && err.name !== "AbortError") { console.error("Error loading members:", err); } } finally { setIsLoading(false); } } fetchMembers(); return () => { controller.abort(); }; }, [activeServer?.id]); /** Closes the members sidebar when clicking outside of it */ useEffect(() => { function handleClickOutside(event: MouseEvent) { const target = event.target as HTMLElement; if (document.querySelector("[data-user-profile-popover]") !== null) { return; } if ( isMembersOpen && desktopSidebarRef.current && !desktopSidebarRef.current.contains(target) ) { closeMembers(); } } if (isMembersOpen) { document.addEventListener("mousedown", handleClickOutside); } return () => document.removeEventListener("mousedown", handleClickOutside); }, [isMembersOpen, closeMembers]); /** Renders loading indicator or the member list depending on state. */ const renderContent = () => { if (isLoading && members.length === 0) { return (
); } return ; }; return ( <> {isMembersOpen && (
)} ); }