Skip to content

Commit 552169e

Browse files
authored
Merge pull request #553 from shlinkio/develop
Release 3.5.0
2 parents ff18216 + 4f03ab1 commit 552169e

File tree

116 files changed

+1791
-600
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+1791
-600
lines changed

Diff for: CHANGELOG.md

+34
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,40 @@ All notable changes to this project will be documented in this file.
44

55
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).
66

7+
## [3.5.0] - 2022-01-01
8+
### Added
9+
* [#407](https://github.com/shlinkio/shlink-web-client/pull/407) Improved how visits (short URLs, tags and orphan) are loaded, to avoid ending up in a page with "There are no visits matching current filter".
10+
11+
Now, the app will try to load visits for the configured default interval, and in parallel, it will load the latest visit.
12+
13+
If the resulting list for that interval is empty, it will try to infer the closest interval with visits, based on the latest visit's date, and reload visits for that interval.
14+
15+
* [#547](https://github.com/shlinkio/shlink-web-client/pull/547) Improved domains page, to tell which of the domains are not properly configured.
16+
17+
Now, when this section is loaded, it tries to call the `GET /rest/health` endpoint for each one of the domains, and displays a warning icon on each one that failed.
18+
19+
The warning includes a link to the documentation, explaining what are the steps to get it fixed.
20+
21+
* [#506](https://github.com/shlinkio/shlink-web-client/pull/506) Improved how servers are handled, displaying a warning when creating or importing servers that already exist.
22+
* [#535](https://github.com/shlinkio/shlink-web-client/pull/535) Allowed editing default domain redirects when consuming Shlink 2.10 or newer.
23+
* [#531](https://github.com/shlinkio/shlink-web-client/pull/531) Added custom slug field to the basic creation form in the Overview page.
24+
* [#537](https://github.com/shlinkio/shlink-web-client/pull/537) Allowed to customize the ordering for every list in the app that supports it, being currently tags and short URLs.
25+
* [#542](https://github.com/shlinkio/shlink-web-client/pull/542) Added ordering for short URLs to the query, so that it is consistent with the rest of the filtering params.
26+
27+
### Changed
28+
* [#534](https://github.com/shlinkio/shlink-web-client/pull/534) Updated axios.
29+
* [#538](https://github.com/shlinkio/shlink-web-client/pull/538) Switched to the `<field>-<dir>` notation in `orderBy` param for short URLs list, in preparation for Shlink v3.0.0
30+
31+
### Deprecated
32+
* *Nothing*
33+
34+
### Removed
35+
* *Nothing*
36+
37+
### Fixed
38+
* *Nothing*
39+
40+
741
## [3.4.2] - 2021-12-07
842
### Added
943
* *Nothing*

Diff for: package-lock.json

+20-15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"@fortawesome/free-regular-svg-icons": "^5.15.2",
2828
"@fortawesome/free-solid-svg-icons": "^5.15.2",
2929
"@fortawesome/react-fontawesome": "^0.1.14",
30-
"axios": "^0.21.1",
30+
"axios": "^0.21.2",
3131
"bootstrap": "^4.6.0",
3232
"bottlejs": "^2.0.0",
3333
"bowser": "^2.11.0",

Diff for: src/api/services/ShlinkApiClient.ts

+24-40
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,34 @@ import {
1111
ShlinkVisits,
1212
ShlinkVisitsParams,
1313
ShlinkShortUrlData,
14-
ShlinkDomain,
1514
ShlinkDomainsResponse,
1615
ShlinkVisitsOverview,
1716
ShlinkEditDomainRedirects,
1817
ShlinkDomainRedirects,
1918
ShlinkShortUrlsListParams,
19+
ShlinkShortUrlsListNormalizedParams,
2020
} from '../types';
2121
import { stringifyQuery } from '../../utils/helpers/query';
22+
import { orderToString } from '../../utils/helpers/ordering';
2223

23-
const buildShlinkBaseUrl = (url: string, apiVersion: number) => url ? `${url}/rest/v${apiVersion}` : '';
24+
const buildShlinkBaseUrl = (url: string) => url ? `${url}/rest/v2` : '';
2425
const rejectNilProps = reject(isNil);
26+
const normalizeOrderByInParams = (params: ShlinkShortUrlsListParams): ShlinkShortUrlsListNormalizedParams => {
27+
const { orderBy = {}, ...rest } = params;
2528

26-
export default class ShlinkApiClient {
27-
private apiVersion: number;
29+
return { ...rest, orderBy: orderToString(orderBy) };
30+
};
2831

32+
export default class ShlinkApiClient {
2933
public constructor(
3034
private readonly axios: AxiosInstance,
3135
private readonly baseUrl: string,
3236
private readonly apiKey: string,
3337
) {
34-
this.apiVersion = 2;
3538
}
3639

3740
public readonly listShortUrls = async (params: ShlinkShortUrlsListParams = {}): Promise<ShlinkShortUrlsResponse> =>
38-
this.performRequest<{ shortUrls: ShlinkShortUrlsResponse }>('/short-urls', 'GET', params)
41+
this.performRequest<{ shortUrls: ShlinkShortUrlsResponse }>('/short-urls', 'GET', normalizeOrderByInParams(params))
3942
.then(({ data }) => data.shortUrls);
4043

4144
public readonly createShortUrl = async (options: ShortUrlData): Promise<ShortUrl> => {
@@ -69,7 +72,10 @@ export default class ShlinkApiClient {
6972
this.performRequest(`/short-urls/${shortCode}`, 'DELETE', { domain })
7073
.then(() => {});
7174

72-
/* @deprecated. If using Shlink 2.6.0 or greater, use updateShortUrl instead */
75+
// eslint-disable-next-line valid-jsdoc
76+
/**
77+
* @deprecated. If using Shlink 2.6.0 or greater, use updateShortUrl instead
78+
*/
7379
public readonly updateShortUrlTags = async (
7480
shortCode: string,
7581
domain: OptionalString,
@@ -107,43 +113,21 @@ export default class ShlinkApiClient {
107113
this.performRequest<ShlinkMercureInfo>('/mercure-info', 'GET')
108114
.then((resp) => resp.data);
109115

110-
public readonly listDomains = async (): Promise<ShlinkDomain[]> =>
111-
this.performRequest<{ domains: ShlinkDomainsResponse }>('/domains', 'GET').then(({ data }) => data.domains.data);
116+
public readonly listDomains = async (): Promise<ShlinkDomainsResponse> =>
117+
this.performRequest<{ domains: ShlinkDomainsResponse }>('/domains', 'GET').then(({ data }) => data.domains);
112118

113119
public readonly editDomainRedirects = async (
114120
domainRedirects: ShlinkEditDomainRedirects,
115121
): Promise<ShlinkDomainRedirects> =>
116122
this.performRequest<ShlinkDomainRedirects>('/domains/redirects', 'PATCH', {}, domainRedirects).then(({ data }) => data);
117123

118-
private readonly performRequest = async <T>(url: string, method: Method = 'GET', query = {}, body = {}): Promise<AxiosResponse<T>> => {
119-
try {
120-
return await this.axios({
121-
method,
122-
url: `${buildShlinkBaseUrl(this.baseUrl, this.apiVersion)}${url}`,
123-
headers: { 'X-Api-Key': this.apiKey },
124-
params: rejectNilProps(query),
125-
data: body,
126-
paramsSerializer: stringifyQuery,
127-
});
128-
} catch (e: any) {
129-
const { response } = e;
130-
131-
// Due to a bug on all previous Shlink versions, requests to non-matching URLs will always result on a CORS error
132-
// when performed from the browser (due to the preflight request not returning a 2xx status.
133-
// See https://github.com/shlinkio/shlink/issues/614), which will make the "response" prop not to be set here.
134-
// The bug will be fixed on upcoming Shlink patches, but for other versions, we can consider this situation as
135-
// if a request has been performed to a not supported API version.
136-
const apiVersionIsNotSupported = !response;
137-
138-
// When the request is not invalid or we have already tried both API versions, throw the error and let the
139-
// caller handle it
140-
if (!apiVersionIsNotSupported || this.apiVersion === 2) {
141-
throw e;
142-
}
143-
144-
this.apiVersion = this.apiVersion - 1;
145-
146-
return await this.performRequest(url, method, query, body);
147-
}
148-
};
124+
private readonly performRequest = async <T>(url: string, method: Method = 'GET', query = {}, body = {}): Promise<AxiosResponse<T>> =>
125+
this.axios({
126+
method,
127+
url: `${buildShlinkBaseUrl(this.baseUrl)}${url}`,
128+
headers: { 'X-Api-Key': this.apiKey },
129+
params: rejectNilProps(query),
130+
data: body,
131+
paramsSerializer: stringifyQuery,
132+
});
149133
}

Diff for: src/api/types/index.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Visit } from '../../visits/types';
22
import { OptionalString } from '../../utils/utils';
3-
import { ShortUrl, ShortUrlMeta } from '../../short-urls/data';
4-
import { OrderBy } from '../../short-urls/reducers/shortUrlsListParams';
3+
import { ShortUrl, ShortUrlMeta, ShortUrlsOrder } from '../../short-urls/data';
54

65
export interface ShlinkShortUrlsResponse {
76
data: ShortUrl[];
@@ -84,6 +83,7 @@ export interface ShlinkDomain {
8483

8584
export interface ShlinkDomainsResponse {
8685
data: ShlinkDomain[];
86+
defaultRedirects?: ShlinkDomainRedirects; // Optional only for Shlink older than 2.10
8787
}
8888

8989
export interface ShlinkShortUrlsListParams {
@@ -93,7 +93,11 @@ export interface ShlinkShortUrlsListParams {
9393
searchTerm?: string;
9494
startDate?: string;
9595
endDate?: string;
96-
orderBy?: OrderBy;
96+
orderBy?: ShortUrlsOrder;
97+
}
98+
99+
export interface ShlinkShortUrlsListNormalizedParams extends Omit<ShlinkShortUrlsListParams, 'orderBy'> {
100+
orderBy?: string;
97101
}
98102

99103
export interface ProblemDetailsError {

Diff for: src/common/NoMenuLayout.tsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import { FC } from 'react';
22
import './NoMenuLayout.scss';
33

4-
const NoMenuLayout: FC = ({ children }) => <div className="no-menu-wrapper container-xl">{children}</div>;
5-
6-
export default NoMenuLayout;
4+
export const NoMenuLayout: FC = ({ children }) => <div className="no-menu-wrapper container-xl">{children}</div>;

Diff for: src/container/index.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ import { ConnectDecorator } from './types';
1818
type LazyActionMap = Record<string, Function>;
1919

2020
const bottle = new Bottle();
21-
const { container } = bottle;
21+
22+
export const { container } = bottle;
2223

2324
const lazyService = <T extends Function, K>(container: IContainer, serviceName: string) =>
2425
(...args: any[]) => (container[serviceName] as T)(...args) as K;
@@ -44,5 +45,3 @@ provideUtilsServices(bottle);
4445
provideMercureServices(bottle);
4546
provideSettingsServices(bottle, connect);
4647
provideDomainsServices(bottle, connect);
47-
48-
export default container;

Diff for: src/container/store.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import ReduxThunk from 'redux-thunk';
22
import { applyMiddleware, compose, createStore } from 'redux';
33
import { save, load, RLSOptions } from 'redux-localstorage-simple';
44
import reducers from '../reducers';
5+
import { migrateDeprecatedSettings } from '../settings/helpers';
6+
import { ShlinkState } from './types';
57

68
const isProduction = process.env.NODE_ENV !== 'production';
79
const composeEnhancers: Function = !isProduction && (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
@@ -12,9 +14,8 @@ const localStorageConfig: RLSOptions = {
1214
namespaceSeparator: '.',
1315
debounce: 300,
1416
};
17+
const preloadedState = migrateDeprecatedSettings(load(localStorageConfig) as ShlinkState);
1518

16-
const store = createStore(reducers, load(localStorageConfig), composeEnhancers(
19+
export const store = createStore(reducers, preloadedState, composeEnhancers(
1720
applyMiddleware(save(localStorageConfig), ReduxThunk),
1821
));
19-
20-
export default store;

Diff for: src/container/types.ts

-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Settings } from '../settings/reducers/settings';
44
import { ShortUrlCreation } from '../short-urls/reducers/shortUrlCreation';
55
import { ShortUrlDeletion } from '../short-urls/reducers/shortUrlDeletion';
66
import { ShortUrlEdition } from '../short-urls/reducers/shortUrlEdition';
7-
import { ShortUrlsListParams } from '../short-urls/reducers/shortUrlsListParams';
87
import { ShortUrlsList } from '../short-urls/reducers/shortUrlsList';
98
import { TagDeletion } from '../tags/reducers/tagDelete';
109
import { TagEdition } from '../tags/reducers/tagEdit';
@@ -20,7 +19,6 @@ export interface ShlinkState {
2019
servers: ServersMap;
2120
selectedServer: SelectedServer;
2221
shortUrlsList: ShortUrlsList;
23-
shortUrlsListParams: ShortUrlsListParams;
2422
shortUrlCreationResult: ShortUrlCreation;
2523
shortUrlDeletion: ShortUrlDeletion;
2624
shortUrlEdition: ShortUrlEdition;

0 commit comments

Comments
 (0)