Files
MoonTVPlus/src/components/MobileBottomNav.tsx
2025-12-22 00:47:45 +08:00

186 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* eslint-disable @typescript-eslint/no-explicit-any */
'use client';
import { Cat, Clover, Film, FolderOpen, Home, Radio, Star, Tv, Users } from 'lucide-react';
import Link from 'next/link';
import { usePathname, useSearchParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import { useWatchRoomContextSafe } from './WatchRoomProvider';
interface MobileBottomNavProps {
/**
* 主动指定当前激活的路径。当未提供时,自动使用 usePathname() 获取的路径。
*/
activePath?: string;
}
const MobileBottomNav = ({ activePath }: MobileBottomNavProps) => {
const pathname = usePathname();
const searchParams = useSearchParams();
const watchRoomContext = useWatchRoomContextSafe();
// 直接使用当前路由状态,确保立即响应路由变化
const getCurrentFullPath = () => {
const queryString = searchParams.toString();
return queryString ? `${pathname}?${queryString}` : pathname;
};
const currentActive = activePath ?? getCurrentFullPath();
const [navItems, setNavItems] = useState([
{ icon: Home, label: '首页', href: '/' },
{
icon: Film,
label: '电影',
href: '/douban?type=movie',
},
{
icon: Tv,
label: '剧集',
href: '/douban?type=tv',
},
{
icon: Cat,
label: '动漫',
href: '/douban?type=anime',
},
{
icon: Clover,
label: '综艺',
href: '/douban?type=show',
},
{
icon: Radio,
label: '直播',
href: '/live',
},
]);
useEffect(() => {
const runtimeConfig = (window as any).RUNTIME_CONFIG;
// 基础导航项(不包括观影室)
let items = [
{ icon: Home, label: '首页', href: '/' },
{
icon: Film,
label: '电影',
href: '/douban?type=movie',
},
{
icon: Tv,
label: '剧集',
href: '/douban?type=tv',
},
{
icon: Cat,
label: '动漫',
href: '/douban?type=anime',
},
{
icon: Clover,
label: '综艺',
href: '/douban?type=show',
},
{
icon: Radio,
label: '直播',
href: '/live',
},
];
// 如果配置了 OpenList添加私人影库入口
if (runtimeConfig?.OPENLIST_ENABLED) {
items.push({
icon: FolderOpen,
label: '私人影库',
href: '/private-library',
});
}
// 如果启用观影室,添加观影室入口
if (watchRoomContext?.isEnabled) {
items.push({
icon: Users,
label: '观影室',
href: '/watch-room',
});
}
// 添加自定义分类(如果有)
if (runtimeConfig?.CUSTOM_CATEGORIES?.length > 0) {
items.push({
icon: Star,
label: '自定义',
href: '/douban?type=custom',
});
}
setNavItems(items);
}, [watchRoomContext?.isEnabled]);
const isActive = (href: string) => {
const typeMatch = href.match(/type=([^&]+)/)?.[1];
// 解码URL以进行正确的比较
const decodedActive = decodeURIComponent(currentActive);
const decodedItemHref = decodeURIComponent(href);
return (
decodedActive === decodedItemHref ||
(decodedActive.startsWith('/douban') &&
decodedActive.includes(`type=${typeMatch}`))
);
};
return (
<nav
className='md:hidden fixed left-0 right-0 z-[600] bg-white/90 backdrop-blur-xl border-t border-gray-200/50 overflow-hidden dark:bg-gray-900/80 dark:border-gray-700/50'
style={{
/* 紧贴视口底部,同时在内部留出安全区高度 */
bottom: 0,
paddingBottom: 'env(safe-area-inset-bottom)',
minHeight: 'calc(3.5rem + env(safe-area-inset-bottom))',
}}
>
<ul className='flex items-center overflow-x-auto scrollbar-hide'>
{navItems.map((item) => {
const active = isActive(item.href);
return (
<li
key={item.href}
className='flex-shrink-0'
style={{ width: '20vw', minWidth: '20vw' }}
>
<Link
href={item.href}
prefetch={false}
className='flex flex-col items-center justify-center w-full h-14 gap-1 text-xs'
>
<item.icon
className={`h-6 w-6 ${active
? 'text-green-600 dark:text-green-400'
: 'text-gray-500 dark:text-gray-400'
}`}
/>
<span
className={
active
? 'text-green-600 dark:text-green-400'
: 'text-gray-600 dark:text-gray-300'
}
>
{item.label}
</span>
</Link>
</li>
);
})}
</ul>
</nav>
);
};
export default MobileBottomNav;