dptxa-proxy / src /components /HomeSectionsLoader.jsx
TXAVLOG
Deploy DPTXA to Hugging Face Spaces
4bea261
Raw
History Blame Contribute Delete
5.47 kB
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>
);
}