diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad9b23ea..69b25f6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
* If it works, it will setup the necessary `EventSource`s, dispatching redux actions when an event is pushed, which will in turn update the UI.
* If it fails, it will assume it is either not configured or not supported by the Shlink version.
+* [#253](https://github.com/shlinkio/shlink-web-client/issues/253) Created new settings page that will be used to define customizations in the app.
+
#### Changed
* *Nothing*
diff --git a/src/App.js b/src/App.js
index 4cecebff..fd6c859c 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,22 +1,29 @@
-import React from 'react';
+import React, { useEffect } from 'react';
import { Route, Switch } from 'react-router-dom';
-import './App.scss';
import NotFound from './common/NotFound';
+import './App.scss';
-const App = (MainHeader, Home, MenuLayout, CreateServer, EditServer) => () => (
-
-
+const App = (MainHeader, Home, MenuLayout, CreateServer, EditServer, Settings) => ({ loadRealTimeUpdates }) => {
+ useEffect(() => {
+ loadRealTimeUpdates();
+ }, []);
-
-
-
-
-
-
-
-
+ return (
+
-
-);
+ );
+};
export default App;
diff --git a/src/common/MainHeader.js b/src/common/MainHeader.js
index 5e582212..eecaf464 100644
--- a/src/common/MainHeader.js
+++ b/src/common/MainHeader.js
@@ -1,37 +1,28 @@
-import { faPlus as plusIcon, faChevronDown as arrowIcon } from '@fortawesome/free-solid-svg-icons';
+import { faPlus as plusIcon, faChevronDown as arrowIcon, faCogs as cogsIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import React from 'react';
+import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
-import classnames from 'classnames';
+import classNames from 'classnames';
import PropTypes from 'prop-types';
+import { useToggle } from '../utils/helpers/hooks';
import shlinkLogo from './shlink-logo-white.png';
import './MainHeader.scss';
-const MainHeader = (ServersDropdown) => class MainHeader extends React.Component {
- static propTypes = {
- location: PropTypes.object,
- };
+const propTypes = {
+ location: PropTypes.object,
+};
- state = { isOpen: false };
- handleToggle = () => {
- this.setState(({ isOpen }) => ({
- isOpen: !isOpen,
- }));
- };
+const MainHeader = (ServersDropdown) => {
+ const MainHeaderComp = ({ location }) => {
+ const [ isOpen, toggleOpen, , close ] = useToggle();
+ const { pathname } = location;
- componentDidUpdate(prevProps) {
- if (this.props.location !== prevProps.location) {
- this.setState({ isOpen: false });
- }
- }
+ useEffect(close, [ location ]);
- render() {
- const { location } = this.props;
const createServerPath = '/server/create';
- const toggleClass = classnames('main-header__toggle-icon', {
- 'main-header__toggle-icon--opened': this.state.isOpen,
- });
+ const settingsPath = '/settings';
+ const toggleClass = classNames('main-header__toggle-icon', { 'main-header__toggle-icon--opened': isOpen });
return (
@@ -39,18 +30,19 @@ const MainHeader = (ServersDropdown) => class MainHeader extends React.Component
Shlink
-
+
-
+
-
+
+ Settings
+
+
+
+
Add server
@@ -59,7 +51,11 @@ const MainHeader = (ServersDropdown) => class MainHeader extends React.Component
);
- }
+ };
+
+ MainHeaderComp.propTypes = propTypes;
+
+ return MainHeaderComp;
};
export default MainHeader;
diff --git a/src/common/NoMenuLayout.js b/src/common/NoMenuLayout.js
new file mode 100644
index 00000000..f63ca0cd
--- /dev/null
+++ b/src/common/NoMenuLayout.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import './NoMenuLayout.scss';
+
+const propTypes = {
+ children: PropTypes.node,
+};
+
+const NoMenuLayout = ({ children }) =>
{children}
;
+
+NoMenuLayout.propTypes = propTypes;
+
+export default NoMenuLayout;
diff --git a/src/common/NoMenuLayout.scss b/src/common/NoMenuLayout.scss
new file mode 100644
index 00000000..157217ac
--- /dev/null
+++ b/src/common/NoMenuLayout.scss
@@ -0,0 +1,3 @@
+.no-menu-wrapper {
+ padding: 40px 20px;
+}
diff --git a/src/container/index.js b/src/container/index.js
index d634dffb..95383b13 100644
--- a/src/container/index.js
+++ b/src/container/index.js
@@ -10,6 +10,7 @@ import provideVisitsServices from '../visits/services/provideServices';
import provideTagsServices from '../tags/services/provideServices';
import provideUtilsServices from '../utils/services/provideServices';
import provideMercureServices from '../mercure/services/provideServices';
+import provideSettingsServices from '../settings/services/provideServices';
const bottle = new Bottle();
const { container } = bottle;
@@ -27,7 +28,8 @@ const connect = (propsFromState, actionServiceNames = []) =>
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);
provideShortUrlsServices(bottle, connect);
@@ -36,5 +38,6 @@ provideTagsServices(bottle, connect);
provideVisitsServices(bottle, connect);
provideUtilsServices(bottle);
provideMercureServices(bottle);
+provideSettingsServices(bottle, connect);
export default container;
diff --git a/src/mercure/helpers/index.js b/src/mercure/helpers/index.js
index 0cdd367c..fd69ce08 100644
--- a/src/mercure/helpers/index.js
+++ b/src/mercure/helpers/index.js
@@ -1,9 +1,10 @@
import { EventSourcePolyfill as EventSource } from 'event-source-polyfill';
-export const bindToMercureTopic = (mercureInfo, topic, onMessage, onTokenExpired) => () => {
+export const bindToMercureTopic = (mercureInfo, realTimeUpdates, topic, onMessage, onTokenExpired) => () => {
+ const { enabled } = realTimeUpdates;
const { mercureHubUrl, token, loading, error } = mercureInfo;
- if (loading || error) {
+ if (!enabled || loading || error) {
return undefined;
}
diff --git a/src/reducers/index.js b/src/reducers/index.js
index 8b96ede8..8e81e18a 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -14,6 +14,7 @@ import tagsListReducer from '../tags/reducers/tagsList';
import tagDeleteReducer from '../tags/reducers/tagDelete';
import tagEditReducer from '../tags/reducers/tagEdit';
import mercureInfoReducer from '../mercure/reducers/mercureInfo';
+import realTimeUpdatesReducer from '../settings/reducers/realTimeUpdates';
export default combineReducers({
servers: serversReducer,
@@ -31,4 +32,5 @@ export default combineReducers({
tagDelete: tagDeleteReducer,
tagEdit: tagEditReducer,
mercureInfo: mercureInfoReducer,
+ realTimeUpdates: realTimeUpdatesReducer,
});
diff --git a/src/servers/CreateServer.js b/src/servers/CreateServer.js
index 235ecd82..91e11862 100644
--- a/src/servers/CreateServer.js
+++ b/src/servers/CreateServer.js
@@ -1,8 +1,9 @@
import React, { useEffect } from 'react';
import { v4 as uuid } from 'uuid';
import PropTypes from 'prop-types';
-import './CreateServer.scss';
+import NoMenuLayout from '../common/NoMenuLayout';
import { ServerForm } from './helpers/ServerForm';
+import './CreateServer.scss';
const SHOW_IMPORT_MSG_TIME = 4000;
const propTypes = {
@@ -29,7 +30,7 @@ const CreateServer = (ImportServersBtn, useStateFlagTimeout) => {
}, []);
return (
-
+
Create server
@@ -44,7 +45,7 @@ const CreateServer = (ImportServersBtn, useStateFlagTimeout) => {
)}
-
+
);
};
diff --git a/src/servers/CreateServer.scss b/src/servers/CreateServer.scss
index 764520c7..cfba848d 100644
--- a/src/servers/CreateServer.scss
+++ b/src/servers/CreateServer.scss
@@ -1,9 +1,5 @@
@import '../utils/base';
-.create-server {
- padding: 40px 20px;
-}
-
.create-server__label {
font-weight: 700;
cursor: pointer;
diff --git a/src/servers/EditServer.js b/src/servers/EditServer.js
index c4d4b0eb..6a6b89b6 100644
--- a/src/servers/EditServer.js
+++ b/src/servers/EditServer.js
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import NoMenuLayout from '../common/NoMenuLayout';
import { ServerForm } from './helpers/ServerForm';
import { withSelectedServer } from './helpers/withSelectedServer';
import { serverType } from './prop-types';
@@ -20,11 +21,11 @@ export const EditServer = (ServerError) => {
};
return (
-