From 320eb97c03356939f830e74fa52244c5d6364b9e Mon Sep 17 00:00:00 2001 From: Frank Stellmacher <171614930+Psychotoxical@users.noreply.github.com> Date: Wed, 13 May 2026 01:57:26 +0200 Subject: [PATCH] =?UTF-8?q?refactor(settings):=20G.56=20=E2=80=94=20extrac?= =?UTF-8?q?t=20Lyrics=20+=20Personalisation=20+=20Input=20tabs=20(#621)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three tab-section components carved out of the Settings() default- export body. Each owns its store hooks + local state; Settings() now only routes via `{activeTab === 'X' && }`. - `LyricsTab.tsx` (~50 LOC, was ~40 inline) — wraps `LyricsSourcesCustomizer` + sidebar lyrics style toggles. Owns the two `useAuthStore(s => s.sidebarLyricsStyle/...)` selectors. - `PersonalisationTab.tsx` (~95 LOC, was ~80 inline) — wraps the four customizers (sidebar / artist layout / home / queue toolbar) with their per-store reset buttons. - `InputTab.tsx` (~185 LOC, was ~170 inline) — keybindings + global shortcuts. Owns the two `listeningFor` / `listeningForGlobal` state slots that were previously hoisted into Settings(). 13 now-unused imports trimmed (4 customizer-store hooks, 6 keybinding helpers, `Music2/AudioLines` kept since the tab-button array still uses them as icons, `LayoutGrid/Keyboard` kept for the same reason). Pure code-move. Settings.tsx: 3037 → 2741 LOC (−296). Phase-G journey: 5298 → 2741 LOC (~48% reduction). --- src/components/settings/InputTab.tsx | 184 +++++++++++ src/components/settings/LyricsTab.tsx | 52 +++ .../settings/PersonalisationTab.tsx | 94 ++++++ src/pages/Settings.tsx | 312 +----------------- 4 files changed, 338 insertions(+), 304 deletions(-) create mode 100644 src/components/settings/InputTab.tsx create mode 100644 src/components/settings/LyricsTab.tsx create mode 100644 src/components/settings/PersonalisationTab.tsx diff --git a/src/components/settings/InputTab.tsx b/src/components/settings/InputTab.tsx new file mode 100644 index 00000000..21e54cbb --- /dev/null +++ b/src/components/settings/InputTab.tsx @@ -0,0 +1,184 @@ +import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Keyboard, RotateCcw, X } from 'lucide-react'; +import { IN_APP_SHORTCUT_ACTIONS, GLOBAL_SHORTCUT_ACTIONS } from '../../config/shortcutActions'; +import { useGlobalShortcutsStore, type GlobalAction, buildGlobalShortcut, formatGlobalShortcut } from '../../store/globalShortcutsStore'; +import { useKeybindingsStore, type KeyAction, buildInAppBinding, formatBinding } from '../../store/keybindingsStore'; +import SettingsSubSection from '../SettingsSubSection'; + +export function InputTab() { + const { t } = useTranslation(); + const kb = useKeybindingsStore(); + const gs = useGlobalShortcutsStore(); + const [listeningFor, setListeningFor] = useState(null); + const [listeningForGlobal, setListeningForGlobal] = useState(null); + + return ( + <> + } + action={ + + } + > +
+
+ {IN_APP_SHORTCUT_ACTIONS.map(({ id: action, getLabel }) => { + const label = getLabel(t); + const bound = kb.bindings[action]; + const isListening = listeningFor === action; + return ( +
+ {label} +
+ + {bound && !isListening && ( + + )} +
+
+ ); + })} +
+
+
+ + } + description={t('settings.globalShortcutsNote')} + action={ + + } + > +
+
+ {GLOBAL_SHORTCUT_ACTIONS.map(({ id: action, getLabel }) => { + const label = getLabel(t); + const bound = gs.shortcuts[action] ?? null; + const isListening = listeningForGlobal === action; + return ( +
+ {label} +
+ + {bound && !isListening && ( + + )} +
+
+ ); + })} +
+
+
+ + ); +} diff --git a/src/components/settings/LyricsTab.tsx b/src/components/settings/LyricsTab.tsx new file mode 100644 index 00000000..9fd8ce73 --- /dev/null +++ b/src/components/settings/LyricsTab.tsx @@ -0,0 +1,52 @@ +import { useTranslation } from 'react-i18next'; +import { AudioLines, Music2 } from 'lucide-react'; +import { useAuthStore } from '../../store/authStore'; +import SettingsSubSection from '../SettingsSubSection'; +import { LyricsSourcesCustomizer } from './LyricsSourcesCustomizer'; + +export function LyricsTab() { + const { t } = useTranslation(); + const sidebarLyricsStyle = useAuthStore(s => s.sidebarLyricsStyle); + const setSidebarLyricsStyle = useAuthStore(s => s.setSidebarLyricsStyle); + + return ( + <> + } + > + + + + } + > +
+ {(['classic', 'apple'] as const).map(style => { + const key = style === 'classic' ? 'Classic' : 'Apple'; + const other = style === 'classic' ? 'apple' : 'classic'; + return ( +
+
+
+
{t(`settings.sidebarLyricsStyle${key}` as any)}
+
{t(`settings.sidebarLyricsStyle${key}Desc` as any)}
+
+ +
+
+ ); + })} +
+
+ + ); +} diff --git a/src/components/settings/PersonalisationTab.tsx b/src/components/settings/PersonalisationTab.tsx new file mode 100644 index 00000000..ee8dde25 --- /dev/null +++ b/src/components/settings/PersonalisationTab.tsx @@ -0,0 +1,94 @@ +import { useTranslation } from 'react-i18next'; +import { LayoutGrid, ListMusic, PanelLeft, RotateCcw, Users } from 'lucide-react'; +import { useArtistLayoutStore } from '../../store/artistLayoutStore'; +import { useHomeStore } from '../../store/homeStore'; +import { useQueueToolbarStore } from '../../store/queueToolbarStore'; +import { useSidebarStore } from '../../store/sidebarStore'; +import SettingsSubSection from '../SettingsSubSection'; +import { ArtistLayoutCustomizer } from './ArtistLayoutCustomizer'; +import { HomeCustomizer } from './HomeCustomizer'; +import { QueueToolbarCustomizer } from './QueueToolbarCustomizer'; +import { SidebarCustomizer } from './SidebarCustomizer'; + +export function PersonalisationTab() { + const { t } = useTranslation(); + return ( + <> + } + action={ + + } + > + + + + } + action={ + + } + > + + + + } + action={ + + } + > + + + + } + action={ + + } + > + + + + ); +} diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 163359e4..f5896696 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -6,8 +6,8 @@ import { useNavigate, useLocation } from 'react-router-dom'; import { Wifi, WifiOff, Globe, Music2, Sliders, LogOut, CheckCircle2, FolderOpen, Palette, Server, Plus, Trash2, Eye, EyeOff, Info, ExternalLink, Shuffle, X, Play, Type, Keyboard, ChevronDown, - PanelLeft, RotateCcw, LayoutGrid, AppWindow, HardDrive, Download, Waves, Star, Clock, ZoomIn, Sparkles, AlertTriangle, Maximize2, AudioLines, User, Lock, - Users, Search, Scale, ListMusic + RotateCcw, LayoutGrid, AppWindow, HardDrive, Download, Waves, Star, Clock, ZoomIn, Sparkles, AlertTriangle, Maximize2, AudioLines, User, Lock, + Users, Search, Scale } from 'lucide-react'; import i18n from '../i18n'; import { showToast } from '../utils/toast'; @@ -30,27 +30,18 @@ import { SeekbarPreview } from '../components/WaveformSeek'; import { IS_LINUX, IS_MACOS, IS_WINDOWS } from '../utils/platform'; import { useThemeStore } from '../store/themeStore'; import { useFontStore, FontId } from '../store/fontStore'; -import { useKeybindingsStore, KeyAction, formatBinding, buildInAppBinding } from '../store/keybindingsStore'; -import { useGlobalShortcutsStore, GlobalAction, buildGlobalShortcut, formatGlobalShortcut } from '../store/globalShortcutsStore'; -import { IN_APP_SHORTCUT_ACTIONS, GLOBAL_SHORTCUT_ACTIONS } from '../config/shortcutActions'; -import { useSidebarStore } from '../store/sidebarStore'; -import { useQueueToolbarStore } from '../store/queueToolbarStore'; import { effectiveLoudnessPreAnalysisAttenuationDb, } from '../utils/loudnessPreAnalysisSlider'; -import { useArtistLayoutStore } from '../store/artistLayoutStore'; -import { useHomeStore } from '../store/homeStore'; import { useDragDrop } from '../contexts/DragDropContext'; import { AddServerForm } from '../components/settings/AddServerForm'; -import { ArtistLayoutCustomizer } from '../components/settings/ArtistLayoutCustomizer'; import { BackupSection } from '../components/settings/BackupSection'; -import { HomeCustomizer } from '../components/settings/HomeCustomizer'; +import { InputTab } from '../components/settings/InputTab'; import { LoudnessLufsButtonGroup } from '../components/settings/LoudnessLufsButtonGroup'; -import { LyricsSourcesCustomizer } from '../components/settings/LyricsSourcesCustomizer'; -import { QueueToolbarCustomizer } from '../components/settings/QueueToolbarCustomizer'; +import { LyricsTab } from '../components/settings/LyricsTab'; +import { PersonalisationTab } from '../components/settings/PersonalisationTab'; import { ServerGripHandle } from '../components/settings/ServerGripHandle'; import { SETTINGS_INDEX, type Tab, matchScore, resolveTab } from '../components/settings/settingsTabs'; -import { SidebarCustomizer } from '../components/settings/SidebarCustomizer'; import { UserManagementSection } from '../components/settings/UserManagementSection'; import { CONTRIBUTORS, MAINTAINERS } from '../config/settingsCredits'; import { buildAudioDeviceSelectOptions, formatAudioDeviceLabel, sortAudioDeviceIds } from '../utils/audioDeviceLabels'; @@ -75,8 +66,6 @@ export default function Settings() { const auth = useAuthStore(); const theme = useThemeStore(); const fontStore = useFontStore(); - const kb = useKeybindingsStore(); - const gs = useGlobalShortcutsStore(); const serverId = auth.activeServerId ?? ''; const clearAllOffline = useOfflineStore(s => s.clearAll); const clearHotCacheDisk = useHotCacheStore(s => s.clearAllDisk); @@ -101,8 +90,6 @@ export default function Settings() { ), [auth.loudnessPreAnalysisAttenuationDb, auth.loudnessTargetLufs], ); - const [listeningFor, setListeningFor] = useState(null); - const [listeningForGlobal, setListeningForGlobal] = useState(null); const navigate = useNavigate(); const location = useLocation(); const routeState = location.state; @@ -1156,46 +1143,7 @@ export default function Settings() { )} {/* ── Lyrics ───────────────────────────────────────────────────────────── */} - {activeTab === 'lyrics' && ( - <> - } - > - - - - } - > -
- {(['classic', 'apple'] as const).map(style => { - const key = style === 'classic' ? 'Classic' : 'Apple'; - const other = style === 'classic' ? 'apple' : 'classic'; - return ( -
-
-
-
{t(`settings.sidebarLyricsStyle${key}` as any)}
-
{t(`settings.sidebarLyricsStyle${key}Desc` as any)}
-
- -
-
- ); - })} -
-
- - )} + {activeTab === 'lyrics' && } {/* ── Integrations ─────────────────────────────────────────────────────── */} {activeTab === 'integrations' && ( @@ -1414,85 +1362,7 @@ export default function Settings() { )} {/* ── Personalisation ──────────────────────────────────────────────────── */} - {activeTab === 'personalisation' && ( - <> - } - action={ - - } - > - - - - } - action={ - - } - > - - - - } - action={ - - } - > - - - - } - action={ - - } - > - - - - )} + {activeTab === 'personalisation' && } {/* ── Library (legacy 'general' + 'server') ────────────────────────────── */} {activeTab === 'library' && ( @@ -2393,174 +2263,8 @@ export default function Settings() { )} {/* ── Input ────────────────────────────────────────────────────────────── */} - {activeTab === 'input' && ( - <> - } - action={ - - } - > -
-
- {IN_APP_SHORTCUT_ACTIONS.map(({ id: action, getLabel }) => { - const label = getLabel(t); - const bound = kb.bindings[action]; - const isListening = listeningFor === action; - return ( -
- {label} -
- - {bound && !isListening && ( - - )} -
-
- ); - })} -
-
-
+ {activeTab === 'input' && } - } - description={t('settings.globalShortcutsNote')} - action={ - - } - > -
-
- {GLOBAL_SHORTCUT_ACTIONS.map(({ id: action, getLabel }) => { - const label = getLabel(t); - const bound = gs.shortcuts[action] ?? null; - const isListening = listeningForGlobal === action; - return ( -
- {label} -
- - {bound && !isListening && ( - - )} -
-
- ); - })} -
-
-
- - )} {/* ── Server ───────────────────────────────────────────────────────────── */} {activeTab === 'servers' && (