refactor(composers): co-locate composers feature into features/composers

This commit is contained in:
Psychotoxical
2026-06-30 18:03:43 +02:00
parent 5fdec6cd2a
commit f443a60c54
10 changed files with 44 additions and 36 deletions
+2 -2
View File
@@ -13,8 +13,8 @@ const Home = lazy(() => import('../pages/Home'));
const Albums = lazy(() => import('@/features/album/pages/Albums'));
const Artists = lazy(() => import('@/features/artist/pages/Artists'));
const ArtistDetail = lazy(() => import('@/features/artist/pages/ArtistDetail'));
const Composers = lazy(() => import('../pages/Composers'));
const ComposerDetail = lazy(() => import('../pages/ComposerDetail'));
const Composers = lazy(() => import('@/features/composers/pages/Composers'));
const ComposerDetail = lazy(() => import('@/features/composers/pages/ComposerDetail'));
const NewReleases = lazy(() => import('@/features/album/pages/NewReleases'));
const Favorites = lazy(() => import('@/features/favorites/pages/Favorites'));
const RandomMix = lazy(() => import('../pages/RandomMix'));
@@ -7,9 +7,9 @@ import {
type ComposerBrowseViewMode,
isComposersBrowsePath,
useComposerBrowseSessionStore,
} from '../store/composerBrowseSessionStore';
import { shouldRestoreComposerBrowseSession } from '../utils/navigation/albumDetailNavigation';
import { useLiveSearchScopeStore } from '../store/liveSearchScopeStore';
} from '@/features/composers/store/composerBrowseSessionStore';
import { shouldRestoreComposerBrowseSession } from '@/utils/navigation/albumDetailNavigation';
import { useLiveSearchScopeStore } from '@/store/liveSearchScopeStore';
export type ComposerBrowseScrollSnapshot = {
scrollTop: number;
@@ -3,8 +3,8 @@ import { useLocation, useNavigationType, type NavigationType } from 'react-route
import {
peekComposerBrowseScrollRestore,
useComposerBrowseSessionStore,
} from '../store/composerBrowseSessionStore';
import { shouldRestoreComposerBrowseSession } from '../utils/navigation/albumDetailNavigation';
} from '@/features/composers/store/composerBrowseSessionStore';
import { shouldRestoreComposerBrowseSession } from '@/utils/navigation/albumDetailNavigation';
type PendingScroll = {
scrollTop: number;
@@ -1,6 +1,6 @@
import { useCallback } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { navigateToComposerDetail } from '../utils/navigation/albumDetailNavigation';
import { navigateToComposerDetail } from '@/utils/navigation/albumDetailNavigation';
/** Navigate to composer detail, remembering the current page for the back button. */
export function useNavigateToComposer() {
+8
View File
@@ -0,0 +1,8 @@
/**
* Composers feature — the composer overview (`Composers`) and per-composer
* works (`ComposerDetail`) browse pages, their browse-filter / scroll-restore /
* navigation hooks, and the composer browse-session store. The pages are
* lazy-loaded by the router via their deep paths, so they are not re-exported
* here. Only the live-search scope predicate is consumed cross-feature.
*/
export { isComposersBrowsePath } from './store/composerBrowseSessionStore';
@@ -5,21 +5,21 @@ import { useEffect, useState, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { ndListAlbumsByArtistRole } from '@/lib/api/navidromeBrowse';
import { AlbumCard } from '@/features/album';
import { ArtistHeroCover } from '../cover/artistHero';
import { coverArtRef } from '../cover/ref';
import { useCoverLightboxSrc } from '../cover/lightbox';
import { ArtistHeroCover } from '@/cover/artistHero';
import { coverArtRef } from '@/cover/ref';
import { useCoverLightboxSrc } from '@/cover/lightbox';
import { ArrowLeft, Users, Heart, Feather, Share2 } from 'lucide-react';
import WikipediaIcon from '../components/WikipediaIcon';
import WikipediaIcon from '@/components/WikipediaIcon';
import { open } from '@tauri-apps/plugin-shell';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useAuthStore } from '../store/authStore';
import { useAuthStore } from '@/store/authStore';
import { useTranslation } from 'react-i18next';
import { copyEntityShareLink } from '../utils/share/copyEntityShareLink';
import { showToast } from '../utils/ui/toast';
import { copyEntityShareLink } from '@/utils/share/copyEntityShareLink';
import { showToast } from '@/utils/ui/toast';
import { sanitizeHtml } from '@/lib/util/sanitizeHtml';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import { albumGridWarmCovers } from '../cover/layoutSizes';
import { VirtualCardGrid } from '../components/VirtualCardGrid';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { albumGridWarmCovers } from '@/cover/layoutSizes';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
export default function ComposerDetail() {
const { t } = useTranslation();
@@ -3,32 +3,32 @@ import { useEffect, useState, useCallback, useRef, useMemo } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { ndListArtistsByRole } from '@/lib/api/navidromeBrowse';
import { LayoutGrid, List } from 'lucide-react';
import StarFilterButton from '../components/StarFilterButton';
import StarFilterButton from '@/components/StarFilterButton';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useAuthStore } from '../store/authStore';
import { useAuthStore } from '@/store/authStore';
import { useTranslation } from 'react-i18next';
import { useVirtualizer } from '@tanstack/react-virtual';
import { APP_MAIN_SCROLL_VIEWPORT_ID, COMPOSERS_INPAGE_SCROLL_VIEWPORT_ID } from '../constants/appScroll';
import { APP_MAIN_SCROLL_VIEWPORT_ID, COMPOSERS_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import { useElementClientHeightById, useElementClientHeightForElement } from '@/lib/hooks/useResizeClientHeight';
import { useMainstageInpageHeaderTight } from '../hooks/useMainstageInpageHeaderTight';
import { useMainstageInpageHeaderTight } from '@/hooks/useMainstageInpageHeaderTight';
import { useBrowseArtistTextSearch } from '@/features/artist';
import { useComposersBrowseFilters, type ComposerBrowseScrollSnapshot } from '../hooks/useComposersBrowseFilters';
import { useComposersBrowseScrollRestore } from '../hooks/useComposersBrowseScrollRestore';
import { useComposersBrowseFilters, type ComposerBrowseScrollSnapshot } from '@/features/composers/hooks/useComposersBrowseFilters';
import { useComposersBrowseScrollRestore } from '@/features/composers/hooks/useComposersBrowseScrollRestore';
import { useArtistsBrowseScrollReset } from '@/features/artist';
import { useNavigateToComposer } from '../hooks/useNavigateToComposer';
import { peekComposerBrowseScrollRestore } from '../store/composerBrowseSessionStore';
import { useScopedBrowseSearchQuery } from '../store/liveSearchScopeStore';
import { readComposerBrowseRestore } from '../utils/navigation/albumDetailNavigation';
import { useNavigateToComposer } from '@/features/composers/hooks/useNavigateToComposer';
import { peekComposerBrowseScrollRestore } from '@/features/composers/store/composerBrowseSessionStore';
import { useScopedBrowseSearchQuery } from '@/store/liveSearchScopeStore';
import { readComposerBrowseRestore } from '@/utils/navigation/albumDetailNavigation';
import { filterArtistsWithRoleAlbumCredits } from '@/lib/library/composerBrowse';
import { ALL_SENTINEL, artistLetterBucket } from '@/features/artist';
import { useLibraryIgnoredArticles } from '../hooks/useLibraryIgnoredArticles';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import { VirtualCardGrid } from '../components/VirtualCardGrid';
import { useLibraryIgnoredArticles } from '@/hooks/useLibraryIgnoredArticles';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import OverlayScrollArea from '@/ui/OverlayScrollArea';
import { useVirtualizerScrollMargin } from '@/lib/hooks/useVirtualizerScrollMargin';
import { useClientSliceInfiniteScroll } from '../hooks/useClientSliceInfiniteScroll';
import { useInpageScrollViewport } from '../hooks/useInpageScrollViewport';
import InpageScrollSentinel from '../components/InpageScrollSentinel';
import { useClientSliceInfiniteScroll } from '@/hooks/useClientSliceInfiniteScroll';
import { useInpageScrollViewport } from '@/hooks/useInpageScrollViewport';
import InpageScrollSentinel from '@/components/InpageScrollSentinel';
const ALPHABET = [ALL_SENTINEL, '#', ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('')];
@@ -4,7 +4,7 @@ import type { LiveSearchScope } from '@/store/liveSearchScopeStore';
import { isAlbumsBrowsePath, isNewReleasesBrowsePath } from '@/features/album';
import { isTracksBrowsePath } from '@/store/advancedSearchSessionStore';
import { isArtistsBrowsePath } from '@/features/artist';
import { isComposersBrowsePath } from '@/store/composerBrowseSessionStore';
import { isComposersBrowsePath } from '@/features/composers';
export const SCOPE_NAV_ITEM: Record<LiveSearchScope, keyof typeof ALL_NAV_ITEMS> = {
artists: 'artists',
@@ -3,7 +3,7 @@ import { useLocation } from 'react-router-dom';
import { isAlbumsBrowsePath, isNewReleasesBrowsePath } from '@/features/album';
import { isArtistsBrowsePath } from '@/features/artist';
import { isTracksBrowsePath } from '@/store/advancedSearchSessionStore';
import { isComposersBrowsePath } from '@/store/composerBrowseSessionStore';
import { isComposersBrowsePath } from '@/features/composers';
import { useLiveSearchScopeStore } from '@/store/liveSearchScopeStore';
/** Keep scope badge in sync with browse routes; clear field text when leaving browse. */