fix: polish favorites page implementation

This commit is contained in:
kuekhaoyang
2026-04-07 22:05:39 +08:00
parent ddcce8341a
commit f4f5597a80
6 changed files with 124 additions and 142 deletions
+15 -11
View File
@@ -1,6 +1,6 @@
'use client';
import { useState, useRef, useCallback, useEffect, memo } from 'react';
import { useState, useRef, useCallback, useEffect, useMemo, memo } from 'react';
import { VideoCard } from '@/components/search/VideoCard';
import { FavoritesEmptyState } from './FavoritesEmptyState';
import type { FavoriteItem, Video } from '@/lib/types';
@@ -20,16 +20,20 @@ export const FavoritesGrid = memo(function FavoritesGrid({
const observerRef = useRef<IntersectionObserver | null>(null);
// Convert FavoriteItem to Video format
const videos: Video[] = favorites.map((favorite) => ({
vod_id: favorite.videoId,
vod_name: favorite.title,
vod_pic: favorite.poster,
vod_remarks: favorite.remarks,
vod_year: favorite.year,
type_name: favorite.type,
source: favorite.source,
sourceName: favorite.sourceName,
}));
const videos: Video[] = useMemo(
() =>
favorites.map((favorite) => ({
vod_id: favorite.videoId,
vod_name: favorite.title,
vod_pic: favorite.poster,
vod_remarks: favorite.remarks,
vod_year: favorite.year,
type_name: favorite.type,
source: favorite.source,
sourceName: favorite.sourceName,
})),
[favorites]
);
// Setup intersection observer for infinite scroll
useEffect(() => {
@@ -0,0 +1,93 @@
'use client';
import { useMemo, useState } from 'react';
import { useRouter } from 'next/navigation';
import { Navbar } from '@/components/layout/Navbar';
import { FavoritesPageHeader } from '@/components/favorites/FavoritesPageHeader';
import { FavoritesGrid } from '@/components/favorites/FavoritesGrid';
import { FavoritesSidebar } from '@/components/favorites/FavoritesSidebar';
import { WatchHistorySidebar } from '@/components/history/WatchHistorySidebar';
import { ConfirmDialog } from '@/components/ui/ConfirmDialog';
import { useFavorites } from '@/lib/store/favorites-store';
interface FavoritesPageContentProps {
isPremium?: boolean;
}
export function FavoritesPageContent({
isPremium = false,
}: FavoritesPageContentProps) {
const router = useRouter();
const { favorites, clearFavorites } = useFavorites(isPremium);
const [sortBy, setSortBy] = useState<'date' | 'title'>('date');
const [isClearDialogOpen, setIsClearDialogOpen] = useState(false);
const sortedFavorites = useMemo(() => {
if (sortBy === 'title') {
return [...favorites].sort((a, b) => a.title.localeCompare(b.title, 'zh-CN'));
}
return favorites;
}, [favorites, sortBy]);
return (
<div
className={
isPremium
? 'min-h-screen bg-black'
: 'min-h-screen bg-[var(--bg-color)] bg-[image:var(--bg-image)] bg-fixed'
}
>
<Navbar
onReset={() => router.push(isPremium ? '/premium' : '/')}
isPremiumMode={isPremium}
/>
<main className="max-w-7xl mx-auto px-4 py-8 space-y-8">
<FavoritesPageHeader
count={favorites.length}
sortBy={sortBy}
onSortChange={setSortBy}
onClearAll={() => setIsClearDialogOpen(true)}
/>
<FavoritesGrid favorites={sortedFavorites} isPremium={isPremium} />
</main>
<FavoritesSidebar isPremium={isPremium} />
<WatchHistorySidebar isPremium={isPremium} />
<ConfirmDialog
isOpen={isClearDialogOpen}
title="清空收藏"
message="确定要清空所有收藏吗?此操作不可撤销。"
confirmText="清空"
cancelText="取消"
onConfirm={() => {
clearFavorites();
setIsClearDialogOpen(false);
}}
onCancel={() => setIsClearDialogOpen(false)}
dangerous
/>
</div>
);
}
interface FavoritesPageFallbackProps {
isPremium?: boolean;
}
export function FavoritesPageFallback({
isPremium = false,
}: FavoritesPageFallbackProps) {
return (
<div
className={`min-h-screen flex items-center justify-center${
isPremium ? ' bg-black' : ''
}`}
>
<div className="animate-spin rounded-full h-16 w-16 border-4 border-[var(--accent-color)] border-t-transparent" />
</div>
);
}
+1 -1
View File
@@ -73,7 +73,7 @@ export function FavoritesPageHeader({
onClick={onClearAll}
className="px-4 py-2 rounded-[var(--radius-full)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)] transition-all text-sm flex items-center gap-2"
>
<Icons.Trash2 size={16} />
<Icons.Trash size={16} />
</button>
)}
+1
View File
@@ -109,6 +109,7 @@ export function Navbar({ onReset, isPremiumMode = false }: NavbarProps) {
href={favoritesHref}
className="w-8 h-8 sm:w-10 sm:h-10 flex items-center justify-center rounded-[var(--radius-full)] bg-[var(--glass-bg)] border border-[var(--glass-border)] text-[var(--text-color)] hover:bg-[color-mix(in_srgb,var(--accent-color)_10%,transparent)] transition-all duration-200 cursor-pointer"
aria-label="我的收藏"
data-focusable
>
<Icons.Heart size={20} />
</Link>