import { Home, Star, LayoutDashboard, FolderGit2, FolderKanban, ArrowLeft, FileText, Link2, } from 'lucide-react'; import { useNavigate, useLocation, useSearchParams, Link } from 'react-router-dom'; import type { Board } from '../api/types'; import type { PostHeading } from '../utils/postHeadings'; import { useAuth } from '../hooks/useAuth'; import { cn } from '@/lib/utils'; import { Skeleton } from '@/components/ui/skeleton'; import { buildHomeUrl, parseFeedSort } from './FeedSortBar'; import { navigateFeed } from '../utils/feedCache'; import BoardIconDisplay from './BoardIconDisplay'; import { getBoardThemeIndex } from '../utils/boardTheme'; import ArticleOutline from './ArticleOutline'; import { useSitePages } from '../hooks/useSitePages'; import { pagePath } from '../utils/permalink'; import { useForumLimits } from '../hooks/useForumLimits'; // 内容页不参与左侧栏高亮(非 feed 浏览上下文) const NEUTRAL_SIDEBAR_PREFIXES = ['/post/', '/profile', '/user/', '/page/']; export function isNeutralSidebarRoute(pathname: string): boolean { return NEUTRAL_SIDEBAR_PREFIXES.some(prefix => pathname.startsWith(prefix)); } function resolveMenuKey(pathname: string, activeBoard: number, keyword = ''): string | null { if (isNeutralSidebarRoute(pathname)) return null; // 搜索结果不属于「全部帖子」或某一板块,取消侧栏选中高亮 if (keyword.trim()) return null; if (pathname.startsWith('/favorites')) return 'favorites'; if (pathname.startsWith('/projects')) return 'projects'; if (pathname.startsWith('/links')) return 'links'; if (pathname.startsWith('/page/')) return 'pages'; if (pathname.startsWith('/admin')) return 'admin'; return activeBoard === 0 ? 'all' : String(activeBoard); } interface Props { boards: Board[]; activeBoard: number; onSelectBoard: (id: number) => void; /** 板块列表首次拉取中 */ boardsLoading?: boolean; /** 帖子详情:左侧切换为文章目录 */ outlineMode?: boolean; outlineHeadings?: PostHeading[]; outlineScrollRoot?: HTMLElement | null; outlineTitle?: string; } export default function Sidebar({ boards, activeBoard, onSelectBoard, boardsLoading = false, outlineMode = false, outlineHeadings = [], outlineScrollRoot = null, outlineTitle, }: Props) { const nav = useNavigate(); const loc = useLocation(); const [params] = useSearchParams(); const sort = parseFeedSort(params.get('sort')); const { user } = useAuth(); const isAdmin = user?.role === 'admin'; const { navPages } = useSitePages(); const { limits } = useForumLimits(); const showFriendLinksNav = limits.nav_show_friend_links !== false; const showSiteSection = navPages.length > 0 || showFriendLinksNav; const keyword = params.get('keyword') || ''; const menuKey = resolveMenuKey(loc.pathname, activeBoard, keyword); const permalinkOpts = { permalink: limits }; const feedNavLink = ( key: string, to: string, label: React.ReactNode, icon: React.ReactNode, selectId: number, className?: string, trailing?: React.ReactNode, ) => ( { e.preventDefault(); onSelectBoard(selectId); navigateFeed(nav, to); }} > {icon} {label} {trailing} ); const navItem = (key: string, label: React.ReactNode, icon?: React.ReactNode, onClick?: () => void) => ( ); if (outlineMode) { return ( ); } return ( ); }