Components: refactored rest of the components to use new state. Minor changes to exports, imports and props

This commit is contained in:
Paweł Malak
2021-11-09 14:33:51 +01:00
parent 89d935e27f
commit 969bdb7d24
29 changed files with 462 additions and 733 deletions

View File

@@ -1,34 +1,33 @@
import { Fragment } from 'react';
import classes from './AppDetails.module.css';
import Button from '../../UI/Buttons/Button/Button';
import { Button } from '../../UI';
import { checkVersion } from '../../../utility';
const AppDetails = (): JSX.Element => {
export const AppDetails = (): JSX.Element => {
return (
<Fragment>
<p className={classes.AppVersion}>
<a
href='https://github.com/pawelmalak/flame'
target='_blank'
rel='noreferrer'>
href="https://github.com/pawelmalak/flame"
target="_blank"
rel="noreferrer"
>
Flame
</a>
{' '}
</a>{' '}
version {process.env.REACT_APP_VERSION}
</p>
<p className={classes.AppVersion}>
See changelog {' '}
See changelog{' '}
<a
href='https://github.com/pawelmalak/flame/blob/master/CHANGELOG.md'
target='_blank'
rel='noreferrer'>
href="https://github.com/pawelmalak/flame/blob/master/CHANGELOG.md"
target="_blank"
rel="noreferrer"
>
here
</a>
</p>
<Button click={() => checkVersion(true)}>Check for updates</Button>
</Fragment>
)
}
export default AppDetails;
);
};

View File

@@ -1,41 +1,28 @@
import { useState, useEffect, ChangeEvent, FormEvent } from 'react';
// Redux
import { connect } from 'react-redux';
import {
createNotification,
updateConfig,
sortApps,
sortCategories,
} from '../../../store/actions';
import { useDispatch, useSelector } from 'react-redux';
// Typescript
import {
Config,
GlobalState,
NewNotification,
OtherSettingsForm,
} from '../../../interfaces';
import { OtherSettingsForm } from '../../../interfaces';
// UI
import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
import Button from '../../UI/Buttons/Button/Button';
import SettingsHeadline from '../../UI/Headlines/SettingsHeadline/SettingsHeadline';
import { InputGroup, Button, SettingsHeadline } from '../../UI';
// Utils
import { otherSettingsTemplate, inputHandler } from '../../../utility';
import { State } from '../../../store/reducers';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../../../store';
interface ComponentProps {
createNotification: (notification: NewNotification) => void;
updateConfig: (formData: OtherSettingsForm) => void;
sortApps: () => void;
sortCategories: () => void;
loading: boolean;
config: Config;
}
export const OtherSettings = (): JSX.Element => {
const { loading, config } = useSelector((state: State) => state.config);
const OtherSettings = (props: ComponentProps): JSX.Element => {
const { config } = props;
const dispatch = useDispatch();
const { updateConfig, sortApps, sortCategories } = bindActionCreators(
actionCreators,
dispatch
);
// Initial state
const [formData, setFormData] = useState<OtherSettingsForm>(
@@ -47,21 +34,21 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
setFormData({
...config,
});
}, [props.loading]);
}, [loading]);
// Form handler
const formSubmitHandler = async (e: FormEvent) => {
e.preventDefault();
// Save settings
await props.updateConfig(formData);
await updateConfig(formData);
// Update local page title
document.title = formData.customTitle;
// Sort apps and categories with new settings
props.sortApps();
props.sortCategories();
sortApps();
sortCategories();
};
// Input handler
@@ -338,19 +325,3 @@ const OtherSettings = (props: ComponentProps): JSX.Element => {
</form>
);
};
const mapStateToProps = (state: GlobalState) => {
return {
loading: state.config.loading,
config: state.config.config,
};
};
const actions = {
createNotification,
updateConfig,
sortApps,
sortCategories,
};
export default connect(mapStateToProps, actions)(OtherSettings);

View File

