mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
fix(library): browse-all-tracks shares the Search song-list view (#854)
* fix(library): browse-all-tracks shares the Search song-list view (#841) "Browse all tracks" rendered virtualized, transform-positioned rows inside its own scroll box, so the sticky column header got painted over while scrolling. Extract the Search / Advanced-Search song-list chrome (sticky header + plain SongRows + IntersectionObserver sentinel paging) into a shared PagedSongList and route all three through it; Browse now flows in the page like the Search pages, so the header can no longer be overlapped. Trade-off: Browse-all drops its bespoke row virtualization to match the Search pages (DOM grows with scroll as they do); paging is unchanged. Also removes the duplicated sentinel/observer in SearchResults and AdvancedSearch. # Conflicts: # src/components/VirtualSongList.tsx # src/pages/SearchResults.tsx * docs(changelog): browse-all-tracks sticky header fix (#854)
This commit is contained in:
committed by
GitHub
parent
cc8e6cc811
commit
cb4d331f99
@@ -8,7 +8,7 @@ import { SlidersVertical } from 'lucide-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import AlbumRow from '../components/AlbumRow';
|
||||
import ArtistRow from '../components/ArtistRow';
|
||||
import SongRow, { SongListHeader } from '../components/SongRow';
|
||||
import PagedSongList from '../components/PagedSongList';
|
||||
import CustomSelect from '../components/CustomSelect';
|
||||
import StarFilterButton from '../components/StarFilterButton';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
@@ -79,7 +79,6 @@ export default function AdvancedSearch() {
|
||||
const [songsServerOffset, setSongsServerOffset] = useState(0);
|
||||
const [songsHasMore, setSongsHasMore] = useState(false);
|
||||
const [loadingMoreSongs, setLoadingMoreSongs] = useState(false);
|
||||
const songsSentinelRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const applySongFilters = (
|
||||
list: SubsonicSong[],
|
||||
@@ -294,17 +293,6 @@ export default function AdvancedSearch() {
|
||||
}
|
||||
}, [loadingMoreSongs, songsHasMore, activeSearch, songsServerOffset, localMode, serverId]);
|
||||
|
||||
// IntersectionObserver on the bottom sentinel — fires loadMoreSongs as it nears the viewport.
|
||||
useEffect(() => {
|
||||
const el = songsSentinelRef.current;
|
||||
if (!el) return;
|
||||
const obs = new IntersectionObserver(entries => {
|
||||
if (entries[0]?.isIntersecting) loadMoreSongs();
|
||||
}, { rootMargin: '600px' });
|
||||
obs.observe(el);
|
||||
return () => obs.disconnect();
|
||||
}, [loadMoreSongs]);
|
||||
|
||||
const handleSubmit = (e?: React.FormEvent) => {
|
||||
e?.preventDefault();
|
||||
runSearch({ query, genre, yearFrom, yearTo, resultType });
|
||||
@@ -461,15 +449,12 @@ export default function AdvancedSearch() {
|
||||
</span>
|
||||
)}
|
||||
</h2>
|
||||
<SongListHeader />
|
||||
{filteredResults.songs.map(song => (
|
||||
<SongRow key={song.id} song={song} />
|
||||
))}
|
||||
{songsHasMore && (
|
||||
<div ref={songsSentinelRef} style={{ display: 'flex', justifyContent: 'center', padding: '1rem' }}>
|
||||
{loadingMoreSongs && <div className="spinner" style={{ width: 20, height: 20 }} />}
|
||||
</div>
|
||||
)}
|
||||
<PagedSongList
|
||||
songs={filteredResults.songs}
|
||||
hasMore={songsHasMore}
|
||||
loadingMore={loadingMoreSongs}
|
||||
onLoadMore={loadMoreSongs}
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Search } from 'lucide-react';
|
||||
import type { SearchResults as ISearchResults } from '../api/subsonicTypes';
|
||||
import AlbumRow from '../components/AlbumRow';
|
||||
import ArtistRow from '../components/ArtistRow';
|
||||
import SongRow, { SongListHeader } from '../components/SongRow';
|
||||
import PagedSongList from '../components/PagedSongList';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAuthStore } from '../store/authStore';
|
||||
import { useLibraryIndexStore } from '../store/libraryIndexStore';
|
||||
@@ -30,7 +30,6 @@ export default function SearchResults() {
|
||||
const [songsHasMore, setSongsHasMore] = useState(false);
|
||||
const [loadingMoreSongs, setLoadingMoreSongs] = useState(false);
|
||||
const [localMode, setLocalMode] = useState(false);
|
||||
const songsSentinelRef = useRef<HTMLDivElement>(null);
|
||||
const searchRunRef = useRef(0);
|
||||
const musicLibraryFilterVersion = useAuthStore(s => s.musicLibraryFilterVersion);
|
||||
const serverId = useAuthStore(s => s.activeServerId);
|
||||
@@ -109,16 +108,6 @@ export default function SearchResults() {
|
||||
}
|
||||
}, [loadingMoreSongs, songsHasMore, query, songsServerOffset, localMode, serverId]);
|
||||
|
||||
useEffect(() => {
|
||||
const el = songsSentinelRef.current;
|
||||
if (!el) return;
|
||||
const obs = new IntersectionObserver(entries => {
|
||||
if (entries[0]?.isIntersecting) loadMoreSongs();
|
||||
}, { rootMargin: '600px' });
|
||||
obs.observe(el);
|
||||
return () => obs.disconnect();
|
||||
}, [loadMoreSongs]);
|
||||
|
||||
const hasResults = results && (results.artists.length || results.albums.length || results.songs.length);
|
||||
|
||||
return (
|
||||
@@ -151,15 +140,12 @@ export default function SearchResults() {
|
||||
{results.songs.length > 0 && (
|
||||
<section>
|
||||
<h2 className="section-title" style={{ marginBottom: '0.75rem' }}>{t('search.songs')}</h2>
|
||||
<SongListHeader />
|
||||
{results.songs.map(song => (
|
||||
<SongRow key={song.id} song={song} />
|
||||
))}
|
||||
{songsHasMore && (
|
||||
<div ref={songsSentinelRef} style={{ display: 'flex', justifyContent: 'center', padding: '1rem' }}>
|
||||
{loadingMoreSongs && <div className="spinner" style={{ width: 20, height: 20 }} />}
|
||||
</div>
|
||||
)}
|
||||
<PagedSongList
|
||||
songs={results.songs}
|
||||
hasMore={songsHasMore}
|
||||
loadingMore={loadingMoreSongs}
|
||||
onLoadMore={loadMoreSongs}
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user