diff --git a/src/App.tsx b/src/App.tsx index 6475483..0bebbf5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import settings from 'electron-settings'; import { ThemeProvider } from 'styled-components'; import { HashRouter as Router, Switch, Route } from 'react-router-dom'; import './styles/App.global.css'; @@ -17,30 +18,26 @@ import AlbumList from './components/library/AlbumList'; import ArtistList from './components/library/ArtistList'; import GenreList from './components/library/GenreList'; import { MockFooter } from './components/settings/styled'; -import { defaultDark, defaultLight } from './styles/styledTheme'; import { useAppSelector } from './redux/hooks'; import PageModal from './components/modal/PageModal'; import NowPlayingMiniView from './components/player/NowPlayingMiniView'; import { GlobalContextMenu } from './components/shared/ContextMenu'; import SearchView from './components/search/SearchView'; import FolderList from './components/library/FolderList'; +import { getTheme } from './shared/utils'; +import { defaultDark } from './styles/styledTheme'; +import { mockSettings } from './shared/mockSettings'; + +const themes: any = + process.env.NODE_ENV === 'test' ? mockSettings.themes : settings.getSync('themes'); const App = () => { const [theme, setTheme] = useState(defaultDark); const [font, setFont] = useState('Poppins'); const misc = useAppSelector((state) => state.misc); + useEffect(() => { - switch (misc.theme) { - case 'defaultDark': - setTheme(defaultDark); - break; - case 'defaultLight': - setTheme(defaultLight); - break; - default: - setTheme(defaultDark); - break; - } + setTheme(getTheme(themes, misc.theme) || defaultDark); }, [misc.theme]); useEffect(() => { diff --git a/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx b/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx index 00c87ab..5b8d66c 100644 --- a/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx +++ b/src/components/settings/ConfigPanels/LookAndFeelConfig.tsx @@ -1,14 +1,15 @@ import React, { useRef, useState } from 'react'; +import { shell } from 'electron'; import settings from 'electron-settings'; -import { RadioGroup, ControlLabel, Nav } from 'rsuite'; +import { ControlLabel, Nav, Icon } from 'rsuite'; import { ConfigPanel } from '../styled'; import { - StyledRadio, StyledInputPicker, StyledNavItem, StyledInputNumber, StyledCheckbox, StyledInputPickerContainer, + StyledLink, } from '../../shared/styled'; import ListViewConfig from './ListViewConfig'; import { Fonts } from '../Fonts'; @@ -37,9 +38,10 @@ const LookAndFeelConfig = () => { const [highlightOnRowHoverChk, setHighlightOnRowHoverChk] = useState( Boolean(settings.getSync('highlightOnRowHover')) ); + const themePickerContainerRef = useRef(null); const fontPickerContainerRef = useRef(null); const titleBarPickerContainerRef = useRef(null); - + const themes: any = settings.getSync('themes'); const songCols: any = settings.getSync('musicListColumns'); const albumCols: any = settings.getSync('albumListColumns'); const playlistCols: any = settings.getSync('playlistListColumns'); @@ -56,23 +58,31 @@ const LookAndFeelConfig = () => { return ( <> -
-

Select the main application theme.

- + shell.openExternal('https://github.com/jeffvli/sonixd/discussions/61')} + > + Check out the theming documentation! + +

+
+ + Application theme +
+ themePickerContainerRef.current} + data={themes} + labelKey="label" + valueKey="value" + cleanable={false} defaultValue={String(settings.getSync('theme'))} - onChange={(e) => { + onChange={(e: string) => { settings.setSync('theme', e); dispatch(setTheme(e)); }} - > - Default Dark - Default Light -
-
+ /> +
- Font
diff --git a/src/shared/mockSettings.ts b/src/shared/mockSettings.ts index b5e7588..8518322 100644 --- a/src/shared/mockSettings.ts +++ b/src/shared/mockSettings.ts @@ -304,4 +304,291 @@ export const mockSettings = { windowMaximize: false, highlightOnRowHover: false, titleBarStyle: 'windows', + themes: [ + { + label: 'Default Dark', + value: 'defaultDark', + fonts: { + size: { + page: '14px', + pageTitle: '40px', + panelTitle: '20px', + button: '14px', + }, + }, + colors: { + primary: '#2196F3', + layout: { + page: { + color: '#D8D8D8', + colorSecondary: '#888e94', + background: '#181A1F', + }, + playerBar: { + color: '#D8D8D8', + colorSecondary: '#888e94', + background: '#101010', + button: { + color: 'rgba(240, 240, 240, 0.8)', + colorHover: '#FFFFFF', + }, + }, + sideBar: { + background: '#101010', + button: { + color: '#D8D8D8', + colorHover: '#FFFFFF', + }, + }, + titleBar: { + color: '#FFFFFF', + background: '#101010', + }, + }, + button: { + default: { + color: '#D8D8D8', + colorHover: '#FFFFFF', + background: '#292D33', + backgroundHover: '#3C3F43', + }, + primary: { + color: '#FFFFFF', + colorHover: '#FFFFFF', + backgroundHover: '#3B89EC', + }, + subtle: { + color: '#D8D8D8', + colorHover: '#D8D8D8', + backgroundHover: 'transparent', + }, + link: { + color: '#2196F3', + colorHover: '#3B89EC', + }, + }, + card: { + overlayButton: { + color: '#FFFFFF', + background: '#000000', + opacity: 0.8, + }, + }, + contextMenu: { + color: '#D8D8D8', + colorDisabled: '#6A6F76', + background: '#1E2125', + backgroundHover: '#292D33', + }, + input: { + color: '#D8D8D8', + background: '#25292E', + backgroundHover: '#353A45', + backgroundActive: 'rgba(240, 240, 240, .2)', + }, + nav: { + color: '#D8D8D8', + }, + popover: { + color: '#D8D8D8', + background: '#1E2125', + }, + slider: { + background: '#3C3F43', + progressBar: '#888E94', + }, + spinner: { + background: 'rgba(233, 235, 240, 0.3)', + foreground: '#2196F3', + }, + table: { + selectedRow: '#4D5156', + }, + tag: { + background: '#3C3F43', + text: '#E2E4E9', + }, + tooltip: { + color: '#D8D8D8', + background: '#1E2125', + }, + }, + other: { + button: { + borderRadius: '0px', + }, + coverArtFilter: 'drop-shadow(0px 3px 5px #000000)', + card: { + borderRadius: '0px', + }, + input: { + borderRadius: '0px', + }, + miniPlayer: { + height: '450px', + opacity: 0.95, + }, + panel: { + borderRadius: '0px', + }, + playerBar: { + borderTop: '1px solid rgba(240, 240, 240, .15)', + borderRight: 'none', + borderBottom: 'none', + borderLeft: 'none', + filter: 'none', + }, + tag: { + borderRadius: '0px', + }, + tooltip: { + border: '1px #3c3f43 solid', + borderRadius: '0px', + }, + }, + }, + { + label: 'Default Light', + value: 'defaultLight', + fonts: { + size: { + page: '14px', + pageTitle: '30px', + panelTitle: '20px', + }, + }, + colors: { + primary: '#285DA0', + layout: { + page: { + color: '#000000', + colorSecondary: '#888e94', + background: '#FFFFFF', + }, + playerBar: { + color: '#FFFFFF', + colorSecondary: '#888e94', + background: '#161B22', + button: { + color: 'rgba(240, 240, 240, 0.8)', + colorHover: '#FFFFFF', + }, + }, + sideBar: { + background: '#161B22', + button: { + color: '#D8D8D8', + colorHover: '#FFFFFF', + }, + }, + titleBar: { + color: '#FFFFFF', + background: '#161B22', + }, + }, + button: { + default: { + color: '#575757', + colorHover: '#000000', + background: '#DFDFE2', + backgroundHover: '#D2D2D6', + }, + primary: { + color: '#FFFFFF', + colorHover: '#FFFFFF', + backgroundHover: '#347AD3', + }, + subtle: { + color: '#575757', + colorHover: '#575757', + backgroundHover: 'transparent', + }, + link: { + color: '#575757', + colorHover: '#575757', + }, + }, + card: { + overlayButton: { + color: '#FFFFFF', + colorHover: '#FFFFFF', + background: '#000000', + backgroundHover: '#285DA0', + opacity: 0.8, + }, + }, + contextMenu: { + color: '#575757', + colorDisabled: '#BABABA', + background: '#FFFFFF', + backgroundHover: '#D2D2D6', + }, + input: { + color: '#000000', + background: '#FFFFFF', + backgroundHover: '#E5E5EA', + backgroundActive: 'rgba(0, 0, 0, .2)', + }, + nav: { + color: '#000000', + }, + popover: { + color: '#000000', + background: '#FFFFFF', + }, + slider: { + background: '#3C3F43', + progressBar: '#888E94', + }, + spinner: { + background: 'rgba(0, 0, 0, 0.3)', + foreground: '#285DA0', + }, + table: { + selectedRow: '#CCCCCC', + }, + tag: { + background: '#DFDFE2', + text: '#000000', + }, + tooltip: { + color: '#000000', + background: '#FFFFFF', + }, + }, + other: { + button: { + borderRadius: '0px', + }, + coverArtFilter: 'drop-shadow(0px 3px 5px #000000)', + card: { + borderRadius: '0px', + }, + input: { + borderRadius: '0px', + }, + miniPlayer: { + height: '450px', + opacity: 0.95, + }, + panel: { + borderRadius: '0px', + }, + playerBar: { + borderTop: '1px solid rgba(240, 240, 240, .15)', + borderRight: 'none', + borderBottom: 'none', + borderLeft: 'none', + filter: 'none', + }, + tag: { + borderRadius: '0px', + }, + tooltip: { + border: '1px #3c3f43 solid', + borderRadius: '0px', + }, + }, + }, + ], }; diff --git a/src/shared/utils.ts b/src/shared/utils.ts index f824eae..eba857e 100644 --- a/src/shared/utils.ts +++ b/src/shared/utils.ts @@ -423,3 +423,7 @@ export const getCurrentEntryList = (playQueue: any) => { return 'entry'; }; + +export const getTheme = (themes: any[], value: string) => { + return themes.find((theme) => theme.value === value); +};