/** * @file components/layout/MemberSidebar.tsx * @description Member sidebar component displaying server members with desktop sliding panel and mobile drawer functionality. */ "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 { User } from "@/db/schema"; /** Properties for the MemberHeader component. */ interface MemberHeaderProps { title: string; onClose: () => void; } /** Renders the header section of the member sidebar with a title and close button. */ function MemberHeader({ title, onClose }: MemberHeaderProps) { return (
Members
); } /** Displays the list of members for the active server in a responsive sidebar or drawer layout. */ export function MemberSidebar() { const { isMembersOpen, closeMembers } = useSidebarStore(); const { activeServer } = useActiveServer(); const desktopSidebarRef = useRef(null); const [members, setMembers] = useState([]); const [isLoading, setIsLoading] = useState(false); 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]); useEffect(() => { function handleClickOutside(event: MouseEvent) { const target = event.target as HTMLElement; if (target.closest('button[title*="Mitgliederliste"]')) return; if ( isMembersOpen && desktopSidebarRef.current && !desktopSidebarRef.current.contains(target) ) { closeMembers(); } } if (isMembersOpen) { document.addEventListener("mousedown", handleClickOutside); } return () => document.removeEventListener("mousedown", handleClickOutside); }, [isMembersOpen, closeMembers]); /** Renders either a loading spinner or the populated member list depending on state. */ const renderContent = () => { if (isLoading && members.length === 0) { return (
); } return ; }; return ( <> {/* Desktop View */} {/* Mobile Backdrop & Drawer */} {isMembersOpen && (
)}
{renderContent()}
); }