mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 23:35:44 +00:00
refactor(utils): split componentHelpers grab-bag to owning layers
Per-file routing of the 9-file utils/componentHelpers grab-bag, each driven by
its verified consumer set:
- contextMenu{Actions,Helpers} → features/contextMenu/utils (sole consumer)
- queuePanelHelpers → features/queue/utils (sole consumer)
- nowPlayingHelpers → features/nowPlaying/utils; isRealArtistImage split out to
cover/ (consumed by cover/artistHero, a lower layer)
- appUpdaterHelpers(+test) + listReorder(+test) → lib/util (config/ + lib/hooks
consume them; pure)
- playlistDetailHelpers → lib/format (consumed by 3 features; pure)
- appShellHelpers → app/ (app-shell consumers only)
- userMgmtHelpers left in place: consumed by the a11y-HELD SongInfoModal, so a
move would rewrite a do-not-touch file (same block as licensesData).
Pure moves; tests pass unmodified.
This commit is contained in:
@@ -70,7 +70,7 @@ import { usePerfProbeFlags } from '@/lib/perf/perfFlags';
|
||||
import {
|
||||
persistSidebarCollapsed,
|
||||
readInitialSidebarCollapsed,
|
||||
} from '../utils/componentHelpers/appShellHelpers';
|
||||
} from '@/app/appShellHelpers';
|
||||
|
||||
/**
|
||||
* The main webview's persistent layout: titlebar (Linux + macOS) + sidebar +
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { PanelRight, PanelRightClose } from 'lucide-react';
|
||||
import { shouldSuppressQueueResizerMouseDown } from '@/utils/componentHelpers/appShellHelpers';
|
||||
import { shouldSuppressQueueResizerMouseDown } from '@/app/appShellHelpers';
|
||||
|
||||
interface Props {
|
||||
isQueueVisible: boolean;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '../../constants/appScroll';
|
||||
import { APP_MAIN_SCROLL_VIEWPORT_ID } from '@/constants/appScroll';
|
||||
|
||||
export const SIDEBAR_COLLAPSED_STORAGE_KEY = 'psysonic_sidebar_collapsed';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { CONTRIBUTORS } from './settingsCredits';
|
||||
import { isNewer } from '../utils/componentHelpers/appUpdaterHelpers';
|
||||
import { isNewer } from '@/lib/util/appUpdaterHelpers';
|
||||
|
||||
describe('CONTRIBUTORS ordering', () => {
|
||||
it('is sorted ascending by the `since` app version', () => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { isNewer } from '../utils/componentHelpers/appUpdaterHelpers';
|
||||
import { isNewer } from '@/lib/util/appUpdaterHelpers';
|
||||
|
||||
// Credits list rendered on the Settings → System tab. Update via PR when adding a contributor.
|
||||
const CONTRIBUTOR_ENTRIES = [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo, useState, type CSSProperties, type ImgHTMLAttributes } from 'react';
|
||||
import type { SubsonicArtistInfo } from '@/lib/api/subsonicTypes';
|
||||
import { isRealArtistImage } from '../utils/componentHelpers/nowPlayingHelpers';
|
||||
import { isRealArtistImage } from './isRealArtistImage';
|
||||
import { CoverArtImage } from './CoverArtImage';
|
||||
import type { CoverArtRef, CoverSurfaceKind } from './types';
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Filter out the well-known Last.fm "no image" placeholder that Subsonic
|
||||
* backends aggregate into `largeImageUrl`/`mediumImageUrl` when no real
|
||||
* artist image exists. The placeholder MD5 is fixed and documented.
|
||||
*/
|
||||
export function isRealArtistImage(url?: string): boolean {
|
||||
if (!url) return false;
|
||||
if (url.includes('2a96cbd8b46e442fc41c2b86b821562f')) return false;
|
||||
return true;
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { showToast } from '@/lib/dom/toast';
|
||||
import {
|
||||
confirmAddAllDuplicates,
|
||||
isSmartPlaylistName,
|
||||
} from '@/utils/componentHelpers/contextMenuHelpers';
|
||||
} from '@/features/contextMenu/utils/contextMenuHelpers';
|
||||
|
||||
interface Props {
|
||||
songIds: string[];
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
downloadAlbum as downloadAlbumAction,
|
||||
startInstantMix as startInstantMixAction,
|
||||
startRadio as startRadioAction,
|
||||
} from '@/utils/componentHelpers/contextMenuActions';
|
||||
} from '@/features/contextMenu/utils/contextMenuActions';
|
||||
import { useContextMenuKeyboardNav } from '@/features/contextMenu/hooks/useContextMenuKeyboardNav';
|
||||
import { useContextMenuRating } from '@/features/contextMenu/hooks/useContextMenuRating';
|
||||
import { usePlaybackLibraryNavigate } from '@/features/playback/hooks/usePlaybackLibraryNavigate';
|
||||
|
||||
@@ -8,7 +8,7 @@ import { showToast } from '@/lib/dom/toast';
|
||||
import {
|
||||
confirmAddAllDuplicates,
|
||||
isSmartPlaylistName,
|
||||
} from '@/utils/componentHelpers/contextMenuHelpers';
|
||||
} from '@/features/contextMenu/utils/contextMenuHelpers';
|
||||
|
||||
interface Props {
|
||||
albumIds: string[];
|
||||
|
||||
@@ -9,7 +9,7 @@ import { showToast } from '@/lib/dom/toast';
|
||||
import {
|
||||
confirmAddAllDuplicates,
|
||||
isSmartPlaylistName,
|
||||
} from '@/utils/componentHelpers/contextMenuHelpers';
|
||||
} from '@/features/contextMenu/utils/contextMenuHelpers';
|
||||
|
||||
interface Props {
|
||||
artistIds: string[];
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { ListMusic, Plus } from 'lucide-react';
|
||||
import { usePlaylistStore } from '@/features/playlist';
|
||||
import { showToast } from '@/lib/dom/toast';
|
||||
import { isSmartPlaylistName } from '@/utils/componentHelpers/contextMenuHelpers';
|
||||
import { isSmartPlaylistName } from '@/features/contextMenu/utils/contextMenuHelpers';
|
||||
|
||||
interface SingleProps {
|
||||
playlist: { id: string; name: string };
|
||||
|
||||
+5
-5
@@ -1,17 +1,17 @@
|
||||
import { join } from '@tauri-apps/api/path';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { getSimilarSongs2, fetchSimilarTracksRouted, getTopSongs } from '@/lib/api/subsonicArtists';
|
||||
import { filterSongsForLuckyMixRatings, getMixMinRatingsConfigFromAuth } from '../mix/mixRatingFilter';
|
||||
import { filterSongsForLuckyMixRatings, getMixMinRatingsConfigFromAuth } from '@/utils/mix/mixRatingFilter';
|
||||
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
|
||||
import { useAuthStore } from '../../store/authStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import type { Track } from '@/lib/media/trackTypes';
|
||||
import { resolveQueueTrack } from '@/features/playback/store/queueTrackView';
|
||||
import { useZipDownloadStore } from '@/features/offline';
|
||||
import { useDownloadModalStore } from '@/features/offline';
|
||||
import type { EntityShareKind } from '../share/shareLink';
|
||||
import { copyEntityShareLink } from '../share/copyEntityShareLink';
|
||||
import { sanitizeFilename, shuffleArray } from './contextMenuHelpers';
|
||||
import type { EntityShareKind } from '@/utils/share/shareLink';
|
||||
import { copyEntityShareLink } from '@/utils/share/copyEntityShareLink';
|
||||
import { sanitizeFilename, shuffleArray } from '@/features/contextMenu/utils/contextMenuHelpers';
|
||||
import { songToTrack } from '@/lib/media/songToTrack';
|
||||
import { showToast } from '@/lib/dom/toast';
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { useConfirmModalStore } from '../../store/confirmModalStore';
|
||||
import { useConfirmModalStore } from '@/store/confirmModalStore';
|
||||
|
||||
/** Psysonic smart playlists (Navidrome); not valid targets for manual add-to-playlist. */
|
||||
export const SMART_PLAYLIST_PREFIX = 'psy-smart-';
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { AudioLines, ChevronRight, Play, Square, X } from 'lucide-react';
|
||||
import type { ColDef } from '@/hooks/useTracklistColumns';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { codecLabel } from '@/utils/componentHelpers/playlistDetailHelpers';
|
||||
import { codecLabel } from '@/lib/format/playlistDetailHelpers';
|
||||
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
|
||||
import i18n from '@/lib/i18n';
|
||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { memo, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Disc3, ExternalLink, Star } from 'lucide-react';
|
||||
import type { SubsonicAlbum, SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { formatTotalDuration } from '@/utils/componentHelpers/nowPlayingHelpers';
|
||||
import { formatTotalDuration } from '@/features/nowPlaying/utils/nowPlayingHelpers';
|
||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||
|
||||
interface AlbumCardProps {
|
||||
|
||||
@@ -2,7 +2,8 @@ import React, { memo, useEffect, useLayoutEffect, useMemo, useRef, useState } fr
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ExternalLink } from 'lucide-react';
|
||||
import type { SubsonicArtistInfo } from '@/lib/api/subsonicTypes';
|
||||
import { isRealArtistImage, sanitizeHtml } from '@/utils/componentHelpers/nowPlayingHelpers';
|
||||
import { isRealArtistImage } from '@/cover/isRealArtistImage';
|
||||
import { sanitizeHtml } from '@/features/nowPlaying/utils/nowPlayingHelpers';
|
||||
import CachedImage from '@/ui/CachedImage';
|
||||
|
||||
export interface ArtistCardTab {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ContributorRow } from '@/utils/componentHelpers/nowPlayingHelpers';
|
||||
import type { ContributorRow } from '@/features/nowPlaying/utils/nowPlayingHelpers';
|
||||
|
||||
interface CreditsCardProps { rows: ContributorRow[]; }
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Calendar, Info } from 'lucide-react';
|
||||
import { open as shellOpen } from '@tauri-apps/plugin-shell';
|
||||
import type { BandsintownEvent } from '@/api/bandsintown';
|
||||
import { isoToParts } from '@/utils/componentHelpers/nowPlayingHelpers';
|
||||
import { isoToParts } from '@/features/nowPlaying/utils/nowPlayingHelpers';
|
||||
|
||||
interface TourCardProps {
|
||||
artistName: string;
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
|
||||
import {
|
||||
buildContributorRows,
|
||||
} from '@/utils/componentHelpers/nowPlayingHelpers';
|
||||
} from '@/features/nowPlaying/utils/nowPlayingHelpers';
|
||||
import NpCardWrap from '@/features/nowPlaying/components/NpCardWrap';
|
||||
import NpColumnEl from '@/features/nowPlaying/components/NpColumnEl';
|
||||
import RadioView from '@/features/nowPlaying/components/RadioView';
|
||||
|
||||
-11
@@ -64,14 +64,3 @@ export function buildContributorRows(song: SubsonicSong | null | undefined, main
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out the well-known Last.fm "no image" placeholder that Subsonic
|
||||
* backends aggregate into `largeImageUrl`/`mediumImageUrl` when no real
|
||||
* artist image exists. The placeholder MD5 is fixed and documented.
|
||||
*/
|
||||
export function isRealArtistImage(url?: string): boolean {
|
||||
if (!url) return false;
|
||||
if (url.includes('2a96cbd8b46e442fc41c2b86b821562f')) return false;
|
||||
return true;
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from '@/features/offline/utils/offlineLibraryHelpers';
|
||||
import { librarySqlServerId } from '@/api/coverCache';
|
||||
import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
|
||||
import { isSmartPlaylistName } from '@/utils/componentHelpers/playlistDetailHelpers';
|
||||
import { isSmartPlaylistName } from '@/lib/format/playlistDetailHelpers';
|
||||
import {
|
||||
enqueueOfflinePin,
|
||||
registerOfflinePinExecutor,
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
syncAllPinnedPlaylists,
|
||||
syncPinnedArtistIfNeeded,
|
||||
} from '@/features/offline/utils/pinnedOfflineSync';
|
||||
import { SMART_PREFIX } from '@/utils/componentHelpers/playlistDetailHelpers';
|
||||
import { SMART_PREFIX } from '@/lib/format/playlistDetailHelpers';
|
||||
|
||||
const getPlaylistMock = vi.fn();
|
||||
const getAlbumForServerMock = vi.fn();
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { PinSource } from '@/store/localPlaybackStore';
|
||||
import { useLocalPlaybackStore } from '@/store/localPlaybackStore';
|
||||
import { useOfflineStore } from '@/features/offline/store/offlineStore';
|
||||
import { usePlaylistStore } from '@/features/playlist';
|
||||
import { isSmartPlaylistName } from '@/utils/componentHelpers/playlistDetailHelpers';
|
||||
import { isSmartPlaylistName } from '@/lib/format/playlistDetailHelpers';
|
||||
import { getMediaDir } from '@/lib/media/mediaDir';
|
||||
import {
|
||||
isActiveServerReachable,
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useThemeStore } from '@/store/themeStore';
|
||||
import { usePlaylistLayoutStore, type PlaylistLayoutItemId } from '@/features/playlist/store/playlistLayoutStore';
|
||||
import {
|
||||
displayPlaylistName, formatSize, isSmartPlaylistName, totalDurationLabel,
|
||||
} from '@/utils/componentHelpers/playlistDetailHelpers';
|
||||
} from '@/lib/format/playlistDetailHelpers';
|
||||
import type { CoverArtId } from '@/cover/types';
|
||||
import { AlbumCoverArtImage } from '@/cover/AlbumCoverArtImage';
|
||||
import { PLAYLIST_MAIN_COVER_CSS_PX } from '@/features/playlist/hooks/usePlaylistCovers';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { AudioLines, ChevronRight, Heart, Play, Square, Trash2 } from 'lucide-react';
|
||||
import type { ColDef } from '@/hooks/useTracklistColumns';
|
||||
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
|
||||
import { codecLabel } from '@/utils/componentHelpers/playlistDetailHelpers';
|
||||
import { codecLabel } from '@/lib/format/playlistDetailHelpers';
|
||||
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
|
||||
import i18n from '@/lib/i18n';
|
||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useThemeStore } from '@/store/themeStore';
|
||||
import { usePlaylistLayoutStore } from '@/features/playlist/store/playlistLayoutStore';
|
||||
import { songToTrack } from '@/lib/media/songToTrack';
|
||||
import { getQueueTracksView } from '@/features/playback/store/queueTrackView';
|
||||
import { codecLabel } from '@/utils/componentHelpers/playlistDetailHelpers';
|
||||
import { codecLabel } from '@/lib/format/playlistDetailHelpers';
|
||||
import { formatLastSeen } from '@/utils/componentHelpers/userMgmtHelpers';
|
||||
import { formatTrackTime } from '@/lib/format/formatDuration';
|
||||
import i18n from '@/lib/i18n';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { join } from '@tauri-apps/api/path';
|
||||
import { buildDownloadUrl } from '@/lib/api/subsonicStreamUrl';
|
||||
import type { SubsonicPlaylist } from '@/lib/api/subsonicTypes';
|
||||
import { useZipDownloadStore } from '@/features/offline';
|
||||
import { sanitizeFilename } from '@/utils/componentHelpers/playlistDetailHelpers';
|
||||
import { sanitizeFilename } from '@/lib/format/playlistDetailHelpers';
|
||||
|
||||
export interface RunPlaylistZipDownloadDeps {
|
||||
playlist: SubsonicPlaylist;
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { PlaybackSourceKind } from '@/features/playback/utils/playback/reso
|
||||
import {
|
||||
formatQueueReplayGainParts,
|
||||
renderStars,
|
||||
} from '@/utils/componentHelpers/queuePanelHelpers';
|
||||
} from '@/features/queue/utils/queuePanelHelpers';
|
||||
import { loudnessGainPlaceholderUntilCacheDb } from '@/features/playback/utils/audio/loudnessPlaceholder';
|
||||
import { effectiveLoudnessPreAnalysisAttenuationDb } from '@/lib/audio/loudnessPreAnalysisSlider';
|
||||
import { formatQueueBpmTech, formatQueueMoodLabels } from '@/lib/library/trackEnrichment';
|
||||
|
||||
@@ -6,7 +6,7 @@ import { usePlayerStore } from '@/features/playback/store/playerStore';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import type { QueueItemRef } from '@/lib/media/trackTypes';
|
||||
import type { QueueDisplayMode } from '@/store/authStoreTypes';
|
||||
import type { DurationMode } from '@/utils/componentHelpers/queuePanelHelpers';
|
||||
import type { DurationMode } from '@/features/queue/utils/queuePanelHelpers';
|
||||
import { formatLongDuration } from '@/lib/format/formatDuration';
|
||||
import { formatClockTime } from '@/lib/format/formatClockTime';
|
||||
import { resolveQueueTrack } from '@/features/playback/store/queueTrackView';
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { Star } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { Track } from '@/lib/media/trackTypes';
|
||||
|
||||
export type { DurationMode } from '../../store/authStoreTypes';
|
||||
export type { DurationMode } from '@/store/authStoreTypes';
|
||||
|
||||
export function formatQueueReplayGainParts(track: Track, t: TFunction): string[] {
|
||||
const parts: string[] = [];
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useArtistLayoutStore, type ArtistSectionConfig, type ArtistSectionId } from '@/features/artist';
|
||||
import { useListReorderDnd } from '@/lib/hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/lib/util/listReorder';
|
||||
import { ReorderGripHandle } from '@/features/settings/components/ReorderGripHandle';
|
||||
|
||||
const ARTIST_SECTION_LABEL_KEYS: Record<ArtistSectionId, string> = {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useShallow } from 'zustand/react/shallow';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import type { LyricsSourceId } from '@/store/authStoreTypes';
|
||||
import { useListReorderDnd } from '@/lib/hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/lib/util/listReorder';
|
||||
import { ReorderGripHandle } from '@/features/settings/components/ReorderGripHandle';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Blend, Infinity as InfinityIcon, ListMusic, MoveRight, Share2, Shuffle, Trash2, Waves } from 'lucide-react';
|
||||
import { useQueueToolbarStore, QueueToolbarButtonId } from '@/store/queueToolbarStore';
|
||||
import { useListReorderDnd } from '@/lib/hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/lib/util/listReorder';
|
||||
import { ReorderGripHandle } from '@/features/settings/components/ReorderGripHandle';
|
||||
|
||||
const QUEUE_TOOLBAR_BUTTON_ICONS: Record<QueueToolbarButtonId, typeof Shuffle | null> = {
|
||||
|
||||
@@ -38,7 +38,7 @@ import { switchActiveServer } from '@/utils/server/switchActiveServer';
|
||||
import { AddServerForm } from '@/features/settings/components/AddServerForm';
|
||||
import { ServerCapabilityHeaderBadge } from '@/features/settings/components/ServerCapabilityHeaderBadge';
|
||||
import { useListReorderDnd } from '@/lib/hooks/useListReorderDnd';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/lib/util/listReorder';
|
||||
import { ReorderGripHandle } from '@/features/settings/components/ReorderGripHandle';
|
||||
import { tooltipAttrs } from '@/ui/tooltipAttrs';
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useLuckyMixAvailable } from '@/features/randomMix';
|
||||
import { ALL_NAV_ITEMS } from '@/config/navItems';
|
||||
import { applySidebarReorderById } from '@/features/sidebar';
|
||||
import { useListReorderDnd } from '@/lib/hooks/useListReorderDnd';
|
||||
import type { ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import type { ListReorderDropTarget } from '@/lib/util/listReorder';
|
||||
import { ReorderGripHandle } from '@/features/settings/components/ReorderGripHandle';
|
||||
import { SettingsGroup } from '@/features/settings/components/SettingsGroup';
|
||||
import { SettingsToggle } from '@/features/settings/components/SettingsToggle';
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from '@/lib/themes/themeRegistry';
|
||||
import { installThemeFromRegistry } from '@/lib/themes/installThemeFromRegistry';
|
||||
import { uninstallTheme } from '@/lib/themes/uninstallTheme';
|
||||
import { isNewer } from '@/utils/componentHelpers/appUpdaterHelpers';
|
||||
import { isNewer } from '@/lib/util/appUpdaterHelpers';
|
||||
|
||||
type ModeFilter = 'all' | 'dark' | 'light';
|
||||
type SortMode = 'newest' | 'name';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { isNewer } from '@/utils/componentHelpers/appUpdaterHelpers';
|
||||
import { isNewer } from '@/lib/util/appUpdaterHelpers';
|
||||
import { fetchRegistry, getCachedRegistry, type Registry, type RegistryTheme } from '@/lib/themes/themeRegistry';
|
||||
import { useInstalledThemesStore } from '@/store/installedThemesStore';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ALL_NAV_ITEMS } from '@/config/navItems';
|
||||
import { CONSERVED_SIDEBAR_NAV_IDS, type SidebarItemConfig } from '@/features/sidebar/store/sidebarStore';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import { applyListReorderById, type ListReorderDropTarget } from '@/lib/util/listReorder';
|
||||
|
||||
export type SidebarNavSection = 'library' | 'system';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { invoke } from '@tauri-apps/api/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { version as currentVersion } from '../../../../package.json';
|
||||
import { IS_LINUX, IS_MACOS, IS_WINDOWS } from '@/lib/util/platform';
|
||||
import { SKIP_KEY, isNewer, isWithinModerationWindow, pickAsset, type ReleaseData, type DlState } from '@/utils/componentHelpers/appUpdaterHelpers';
|
||||
import { SKIP_KEY, isNewer, isWithinModerationWindow, pickAsset, type ReleaseData, type DlState } from '@/lib/util/appUpdaterHelpers';
|
||||
|
||||
/** All update-modal state, the GitHub release probe and the download/relaunch
|
||||
* handlers. The component owns only the early-return guard and the JSX. */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useDragDrop } from '@/lib/dnd/DragDropContext';
|
||||
import type { ListReorderDropTarget } from '@/utils/componentHelpers/listReorder';
|
||||
import type { ListReorderDropTarget } from '@/lib/util/listReorder';
|
||||
|
||||
interface Options {
|
||||
/** Payload discriminator the drag source emits, e.g. `'lyrics_source_reorder'`. */
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { isWithinModerationWindow, WINGET_MODERATION_DELAY_MS } from './appUpdaterHelpers';
|
||||
import { isWithinModerationWindow, WINGET_MODERATION_DELAY_MS } from '@/lib/util/appUpdaterHelpers';
|
||||
|
||||
describe('isWithinModerationWindow', () => {
|
||||
const published = '2026-06-27T00:00:00Z';
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { applyListReorderById } from './listReorder';
|
||||
import { applyListReorderById } from '@/lib/util/listReorder';
|
||||
|
||||
type Item = { id: string; visible?: boolean };
|
||||
|
||||
Reference in New Issue
Block a user