/** * Component: Homepage - Audiobook Discovery (Dynamic Sections) * Documentation: documentation/features/home-sections.md */ 'use client'; import { useState, useRef, useEffect, useCallback, createRef } from 'react'; import { Header } from '@/components/layout/Header'; import { ProtectedRoute } from '@/components/auth/ProtectedRoute'; import { UnifiedPagination, PaginationSection } from '@/components/ui/UnifiedPagination'; import { HomeSection, SECTION_DOT_COLORS } from '@/components/home/HomeSection'; import { HomeSectionConfigModal } from '@/components/home/HomeSectionConfigModal'; import { useHomeSections } from '@/lib/hooks/useHomeSections'; import { usePreferences } from '@/contexts/PreferencesContext'; import { Cog6ToothIcon } from '@heroicons/react/24/outline'; function getSectionTitle(sectionType: string, categoryName?: string | null): string { if (sectionType === 'popular') return 'Popular Audiobooks'; if (sectionType === 'new_releases') return 'New Releases'; return categoryName || 'Category'; } export default function HomePage() { const { sections, nextRefresh, isLoading: sectionsLoading, saveSections } = useHomeSections(); const { cardSize, setCardSize, squareCovers, setSquareCovers, hideAvailable, setHideAvailable } = usePreferences(); // Per-section pagination state const [pages, setPages] = useState>({}); const [totalPagesMap, setTotalPagesMap] = useState>({}); const [configOpen, setConfigOpen] = useState(false); const footerRef = useRef(null); // Create stable refs for each section const sectionRefsMap = useRef>>(new Map()); const getSectionKey = (s: { sectionType: string; categoryId: string | null }) => s.sectionType === 'category' ? `category:${s.categoryId}` : s.sectionType; // Ensure refs exist for current sections sections.forEach((s) => { const key = getSectionKey(s); if (!sectionRefsMap.current.has(key)) { sectionRefsMap.current.set(key, createRef()); } }); // Reset pages and totalPages when hideAvailable changes useEffect(() => { setPages({}); setTotalPagesMap({}); }, [hideAvailable]); const getPage = (key: string) => pages[key] || 1; const setPage = useCallback((key: string, page: number) => { setPages((prev) => ({ ...prev, [key]: page })); }, []); const handleTotalPagesChange = useCallback((key: string, totalPages: number) => { setTotalPagesMap((prev) => { if (prev[key] === totalPages) return prev; return { ...prev, [key]: totalPages }; }); }, []); // Build pagination sections for the floating pill const paginationSections: PaginationSection[] = sections.map((s, i) => { const key = getSectionKey(s); const ref = sectionRefsMap.current.get(key)!; return { label: getSectionTitle(s.sectionType, s.categoryName), accentColor: SECTION_DOT_COLORS[i % SECTION_DOT_COLORS.length], currentPage: getPage(key), totalPages: totalPagesMap[key] || 1, onPageChange: (page: number) => { setPage(key, page); ref.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }, sectionRef: ref, onScrollToSection: () => ref.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }), }; }); return (
{/* Loading state */} {sectionsLoading && (
)} {/* Empty state */} {!sectionsLoading && sections.length === 0 && (

No sections configured. Click Customize to add sections to your home page.

)} {/* Dynamic sections */} {!sectionsLoading && sections.map((section, index) => { const key = getSectionKey(section); const ref = sectionRefsMap.current.get(key)!; return ( { setPage(key, page); ref.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }); }} sectionRef={ref} cardSize={cardSize} squareCovers={squareCovers} hideAvailable={hideAvailable} onToggleHideAvailable={setHideAvailable} onToggleSquareCovers={setSquareCovers} onCardSizeChange={setCardSize} onConfigOpen={index === 0 ? () => setConfigOpen(true) : undefined} onTotalPagesChange={(tp) => handleTotalPagesChange(key, tp)} nextRefresh={nextRefresh} /> ); })} {/* Call to Action */}

Can't find what you're looking for?

Use our search to find any audiobook from Audible

Search Audiobooks
{/* Footer */}

ReadMeABook - Audiobook Library Management System

{/* Unified Pagination — dynamic sections */} {paginationSections.length > 0 && ( )} {/* Config Modal */} setConfigOpen(false)} sections={sections} onSave={saveSections} />
); }