refactor(ui): move generic presentational primitives into ui/ (CoverLightbox, StarRating, LongPressWaveOverlay, icons, VirtualCardGrid)

This commit is contained in:
Psychotoxical
2026-06-30 18:28:54 +02:00
parent 8cd23e86db
commit 41877d10d8
50 changed files with 52 additions and 52 deletions
+1 -1
View File
@@ -18,7 +18,7 @@ import { acquireUrl } from '@/utils/imageCache/urlPool';
import { OpenArtistRefInline } from '@/features/artist';
import { fetchAlbumTracks, playAlbum, playAlbumShuffled } from '@/features/playback/utils/playback/playAlbum';
import { useLongPressAction } from '@/lib/hooks/useLongPressAction';
import { LongPressWaveOverlay } from '@/components/LongPressWaveOverlay';
import { LongPressWaveOverlay } from '@/ui/LongPressWaveOverlay';
import { useDragDrop } from '@/lib/dnd/DragDropContext';
import { isAlbumRecentlyAdded } from '@/features/album/utils/albumRecency';
import { albumArtistDisplayName, deriveAlbumArtistRefs } from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
@@ -18,7 +18,7 @@ vi.mock('@/cover/lightbox', () => ({ useCoverLightboxSrc: () => ({ open: vi.fn()
vi.mock('@/features/album/hooks/useAlbumDetailBack', () => ({ useAlbumDetailBack: () => vi.fn() }));
vi.mock('@/lib/hooks/useIsMobile', () => ({ useIsMobile: () => false }));
vi.mock('@/store/themeStore', () => ({ useThemeStore: () => false }));
vi.mock('@/components/StarRating', () => ({ default: () => null }));
vi.mock('@/ui/StarRating', () => ({ default: () => null }));
vi.mock('@/features/artist', () => ({ OpenArtistRefInline: () => null }));
vi.mock('@/cover/CoverArtImage', () => ({ CoverArtImage: () => null }));
@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
import { useIsMobile } from '@/lib/hooks/useIsMobile';
import { useAlbumDetailBack } from '@/features/album/hooks/useAlbumDetailBack';
import { useThemeStore } from '@/store/themeStore';
import StarRating from '@/components/StarRating';
import StarRating from '@/ui/StarRating';
import { copyEntityShareLink } from '@/utils/share/copyEntityShareLink';
import { showToast } from '@/utils/ui/toast';
import { isAlbumRecentlyAdded } from '@/features/album/utils/albumRecency';
+1 -1
View File
@@ -9,7 +9,7 @@ import { songToTrack } from '@/lib/media/songToTrack';
import { useSelectionStore } from '@/store/selectionStore';
import { useThemeStore } from '@/store/themeStore';
import { previewInputFromSong, usePreviewStore } from '@/features/playback/store/previewStore';
import StarRating from '@/components/StarRating';
import StarRating from '@/ui/StarRating';
import { codecLabel, type ColKey } from '@/features/album/utils/albumTrackListHelpers';
import { formatLongDuration } from '@/lib/format/formatDuration';
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
+1 -1
View File
@@ -41,7 +41,7 @@ import { sanitizeFilename } from '@/features/album/utils/albumDetailHelpers';
import { albumArtistDisplayName, deriveAlbumHeaderArtistRefs } from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { albumGridWarmCovers } from '@/cover/layoutSizes';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import LosslessModeBanner from '@/components/LosslessModeBanner';
import { isLosslessSuffix } from '@/lib/library/losslessFormats';
import { isLosslessMode } from '@/lib/library/losslessMode';
+1 -1
View File
@@ -30,7 +30,7 @@ import { useRangeSelection } from '@/lib/hooks/useRangeSelection';
import { useMainstageInpageHeaderTight } from '@/hooks/useMainstageInpageHeaderTight';
import { useInpageScrollViewport } from '@/hooks/useInpageScrollViewport';
import InpageScrollSentinel from '@/ui/InpageScrollSentinel';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import OverlayScrollArea from '@/ui/OverlayScrollArea';
import { ALBUMS_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import { useLibraryIndexStore } from '@/store/libraryIndexStore';
+1 -1
View File
@@ -8,7 +8,7 @@ import { useTranslation } from 'react-i18next';
import { useAuthStore } from '@/store/authStore';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { albumGridWarmCovers } from '@/cover/layoutSizes';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
export default function LabelAlbums() {
const { t } = useTranslation();
+1 -1
View File
@@ -21,7 +21,7 @@ import { join } from '@tauri-apps/api/path';
import { Download, HardDriveDownload, ListPlus } from 'lucide-react';
import SelectionToggleButton from '@/ui/SelectionToggleButton';
import { albumGridWarmCovers } from '@/cover/layoutSizes';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import OverlayScrollArea from '@/ui/OverlayScrollArea';
import { LOSSLESS_ALBUMS_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import { useInpageScrollSentinel } from '@/hooks/useInpageScrollSentinel';
+1 -1
View File
@@ -11,7 +11,7 @@ import { AlbumCoverArtImage } from '@/cover/AlbumCoverArtImage';
import { ArtistCoverArtImage } from '@/cover/ArtistCoverArtImage';
import { playAlbum, playAlbumShuffled } from '@/features/playback/utils/playback/playAlbum';
import { useLongPressAction } from '@/lib/hooks/useLongPressAction';
import { LongPressWaveOverlay } from '@/components/LongPressWaveOverlay';
import { LongPressWaveOverlay } from '@/ui/LongPressWaveOverlay';
import { useTranslation } from 'react-i18next';
import { albumArtistDisplayName } from '@/features/album/utils/deriveAlbumHeaderArtistRefs';
+1 -1
View File
@@ -22,7 +22,7 @@ import { useRangeSelection } from '@/lib/hooks/useRangeSelection';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { useMainstageInpageHeaderTight } from '@/hooks/useMainstageInpageHeaderTight';
import { albumGridWarmCovers } from '@/cover/layoutSizes';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import OverlayScrollArea from '@/ui/OverlayScrollArea';
import { NEW_RELEASES_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import { useAsyncInpagePagination } from '@/hooks/useAsyncInpagePagination';
+1 -1
View File
@@ -28,7 +28,7 @@ import { albumGridWarmCovers, COVER_DENSE_GRID_MIN_CELL_CSS_PX } from '@/cover/l
import {
primeAlbumCoversForDisplay,
} from '@/cover/warmDiskPeek';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import OverlayScrollArea from '@/ui/OverlayScrollArea';
import { RANDOM_ALBUMS_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import { useMainstageInpageHeaderTight } from '@/hooks/useMainstageInpageHeaderTight';
+1 -1
View File
@@ -3,7 +3,7 @@ import React from 'react';
import { renderToStaticMarkup } from 'react-dom/server';
import { coverArtRef } from '@/cover/ref';
import { loadCoverBlobForExport } from '@/cover/integrations/export';
import PsysonicLogo from '@/components/PsysonicLogo';
import PsysonicLogo from '@/ui/PsysonicLogo';
export type ExportFormat = 'story' | 'square' | 'twitter';
export type ExportGridSize = 3 | 4 | 5;
@@ -18,9 +18,9 @@ import { usePlaybackCoverArt } from '@/cover/usePlaybackCoverArt';
import { useCachedUrl } from '@/ui/CachedImage';
import { useCoverLightboxSrc } from '@/cover/lightbox';
import type { CoverArtRef } from '@/cover/types';
import LastfmIcon from '@/components/LastfmIcon';
import WikipediaIcon from '@/components/WikipediaIcon';
import StarRating from '@/components/StarRating';
import LastfmIcon from '@/ui/LastfmIcon';
import WikipediaIcon from '@/ui/WikipediaIcon';
import StarRating from '@/ui/StarRating';
import { tooltipAttrs } from '@/ui/tooltipAttrs';
import { offlineActionPolicy, type OfflineActionPolicy } from '@/features/offline';
@@ -4,7 +4,7 @@ import type { TFunction } from 'i18next';
import type { SubsonicArtist } from '@/lib/api/subsonicTypes';
import type { PlayerState } from '@/features/playback/store/playerStoreTypes';
import { ARTISTS_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import { ArtistCardAvatar } from '@/features/artist/components/ArtistAvatars';
interface TileProps {
+1 -1
View File
@@ -35,7 +35,7 @@ import { albumGridWarmCovers, COVER_DENSE_GRID_MIN_CELL_CSS_PX, GRID_COVER_WARM_
import { artistDetailCoverWarmAlbums } from '@/features/artist/components/topSongAlbumForCover';
import { useLibraryCoverPrefetch } from '@/cover/useLibraryCoverPrefetch';
import { useWarmGridCovers } from '@/hooks/useWarmGridCovers';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import { LOSSLESS_MODE_QUERY } from '@/lib/library/losslessMode';
import { sortArtistAlbumsByYear } from '@/features/artist/utils/sortArtistAlbums';
import { readDetailServerId } from '@/utils/navigation/detailServerScope';
@@ -9,7 +9,7 @@ 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 '@/ui/WikipediaIcon';
import { open } from '@tauri-apps/plugin-shell';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useAuthStore } from '@/store/authStore';
@@ -19,7 +19,7 @@ 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 { VirtualCardGrid } from '@/ui/VirtualCardGrid';
export default function ComposerDetail() {
const { t } = useTranslation();
+1 -1
View File
@@ -23,7 +23,7 @@ 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 { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import OverlayScrollArea from '@/ui/OverlayScrollArea';
import { useVirtualizerScrollMargin } from '@/lib/hooks/useVirtualizerScrollMargin';
import { useClientSliceInfiniteScroll } from '@/hooks/useClientSliceInfiniteScroll';
@@ -7,7 +7,7 @@ import { codecLabel } from '@/utils/componentHelpers/playlistDetailHelpers';
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
import i18n from '@/lib/i18n';
import { formatTrackTime } from '@/lib/format/formatDuration';
import StarRating from '@/components/StarRating';
import StarRating from '@/ui/StarRating';
import { OpenArtistRefInline } from '@/features/artist';
import { resolveTrackArtistRefs } from '@/features/playback/utils/playback/trackArtistRefs';
+2 -2
View File
@@ -3,10 +3,10 @@ import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { ArrowLeft, Disc3, Play, ListPlus, Loader2 } from 'lucide-react';
import { AlbumCard } from '@/features/album';
import { LongPressWaveOverlay } from '@/components/LongPressWaveOverlay';
import { LongPressWaveOverlay } from '@/ui/LongPressWaveOverlay';
import InpageScrollSentinel from '@/ui/InpageScrollSentinel';
import OverlayScrollArea from '@/ui/OverlayScrollArea';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import { GENRE_DETAIL_INPAGE_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
import { albumGridWarmCovers } from '@/cover/layoutSizes';
import { useAlbumBrowseScrollSnapshotSync, type AlbumBrowseScrollSnapshot } from '@/features/album';
@@ -22,7 +22,7 @@ import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useAuthStore } from '@/store/authStore';
import { playAlbum, playAlbumShuffled } from '@/features/playback/utils/playback/playAlbum';
import { useLongPressAction } from '@/lib/hooks/useLongPressAction';
import { LongPressWaveOverlay } from '@/components/LongPressWaveOverlay';
import { LongPressWaveOverlay } from '@/ui/LongPressWaveOverlay';
import { formatHumanHoursMinutes } from '@/lib/format/formatHumanDuration';
import { AlbumRow } from '@/features/album';
import { albumArtistDisplayName } from '@/features/album';
+1 -1
View File
@@ -21,7 +21,7 @@ import { filterAlbumsByMixRatings, getMixMinRatingsConfigFromAuth } from '@/util
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { playAlbum, playAlbumShuffled } from '@/features/playback/utils/playback/playAlbum';
import { useLongPressAction } from '@/lib/hooks/useLongPressAction';
import { LongPressWaveOverlay } from '@/components/LongPressWaveOverlay';
import { LongPressWaveOverlay } from '@/ui/LongPressWaveOverlay';
import { albumArtistDisplayName, deriveAlbumArtistRefs } from '@/features/album';
const INTERVAL_MS = 10000;
@@ -11,7 +11,7 @@ import { AlbumCoverArtImage } from '@/cover/AlbumCoverArtImage';
import { coverServerScopeForServerId } from '@/cover/serverScope';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { GRID_COVER_WARM_LIMIT } from '@/cover/layoutSizes';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import { OfflineLibraryDiskStat } from '@/features/offline/components/OfflineLibraryDiskStat';
import {
buildOfflineCacheQueueTracks,
@@ -5,7 +5,7 @@ import type { SubsonicPlaylist } from '@/lib/api/subsonicTypes';
import { usePlaylistFolderStore } from '@/features/playlist/store/playlistFolderStore';
import type { PlaylistFolder } from '@/features/playlist/utils/playlistFolders';
import { useDragDrop } from '@/lib/dnd/DragDropContext';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
interface Props {
serverId: string;
@@ -7,7 +7,7 @@ import { codecLabel } from '@/utils/componentHelpers/playlistDetailHelpers';
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
import i18n from '@/lib/i18n';
import { formatTrackTime } from '@/lib/format/formatDuration';
import StarRating from '@/components/StarRating';
import StarRating from '@/ui/StarRating';
import { PlaylistArtistCell } from '@/features/playlist/components/PlaylistArtistCell';
export interface PlaylistRowCallbacks {
@@ -6,7 +6,7 @@ import type { ColDef } from '@/utils/useTracklistColumns';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { usePreviewStore } from '@/features/playback/store/previewStore';
import StarRating from '@/components/StarRating';
import StarRating from '@/ui/StarRating';
import { PlaylistArtistCell } from '@/features/playlist/components/PlaylistArtistCell';
import { useThemeStore } from '@/store/themeStore';
import { usePlaylistLayoutStore } from '@/features/playlist/store/playlistLayoutStore';
@@ -1,7 +1,7 @@
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { Plus } from 'lucide-react';
import StarRating from '@/components/StarRating';
import StarRating from '@/ui/StarRating';
import CustomSelect from '@/ui/CustomSelect';
import {
LIMIT_MAX, YEAR_MAX, YEAR_MIN, clampYear, defaultSmartFilters,
+1 -1
View File
@@ -26,7 +26,7 @@ import PlaylistsSmartEditor from '@/features/playlist/components/PlaylistsSmartE
import PlaylistsHeader from '@/features/playlist/components/PlaylistsHeader';
import PlaylistCard from '@/features/playlist/components/PlaylistCard';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import { useOfflineBrowseContext } from '@/features/offline';
import { offlineActionPolicy } from '@/features/offline';
import { Info } from 'lucide-react';
+1 -1
View File
@@ -14,7 +14,7 @@ import RadioCard from '@/features/radio/components/RadioCard';
import RadioEditModal from '@/features/radio/components/RadioEditModal';
import RadioDirectoryModal from '@/features/radio/components/RadioDirectoryModal';
import { usePerfProbeFlags } from '@/utils/perf/perfFlags';
import { VirtualCardGrid } from '@/components/VirtualCardGrid';
import { VirtualCardGrid } from '@/ui/VirtualCardGrid';
import { useNavidromeAdminRole, canManageNavidromeRadio } from '@/hooks/useNavidromeAdminRole';
export default function InternetRadio() {
@@ -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 { SettingsSubCard, SettingsField } from '@/features/settings/components/SettingsSubCard';
import StarRating from '@/components/StarRating';
import StarRating from '@/ui/StarRating';
import AnalyticsStrategySection from '@/features/settings/components/AnalyticsStrategySection';
const AUDIOBOOK_GENRES_DISPLAY = ['Hörbuch', 'Hoerbuch', 'Hörspiel', 'Hoerspiel', 'Audiobook', 'Audio Book', 'Spoken Word', 'Spokenword', 'Podcast', 'Kapitel', 'Thriller', 'Krimi', 'Speech', 'Fantasy', 'Comedy', 'Literature'];
@@ -1,7 +1,7 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Gauge, Heart, PictureInPicture2, SlidersVertical, Star } from 'lucide-react';
import LastfmIcon from '@/components/LastfmIcon';
import LastfmIcon from '@/ui/LastfmIcon';
import {
usePlayerBarLayoutStore,
type PlayerBarLayoutItemId,
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Check, ChevronLeft, ChevronRight, Download, RefreshCw, Trash2, WifiOff } from 'lucide-react';
import { open as openUrl } from '@tauri-apps/plugin-shell';
import CoverLightbox from '@/components/CoverLightbox';
import CoverLightbox from '@/ui/CoverLightbox';
import { useThemeAnimationRisk } from '@/hooks/useThemeAnimationRisk';
import { AnimatedThemeBadge } from '@/features/settings/components/AnimatedThemeBadge';
import CustomSelect from '@/ui/CustomSelect';
+2 -2
View File
@@ -9,8 +9,8 @@ import { useSidebarStore } from '@/features/sidebar/store/sidebarStore';
import { useLocation } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { PanelLeft, PanelLeftClose, Trash2 } from 'lucide-react';
import PsysonicLogo from '@/components/PsysonicLogo';
import PSmallLogo from '@/components/PSmallLogo';
import PsysonicLogo from '@/ui/PsysonicLogo';
import PSmallLogo from '@/ui/PSmallLogo';
import { usePlaylistStore } from '@/features/playlist';
import OverlayScrollArea from '@/ui/OverlayScrollArea';
import {