refactor(home): co-locate Mainstage/home feature into features/home

This commit is contained in:
Psychotoxical
2026-06-30 18:12:27 +02:00
parent f05183d412
commit 2f4303ecc8
13 changed files with 49 additions and 38 deletions
+1 -1
View File
@@ -9,7 +9,7 @@ import { resolveStartRoute } from '@/features/sidebar';
// Route-level lazy loading: keeps the non-page graph (shell, player, stores) in
// the entry chunk; each page is fetched when its route is first visited.
const Home = lazy(() => import('../pages/Home'));
const Home = lazy(() => import('@/features/home/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'));
+1 -1
View File
@@ -7,7 +7,7 @@ import { Play, ListPlus, RefreshCw, Sparkles } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useAuthStore } from '../../store/authStore';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import SongRail from '../SongRail';
import { SongRail } from '@/features/home';
import { playSongNow } from '@/features/playback/utils/playback/playSong';
import { ndListSongs, ndInvalidateSongsCache } from '@/lib/api/navidromeBrowse';
import { usePerfProbeFlags } from '../../utils/perf/perfFlags';
@@ -8,21 +8,21 @@ import React, { memo, useEffect, useLayoutEffect, useMemo, useRef, useState } fr
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Play, ListPlus, Music } from 'lucide-react';
import { useAlbumCoverRef } from '../cover/useLibraryCoverRef';
import { useLibraryCoverPrefetch } from '../cover/useLibraryCoverPrefetch';
import { coverImgSrc } from '../cover/imgSrc';
import { useCoverArt } from '../cover/useCoverArt';
import { primeAlbumCoversForDisplay } from '../cover/warmDiskPeek';
import { useAlbumCoverRef } from '@/cover/useLibraryCoverRef';
import { useLibraryCoverPrefetch } from '@/cover/useLibraryCoverPrefetch';
import { coverImgSrc } from '@/cover/imgSrc';
import { useCoverArt } from '@/cover/useCoverArt';
import { primeAlbumCoversForDisplay } from '@/cover/warmDiskPeek';
import {
readBecauseYouLikeCache,
writeBecauseYouLikeCache,
type BecauseYouLikeAnchor,
} from '../store/becauseYouLikeCache';
} from '@/features/home/store/becauseYouLikeCache';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useAuthStore } from '../store/authStore';
import { useAuthStore } from '@/store/authStore';
import { playAlbum, playAlbumShuffled } from '@/features/playback/utils/playback/playAlbum';
import { useLongPressAction } from '@/lib/hooks/useLongPressAction';
import { LongPressWaveOverlay } from './LongPressWaveOverlay';
import { LongPressWaveOverlay } from '@/components/LongPressWaveOverlay';
import { formatHumanHoursMinutes } from '@/lib/format/formatHumanDuration';
import { AlbumRow } from '@/features/album';
import { albumArtistDisplayName } from '@/features/album';
@@ -5,23 +5,23 @@ import { songToTrack } from '@/lib/media/songToTrack';
import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
import { useNavigateToAlbum } from '@/features/album';
import { Play, ListPlus, ChevronLeft, ChevronRight } from 'lucide-react';
import { CoverArtImage } from '../cover/CoverArtImage';
import { useAlbumCoverRef } from '../cover/useLibraryCoverRef';
import { useArtistBanner, useArtistFanart } from '../cover/useArtistFanart';
import { useCoverArt } from '../cover/useCoverArt';
import { useHeroBackdrop } from '../cover/useHeroBackdrop';
import { CoverArtImage } from '@/cover/CoverArtImage';
import { useAlbumCoverRef } from '@/cover/useLibraryCoverRef';
import { useArtistBanner, useArtistFanart } from '@/cover/useArtistFanart';
import { useCoverArt } from '@/cover/useCoverArt';
import { useHeroBackdrop } from '@/cover/useHeroBackdrop';
import { useCachedUrl } from '@/ui/CachedImage';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useTranslation } from 'react-i18next';
import { useIsMobile } from '@/lib/hooks/useIsMobile';
import { useWindowVisibility } from '@/lib/hooks/useWindowVisibility';
import { useAuthStore } from '../store/authStore';
import { useThemeStore } from '../store/themeStore';
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '../utils/mix/mixRatingFilter';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import { useAuthStore } from '@/store/authStore';
import { useThemeStore } from '@/store/themeStore';
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '@/utils/mix/mixRatingFilter';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { playAlbum, playAlbumShuffled } from '@/features/playback/utils/playback/playAlbum';
import { useLongPressAction } from '@/lib/hooks/useLongPressAction';
import { LongPressWaveOverlay } from './LongPressWaveOverlay';
import { LongPressWaveOverlay } from '@/components/LongPressWaveOverlay';
import { albumArtistDisplayName, deriveAlbumArtistRefs } from '@/features/album';
const INTERVAL_MS = 10000;
@@ -1,8 +1,8 @@
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import React, { useRef, useState, useEffect, useMemo } from 'react';
import { ChevronLeft, ChevronRight, RefreshCw } from 'lucide-react';
import SongCard from './SongCard';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import SongCard from '@/components/SongCard';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { dedupeById } from '@/lib/util/dedupeById';
interface Props {
+11
View File
@@ -0,0 +1,11 @@
/**
* Home (Mainstage) feature — the Home landing page, its Hero banner, the song
* rail + "because you like" recommendation rail, the mainstage section config
* store, and the home-feed / because-you-like client caches. The page is
* lazy-loaded by the router via its deep path, so it is not re-exported here.
*
* Cross-feature consumers pull only the section config store (settings'
* mainstage customiser, the tracks-page chrome).
*/
export { useHomeStore, type HomeSectionId, type HomeSectionConfig, DEFAULT_HOME_SECTIONS } from './store/homeStore';
export { default as SongRail } from './components/SongRail';
@@ -3,32 +3,32 @@ import { getAlbumList, getRandomSongs } from '@/lib/api/subsonicLibrary';
import type { SubsonicAlbum, SubsonicArtist, SubsonicSong } from '@/lib/api/subsonicTypes';
import { runLocalRandomSongs } from '@/lib/library/browseTextSearch';
import React, { useEffect, useState } from 'react';
import Hero from '../components/Hero';
import Hero from '@/features/home/components/Hero';
import { AlbumRow } from '@/features/album';
import SongRail from '../components/SongRail';
import BecauseYouLikeRail from '../components/BecauseYouLikeRail';
import SongRail from '@/features/home/components/SongRail';
import BecauseYouLikeRail from '@/features/home/components/BecauseYouLikeRail';
import { LosslessAlbumsRail } from '@/features/album';
import { useTranslation } from 'react-i18next';
import { NavLink, useNavigate } from 'react-router-dom';
import { ChevronRight } from 'lucide-react';
import { useHomeStore } from '../store/homeStore';
import { useAuthStore } from '../store/authStore';
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '../utils/mix/mixRatingFilter';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import { bumpPerfCounter } from '../utils/perf/perfTelemetry';
import { useHomeStore } from '@/features/home/store/homeStore';
import { useAuthStore } from '@/store/authStore';
import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '@/utils/mix/mixRatingFilter';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { bumpPerfCounter } from '@/utils/perf/perfTelemetry';
import { dedupeById } from '@/lib/util/dedupeById';
import { shuffleArray } from '@/lib/util/shuffleArray';
import { useLibraryCoverPrefetch } from '../cover/useLibraryCoverPrefetch';
import { primeAlbumCoversForDisplay, warmHomeMainstageCovers } from '../cover/warmDiskPeek';
import { readBecauseYouLikeCache } from '../store/becauseYouLikeCache';
import { useLibraryCoverPrefetch } from '@/cover/useLibraryCoverPrefetch';
import { primeAlbumCoversForDisplay, warmHomeMainstageCovers } from '@/cover/warmDiskPeek';
import { readBecauseYouLikeCache } from '@/features/home/store/becauseYouLikeCache';
import {
isHomeFeedSnapshotEmpty,
readHomeFeedCache,
readHomeFeedCacheStale,
writeHomeFeedCache,
type HomeFeedSnapshot,
} from '../store/homeFeedCache';
import { useConnectionStatus } from '../hooks/useConnectionStatus';
} from '@/features/home/store/homeFeedCache';
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { useOfflineBrowseContext } from '@/features/offline';
import { useOfflineBrowseReloadToken } from '@/features/offline';
import { useDevOfflineBrowseStore } from '@/features/offline';
@@ -3,7 +3,7 @@ import {
clearBecauseYouLikeCache,
readBecauseYouLikeCache,
writeBecauseYouLikeCache,
} from './becauseYouLikeCache';
} from '@/features/home/store/becauseYouLikeCache';
describe('becauseYouLikeCache', () => {
it('invalidates when music library filter version changes', () => {
@@ -1,5 +1,5 @@
import { useTranslation } from 'react-i18next';
import { useHomeStore, HomeSectionId } from '@/store/homeStore';
import { useHomeStore, HomeSectionId } from '@/features/home';
export function HomeCustomizer() {
const { t } = useTranslation();
@@ -3,7 +3,7 @@ import { Disc3, LayoutGrid, ListOrdered, ListTodo, PanelLeft, RotateCcw, Users }
import { useArtistLayoutStore } from '@/features/artist';
import { useAuthStore } from '@/store/authStore';
import type { QueueDisplayMode } from '@/store/authStoreTypes';
import { useHomeStore } from '@/store/homeStore';
import { useHomeStore } from '@/features/home';
import { usePlayerBarLayoutStore } from '@/store/playerBarLayoutStore';
import { usePlaylistLayoutStore } from '@/features/playlist';
import { useQueueToolbarStore } from '@/store/queueToolbarStore';