diff --git a/src/common/AsideMenu.js b/src/common/AsideMenu.js index 80f0cd2c..56e7d10c 100644 --- a/src/common/AsideMenu.js +++ b/src/common/AsideMenu.js @@ -24,7 +24,7 @@ const propTypes = { showOnMobile: PropTypes.bool, }; -const AsideMenu = (DeleteServerButton, ShlinkVersions) => { +const AsideMenu = (DeleteServerButton) => { const AsideMenu = ({ selectedServer, className, showOnMobile }) => { const serverId = selectedServer ? selectedServer.id : ''; const asideClass = classNames('aside-menu', className, { @@ -49,7 +49,6 @@ const AsideMenu = (DeleteServerButton, ShlinkVersions) => { Manage tags - diff --git a/src/common/MenuLayout.js b/src/common/MenuLayout.js index 0038e907..23b881fe 100644 --- a/src/common/MenuLayout.js +++ b/src/common/MenuLayout.js @@ -61,15 +61,17 @@ const MenuLayout = (TagsList, ShortUrls, AsideMenu, CreateShortUrl, ShortUrlVisi
setShowSidebar(false)}> - - - - - - } - /> - +
+ + + + + + } + /> + +
diff --git a/src/common/ShlinkVersions.js b/src/common/ShlinkVersions.js index 37726936..c83e21cb 100644 --- a/src/common/ShlinkVersions.js +++ b/src/common/ShlinkVersions.js @@ -1,14 +1,17 @@ import React from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames'; import { serverType } from '../servers/prop-types'; const propTypes = { selectedServer: serverType, + className: PropTypes.string, }; -const ShlinkVersions = ({ selectedServer }) => { - const { version } = selectedServer; +const ShlinkVersions = ({ selectedServer, className }) => { + const { printableVersion } = selectedServer; - return Server: v{version}; + return Client: v2.3.1 / Server: {printableVersion}; }; ShlinkVersions.propTypes = propTypes; diff --git a/src/common/services/provideServices.js b/src/common/services/provideServices.js index fdb25687..d5861a2d 100644 --- a/src/common/services/provideServices.js +++ b/src/common/services/provideServices.js @@ -31,7 +31,7 @@ const provideServices = (bottle, connect, withRouter) => { bottle.decorator('MenuLayout', connect([ 'selectedServer', 'shortUrlsListParams' ], [ 'selectServer' ])); bottle.decorator('MenuLayout', withRouter); - bottle.serviceFactory('AsideMenu', AsideMenu, 'DeleteServerButton', 'ShlinkVersions'); + bottle.serviceFactory('AsideMenu', AsideMenu, 'DeleteServerButton'); bottle.serviceFactory('ShlinkVersions', () => ShlinkVersions); bottle.decorator('ShlinkVersions', connect([ 'selectedServer' ])); diff --git a/src/servers/DeleteServerButton.js b/src/servers/DeleteServerButton.js index 8d0caaff..2bc51601 100644 --- a/src/servers/DeleteServerButton.js +++ b/src/servers/DeleteServerButton.js @@ -9,7 +9,7 @@ const propTypes = { className: PropTypes.string, }; -const DeleteServerButton = (DeleteServerModal) => { +const DeleteServerButton = (DeleteServerModal, ShlinkVersions) => { const DeleteServerButtonComp = ({ server, className }) => { const [ isModalOpen, setModalOpen ] = useState(false); @@ -20,6 +20,8 @@ const DeleteServerButton = (DeleteServerModal) => { Remove this server + + setModalOpen(!isModalOpen)} diff --git a/src/servers/prop-types/index.js b/src/servers/prop-types/index.js index 35d43e49..caec6f92 100644 --- a/src/servers/prop-types/index.js +++ b/src/servers/prop-types/index.js @@ -6,4 +6,5 @@ export const serverType = PropTypes.shape({ url: PropTypes.string, apiKey: PropTypes.string, version: PropTypes.string, + printableVersion: PropTypes.string, }); diff --git a/src/servers/reducers/selectedServer.js b/src/servers/reducers/selectedServer.js index 29dfb4e9..095f9342 100644 --- a/src/servers/reducers/selectedServer.js +++ b/src/servers/reducers/selectedServer.js @@ -1,4 +1,5 @@ import { createAction, handleActions } from 'redux-actions'; +import { pipe } from 'ramda'; import { resetShortUrlParams } from '../../short-urls/reducers/shortUrlsListParams'; import { versionIsValidSemVer } from '../../utils/utils'; @@ -12,6 +13,11 @@ export const LATEST_VERSION_CONSTRAINT = 'latest'; /* eslint-enable padding-line-between-statements */ const initialState = null; +const versionToSemVer = pipe( + (version) => version === LATEST_VERSION_CONSTRAINT ? MAX_FALLBACK_VERSION : version, + (version) => !versionIsValidSemVer(version) ? MIN_FALLBACK_VERSION : version +); +const versionToPrintable = (version) => !versionIsValidSemVer(version) ? version : `v${version}`; export const resetSelectedServer = createAction(RESET_SELECTED_SERVER); @@ -20,16 +26,14 @@ export const selectServer = ({ findServerById }, buildShlinkApiClient) => (serve const selectedServer = findServerById(serverId); const { health } = buildShlinkApiClient(selectedServer); - const version = await health() - .then(({ version }) => version === LATEST_VERSION_CONSTRAINT ? MAX_FALLBACK_VERSION : version) - .then((version) => !versionIsValidSemVer(version) ? MIN_FALLBACK_VERSION : version) - .catch(() => MIN_FALLBACK_VERSION); + const { version } = await health().catch(() => MIN_FALLBACK_VERSION); dispatch({ type: SELECT_SERVER, selectedServer: { ...selectedServer, - version, + version: versionToSemVer(version), + printableVersion: versionToPrintable(version), }, }); }; diff --git a/src/servers/services/provideServices.js b/src/servers/services/provideServices.js index e4231e87..d29fddc3 100644 --- a/src/servers/services/provideServices.js +++ b/src/servers/services/provideServices.js @@ -24,7 +24,7 @@ const provideServices = (bottle, connect, withRouter) => { bottle.decorator('DeleteServerModal', withRouter); bottle.decorator('DeleteServerModal', connect(null, [ 'deleteServer' ])); - bottle.serviceFactory('DeleteServerButton', DeleteServerButton, 'DeleteServerModal'); + bottle.serviceFactory('DeleteServerButton', DeleteServerButton, 'DeleteServerModal', 'ShlinkVersions'); bottle.serviceFactory('ImportServersBtn', ImportServersBtn, 'ServersImporter'); bottle.decorator('ImportServersBtn', connect(null, [ 'createServers' ])); diff --git a/src/short-urls/CreateShortUrl.js b/src/short-urls/CreateShortUrl.js index d1ace683..e8fe9742 100644 --- a/src/short-urls/CreateShortUrl.js +++ b/src/short-urls/CreateShortUrl.js @@ -77,83 +77,81 @@ const CreateShortUrl = ( const disableDomain = isEmpty(currentServerVersion) || compareVersions(currentServerVersion, '<', '1.19.0-beta.1'); return ( -
-
+ +
+ this.setState({ longUrl: e.target.value })} + /> +
+ +
- this.setState({ longUrl: e.target.value })} - /> +
- -
- +
+
+ {renderOptionalInput('customSlug', 'Custom slug')}
- -
-
- {renderOptionalInput('customSlug', 'Custom slug')} -
-
- {renderOptionalInput('domain', 'Domain', 'text', { - disabled: disableDomain, - ...disableDomain && { title: 'Shlink 1.19.0 or higher is required to be able to provide the domain' }, - })} -
+
+ {renderOptionalInput('domain', 'Domain', 'text', { + disabled: disableDomain, + ...disableDomain && { title: 'Shlink 1.19.0 or higher is required to be able to provide the domain' }, + })}
- -
-
- {renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })} -
-
- {renderDateInput('validSince', 'Enabled since...', { maxDate: this.state.validUntil })} -
-
- {renderDateInput('validUntil', 'Enabled until...', { minDate: this.state.validSince })} -
-
- - -
- this.setState({ findIfExists })} - > - Use existing URL if found - - -
-
- - -
- -
- - -
+
+
+ {renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })} +
+
+ {renderDateInput('validSince', 'Enabled since...', { maxDate: this.state.validUntil })} +
+
+ {renderDateInput('validUntil', 'Enabled until...', { minDate: this.state.validSince })} +
+
+ + +
+ this.setState({ findIfExists })} + > + Use existing URL if found + + +
+
+ + +
+ + +
+ + + ); } }; diff --git a/src/short-urls/ShortUrls.js b/src/short-urls/ShortUrls.js index 3bfeec46..3efe9868 100644 --- a/src/short-urls/ShortUrls.js +++ b/src/short-urls/ShortUrls.js @@ -19,11 +19,11 @@ const ShortUrls = (SearchBar, ShortUrlsList) => { const urlsListKey = `${serverId}_${page}`; return ( -
+
-
+ ); }; diff --git a/src/tags/TagsList.js b/src/tags/TagsList.js index e9964c22..9cee6953 100644 --- a/src/tags/TagsList.js +++ b/src/tags/TagsList.js @@ -69,14 +69,14 @@ const TagsList = (TagCard) => class TagsList extends React.Component { const { filterTags } = this.props; return ( -
+ {!this.props.tagsList.loading && }
{this.renderContent()}
-
+ ); } }; diff --git a/src/utils/utils.js b/src/utils/utils.js index 48af3f55..da40702d 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -2,7 +2,7 @@ import L from 'leaflet'; import marker2x from 'leaflet/dist/images/marker-icon-2x.png'; import marker from 'leaflet/dist/images/marker-icon.png'; import markerShadow from 'leaflet/dist/images/marker-shadow.png'; -import { range } from 'ramda'; +import { identity, memoizeWith, range } from 'ramda'; import { useState } from 'react'; import { compare } from 'compare-versions'; @@ -59,13 +59,13 @@ export const compareVersions = (firstVersion, operator, secondVersion) => compar operator ); -export const versionIsValidSemVer = (version) => { +export const versionIsValidSemVer = memoizeWith(identity, (version) => { try { return compareVersions(version, '=', version); } catch (e) { return false; } -}; +}); export const formatDate = (format = 'YYYY-MM-DD') => (date) => date && date.format ? date.format(format) : date; diff --git a/src/visits/ShortUrlVisits.js b/src/visits/ShortUrlVisits.js index e235ffef..a0875b0b 100644 --- a/src/visits/ShortUrlVisits.js +++ b/src/visits/ShortUrlVisits.js @@ -133,7 +133,7 @@ const ShortUrlVisits = ( const setDate = (dateField) => (date) => this.setState({ [dateField]: date }, this.loadVisits); return ( -
+
@@ -148,7 +148,7 @@ const ShortUrlVisits = (
{renderVisitsContent()}
-
+ ); } };