From 87c3c588d97111c851689ed7f6435fef2536e10b Mon Sep 17 00:00:00 2001 From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com> Date: Wed, 1 Jul 2026 00:03:26 +0200 Subject: [PATCH] refactor(search): extract advanced-search results area into a component --- .../components/AdvancedSearchResults.tsx | 152 ++++++++++++++++++ .../search/pages/SearchBrowsePage.tsx | 117 +++----------- 2 files changed, 170 insertions(+), 99 deletions(-) create mode 100644 src/features/search/components/AdvancedSearchResults.tsx diff --git a/src/features/search/components/AdvancedSearchResults.tsx b/src/features/search/components/AdvancedSearchResults.tsx new file mode 100644 index 00000000..661dae4e --- /dev/null +++ b/src/features/search/components/AdvancedSearchResults.tsx @@ -0,0 +1,152 @@ +import { type MutableRefObject } from 'react'; +import { useTranslation } from 'react-i18next'; +import { AlbumRow } from '@/features/album'; +import { ArtistRow } from '@/features/artist'; +import PagedSongList from '@/features/search/components/PagedSongList'; +import { LOSSLESS_MODE_QUERY } from '@/lib/library/losslessMode'; +import type { Results, SearchOpts } from '@/features/search/searchBrowseTypes'; + +interface AdvancedSearchResultsProps { + showAdvancedPanel: boolean; + hasSearched: boolean; + loading: boolean; + basicSearchMode: boolean; + query: string; + filteredResults: Results | null; + activeSearch: SearchOpts | null; + genreNote: boolean; + songsHasMore: boolean; + loadingMoreSongs: boolean; + loadMoreSongs: () => void; + artistRowScrollLeftRestoreRef: MutableRefObject; + artistRowScrollLeftRef: MutableRefObject; + albumRowScrollLeftRestoreRef: MutableRefObject; + albumRowScrollLeftRef: MutableRefObject; +} + +/** Results area for the search shell: empty/loading/no-results states + artist/album/song sections. */ +export default function AdvancedSearchResults({ + showAdvancedPanel, + hasSearched, + loading, + basicSearchMode, + query, + filteredResults, + activeSearch, + genreNote, + songsHasMore, + loadingMoreSongs, + loadMoreSongs, + artistRowScrollLeftRestoreRef, + artistRowScrollLeftRef, + albumRowScrollLeftRestoreRef, + albumRowScrollLeftRef, +}: AdvancedSearchResultsProps) { + const { t } = useTranslation(); + const total = filteredResults + ? filteredResults.artists.length + filteredResults.albums.length + filteredResults.songs.length + : 0; + + if (showAdvancedPanel && !hasSearched) { + return ( +
+ {t('search.advancedEmpty')} +
+ ); + } + if (loading) { + return ( +
+
+
+ ); + } + if (total === 0) { + return ( +
+ {basicSearchMode && query.trim() + ? t('search.noResults', { query }) + : t('search.advancedNoResults')} +
+ ); + } + + return ( +
+ + {filteredResults && filteredResults.artists.length > 0 && ( +
+ 0 + // React Compiler refs rule: ref read imperatively outside reactive rendering; not used to compute the render output. + // eslint-disable-next-line react-hooks/refs + ? artistRowScrollLeftRestoreRef.current + : undefined + } + onScrollLeftSnapshot={(left) => { + artistRowScrollLeftRef.current = left; + }} + /> +
+ )} + + {filteredResults && filteredResults.albums.length > 0 && ( +
+ 0 + // React Compiler refs rule: ref read imperatively outside reactive rendering; not used to compute the render output. + // eslint-disable-next-line react-hooks/refs + ? albumRowScrollLeftRestoreRef.current + : undefined + } + onScrollLeftSnapshot={(left) => { + albumRowScrollLeftRef.current = left; + }} + /> +
+ )} + + {filteredResults && filteredResults.songs.length > 0 && ( +
+

+ {t('search.songs')} + {genreNote && ( + + — {t('search.advancedGenreNote')} + + )} +

+ +
+ )} +
+ ); +} diff --git a/src/features/search/pages/SearchBrowsePage.tsx b/src/features/search/pages/SearchBrowsePage.tsx index 556e6dea..6f13e8fc 100644 --- a/src/features/search/pages/SearchBrowsePage.tsx +++ b/src/features/search/pages/SearchBrowsePage.tsx @@ -7,10 +7,8 @@ import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useSta import { useLocation, useNavigate, useNavigationType, useSearchParams } from 'react-router-dom'; import { SlidersVertical, Search } from 'lucide-react'; import { useTranslation } from 'react-i18next'; -import { AlbumRow } from '@/features/album'; -import { ArtistRow } from '@/features/artist'; -import PagedSongList from '@/features/search/components/PagedSongList'; import AdvancedSearchFilterPanel from '@/features/search/components/AdvancedSearchFilterPanel'; +import AdvancedSearchResults from '@/features/search/components/AdvancedSearchResults'; import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll'; import { useAuthStore } from '@/store/authStore'; import { usePlayerStore } from '@/features/playback/store/playerStore'; @@ -43,7 +41,6 @@ import { tryRunLocalAdvancedSearch, } from '@/lib/library/advancedSearchLocal'; import { isLosslessSuffix } from '@/lib/library/losslessFormats'; -import { LOSSLESS_MODE_QUERY } from '@/lib/library/losslessMode'; import { OXIMEDIA_MOOD_SEARCH_ENABLED } from '@/lib/library/trackEnrichment'; import { raceSearchSources } from '@/lib/library/searchRace'; import { logLibrarySearch } from '@/lib/library/libraryDevLog'; @@ -113,9 +110,6 @@ export default function SearchBrowsePage() { songs: results.songs.filter(s => isFav(s.id, s.starred)), }; }, [results, starredOnly, starredOverrides]); - const total = filteredResults - ? filteredResults.artists.length + filteredResults.albums.length + filteredResults.songs.length - : 0; const [loading, setLoading] = useState(false); const [hasSearched, setHasSearched] = useState(() => restoreStash?.hasSearched ?? false); const [genreNote, setGenreNote] = useState(() => restoreStash?.genreNote ?? false); @@ -911,98 +905,23 @@ export default function SearchBrowsePage() { )} {/* ── Results ───────────────────────────────────────────── */} - {showAdvancedPanel && !hasSearched ? ( -
- {t('search.advancedEmpty')} -
- ) : loading ? ( -
-
-
- ) : total === 0 ? ( -
- {basicSearchMode && query.trim() - ? t('search.noResults', { query }) - : t('search.advancedNoResults')} -
- ) : ( -
- - {filteredResults && filteredResults.artists.length > 0 && ( -
- 0 - // React Compiler refs rule: ref read imperatively outside reactive rendering; not used to compute the render output. - // eslint-disable-next-line react-hooks/refs - ? artistRowScrollLeftRestoreRef.current - : undefined - } - onScrollLeftSnapshot={(left) => { - artistRowScrollLeftRef.current = left; - }} - /> -
- )} - - {filteredResults && filteredResults.albums.length > 0 && ( -
- 0 - // React Compiler refs rule: ref read imperatively outside reactive rendering; not used to compute the render output. - // eslint-disable-next-line react-hooks/refs - ? albumRowScrollLeftRestoreRef.current - : undefined - } - onScrollLeftSnapshot={(left) => { - albumRowScrollLeftRef.current = left; - }} - /> -
- )} - - {filteredResults && filteredResults.songs.length > 0 && ( -
-

- {t('search.songs')} - {genreNote && ( - - — {t('search.advancedGenreNote')} - - )} -

- -
- )} -
- )} + )}