Added short code length field to form to create short URLs

This commit is contained in:
Alejandro Celaya
2020-03-29 18:55:41 +02:00
parent 9a20b4428d
commit bd29670108
5 changed files with 60 additions and 19 deletions

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { serverType } from '../prop-types'; import { serverType } from '../prop-types';
import { compareVersions } from '../../utils/helpers/version'; import { versionMatch } from '../../utils/helpers/version';
const propTypes = { const propTypes = {
minVersion: PropTypes.string, minVersion: PropTypes.string,
@@ -16,10 +16,9 @@ const ForServerVersion = ({ minVersion, maxVersion, selectedServer, children })
} }
const { version } = selectedServer; const { version } = selectedServer;
const matchesMinVersion = !minVersion || compareVersions(version, '>=', minVersion); const matchesVersion = versionMatch(version, { maxVersion, minVersion });
const matchesMaxVersion = !maxVersion || compareVersions(version, '<=', maxVersion);
if (!matchesMinVersion || !matchesMaxVersion) { if (!matchesVersion) {
return null; return null;
} }

View File

@@ -7,7 +7,8 @@ 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 { serverType } from '../servers/prop-types'; import { serverType } from '../servers/prop-types';
import { compareVersions } from '../utils/helpers/version'; import { versionMatch } from '../utils/helpers/version';
import { hasValue } from '../utils/utils';
import { createShortUrlResultType } from './reducers/shortUrlCreation'; import { createShortUrlResultType } from './reducers/shortUrlCreation';
import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon'; import UseExistingIfFoundInfoIcon from './UseExistingIfFoundInfoIcon';
@@ -30,6 +31,7 @@ const CreateShortUrl = (
longUrl: '', longUrl: '',
tags: [], tags: [],
customSlug: undefined, customSlug: undefined,
shortCodeLength: undefined,
domain: undefined, domain: undefined,
validSince: undefined, validSince: undefined,
validUntil: undefined, validUntil: undefined,
@@ -73,8 +75,9 @@ const CreateShortUrl = (
assoc('validUntil', formatDate(this.state.validUntil)) assoc('validUntil', formatDate(this.state.validUntil))
)(this.state)); )(this.state));
}; };
const currentServerVersion = this.props.selectedServer ? this.props.selectedServer.version : ''; const currentServerVersion = this.props.selectedServer && this.props.selectedServer.version;
const disableDomain = isEmpty(currentServerVersion) || compareVersions(currentServerVersion, '<', '1.19.0-beta.1'); const disableDomain = !versionMatch(currentServerVersion, { minVersion: '1.19.0-beta.1' });
const disableShortCodeLength = !versionMatch(currentServerVersion, { minVersion: '2.1.0' });
return ( return (
<form onSubmit={save}> <form onSubmit={save}>
@@ -95,10 +98,19 @@ const CreateShortUrl = (
</div> </div>
<div className="row"> <div className="row">
<div className="col-sm-6"> <div className="col-sm-4">
{renderOptionalInput('customSlug', 'Custom slug')} {renderOptionalInput('customSlug', 'Custom slug')}
</div> </div>
<div className="col-sm-6"> <div className="col-sm-4">
{renderOptionalInput('shortCodeLength', 'Short code length', 'number', {
min: 4,
disabled: disableShortCodeLength || hasValue(this.state.customSlug),
...disableShortCodeLength && {
title: 'Shlink 2.1.0 or higher is required to be able to provide the short code length',
},
})}
</div>
<div className="col-sm-4">
{renderOptionalInput('domain', 'Domain', 'text', { {renderOptionalInput('domain', 'Domain', 'text', {
disabled: disableDomain, disabled: disableDomain,
...disableDomain && { title: 'Shlink 1.19.0 or higher is required to be able to provide the domain' }, ...disableDomain && { title: 'Shlink 1.19.0 or higher is required to be able to provide the domain' },
@@ -107,13 +119,13 @@ const CreateShortUrl = (
</div> </div>
<div className="row"> <div className="row">
<div className="col-sm-6"> <div className="col-sm-4">
{renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })} {renderOptionalInput('maxVisits', 'Maximum number of visits allowed', 'number', { min: 1 })}
</div> </div>
<div className="col-sm-3"> <div className="col-sm-4">
{renderDateInput('validSince', 'Enabled since...', { maxDate: this.state.validUntil })} {renderDateInput('validSince', 'Enabled since...', { maxDate: this.state.validUntil })}
</div> </div>
<div className="col-sm-3"> <div className="col-sm-4">
{renderDateInput('validUntil', 'Enabled until...', { minDate: this.state.validSince })} {renderDateInput('validUntil', 'Enabled until...', { minDate: this.state.validSince })}
</div> </div>
</div> </div>

View File

@@ -1,15 +1,21 @@
import { compare } from 'compare-versions'; import { compare } from 'compare-versions';
import { identity, memoizeWith } from 'ramda'; import { identity, memoizeWith } from 'ramda';
import { hasValue } from '../utils';
export const compareVersions = (firstVersion, operator, secondVersion) => compare( export const versionMatch = (versionToMatch, { maxVersion, minVersion }) => {
firstVersion, if (!hasValue(versionToMatch)) {
secondVersion, return false;
operator, }
);
const matchesMinVersion = !minVersion || compare(versionToMatch, minVersion, '>=');
const matchesMaxVersion = !maxVersion || compare(versionToMatch, maxVersion, '<=');
return !!(matchesMaxVersion && matchesMinVersion);
};
const versionIsValidSemVer = memoizeWith(identity, (version) => { const versionIsValidSemVer = memoizeWith(identity, (version) => {
try { try {
return compareVersions(version, '=', version); return compare(version, version, '=');
} catch (e) { } catch (e) {
return false; return false;
} }

View File

@@ -2,7 +2,7 @@ import L from 'leaflet';
import marker2x from 'leaflet/dist/images/marker-icon-2x.png'; import marker2x from 'leaflet/dist/images/marker-icon-2x.png';
import marker from 'leaflet/dist/images/marker-icon.png'; import marker from 'leaflet/dist/images/marker-icon.png';
import markerShadow from 'leaflet/dist/images/marker-shadow.png'; import markerShadow from 'leaflet/dist/images/marker-shadow.png';
import { range } from 'ramda'; import { isEmpty, isNil, range } from 'ramda';
const TEN_ROUNDING_NUMBER = 10; const TEN_ROUNDING_NUMBER = 10;
const DEFAULT_TIMEOUT_DELAY = 2000; const DEFAULT_TIMEOUT_DELAY = 2000;
@@ -45,3 +45,4 @@ export const rangeOf = (size, mappingFn, startAt = 1) => range(startAt, size + 1
export const roundTen = (number) => ceil(number / TEN_ROUNDING_NUMBER) * TEN_ROUNDING_NUMBER; export const roundTen = (number) => ceil(number / TEN_ROUNDING_NUMBER) * TEN_ROUNDING_NUMBER;
export const hasValue = (value) => !isNil(value) && !isEmpty(value);

View File

@@ -0,0 +1,23 @@
import { versionMatch } from '../../../src/utils/helpers/version';
describe('version', () => {
describe('versionMatch', () => {
it.each([
[ undefined, {}, false ],
[ null, {}, false ],
[ '', {}, false ],
[[], {}, false ],
[ '2.8.3', {}, true ],
[ '2.8.3', { minVersion: '2.0.0' }, true ],
[ '2.0.0', { minVersion: '2.0.0' }, true ],
[ '1.8.0', { maxVersion: '1.8.0' }, true ],
[ '1.7.1', { maxVersion: '1.8.0' }, true ],
[ '1.7.3', { minVersion: '1.7.0', maxVersion: '1.8.0' }, true ],
[ '1.8.3', { minVersion: '2.0.0' }, false ],
[ '1.8.3', { maxVersion: '1.8.0' }, false ],
[ '1.8.3', { minVersion: '1.7.0', maxVersion: '1.8.0' }, false ],
])('properly matches versions based on what is provided', (version, versionConstraints, expected) => {
expect(versionMatch(version, versionConstraints)).toEqual(expected);
});
});
});