Spaces:
Sleeping
Sleeping
File size: 5,467 Bytes
4bea261 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | import { useEffect, useState, useRef } from 'react';
import MovieSection from './MovieSection.jsx';
import CombinedRegionSection from './CombinedRegionSection.jsx';
const SECTION_CONFIGS = [
{ type: 'newest', title: 'Phim mới cập nhật', href: '/danh-sach/phim-moi-cap-nhat' },
{ type: 'series', title: 'Phim bộ đặc sắc', href: '/danh-sach/phim-bo' },
{ type: 'single', title: 'Phim Điện Ảnh Mới Coóng', href: '/danh-sach/phim-le' },
{ category: 'phim-chieu-rap', title: 'Mãn Nhãn với Phim Chiếu Rạp', href: '/the-loai/phim-chieu-rap' },
{ type: 'cartoon', title: 'Thế giới hoạt hình', href: '/danh-sach/hoat-hinh' },
{ country: 'trung-quoc', title: 'Phim Trung Quốc mới', href: '/quoc-gia/trung-quoc' },
{ country: 'han-quoc', title: 'Phim Hàn Quốc mới', href: '/quoc-gia/han-quoc' },
{ country: 'au-my', title: 'Phim Âu-Mỹ mới', href: '/quoc-gia/au-my' }
];
export default function HomeSectionsLoader() {
const [loadedSections, setLoadedSections] = useState([]);
const [loading, setLoading] = useState(false);
const [currentIndex, setCurrentIndex] = useState(0);
const loaderRef = useRef(null);
const fetchSectionData = async (config) => {
try {
const params = new URLSearchParams();
if (config.type) params.append('type', config.type);
if (config.category) params.append('category', config.category);
if (config.country) params.append('country', config.country);
const res = await fetch(`/api/movies?${params.toString()}`);
if (!res.ok) throw new Error('Failed to fetch movies');
const data = await res.json();
return data.items || [];
} catch (e) {
console.error(`[HomeSectionsLoader] Error loading ${config.title}:`, e);
return [];
}
};
// Tải danh mục tiếp theo khi cuộn xuống
const loadNextSection = async () => {
if (loading || currentIndex >= SECTION_CONFIGS.length) return;
setLoading(true);
const config = SECTION_CONFIGS[currentIndex];
const movies = await fetchSectionData(config);
if (movies.length > 0) {
setLoadedSections(prev => {
if (prev.some(s => s.title === config.title)) return prev;
return [...prev, { ...config, movies }];
});
}
setCurrentIndex(prev => prev + 1);
setLoading(false);
};
// Lần đầu tải 2 Section đầu tiên để lấp đầy khung nhìn ngay lập tức
useEffect(() => {
let active = true;
const initLoad = async () => {
setLoading(true);
// Load Section 1
const config1 = SECTION_CONFIGS[0];
const movies1 = await fetchSectionData(config1);
if (active && movies1.length > 0) {
setLoadedSections([{ ...config1, movies: movies1 }]);
}
// Load Section 2
const config2 = SECTION_CONFIGS[1];
const movies2 = await fetchSectionData(config2);
if (active && movies2.length > 0) {
setLoadedSections(prev => [...prev, { ...config2, movies: movies2 }]);
}
if (active) {
setCurrentIndex(2);
setLoading(false);
}
};
initLoad();
return () => {
active = false;
};
}, []);
// Lắng nghe Intersection Observer để kích hoạt tải Section tiếp theo khi cuộn xuống gần cuối
useEffect(() => {
if (!loaderRef.current || currentIndex >= SECTION_CONFIGS.length || loading) return;
const observer = new IntersectionObserver((entries) => {
const target = entries[0];
if (target.isIntersecting && !loading) {
loadNextSection();
}
}, {
rootMargin: '350px', // Bắt đầu tải ngầm trước khi cuộn tới 350px để tạo trải nghiệm bất tận mượt mà
threshold: 0.1
});
observer.observe(loaderRef.current);
return () => observer.disconnect();
}, [currentIndex, loading]);
const normalSections = loadedSections.filter(sec => !['trung-quoc', 'han-quoc', 'au-my'].includes(sec.country));
const regionSections = loadedSections.filter(sec => ['trung-quoc', 'han-quoc', 'au-my'].includes(sec.country));
return (
<div className="space-y-4">
{normalSections.map((sec, i) => (
<MovieSection
key={`${sec.type || sec.category || sec.country}-${i}`}
title={sec.title}
movies={sec.movies}
href={sec.href}
/>
))}
{regionSections.length > 0 && (
<CombinedRegionSection sections={regionSections} />
)}
{/* Điểm kích hoạt cuộn tải Section */}
{currentIndex < SECTION_CONFIGS.length && (
<div ref={loaderRef} className="flex flex-col items-center justify-center py-12">
{loading && (
<>
<div className="relative flex h-16 w-16 items-center justify-center">
{/* Vòng quay hiệu ứng Neon cao cấp */}
<div className="absolute inset-0 animate-spin rounded-full border-4 border-t-primary border-r-accent border-b-transparent border-l-transparent"></div>
<div className="h-6 w-6 animate-pulse rounded-full bg-primary/80 blur-[2px]"></div>
</div>
<span className="mt-4 text-xs font-black uppercase tracking-widest text-gray-500 animate-pulse">
Đang tải thêm danh mục phim...
</span>
</>
)}
</div>
)}
</div>
);
}
|