import React, { useEffect, useState, useCallback, useRef } from 'react'; import AlbumCard from '../components/AlbumCard'; import { getAlbumList, SubsonicAlbum } from '../api/subsonic'; import { useTranslation } from 'react-i18next'; type SortType = 'alphabeticalByName' | 'alphabeticalByArtist'; export default function Albums() { const { t } = useTranslation(); const [albums, setAlbums] = useState([]); const [sort, setSort] = useState('alphabeticalByName'); const [loading, setLoading] = useState(true); const [page, setPage] = useState(0); const [hasMore, setHasMore] = useState(true); const PAGE_SIZE = 30; const observerTarget = useRef(null); const load = useCallback(async (sortType: SortType, offset: number, append = false) => { setLoading(true); try { const data = await getAlbumList(sortType, PAGE_SIZE, offset); if (append) setAlbums(prev => [...prev, ...data]); else setAlbums(data); setHasMore(data.length === PAGE_SIZE); } finally { setLoading(false); } }, []); useEffect(() => { setPage(0); load(sort, 0); }, [sort, load]); const loadMore = useCallback(() => { if (loading || !hasMore) return; const next = page + 1; setPage(next); load(sort, next * PAGE_SIZE, true); }, [loading, hasMore, page, sort, load]); useEffect(() => { const observer = new IntersectionObserver( entries => { if (entries[0].isIntersecting) { loadMore(); } }, { rootMargin: '200px' } ); if (observerTarget.current) { observer.observe(observerTarget.current); } return () => observer.disconnect(); }, [loadMore]); const sortOptions: { value: SortType; label: string }[] = [ { value: 'alphabeticalByName', label: t('albums.sortByName') }, { value: 'alphabeticalByArtist', label: t('albums.sortByArtist') }, ]; return (

{t('albums.title')}

{sortOptions.map(o => ( ))}
{loading && albums.length === 0 ? (
) : ( <>
{albums.map(a => )}
{loading && hasMore &&
}
)}
); }