From 31bc24a17862d794aa1ae60e4ffa4c5519d4c230 Mon Sep 17 00:00:00 2001 From: Psychotoxical <171614930+Psychotoxical@users.noreply.github.com> Date: Tue, 30 Jun 2026 19:30:41 +0200 Subject: [PATCH] refactor(hooks): co-locate feature-coupled hooks (playback server/navigate/timeline/audio-devices->playback, smart-collage/pending-polling->playlist, contextmenu rating/keyboard->contextMenu) --- src/features/contextMenu/components/ContextMenu.tsx | 6 +++--- .../contextMenu}/hooks/useContextMenuKeyboardNav.ts | 0 .../contextMenu}/hooks/useContextMenuRating.ts | 4 ++-- src/features/nowPlaying/components/MobilePlayerView.tsx | 2 +- src/features/nowPlaying/components/NowPlayingInfo.tsx | 2 +- src/features/nowPlaying/hooks/useNowPlayingPrewarm.ts | 2 +- src/features/nowPlaying/pages/NowPlaying.tsx | 4 ++-- src/features/playback/components/PlayerBar.tsx | 2 +- src/{ => features/playback}/hooks/useAudioDevicesProbe.ts | 4 ++-- .../playback}/hooks/usePlaybackLibraryNavigate.ts | 2 +- src/{ => features/playback}/hooks/usePlaybackServerId.ts | 2 +- src/{ => features/playback}/hooks/useTimelinePlayHistory.ts | 4 ++-- src/{ => features/playlist}/hooks/usePendingSmartPolling.ts | 0 src/{ => features/playlist}/hooks/useSmartCoverCollage.ts | 0 src/features/playlist/pages/Playlists.tsx | 4 ++-- src/features/queue/components/QueuePanel.tsx | 4 ++-- src/features/queue/hooks/useQueueTrackEnrichment.ts | 2 +- src/features/settings/components/AudioTab.tsx | 2 +- src/hooks/usePlaybackServerId.test.ts | 2 +- src/hooks/useTimelinePlayHistory.test.ts | 2 +- 20 files changed, 25 insertions(+), 25 deletions(-) rename src/{ => features/contextMenu}/hooks/useContextMenuKeyboardNav.ts (100%) rename src/{ => features/contextMenu}/hooks/useContextMenuRating.ts (98%) rename src/{ => features/playback}/hooks/useAudioDevicesProbe.ts (97%) rename src/{ => features/playback}/hooks/usePlaybackLibraryNavigate.ts (86%) rename src/{ => features/playback}/hooks/usePlaybackServerId.ts (95%) rename src/{ => features/playback}/hooks/useTimelinePlayHistory.ts (96%) rename src/{ => features/playlist}/hooks/usePendingSmartPolling.ts (100%) rename src/{ => features/playlist}/hooks/useSmartCoverCollage.ts (100%) diff --git a/src/features/contextMenu/components/ContextMenu.tsx b/src/features/contextMenu/components/ContextMenu.tsx index ccf780d4..6df22925 100644 --- a/src/features/contextMenu/components/ContextMenu.tsx +++ b/src/features/contextMenu/components/ContextMenu.tsx @@ -13,9 +13,9 @@ import { startInstantMix as startInstantMixAction, startRadio as startRadioAction, } from '@/utils/componentHelpers/contextMenuActions'; -import { useContextMenuKeyboardNav } from '@/hooks/useContextMenuKeyboardNav'; -import { useContextMenuRating } from '@/hooks/useContextMenuRating'; -import { usePlaybackLibraryNavigate } from '@/hooks/usePlaybackLibraryNavigate'; +import { useContextMenuKeyboardNav } from '@/features/contextMenu/hooks/useContextMenuKeyboardNav'; +import { useContextMenuRating } from '@/features/contextMenu/hooks/useContextMenuRating'; +import { usePlaybackLibraryNavigate } from '@/features/playback/hooks/usePlaybackLibraryNavigate'; import { useNavigate } from 'react-router-dom'; import { useOfflineBrowseContext } from '@/features/offline'; import { diff --git a/src/hooks/useContextMenuKeyboardNav.ts b/src/features/contextMenu/hooks/useContextMenuKeyboardNav.ts similarity index 100% rename from src/hooks/useContextMenuKeyboardNav.ts rename to src/features/contextMenu/hooks/useContextMenuKeyboardNav.ts diff --git a/src/hooks/useContextMenuRating.ts b/src/features/contextMenu/hooks/useContextMenuRating.ts similarity index 98% rename from src/hooks/useContextMenuRating.ts rename to src/features/contextMenu/hooks/useContextMenuRating.ts index 44fb39df..e90b5a7b 100644 --- a/src/hooks/useContextMenuRating.ts +++ b/src/features/contextMenu/hooks/useContextMenuRating.ts @@ -3,8 +3,8 @@ import { setRating } from '@/lib/api/subsonicStarRating'; import { queueSongRating } from '@/features/playback/store/pendingStarSync'; import type { SubsonicAlbum, SubsonicArtist } from '@/lib/api/subsonicTypes'; import type { Track } from '@/lib/media/trackTypes'; -import { useAuthStore } from '../store/authStore'; -import { showToast } from '../utils/ui/toast'; +import { useAuthStore } from '@/store/authStore'; +import { showToast } from '@/utils/ui/toast'; type RatingKind = 'song' | 'album' | 'artist'; diff --git a/src/features/nowPlaying/components/MobilePlayerView.tsx b/src/features/nowPlaying/components/MobilePlayerView.tsx index fdf75d6c..ee682ea2 100644 --- a/src/features/nowPlaying/components/MobilePlayerView.tsx +++ b/src/features/nowPlaying/components/MobilePlayerView.tsx @@ -5,7 +5,7 @@ import type { Track } from '@/lib/media/trackTypes'; import { getPlaybackProgressSnapshot, subscribePlaybackProgress } from '@/features/playback/store/playbackProgress'; import React, { useState, useCallback, useRef, useEffect, useSyncExternalStore, CSSProperties } from 'react'; import { useNavigate } from 'react-router-dom'; -import { usePlaybackLibraryNavigate } from '@/hooks/usePlaybackLibraryNavigate'; +import { usePlaybackLibraryNavigate } from '@/features/playback/hooks/usePlaybackLibraryNavigate'; import { useTranslation } from 'react-i18next'; import { useVirtualizer } from '@tanstack/react-virtual'; import { diff --git a/src/features/nowPlaying/components/NowPlayingInfo.tsx b/src/features/nowPlaying/components/NowPlayingInfo.tsx index 1214e24c..3be4eaec 100644 --- a/src/features/nowPlaying/components/NowPlayingInfo.tsx +++ b/src/features/nowPlaying/components/NowPlayingInfo.tsx @@ -7,7 +7,7 @@ import { Info } from 'lucide-react'; import { open as shellOpen } from '@tauri-apps/plugin-shell'; import { usePlayerStore } from '@/features/playback/store/playerStore'; import { useAuthStore } from '@/store/authStore'; -import { usePlaybackServerId } from '@/hooks/usePlaybackServerId'; +import { usePlaybackServerId } from '@/features/playback/hooks/usePlaybackServerId'; import { fetchBandsintownEvents, type BandsintownEvent } from '@/api/bandsintown'; import CachedImage from '@/ui/CachedImage'; import OverlayScrollArea from '@/ui/OverlayScrollArea'; diff --git a/src/features/nowPlaying/hooks/useNowPlayingPrewarm.ts b/src/features/nowPlaying/hooks/useNowPlayingPrewarm.ts index a840b116..4316adc4 100644 --- a/src/features/nowPlaying/hooks/useNowPlayingPrewarm.ts +++ b/src/features/nowPlaying/hooks/useNowPlayingPrewarm.ts @@ -11,7 +11,7 @@ import type { CoverArtRef } from '@/cover/types'; import { prewarmNowPlayingFetchers } from '@/features/nowPlaying/hooks/useNowPlayingFetchers'; import { useAuthStore } from '@/store/authStore'; import { usePlayerStore } from '@/features/playback/store/playerStore'; -import { usePlaybackServerId } from '@/hooks/usePlaybackServerId'; +import { usePlaybackServerId } from '@/features/playback/hooks/usePlaybackServerId'; import { primaryTrackArtistRef } from '@/features/playback/utils/playback/trackArtistRefs'; const NOW_PLAYING_COVER_CSS_PX = 800; diff --git a/src/features/nowPlaying/pages/NowPlaying.tsx b/src/features/nowPlaying/pages/NowPlaying.tsx index 93786204..dff22708 100644 --- a/src/features/nowPlaying/pages/NowPlaying.tsx +++ b/src/features/nowPlaying/pages/NowPlaying.tsx @@ -4,8 +4,8 @@ import { usePlaybackTrackCoverRef } from '@/cover/useLibraryCoverRef'; import { coverArtIdFromRadio } from '@/cover/ids'; import type { SubsonicArtistInfo, SubsonicSong } from '@/lib/api/subsonicTypes'; import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react'; -import { usePlaybackLibraryNavigate } from '@/hooks/usePlaybackLibraryNavigate'; -import { usePlaybackServerId } from '@/hooks/usePlaybackServerId'; +import { usePlaybackLibraryNavigate } from '@/features/playback/hooks/usePlaybackLibraryNavigate'; +import { usePlaybackServerId } from '@/features/playback/hooks/usePlaybackServerId'; import { useTranslation } from 'react-i18next'; import { Music, EyeOff, LayoutGrid, RotateCcw, Eye } from 'lucide-react'; import { usePlayerStore } from '@/features/playback/store/playerStore'; diff --git a/src/features/playback/components/PlayerBar.tsx b/src/features/playback/components/PlayerBar.tsx index a00518df..be50559a 100644 --- a/src/features/playback/components/PlayerBar.tsx +++ b/src/features/playback/components/PlayerBar.tsx @@ -14,7 +14,7 @@ import { useAuthStore } from '@/store/authStore'; import { useThemeStore } from '@/store/themeStore'; import { Equalizer } from '@/features/equalizer'; import { useTranslation } from 'react-i18next'; -import { usePlaybackLibraryNavigate } from '@/hooks/usePlaybackLibraryNavigate'; +import { usePlaybackLibraryNavigate } from '@/features/playback/hooks/usePlaybackLibraryNavigate'; import { useRadioMetadata } from '@/features/radio'; import { useRadioMprisSync } from '@/features/radio'; import { usePlaybackDelayPress } from '@/hooks/usePlaybackDelayPress'; diff --git a/src/hooks/useAudioDevicesProbe.ts b/src/features/playback/hooks/useAudioDevicesProbe.ts similarity index 97% rename from src/hooks/useAudioDevicesProbe.ts rename to src/features/playback/hooks/useAudioDevicesProbe.ts index b22fecfa..a2288234 100644 --- a/src/hooks/useAudioDevicesProbe.ts +++ b/src/features/playback/hooks/useAudioDevicesProbe.ts @@ -2,10 +2,10 @@ import { useCallback, useEffect, useState } from 'react'; import { invoke } from '@tauri-apps/api/core'; import { listen } from '@tauri-apps/api/event'; import type { TFunction } from 'i18next'; -import { useAuthStore } from '../store/authStore'; +import { useAuthStore } from '@/store/authStore'; import { IS_MACOS } from '@/lib/util/platform'; import { sortAudioDeviceIds } from '@/features/playback/utils/audio/audioDeviceLabels'; -import { showToast } from '../utils/ui/toast'; +import { showToast } from '@/utils/ui/toast'; interface UseAudioDevicesProbeResult { audioDevices: string[]; diff --git a/src/hooks/usePlaybackLibraryNavigate.ts b/src/features/playback/hooks/usePlaybackLibraryNavigate.ts similarity index 86% rename from src/hooks/usePlaybackLibraryNavigate.ts rename to src/features/playback/hooks/usePlaybackLibraryNavigate.ts index 0d7e9bca..21ab77bb 100644 --- a/src/hooks/usePlaybackLibraryNavigate.ts +++ b/src/features/playback/hooks/usePlaybackLibraryNavigate.ts @@ -1,7 +1,7 @@ import { useCallback } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { ensurePlaybackServerActive } from '@/features/playback/utils/playback/playbackServer'; -import { navigatePathWithAlbumReturnTo } from '../utils/navigation/albumDetailNavigation'; +import { navigatePathWithAlbumReturnTo } from '@/utils/navigation/albumDetailNavigation'; /** Navigate to library routes for the playing queue — switches to {@link queueServerId} when needed. */ export function usePlaybackLibraryNavigate() { diff --git a/src/hooks/usePlaybackServerId.ts b/src/features/playback/hooks/usePlaybackServerId.ts similarity index 95% rename from src/hooks/usePlaybackServerId.ts rename to src/features/playback/hooks/usePlaybackServerId.ts index 116109ef..fdfebf97 100644 --- a/src/hooks/usePlaybackServerId.ts +++ b/src/features/playback/hooks/usePlaybackServerId.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import { useAuthStore } from '../store/authStore'; +import { useAuthStore } from '@/store/authStore'; import { usePlayerStore } from '@/features/playback/store/playerStore'; import { getPlaybackServerId } from '@/features/playback/utils/playback/playbackServer'; diff --git a/src/hooks/useTimelinePlayHistory.ts b/src/features/playback/hooks/useTimelinePlayHistory.ts similarity index 96% rename from src/hooks/useTimelinePlayHistory.ts rename to src/features/playback/hooks/useTimelinePlayHistory.ts index 0f6185fe..157a7bf6 100644 --- a/src/hooks/useTimelinePlayHistory.ts +++ b/src/features/playback/hooks/useTimelinePlayHistory.ts @@ -13,8 +13,8 @@ import { import { bootstrapTrackFromPlaySession, timelineHistoryToQueueRefs, -} from '../utils/queue/timelineHistoryRefs'; -import { timelineBootstrapIndexReady } from '../utils/queue/timelineBootstrapReady'; +} from '@/utils/queue/timelineHistoryRefs'; +import { timelineBootstrapIndexReady } from '@/utils/queue/timelineBootstrapReady'; const BOOTSTRAP_RETRY_MS = 2_000; diff --git a/src/hooks/usePendingSmartPolling.ts b/src/features/playlist/hooks/usePendingSmartPolling.ts similarity index 100% rename from src/hooks/usePendingSmartPolling.ts rename to src/features/playlist/hooks/usePendingSmartPolling.ts diff --git a/src/hooks/useSmartCoverCollage.ts b/src/features/playlist/hooks/useSmartCoverCollage.ts similarity index 100% rename from src/hooks/useSmartCoverCollage.ts rename to src/features/playlist/hooks/useSmartCoverCollage.ts diff --git a/src/features/playlist/pages/Playlists.tsx b/src/features/playlist/pages/Playlists.tsx index 6f48b1ac..b9a68941 100644 --- a/src/features/playlist/pages/Playlists.tsx +++ b/src/features/playlist/pages/Playlists.tsx @@ -14,9 +14,9 @@ import { defaultSmartFilters, type SmartFilters, type PendingSmartPlaylist, } from '@/features/playlist/utils/playlistsSmart'; -import { useSmartCoverCollage } from '@/hooks/useSmartCoverCollage'; +import { useSmartCoverCollage } from '@/features/playlist/hooks/useSmartCoverCollage'; import { usePlaylistsLibraryScopeCounts } from '@/features/playlist/hooks/usePlaylistsLibraryScopeCounts'; -import { usePendingSmartPolling } from '@/hooks/usePendingSmartPolling'; +import { usePendingSmartPolling } from '@/features/playlist/hooks/usePendingSmartPolling'; import { runPlaylistsOpenSmartEditor } from '@/features/playlist/utils/runPlaylistsOpenSmartEditor'; import { runPlaylistsSaveSmart } from '@/features/playlist/utils/runPlaylistsSaveSmart'; import { diff --git a/src/features/queue/components/QueuePanel.tsx b/src/features/queue/components/QueuePanel.tsx index 7438e913..68951ea6 100644 --- a/src/features/queue/components/QueuePanel.tsx +++ b/src/features/queue/components/QueuePanel.tsx @@ -10,7 +10,7 @@ import { OrbitGuestQueue, OrbitQueueHead } from '@/features/orbit'; import HostApprovalQueue from '@/features/orbit/components/HostApprovalQueue'; import { usePlaylistStore } from '@/features/playlist'; import { useTranslation } from 'react-i18next'; -import { usePlaybackLibraryNavigate } from '@/hooks/usePlaybackLibraryNavigate'; +import { usePlaybackLibraryNavigate } from '@/features/playback/hooks/usePlaybackLibraryNavigate'; import { useAuthStore } from '@/store/authStore'; import { encodeSharePayload } from '@/utils/share/shareLink'; import { serverShareBaseUrl } from '@/utils/server/serverEndpoint'; @@ -32,7 +32,7 @@ import { QueueToolbar } from '@/features/queue/components/QueueToolbar'; import { QueueList } from '@/features/queue/components/QueueList'; import { QueueTabBar } from '@/features/queue/components/QueueTabBar'; import { useQueueAutoScroll } from '@/features/queue/hooks/useQueueAutoScroll'; -import { useTimelineBootstrapOnMode, useTimelineHistoryResolver, useTimelinePlayHistory } from '@/hooks/useTimelinePlayHistory'; +import { useTimelineBootstrapOnMode, useTimelineHistoryResolver, useTimelinePlayHistory } from '@/features/playback/hooks/useTimelinePlayHistory'; import { buildTimelineDisplayRows } from '@/utils/queue/buildTimelineDisplayRows'; import { activeServerQueueTrackIds } from '@/features/playback/utils/playback/trackServerScope'; diff --git a/src/features/queue/hooks/useQueueTrackEnrichment.ts b/src/features/queue/hooks/useQueueTrackEnrichment.ts index 38d8e655..cf12db2a 100644 --- a/src/features/queue/hooks/useQueueTrackEnrichment.ts +++ b/src/features/queue/hooks/useQueueTrackEnrichment.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { listen } from '@tauri-apps/api/event'; import { libraryGetFacts, libraryGetTrack } from '@/lib/api/library'; -import { usePlaybackServerId } from '@/hooks/usePlaybackServerId'; +import { usePlaybackServerId } from '@/features/playback/hooks/usePlaybackServerId'; import { useLibraryIndexStore } from '@/store/libraryIndexStore'; import { enrichmentDisplayComplete, diff --git a/src/features/settings/components/AudioTab.tsx b/src/features/settings/components/AudioTab.tsx index 2214c5dc..c7ee792e 100644 --- a/src/features/settings/components/AudioTab.tsx +++ b/src/features/settings/components/AudioTab.tsx @@ -7,7 +7,7 @@ import SettingsSubSection from '@/features/settings/components/SettingsSubSectio import { SettingsGroup } from '@/features/settings/components/SettingsGroup'; import { SettingsToggle } from '@/features/settings/components/SettingsToggle'; import { effectiveLoudnessPreAnalysisAttenuationDb } from '@/utils/audio/loudnessPreAnalysisSlider'; -import { useAudioDevicesProbe } from '@/hooks/useAudioDevicesProbe'; +import { useAudioDevicesProbe } from '@/features/playback/hooks/useAudioDevicesProbe'; import { IS_MACOS } from '@/lib/util/platform'; import { AudioOutputDeviceSection } from '@/features/settings/components/audio/AudioOutputDeviceSection'; import { NormalizationBlock } from '@/features/settings/components/audio/NormalizationBlock'; diff --git a/src/hooks/usePlaybackServerId.test.ts b/src/hooks/usePlaybackServerId.test.ts index 6c0d6830..be906c87 100644 --- a/src/hooks/usePlaybackServerId.test.ts +++ b/src/hooks/usePlaybackServerId.test.ts @@ -2,7 +2,7 @@ import { renderHook } from '@testing-library/react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { useAuthStore } from '../store/authStore'; import { usePlayerStore } from '@/features/playback/store/playerStore'; -import { usePlaybackServerId } from './usePlaybackServerId'; +import { usePlaybackServerId } from '@/features/playback/hooks/usePlaybackServerId'; vi.mock('../utils/server/switchActiveServer', () => ({ switchActiveServer: vi.fn(async () => true), diff --git a/src/hooks/useTimelinePlayHistory.test.ts b/src/hooks/useTimelinePlayHistory.test.ts index df764206..c89d469a 100644 --- a/src/hooks/useTimelinePlayHistory.test.ts +++ b/src/hooks/useTimelinePlayHistory.test.ts @@ -6,7 +6,7 @@ import { import { _resetTimelineBootstrapInFlightForTest, ensureTimelineBootstrap, -} from './useTimelinePlayHistory'; +} from '@/features/playback/hooks/useTimelinePlayHistory'; vi.mock('@/lib/api/library', () => ({ libraryGetRecentPlaySessions: vi.fn(async () => []),