Browse Source

split config panels out

master
jeffvli 3 years ago
parent
commit
ff33fc8fd4
  1. 464
      src/components/settings/Config.tsx
  2. 146
      src/components/settings/ConfigPanels/CacheConfig.tsx
  3. 37
      src/components/settings/ConfigPanels/DebugConfig.tsx
  4. 2
      src/components/settings/ConfigPanels/ListViewConfig.tsx
  5. 139
      src/components/settings/ConfigPanels/LookAndFeelConfig.tsx
  6. 132
      src/components/settings/ConfigPanels/PlaybackConfig.tsx
  7. 42
      src/components/settings/ConfigPanels/PlayerConfig.tsx

464
src/components/settings/Config.tsx

@ -1,106 +1,21 @@
import React, { useEffect, useState } from 'react';
import fs from 'fs';
import path from 'path';
import settings from 'electron-settings';
import {
Button,
ControlLabel,
Tag,
Nav,
Icon,
InputGroup,
Message,
Whisper,
Popover,
RadioGroup,
} from 'rsuite';
import { ConfigPanel } from './styled';
import { Button, Whisper, Popover } from 'rsuite';
import { startScan, getScanStatus } from '../../api/api';
import GenericPage from '../layout/GenericPage';
import DisconnectButton from './DisconnectButton';
import GenericPageHeader from '../layout/GenericPageHeader';
import ListViewConfig from './ListViewConfig';
import {
songColumnList,
songColumnPicker,
albumColumnList,
albumColumnPicker,
playlistColumnList,
playlistColumnPicker,
} from './ListViewColumns';
import { getImageCachePath, getSongCachePath } from '../../shared/utils';
import setDefaultSettings from '../shared/setDefaultSettings';
import {
HeaderButton,
StyledCheckbox,
StyledInput,
StyledInputNumber,
StyledInputPicker,
StyledNavItem,
StyledRadio,
} from '../shared/styled';
import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import {
setPlaybackSetting,
setPlayerVolume,
} from '../../redux/playQueueSlice';
import { Fonts } from './Fonts';
import { setFont, setTheme } from '../../redux/miscSlice';
const fsUtils = require('nodejs-fs-utils');
import { HeaderButton } from '../shared/styled';
import PlaybackConfig from './ConfigPanels/PlaybackConfig';
import LookAndFeelConfig from './ConfigPanels/LookAndFeelConfig';
import PlayerConfig from './ConfigPanels/PlayerConfig';
import CacheConfig from './ConfigPanels/CacheConfig';
import DebugConfig from './ConfigPanels/DebugConfig';
const Config = () => {
const dispatch = useAppDispatch();
const playQueue = useAppSelector((state) => state.playQueue);
const [isScanning, setIsScanning] = useState(false);
const [scanProgress, setScanProgress] = useState(0);
const [imgCacheSize, setImgCacheSize] = useState(0);
const [songCacheSize, setSongCacheSize] = useState(0);
const [currentLAFTab, setCurrentLAFTab] = useState('songList');
const [isEditingCachePath, setIsEditingCachePath] = useState(false);
const [newCachePath, setNewCachePath] = useState('');
const [errorMessage, setErrorMessage] = useState('');
const [requiresReload] = useState(false);
const [cacheSongs, setCacheSongs] = useState(
Boolean(settings.getSync('cacheSongs'))
);
const [cacheImages, setCacheImages] = useState(
Boolean(settings.getSync('cacheImages'))
);
const [showDebugWindow, setShowDebugWindow] = useState(
Boolean(settings.getSync('showDebugWindow'))
);
const songCols: any = settings.getSync('songListColumns');
const albumCols: any = settings.getSync('albumListColumns');
const playlistCols: any = settings.getSync('playlistListColumns');
const miniCols: any = settings.getSync('miniListColumns');
const currentSongColumns = songCols?.map((column: any) => column.label) || [];
const currentAlbumColumns =
albumCols?.map((column: any) => column.label) || [];
const currentPlaylistColumns =
playlistCols?.map((column: any) => column.label) || [];
const currentMiniColumns = miniCols?.map((column: any) => column.label) || [];
useEffect(() => {
// Retrieve cache sizes on render
try {
setImgCacheSize(
Number(
(fsUtils.fsizeSync(getImageCachePath()) / 1000 / 1000).toFixed(0)
)
);
setSongCacheSize(
Number((fsUtils.fsizeSync(getSongCachePath()) / 1000 / 1000).toFixed(0))
);
} catch (err) {
setImgCacheSize(0);
setSongCacheSize(0);
fs.mkdirSync(getSongCachePath(), { recursive: true });
fs.mkdirSync(getImageCachePath(), { recursive: true });
}
}, []);
useEffect(() => {
// Check scan status on render
@ -194,366 +109,11 @@ const Config = () => {
/>
}
>
<ConfigPanel header="Playback" bordered>
<p>
Fading works by polling the audio player on an interval to determine
when to start fading to the next track. Due to this, you may notice
the fade timing may not be 100% perfect. Lowering the player polling
interval can increase the accuracy of the fade, but may also decrease
application performance as calculations are running for the fade.
</p>
<p>
If volume fade is disabled, then the fading-in track will start at the
specified crossfade duration at full volume.
</p>
<p>
Setting the crossfade duration to <code>0</code> will enable{' '}
<strong>gapless playback</strong>. All other playback settings except
the polling interval will be ignored. It is recommended that you use a
polling interval between <code>1</code> and <code>20</code> for
increased transition accuracy.
</p>
<p style={{ fontSize: 'smaller' }}>
*Enable the debug window if you want to view the differences between
each fade type
</p>
<div style={{ width: '300px', paddingTop: '20px' }}>
<ControlLabel>Crossfade duration (s)</ControlLabel>
<StyledInputNumber
defaultValue={String(settings.getSync('fadeDuration')) || '0'}
step={0.05}
min={0}
max={100}
width={150}
onChange={(e: any) => {
settings.setSync('fadeDuration', Number(e));
dispatch(
setPlaybackSetting({
setting: 'fadeDuration',
value: Number(e),
})
);
if (Number(e) === 0) {
dispatch(
setPlayerVolume({ player: 1, volume: playQueue.volume })
);
dispatch(
setPlayerVolume({ player: 2, volume: playQueue.volume })
);
}
}}
/>
<br />
<ControlLabel>Polling interval (ms)</ControlLabel>
<StyledInputNumber
defaultValue={String(settings.getSync('pollingInterval'))}
step={1}
min={1}
max={1000}
width={150}
onChange={(e: any) => {
settings.setSync('pollingInterval', Number(e));
dispatch(
setPlaybackSetting({
setting: 'pollingInterval',
value: Number(e),
})
);
}}
/>
<br />
<ControlLabel>Crossfade type</ControlLabel>
<RadioGroup
name="fadeTypeRadioList"
appearance="default"
defaultValue={String(settings.getSync('fadeType'))}
onChange={(e) => {
settings.setSync('fadeType', e);
dispatch(setPlaybackSetting({ setting: 'fadeType', value: e }));
}}
>
<StyledRadio value="equalPower">Equal Power</StyledRadio>
<StyledRadio value="linear">Linear</StyledRadio>
<StyledRadio value="dipped">Dipped</StyledRadio>
<StyledRadio value="constantPower">Constant Power</StyledRadio>
<StyledRadio value="constantPowerSlowFade">
Constant Power (slow fade)
</StyledRadio>
<StyledRadio value="constantPowerSlowCut">
Constant Power (slow cut)
</StyledRadio>
<StyledRadio value="constantPowerFastCut">
Constant Power (fast cut)
</StyledRadio>
</RadioGroup>
<br />
<ControlLabel>Volume fade</ControlLabel>
<RadioGroup
name="volumeFadeRadioList"
appearance="default"
defaultValue={Boolean(settings.getSync('volumeFade'))}
onChange={(e) => {
settings.setSync('volumeFade', e);
dispatch(setPlaybackSetting({ setting: 'volumeFade', value: e }));
}}
>
<StyledRadio value>Enabled</StyledRadio>
<StyledRadio value={false}>Disabled</StyledRadio>
</RadioGroup>
</div>
</ConfigPanel>
<ConfigPanel header="Look & Feel" bordered>
<div style={{ width: '300px' }}>
<p>Select the main application theme.</p>
<RadioGroup
name="themeRadioList"
appearance="default"
defaultValue={String(settings.getSync('theme'))}
onChange={(e) => {
settings.setSync('theme', e);
dispatch(setTheme(e));
}}
>
<StyledRadio value="defaultDark">Default Dark</StyledRadio>
<StyledRadio value="defaultLight">Default Light</StyledRadio>
</RadioGroup>
<br />
<ControlLabel>Font</ControlLabel>
<br />
<StyledInputPicker
data={Fonts}
groupBy="role"
defaultValue={String(settings.getSync('font'))}
onChange={(e: string) => {
settings.setSync('font', e);
dispatch(setFont(e));
}}
/>
</div>
<br />
<ConfigPanel bordered>
<p>
Select the columns you want displayed on pages with a list-view.
</p>
<Nav
style={{ paddingTop: '10px' }}
activeKey={currentLAFTab}
onSelect={(e) => setCurrentLAFTab(e)}
>
<StyledNavItem eventKey="songList">Song List</StyledNavItem>
<StyledNavItem eventKey="albumList">Album List</StyledNavItem>
<StyledNavItem eventKey="playlistList">Playlist List</StyledNavItem>
<StyledNavItem eventKey="miniList">Miniplayer List</StyledNavItem>
</Nav>
{currentLAFTab === 'songList' && (
<ListViewConfig
title="Song List"
defaultColumns={currentSongColumns}
columnPicker={songColumnPicker}
columnList={songColumnList}
settingsConfig={{
columnList: 'songListColumns',
rowHeight: 'songListRowHeight',
fontSize: 'songListFontSize',
}}
/>
)}
{currentLAFTab === 'albumList' && (
<ListViewConfig
title="Album List"
defaultColumns={currentAlbumColumns}
columnPicker={albumColumnPicker}
columnList={albumColumnList}
settingsConfig={{
columnList: 'albumListColumns',
rowHeight: 'albumListRowHeight',
fontSize: 'albumListFontSize',
}}
/>
)}
{currentLAFTab === 'playlistList' && (
<ListViewConfig
title="Playlist List"
defaultColumns={currentPlaylistColumns}
columnPicker={playlistColumnPicker}
columnList={playlistColumnList}
settingsConfig={{
columnList: 'playlistListColumns',
rowHeight: 'playlistListRowHeight',
fontSize: 'playlistListFontSize',
}}
/>
)}
{currentLAFTab === 'miniList' && (
<ListViewConfig
title="Miniplayer List"
defaultColumns={currentMiniColumns}
columnPicker={songColumnPicker}
columnList={songColumnList}
settingsConfig={{
columnList: 'miniListColumns',
rowHeight: 'miniListRowHeight',
fontSize: 'miniListFontSize',
}}
/>
)}
</ConfigPanel>
</ConfigPanel>
<ConfigPanel header="Player" bordered>
<p>
Configure the number of seconds to skip forwards/backwards by when
clicking the seek forward/backward buttons.
</p>
<br />
<ControlLabel>Seek forward (s)</ControlLabel>
<StyledInputNumber
defaultValue={String(settings.getSync('seekForwardInterval')) || '0'}
step={0.5}
min={0}
max={100}
width={150}
onChange={(e: any) => {
settings.setSync('seekForwardInterval', Number(e));
}}
/>
<br />
<ControlLabel>Seek backward (s)</ControlLabel>
<StyledInputNumber
defaultValue={String(settings.getSync('seekBackwardInterval')) || '0'}
step={0.5}
min={0}
max={100}
width={150}
onChange={(e: any) => {
settings.setSync('seekBackwardInterval', Number(e));
}}
/>
</ConfigPanel>
<ConfigPanel header="Cache" bordered>
{errorMessage !== '' && (
<>
<Message showIcon type="error" description={errorMessage} />
<br />
</>
)}
<p>
Songs are cached only when playback for the track fully completes and
ends. Skipping to the next or previous track after only partially
completing the track will not begin the caching process.
</p>
<br />
{isEditingCachePath && (
<>
<InputGroup>
<StyledInput
value={newCachePath}
onChange={(e: string) => setNewCachePath(e)}
/>
<InputGroup.Button
onClick={() => {
const check = fs.existsSync(newCachePath);
if (check) {
settings.setSync('cachePath', newCachePath);
fs.mkdirSync(getSongCachePath(), { recursive: true });
fs.mkdirSync(getImageCachePath(), { recursive: true });
setErrorMessage('');
return setIsEditingCachePath(false);
}
return setErrorMessage(
`Path: ${newCachePath} not found. Enter a valid path.`
);
}}
>
<Icon icon="check" />
</InputGroup.Button>
<InputGroup.Button
onClick={() => {
setIsEditingCachePath(false);
setErrorMessage('');
}}
>
<Icon icon="close" />
</InputGroup.Button>
</InputGroup>
<p style={{ fontSize: 'smaller' }}>
*You will need to manually move any existing cached files to their
new location.
</p>
</>
)}
{!isEditingCachePath && (
<div style={{ overflow: 'auto' }}>
Location:{' '}
<code>
{path.join(
String(settings.getSync('cachePath')),
'sonixdCache',
String(localStorage.getItem('serverBase64'))
)}
</code>
</div>
)}
<div style={{ width: '300px', marginTop: '20px' }}>
<StyledCheckbox
defaultChecked={cacheSongs}
onChange={() => {
settings.setSync('cacheSongs', !settings.getSync('cacheSongs'));
setCacheSongs(!cacheSongs);
}}
>
Songs{' '}
<Tag>
{songCacheSize} MB{' '}
{imgCacheSize === 9999999 && '- Folder not found'}
</Tag>
</StyledCheckbox>
<StyledCheckbox
defaultChecked={cacheImages}
onChange={() => {
settings.setSync('cacheImages', !settings.getSync('cacheImages'));
setCacheImages(!cacheImages);
}}
>
Images{' '}
<Tag>
{imgCacheSize} MB{' '}
{imgCacheSize === 9999999 && '- Folder not found'}
</Tag>
</StyledCheckbox>
<br />
<Button onClick={() => setIsEditingCachePath(true)}>
Edit cache location
</Button>
</div>
</ConfigPanel>
<ConfigPanel header="Advanced" bordered>
<StyledCheckbox
defaultChecked={showDebugWindow}
onChange={() => {
settings.setSync(
'showDebugWindow',
!settings.getSync('showDebugWindow')
);
dispatch(
setPlaybackSetting({
setting: 'showDebugWindow',
value: settings.getSync('showDebugWindow'),
})
);
setShowDebugWindow(!showDebugWindow);
}}
>
Show debug window
</StyledCheckbox>
</ConfigPanel>
<PlaybackConfig />
<LookAndFeelConfig />
<PlayerConfig />
<CacheConfig />
<DebugConfig />
</GenericPage>
);
};

146
src/components/settings/ConfigPanels/CacheConfig.tsx

@ -0,0 +1,146 @@
import React, { useState, useEffect } from 'react';
import settings from 'electron-settings';
import fs from 'fs';
import path from 'path';
import { InputGroup, Button, Tag, Message, Icon } from 'rsuite';
import { ConfigPanel } from '../styled';
import { StyledInput, StyledCheckbox } from '../../shared/styled';
import { getSongCachePath, getImageCachePath } from '../../../shared/utils';
const fsUtils = require('nodejs-fs-utils');
const CacheConfig = () => {
const [imgCacheSize, setImgCacheSize] = useState(0);
const [songCacheSize, setSongCacheSize] = useState(0);
const [isEditingCachePath, setIsEditingCachePath] = useState(false);
const [newCachePath, setNewCachePath] = useState('');
const [errorMessage, setErrorMessage] = useState('');
const [cacheSongs, setCacheSongs] = useState(
Boolean(settings.getSync('cacheSongs'))
);
const [cacheImages, setCacheImages] = useState(
Boolean(settings.getSync('cacheImages'))
);
useEffect(() => {
// Retrieve cache sizes on render
try {
setImgCacheSize(
Number(
(fsUtils.fsizeSync(getImageCachePath()) / 1000 / 1000).toFixed(0)
)
);
setSongCacheSize(
Number((fsUtils.fsizeSync(getSongCachePath()) / 1000 / 1000).toFixed(0))
);
} catch (err) {
setImgCacheSize(0);
setSongCacheSize(0);
fs.mkdirSync(getSongCachePath(), { recursive: true });
fs.mkdirSync(getImageCachePath(), { recursive: true });
}
}, []);
return (
<ConfigPanel header="Cache" bordered>
{errorMessage !== '' && (
<>
<Message showIcon type="error" description={errorMessage} />
<br />
</>
)}
<p>
Songs are cached only when playback for the track fully completes and
ends. Skipping to the next or previous track after only partially
completing the track will not begin the caching process.
</p>
<br />
{isEditingCachePath && (
<>
<InputGroup>
<StyledInput
value={newCachePath}
onChange={(e: string) => setNewCachePath(e)}
/>
<InputGroup.Button
onClick={() => {
const check = fs.existsSync(newCachePath);
if (check) {
settings.setSync('cachePath', newCachePath);
fs.mkdirSync(getSongCachePath(), { recursive: true });
fs.mkdirSync(getImageCachePath(), { recursive: true });
setErrorMessage('');
return setIsEditingCachePath(false);
}
return setErrorMessage(
`Path: ${newCachePath} not found. Enter a valid path.`
);
}}
>
<Icon icon="check" />
</InputGroup.Button>
<InputGroup.Button
onClick={() => {
setIsEditingCachePath(false);
setErrorMessage('');
}}
>
<Icon icon="close" />
</InputGroup.Button>
</InputGroup>
<p style={{ fontSize: 'smaller' }}>
*You will need to manually move any existing cached files to their
new location.
</p>
</>
)}
{!isEditingCachePath && (
<div style={{ overflow: 'auto' }}>
Location:{' '}
<code>
{path.join(
String(settings.getSync('cachePath')),
'sonixdCache',
String(localStorage.getItem('serverBase64'))
)}
</code>
</div>
)}
<div style={{ width: '300px', marginTop: '20px' }}>
<StyledCheckbox
defaultChecked={cacheSongs}
onChange={() => {
settings.setSync('cacheSongs', !settings.getSync('cacheSongs'));
setCacheSongs(!cacheSongs);
}}
>
Songs{' '}
<Tag>
{songCacheSize} MB{' '}
{imgCacheSize === 9999999 && '- Folder not found'}
</Tag>
</StyledCheckbox>
<StyledCheckbox
defaultChecked={cacheImages}
onChange={() => {
settings.setSync('cacheImages', !settings.getSync('cacheImages'));
setCacheImages(!cacheImages);
}}
>
Images{' '}
<Tag>
{imgCacheSize} MB {imgCacheSize === 9999999 && '- Folder not found'}
</Tag>
</StyledCheckbox>
<br />
<Button onClick={() => setIsEditingCachePath(true)}>
Edit cache location
</Button>
</div>
</ConfigPanel>
);
};
export default CacheConfig;

37
src/components/settings/ConfigPanels/DebugConfig.tsx

@ -0,0 +1,37 @@
import React, { useState } from 'react';
import settings from 'electron-settings';
import { ConfigPanel } from '../styled';
import { StyledCheckbox } from '../../shared/styled';
import { useAppDispatch } from '../../../redux/hooks';
import { setPlaybackSetting } from '../../../redux/playQueueSlice';
const DebugConfig = () => {
const dispatch = useAppDispatch();
const [showDebugWindow, setShowDebugWindow] = useState(
Boolean(settings.getSync('showDebugWindow'))
);
return (
<ConfigPanel header="Advanced" bordered>
<StyledCheckbox
defaultChecked={showDebugWindow}
onChange={() => {
settings.setSync(
'showDebugWindow',
!settings.getSync('showDebugWindow')
);
dispatch(
setPlaybackSetting({
setting: 'showDebugWindow',
value: settings.getSync('showDebugWindow'),
})
);
setShowDebugWindow(!showDebugWindow);
}}
>
Show debug window
</StyledCheckbox>
</ConfigPanel>
);
};
export default DebugConfig;

2
src/components/settings/ListViewConfig.tsx → src/components/settings/ConfigPanels/ListViewConfig.tsx

@ -1,7 +1,7 @@
import React from 'react';
import settings from 'electron-settings';
import { TagPicker, ControlLabel } from 'rsuite';
import { StyledInputNumber } from '../shared/styled';
import { StyledInputNumber } from '../../shared/styled';
const ListViewConfig = ({
defaultColumns,

139
src/components/settings/ConfigPanels/LookAndFeelConfig.tsx

@ -0,0 +1,139 @@
import React, { useState } from 'react';
import settings from 'electron-settings';
import { RadioGroup, ControlLabel, Nav } from 'rsuite';
import { ConfigPanel } from '../styled';
import {
StyledRadio,
StyledInputPicker,
StyledNavItem,
} from '../../shared/styled';
import ListViewConfig from './ListViewConfig';
import { Fonts } from '../Fonts';
import { useAppDispatch } from '../../../redux/hooks';
import { setTheme, setFont } from '../../../redux/miscSlice';
import {
songColumnPicker,
songColumnList,
albumColumnPicker,
albumColumnList,
playlistColumnPicker,
playlistColumnList,
} from '../ListViewColumns';
const LookAndFeelConfig = () => {
const dispatch = useAppDispatch();
const [currentLAFTab, setCurrentLAFTab] = useState('songList');
const songCols: any = settings.getSync('songListColumns');
const albumCols: any = settings.getSync('albumListColumns');
const playlistCols: any = settings.getSync('playlistListColumns');
const miniCols: any = settings.getSync('miniListColumns');
const currentSongColumns = songCols?.map((column: any) => column.label) || [];
const currentAlbumColumns =
albumCols?.map((column: any) => column.label) || [];
const currentPlaylistColumns =
playlistCols?.map((column: any) => column.label) || [];
const currentMiniColumns = miniCols?.map((column: any) => column.label) || [];
return (
<ConfigPanel header="Look & Feel" bordered>
<div style={{ width: '300px' }}>
<p>Select the main application theme.</p>
<RadioGroup
name="themeRadioList"
appearance="default"
defaultValue={String(settings.getSync('theme'))}
onChange={(e) => {
settings.setSync('theme', e);
dispatch(setTheme(e));
}}
>
<StyledRadio value="defaultDark">Default Dark</StyledRadio>
<StyledRadio value="defaultLight">Default Light</StyledRadio>
</RadioGroup>
<br />
<ControlLabel>Font</ControlLabel>
<br />
<StyledInputPicker
data={Fonts}
groupBy="role"
defaultValue={String(settings.getSync('font'))}
onChange={(e: string) => {
settings.setSync('font', e);
dispatch(setFont(e));
}}
/>
</div>
<br />
<ConfigPanel bordered>
<p>Select the columns you want displayed on pages with a list-view.</p>
<Nav
style={{ paddingTop: '10px' }}
activeKey={currentLAFTab}
onSelect={(e) => setCurrentLAFTab(e)}
>
<StyledNavItem eventKey="songList">Song List</StyledNavItem>
<StyledNavItem eventKey="albumList">Album List</StyledNavItem>
<StyledNavItem eventKey="playlistList">Playlist List</StyledNavItem>
<StyledNavItem eventKey="miniList">Miniplayer List</StyledNavItem>
</Nav>
{currentLAFTab === 'songList' && (
<ListViewConfig
title="Song List"
defaultColumns={currentSongColumns}
columnPicker={songColumnPicker}
columnList={songColumnList}
settingsConfig={{
columnList: 'songListColumns',
rowHeight: 'songListRowHeight',
fontSize: 'songListFontSize',
}}
/>
)}
{currentLAFTab === 'albumList' && (
<ListViewConfig
title="Album List"
defaultColumns={currentAlbumColumns}
columnPicker={albumColumnPicker}
columnList={albumColumnList}
settingsConfig={{
columnList: 'albumListColumns',
rowHeight: 'albumListRowHeight',
fontSize: 'albumListFontSize',
}}
/>
)}
{currentLAFTab === 'playlistList' && (
<ListViewConfig
title="Playlist List"
defaultColumns={currentPlaylistColumns}
columnPicker={playlistColumnPicker}
columnList={playlistColumnList}
settingsConfig={{
columnList: 'playlistListColumns',
rowHeight: 'playlistListRowHeight',
fontSize: 'playlistListFontSize',
}}
/>
)}
{currentLAFTab === 'miniList' && (
<ListViewConfig
title="Miniplayer List"
defaultColumns={currentMiniColumns}
columnPicker={songColumnPicker}
columnList={songColumnList}
settingsConfig={{
columnList: 'miniListColumns',
rowHeight: 'miniListRowHeight',
fontSize: 'miniListFontSize',
}}
/>
)}
</ConfigPanel>
</ConfigPanel>
);
};
export default LookAndFeelConfig;

132
src/components/settings/ConfigPanels/PlaybackConfig.tsx

@ -0,0 +1,132 @@
import React from 'react';
import settings from 'electron-settings';
import { ControlLabel, RadioGroup } from 'rsuite';
import { ConfigPanel } from '../styled';
import { StyledInputNumber, StyledRadio } from '../../shared/styled';
import { useAppDispatch, useAppSelector } from '../../../redux/hooks';
import {
setPlaybackSetting,
setPlayerVolume,
} from '../../../redux/playQueueSlice';
const PlaybackConfig = () => {
const dispatch = useAppDispatch();
const playQueue = useAppSelector((state) => state.playQueue);
return (
<ConfigPanel header="Playback" bordered>
<p>
Fading works by polling the audio player on an interval to determine
when to start fading to the next track. Due to this, you may notice the
fade timing may not be 100% perfect. Lowering the player polling
interval can increase the accuracy of the fade, but may also decrease
application performance as calculations are running for the fade.
</p>
<p>
If volume fade is disabled, then the fading-in track will start at the
specified crossfade duration at full volume.
</p>
<p>
Setting the crossfade duration to <code>0</code> will enable{' '}
<strong>gapless playback</strong>. All other playback settings except
the polling interval will be ignored. It is recommended that you use a
polling interval between <code>1</code> and <code>20</code> for
increased transition accuracy.
</p>
<p style={{ fontSize: 'smaller' }}>
*Enable the debug window if you want to view the differences between
each fade type
</p>
<div style={{ width: '300px', paddingTop: '20px' }}>
<ControlLabel>Crossfade duration (s)</ControlLabel>
<StyledInputNumber
defaultValue={String(settings.getSync('fadeDuration')) || '0'}
step={0.05}
min={0}
max={100}
width={150}
onChange={(e: any) => {
settings.setSync('fadeDuration', Number(e));
dispatch(
setPlaybackSetting({
setting: 'fadeDuration',
value: Number(e),
})
);
if (Number(e) === 0) {
dispatch(
setPlayerVolume({ player: 1, volume: playQueue.volume })
);
dispatch(
setPlayerVolume({ player: 2, volume: playQueue.volume })
);
}
}}
/>
<br />
<ControlLabel>Polling interval (ms)</ControlLabel>
<StyledInputNumber
defaultValue={String(settings.getSync('pollingInterval'))}
step={1}
min={1}
max={1000}
width={150}
onChange={(e: any) => {
settings.setSync('pollingInterval', Number(e));
dispatch(
setPlaybackSetting({
setting: 'pollingInterval',
value: Number(e),
})
);
}}
/>
<br />
<ControlLabel>Crossfade type</ControlLabel>
<RadioGroup
name="fadeTypeRadioList"
appearance="default"
defaultValue={String(settings.getSync('fadeType'))}
onChange={(e) => {
settings.setSync('fadeType', e);
dispatch(setPlaybackSetting({ setting: 'fadeType', value: e }));
}}
>
<StyledRadio value="equalPower">Equal Power</StyledRadio>
<StyledRadio value="linear">Linear</StyledRadio>
<StyledRadio value="dipped">Dipped</StyledRadio>
<StyledRadio value="constantPower">Constant Power</StyledRadio>
<StyledRadio value="constantPowerSlowFade">
Constant Power (slow fade)
</StyledRadio>
<StyledRadio value="constantPowerSlowCut">
Constant Power (slow cut)
</StyledRadio>
<StyledRadio value="constantPowerFastCut">
Constant Power (fast cut)
</StyledRadio>
</RadioGroup>
<br />
<ControlLabel>Volume fade</ControlLabel>
<RadioGroup
name="volumeFadeRadioList"
appearance="default"
defaultValue={Boolean(settings.getSync('volumeFade'))}
onChange={(e) => {
settings.setSync('volumeFade', e);
dispatch(setPlaybackSetting({ setting: 'volumeFade', value: e }));
}}
>
<StyledRadio value>Enabled</StyledRadio>
<StyledRadio value={false}>Disabled</StyledRadio>
</RadioGroup>
</div>
</ConfigPanel>
);
};
export default PlaybackConfig;

42
src/components/settings/ConfigPanels/PlayerConfig.tsx

@ -0,0 +1,42 @@
import React from 'react';
import settings from 'electron-settings';
import { ControlLabel } from 'rsuite';
import { ConfigPanel } from '../styled';
import { StyledInputNumber } from '../../shared/styled';
const PlayerConfig = () => {
return (
<ConfigPanel header="Player" bordered>
<p>
Configure the number of seconds to skip forwards/backwards by when
clicking the seek forward/backward buttons.
</p>
<br />
<ControlLabel>Seek forward (s)</ControlLabel>
<StyledInputNumber
defaultValue={String(settings.getSync('seekForwardInterval')) || '0'}
step={0.5}
min={0}
max={100}
width={150}
onChange={(e: any) => {
settings.setSync('seekForwardInterval', Number(e));
}}
/>
<br />
<ControlLabel>Seek backward (s)</ControlLabel>
<StyledInputNumber
defaultValue={String(settings.getSync('seekBackwardInterval')) || '0'}
step={0.5}
min={0}
max={100}
width={150}
onChange={(e: any) => {
settings.setSync('seekBackwardInterval', Number(e));
}}
/>
</ConfigPanel>
);
};
export default PlayerConfig;
Loading…
Cancel
Save