mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 01:20:24 +03:00
Move all relevant API types to api-contract
This commit is contained in:
parent
d97db9e17c
commit
f16f51a2b3
10 changed files with 66 additions and 61 deletions
|
@ -1,12 +1,12 @@
|
|||
import type { ShortUrlData } from '../short-urls/data';
|
||||
import type {
|
||||
ShlinkCreateShortUrlData,
|
||||
ShlinkDomainRedirects,
|
||||
ShlinkDomainsResponse,
|
||||
ShlinkEditDomainRedirects,
|
||||
ShlinkEditShortUrlData,
|
||||
ShlinkHealth,
|
||||
ShlinkMercureInfo,
|
||||
ShlinkShortUrl,
|
||||
ShlinkShortUrlData,
|
||||
ShlinkShortUrlsListParams,
|
||||
ShlinkShortUrlsResponse,
|
||||
ShlinkTags,
|
||||
|
@ -21,7 +21,7 @@ export type ShlinkApiClient = {
|
|||
|
||||
listShortUrls(params?: ShlinkShortUrlsListParams): Promise<ShlinkShortUrlsResponse>;
|
||||
|
||||
createShortUrl(options: ShortUrlData): Promise<ShlinkShortUrl>;
|
||||
createShortUrl(options: ShlinkCreateShortUrlData): Promise<ShlinkShortUrl>;
|
||||
|
||||
getShortUrlVisits(shortCode: string, query?: ShlinkVisitsParams): Promise<ShlinkVisits>;
|
||||
|
||||
|
@ -42,7 +42,7 @@ export type ShlinkApiClient = {
|
|||
updateShortUrl(
|
||||
shortCode: string,
|
||||
domain: string | null | undefined,
|
||||
body: ShlinkShortUrlData,
|
||||
body: ShlinkEditShortUrlData,
|
||||
): Promise<ShlinkShortUrl>;
|
||||
|
||||
listTags(): Promise<ShlinkTags>;
|
||||
|
|
|
@ -31,6 +31,34 @@ export interface ShlinkShortUrl {
|
|||
forwardQuery?: boolean;
|
||||
}
|
||||
|
||||
export interface ShlinkEditShortUrlData {
|
||||
longUrl?: string;
|
||||
title?: string | null;
|
||||
tags?: string[];
|
||||
deviceLongUrls?: ShlinkDeviceLongUrls;
|
||||
crawlable?: boolean;
|
||||
forwardQuery?: boolean;
|
||||
validSince?: string | null;
|
||||
validUntil?: string | null;
|
||||
maxVisits?: number | null;
|
||||
|
||||
/** @deprecated */
|
||||
validateUrl?: boolean;
|
||||
}
|
||||
|
||||
export interface ShlinkCreateShortUrlData extends Omit<ShlinkEditShortUrlData, 'deviceLongUrls'> {
|
||||
longUrl: string;
|
||||
customSlug?: string;
|
||||
shortCodeLength?: number;
|
||||
domain?: string;
|
||||
findIfExists?: boolean;
|
||||
deviceLongUrls?: {
|
||||
android?: string;
|
||||
ios?: string;
|
||||
desktop?: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ShlinkShortUrlsResponse {
|
||||
data: ShlinkShortUrl[];
|
||||
pagination: ShlinkPaginator;
|
||||
|
@ -106,20 +134,6 @@ export interface ShlinkVisitsParams {
|
|||
excludeBots?: boolean;
|
||||
}
|
||||
|
||||
export interface ShlinkShortUrlData {
|
||||
longUrl?: string;
|
||||
title?: string | null;
|
||||
/** @deprecated */
|
||||
validateUrl?: boolean;
|
||||
tags?: string[];
|
||||
deviceLongUrls?: ShlinkDeviceLongUrls;
|
||||
crawlable?: boolean;
|
||||
forwardQuery?: boolean;
|
||||
validSince?: string | null;
|
||||
validUntil?: string | null;
|
||||
maxVisits?: number | null;
|
||||
}
|
||||
|
||||
export interface ShlinkDomainRedirects {
|
||||
baseUrlRedirect: string | null;
|
||||
regular404Redirect: string | null;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import type { ShlinkCreateShortUrlData } from '@shlinkio/shlink-web-component/api-contract';
|
||||
import type { FC } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import type { ShortUrlCreationSettings } from '../utils/settings';
|
||||
import { useSetting } from '../utils/settings';
|
||||
import type { ShortUrlData } from './data';
|
||||
import type { CreateShortUrlResultProps } from './helpers/CreateShortUrlResult';
|
||||
import type { ShortUrlCreation } from './reducers/shortUrlCreation';
|
||||
import type { ShortUrlFormProps } from './ShortUrlForm';
|
||||
|
@ -13,11 +13,11 @@ export interface CreateShortUrlProps {
|
|||
|
||||
interface CreateShortUrlConnectProps extends CreateShortUrlProps {
|
||||
shortUrlCreation: ShortUrlCreation;
|
||||
createShortUrl: (data: ShortUrlData) => Promise<void>;
|
||||
createShortUrl: (data: ShlinkCreateShortUrlData) => Promise<void>;
|
||||
resetCreateShortUrl: () => void;
|
||||
}
|
||||
|
||||
const getInitialState = (settings?: ShortUrlCreationSettings): ShortUrlData => ({
|
||||
const getInitialState = (settings?: ShortUrlCreationSettings): ShlinkCreateShortUrlData => ({
|
||||
longUrl: '',
|
||||
tags: [],
|
||||
customSlug: '',
|
||||
|
@ -33,7 +33,7 @@ const getInitialState = (settings?: ShortUrlCreationSettings): ShortUrlData => (
|
|||
});
|
||||
|
||||
export const CreateShortUrl = (
|
||||
ShortUrlForm: FC<ShortUrlFormProps>,
|
||||
ShortUrlForm: FC<ShortUrlFormProps<ShlinkCreateShortUrlData>>,
|
||||
CreateShortUrlResult: FC<CreateShortUrlResultProps>,
|
||||
) => ({
|
||||
createShortUrl,
|
||||
|
@ -50,7 +50,7 @@ export const CreateShortUrl = (
|
|||
initialState={initialState}
|
||||
saving={shortUrlCreation.saving}
|
||||
mode={basicMode ? 'create-basic' : 'create'}
|
||||
onSave={async (data: ShortUrlData) => {
|
||||
onSave={async (data) => {
|
||||
resetCreateShortUrl();
|
||||
return createShortUrl(data);
|
||||
}}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { faArrowLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Message, parseQuery, Result } from '@shlinkio/shlink-frontend-kit';
|
||||
import type { ShlinkEditShortUrlData } from '@shlinkio/shlink-web-component/api-contract';
|
||||
import type { FC } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { ExternalLink } from 'react-external-link';
|
||||
|
@ -22,7 +23,7 @@ interface EditShortUrlConnectProps {
|
|||
editShortUrl: (editShortUrl: EditShortUrlInfo) => void;
|
||||
}
|
||||
|
||||
export const EditShortUrl = (ShortUrlForm: FC<ShortUrlFormProps>) => ({
|
||||
export const EditShortUrl = (ShortUrlForm: FC<ShortUrlFormProps<ShlinkEditShortUrlData>>) => ({
|
||||
shortUrlDetail,
|
||||
getShortUrlDetail,
|
||||
shortUrlEdition,
|
||||
|
|
|
@ -9,7 +9,7 @@ import type { ChangeEvent, FC } from 'react';
|
|||
import { useEffect, useState } from 'react';
|
||||
import { Button, FormGroup, Input, Row } from 'reactstrap';
|
||||
import type { InputType } from 'reactstrap/types/lib/Input';
|
||||
import type { ShlinkDeviceLongUrls } from '../api-contract';
|
||||
import type { ShlinkCreateShortUrlData, ShlinkDeviceLongUrls, ShlinkEditShortUrlData } from '../api-contract';
|
||||
import type { DomainSelectorProps } from '../domains/DomainSelector';
|
||||
import type { TagsSelectorProps } from '../tags/helpers/TagsSelector';
|
||||
import { IconInput } from '../utils/components/IconInput';
|
||||
|
@ -18,7 +18,6 @@ import { DateTimeInput } from '../utils/dates/DateTimeInput';
|
|||
import { formatIsoDate } from '../utils/dates/helpers/date';
|
||||
import { useFeature } from '../utils/features';
|
||||
import { handleEventPreventingDefault, hasValue } from '../utils/helpers';
|
||||
import type { ShortUrlData } from './data';
|
||||
import { ShortUrlFormCheckboxGroup } from './helpers/ShortUrlFormCheckboxGroup';
|
||||
import { UseExistingIfFoundInfoIcon } from './UseExistingIfFoundInfoIcon';
|
||||
import './ShortUrlForm.scss';
|
||||
|
@ -28,25 +27,32 @@ export type Mode = 'create' | 'create-basic' | 'edit';
|
|||
type DateFields = 'validSince' | 'validUntil';
|
||||
type NonDateFields = 'longUrl' | 'customSlug' | 'shortCodeLength' | 'domain' | 'maxVisits' | 'title';
|
||||
|
||||
export interface ShortUrlFormProps {
|
||||
export interface ShortUrlFormProps<T extends ShlinkCreateShortUrlData | ShlinkEditShortUrlData> {
|
||||
// FIXME Try to get rid of the mode param, and infer creation or edition from initialState if possible
|
||||
mode: Mode;
|
||||
saving: boolean;
|
||||
initialState: ShortUrlData;
|
||||
onSave: (shortUrlData: ShortUrlData) => Promise<unknown>;
|
||||
initialState: T;
|
||||
onSave: (shortUrlData: T) => Promise<unknown>;
|
||||
}
|
||||
|
||||
const normalizeTag = pipe(trim, replace(/ /g, '-'));
|
||||
const toDate = (date?: string | Date): Date | undefined => (typeof date === 'string' ? parseISO(date) : date);
|
||||
|
||||
const isCreationData = (data: ShlinkCreateShortUrlData | ShlinkEditShortUrlData): data is ShlinkCreateShortUrlData =>
|
||||
'shortCodeLength' in data && 'customSlug' in data && 'domain' in data;
|
||||
|
||||
export const ShortUrlForm = (
|
||||
TagsSelector: FC<TagsSelectorProps>,
|
||||
DomainSelector: FC<DomainSelectorProps>,
|
||||
): FC<ShortUrlFormProps> => ({ mode, saving, onSave, initialState }) => {
|
||||
) => function ShortUrlFormComp<T extends ShlinkCreateShortUrlData | ShlinkEditShortUrlData>(
|
||||
{ mode, saving, onSave, initialState }: ShortUrlFormProps<T>,
|
||||
) {
|
||||
const [shortUrlData, setShortUrlData] = useState(initialState);
|
||||
const reset = () => setShortUrlData(initialState);
|
||||
const supportsDeviceLongUrls = useFeature('deviceLongUrls');
|
||||
|
||||
const isEdit = mode === 'edit';
|
||||
const isCreation = isCreationData(shortUrlData);
|
||||
const isBasicMode = mode === 'create-basic';
|
||||
const changeTags = (tags: string[]) => setShortUrlData({ ...shortUrlData, tags: tags.map(normalizeTag) });
|
||||
const setResettableValue = (value: string, initialValue?: any) => {
|
||||
|
@ -82,6 +88,7 @@ export const ShortUrlForm = (
|
|||
id={id}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
// @ts-expect-error FIXME Make sure id is a key from T
|
||||
value={shortUrlData[id] ?? ''}
|
||||
onChange={props.onChange ?? ((e) => setShortUrlData({ ...shortUrlData, [id]: e.target.value }))}
|
||||
{...props}
|
||||
|
@ -171,7 +178,7 @@ export const ShortUrlForm = (
|
|||
title: setResettableValue(target.value, initialState.title),
|
||||
}),
|
||||
})}
|
||||
{!isEdit && (
|
||||
{!isEdit && isCreation && (
|
||||
<>
|
||||
<Row>
|
||||
<div className="col-lg-6">
|
||||
|
@ -216,7 +223,7 @@ export const ShortUrlForm = (
|
|||
>
|
||||
Validate URL
|
||||
</ShortUrlFormCheckboxGroup>
|
||||
{!isEdit && (
|
||||
{!isEdit && isCreation && (
|
||||
<p>
|
||||
<Checkbox
|
||||
inline
|
||||
|
|
|
@ -1,20 +1,7 @@
|
|||
import type { Order } from '@shlinkio/shlink-frontend-kit';
|
||||
import type { ShlinkShortUrl, ShlinkShortUrlData } from '../../api-contract';
|
||||
import type { ShlinkShortUrl } from '../../api-contract';
|
||||
import type { OptionalString } from '../../utils/helpers';
|
||||
|
||||
export interface ShortUrlData extends Omit<ShlinkShortUrlData, 'deviceLongUrls'> {
|
||||
longUrl: string;
|
||||
customSlug?: string;
|
||||
shortCodeLength?: number;
|
||||
domain?: string;
|
||||
findIfExists?: boolean;
|
||||
deviceLongUrls?: {
|
||||
android?: string;
|
||||
ios?: string;
|
||||
desktop?: string;
|
||||
}
|
||||
}
|
||||
|
||||
export interface ShortUrlIdentifier {
|
||||
shortCode: string;
|
||||
domain?: OptionalString;
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { isNil } from 'ramda';
|
||||
import type { ShlinkShortUrl } from '../../api-contract';
|
||||
import type { ShlinkCreateShortUrlData, ShlinkShortUrl } from '../../api-contract';
|
||||
import type { OptionalString } from '../../utils/helpers';
|
||||
import type { ShortUrlCreationSettings } from '../../utils/settings';
|
||||
import { DEFAULT_DOMAIN } from '../../visits/reducers/domainVisits';
|
||||
import type { ShortUrlData } from '../data';
|
||||
|
||||
export const shortUrlMatches = (shortUrl: ShlinkShortUrl, shortCode: string, domain: OptionalString): boolean => {
|
||||
if (isNil(domain)) {
|
||||
|
@ -21,10 +20,11 @@ export const domainMatches = (shortUrl: ShlinkShortUrl, domain: string): boolean
|
|||
return shortUrl.domain === domain;
|
||||
};
|
||||
|
||||
// FIXME This should return ShlinkEditShortUrlData
|
||||
export const shortUrlDataFromShortUrl = (
|
||||
shortUrl?: ShlinkShortUrl,
|
||||
settings?: ShortUrlCreationSettings,
|
||||
): ShortUrlData => {
|
||||
): ShlinkCreateShortUrlData => {
|
||||
const validateUrl = settings?.validateUrls ?? false;
|
||||
|
||||
if (!shortUrl) {
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { ProblemDetailsError, ShlinkApiClient, ShlinkShortUrl } from '../../api-contract';
|
||||
import type { ProblemDetailsError, ShlinkApiClient, ShlinkCreateShortUrlData, ShlinkShortUrl } from '../../api-contract';
|
||||
import { parseApiError } from '../../api-contract/utils';
|
||||
import { createAsyncThunk } from '../../utils/redux';
|
||||
import type { ShortUrlData } from '../data';
|
||||
|
||||
const REDUCER_PREFIX = 'shlink/shortUrlCreation';
|
||||
|
||||
|
@ -27,8 +25,6 @@ export type ShortUrlCreation = {
|
|||
error: false;
|
||||
};
|
||||
|
||||
export type CreateShortUrlAction = PayloadAction<ShlinkShortUrl>;
|
||||
|
||||
const initialState: ShortUrlCreation = {
|
||||
saving: false,
|
||||
saved: false,
|
||||
|
@ -37,7 +33,7 @@ const initialState: ShortUrlCreation = {
|
|||
|
||||
export const createShortUrl = (apiClientFactory: () => ShlinkApiClient) => createAsyncThunk(
|
||||
`${REDUCER_PREFIX}/createShortUrl`,
|
||||
(data: ShortUrlData): Promise<ShlinkShortUrl> => apiClientFactory().createShortUrl(data),
|
||||
(data: ShlinkCreateShortUrlData): Promise<ShlinkShortUrl> => apiClientFactory().createShortUrl(data),
|
||||
);
|
||||
|
||||
export const shortUrlCreationReducerCreator = (createShortUrlThunk: ReturnType<typeof createShortUrl>) => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { ProblemDetailsError, ShlinkApiClient, ShlinkShortUrl, ShlinkShortUrlData } from '../../api-contract';
|
||||
import type { ProblemDetailsError, ShlinkApiClient, ShlinkEditShortUrlData, ShlinkShortUrl } from '../../api-contract';
|
||||
import { parseApiError } from '../../api-contract/utils';
|
||||
import { createAsyncThunk } from '../../utils/redux';
|
||||
import type { ShortUrlIdentifier } from '../data';
|
||||
|
@ -15,7 +15,7 @@ export interface ShortUrlEdition {
|
|||
}
|
||||
|
||||
export interface EditShortUrl extends ShortUrlIdentifier {
|
||||
data: ShlinkShortUrlData;
|
||||
data: ShlinkEditShortUrlData;
|
||||
}
|
||||
|
||||
const initialState: ShortUrlEdition = {
|
||||
|
|
|
@ -2,13 +2,14 @@ import { orderToString, stringifyQuery } from '@shlinkio/shlink-frontend-kit';
|
|||
import type {
|
||||
RegularNotFound,
|
||||
ShlinkApiClient as BaseShlinkApiClient,
|
||||
ShlinkCreateShortUrlData,
|
||||
ShlinkDomainRedirects,
|
||||
ShlinkDomainsResponse,
|
||||
ShlinkEditDomainRedirects,
|
||||
ShlinkEditShortUrlData,
|
||||
ShlinkHealth,
|
||||
ShlinkMercureInfo,
|
||||
ShlinkShortUrl,
|
||||
ShlinkShortUrlData,
|
||||
ShlinkShortUrlsListNormalizedParams,
|
||||
ShlinkShortUrlsListParams,
|
||||
ShlinkShortUrlsResponse,
|
||||
|
@ -24,7 +25,6 @@ import {
|
|||
ErrorTypeV3,
|
||||
} from '@shlinkio/shlink-web-component/api-contract';
|
||||
import { isEmpty, isNil, reject } from 'ramda';
|
||||
import type { ShortUrlData } from '../../../shlink-web-component/src/short-urls/data';
|
||||
import type { HttpClient } from '../../common/services/HttpClient';
|
||||
import { replaceAuthorityFromUri } from '../../utils/helpers/uri';
|
||||
import type { OptionalString } from '../../utils/utils';
|
||||
|
@ -73,7 +73,7 @@ export class ShlinkApiClient implements BaseShlinkApiClient {
|
|||
{ url: '/short-urls', query: normalizeListParams(params) },
|
||||
).then(({ shortUrls }) => shortUrls);
|
||||
|
||||
public readonly createShortUrl = async (options: ShortUrlData): Promise<ShlinkShortUrl> => {
|
||||
public readonly createShortUrl = async (options: ShlinkCreateShortUrlData): Promise<ShlinkShortUrl> => {
|
||||
const body = reject((value) => isEmpty(value) || isNil(value), options as any);
|
||||
return this.performRequest<ShlinkShortUrl>({ url: '/short-urls', method: 'POST', body });
|
||||
};
|
||||
|
@ -106,7 +106,7 @@ export class ShlinkApiClient implements BaseShlinkApiClient {
|
|||
public readonly updateShortUrl = async (
|
||||
shortCode: string,
|
||||
domain: OptionalString,
|
||||
body: ShlinkShortUrlData,
|
||||
body: ShlinkEditShortUrlData,
|
||||
): Promise<ShlinkShortUrl> =>
|
||||
this.performRequest<ShlinkShortUrl>({ url: `/short-urls/${shortCode}`, method: 'PATCH', query: { domain }, body });
|
||||
|
||||
|
|
Loading…
Reference in a new issue