@@ -1,29 +1,31 @@
import { Fragment, useState } from 'react';
import { connect } from 'react-redux';
// Redux
import { useDispatch, useSelector } from 'react-redux';
import { State } from '../../../../store/reducers';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../../../../store';
// Typescript
import { Query } from '../../../../interfaces';
// CSS
import classes from './CustomQueries.module.css';
import Modal from '../../../UI/Modal/Modal';
import Icon from '../../../UI/Icons/Icon/Icon';
import {
Config,
GlobalState,
NewNotification,
Query,
} from '../../../../interfaces';
import QueriesForm from './QueriesForm';
import { deleteQuery, createNotification } from '../../../../store/actions';
import Button from '../../../UI/Buttons/Button/Button';
// UI
import { Modal, Icon, Button } from '../../../UI';
interface Props {
customQueries: Query[];
deleteQuery: (prefix: string) => {};
createNotification: (notification: NewNotification) => void;
config: Config;
}
// Components
import { QueriesForm } from './QueriesForm';
const CustomQueries = (props: Props): JSX.Element => {
const { customQueries, deleteQuery, createNotification } = props;
export const CustomQueries = (): JSX.Element => {
const { customQueries, config } = useSelector((state: State) => state.config);
const dispatch = useDispatch();
const { deleteQuery, createNotification } = bindActionCreators(
actionCreators,
dispatch
);
const [modalIsOpen, setModalIsOpen] = useState(false);
const [editableQuery, setEditableQuery] = useState<Query | null>(null);
@@ -34,7 +36,7 @@ const CustomQueries = (props: Props): JSX.Element => {
};
const deleteHandler = (query: Query) => {
const currentProvider = props.config.defaultSearchProvider;
const currentProvider = config.defaultSearchProvider;
const isCurrent = currentProvider === query.prefix;
if (isCurrent) {
@@ -105,14 +107,3 @@ const CustomQueries = (props: Props): JSX.Element => {
</Fragment>
);
};
const mapStateToProps = (state: GlobalState) => {
return {
customQueries: state.config.customQueries,
config: state.config.config,
};
};
export default connect(mapStateToProps, { deleteQuery, createNotification })(
CustomQueries
);

View File

@@ -1,20 +1,26 @@
import { ChangeEvent, FormEvent, useState, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../../../../store';
import { Query } from '../../../../interfaces';
import Button from '../../../UI/Buttons/Button/Button';
import InputGroup from '../../../UI/Forms/InputGroup/InputGroup';
import ModalForm from '../../../UI/Forms/ModalForm/ModalForm';
import { connect } from 'react-redux';
import { addQuery, updateQuery } from '../../../../store/actions';
import { Button, InputGroup, ModalForm } from '../../../UI';
interface Props {
modalHandler: () => void;
addQuery: (query: Query) => {};
updateQuery: (query: Query, Oldprefix: string) => {};
query?: Query;
}
const QueriesForm = (props: Props): JSX.Element => {
const { modalHandler, addQuery, updateQuery, query } = props;
export const QueriesForm = (props: Props): JSX.Element => {
const dispatch = useDispatch();
const { addQuery, updateQuery } = bindActionCreators(
actionCreators,
dispatch
);
const { modalHandler, query } = props;
const [formData, setFormData] = useState<Query>({
name: '',
@@ -77,6 +83,7 @@ const QueriesForm = (props: Props): JSX.Element => {
onChange={(e) => inputChangeHandler(e)}
/>
</InputGroup>
<InputGroup>
<label htmlFor="name">Prefix</label>
<input
@@ -89,6 +96,7 @@ const QueriesForm = (props: Props): JSX.Element => {
onChange={(e) => inputChangeHandler(e)}
/>
</InputGroup>
<InputGroup>
<label htmlFor="name">Query Template</label>
<input
@@ -101,9 +109,8 @@ const QueriesForm = (props: Props): JSX.Element => {
onChange={(e) => inputChangeHandler(e)}
/>
</InputGroup>
{query ? <Button>Update provider</Button> : <Button>Add provider</Button>}
</ModalForm>
);
};
export default connect(null, { addQuery, updateQuery })(QueriesForm);

View File

@@ -1,58 +1,49 @@
// React
import { useState, useEffect, FormEvent, ChangeEvent, Fragment } from 'react';
import { connect } from 'react-redux';
// State
import { createNotification, updateConfig } from '../../../store/actions';
import { useDispatch, useSelector } from 'react-redux';
// Typescript
import {
Config,
GlobalState,
NewNotification,
Query,
SearchForm,
} from '../../../interfaces';
import { Query, SearchForm } from '../../../interfaces';
// Components
import CustomQueries from './CustomQueries/CustomQueries';
import { CustomQueries } from './CustomQueries/CustomQueries';
// UI
import Button from '../../UI/Buttons/Button/Button';
import SettingsHeadline from '../../UI/Headlines/SettingsHeadline/SettingsHeadline';
import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
import { Button, SettingsHeadline, InputGroup } from '../../UI';
// Utils
import { inputHandler, searchSettingsTemplate } from '../../../utility';
// Data
import { queries } from '../../../utility/searchQueries.json';
import { State } from '../../../store/reducers';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../../../store';
interface Props {
createNotification: (notification: NewNotification) => void;
updateConfig: (formData: SearchForm) => void;
loading: boolean;
customQueries: Query[];
config: Config;
}
export const SearchSettings = (): JSX.Element => {
const { loading, customQueries, config } = useSelector(
(state: State) => state.config
);
const dispatch = useDispatch();
const { updateConfig } = bindActionCreators(actionCreators, dispatch);
const SearchSettings = (props: Props): JSX.Element => {
// Initial state
const [formData, setFormData] = useState<SearchForm>(searchSettingsTemplate);
// Get config
useEffect(() => {
setFormData({
...props.config,
...config,
});
}, [props.loading]);
}, [loading]);
// Form handler
const formSubmitHandler = async (e: FormEvent) => {
e.preventDefault();
// Save settings
await props.updateConfig(formData);
await updateConfig(formData);
};
// Input handler
@@ -84,7 +75,7 @@ const SearchSettings = (props: Props): JSX.Element => {
value={formData.defaultSearchProvider}
onChange={(e) => inputChangeHandler(e)}
>
{[...queries, ...props.customQueries].map((query: Query, idx) => {
{[...queries, ...customQueries].map((query: Query, idx) => {
const isCustom = idx >= queries.length;
return (
@@ -95,6 +86,7 @@ const SearchSettings = (props: Props): JSX.Element => {
})}
</select>
</InputGroup>
<InputGroup>
<label htmlFor="searchSameTab">
Open search results in the same tab
@@ -109,6 +101,7 @@ const SearchSettings = (props: Props): JSX.Element => {
<option value={0}>False</option>
</select>
</InputGroup>
<InputGroup>
<label htmlFor="hideSearch">Hide search bar</label>
<select
@@ -121,6 +114,7 @@ const SearchSettings = (props: Props): JSX.Element => {
<option value={0}>False</option>
</select>
</InputGroup>
<InputGroup>
<label htmlFor="disableAutofocus">Disable search bar autofocus</label>
<select
@@ -133,6 +127,7 @@ const SearchSettings = (props: Props): JSX.Element => {
<option value={0}>False</option>
</select>
</InputGroup>
<Button>Save changes</Button>
</form>
@@ -142,18 +137,3 @@ const SearchSettings = (props: Props): JSX.Element => {
</Fragment>
);
};
const mapStateToProps = (state: GlobalState) => {
return {
loading: state.config.loading,
customQueries: state.config.customQueries,
config: state.config.config,
};
};
const actions = {
createNotification,
updateConfig,
};
export default connect(mapStateToProps, actions)(SearchSettings);

View File

@@ -1,4 +1,3 @@
//
import { NavLink, Link, Switch, Route } from 'react-router-dom';
// Typescript
@@ -8,21 +7,20 @@ import { Route as SettingsRoute } from '../../interfaces';
import classes from './Settings.module.css';
// Components
import Themer from '../Themer/Themer';
import WeatherSettings from './WeatherSettings/WeatherSettings';
import OtherSettings from './OtherSettings/OtherSettings';
import AppDetails from './AppDetails/AppDetails';
import StyleSettings from './StyleSettings/StyleSettings';
import SearchSettings from './SearchSettings/SearchSettings';
import { Themer } from '../Themer/Themer';
import { WeatherSettings } from './WeatherSettings/WeatherSettings';
import { OtherSettings } from './OtherSettings/OtherSettings';
import { AppDetails } from './AppDetails/AppDetails';
import { StyleSettings } from './StyleSettings/StyleSettings';
import { SearchSettings } from './SearchSettings/SearchSettings';
// UI
import { Container } from '../UI/Layout/Layout';
import Headline from '../UI/Headlines/Headline/Headline';
import { Container, Headline } from '../UI';
// Data
import { routes } from './settings.json';
const Settings = (): JSX.Element => {
export const Settings = (): JSX.Element => {
return (
<Container>
<Headline title="Settings" subtitle={<Link to="/">Go back</Link>} />
@@ -57,5 +55,3 @@ const Settings = (): JSX.Element => {
</Container>
);
};
export default Settings;

View File

@@ -2,54 +2,55 @@ import { useState, useEffect, ChangeEvent, FormEvent } from 'react';
import axios from 'axios';
// Redux
import { connect } from 'react-redux';
import { createNotification } from '../../../store/actions';
import { useDispatch } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../../../store';
// Typescript
import { ApiResponse, NewNotification } from '../../../interfaces';
import { ApiResponse } from '../../../interfaces';
// UI
import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
import Button from '../../UI/Buttons/Button/Button';
import { InputGroup, Button } from '../../UI';
interface ComponentProps {
createNotification: (notification: NewNotification) => void;
}
export const StyleSettings = (): JSX.Element => {
const dispatch = useDispatch();
const { createNotification } = bindActionCreators(actionCreators, dispatch);
const StyleSettings = (props: ComponentProps): JSX.Element => {
const [customStyles, setCustomStyles] = useState<string>('');
useEffect(() => {
axios.get<ApiResponse<string>>('/api/config/0/css')
.then(data => setCustomStyles(data.data.data))
.catch(err => console.log(err.response));
}, [])
axios
.get<ApiResponse<string>>('/api/config/0/css')
.then((data) => setCustomStyles(data.data.data))
.catch((err) => console.log(err.response));
}, []);
const inputChangeHandler = (e: ChangeEvent<HTMLTextAreaElement>) => {
e.preventDefault();
setCustomStyles(e.target.value);
}
};
const formSubmitHandler = (e: FormEvent) => {
e.preventDefault();
axios.put<ApiResponse<{}>>('/api/config/0/css', { styles: customStyles })
axios
.put<ApiResponse<{}>>('/api/config/0/css', { styles: customStyles })
.then(() => {
props.createNotification({
createNotification({
title: 'Success',
message: 'CSS saved. Reload page to see changes'
})
message: 'CSS saved. Reload page to see changes',
});
})
.catch(err => console.log(err.response));
}
.catch((err) => console.log(err.response));
};
return (
<form onSubmit={(e) => formSubmitHandler(e)}>
<InputGroup>
<label htmlFor='customStyles'>Custom CSS</label>
<label htmlFor="customStyles">Custom CSS</label>
<textarea
id='customStyles'
name='customStyles'
id="customStyles"
name="customStyles"
value={customStyles}
onChange={(e) => inputChangeHandler(e)}
spellCheck={false}
@@ -57,7 +58,5 @@ const StyleSettings = (props: ComponentProps): JSX.Element => {
</InputGroup>
<Button>Save CSS</Button>
</form>
)
}
export default connect(null, { createNotification })(StyleSettings);
);
};

View File

@@ -2,34 +2,29 @@ import { useState, ChangeEvent, useEffect, FormEvent } from 'react';
import axios from 'axios';
// Redux
import { connect } from 'react-redux';
import { createNotification, updateConfig } from '../../../store/actions';
import { useDispatch, useSelector } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../../../store';
// Typescript
import {
ApiResponse,
Config,
GlobalState,
NewNotification,
Weather,
WeatherForm,
} from '../../../interfaces';
import { ApiResponse, Weather, WeatherForm } from '../../../interfaces';
// UI
import InputGroup from '../../UI/Forms/InputGroup/InputGroup';
import Button from '../../UI/Buttons/Button/Button';
import { InputGroup, Button } from '../../UI';
// Utils
import { inputHandler, weatherSettingsTemplate } from '../../../utility';
import { State } from '../../../store/reducers';
interface ComponentProps {
createNotification: (notification: NewNotification) => void;
updateConfig: (formData: WeatherForm) => void;
loading: boolean;
config: Config;
}
export const WeatherSettings = (): JSX.Element => {
const { loading, config } = useSelector((state: State) => state.config);
const dispatch = useDispatch();
const { createNotification, updateConfig } = bindActionCreators(
actionCreators,
dispatch
);
const WeatherSettings = (props: ComponentProps): JSX.Element => {
// Initial state
const [formData, setFormData] = useState<WeatherForm>(
weatherSettingsTemplate
@@ -38,9 +33,9 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
// Get config
useEffect(() => {
setFormData({
...props.config,
...config,
});
}, [props.loading]);
}, [loading]);
// Form handler
const formSubmitHandler = async (e: FormEvent) => {
@@ -48,26 +43,26 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
// Check for api key input
if ((formData.lat || formData.long) && !formData.WEATHER_API_KEY) {
props.createNotification({
createNotification({
title: 'Warning',
message: 'API key is missing. Weather Module will NOT work',
});
}
// Save settings
await props.updateConfig(formData);
await updateConfig(formData);
// Update weather
axios
.get<ApiResponse<Weather>>('/api/weather/update')
.then(() => {
props.createNotification({
createNotification({
title: 'Success',
message: 'Weather updated',
});
})
.catch((err) => {
props.createNotification({
createNotification({
title: 'Error',
message: err.response.data.error,
});
@@ -108,6 +103,7 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
. Key is required for weather module to work.
</span>
</InputGroup>
<InputGroup>
<label htmlFor="lat">Location latitude</label>
<input
@@ -131,6 +127,7 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
</a>
</span>
</InputGroup>
<InputGroup>
<label htmlFor="long">Location longitude</label>
<input
@@ -144,6 +141,7 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
lang="en-150"
/>
</InputGroup>
<InputGroup>
<label htmlFor="isCelsius">Temperature unit</label>
<select
@@ -156,18 +154,8 @@ const WeatherSettings = (props: ComponentProps): JSX.Element => {
<option value={0}>Fahrenheit</option>
</select>
</InputGroup>
<Button>Save changes</Button>
</form>
);
};
const mapStateToProps = (state: GlobalState) => {
return {
loading: state.config.loading,
config: state.config.config,
};
};
export default connect(mapStateToProps, { createNotification, updateConfig })(
WeatherSettings
);