mirror of
https://github.com/MoonTechLab/LunaTV.git
synced 2026-02-22 02:16:10 +08:00
feat: favorites in aggreagte page
This commit is contained in:
@@ -2,11 +2,12 @@
|
||||
|
||||
'use client';
|
||||
|
||||
import { LinkIcon } from 'lucide-react';
|
||||
import { Heart, LinkIcon } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import { Suspense, useEffect, useState } from 'react';
|
||||
|
||||
import { isFavorited, toggleFavorite } from '@/lib/db.client';
|
||||
import { SearchResult } from '@/lib/types';
|
||||
|
||||
import PageLayout from '@/components/PageLayout';
|
||||
@@ -142,6 +143,88 @@ function AggregatePageClient() {
|
||||
// 详情映射,便于快速获取每个源的集数
|
||||
const sourceDetailMap = new Map(results.map((d) => [d.source, d]));
|
||||
|
||||
// 新增:播放源卡片组件,包含收藏逻辑
|
||||
const SourceCard = ({ src }: { src: SearchResult }) => {
|
||||
const d = sourceDetailMap.get(src.source);
|
||||
const epCount = d ? d.episodes.length : src.episodes.length;
|
||||
|
||||
const [favorited, setFavorited] = useState(false);
|
||||
|
||||
// 初次加载检查收藏状态
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
try {
|
||||
const fav = await isFavorited(src.source, src.id);
|
||||
setFavorited(fav);
|
||||
} catch {
|
||||
/* 忽略错误 */
|
||||
}
|
||||
})();
|
||||
}, [src.source, src.id]);
|
||||
|
||||
// 切换收藏状态
|
||||
const handleToggleFavorite = async (
|
||||
e: React.MouseEvent<HTMLSpanElement | SVGElement, MouseEvent>
|
||||
) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
try {
|
||||
const newState = await toggleFavorite(src.source, src.id, {
|
||||
title: src.title,
|
||||
source_name: src.source_name,
|
||||
year: src.year,
|
||||
cover: src.poster,
|
||||
total_episodes: src.episodes.length,
|
||||
save_time: Date.now(),
|
||||
});
|
||||
setFavorited(newState);
|
||||
} catch {
|
||||
/* 忽略错误 */
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<a
|
||||
key={src.source}
|
||||
href={`/play?source=${src.source}&id=${
|
||||
src.id
|
||||
}&title=${encodeURIComponent(src.title)}${
|
||||
src.year ? `&year=${src.year}` : ''
|
||||
}&from=aggregate`}
|
||||
className='group relative flex items-center justify-center w-full h-14 bg-gray-500/80 hover:bg-green-500 dark:bg-gray-700/80 dark:hover:bg-green-600 rounded-lg transition-colors'
|
||||
>
|
||||
{/* 收藏爱心 */}
|
||||
<span
|
||||
onClick={handleToggleFavorite}
|
||||
title={favorited ? '移除收藏' : '加入收藏'}
|
||||
className={`absolute top-[2px] left-1 inline-flex items-center justify-center cursor-pointer transition-opacity duration-200 ${
|
||||
favorited ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'
|
||||
}`}
|
||||
>
|
||||
<Heart
|
||||
className={`w-5 h-5 ${
|
||||
favorited ? 'text-red-500' : 'text-white/90'
|
||||
}`}
|
||||
strokeWidth={2}
|
||||
fill={favorited ? 'currentColor' : 'none'}
|
||||
/>
|
||||
</span>
|
||||
|
||||
{/* 名称 */}
|
||||
<span className='px-1 text-white text-sm font-medium truncate whitespace-nowrap'>
|
||||
{src.source_name}
|
||||
</span>
|
||||
{/* 集数徽标 */}
|
||||
{epCount && epCount > 1 ? (
|
||||
<span className='absolute top-[2px] right-1 text-[10px] font-semibold text-green-900 bg-green-300/90 rounded-full px-1 pointer-events-none'>
|
||||
{epCount}集
|
||||
</span>
|
||||
) : null}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<PageLayout activePath='/aggregate'>
|
||||
<div className='flex flex-col min-h-full px-2 sm:px-10 pt-4 sm:pt-8 pb-[calc(3.5rem+env(safe-area-inset-bottom))] overflow-visible'>
|
||||
@@ -247,32 +330,9 @@ function AggregatePageClient() {
|
||||
</div>
|
||||
</div>
|
||||
<div className='grid grid-cols-3 gap-2 sm:grid-cols-[repeat(auto-fill,_minmax(6rem,_1fr))] sm:gap-4 justify-start'>
|
||||
{uniqueSources.map((src) => {
|
||||
const d = sourceDetailMap.get(src.source);
|
||||
const epCount = d ? d.episodes.length : src.episodes.length;
|
||||
return (
|
||||
<a
|
||||
key={src.source}
|
||||
href={`/play?source=${src.source}&id=${
|
||||
src.id
|
||||
}&title=${encodeURIComponent(src.title)}${
|
||||
src.year ? `&year=${src.year}` : ''
|
||||
}&from=aggregate`}
|
||||
className='relative flex items-center justify-center w-full h-14 bg-gray-500/80 hover:bg-green-500 dark:bg-gray-700/80 dark:hover:bg-green-600 rounded-lg transition-colors'
|
||||
>
|
||||
{/* 名称 */}
|
||||
<span className='px-1 text-white text-sm font-medium truncate whitespace-nowrap'>
|
||||
{src.source_name}
|
||||
</span>
|
||||
{/* 集数徽标 */}
|
||||
{epCount && epCount > 1 ? (
|
||||
<span className='absolute top-[2px] right-1 text-[10px] font-semibold text-green-900 bg-green-300/90 rounded-full px-1 pointer-events-none'>
|
||||
{epCount}集
|
||||
</span>
|
||||
) : null}
|
||||
</a>
|
||||
);
|
||||
})}
|
||||
{uniqueSources.map((src) => (
|
||||
<SourceCard key={src.source} src={src} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user