import { Link, useLocation } from 'react-router-dom';
import { Icon } from './Icon';
interface NavRailItem {
to: string;
icon: string;
label: string;
badge?: number;
}
const TOP_ITEMS: NavRailItem[] = [
{ to: '/messages', icon: 'chat_bubble', label: '消息' },
{ to: '/contacts', icon: 'contacts', label: '通讯录' },
{ to: '/favorites', icon: 'inventory_2', label: '收藏' },
{ to: '/files', icon: 'folder', label: '文件助手' },
];
const BOTTOM_ITEMS: NavRailItem[] = [
{ to: '/calls', icon: 'phone', label: '通话记录' },
{ to: '/settings', icon: 'settings', label: '设置' },
];
/** `/files` is `` (App.tsx) -- the real URL right after
* clicking "文件助手" is `/messages/slf`, which a plain (non-`end`) NavLink match makes "消息"
* light up for (a same-topicId prefix match) while "文件助手" itself (`to: '/files'`, a path that
* no longer exists once redirected) never matches at all. Overriding both items' active state off
* the real pathname instead of trusting NavLink's own prefix match fixes both sides of the mix-up. */
function isNavItemActive(to: string, pathname: string): boolean {
if (to === '/files') return pathname === '/messages/slf';
if (to === '/messages') return pathname.startsWith('/messages') && pathname !== '/messages/slf';
return pathname === to || pathname.startsWith(`${to}/`);
}
function NavRailButton({ item }: { item: NavRailItem }) {
const location = useLocation();
const isActive = isNavItemActive(item.to, location.pathname);
return (
// Plain Link, not NavLink -- NavLink computes its OWN `aria-current` from ITS OWN prefix-match
// logic unconditionally (confirmed by reading react-router's source: passing an `aria-current`
// prop only changes the STRING used, not whether it's applied at all, which stays gated on
// NavLink's internal isActive). That's exactly `/files`'s own problem above -- NavLink's naive
// match would light up "消息" for `/messages/slf`, not "文件助手" -- so a second, independent
// aria-current computation fighting the same override this file already needed for `className`
// is a real, verifiable a11y bug (screen readers and sighted highlighting would disagree on
// which icon is current). Driving both off the one already-correct `isActive` avoids that
// entirely.
{item.badge ? (
{item.badge}
) : null}
);
}
/** Persistent 64px icon rail, visible at >=640px (see App.tsx for the <640 bottom-tab fallback). */
export function NavRail({ selfInitial = '我', unreadMessages = 0 }: { selfInitial?: string; unreadMessages?: number }) {
return (
);
}