/** * @file components/sidebar/ChannelSidebar.tsx * @description Channel sidebar component listing categories and channels with actions for creation and editing. */ "use client"; import { useState } from "react"; import { useParams } from "next/navigation"; import { useActiveServer } from "@/lib/context/ServerContext"; import { useSidebarStore } from "@/lib/stores/useSidebarStore"; import { CreateChannelModal } from "@/components/modals/CreateChannelModal"; import { EditChannelModal } from "@/components/modals/EditChannelModal"; import { ChevronDown, ChevronRight, PanelLeftClose, Plus, Settings, } from "lucide-react"; import type { Channel } from "@/db/schema"; import Link from "next/link"; /** Renders the channel navigation sidebar for the active server. */ export function ChannelSidebar() { const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const [editingChannel, setEditingChannel] = useState(null); const [selectedCategoryId, setSelectedCategoryId] = useState( null, ); const [collapsedCategories, setCollapsedCategories] = useState< Record >({}); const params = useParams(); const currentChannelId = params?.channelId as string; const { activeServer } = useActiveServer(); const { closeNav, toggleNav } = useSidebarStore(); if (!activeServer) return null; /** Toggles the collapsed state of a channel category. */ const toggleCategory = (categoryId: string) => { setCollapsedCategories((prev) => ({ ...prev, [categoryId]: !prev[categoryId], })); }; /** Handles mobile navigation closure when clicking a channel link. */ const handleChannelClick = () => { if (!window.matchMedia("(min-width: 768px)").matches) { closeNav(); } }; /** Opens the edit channel modal for a specific channel. */ const handleOpenSettings = (e: React.MouseEvent, channel: Channel) => { e.preventDefault(); e.stopPropagation(); setEditingChannel(channel); }; /** Opens the create channel modal for a specific category or uncategorized. */ const handleOpenCreateModal = (categoryId: string | null = null) => { setSelectedCategoryId(categoryId); setIsCreateModalOpen(true); }; const uncategorizedChannels = activeServer.channels.filter( (c) => !c.categoryId, ); const categoriesList = (activeServer.categories || []).map((cat) => ({ ...cat, channels: activeServer.channels.filter((c) => c.categoryId === cat.id), })); /** Renders an individual channel item in the list. */ const renderChannelItem = (channel: Channel) => { const isActive = currentChannelId === channel.id; return (
# {channel.name}
{!channel.isDefault && ( )} ); }; return ( <>
{/* Server Header */}
{activeServer.name}
{/* Channel List */}
{!collapsedCategories["uncategorized"] && (
{uncategorizedChannels.map(renderChannelItem)}
)}
{/* 2. Custom Kategorien */} {categoriesList.map((category) => { const isCollapsed = collapsedCategories[category.id]; return (
{!isCollapsed && (
{category.channels.map(renderChannelItem)}
)}
); })}
{/* Modals */} setIsCreateModalOpen(false)} serverId={activeServer.id} /> setEditingChannel(null)} channel={editingChannel} /> ); }