diff --git a/src/__mocks__/heroui-react.tsx b/src/__mocks__/heroui-react.tsx index 11ff17d..d572ca5 100644 --- a/src/__mocks__/heroui-react.tsx +++ b/src/__mocks__/heroui-react.tsx @@ -199,6 +199,45 @@ export const Card = Object.assign( } ); +export const Link = ({ + children, + href, + ...props +}: React.AnchorHTMLAttributes) => ( + + {children} + +); + +export const Tooltip = Object.assign( + ({ children, ...props }: React.HTMLAttributes) => ( +
{children}
+ ), + { + Trigger: ({ children, ...props }: React.HTMLAttributes) => ( +
{children}
+ ), + Content: ({ children, ...props }: React.HTMLAttributes) => ( +
{children}
+ ), + } +); + +export const Badge = Object.assign( + ({ children, ...props }: React.HTMLAttributes) => ( + {children} + ), + { + Label: ({ children, ...props }: React.HTMLAttributes) => ( + {children} + ), + } +); + +export const Separator = (props: React.HTMLAttributes) => ( +
+); + const ModalRoot = ({ children, state, diff --git a/src/app/page.tsx b/src/app/page.tsx index 09b362e..7c5be2e 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -11,10 +11,6 @@ import { } from '@heroui/react'; import { Suspense, useEffect, useState } from 'react'; -import { - BangumiCalendarData, - GetBangumiCalendarData, -} from '@/lib/bangumi.client'; // 客户端收藏 API import { clearAllFavorites, @@ -38,9 +34,6 @@ function HomeClient() { const [hotMovies, setHotMovies] = useState([]); const [hotTvShows, setHotTvShows] = useState([]); const [hotVarietyShows, setHotVarietyShows] = useState([]); - const [bangumiCalendarData, setBangumiCalendarData] = useState< - BangumiCalendarData[] - >([]); const [loading, setLoading] = useState(true); const { announcement } = useSite(); @@ -79,7 +72,7 @@ function HomeClient() { setLoading(true); // 并行获取热门电影、热门剧集和热门综艺 - const [moviesData, tvShowsData, varietyShowsData, bangumiCalendarData] = + const [moviesData, tvShowsData, varietyShowsData] = await Promise.all([ getDoubanCategories({ kind: 'movie', @@ -88,7 +81,6 @@ function HomeClient() { }), getDoubanCategories({ kind: 'tv', category: 'tv', type: 'tv' }), getDoubanCategories({ kind: 'tv', category: 'show', type: 'show' }), - GetBangumiCalendarData(), ]); if (moviesData.code === 200) { @@ -102,8 +94,6 @@ function HomeClient() { if (varietyShowsData.code === 200) { setHotVarietyShows(varietyShowsData.list); } - - setBangumiCalendarData(bangumiCalendarData); } catch (error) { console.error('获取推荐数据失败:', error); } finally { @@ -322,75 +312,6 @@ function HomeClient() { - {/* 每日新番放送 */} - - -
- Bangumi - 新番放送 -
- 查看更多 -
- - {loading - ? // 加载状态显示灰色占位数据 - Array.from({ length: 8 }).map((_, index) => ( -
- - -
- )) - : // 展示当前日期的番剧 - (() => { - // 获取当前日期对应的星期 - const today = new Date(); - const weekdays = [ - 'Sun', - 'Mon', - 'Tue', - 'Wed', - 'Thu', - 'Fri', - 'Sat', - ]; - const currentWeekday = weekdays[today.getDay()]; - - // 找到当前星期对应的番剧数据 - const todayAnimes = - bangumiCalendarData.find( - (item) => item.weekday.en === currentWeekday - )?.items || []; - - return todayAnimes.map((anime, index) => ( -
- -
- )); - })()} -
-
- {/* 热门综艺 */} diff --git a/src/app/search/page.tsx b/src/app/search/page.tsx index c576c3b..55f35e4 100644 --- a/src/app/search/page.tsx +++ b/src/app/search/page.tsx @@ -1037,7 +1037,7 @@ function SearchPageClient() { value={searchQuery} onChange={handleInputChange} onFocus={handleInputFocus} - placeholder='搜索电影、电视剧、短剧...' + placeholder='搜索电影、电视剧...' autoComplete="off" fullWidth className='pl-10 pr-12' diff --git a/src/components/MobileBottomNav.tsx b/src/components/MobileBottomNav.tsx index 43a2e3e..b1eb879 100644 --- a/src/components/MobileBottomNav.tsx +++ b/src/components/MobileBottomNav.tsx @@ -2,7 +2,7 @@ 'use client'; -import { Cat, Clover, Film, Home, Play, Radio, Star, Tv } from 'lucide-react'; +import { Clover, Film, Home, Star, Tv } from 'lucide-react'; import { Button, Card, ScrollShadow } from '@heroui/react'; import { usePathname, useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; @@ -33,26 +33,11 @@ const MobileBottomNav = ({ activePath }: MobileBottomNavProps) => { label: '剧集', href: '/douban?type=tv', }, - { - icon: Play, - label: '短剧', - href: '/shortdrama', - }, - { - icon: Cat, - label: '动漫', - href: '/douban?type=anime', - }, { icon: Clover, label: '综艺', href: '/douban?type=show', }, - { - icon: Radio, - label: '直播', - href: '/live', - }, ]); useEffect(() => { @@ -81,11 +66,6 @@ const MobileBottomNav = ({ activePath }: MobileBottomNavProps) => { return true; } - // 短剧页面的特殊处理 - if (href === '/shortdrama' && decodedActive.startsWith('/shortdrama')) { - return true; - } - // 豆瓣页面的类型匹配 if (decodedActive.startsWith('/douban') && typeMatch && decodedActive.includes(`type=${typeMatch}`)) { diff --git a/src/components/Sidebar.tsx b/src/components/Sidebar.tsx index 0c01312..db7db44 100644 --- a/src/components/Sidebar.tsx +++ b/src/components/Sidebar.tsx @@ -3,14 +3,11 @@ 'use client'; import { - Cat, Clover, ExternalLink, Film, Home, Menu, - PlayCircle, - Radio, Search, Star, Tv, @@ -176,31 +173,16 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => { label: '电影', href: '/douban?type=movie', }, - { - icon: PlayCircle, - label: '短剧', - href: '/shortdrama', - }, { 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(() => { @@ -327,8 +309,7 @@ const Sidebar = ({ onToggle, activePath = '/' }: SidebarProps) => { const isActive = decodedActive === decodedItemHref || (decodedActive.startsWith('/douban') && - decodedActive.includes(`type=${typeMatch}`)) || - (item.href === '/shortdrama' && decodedActive.startsWith('/shortdrama')); + decodedActive.includes(`type=${typeMatch}`)); const Icon = item.icon; return (
diff --git a/src/components/ThemeToggle.tsx b/src/components/ThemeToggle.tsx index b4b2225..40d2da8 100644 --- a/src/components/ThemeToggle.tsx +++ b/src/components/ThemeToggle.tsx @@ -1,48 +1,20 @@ -/* eslint-disable @typescript-eslint/no-explicit-any,react-hooks/exhaustive-deps */ +/* eslint-disable @typescript-eslint/no-explicit-any */ 'use client'; -import { MessageCircle, Moon, Sun } from 'lucide-react'; +import { Moon, Sun } from 'lucide-react'; import { usePathname } from 'next/navigation'; import { useTheme } from 'next-themes'; -import { useEffect, useState, useCallback } from 'react'; -import { Badge } from '@heroui/react'; -import { ChatModal } from './ChatModal'; +import { useEffect, useState } from 'react'; + import { AppIconButton } from './ui/HeroPrimitives'; -import { useWebSocket } from '../hooks/useWebSocket'; -import { WebSocketMessage } from '../lib/types'; export function ThemeToggle() { const [mounted, setMounted] = useState(false); - const [isChatModalOpen, setIsChatModalOpen] = useState(false); - const [messageCount, setMessageCount] = useState(0); - const [chatCount, setChatCount] = useState(0); - const [friendRequestCount, setFriendRequestCount] = useState(0); const [isMobile, setIsMobile] = useState(false); const { setTheme, resolvedTheme } = useTheme(); const pathname = usePathname(); - // 不再在ThemeToggle中创建独立的WebSocket连接 - // 改为依赖ChatModal传递的消息计数 - - // 直接使用ChatModal传来的消息计数 - const handleMessageCountFromModal = useCallback((totalCount: number) => { - console.log('📊 [ThemeToggle] 收到ChatModal传来的消息计数:', totalCount); - setMessageCount(totalCount); - }, []); - - // 处理聊天消息计数重置(当用户查看对话时) - const handleChatCountReset = useCallback((resetCount: number) => { - console.log('💬 [ThemeToggle] 重置聊天计数:', resetCount); - // 这些回调函数现在主要用于同步状态,实际计数由ChatModal管理 - }, []); - - // 处理好友请求计数重置(当用户查看好友请求时) - const handleFriendRequestCountReset = useCallback((resetCount: number) => { - console.log('👥 [ThemeToggle] 重置好友请求计数:', resetCount); - // 这些回调函数现在主要用于同步状态,实际计数由ChatModal管理 - }, []); - const setThemeColor = (theme?: string) => { const meta = document.querySelector('meta[name="theme-color"]'); if (!meta) { @@ -96,28 +68,9 @@ export function ThemeToggle() { }); }; - // 检查是否在登录页面 - const isLoginPage = pathname === '/login'; - return ( <>
- {/* 聊天按钮 - 在登录页面不显示 */} - {!isLoginPage && ( - setIsChatModalOpen(true)} - size={isMobile ? 'sm' : 'md'} - aria-label='Open chat' - > - {messageCount > 0 && ( - - {messageCount > 99 ? '99+' : messageCount} - - )} - - - )} - {/* 主题切换按钮 */}
- - {/* 聊天模态框 - 在登录页面不渲染 */} - {!isLoginPage && ( - setIsChatModalOpen(false)} - onMessageCountChange={handleMessageCountFromModal} - onChatCountReset={handleChatCountReset} - onFriendRequestCountReset={handleFriendRequestCountReset} - /> - )} ); } diff --git a/src/components/__tests__/HiddenFeOptions.test.tsx b/src/components/__tests__/HiddenFeOptions.test.tsx new file mode 100644 index 0000000..828e3f7 --- /dev/null +++ b/src/components/__tests__/HiddenFeOptions.test.tsx @@ -0,0 +1,64 @@ +import { render, screen, waitFor } from '@testing-library/react'; + +import MobileBottomNav from '../MobileBottomNav'; +import Sidebar from '../Sidebar'; +import { ThemeToggle } from '../ThemeToggle'; + +const push = jest.fn(); + +jest.mock('next/navigation', () => ({ + usePathname: () => '/', + useRouter: () => ({ push }), + useSearchParams: () => new URLSearchParams(), +})); + +jest.mock('next-themes', () => ({ + useTheme: () => ({ + resolvedTheme: 'dark', + setTheme: jest.fn(), + }), +})); + +jest.mock('../ChatModal', () => ({ + ChatModal: () =>
聊天
, +})); + +describe('hidden front-end options', () => { + beforeEach(() => { + push.mockClear(); + localStorage.clear(); + }); + + it('does not render manga, short drama, or live in desktop navigation', () => { + render(); + + expect(screen.queryByRole('button', { name: '动漫' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: '短剧' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: '直播' })).not.toBeInTheDocument(); + expect(screen.getByRole('button', { name: '电影' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '剧集' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '综艺' })).toBeInTheDocument(); + }); + + it('does not render manga, short drama, or live in mobile navigation', () => { + render(); + + expect(screen.queryByRole('button', { name: '动漫' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: '短剧' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: '直播' })).not.toBeInTheDocument(); + expect(screen.getByRole('button', { name: '电影' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '剧集' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: '综艺' })).toBeInTheDocument(); + }); + + it('does not render the chat entry point or chat modal', async () => { + render(); + + await waitFor(() => { + expect(screen.getByRole('button', { name: 'Toggle theme' })).toBeInTheDocument(); + }); + + expect(screen.queryByRole('button', { name: 'Open chat' })).not.toBeInTheDocument(); + expect(screen.queryByRole('dialog', { name: '聊天' })).not.toBeInTheDocument(); + }); +});