mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
refactor(randomMix): co-locate random/lucky-mix feature into features/randomMix
This commit is contained in:
@@ -4,7 +4,7 @@ import MobilePlayerView from '../components/MobilePlayerView';
|
|||||||
import { useIsMobile } from '@/lib/hooks/useIsMobile';
|
import { useIsMobile } from '@/lib/hooks/useIsMobile';
|
||||||
import { useSidebarStore } from '@/features/sidebar';
|
import { useSidebarStore } from '@/features/sidebar';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { useLuckyMixAvailable } from '../hooks/useLuckyMixAvailable';
|
import { useLuckyMixAvailable } from '@/features/randomMix';
|
||||||
import { resolveStartRoute } from '@/features/sidebar';
|
import { resolveStartRoute } from '@/features/sidebar';
|
||||||
|
|
||||||
// Route-level lazy loading: keeps the non-page graph (shell, player, stores) in
|
// Route-level lazy loading: keeps the non-page graph (shell, player, stores) in
|
||||||
@@ -17,13 +17,13 @@ const Composers = lazy(() => import('@/features/composers/pages/Composers'));
|
|||||||
const ComposerDetail = lazy(() => import('@/features/composers/pages/ComposerDetail'));
|
const ComposerDetail = lazy(() => import('@/features/composers/pages/ComposerDetail'));
|
||||||
const NewReleases = lazy(() => import('@/features/album/pages/NewReleases'));
|
const NewReleases = lazy(() => import('@/features/album/pages/NewReleases'));
|
||||||
const Favorites = lazy(() => import('@/features/favorites/pages/Favorites'));
|
const Favorites = lazy(() => import('@/features/favorites/pages/Favorites'));
|
||||||
const RandomMix = lazy(() => import('../pages/RandomMix'));
|
const RandomMix = lazy(() => import('@/features/randomMix/pages/RandomMix'));
|
||||||
const RandomLanding = lazy(() => import('../pages/RandomLanding'));
|
const RandomLanding = lazy(() => import('@/features/randomMix/pages/RandomLanding'));
|
||||||
const AlbumDetail = lazy(() => import('@/features/album/pages/AlbumDetail'));
|
const AlbumDetail = lazy(() => import('@/features/album/pages/AlbumDetail'));
|
||||||
const MostPlayed = lazy(() => import('@/features/album/pages/MostPlayed'));
|
const MostPlayed = lazy(() => import('@/features/album/pages/MostPlayed'));
|
||||||
const LosslessAlbums = lazy(() => import('@/features/album/pages/LosslessAlbums'));
|
const LosslessAlbums = lazy(() => import('@/features/album/pages/LosslessAlbums'));
|
||||||
const RandomAlbums = lazy(() => import('@/features/album/pages/RandomAlbums'));
|
const RandomAlbums = lazy(() => import('@/features/album/pages/RandomAlbums'));
|
||||||
const LuckyMixPage = lazy(() => import('../pages/LuckyMix'));
|
const LuckyMixPage = lazy(() => import('@/features/randomMix/pages/LuckyMix'));
|
||||||
const Playlists = lazy(() => import('@/features/playlist/pages/Playlists'));
|
const Playlists = lazy(() => import('@/features/playlist/pages/Playlists'));
|
||||||
const PlaylistDetail = lazy(() => import('@/features/playlist/pages/PlaylistDetail'));
|
const PlaylistDetail = lazy(() => import('@/features/playlist/pages/PlaylistDetail'));
|
||||||
const NowPlayingPage = lazy(() => import('@/features/nowPlaying/pages/NowPlaying'));
|
const NowPlayingPage = lazy(() => import('@/features/nowPlaying/pages/NowPlaying'));
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Settings, HardDriveDownload } from 'lucide-react';
|
|||||||
import { useSidebarStore } from '@/features/sidebar';
|
import { useSidebarStore } from '@/features/sidebar';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '../store/authStore';
|
||||||
import { ALL_NAV_ITEMS } from '../config/navItems';
|
import { ALL_NAV_ITEMS } from '../config/navItems';
|
||||||
import { useLuckyMixAvailable } from '../hooks/useLuckyMixAvailable';
|
import { useLuckyMixAvailable } from '@/features/randomMix';
|
||||||
import { isOfflineSidebarNavAllowed } from '@/features/offline';
|
import { isOfflineSidebarNavAllowed } from '@/features/offline';
|
||||||
import { useOfflineBrowseContext } from '@/features/offline';
|
import { useOfflineBrowseContext } from '@/features/offline';
|
||||||
import { offlineBrowseNavFlags } from '@/features/offline';
|
import { offlineBrowseNavFlags } from '@/features/offline';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Play } from 'lucide-react';
|
|||||||
import type { TFunction } from 'i18next';
|
import type { TFunction } from 'i18next';
|
||||||
import OverlayScrollArea from '@/ui/OverlayScrollArea';
|
import OverlayScrollArea from '@/ui/OverlayScrollArea';
|
||||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||||
import { useLuckyMixStore } from '@/store/luckyMixStore';
|
import { useLuckyMixStore } from '@/features/randomMix';
|
||||||
import type { QueueItemRef } from '@/lib/media/trackTypes';
|
import type { QueueItemRef } from '@/lib/media/trackTypes';
|
||||||
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
|
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
|
||||||
import type { QueueDisplayMode } from '@/store/authStoreTypes';
|
import type { QueueDisplayMode } from '@/store/authStoreTypes';
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { useThemeStore } from '@/store/themeStore';
|
|||||||
import { useLyricsStore } from '@/store/lyricsStore';
|
import { useLyricsStore } from '@/store/lyricsStore';
|
||||||
import LyricsPane from '@/components/LyricsPane';
|
import LyricsPane from '@/components/LyricsPane';
|
||||||
import { NowPlayingInfo } from '@/features/nowPlaying';
|
import { NowPlayingInfo } from '@/features/nowPlaying';
|
||||||
import { useLuckyMixStore } from '@/store/luckyMixStore';
|
import { useLuckyMixStore } from '@/features/randomMix';
|
||||||
import { useQueueToolbarStore } from '@/store/queueToolbarStore';
|
import { useQueueToolbarStore } from '@/store/queueToolbarStore';
|
||||||
import { SavePlaylistModal } from '@/features/queue/components/SavePlaylistModal';
|
import { SavePlaylistModal } from '@/features/queue/components/SavePlaylistModal';
|
||||||
import { LoadPlaylistModal } from '@/features/queue/components/LoadPlaylistModal';
|
import { LoadPlaylistModal } from '@/features/queue/components/LoadPlaylistModal';
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
import { RANDOM_MIX_SIZE_OPTIONS } from '../../store/authStoreDefaults';
|
import { RANDOM_MIX_SIZE_OPTIONS } from '@/store/authStoreDefaults';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isMobile: boolean;
|
isMobile: boolean;
|
||||||
+1
-1
@@ -5,7 +5,7 @@ import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
|||||||
import type { Track } from '@/lib/media/trackTypes';
|
import type { Track } from '@/lib/media/trackTypes';
|
||||||
import { previewInputFromSong, usePreviewStore } from '@/features/playback/store/previewStore';
|
import { previewInputFromSong, usePreviewStore } from '@/features/playback/store/previewStore';
|
||||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||||
import { formatRandomMixDuration } from '../../utils/componentHelpers/randomMixHelpers';
|
import { formatRandomMixDuration } from '@/features/randomMix/utils/randomMixHelpers';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
song: SubsonicSong;
|
song: SubsonicSong;
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether "Lucky Mix" should be exposed as a navigable menu/card entry.
|
* Whether "Lucky Mix" should be exposed as a navigable menu/card entry.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Random / Lucky Mix feature — the random landing, random-mix browser, and
|
||||||
|
* lucky-mix trigger pages, the random-mix panels (filters/genre/header/row),
|
||||||
|
* the lucky-mix availability hook + session store, and the lucky-mix /
|
||||||
|
* random-mix queue-build helpers. The pages are lazy-loaded by the router via
|
||||||
|
* their deep paths, so they are not re-exported here.
|
||||||
|
*
|
||||||
|
* Stays OUT (shared infra, consumed by the playback core too, not owned):
|
||||||
|
* `utils/mix/mixRatingFilter` (a generic rating-window filter used by the
|
||||||
|
* infinite-queue builder + home/album/CLI → a `lib/` candidate).
|
||||||
|
*/
|
||||||
|
export { useLuckyMixAvailable, isLuckyMixAvailable } from './hooks/useLuckyMixAvailable';
|
||||||
|
export { useLuckyMixStore } from './store/luckyMixStore';
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { buildAndPlayLuckyMix } from '../utils/mix/luckyMix';
|
import { buildAndPlayLuckyMix } from '@/features/randomMix/utils/luckyMix';
|
||||||
|
|
||||||
export default function LuckyMixPage() {
|
export default function LuckyMixPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -2,7 +2,7 @@ import React from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Shuffle, Dices, Sparkles } from 'lucide-react';
|
import { Shuffle, Dices, Sparkles } from 'lucide-react';
|
||||||
import { useLuckyMixAvailable } from '../hooks/useLuckyMixAvailable';
|
import { useLuckyMixAvailable } from '@/features/randomMix/hooks/useLuckyMixAvailable';
|
||||||
|
|
||||||
interface MixCard {
|
interface MixCard {
|
||||||
icon: React.ElementType;
|
icon: React.ElementType;
|
||||||
@@ -4,21 +4,21 @@ import { songToTrack } from '@/lib/media/songToTrack';
|
|||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||||
import { usePreviewStore } from '@/features/playback/store/previewStore';
|
import { usePreviewStore } from '@/features/playback/store/previewStore';
|
||||||
import { useAuthStore } from '../store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useLibraryIndexStore } from '../store/libraryIndexStore';
|
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useIsMobile } from '@/lib/hooks/useIsMobile';
|
import { useIsMobile } from '@/lib/hooks/useIsMobile';
|
||||||
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
import { useOrbitSongRowBehavior } from '@/features/orbit';
|
||||||
import {
|
import {
|
||||||
fetchRandomMixSongsUntilFull,
|
fetchRandomMixSongsUntilFull,
|
||||||
getMixMinRatingsConfigFromAuth,
|
getMixMinRatingsConfigFromAuth,
|
||||||
} from '../utils/mix/mixRatingFilter';
|
} from '@/utils/mix/mixRatingFilter';
|
||||||
import { fetchGenreCatalog } from '@/features/playback/utils/playback/genreBrowsePlayback';
|
import { fetchGenreCatalog } from '@/features/playback/utils/playback/genreBrowsePlayback';
|
||||||
import { AUDIOBOOK_GENRES, filterRandomMixSongs } from '../utils/componentHelpers/randomMixHelpers';
|
import { AUDIOBOOK_GENRES, filterRandomMixSongs } from '@/features/randomMix/utils/randomMixHelpers';
|
||||||
import RandomMixHeader from '../components/randomMix/RandomMixHeader';
|
import RandomMixHeader from '@/features/randomMix/components/RandomMixHeader';
|
||||||
import RandomMixFiltersPanel from '../components/randomMix/RandomMixFiltersPanel';
|
import RandomMixFiltersPanel from '@/features/randomMix/components/RandomMixFiltersPanel';
|
||||||
import RandomMixGenrePanel from '../components/randomMix/RandomMixGenrePanel';
|
import RandomMixGenrePanel from '@/features/randomMix/components/RandomMixGenrePanel';
|
||||||
import RandomMixTrackRow from '../components/randomMix/RandomMixTrackRow';
|
import RandomMixTrackRow from '@/features/randomMix/components/RandomMixTrackRow';
|
||||||
|
|
||||||
export default function RandomMix() {
|
export default function RandomMix() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -5,12 +5,12 @@ import type { QueueItemRef } from '@/lib/media/trackTypes';
|
|||||||
import { songToTrack } from '@/lib/media/songToTrack';
|
import { songToTrack } from '@/lib/media/songToTrack';
|
||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
import i18n from '@/lib/i18n';
|
import i18n from '@/lib/i18n';
|
||||||
import { useAuthStore } from '../../store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { pushQueueUndoFromGetter } from '@/features/playback/store/queueUndo';
|
import { pushQueueUndoFromGetter } from '@/features/playback/store/queueUndo';
|
||||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||||
import { useLuckyMixStore } from '../../store/luckyMixStore';
|
import { useLuckyMixStore } from '@/features/randomMix/store/luckyMixStore';
|
||||||
import { isLuckyMixAvailable } from '../../hooks/useLuckyMixAvailable';
|
import { isLuckyMixAvailable } from '@/features/randomMix/hooks/useLuckyMixAvailable';
|
||||||
import { showToast } from '../ui/toast';
|
import { showToast } from '@/utils/ui/toast';
|
||||||
import {
|
import {
|
||||||
bindQueueServerForPlayback,
|
bindQueueServerForPlayback,
|
||||||
playbackServerDiffersFromActive,
|
playbackServerDiffersFromActive,
|
||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
filterSongsForLuckyMixRatings,
|
filterSongsForLuckyMixRatings,
|
||||||
filterTopArtistsForMixRatings,
|
filterTopArtistsForMixRatings,
|
||||||
getMixMinRatingsConfigFromAuth,
|
getMixMinRatingsConfigFromAuth,
|
||||||
} from './mixRatingFilter';
|
} from '@/utils/mix/mixRatingFilter';
|
||||||
import {
|
import {
|
||||||
MIX_TARGET_SIZE,
|
MIX_TARGET_SIZE,
|
||||||
SEED_TARGET_SIZE,
|
SEED_TARGET_SIZE,
|
||||||
@@ -33,7 +33,7 @@ import {
|
|||||||
pickSongsForArtist,
|
pickSongsForArtist,
|
||||||
pickSongsForAlbum,
|
pickSongsForAlbum,
|
||||||
pickGoodRatedSongs,
|
pickGoodRatedSongs,
|
||||||
} from './luckyMixHelpers';
|
} from '@/features/randomMix/utils/luckyMixHelpers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sentinel thrown inside the build loop when `useLuckyMixStore.cancelRequested`
|
* Sentinel thrown inside the build loop when `useLuckyMixStore.cancelRequested`
|
||||||
@@ -5,7 +5,7 @@ import type { SubsonicAlbum, SubsonicSong } from '@/lib/api/subsonicTypes';
|
|||||||
import {
|
import {
|
||||||
filterSongsForLuckyMixRatings,
|
filterSongsForLuckyMixRatings,
|
||||||
type MixMinRatingsConfig,
|
type MixMinRatingsConfig,
|
||||||
} from './mixRatingFilter';
|
} from '@/utils/mix/mixRatingFilter';
|
||||||
|
|
||||||
export interface TopArtist {
|
export interface TopArtist {
|
||||||
id: string;
|
id: string;
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from 'vitest';
|
import { describe, expect, it } from 'vitest';
|
||||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { filterRandomMixSongs } from './randomMixHelpers';
|
import { filterRandomMixSongs } from '@/features/randomMix/utils/randomMixHelpers';
|
||||||
|
|
||||||
function song(id: string): SubsonicSong {
|
function song(id: string): SubsonicSong {
|
||||||
return {
|
return {
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||||
import { passesMixMinRatings, type MixMinRatingsConfig } from '../mix/mixRatingFilter';
|
import { passesMixMinRatings, type MixMinRatingsConfig } from '@/utils/mix/mixRatingFilter';
|
||||||
|
|
||||||
export const AUDIOBOOK_GENRES = [
|
export const AUDIOBOOK_GENRES = [
|
||||||
'hörbuch', 'hoerbuch', 'hörspiel', 'hoerspiel',
|
'hörbuch', 'hoerbuch', 'hörspiel', 'hoerspiel',
|
||||||
@@ -2,7 +2,7 @@ import { useCallback, useRef } from 'react';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useAuthStore } from '@/store/authStore';
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { useSidebarStore, SidebarItemConfig, CONSERVED_SIDEBAR_NAV_IDS } from '@/features/sidebar';
|
import { useSidebarStore, SidebarItemConfig, CONSERVED_SIDEBAR_NAV_IDS } from '@/features/sidebar';
|
||||||
import { useLuckyMixAvailable } from '@/hooks/useLuckyMixAvailable';
|
import { useLuckyMixAvailable } from '@/features/randomMix';
|
||||||
import { ALL_NAV_ITEMS } from '@/config/navItems';
|
import { ALL_NAV_ITEMS } from '@/config/navItems';
|
||||||
import { applySidebarReorderById } from '@/features/sidebar';
|
import { applySidebarReorderById } from '@/features/sidebar';
|
||||||
import { useListReorderDnd } from '@/hooks/useListReorderDnd';
|
import { useListReorderDnd } from '@/hooks/useListReorderDnd';
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
getLibraryItemsForReorder,
|
getLibraryItemsForReorder,
|
||||||
getSystemItemsForReorder,
|
getSystemItemsForReorder,
|
||||||
} from '@/features/sidebar/utils/sidebarNavReorder';
|
} from '@/features/sidebar/utils/sidebarNavReorder';
|
||||||
import { useLuckyMixAvailable } from '@/hooks/useLuckyMixAvailable';
|
import { useLuckyMixAvailable } from '@/features/randomMix';
|
||||||
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
|
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
|
||||||
import { useSidebarNewReleasesUnread } from '@/features/sidebar/hooks/useSidebarNewReleasesUnread';
|
import { useSidebarNewReleasesUnread } from '@/features/sidebar/hooks/useSidebarNewReleasesUnread';
|
||||||
import { useSidebarNavDnd } from '@/features/sidebar/hooks/useSidebarNavDnd';
|
import { useSidebarNavDnd } from '@/features/sidebar/hooks/useSidebarNavDnd';
|
||||||
|
|||||||
Reference in New Issue
Block a user