From 67a23bfe33f76ed7f58a60ed0aadd2b3f5159d43 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 5 Oct 2019 09:02:02 +0200 Subject: [PATCH 1/6] Added domain input to create short url form --- src/short-urls/CreateShortUrl.js | 22 ++++++++++++++++---- src/short-urls/UseExistingIfFoundInfoIcon.js | 5 +---- test/short-urls/CreateShortUrl.test.js | 3 +++ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/short-urls/CreateShortUrl.js b/src/short-urls/CreateShortUrl.js index 38d1d4be..84ab63b9 100644 --- a/src/short-urls/CreateShortUrl.js +++ b/src/short-urls/CreateShortUrl.js @@ -1,6 +1,6 @@ import { faAngleDoubleDown as downIcon, faAngleDoubleUp as upIcon } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { assoc, dissoc, isNil, pipe, replace, trim } from 'ramda'; +import { assoc, dissoc, isEmpty, isNil, pipe, replace, trim } from 'ramda'; import React from 'react'; import { Collapse } from 'reactstrap'; import * as PropTypes from 'prop-types'; @@ -23,6 +23,7 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort longUrl: '', tags: [], customSlug: undefined, + domain: undefined, validSince: undefined, validUntil: undefined, maxVisits: undefined, @@ -89,15 +90,25 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort
{renderOptionalInput('customSlug', 'Custom slug')} - {renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
+ {renderOptionalInput('domain', 'Domain', 'text')} +
+
+ +
+
+ {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 })}
-
+
class CreateShort   {this.state.moreOptionsVisible ? 'Less' : 'More'} options -
diff --git a/src/short-urls/UseExistingIfFoundInfoIcon.js b/src/short-urls/UseExistingIfFoundInfoIcon.js index 7e155ac4..d91b0e24 100644 --- a/src/short-urls/UseExistingIfFoundInfoIcon.js +++ b/src/short-urls/UseExistingIfFoundInfoIcon.js @@ -20,7 +20,7 @@ const renderInfoModal = (isOpen, toggle) => ( -
- Important: This feature will be ignored while using a Shlink version older than v1.16.0. -
); diff --git a/test/short-urls/CreateShortUrl.test.js b/test/short-urls/CreateShortUrl.test.js index f7b08096..6d281b7c 100644 --- a/test/short-urls/CreateShortUrl.test.js +++ b/test/short-urls/CreateShortUrl.test.js @@ -32,6 +32,7 @@ describe('', () => { const urlInput = wrapper.find('.form-control-lg'); const tagsInput = wrapper.find(TagsSelector); const customSlugInput = wrapper.find('#customSlug'); + const domain = wrapper.find('#domain'); const maxVisitsInput = wrapper.find('#maxVisits'); const dateInputs = wrapper.find(DateInput); const validSinceInput = dateInputs.at(0); @@ -40,6 +41,7 @@ describe('', () => { urlInput.simulate('change', { target: { value: 'https://long-domain.com/foo/bar' } }); tagsInput.simulate('change', [ 'tag_foo', 'tag_bar' ]); customSlugInput.simulate('change', { target: { value: 'my-slug' } }); + domain.simulate('change', { target: { value: 'example.com' } }); maxVisitsInput.simulate('change', { target: { value: '20' } }); validSinceInput.simulate('change', validSince); validUntilInput.simulate('change', validUntil); @@ -53,6 +55,7 @@ describe('', () => { longUrl: 'https://long-domain.com/foo/bar', tags: [ 'tag_foo', 'tag_bar' ], customSlug: 'my-slug', + domain: 'example.com', validSince: validSince.format(), validUntil: validUntil.format(), maxVisits: '20', From 4120d0922015c07aa2ebb85aa123173b1d7be49a Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 5 Oct 2019 10:20:33 +0200 Subject: [PATCH 2/6] Loaded version of selected server and created component to filter content based on that version --- package-lock.json | 5 ++++ package.json | 1 + src/servers/reducers/selectedServer.js | 9 +++++-- src/servers/services/provideServices.js | 2 +- src/short-urls/CreateShortUrl.js | 28 +++++++++++++------- src/short-urls/UseExistingIfFoundInfoIcon.js | 4 +-- src/short-urls/services/provideServices.js | 2 +- src/utils/ForVersion.js | 19 +++++++++++++ src/utils/services/ShlinkApiClient.js | 2 ++ src/utils/services/ShlinkApiClientBuilder.js | 6 +++-- test/servers/reducers/selectedServer.test.js | 14 +++++++--- 11 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 src/utils/ForVersion.js diff --git a/package-lock.json b/package-lock.json index 1aad2d52..d0b8d605 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4951,6 +4951,11 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "compare-versions": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.5.1.tgz", + "integrity": "sha512-9fGPIB7C6AyM18CJJBHt5EnCZDG3oiTJYy0NjfIAGjKpzv0tkxWko7TNQHF5ymqm7IH03tqmeuBxtvD+Izh6mg==" + }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz", diff --git a/package.json b/package.json index 09dc06f4..e7195a3d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "bottlejs": "^1.7.1", "chart.js": "^2.7.2", "classnames": "^2.2.6", + "compare-versions": "^3.5.1", "csvjson": "^5.1.0", "leaflet": "^1.4.0", "moment": "^2.22.2", diff --git a/src/servers/reducers/selectedServer.js b/src/servers/reducers/selectedServer.js index bd14e80f..cef75410 100644 --- a/src/servers/reducers/selectedServer.js +++ b/src/servers/reducers/selectedServer.js @@ -10,14 +10,19 @@ const initialState = null; export const resetSelectedServer = createAction(RESET_SELECTED_SERVER); -export const selectServer = ({ findServerById }) => (serverId) => (dispatch) => { +export const selectServer = ({ findServerById }, buildShlinkApiClient) => (serverId) => async (dispatch) => { dispatch(resetShortUrlParams()); const selectedServer = findServerById(serverId); + const { health } = await buildShlinkApiClient(selectedServer); + const { version } = await health(); dispatch({ type: SELECT_SERVER, - selectedServer, + selectedServer: { + ...selectedServer, + version, + }, }); }; diff --git a/src/servers/services/provideServices.js b/src/servers/services/provideServices.js index 0b135491..12755474 100644 --- a/src/servers/services/provideServices.js +++ b/src/servers/services/provideServices.js @@ -34,7 +34,7 @@ const provideServices = (bottle, connect, withRouter) => { bottle.service('ServersExporter', ServersExporter, 'ServersService', 'window', 'csvjson'); // Actions - bottle.serviceFactory('selectServer', selectServer, 'ServersService'); + bottle.serviceFactory('selectServer', selectServer, 'ServersService', 'buildShlinkApiClient'); bottle.serviceFactory('createServer', createServer, 'ServersService', 'listServers'); bottle.serviceFactory('createServers', createServers, 'ServersService', 'listServers'); bottle.serviceFactory('deleteServer', deleteServer, 'ServersService', 'listServers'); diff --git a/src/short-urls/CreateShortUrl.js b/src/short-urls/CreateShortUrl.js index 84ab63b9..4a2aa168 100644 --- a/src/short-urls/CreateShortUrl.js +++ b/src/short-urls/CreateShortUrl.js @@ -6,6 +6,8 @@ import { Collapse } from 'reactstrap'; import * as PropTypes from 'prop-types'; import DateInput from '../utils/DateInput'; import Checkbox from '../utils/Checkbox'; +import ForVersion from '../utils/ForVersion'; +import { serverType } from '../servers/prop-types'; import { createShortUrlResultType } from './reducers/shortUrlCreation'; import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon'; @@ -17,6 +19,7 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort createShortUrl: PropTypes.func, shortUrlCreationResult: createShortUrlResultType, resetCreateShortUrl: PropTypes.func, + selectedServer: serverType, }; state = { @@ -108,16 +111,21 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort
-
- this.setState({ findIfExists })} - > - Use existing URL if found - - -
+ +
+ this.setState({ findIfExists })} + > + Use existing URL if found + + +
+
diff --git a/src/short-urls/UseExistingIfFoundInfoIcon.js b/src/short-urls/UseExistingIfFoundInfoIcon.js index d91b0e24..851f04a5 100644 --- a/src/short-urls/UseExistingIfFoundInfoIcon.js +++ b/src/short-urls/UseExistingIfFoundInfoIcon.js @@ -23,8 +23,8 @@ const renderInfoModal = (isOpen, toggle) => ( if none is found.
  • - When long URL and custom slug are provided: Same as in previous case, but it will try to match the short URL - using both the long URL and the slug. + When long URL and custom slug and/or domain are provided: Same as in previous case, but it will try to match + the short URL using both the long URL and the slug, the long URL and the domain, or the three of them.
    If the slug is being used by another long URL, an error will be returned.
  • diff --git a/src/short-urls/services/provideServices.js b/src/short-urls/services/provideServices.js index 05518828..c0fe583b 100644 --- a/src/short-urls/services/provideServices.js +++ b/src/short-urls/services/provideServices.js @@ -39,7 +39,7 @@ const provideServices = (bottle, connect) => { bottle.serviceFactory('CreateShortUrl', CreateShortUrl, 'TagsSelector', 'CreateShortUrlResult'); bottle.decorator( 'CreateShortUrl', - connect([ 'shortUrlCreationResult' ], [ 'createShortUrl', 'resetCreateShortUrl' ]) + connect([ 'shortUrlCreationResult', 'selectedServer' ], [ 'createShortUrl', 'resetCreateShortUrl' ]) ); bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal); diff --git a/src/utils/ForVersion.js b/src/utils/ForVersion.js new file mode 100644 index 00000000..10f9befa --- /dev/null +++ b/src/utils/ForVersion.js @@ -0,0 +1,19 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { compare } from 'compare-versions'; +import { isEmpty } from 'ramda'; + +const propTypes = { + minVersion: PropTypes.string.isRequired, + currentServerVersion: PropTypes.string.isRequired, + children: PropTypes.node.isRequired, +}; + +const ForVersion = ({ minVersion, currentServerVersion, children }) => + isEmpty(currentServerVersion) || compare(minVersion, currentServerVersion, '>') + ? null + : {children}; + +ForVersion.propTypes = propTypes; + +export default ForVersion; diff --git a/src/utils/services/ShlinkApiClient.js b/src/utils/services/ShlinkApiClient.js index 42d27512..2884b721 100644 --- a/src/utils/services/ShlinkApiClient.js +++ b/src/utils/services/ShlinkApiClient.js @@ -50,6 +50,8 @@ export default class ShlinkApiClient { this._performRequest('/tags', 'PUT', {}, { oldName, newName }) .then(() => ({ oldName, newName })); + health = () => this._performRequest('/health', 'GET').then((resp) => resp.data); + _performRequest = async (url, method = 'GET', query = {}, body = {}) => await this.axios({ method, diff --git a/src/utils/services/ShlinkApiClientBuilder.js b/src/utils/services/ShlinkApiClientBuilder.js index c239bbc6..be170789 100644 --- a/src/utils/services/ShlinkApiClientBuilder.js +++ b/src/utils/services/ShlinkApiClientBuilder.js @@ -13,8 +13,10 @@ const getSelectedServerFromState = async (getState) => { return selectedServer; }; -const buildShlinkApiClient = (axios) => async (getState) => { - const { url, apiKey } = await getSelectedServerFromState(getState); +const buildShlinkApiClient = (axios) => async (getStateOrSelectedServer) => { + const { url, apiKey } = typeof getStateOrSelectedServer === 'function' + ? await getSelectedServerFromState(getStateOrSelectedServer) + : getStateOrSelectedServer; const clientKey = `${url}_${apiKey}`; if (!apiClients[clientKey]) { diff --git a/test/servers/reducers/selectedServer.test.js b/test/servers/reducers/selectedServer.test.js index dcab78e6..63f2d87a 100644 --- a/test/servers/reducers/selectedServer.test.js +++ b/test/servers/reducers/selectedServer.test.js @@ -29,22 +29,30 @@ describe('selectedServerReducer', () => { const selectedServer = { id: serverId, }; + const version = '1.19.0'; const ServersServiceMock = { findServerById: jest.fn(() => selectedServer), }; + const apiClientMock = { + health: jest.fn().mockResolvedValue({ version }), + }; afterEach(() => { ServersServiceMock.findServerById.mockClear(); }); - it('dispatches proper actions', () => { + it('dispatches proper actions', async () => { const dispatch = jest.fn(); + const expectedSelectedServer = { + ...selectedServer, + version, + }; - selectServer(ServersServiceMock)(serverId)(dispatch); + await selectServer(ServersServiceMock, async () => apiClientMock)(serverId)(dispatch); expect(dispatch).toHaveBeenCalledTimes(2); expect(dispatch).toHaveBeenNthCalledWith(1, { type: RESET_SHORT_URL_PARAMS }); - expect(dispatch).toHaveBeenNthCalledWith(2, { type: SELECT_SERVER, selectedServer }); + expect(dispatch).toHaveBeenNthCalledWith(2, { type: SELECT_SERVER, selectedServer: expectedSelectedServer }); }); it('invokes dependencies', () => { From 6d996baf5db67a6cb10f310565fb77c40892eb25 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 5 Oct 2019 10:40:32 +0200 Subject: [PATCH 3/6] Added tests for new logics --- src/utils/services/ShlinkApiClient.js | 5 ++- test/utils/ForVersion.test.js | 43 +++++++++++++++++++ test/utils/services/ShlinkApiClient.test.js | 16 +++++++ .../services/ShlinkApiClientBuilder.test.js | 10 +++++ 4 files changed, 72 insertions(+), 2 deletions(-) create mode 100644 test/utils/ForVersion.test.js diff --git a/src/utils/services/ShlinkApiClient.js b/src/utils/services/ShlinkApiClient.js index 2884b721..ac0b6fbf 100644 --- a/src/utils/services/ShlinkApiClient.js +++ b/src/utils/services/ShlinkApiClient.js @@ -2,12 +2,13 @@ import qs from 'qs'; import { isEmpty, isNil, reject } from 'ramda'; const API_VERSION = '1'; -const buildRestUrl = (url) => url ? `${url}/rest/v${API_VERSION}` : ''; + +export const buildShlinkBaseUrl = (url) => url ? `${url}/rest/v${API_VERSION}` : ''; export default class ShlinkApiClient { constructor(axios, baseUrl, apiKey) { this.axios = axios; - this._baseUrl = buildRestUrl(baseUrl); + this._baseUrl = buildShlinkBaseUrl(baseUrl); this._apiKey = apiKey || ''; } diff --git a/test/utils/ForVersion.test.js b/test/utils/ForVersion.test.js new file mode 100644 index 00000000..83be6fc5 --- /dev/null +++ b/test/utils/ForVersion.test.js @@ -0,0 +1,43 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import ForVersion from '../../src/utils/ForVersion'; + +describe('', () => { + let wrapped; + + const renderComponent = (minVersion, currentServerVersion) => { + wrapped = mount( + + Hello + + ); + + return wrapped; + }; + + afterEach(() => wrapped && wrapped.unmount()); + + it('does not render children when current version is empty', () => { + const wrapped = renderComponent('1', ''); + + expect(wrapped.html()).toBeNull(); + }); + + it('does not render children when current version is lower than min version', () => { + const wrapped = renderComponent('2.0.0', '1.8.3'); + + expect(wrapped.html()).toBeNull(); + }); + + it('renders children when current version is equal min version', () => { + const wrapped = renderComponent('2.0.0', '2.0.0'); + + expect(wrapped.html()).toContain('Hello'); + }); + + it('renders children when current version is higher than min version', () => { + const wrapped = renderComponent('2.0.0', '2.1.0'); + + expect(wrapped.html()).toContain('Hello'); + }); +}); diff --git a/test/utils/services/ShlinkApiClient.test.js b/test/utils/services/ShlinkApiClient.test.js index 024febf3..c2ef2f2a 100644 --- a/test/utils/services/ShlinkApiClient.test.js +++ b/test/utils/services/ShlinkApiClient.test.js @@ -165,4 +165,20 @@ describe('ShlinkApiClient', () => { })); }); }); + + describe('health', () => { + it('returns health data', async () => { + const expectedData = { + status: 'pass', + version: '1.19.0', + }; + const axiosSpy = jest.fn(createAxiosMock({ data: expectedData })); + const { health } = new ShlinkApiClient(axiosSpy); + + const result = await health(); + + expect(axiosSpy).toHaveBeenCalled(); + expect(result).toEqual(expectedData); + }); + }); }); diff --git a/test/utils/services/ShlinkApiClientBuilder.test.js b/test/utils/services/ShlinkApiClientBuilder.test.js index c6875ed0..02ef8cd8 100644 --- a/test/utils/services/ShlinkApiClientBuilder.test.js +++ b/test/utils/services/ShlinkApiClientBuilder.test.js @@ -1,4 +1,5 @@ import buildShlinkApiClient from '../../../src/utils/services/ShlinkApiClientBuilder'; +import { buildShlinkBaseUrl } from '../../../src/utils/services/ShlinkApiClient'; describe('ShlinkApiClientBuilder', () => { const createBuilder = () => { @@ -33,4 +34,13 @@ describe('ShlinkApiClientBuilder', () => { expect(firstApiClient).toBe(thirdApiClient); expect(secondApiClient).toBe(thirdApiClient); }); + + it('does not fetch from state when provided param is already selected server', async () => { + const url = 'url'; + const apiKey = 'apiKey'; + const apiClient = await buildShlinkApiClient({})({ url, apiKey }); + + expect(apiClient._baseUrl).toEqual(buildShlinkBaseUrl(url)); + expect(apiClient._apiKey).toEqual(apiKey); + }); }); From 354d19af1b576fda5211eeae440140eaa035f20d Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 5 Oct 2019 10:54:58 +0200 Subject: [PATCH 4/6] Disabled domain component for Shlink versions not supporting it --- src/short-urls/CreateShortUrl.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/short-urls/CreateShortUrl.js b/src/short-urls/CreateShortUrl.js index 4a2aa168..9119bc7b 100644 --- a/src/short-urls/CreateShortUrl.js +++ b/src/short-urls/CreateShortUrl.js @@ -4,6 +4,7 @@ import { assoc, dissoc, isEmpty, isNil, pipe, replace, trim } from 'ramda'; import React from 'react'; import { Collapse } from 'reactstrap'; import * as PropTypes from 'prop-types'; +import { compare } from 'compare-versions'; import DateInput from '../utils/DateInput'; import Checkbox from '../utils/Checkbox'; import ForVersion from '../utils/ForVersion'; @@ -70,6 +71,8 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort assoc('validUntil', formatDate(this.state.validUntil)) )(this.state)); }; + const currentServerVersion = this.props.selectedServer ? this.props.selectedServer.version : '9999'; + const disableDomain = compare('1.19.0-beta.1', currentServerVersion, '>'); return (
    @@ -95,26 +98,26 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort {renderOptionalInput('customSlug', 'Custom slug')}
    - {renderOptionalInput('domain', 'Domain', 'text')} + {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 })}
    - +
    Date: Sat, 5 Oct 2019 11:03:17 +0200 Subject: [PATCH 5/6] Defined custom function to compare versions which defines the operator in the middle --- src/short-urls/CreateShortUrl.js | 6 +++--- src/utils/ForVersion.js | 4 ++-- src/utils/utils.js | 7 +++++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/short-urls/CreateShortUrl.js b/src/short-urls/CreateShortUrl.js index 9119bc7b..57d604af 100644 --- a/src/short-urls/CreateShortUrl.js +++ b/src/short-urls/CreateShortUrl.js @@ -4,11 +4,11 @@ import { assoc, dissoc, isEmpty, isNil, pipe, replace, trim } from 'ramda'; import React from 'react'; import { Collapse } from 'reactstrap'; import * as PropTypes from 'prop-types'; -import { compare } from 'compare-versions'; import DateInput from '../utils/DateInput'; import Checkbox from '../utils/Checkbox'; import ForVersion from '../utils/ForVersion'; import { serverType } from '../servers/prop-types'; +import { compareVersions } from '../utils/utils'; import { createShortUrlResultType } from './reducers/shortUrlCreation'; import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon'; @@ -71,8 +71,8 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort assoc('validUntil', formatDate(this.state.validUntil)) )(this.state)); }; - const currentServerVersion = this.props.selectedServer ? this.props.selectedServer.version : '9999'; - const disableDomain = compare('1.19.0-beta.1', currentServerVersion, '>'); + const currentServerVersion = this.props.selectedServer ? this.props.selectedServer.version : ''; + const disableDomain = isEmpty(currentServerVersion) || compareVersions(currentServerVersion, '<', '1.19.0-beta.1'); return (
    diff --git a/src/utils/ForVersion.js b/src/utils/ForVersion.js index 10f9befa..7ed39890 100644 --- a/src/utils/ForVersion.js +++ b/src/utils/ForVersion.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { compare } from 'compare-versions'; import { isEmpty } from 'ramda'; +import { compareVersions } from './utils'; const propTypes = { minVersion: PropTypes.string.isRequired, @@ -10,7 +10,7 @@ const propTypes = { }; const ForVersion = ({ minVersion, currentServerVersion, children }) => - isEmpty(currentServerVersion) || compare(minVersion, currentServerVersion, '>') + isEmpty(currentServerVersion) || compareVersions(currentServerVersion, '<', minVersion) ? null : {children}; diff --git a/src/utils/utils.js b/src/utils/utils.js index b1daf53b..3edcb7fb 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -4,6 +4,7 @@ import marker from 'leaflet/dist/images/marker-icon.png'; import markerShadow from 'leaflet/dist/images/marker-shadow.png'; import { range } from 'ramda'; import { useState } from 'react'; +import { compare } from 'compare-versions'; const TEN_ROUNDING_NUMBER = 10; const DEFAULT_TIMEOUT_DELAY = 2000; @@ -53,3 +54,9 @@ export const useToggle = (initialValue = false) => { }; export const wait = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)); + +export const compareVersions = (firstVersion, operator, secondVersion) => compare( + firstVersion, + secondVersion, + operator +); From c181948afe24ba8a6920849787173bcccd7436da Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 5 Oct 2019 11:05:03 +0200 Subject: [PATCH 6/6] Updated changelog --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15b5479d..b74f4a13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org). +## [Unreleased] + +#### Added + +* [#144](https://github.com/shlinkio/shlink-web-client/issues/144) Added domain input to create domain page. + +#### Changed + +* *Nothing* + +#### Deprecated + +* *Nothing* + +#### Removed + +* *Nothing* + +#### Fixed + +* *Nothing* + + ## 2.1.1 - 2019-09-22 #### Added