mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Created settings page and reducers to handle real-time updates config
This commit is contained in:
parent
7516ca8dd9
commit
41f885d8ec
9 changed files with 131 additions and 18 deletions
37
src/App.js
37
src/App.js
|
@ -1,24 +1,29 @@
|
||||||
import React from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Route, Switch } from 'react-router-dom';
|
import { Route, Switch } from 'react-router-dom';
|
||||||
import NotFound from './common/NotFound';
|
import NotFound from './common/NotFound';
|
||||||
import Settings from './settings/Settings';
|
|
||||||
import './App.scss';
|
import './App.scss';
|
||||||
|
|
||||||
const App = (MainHeader, Home, MenuLayout, CreateServer, EditServer) => () => (
|
const App = (MainHeader, Home, MenuLayout, CreateServer, EditServer, Settings) => ({ loadRealTimeUpdates }) => {
|
||||||
<div className="container-fluid app-container">
|
useEffect(() => {
|
||||||
<MainHeader />
|
loadRealTimeUpdates();
|
||||||
|
}, []);
|
||||||
|
|
||||||
<div className="app">
|
return (
|
||||||
<Switch>
|
<div className="container-fluid app-container">
|
||||||
<Route exact path="/" component={Home} />
|
<MainHeader />
|
||||||
<Route exact path="/server/create" component={CreateServer} />
|
|
||||||
<Route exact path="/server/:serverId/edit" component={EditServer} />
|
<div className="app">
|
||||||
<Route exact path="/settings" component={Settings} />
|
<Switch>
|
||||||
<Route path="/server/:serverId" component={MenuLayout} />
|
<Route exact path="/" component={Home} />
|
||||||
<Route component={NotFound} />
|
<Route exact path="/settings" component={Settings} />
|
||||||
</Switch>
|
<Route exact path="/server/create" component={CreateServer} />
|
||||||
|
<Route exact path="/server/:serverId/edit" component={EditServer} />
|
||||||
|
<Route path="/server/:serverId" component={MenuLayout} />
|
||||||
|
<Route component={NotFound} />
|
||||||
|
</Switch>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
);
|
};
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import provideVisitsServices from '../visits/services/provideServices';
|
||||||
import provideTagsServices from '../tags/services/provideServices';
|
import provideTagsServices from '../tags/services/provideServices';
|
||||||
import provideUtilsServices from '../utils/services/provideServices';
|
import provideUtilsServices from '../utils/services/provideServices';
|
||||||
import provideMercureServices from '../mercure/services/provideServices';
|
import provideMercureServices from '../mercure/services/provideServices';
|
||||||
|
import provideSettingsServices from '../settings/services/provideServices';
|
||||||
|
|
||||||
const bottle = new Bottle();
|
const bottle = new Bottle();
|
||||||
const { container } = bottle;
|
const { container } = bottle;
|
||||||
|
@ -27,7 +28,8 @@ const connect = (propsFromState, actionServiceNames = []) =>
|
||||||
actionServiceNames.reduce(mapActionService, {})
|
actionServiceNames.reduce(mapActionService, {})
|
||||||
);
|
);
|
||||||
|
|
||||||
bottle.serviceFactory('App', App, 'MainHeader', 'Home', 'MenuLayout', 'CreateServer', 'EditServer');
|
bottle.serviceFactory('App', App, 'MainHeader', 'Home', 'MenuLayout', 'CreateServer', 'EditServer', 'Settings');
|
||||||
|
bottle.decorator('App', connect(null, [ 'loadRealTimeUpdates' ]));
|
||||||
|
|
||||||
provideCommonServices(bottle, connect, withRouter);
|
provideCommonServices(bottle, connect, withRouter);
|
||||||
provideShortUrlsServices(bottle, connect);
|
provideShortUrlsServices(bottle, connect);
|
||||||
|
@ -36,5 +38,6 @@ provideTagsServices(bottle, connect);
|
||||||
provideVisitsServices(bottle, connect);
|
provideVisitsServices(bottle, connect);
|
||||||
provideUtilsServices(bottle);
|
provideUtilsServices(bottle);
|
||||||
provideMercureServices(bottle);
|
provideMercureServices(bottle);
|
||||||
|
provideSettingsServices(bottle, connect);
|
||||||
|
|
||||||
export default container;
|
export default container;
|
||||||
|
|
|
@ -14,6 +14,7 @@ import tagsListReducer from '../tags/reducers/tagsList';
|
||||||
import tagDeleteReducer from '../tags/reducers/tagDelete';
|
import tagDeleteReducer from '../tags/reducers/tagDelete';
|
||||||
import tagEditReducer from '../tags/reducers/tagEdit';
|
import tagEditReducer from '../tags/reducers/tagEdit';
|
||||||
import mercureInfoReducer from '../mercure/reducers/mercureInfo';
|
import mercureInfoReducer from '../mercure/reducers/mercureInfo';
|
||||||
|
import realTimeUpdatesReducer from '../settings/reducers/realTimeUpdates';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
servers: serversReducer,
|
servers: serversReducer,
|
||||||
|
@ -31,4 +32,5 @@ export default combineReducers({
|
||||||
tagDelete: tagDeleteReducer,
|
tagDelete: tagDeleteReducer,
|
||||||
tagEdit: tagEditReducer,
|
tagEdit: tagEditReducer,
|
||||||
mercureInfo: mercureInfoReducer,
|
mercureInfo: mercureInfoReducer,
|
||||||
|
realTimeUpdates: realTimeUpdatesReducer,
|
||||||
});
|
});
|
||||||
|
|
31
src/settings/RealTimeUpdates.js
Normal file
31
src/settings/RealTimeUpdates.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { Card, CardBody, CardHeader, UncontrolledTooltip } from 'reactstrap';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import Checkbox from '../utils/Checkbox';
|
||||||
|
import { RealTimeUpdatesType } from './reducers/realTimeUpdates';
|
||||||
|
|
||||||
|
const propTypes = {
|
||||||
|
realTimeUpdates: RealTimeUpdatesType,
|
||||||
|
setRealTimeUpdates: PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
const RealTimeUpdates = ({ realTimeUpdates, setRealTimeUpdates }) => (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>Real-time updates</CardHeader>
|
||||||
|
<CardBody>
|
||||||
|
<Checkbox checked={realTimeUpdates.enabled} onChange={setRealTimeUpdates}>
|
||||||
|
Enable real-time updates
|
||||||
|
<FontAwesomeIcon icon={faInfoCircle} className="ml-2" id="realTimeUpdatesInfo" />
|
||||||
|
</Checkbox>
|
||||||
|
<UncontrolledTooltip target="realTimeUpdatesInfo">
|
||||||
|
Enable or disable real-time updates, when using Shlink v2.2.0 or newer.
|
||||||
|
</UncontrolledTooltip>
|
||||||
|
</CardBody>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
|
||||||
|
RealTimeUpdates.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default RealTimeUpdates;
|
|
@ -1,6 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import NoMenuLayout from '../common/NoMenuLayout';
|
import NoMenuLayout from '../common/NoMenuLayout';
|
||||||
|
|
||||||
const Settings = () => <NoMenuLayout>Settings</NoMenuLayout>;
|
const Settings = (RealTimeUpdates) => () => (
|
||||||
|
<NoMenuLayout>
|
||||||
|
<RealTimeUpdates />
|
||||||
|
</NoMenuLayout>
|
||||||
|
);
|
||||||
|
|
||||||
export default Settings;
|
export default Settings;
|
||||||
|
|
32
src/settings/reducers/realTimeUpdates.js
Normal file
32
src/settings/reducers/realTimeUpdates.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { handleActions } from 'redux-actions';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
const LOAD_REAL_TIME_UPDATES = 'shlink/realTimeUpdates/LOAD_REAL_TIME_UPDATES';
|
||||||
|
|
||||||
|
export const RealTimeUpdatesType = PropTypes.shape({
|
||||||
|
enabled: PropTypes.bool.isRequired,
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
enabled: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default handleActions({
|
||||||
|
[LOAD_REAL_TIME_UPDATES]: (state, { enabled }) => ({ ...state, enabled }),
|
||||||
|
}, initialState);
|
||||||
|
|
||||||
|
export const setRealTimeUpdates = ({ updateSettings }, loadRealTimeUpdatesAction) => (enabled) => {
|
||||||
|
updateSettings({ realTimeUpdates: { enabled } });
|
||||||
|
|
||||||
|
return loadRealTimeUpdatesAction();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadRealTimeUpdates = ({ loadSettings }) => () => {
|
||||||
|
const { realTimeUpdates = {} } = loadSettings();
|
||||||
|
const { enabled = true } = realTimeUpdates;
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: LOAD_REAL_TIME_UPDATES,
|
||||||
|
enabled,
|
||||||
|
};
|
||||||
|
};
|
14
src/settings/services/SettingsService.js
Normal file
14
src/settings/services/SettingsService.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
const SETTINGS_STORAGE_KEY = 'settings';
|
||||||
|
|
||||||
|
export default class SettingsService {
|
||||||
|
constructor(storage) {
|
||||||
|
this.storage = storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSettings = () => this.storage.get(SETTINGS_STORAGE_KEY) || {};
|
||||||
|
|
||||||
|
updateSettings = (settingsToUpdate) => this.storage.set(SETTINGS_STORAGE_KEY, {
|
||||||
|
...this.loadSettings(),
|
||||||
|
...settingsToUpdate,
|
||||||
|
})
|
||||||
|
}
|
21
src/settings/services/provideServices.js
Normal file
21
src/settings/services/provideServices.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import RealTimeUpdates from '../RealTimeUpdates';
|
||||||
|
import Settings from '../Settings';
|
||||||
|
import { loadRealTimeUpdates, setRealTimeUpdates } from '../reducers/realTimeUpdates';
|
||||||
|
import SettingsService from './SettingsService';
|
||||||
|
|
||||||
|
const provideServices = (bottle, connect) => {
|
||||||
|
// Components
|
||||||
|
bottle.serviceFactory('Settings', Settings, 'RealTimeUpdates');
|
||||||
|
|
||||||
|
bottle.serviceFactory('RealTimeUpdates', () => RealTimeUpdates);
|
||||||
|
bottle.decorator('RealTimeUpdates', connect([ 'realTimeUpdates' ], [ 'setRealTimeUpdates' ]));
|
||||||
|
|
||||||
|
// Services
|
||||||
|
bottle.service('SettingsService', SettingsService, 'Storage');
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
bottle.serviceFactory('setRealTimeUpdates', setRealTimeUpdates, 'SettingsService', 'loadRealTimeUpdates');
|
||||||
|
bottle.serviceFactory('loadRealTimeUpdates', loadRealTimeUpdates, 'SettingsService');
|
||||||
|
};
|
||||||
|
|
||||||
|
export default provideServices;
|
|
@ -21,6 +21,7 @@ describe('<App />', () => {
|
||||||
const routes = wrapper.find(Route);
|
const routes = wrapper.find(Route);
|
||||||
const expectedPaths = [
|
const expectedPaths = [
|
||||||
'/',
|
'/',
|
||||||
|
'/settings',
|
||||||
'/server/create',
|
'/server/create',
|
||||||
'/server/:serverId/edit',
|
'/server/:serverId/edit',
|
||||||
'/server/:serverId',
|
'/server/:serverId',
|
||||||
|
|
Loading…
Reference in a new issue