Skip to content

Commit 5354725

Browse files
authored
Merge pull request #279 from acelaya-forks/feature/shlink-js-sdk-1
Update to shlink-js-sdk 1.0.0
2 parents d6e5058 + 923f8e2 commit 5354725

32 files changed

+124
-156
lines changed

CHANGELOG.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
1111
### Changed
1212
* [#249](https://github.com/shlinkio/shlink-web-component/issues/249) Replace `react-datepicker` with native `input[type="date"]` and `input[type="datetime-local"]` elements.
1313
* [#257](https://github.com/shlinkio/shlink-web-component/issues/257) Remove dependency on `react-copy-to-clipboard`.
14+
* [#278](https://github.com/shlinkio/shlink-web-component/issues/278) Update to `@shlinkio/shlink-js-sdk` v1.0.0.
1415

1516
### Deprecated
1617
* *Nothing*
1718

1819
### Removed
19-
* *Nothing*
20+
* [#276](https://github.com/shlinkio/shlink-web-component/issues/276) Drop support for Shlink older than 3.3.0.
2021

2122
### Fixed
2223
* *Nothing*

docker-compose.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ services:
44
shlink_web_component:
55
container_name: shlink_web_component
66
image: node:20.5-alpine
7-
command: /bin/sh -c "cd /shlink-web-component && npm run dev"
7+
command: /bin/sh -c "cd /shlink-web-component && npm i && npm run dev"
88
volumes:
99
- ./:/shlink-web-component
1010
ports:

package-lock.json

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

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"@fortawesome/react-fontawesome": "^0.2.0",
4747
"@reduxjs/toolkit": "^2.0.1",
4848
"@shlinkio/shlink-frontend-kit": "^0.4.2",
49-
"@shlinkio/shlink-js-sdk": "^0.2.2",
49+
"@shlinkio/shlink-js-sdk": "^1.0.0",
5050
"react": "^18.2.0",
5151
"react-dom": "^18.2.0",
5252
"react-redux": "^9.0.1",

src/Main.tsx

+1-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
88
import { AsideMenu } from './common/AsideMenu';
99
import type { FCWithDeps } from './container/utils';
1010
import { componentFactory, useDependencies } from './container/utils';
11-
import { useFeature } from './utils/features';
1211
import { useSwipeable } from './utils/helpers/hooks';
1312
import { useRoutesPrefix } from './utils/routesPrefix';
1413
import './Main.scss';
@@ -59,7 +58,6 @@ const Main: FCWithDeps<MainProps, MainDeps> = ({ createNotFound }) => {
5958
// Hide sidebar every time the route changes
6059
useEffect(() => hideSidebar(), [location, hideSidebar]);
6160

62-
const addDomainVisitsRoute = useFeature('domainVisits');
6361
const burgerClasses = clsx('shlink-layout__burger-icon', { 'shlink-layout__burger-icon--active': sidebarVisible });
6462
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
6563

@@ -82,7 +80,7 @@ const Main: FCWithDeps<MainProps, MainDeps> = ({ createNotFound }) => {
8280
<Route path="/short-urls/compare-visits" element={<ShortUrlVisitsComparison />} />
8381
<Route path="/tag/:tag/visits/*" element={<TagVisits />} />
8482
<Route path="/tags/compare-visits" element={<TagVisitsComparison />} />
85-
{addDomainVisitsRoute && <Route path="/domain/:domain/visits/*" element={<DomainVisits />} />}
83+
<Route path="/domain/:domain/visits/*" element={<DomainVisits />} />
8684
<Route path="/orphan-visits/*" element={<OrphanVisits />} />
8785
<Route path="/non-orphan-visits/*" element={<NonOrphanVisits />} />
8886
<Route path="/manage-tags" element={<TagsList />} />

src/api-contract/index.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import type { ErrorTypeV2, ErrorTypeV3, ProblemDetailsError } from '@shlinkio/shlink-js-sdk/api-contract';
1+
import type { ErrorType, ProblemDetailsError } from '@shlinkio/shlink-js-sdk/api-contract';
22

33
// Re-export every type from the SDK
44
export * from '@shlinkio/shlink-js-sdk/api-contract';
55

66
export interface InvalidArgumentError extends ProblemDetailsError {
7-
type: ErrorTypeV2.INVALID_ARGUMENT | ErrorTypeV3.INVALID_ARGUMENT;
7+
type: ErrorType.INVALID_ARGUMENT;
88
invalidElements: string[];
99
}
1010

1111
export interface InvalidShortUrlDeletion extends ProblemDetailsError {
12-
type: 'INVALID_SHORTCODE_DELETION' | ErrorTypeV2.INVALID_SHORT_URL_DELETION | ErrorTypeV3.INVALID_SHORT_URL_DELETION;
12+
type: ErrorType.INVALID_SHORT_URL_DELETION;
1313
threshold: number;
1414
}

src/api-contract/utils.ts

+4-13
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
1-
import type {
2-
InvalidArgumentError,
3-
InvalidShortUrlDeletion,
4-
ProblemDetailsError,
5-
} from '.';
6-
import {
7-
ErrorTypeV2,
8-
ErrorTypeV3,
9-
} from '.';
1+
import type { InvalidArgumentError, InvalidShortUrlDeletion, ProblemDetailsError } from '.';
2+
import { ErrorType } from '.';
103

114
export const isInvalidArgumentError = (error?: ProblemDetailsError): error is InvalidArgumentError =>
12-
error?.type === ErrorTypeV2.INVALID_ARGUMENT || error?.type === ErrorTypeV3.INVALID_ARGUMENT;
5+
error?.type === ErrorType.INVALID_ARGUMENT;
136

147
export const isInvalidDeletionError = (error?: ProblemDetailsError): error is InvalidShortUrlDeletion =>
15-
error?.type === 'INVALID_SHORTCODE_DELETION'
16-
|| error?.type === ErrorTypeV2.INVALID_SHORT_URL_DELETION
17-
|| error?.type === ErrorTypeV3.INVALID_SHORT_URL_DELETION;
8+
error?.type === ErrorType.INVALID_SHORT_URL_DELETION;
189

1910
const isProblemDetails = (e: unknown): e is ProblemDetailsError =>
2011
!!e && typeof e === 'object' && ['type', 'detail', 'title', 'status'].every((prop) => prop in e);

src/domains/helpers/DomainDropdown.tsx

+17-22
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import { RowDropdownBtn, useToggle } from '@shlinkio/shlink-frontend-kit';
88
import type { FC } from 'react';
99
import { Link } from 'react-router-dom';
1010
import { DropdownItem } from 'reactstrap';
11-
import { useFeature } from '../../utils/features';
1211
import { useRoutesPrefix } from '../../utils/routesPrefix';
1312
import { DEFAULT_DOMAIN } from '../../visits/reducers/domainVisits';
1413
import { useVisitsComparisonContext } from '../../visits/visits-comparison/VisitsComparisonContext';
@@ -23,33 +22,29 @@ interface DomainDropdownProps {
2322

2423
export const DomainDropdown: FC<DomainDropdownProps> = ({ domain, editDomainRedirects }) => {
2524
const [isModalOpen, toggleModal] = useToggle();
26-
const withVisits = useFeature('domainVisits');
2725
const routesPrefix = useRoutesPrefix();
2826
const visitsComparison = useVisitsComparisonContext();
2927

3028
return (
3129
<RowDropdownBtn>
32-
{withVisits && (
33-
<>
34-
<DropdownItem
35-
tag={Link}
36-
to={`${routesPrefix}/domain/${domain.domain}${domain.isDefault ? `_${DEFAULT_DOMAIN}` : ''}/visits`}
37-
>
38-
<FontAwesomeIcon icon={pieChartIcon} fixedWidth /> Visit stats
39-
</DropdownItem>
40-
<DropdownItem
41-
disabled={!visitsComparison || !visitsComparison.canAddItemWithName(domain.domain)}
42-
onClick={() => visitsComparison?.addItemToCompare({
43-
name: domain.domain,
44-
query: domain.domain,
45-
})}
46-
>
47-
<FontAwesomeIcon icon={lineChartIcon} fixedWidth /> Compare visits
48-
</DropdownItem>
30+
<DropdownItem
31+
tag={Link}
32+
to={`${routesPrefix}/domain/${domain.domain}${domain.isDefault ? `_${DEFAULT_DOMAIN}` : ''}/visits`}
33+
>
34+
<FontAwesomeIcon icon={pieChartIcon} fixedWidth /> Visit stats
35+
</DropdownItem>
36+
<DropdownItem
37+
disabled={!visitsComparison || !visitsComparison.canAddItemWithName(domain.domain)}
38+
onClick={() => visitsComparison?.addItemToCompare({
39+
name: domain.domain,
40+
query: domain.domain,
41+
})}
42+
>
43+
<FontAwesomeIcon icon={lineChartIcon} fixedWidth /> Compare visits
44+
</DropdownItem>
45+
46+
<DropdownItem divider tag="hr" />
4947

50-
<DropdownItem divider tag="hr" />
51-
</>
52-
)}
5348
<DropdownItem onClick={toggleModal}>
5449
<FontAwesomeIcon fixedWidth icon={editIcon} /> Edit redirects
5550
</DropdownItem>

src/short-urls/helpers/ExportShortUrlsBtn.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ const ExportShortUrlsBtn: FCWithDeps<ExportShortUrlsBtnProps, ExportShortUrlsBtn
5252
longUrl: shortUrl.longUrl,
5353
title: shortUrl.title ?? '',
5454
tags: shortUrl.tags.join('|'),
55-
visits: shortUrl?.visitsSummary?.total ?? shortUrl.visitsCount,
55+
visits: shortUrl?.visitsSummary?.total ?? shortUrl.visitsCount ?? 0,
5656
};
5757
}));
5858
stopLoading();

src/short-urls/helpers/ShortUrlStatus.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ interface StatusResult {
2121
const resolveShortUrlStatus = (shortUrl: ShlinkShortUrl): StatusResult => {
2222
const { meta, visitsCount, visitsSummary } = shortUrl;
2323
const { maxVisits, validSince, validUntil } = meta;
24-
const totalVisits = visitsSummary?.total ?? visitsCount;
24+
const totalVisits = visitsSummary?.total ?? visitsCount ?? 0;
2525

2626
if (maxVisits && totalVisits >= maxVisits) {
2727
return {

src/short-urls/helpers/ShortUrlsRow.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ const ShortUrlsRow: FCWithDeps<ShortUrlsRowProps, ShortUrlsRowDeps> = ({ shortUr
8080
<ShortUrlVisitsCount
8181
visitsCount={(
8282
doExcludeBots ? shortUrl.visitsSummary?.nonBots : shortUrl.visitsSummary?.total
83-
) ?? shortUrl.visitsCount}
83+
) ?? shortUrl.visitsCount ?? 0}
8484
shortUrl={shortUrl}
8585
active={active}
8686
asLink

src/short-urls/reducers/shortUrlsList.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createSlice } from '@reduxjs/toolkit';
2-
import type { ShlinkApiClient, ShlinkShortUrlsListParams, ShlinkShortUrlsResponse } from '../../api-contract';
2+
import type { ShlinkApiClient, ShlinkShortUrlsList, ShlinkShortUrlsListParams } from '../../api-contract';
33
import { createAsyncThunk } from '../../utils/redux';
44
import { createNewVisits } from '../../visits/reducers/visitCreation';
55
import { shortUrlMatches } from '../helpers';
@@ -11,7 +11,7 @@ const REDUCER_PREFIX = 'shlink/shortUrlsList';
1111
export const ITEMS_IN_OVERVIEW_PAGE = 5;
1212

1313
export interface ShortUrlsList {
14-
shortUrls?: ShlinkShortUrlsResponse;
14+
shortUrls?: ShlinkShortUrlsList;
1515
loading: boolean;
1616
error: boolean;
1717
}
@@ -23,7 +23,7 @@ const initialState: ShortUrlsList = {
2323

2424
export const listShortUrls = (apiClientFactory: () => ShlinkApiClient) => createAsyncThunk(
2525
`${REDUCER_PREFIX}/listShortUrls`,
26-
(params: ShlinkShortUrlsListParams | void): Promise<ShlinkShortUrlsResponse> => apiClientFactory().listShortUrls(
26+
(params: ShlinkShortUrlsListParams | void): Promise<ShlinkShortUrlsList> => apiClientFactory().listShortUrls(
2727
params ?? {},
2828
),
2929
);

src/tags/reducers/tagsList.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createAction, createSlice } from '@reduxjs/toolkit';
2-
import type { ProblemDetailsError, ShlinkApiClient, ShlinkTags } from '../../api-contract';
2+
import type { ProblemDetailsError, ShlinkApiClient } from '../../api-contract';
33
import { parseApiError } from '../../api-contract/utils';
44
import type { createShortUrl } from '../../short-urls/reducers/shortUrlCreation';
55
import { createAsyncThunk } from '../../utils/redux';
@@ -57,7 +57,7 @@ const increaseVisitsForTags = (tags: TagIncrease[], stats: TagsStatsMap) => tags
5757
bots: tagStats.visitsSummary.bots + bots,
5858
nonBots: tagStats.visitsSummary.nonBots + nonBots,
5959
},
60-
visitsCount: tagStats.visitsCount + bots + nonBots,
60+
visitsCount: (tagStats.visitsCount ?? 0) + bots + nonBots,
6161
},
6262
};
6363
}, { ...stats });
@@ -82,11 +82,12 @@ const calculateVisitsPerTag = (createdVisits: CreateVisit[]): TagIncrease[] => O
8282
export const listTags = (apiClientFactory: () => ShlinkApiClient) => createAsyncThunk(
8383
`${REDUCER_PREFIX}/listTags`,
8484
async (): Promise<ListTags> => {
85-
const { tags, stats }: ShlinkTags = await apiClientFactory().tagsStats();
85+
const { data: stats } = await apiClientFactory().tagsStats();
8686
const processedStats = stats.reduce<TagsStatsMap>((acc, { tag, ...rest }) => {
8787
acc[tag] = rest;
8888
return acc;
8989
}, {});
90+
const tags = Object.keys(processedStats);
9091

9192
return { tags, stats: processedStats };
9293
},

src/utils/features.ts

-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type { SemVer, SemVerOrLatest } from './helpers/version';
33
import { versionMatch } from './helpers/version';
44

55
const supportedFeatures = {
6-
domainVisits: '3.1.0',
76
excludeBotsOnShortUrls: '3.4.0',
87
filterDisabledUrls: '3.4.0',
98
deviceLongUrls: '3.5.0',
@@ -19,7 +18,6 @@ const isFeatureEnabledForVersion = (feature: Feature, serverVersion: SemVerOrLat
1918
serverVersion === 'latest' || versionMatch(serverVersion, { minVersion: supportedFeatures[feature] });
2019

2120
const getFeaturesForVersion = (serverVersion: SemVerOrLatest): Record<Feature, boolean> => ({
22-
domainVisits: isFeatureEnabledForVersion('domainVisits', serverVersion),
2321
excludeBotsOnShortUrls: isFeatureEnabledForVersion('excludeBotsOnShortUrls', serverVersion),
2422
filterDisabledUrls: isFeatureEnabledForVersion('filterDisabledUrls', serverVersion),
2523
deviceLongUrls: isFeatureEnabledForVersion('deviceLongUrls', serverVersion),

src/visits/reducers/common/createLoadVisits.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { range, splitEvery } from '@shlinkio/data-manipulation';
22
import type { ShlinkVisitsParams } from '@shlinkio/shlink-js-sdk/api-contract';
3-
import type { ShlinkPaginator, ShlinkVisit, ShlinkVisits } from '../../../api-contract';
3+
import type { ShlinkPaginator, ShlinkVisit, ShlinkVisitsList } from '../../../api-contract';
44

55
const ITEMS_PER_PAGE = 5000;
66
const PARALLEL_STARTING_PAGE = 2;
@@ -10,7 +10,7 @@ export const DEFAULT_BATCH_SIZE = 4;
1010
const isLastPage = ({ currentPage, pagesCount }: ShlinkPaginator): boolean => currentPage >= pagesCount;
1111
const calcProgress = (total: number, current: number): number => (current * 100) / total;
1212

13-
export type VisitsLoader = (query: ShlinkVisitsParams) => Promise<ShlinkVisits>;
13+
export type VisitsLoader = (query: ShlinkVisitsParams) => Promise<ShlinkVisitsList>;
1414

1515
export type LastVisitLoader = (excludeBots?: boolean) => Promise<ShlinkVisit | undefined>;
1616

@@ -93,7 +93,7 @@ export const createLoadVisits = ({
9393

9494
export const lastVisitLoaderForLoader = (
9595
doIntervalFallback: boolean,
96-
loader: (params: ShlinkVisitsParams) => Promise<ShlinkVisits>,
96+
loader: (params: ShlinkVisitsParams) => Promise<ShlinkVisitsList>,
9797
): LastVisitLoader => async (excludeBots?: boolean) => (
9898
!doIntervalFallback
9999
? Promise.resolve(undefined)

src/visits/reducers/orphanVisits.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ShlinkVisits, ShlinkVisitsParams } from '@shlinkio/shlink-js-sdk/api-contract';
1+
import type { ShlinkVisitsList, ShlinkVisitsParams } from '@shlinkio/shlink-js-sdk/api-contract';
22
import type { ShlinkApiClient, ShlinkOrphanVisit, ShlinkOrphanVisitType } from '../../api-contract';
33
import { isBetween } from '../../utils/dates/helpers/date';
44
import { isOrphanVisit } from '../helpers';
@@ -23,7 +23,7 @@ const initialState: VisitsInfo = {
2323
const matchesType = (visit: ShlinkOrphanVisit, orphanVisitsType?: ShlinkOrphanVisitType) =>
2424
!orphanVisitsType || orphanVisitsType === visit.type;
2525

26-
const filterOrphanVisitsByType = ({ data, ...rest }: ShlinkVisits, type?: ShlinkOrphanVisitType) => {
26+
const filterOrphanVisitsByType = ({ data, ...rest }: ShlinkVisitsList, type?: ShlinkOrphanVisitType) => {
2727
const visits = data.filter((visit) => isOrphanVisit(visit) && matchesType(visit, type));
2828
return { ...rest, data: visits };
2929
};

src/visits/reducers/orphanVisitsDeletion.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { createSlice } from '@reduxjs/toolkit';
2-
import type { ShlinkApiClient, ShlinkDeleteVisitsResponse } from '../../api-contract';
2+
import type { ShlinkApiClient, ShlinkDeleteVisitsResult } from '../../api-contract';
33
import { parseApiError } from '../../api-contract/utils';
44
import { createAsyncThunk } from '../../utils/redux';
55
import type { VisitsDeletion } from './types';
66

77
const REDUCER_PREFIX = 'shlink/orphanVisitsDeletion';
88

9-
export type OrphanVisitsDeletion = VisitsDeletion & ShlinkDeleteVisitsResponse;
9+
export type OrphanVisitsDeletion = VisitsDeletion & ShlinkDeleteVisitsResult;
1010

1111
const initialState: OrphanVisitsDeletion = {
1212
deletedVisits: 0,
@@ -16,7 +16,7 @@ const initialState: OrphanVisitsDeletion = {
1616

1717
export const deleteOrphanVisits = (apiClientFactory: () => ShlinkApiClient) => createAsyncThunk(
1818
`${REDUCER_PREFIX}/deleteOrphanVisits`,
19-
(): Promise<ShlinkDeleteVisitsResponse> => apiClientFactory().deleteOrphanVisits(),
19+
(): Promise<ShlinkDeleteVisitsResult> => apiClientFactory().deleteOrphanVisits(),
2020
);
2121

2222
export const orphanVisitsDeletionReducerCreator = (

src/visits/reducers/shortUrlVisitsDeletion.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { createSlice } from '@reduxjs/toolkit';
2-
import type { ShlinkApiClient, ShlinkDeleteVisitsResponse } from '../../api-contract';
2+
import type { ShlinkApiClient, ShlinkDeleteVisitsResult } from '../../api-contract';
33
import { parseApiError } from '../../api-contract/utils';
44
import type { ShortUrlIdentifier } from '../../short-urls/data';
55
import { createAsyncThunk } from '../../utils/redux';
66
import type { VisitsDeletion } from './types';
77

88
const REDUCER_PREFIX = 'shlink/shortUrlVisitsDeletion';
99

10-
type DeleteVisitsResult = ShlinkDeleteVisitsResponse & ShortUrlIdentifier;
10+
type DeleteVisitsResult = ShlinkDeleteVisitsResult & ShortUrlIdentifier;
1111

1212
export type ShortUrlVisitsDeletion = VisitsDeletion & DeleteVisitsResult;
1313

src/visits/reducers/visitsOverview.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const countBots = (visits: CreateVisit[]) => visits.filter(({ visit }) => visit.
3939
export const loadVisitsOverview = (apiClientFactory: () => ShlinkApiClient) => createAsyncThunk(
4040
`${REDUCER_PREFIX}/loadVisitsOverview`,
4141
(): Promise<ParsedVisitsOverview> => apiClientFactory().getVisitsOverview().then(
42-
({ nonOrphanVisits, visitsCount, orphanVisits, orphanVisitsCount }) => ({
42+
({ nonOrphanVisits, visitsCount = 0, orphanVisits, orphanVisitsCount = 0 }) => ({
4343
nonOrphanVisits: {
4444
total: nonOrphanVisits?.total ?? visitsCount,
4545
nonBots: nonOrphanVisits?.nonBots,

0 commit comments

Comments
 (0)