diff --git a/src/components/settings/Config.tsx b/src/components/settings/Config.tsx
index 1620412..ac22c86 100644
--- a/src/components/settings/Config.tsx
+++ b/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 = () => {
/>
}
>
-
-
- 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.
-
-
-
- If volume fade is disabled, then the fading-in track will start at the
- specified crossfade duration at full volume.
-
-
-
- Setting the crossfade duration to 0 will enable{' '}
- gapless playback . All other playback settings except
- the polling interval will be ignored. It is recommended that you use a
- polling interval between 1 and 20 for
- increased transition accuracy.
-
-
- *Enable the debug window if you want to view the differences between
- each fade type
-
-
-
- Crossfade duration (s)
- {
- 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 })
- );
- }
- }}
- />
-
-
- Polling interval (ms)
- {
- settings.setSync('pollingInterval', Number(e));
- dispatch(
- setPlaybackSetting({
- setting: 'pollingInterval',
- value: Number(e),
- })
- );
- }}
- />
-
- Crossfade type
- {
- settings.setSync('fadeType', e);
- dispatch(setPlaybackSetting({ setting: 'fadeType', value: e }));
- }}
- >
- Equal Power
- Linear
- Dipped
- Constant Power
-
- Constant Power (slow fade)
-
-
- Constant Power (slow cut)
-
-
- Constant Power (fast cut)
-
-
-
- Volume fade
- {
- settings.setSync('volumeFade', e);
- dispatch(setPlaybackSetting({ setting: 'volumeFade', value: e }));
- }}
- >
- Enabled
- Disabled
-
-
-
-
-
-
Select the main application theme.
-
{
- settings.setSync('theme', e);
- dispatch(setTheme(e));
- }}
- >
- Default Dark
- Default Light
-
-
-
Font
-
-
{
- settings.setSync('font', e);
- dispatch(setFont(e));
- }}
- />
-
-
-
-
- Select the columns you want displayed on pages with a list-view.
-
- setCurrentLAFTab(e)}
- >
- Song List
- Album List
- Playlist List
- Miniplayer List
-
- {currentLAFTab === 'songList' && (
-
- )}
-
- {currentLAFTab === 'albumList' && (
-
- )}
-
- {currentLAFTab === 'playlistList' && (
-
- )}
- {currentLAFTab === 'miniList' && (
-
- )}
-
-
-
-
- Configure the number of seconds to skip forwards/backwards by when
- clicking the seek forward/backward buttons.
-
-
- Seek forward (s)
- {
- settings.setSync('seekForwardInterval', Number(e));
- }}
- />
-
- Seek backward (s)
- {
- settings.setSync('seekBackwardInterval', Number(e));
- }}
- />
-
-
- {errorMessage !== '' && (
- <>
-
-
- >
- )}
-
- 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.
-
-
- {isEditingCachePath && (
- <>
-
- setNewCachePath(e)}
- />
- {
- 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.`
- );
- }}
- >
-
-
- {
- setIsEditingCachePath(false);
- setErrorMessage('');
- }}
- >
-
-
-
-
- *You will need to manually move any existing cached files to their
- new location.
-
- >
- )}
- {!isEditingCachePath && (
-
- Location:{' '}
-
- {path.join(
- String(settings.getSync('cachePath')),
- 'sonixdCache',
- String(localStorage.getItem('serverBase64'))
- )}
-
-
- )}
-
- {
- settings.setSync('cacheSongs', !settings.getSync('cacheSongs'));
- setCacheSongs(!cacheSongs);
- }}
- >
- Songs{' '}
-
- {songCacheSize} MB{' '}
- {imgCacheSize === 9999999 && '- Folder not found'}
-
-
- {
- settings.setSync('cacheImages', !settings.getSync('cacheImages'));
- setCacheImages(!cacheImages);
- }}
- >
- Images{' '}
-
- {imgCacheSize} MB{' '}
- {imgCacheSize === 9999999 && '- Folder not found'}
-
-
-
- setIsEditingCachePath(true)}>
- Edit cache location
-
-
-
-
- {
- settings.setSync(
- 'showDebugWindow',
- !settings.getSync('showDebugWindow')
- );
- dispatch(
- setPlaybackSetting({
- setting: 'showDebugWindow',
- value: settings.getSync('showDebugWindow'),
- })
- );
- setShowDebugWindow(!showDebugWindow);
- }}
- >
- Show debug window
-
-
+
+
+
+
+
);
};
diff --git a/src/components/settings/ConfigPanels/CacheConfig.tsx b/src/components/settings/ConfigPanels/CacheConfig.tsx
new file mode 100644
index 0000000..74122b7
--- /dev/null
+++ b/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 (
+
+ {errorMessage !== '' && (
+ <>
+
+
+ >
+ )}
+
+ 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.
+
+
+ {isEditingCachePath && (
+ <>
+
+ setNewCachePath(e)}
+ />
+ {
+ 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.`
+ );
+ }}
+ >
+
+
+ {
+ setIsEditingCachePath(false);
+ setErrorMessage('');
+ }}
+ >
+
+
+
+
+ *You will need to manually move any existing cached files to their
+ new location.
+
+ >
+ )}
+ {!isEditingCachePath && (
+
+ Location:{' '}
+
+ {path.join(
+ String(settings.getSync('cachePath')),
+ 'sonixdCache',
+ String(localStorage.getItem('serverBase64'))
+ )}
+
+
+ )}
+
+ {
+ settings.setSync('cacheSongs', !settings.getSync('cacheSongs'));
+ setCacheSongs(!cacheSongs);
+ }}
+ >
+ Songs{' '}
+
+ {songCacheSize} MB{' '}
+ {imgCacheSize === 9999999 && '- Folder not found'}
+
+
+ {
+ settings.setSync('cacheImages', !settings.getSync('cacheImages'));
+ setCacheImages(!cacheImages);
+ }}
+ >
+ Images{' '}
+
+ {imgCacheSize} MB {imgCacheSize === 9999999 && '- Folder not found'}
+
+
+
+ setIsEditingCachePath(true)}>
+ Edit cache location
+
+
+
+ );
+};
+
+export default CacheConfig;
diff --git a/src/components/settings/ConfigPanels/DebugConfig.tsx b/src/components/settings/ConfigPanels/DebugConfig.tsx
new file mode 100644
index 0000000..a37a803
--- /dev/null
+++ b/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 (
+
+ {
+ settings.setSync(
+ 'showDebugWindow',
+ !settings.getSync('showDebugWindow')
+ );
+ dispatch(
+ setPlaybackSetting({
+ setting: 'showDebugWindow',
+ value: settings.getSync('showDebugWindow'),
+ })
+ );
+ setShowDebugWindow(!showDebugWindow);
+ }}
+ >
+ Show debug window
+
+
+ );
+};
+
+export default DebugConfig;
diff --git a/src/components/settings/ListViewConfig.tsx b/src/components/settings/ConfigPanels/ListViewConfig.tsx
similarity index 97%
rename from src/components/settings/ListViewConfig.tsx
rename to src/components/settings/ConfigPanels/ListViewConfig.tsx
index b96abf6..2de310f 100644
--- a/src/components/settings/ListViewConfig.tsx
+++ b/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,
diff --git a/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx b/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx
new file mode 100644
index 0000000..6e2e948
--- /dev/null
+++ b/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 (
+
+
+
Select the main application theme.
+
{
+ settings.setSync('theme', e);
+ dispatch(setTheme(e));
+ }}
+ >
+ Default Dark
+ Default Light
+
+
+
Font
+
+
{
+ settings.setSync('font', e);
+ dispatch(setFont(e));
+ }}
+ />
+
+
+
+ Select the columns you want displayed on pages with a list-view.
+ setCurrentLAFTab(e)}
+ >
+ Song List
+ Album List
+ Playlist List
+ Miniplayer List
+
+ {currentLAFTab === 'songList' && (
+
+ )}
+
+ {currentLAFTab === 'albumList' && (
+
+ )}
+
+ {currentLAFTab === 'playlistList' && (
+
+ )}
+ {currentLAFTab === 'miniList' && (
+
+ )}
+
+
+ );
+};
+
+export default LookAndFeelConfig;
diff --git a/src/components/settings/ConfigPanels/PlaybackConfig.tsx b/src/components/settings/ConfigPanels/PlaybackConfig.tsx
new file mode 100644
index 0000000..e20afcd
--- /dev/null
+++ b/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 (
+
+
+ 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.
+
+
+
+ If volume fade is disabled, then the fading-in track will start at the
+ specified crossfade duration at full volume.
+
+
+
+ Setting the crossfade duration to 0 will enable{' '}
+ gapless playback . All other playback settings except
+ the polling interval will be ignored. It is recommended that you use a
+ polling interval between 1 and 20 for
+ increased transition accuracy.
+
+
+ *Enable the debug window if you want to view the differences between
+ each fade type
+
+
+
+ Crossfade duration (s)
+ {
+ 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 })
+ );
+ }
+ }}
+ />
+
+
+ Polling interval (ms)
+ {
+ settings.setSync('pollingInterval', Number(e));
+ dispatch(
+ setPlaybackSetting({
+ setting: 'pollingInterval',
+ value: Number(e),
+ })
+ );
+ }}
+ />
+
+ Crossfade type
+ {
+ settings.setSync('fadeType', e);
+ dispatch(setPlaybackSetting({ setting: 'fadeType', value: e }));
+ }}
+ >
+ Equal Power
+ Linear
+ Dipped
+ Constant Power
+
+ Constant Power (slow fade)
+
+
+ Constant Power (slow cut)
+
+
+ Constant Power (fast cut)
+
+
+
+ Volume fade
+ {
+ settings.setSync('volumeFade', e);
+ dispatch(setPlaybackSetting({ setting: 'volumeFade', value: e }));
+ }}
+ >
+ Enabled
+ Disabled
+
+
+
+ );
+};
+
+export default PlaybackConfig;
diff --git a/src/components/settings/ConfigPanels/PlayerConfig.tsx b/src/components/settings/ConfigPanels/PlayerConfig.tsx
new file mode 100644
index 0000000..304c388
--- /dev/null
+++ b/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 (
+
+
+ Configure the number of seconds to skip forwards/backwards by when
+ clicking the seek forward/backward buttons.
+
+
+ Seek forward (s)
+ {
+ settings.setSync('seekForwardInterval', Number(e));
+ }}
+ />
+
+ Seek backward (s)
+ {
+ settings.setSync('seekBackwardInterval', Number(e));
+ }}
+ />
+
+ );
+};
+
+export default PlayerConfig;