mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
refactor(audio-tab): I.3 — split AudioTab.tsx 521 → 97 LOC across 6 files (#674)
* refactor(audio-tab): extract useAudioDevicesProbe hook Pull the device-list state, refreshAudioDevices callback, mount probe, and the audio:device-changed / audio:device-reset listener wiring into hooks/useAudioDevicesProbe.ts. macOS short-circuit lives in the hook. AudioTab.tsx: 521 → 463 LOC. * refactor(audio-tab): extract AudioOutputDeviceSection Pull the audio output device picker (macOS notice + CustomSelect + refresh button) into components/settings/audio/AudioOutputDeviceSection.tsx. AudioTab.tsx: 463 → 418 LOC. * refactor(audio-tab): extract NormalizationBlock Pull the engine picker (Off / ReplayGain / LUFS) and the engine-specific config blocks (RG mode + pre-gain + fallback; LUFS target + pre-analysis attenuation with reset) into components/settings/audio/NormalizationBlock.tsx. AudioTab.tsx: 418 → 267 LOC. * refactor(audio-tab): extract PlaybackBehaviorBlock Pull Crossfade ↔ Gapless mutually-exclusive toggles + Preserve Play Next Order into components/settings/audio/PlaybackBehaviorBlock.tsx. The crossfade-seconds slider only renders while crossfade is the active mode. AudioTab.tsx: 267 → 201 LOC. * refactor(audio-tab): extract TrackPreviewsSection Pull the track previews subsection (master toggle, per-location grid, start-ratio slider, duration slider) into components/settings/audio/TrackPreviewsSection.tsx. AudioTab.tsx: 201 → 97 LOC.
This commit is contained in:
committed by
GitHub
parent
f14c8f21e6
commit
59772db5ee
@@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { AudioLines, RotateCcw } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import CustomSelect from '../../CustomSelect';
|
||||
import SettingsSubSection from '../../SettingsSubSection';
|
||||
import { useAuthStore } from '../../../store/authStore';
|
||||
import { IS_MACOS } from '../../../utils/platform';
|
||||
import { buildAudioDeviceSelectOptions } from '../../../utils/audioDeviceLabels';
|
||||
|
||||
interface Props {
|
||||
audioDevices: string[];
|
||||
osDefaultAudioDeviceId: string | null;
|
||||
deviceSwitching: boolean;
|
||||
devicesLoading: boolean;
|
||||
setDeviceSwitching: (v: boolean) => void;
|
||||
refreshAudioDevices: (opts?: { silent?: boolean }) => void;
|
||||
t: TFunction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Audio output device picker. macOS is hard-pinned to the system default,
|
||||
* so the picker collapses to a notice on that platform.
|
||||
*
|
||||
* The device switch is best-effort: if `audio_set_device` rejects (e.g.
|
||||
* device disappeared) we leave the previous selection in the store.
|
||||
*/
|
||||
export function AudioOutputDeviceSection({
|
||||
audioDevices,
|
||||
osDefaultAudioDeviceId,
|
||||
deviceSwitching,
|
||||
devicesLoading,
|
||||
setDeviceSwitching,
|
||||
refreshAudioDevices,
|
||||
t,
|
||||
}: Props) {
|
||||
const audioOutputDevice = useAuthStore(s => s.audioOutputDevice);
|
||||
const setAudioOutputDevice = useAuthStore(s => s.setAudioOutputDevice);
|
||||
|
||||
return (
|
||||
<SettingsSubSection
|
||||
title={t('settings.audioOutputDevice')}
|
||||
icon={<AudioLines size={16} />}
|
||||
>
|
||||
<div className="settings-card">
|
||||
{IS_MACOS ? (
|
||||
<div style={{ fontSize: 12, color: 'var(--text-muted)', lineHeight: 1.55 }}>
|
||||
{t('settings.audioOutputDeviceMacNotice')}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: '0.75rem' }}>
|
||||
{t('settings.audioOutputDeviceDesc')}
|
||||
</div>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
|
||||
<CustomSelect
|
||||
style={{ flex: 1 }}
|
||||
value={audioOutputDevice ?? ''}
|
||||
disabled={deviceSwitching || devicesLoading}
|
||||
onChange={async (val) => {
|
||||
const device = val || null;
|
||||
setDeviceSwitching(true);
|
||||
try {
|
||||
await invoke('audio_set_device', { deviceName: device });
|
||||
setAudioOutputDevice(device);
|
||||
} catch { /* device open failed — don't persist */ }
|
||||
setDeviceSwitching(false);
|
||||
}}
|
||||
options={buildAudioDeviceSelectOptions(
|
||||
audioDevices,
|
||||
t('settings.audioOutputDeviceDefault'),
|
||||
osDefaultAudioDeviceId,
|
||||
t('settings.audioOutputDeviceOsDefaultNow'),
|
||||
audioOutputDevice,
|
||||
t('settings.audioOutputDeviceNotInCurrentList'),
|
||||
)}
|
||||
/>
|
||||
<button
|
||||
className="icon-btn"
|
||||
onClick={() => refreshAudioDevices()}
|
||||
disabled={devicesLoading || deviceSwitching}
|
||||
data-tooltip={t('settings.audioOutputDeviceRefresh')}
|
||||
>
|
||||
<RotateCcw size={15} className={devicesLoading ? 'spin' : ''} />
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</SettingsSubSection>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user