import { useState, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import api from '../api'; import SEO from '../components/SEO'; import PluginCard from '../components/PluginCard'; // Global cache to persist data across component unmounts (navigation) // This allows the page to render immediately when going "back", // which is required for the browser to restore scroll position. let homeCache: { popular: any[]; newest: any[]; } | null = null; const Home = () => { const { t } = useTranslation(); const [popular, setPopular] = useState(homeCache?.popular || []); const [newest, setNewest] = useState(homeCache?.newest || []); // Load sections lazily when the user scrolls them into view const [loading, setLoading] = useState(false); const popularRef = useRef(null); const newestRef = useRef(null); const fetched = useRef({ popular: !!homeCache?.popular, newest: !!homeCache?.newest }); const saveSection = (key: 'popular' | 'newest', data: any[]) => { if (key === 'popular') setPopular(data); if (key === 'newest') setNewest(data); homeCache = { popular: key === 'popular' ? data : homeCache?.popular || [], newest: key === 'newest' ? data : homeCache?.newest || [] }; fetched.current[key] = true; }; useEffect(() => { if (fetched.current.popular && fetched.current.newest) return; const observers: IntersectionObserver[] = []; const createObserver = (el: HTMLElement | null, key: 'popular' | 'newest', params: any) => { if (!el || fetched.current[key]) return; const obs = new IntersectionObserver(async (entries, observer) => { for (const entry of entries) { if (entry.isIntersecting) { fetched.current[key] = true; // prevent duplicate triggers try { setLoading(true); const res = await api.get('/plugins', { params }); const list = Array.isArray(res.data) ? res.data : []; saveSection(key, list); } catch (err) { console.error('Fetch error for', key, err); } finally { setLoading(false); observer.disconnect(); } } } }, { rootMargin: '200px' }); obs.observe(el); observers.push(obs); }; // Ensure refs are attached before creating observers requestAnimationFrame(() => { createObserver(popularRef.current, 'popular', { sort: 'downloads', limit: 8 }); createObserver(newestRef.current, 'newest', { sort: 'newest', limit: 12 }); }); return () => observers.forEach(o => o.disconnect()); }, []); if (loading) { return (

Discovering best plugins...

); } return ( <>

{t('welcome')}

Explore powerful plugins for your PumpkinMC experience.

Most Popular

{popular.length > 0 ? ( popular.map((plugin) => ( )) ) : (
{loading ? 'Loading popular plugins...' : 'Scroll to load popular plugins'}
)}

New & Trending

{newest.length > 0 ? ( newest.map((plugin) => ( )) ) : (
{loading ? 'Loading new plugins...' : 'Scroll to load new plugins'}
)}
{(fetched.current?.popular && fetched.current?.newest && popular.length === 0 && newest.length === 0) && (

No plugins found. Check back later!

)}
); }; export default Home;