Moved Themer to Settings. Added option to set default theme

This commit is contained in:
Paweł Malak 2021-11-18 13:47:27 +01:00
parent 426766225b
commit e2285e2deb
20 changed files with 156 additions and 54 deletions

View file

@ -1,4 +1,5 @@
### v2.0.1 (TBA)
- Added option to set default theme for all new users ([#165](https://github.com/pawelmalak/flame/issues/165))
- Fixed bug with custom icons not working with apps when "pin by default" was disabled
### v2.0.0 (2021-11-15)

View file

@ -1,7 +1,13 @@
import { useEffect } from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import 'external-svg-loader';
// Redux
import { useDispatch, useSelector } from 'react-redux';
import { bindActionCreators } from 'redux';
import { autoLogin, getConfig } from './store/action-creators';
import { actionCreators, store } from './store';
import 'external-svg-loader';
import { State } from './store/reducers';
// Utils
import { checkVersion, decodeToken } from './utility';
@ -12,9 +18,6 @@ import { Apps } from './components/Apps/Apps';
import { Settings } from './components/Settings/Settings';
import { Bookmarks } from './components/Bookmarks/Bookmarks';
import { NotificationCenter } from './components/NotificationCenter/NotificationCenter';
import { useDispatch } from 'react-redux';
import { bindActionCreators } from 'redux';
import { useEffect } from 'react';
// Get config
store.dispatch<any>(getConfig());
@ -25,6 +28,8 @@ if (localStorage.token) {
}
export const App = (): JSX.Element => {
const { config, loading } = useSelector((state: State) => state.config);
const dispath = useDispatch();
const { fetchQueries, setTheme, logout, createNotification } =
bindActionCreators(actionCreators, dispath);
@ -46,7 +51,7 @@ export const App = (): JSX.Element => {
}
}, 1000);
// set theme
// set user theme if present
if (localStorage.theme) {
setTheme(localStorage.theme);
}
@ -60,6 +65,13 @@ export const App = (): JSX.Element => {
return () => window.clearInterval(tokenIsValid);
}, []);
// If there is no user theme, set the default one
useEffect(() => {
if (!loading && !localStorage.theme) {
setTheme(config.defaultTheme);
}
}, [loading]);
return (
<>
<BrowserRouter>

View file

@ -59,7 +59,7 @@ export const DockerSettings = (): JSX.Element => {
<SettingsHeadline text="Docker" />
{/* CUSTOM DOCKER SOCKET HOST */}
<InputGroup>
<label htmlFor="dockerHost">Docker Host</label>
<label htmlFor="dockerHost">Docker host</label>
<input
type="text"
id="dockerHost"

View file

@ -70,7 +70,7 @@ export const SearchSettings = (): JSX.Element => {
>
<SettingsHeadline text="General" />
<InputGroup>
<label htmlFor="defaultSearchProvider">Default Search Provider</label>
<label htmlFor="defaultSearchProvider">Default search provider</label>
<select
id="defaultSearchProvider"
name="defaultSearchProvider"

View file

@ -11,7 +11,7 @@ import { Route as SettingsRoute } from '../../interfaces';
import classes from './Settings.module.css';
// Components
import { Themer } from '../Themer/Themer';
import { Themer } from './Themer/Themer';
import { WeatherSettings } from './WeatherSettings/WeatherSettings';
import { UISettings } from './UISettings/UISettings';
import { AppDetails } from './AppDetails/AppDetails';

View file

@ -14,7 +14,6 @@
text-transform: capitalize;
margin: 8px 0;
color: var(--color-primary);
/* align-self: flex-start; */
}
.ColorsPreview {
@ -32,4 +31,4 @@
width: 40px;
height: 40px;
}
}
}

View file

