refactor(hooks): move cross-cutting hooks into lib/hooks

The five residual src/hooks/ hooks (useConnectionStatus, useCardGridMetrics,
useTracklistColumns, useNavidromeAdminRole, useAnalysisPerfListener) are all
domain-agnostic — none imports @/features — so they join the other shared hooks
in src/lib/hooks/ (with their two colocated tests). Consumers repointed;
relative-up imports in the moved files become the @/ alias. src/hooks/ is removed.

Pure move + import updates, no behavior change. The layering baseline gains five
lib->store entries (the three hooks that read authStore/devOfflineBrowseStore are
only now caught under lib/) — same documented debt class as the api move.
This commit is contained in:
Psychotoxical
2026-07-01 14:38:23 +02:00
parent 25055dc79a
commit f2cf8a08df
38 changed files with 120 additions and 35 deletions
@@ -1,7 +1,7 @@
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import type { Track } from '@/lib/media/trackTypes';
import React, { useMemo, useState, useEffect } from 'react';
import { useTracklistColumns } from '@/hooks/useTracklistColumns';
import { useTracklistColumns } from '@/lib/hooks/useTracklistColumns';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useTranslation } from 'react-i18next';
import { useIsMobile } from '@/lib/hooks/useIsMobile';
+1 -1
View File
@@ -2,7 +2,7 @@ import React from 'react';
import { AudioLines, ChevronRight, Heart, Play, Square } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import type { ColDef } from '@/hooks/useTracklistColumns';
import type { ColDef } from '@/lib/hooks/useTracklistColumns';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import type { Track } from '@/lib/media/trackTypes';
import { songToTrack } from '@/lib/media/songToTrack';
@@ -1,6 +1,6 @@
import React from 'react';
import type { TFunction } from 'i18next';
import type { ColDef } from '@/hooks/useTracklistColumns';
import type { ColDef } from '@/lib/hooks/useTracklistColumns';
import { CENTERED_COLS, isSortable, type ColKey, type SortKey } from '@/features/album/utils/albumTrackListHelpers';
interface Props {
@@ -1,4 +1,4 @@
import type { ColDef } from '@/hooks/useTracklistColumns';
import type { ColDef } from '@/lib/hooks/useTracklistColumns';
export function codecLabel(song: { suffix?: string; bitRate?: number }, showBitrate: boolean): string {
const parts: string[] = [];
@@ -6,7 +6,7 @@ import type {
SubsonicAlbum, SubsonicArtist, SubsonicArtistInfo, SubsonicSong,
} from '@/lib/api/subsonicTypes';
import { useAuthStore } from '@/store/authStore';
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { useConnectionStatus } from '@/lib/hooks/useConnectionStatus';
import { loadArtistFromLibraryIndex } from '@/features/offline';
import { useOfflineBrowseContext } from '@/features/offline';
import { loadArtistFromLocalPlayback, offlineLocalBrowseEnabled } from '@/features/offline';
@@ -1,7 +1,7 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { AudioLines, ChevronRight, Play, Square, X } from 'lucide-react';
import type { ColDef } from '@/hooks/useTracklistColumns';
import type { ColDef } from '@/lib/hooks/useTracklistColumns';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { codecLabel } from '@/lib/format/playlistDetailHelpers';
import { formatLastSeen } from '@/lib/format/userMgmtHelpers';
@@ -4,7 +4,7 @@ import FavoriteSongRow, { type FavoriteSongRowCallbacks } from '@/features/favor
import { TracklistColumnPicker } from '@/ui/TracklistColumnPicker';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import type { ColDef } from '@/hooks/useTracklistColumns';
import type { ColDef } from '@/lib/hooks/useTracklistColumns';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { previewInputFromSong, usePreviewStore } from '@/features/playback/store/previewStore';
@@ -7,7 +7,7 @@ import type {
import { useAuthStore } from '@/store/authStore';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import type { TopFavoriteArtist } from '@/features/favorites/components/TopFavoriteArtists';
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { useConnectionStatus } from '@/lib/hooks/useConnectionStatus';
import { isActiveServerReachable } from '@/lib/network/activeServerReachability';
import { useOfflineBrowseContext } from '@/features/offline';
import { useOfflineBrowseReloadToken } from '@/features/offline';
+1 -1
View File
@@ -1,6 +1,6 @@
import { queueSongStar, queueSongRating } from '@/features/playback/store/pendingStarSync';
import React, { useEffect, useMemo, useState } from 'react';
import { useTracklistColumns, type ColDef } from '@/hooks/useTracklistColumns';
import { useTracklistColumns, type ColDef } from '@/lib/hooks/useTracklistColumns';
import { TopFavoriteArtistsRow } from '@/features/favorites/components/TopFavoriteArtists';
import { RadioStationRow } from '@/features/favorites/components/RadioFavorites';
import FavoritesSongsSectionHeader from '@/features/favorites/components/FavoritesSongsSectionHeader';
+1 -1
View File
@@ -28,7 +28,7 @@ import {
writeHomeFeedCache,
type HomeFeedSnapshot,
} from '@/features/home/store/homeFeedCache';
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { useConnectionStatus } from '@/lib/hooks/useConnectionStatus';
import { useOfflineBrowseContext } from '@/features/offline';
import { useOfflineBrowseReloadToken } from '@/features/offline';
import { useDevOfflineBrowseStore } from '@/features/offline';
@@ -7,7 +7,7 @@ import type { ArtistStats, TrackStats } from '@/music-network';
import { getMusicNetworkRuntimeOrNull } from '@/music-network';
import { makeCache } from '@/lib/cache/nowPlayingCache';
import { shouldAttemptSubsonicForServer } from '@/lib/network/subsonicNetworkGuard';
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { useConnectionStatus } from '@/lib/hooks/useConnectionStatus';
// Module-level TTL caches (shared across mounts)
const songMetaCache = makeCache<SubsonicSong | null>();
@@ -1,6 +1,6 @@
import { useAuthStore } from '@/store/authStore';
import { useOfflineStore } from '@/features/offline/store/offlineStore';
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { useConnectionStatus } from '@/lib/hooks/useConnectionStatus';
import { usePlayerStatsRecordingEnabled } from '@/features/stats';
import { hasOfflineBrowsingContent } from '@/features/offline/utils/favoritesOfflineBrowse';
import { useOfflineBrowseActive } from '@/features/offline/utils/offlineBrowseMode';
@@ -1,4 +1,4 @@
import type { ConnectionStatus } from '@/hooks/useConnectionStatus';
import type { ConnectionStatus } from '@/lib/hooks/useConnectionStatus';
import type { OfflineAlbumMeta } from '@/features/offline/store/offlineStore';
import { favoritesOfflineBrowseEnabled } from '@/features/offline/utils/favoritesOfflineBrowse';
import { hasOfflineBrowseCapability } from '@/features/offline/utils/offlineBrowseRouting';
@@ -2,7 +2,7 @@ import {
isDevOfflineBrowseForced,
useDevOfflineBrowseStore,
} from '@/store/devOfflineBrowseStore';
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { useConnectionStatus } from '@/lib/hooks/useConnectionStatus';
import { isActiveServerReachable } from '@/lib/network/activeServerReachability';
/** True when browse/detail pages should use local-bytes-only data sources. */
@@ -1,7 +1,7 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { AudioLines, ChevronRight, Heart, Play, Square, Trash2 } from 'lucide-react';
import type { ColDef } from '@/hooks/useTracklistColumns';
import type { ColDef } from '@/lib/hooks/useTracklistColumns';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { codecLabel } from '@/lib/format/playlistDetailHelpers';
import { formatLastSeen } from '@/lib/format/userMgmtHelpers';
@@ -2,7 +2,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { ChevronRight, Heart, Play, Plus, RefreshCw, Square } from 'lucide-react';
import type { ColDef } from '@/hooks/useTracklistColumns';
import type { ColDef } from '@/lib/hooks/useTracklistColumns';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { usePreviewStore } from '@/features/playback/store/previewStore';
@@ -9,7 +9,7 @@ import { useNavigate } from 'react-router-dom';
import {
ListPlus, Search, Trash2, X,
} from 'lucide-react';
import type { ColDef } from '@/hooks/useTracklistColumns';
import type { ColDef } from '@/lib/hooks/useTracklistColumns';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import type { Track } from '@/lib/media/trackTypes';
import { usePlayerStore } from '@/features/playback/store/playerStore';
@@ -2,7 +2,7 @@ import { updatePlaylist } from '@/lib/api/subsonicPlaylists';
import type { SubsonicPlaylist, SubsonicSong } from '@/lib/api/subsonicTypes';
import React, { useEffect, useState, useCallback, useMemo } from 'react';
import { useParams, useNavigate, useLocation } from 'react-router-dom';
import { useTracklistColumns, type ColDef } from '@/hooks/useTracklistColumns';
import { useTracklistColumns, type ColDef } from '@/lib/hooks/useTracklistColumns';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useShallow } from 'zustand/react/shallow';
import { usePlaylistStore } from '@/features/playlist/store/playlistStore';
+1 -1
View File
@@ -15,7 +15,7 @@ import RadioEditModal from '@/features/radio/components/RadioEditModal';
import RadioDirectoryModal from '@/features/radio/components/RadioDirectoryModal';
import { usePerfProbeFlags } from '@/lib/perf/perfFlags';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import { useNavidromeAdminRole, canManageNavidromeRadio } from '@/hooks/useNavidromeAdminRole';
import { useNavidromeAdminRole, canManageNavidromeRadio } from '@/lib/hooks/useNavidromeAdminRole';
export default function InternetRadio() {
const { t } = useTranslation();
@@ -1,7 +1,7 @@
import { useAuthStore } from '@/store/authStore';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useConnectionStatus } from '@/hooks/useConnectionStatus';
import { useNavidromeAdminRole } from '@/hooks/useNavidromeAdminRole';
import { useConnectionStatus } from '@/lib/hooks/useConnectionStatus';
import { useNavidromeAdminRole } from '@/lib/hooks/useNavidromeAdminRole';
import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
import { findServerByIdOrIndexKey } from '@/lib/server/serverLookup';
import { PerfProbeMetricSection } from '@/features/sidebar/components/perfProbe/PerfProbeMetricCard';
@@ -1,5 +1,5 @@
import { useAuthStore } from '@/store/authStore';
import type { NavidromeAdminRole } from '@/hooks/useNavidromeAdminRole';
import type { NavidromeAdminRole } from '@/lib/hooks/useNavidromeAdminRole';
import { isNavidromeServer, formatServerSoftware } from '@/lib/server/subsonicServerIdentity';
import { FEATURE_AUDIOMUSE_SIMILAR_TRACKS, OP_SIMILAR_TRACKS } from '@/serverCapabilities/catalog';
import {
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import { acquirePerfLivePoll, patchPerfLiveAnalysis } from '@/lib/perf/perfLiveStore';
import { setPerfProbeTelemetryActive } from '@/lib/perf/perfTelemetry';
import { useAnalysisPerfLast } from '@/lib/perf/analysisPerfStore';
import { useAnalysisPerfListener } from '@/hooks/useAnalysisPerfListener';
import { useAnalysisPerfListener } from '@/lib/hooks/useAnalysisPerfListener';
import { useCoverPerfListener, useCoverUiThroughputPoll } from '@/cover/useCoverPerfListener';
import {
getPerfProbeFlags,