mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Loaded version of selected server and created component to filter content based on that version
This commit is contained in:
parent
67a23bfe33
commit
4120d09220
11 changed files with 71 additions and 21 deletions
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -4951,6 +4951,11 @@
|
||||||
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
|
||||||
"dev": true
|
"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": {
|
"component-emitter": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz",
|
"resolved": "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz",
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
"bottlejs": "^1.7.1",
|
"bottlejs": "^1.7.1",
|
||||||
"chart.js": "^2.7.2",
|
"chart.js": "^2.7.2",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
|
"compare-versions": "^3.5.1",
|
||||||
"csvjson": "^5.1.0",
|
"csvjson": "^5.1.0",
|
||||||
"leaflet": "^1.4.0",
|
"leaflet": "^1.4.0",
|
||||||
"moment": "^2.22.2",
|
"moment": "^2.22.2",
|
||||||
|
|
|
@ -10,14 +10,19 @@ const initialState = null;
|
||||||
|
|
||||||
export const resetSelectedServer = createAction(RESET_SELECTED_SERVER);
|
export const resetSelectedServer = createAction(RESET_SELECTED_SERVER);
|
||||||
|
|
||||||
export const selectServer = ({ findServerById }) => (serverId) => (dispatch) => {
|
export const selectServer = ({ findServerById }, buildShlinkApiClient) => (serverId) => async (dispatch) => {
|
||||||
dispatch(resetShortUrlParams());
|
dispatch(resetShortUrlParams());
|
||||||
|
|
||||||
const selectedServer = findServerById(serverId);
|
const selectedServer = findServerById(serverId);
|
||||||
|
const { health } = await buildShlinkApiClient(selectedServer);
|
||||||
|
const { version } = await health();
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SELECT_SERVER,
|
type: SELECT_SERVER,
|
||||||
selectedServer,
|
selectedServer: {
|
||||||
|
...selectedServer,
|
||||||
|
version,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ const provideServices = (bottle, connect, withRouter) => {
|
||||||
bottle.service('ServersExporter', ServersExporter, 'ServersService', 'window', 'csvjson');
|
bottle.service('ServersExporter', ServersExporter, 'ServersService', 'window', 'csvjson');
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
bottle.serviceFactory('selectServer', selectServer, 'ServersService');
|
bottle.serviceFactory('selectServer', selectServer, 'ServersService', 'buildShlinkApiClient');
|
||||||
bottle.serviceFactory('createServer', createServer, 'ServersService', 'listServers');
|
bottle.serviceFactory('createServer', createServer, 'ServersService', 'listServers');
|
||||||
bottle.serviceFactory('createServers', createServers, 'ServersService', 'listServers');
|
bottle.serviceFactory('createServers', createServers, 'ServersService', 'listServers');
|
||||||
bottle.serviceFactory('deleteServer', deleteServer, 'ServersService', 'listServers');
|
bottle.serviceFactory('deleteServer', deleteServer, 'ServersService', 'listServers');
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { Collapse } from 'reactstrap';
|
||||||
import * as PropTypes from 'prop-types';
|
import * as PropTypes from 'prop-types';
|
||||||
import DateInput from '../utils/DateInput';
|
import DateInput from '../utils/DateInput';
|
||||||
import Checkbox from '../utils/Checkbox';
|
import Checkbox from '../utils/Checkbox';
|
||||||
|
import ForVersion from '../utils/ForVersion';
|
||||||
|
import { serverType } from '../servers/prop-types';
|
||||||
import { createShortUrlResultType } from './reducers/shortUrlCreation';
|
import { createShortUrlResultType } from './reducers/shortUrlCreation';
|
||||||
import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon';
|
import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon';
|
||||||
|
|
||||||
|
@ -17,6 +19,7 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort
|
||||||
createShortUrl: PropTypes.func,
|
createShortUrl: PropTypes.func,
|
||||||
shortUrlCreationResult: createShortUrlResultType,
|
shortUrlCreationResult: createShortUrlResultType,
|
||||||
resetCreateShortUrl: PropTypes.func,
|
resetCreateShortUrl: PropTypes.func,
|
||||||
|
selectedServer: serverType,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -108,16 +111,21 @@ const CreateShortUrl = (TagsSelector, CreateShortUrlResult) => class CreateShort
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-4 text-right">
|
<ForVersion
|
||||||
<Checkbox
|
minVersion="1.16.0"
|
||||||
className="mr-2"
|
currentServerVersion={this.props.selectedServer ? this.props.selectedServer.version : ''}
|
||||||
checked={this.state.findIfExists}
|
>
|
||||||
onChange={(findIfExists) => this.setState({ findIfExists })}
|
<div className="mb-4 text-right">
|
||||||
>
|
<Checkbox
|
||||||
Use existing URL if found
|
className="mr-2"
|
||||||
</Checkbox>
|
checked={this.state.findIfExists}
|
||||||
<UseExistingIfFoundInfoIcon />
|
onChange={(findIfExists) => this.setState({ findIfExists })}
|
||||||
</div>
|
>
|
||||||
|
Use existing URL if found
|
||||||
|
</Checkbox>
|
||||||
|
<UseExistingIfFoundInfoIcon />
|
||||||
|
</div>
|
||||||
|
</ForVersion>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -23,8 +23,8 @@ const renderInfoModal = (isOpen, toggle) => (
|
||||||
if none is found.
|
if none is found.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
When long URL and custom slug are provided: Same as in previous case, but it will try to match the short URL
|
When long URL and custom slug and/or domain are provided: Same as in previous case, but it will try to match
|
||||||
using both the long URL and the slug.
|
the short URL using both the long URL and the slug, the long URL and the domain, or the three of them.
|
||||||
<br />
|
<br />
|
||||||
If the slug is being used by another long URL, an error will be returned.
|
If the slug is being used by another long URL, an error will be returned.
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -39,7 +39,7 @@ const provideServices = (bottle, connect) => {
|
||||||
bottle.serviceFactory('CreateShortUrl', CreateShortUrl, 'TagsSelector', 'CreateShortUrlResult');
|
bottle.serviceFactory('CreateShortUrl', CreateShortUrl, 'TagsSelector', 'CreateShortUrlResult');
|
||||||
bottle.decorator(
|
bottle.decorator(
|
||||||
'CreateShortUrl',
|
'CreateShortUrl',
|
||||||
connect([ 'shortUrlCreationResult' ], [ 'createShortUrl', 'resetCreateShortUrl' ])
|
connect([ 'shortUrlCreationResult', 'selectedServer' ], [ 'createShortUrl', 'resetCreateShortUrl' ])
|
||||||
);
|
);
|
||||||
|
|
||||||
bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal);
|
bottle.serviceFactory('DeleteShortUrlModal', () => DeleteShortUrlModal);
|
||||||
|
|
19
src/utils/ForVersion.js
Normal file
19
src/utils/ForVersion.js
Normal file
|
@ -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
|
||||||
|
: <React.Fragment>{children}</React.Fragment>;
|
||||||
|
|
||||||
|
ForVersion.propTypes = propTypes;
|
||||||
|
|
||||||
|
export default ForVersion;
|
|
@ -50,6 +50,8 @@ export default class ShlinkApiClient {
|
||||||
this._performRequest('/tags', 'PUT', {}, { oldName, newName })
|
this._performRequest('/tags', 'PUT', {}, { oldName, newName })
|
||||||
.then(() => ({ oldName, newName }));
|
.then(() => ({ oldName, newName }));
|
||||||
|
|
||||||
|
health = () => this._performRequest('/health', 'GET').then((resp) => resp.data);
|
||||||
|
|
||||||
_performRequest = async (url, method = 'GET', query = {}, body = {}) =>
|
_performRequest = async (url, method = 'GET', query = {}, body = {}) =>
|
||||||
await this.axios({
|
await this.axios({
|
||||||
method,
|
method,
|
||||||
|
|
|
@ -13,8 +13,10 @@ const getSelectedServerFromState = async (getState) => {
|
||||||
return selectedServer;
|
return selectedServer;
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildShlinkApiClient = (axios) => async (getState) => {
|
const buildShlinkApiClient = (axios) => async (getStateOrSelectedServer) => {
|
||||||
const { url, apiKey } = await getSelectedServerFromState(getState);
|
const { url, apiKey } = typeof getStateOrSelectedServer === 'function'
|
||||||
|
? await getSelectedServerFromState(getStateOrSelectedServer)
|
||||||
|
: getStateOrSelectedServer;
|
||||||
const clientKey = `${url}_${apiKey}`;
|
const clientKey = `${url}_${apiKey}`;
|
||||||
|
|
||||||
if (!apiClients[clientKey]) {
|
if (!apiClients[clientKey]) {
|
||||||
|
|
|
@ -29,22 +29,30 @@ describe('selectedServerReducer', () => {
|
||||||
const selectedServer = {
|
const selectedServer = {
|
||||||
id: serverId,
|
id: serverId,
|
||||||
};
|
};
|
||||||
|
const version = '1.19.0';
|
||||||
const ServersServiceMock = {
|
const ServersServiceMock = {
|
||||||
findServerById: jest.fn(() => selectedServer),
|
findServerById: jest.fn(() => selectedServer),
|
||||||
};
|
};
|
||||||
|
const apiClientMock = {
|
||||||
|
health: jest.fn().mockResolvedValue({ version }),
|
||||||
|
};
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
ServersServiceMock.findServerById.mockClear();
|
ServersServiceMock.findServerById.mockClear();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('dispatches proper actions', () => {
|
it('dispatches proper actions', async () => {
|
||||||
const dispatch = jest.fn();
|
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).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(1, { type: RESET_SHORT_URL_PARAMS });
|
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', () => {
|
it('invokes dependencies', () => {
|
||||||
|
|
Loading…
Reference in a new issue