@ -1,4 +1,4 @@
import { Theme } from '../../interfaces/Theme';
import { Theme } from '../../../interfaces/Theme';
import classes from './ThemePreview.module.css';
interface Props {

View file

@ -0,0 +1,101 @@
import { ChangeEvent, FormEvent, Fragment, useEffect, useState } from 'react';
// Redux
import { useDispatch, useSelector } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../../../store';
// Typescript
import { Theme, ThemeSettingsForm } from '../../../interfaces';
// Components
import { ThemePreview } from './ThemePreview';
import { Button, InputGroup, SettingsHeadline } from '../../UI';
// Other
import classes from './Themer.module.css';
import { themes } from './themes.json';
import { State } from '../../../store/reducers';
import { inputHandler, themeSettingsTemplate } from '../../../utility';
export const Themer = (): JSX.Element => {
const {
auth: { isAuthenticated },
config: { loading, config },
} = useSelector((state: State) => state);
const dispatch = useDispatch();
const { setTheme, updateConfig } = bindActionCreators(
actionCreators,
dispatch
);
// Initial state
const [formData, setFormData] = useState<ThemeSettingsForm>(
themeSettingsTemplate
);
// Get config
useEffect(() => {
setFormData({
...config,
});
}, [loading]);
// Form handler
const formSubmitHandler = async (e: FormEvent) => {
e.preventDefault();
// Save settings
await updateConfig(formData);
};
// Input handler
const inputChangeHandler = (
e: ChangeEvent<HTMLInputElement | HTMLSelectElement>,
options?: { isNumber?: boolean; isBool?: boolean }
) => {
inputHandler<ThemeSettingsForm>({
e,
options,
setStateHandler: setFormData,
state: formData,
});
};
return (
<Fragment>
<SettingsHeadline text="Set theme" />
<div className={classes.ThemerGrid}>
{themes.map(
(theme: Theme, idx: number): JSX.Element => (
<ThemePreview key={idx} theme={theme} applyTheme={setTheme} />
)
)}
</div>
{isAuthenticated && (
<form onSubmit={formSubmitHandler}>
<SettingsHeadline text="Other settings" />
<InputGroup>
<label htmlFor="defaultTheme">Default theme (for new users)</label>
<select
id="defaultTheme"
name="defaultTheme"
value={formData.defaultTheme}
onChange={(e) => inputChangeHandler(e)}
>
{themes.map((theme: Theme, idx) => (
<option key={idx} value={theme.name}>
{theme.name}
</option>
))}
</select>
</InputGroup>
<Button>Save changes</Button>
</form>
)}
</Fragment>
);
};

View file

@ -1,29 +0,0 @@
import { Fragment } from 'react';
import { useDispatch } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../../store';
import classes from './Themer.module.css';
import { themes } from './themes.json';
import { Theme } from '../../interfaces/Theme';
import { ThemePreview } from './ThemePreview';
export const Themer = (): JSX.Element => {
const dispatch = useDispatch();
const { setTheme } = bindActionCreators(actionCreators, dispatch);
return (
<Fragment>
<div>
<div className={classes.ThemerGrid}>
{themes.map(
(theme: Theme, idx: number): JSX.Element => (
<ThemePreview key={idx} theme={theme} applyTheme={setTheme} />
)
)}
</div>
</div>
</Fragment>
);
};

View file

@ -25,4 +25,5 @@ export interface Config {
daySchema: string;
monthSchema: string;
showTime: boolean;
defaultTheme: string;
}

View file

@ -35,3 +35,7 @@ export interface DockerSettingsForm {
kubernetesApps: boolean;
unpinStoppedApps: boolean;
}
export interface ThemeSettingsForm {
defaultTheme: string;
}

View file

@ -8,17 +8,10 @@ import {
UpdateQueryAction,
} from '../actions/config';
import axios from 'axios';
import {
ApiResponse,
Config,
DockerSettingsForm,
OtherSettingsForm,
Query,
SearchForm,
WeatherForm,
} from '../../interfaces';
import { ApiResponse, Config, Query } from '../../interfaces';
import { ActionType } from '../action-types';
import { storeUIConfig, applyAuth } from '../../utility';
import { ConfigFormData } from '../../types';
const keys: (keyof Config)[] = [
'useAmericanDate',
@ -50,9 +43,7 @@ export const getConfig = () => async (dispatch: Dispatch<GetConfigAction>) => {
};
export const updateConfig =
(
formData: WeatherForm | OtherSettingsForm | SearchForm | DockerSettingsForm
) =>
(formData: ConfigFormData) =>
async (dispatch: Dispatch<UpdateConfigAction>) => {
try {
const res = await axios.put<ApiResponse<Config>>(

View file

@ -2,7 +2,7 @@ import { Dispatch } from 'redux';
import { SetThemeAction } from '../actions/theme';
import { ActionType } from '../action-types';
import { Theme } from '../../interfaces/Theme';
import { themes } from '../../components/Themer/themes.json';
import { themes } from '../../components/Settings/Themer/themes.json';
export const setTheme =
(name: string) => (dispatch: Dispatch<SetThemeAction>) => {

View file

@ -0,0 +1,14 @@
import {
DockerSettingsForm,
OtherSettingsForm,
SearchForm,
ThemeSettingsForm,
WeatherForm,
} from '../interfaces';
export type ConfigFormData =
| WeatherForm
| SearchForm
| DockerSettingsForm
| OtherSettingsForm
| ThemeSettingsForm;

View file

@ -0,0 +1 @@
export * from './ConfigFormData';

View file

@ -28,4 +28,5 @@ export const configTemplate: Config = {
monthSchema:
'January;February;March;April;May;June;July;August;September;October;November;December',
showTime: false,
defaultTheme: 'tron',
};

View file

@ -2,6 +2,7 @@ import {
DockerSettingsForm,
OtherSettingsForm,
SearchForm,
ThemeSettingsForm,
WeatherForm,
} from '../../interfaces';
@ -43,3 +44,7 @@ export const dockerSettingsTemplate: DockerSettingsForm = {
kubernetesApps: true,
unpinStoppedApps: true,
};
export const themeSettingsTemplate: ThemeSettingsForm = {
defaultTheme: 'tron',
};

View file

@ -24,5 +24,6 @@
"greetingsSchema": "Good evening!;Good afternoon!;Good morning!;Good night!",
"daySchema": "Sunday;Monday;Tuesday;Wednesday;Thursday;Friday;Saturday",
"monthSchema": "January;February;March;April;May;June;July;August;September;October;November;December",
"showTime": false
"showTime": false,
"defaultTheme": "tron"
}