Migrated first short URL reducers to typescript

This commit is contained in:
Alejandro Celaya
2020-08-24 18:52:52 +02:00
parent fefa4e7848
commit d8f3952920
9 changed files with 134 additions and 35 deletions

View File

@@ -2,6 +2,8 @@ import { MercureInfo } from '../mercure/reducers/mercureInfo';
import { ServersMap } from '../servers/reducers/servers';
import { SelectedServer } from '../servers/data';
import { Settings } from '../settings/reducers/settings';
import { ShortUrlMetaEdition } from '../short-urls/reducers/shortUrlMeta';
import { ShortUrlCreation } from '../short-urls/reducers/shortUrlCreation';
export type ConnectDecorator = (props: string[], actions?: string[]) => any;
@@ -10,10 +12,10 @@ export interface ShlinkState {
selectedServer: SelectedServer;
shortUrlsList: any;
shortUrlsListParams: any;
shortUrlCreationResult: any;
shortUrlCreationResult: ShortUrlCreation;
shortUrlDeletion: any;
shortUrlTags: any;
shortUrlMeta: any;
shortUrlMeta: ShortUrlMetaEdition;
shortUrlEdition: any;
shortUrlVisits: any;
tagVisits: any;

View File

@@ -0,0 +1,29 @@
import { Nullable } from '../../utils/utils';
export interface ShortUrlData {
longUrl: string;
tags?: string[];
customSlug?: string;
shortCodeLength?: number;
domain?: string;
validSince?: string;
validUntil?: string;
maxVisits?: number;
findIfExists?: boolean;
}
export interface ShortUrl {
shortCode: string;
shortUrl: string;
longUrl: string;
visitsCount: number;
meta: Required<Nullable<ShortUrlMeta>>;
tags: string[];
domain: string | null;
}
export interface ShortUrlMeta {
validSince?: string;
validUntil?: string;
maxVisits?: number;
}

View File

@@ -1,5 +1,9 @@
import PropTypes from 'prop-types';
import { createAction, handleActions } from 'redux-actions';
import { Action, Dispatch } from 'redux';
import { ShlinkApiClientBuilder } from '../../utils/services/types';
import { GetState } from '../../container/types';
import { ShortUrl, ShortUrlData } from '../data';
/* eslint-disable padding-line-between-statements */
export const CREATE_SHORT_URL_START = 'shlink/createShortUrl/CREATE_SHORT_URL_START';
@@ -8,6 +12,7 @@ export const CREATE_SHORT_URL = 'shlink/createShortUrl/CREATE_SHORT_URL';
export const RESET_CREATE_SHORT_URL = 'shlink/createShortUrl/RESET_CREATE_SHORT_URL';
/* eslint-enable padding-line-between-statements */
/** @deprecated Use ShortUrlCreation interface instead */
export const createShortUrlResultType = PropTypes.shape({
result: PropTypes.shape({
shortUrl: PropTypes.string,
@@ -16,27 +21,36 @@ export const createShortUrlResultType = PropTypes.shape({
error: PropTypes.bool,
});
const initialState = {
export interface ShortUrlCreation {
result: ShortUrl | null;
saving: boolean;
error: boolean;
}
const initialState: ShortUrlCreation = {
result: null,
saving: false,
error: false,
};
export default handleActions({
export default handleActions<ShortUrlCreation, any>({
[CREATE_SHORT_URL_START]: (state) => ({ ...state, saving: true, error: false }),
[CREATE_SHORT_URL_ERROR]: (state) => ({ ...state, saving: false, error: true }),
[CREATE_SHORT_URL]: (state, { result }) => ({ result, saving: false, error: false }),
[CREATE_SHORT_URL]: (_, { result }: any) => ({ result, saving: false, error: false }),
[RESET_CREATE_SHORT_URL]: () => initialState,
}, initialState);
export const createShortUrl = (buildShlinkApiClient) => (data) => async (dispatch, getState) => {
export const createShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => (data: ShortUrlData) => async (
dispatch: Dispatch,
getState: GetState,
) => {
dispatch({ type: CREATE_SHORT_URL_START });
const { createShortUrl } = buildShlinkApiClient(getState);
try {
const result = await createShortUrl(data);
dispatch({ type: CREATE_SHORT_URL, result });
dispatch<Action & { result: ShortUrl }>({ type: CREATE_SHORT_URL, result });
} catch (e) {
dispatch({ type: CREATE_SHORT_URL_ERROR });

View File

@@ -1,5 +1,9 @@
import { createAction, handleActions } from 'redux-actions';
import { createAction, handleActions, Action } from 'redux-actions';
import PropTypes from 'prop-types';
import { Dispatch } from 'redux';
import { ShortUrlMeta } from '../data';
import { ShlinkApiClientBuilder } from '../../utils/services/types';
import { GetState } from '../../container/types';
/* eslint-disable padding-line-between-statements */
export const EDIT_SHORT_URL_META_START = 'shlink/shortUrlMeta/EDIT_SHORT_URL_META_START';
@@ -8,12 +12,14 @@ export const SHORT_URL_META_EDITED = 'shlink/shortUrlMeta/SHORT_URL_META_EDITED'
export const RESET_EDIT_SHORT_URL_META = 'shlink/shortUrlMeta/RESET_EDIT_SHORT_URL_META';
/* eslint-enable padding-line-between-statements */
/** @deprecated Use ShortUrlMeta interface instead */
export const shortUrlMetaType = PropTypes.shape({
validSince: PropTypes.string,
validUntil: PropTypes.string,
maxVisits: PropTypes.number,
});
/** @deprecated Use ShortUrlMetaEdition interface instead */
export const shortUrlEditMetaType = PropTypes.shape({
shortCode: PropTypes.string,
meta: shortUrlMetaType.isRequired,
@@ -21,27 +27,47 @@ export const shortUrlEditMetaType = PropTypes.shape({
error: PropTypes.bool.isRequired,
});
const initialState = {
export interface ShortUrlMetaEdition {
shortCode: string | null;
meta: ShortUrlMeta;
saving: boolean;
error: boolean;
}
interface ShortUrlMetaEditedAction {
shortCode: string;
domain?: string | null;
meta: ShortUrlMeta;
}
const initialState: ShortUrlMetaEdition = {
shortCode: null,
meta: {},
saving: false,
error: false,
};
export default handleActions({
export default handleActions<ShortUrlMetaEdition, ShortUrlMetaEditedAction>({
[EDIT_SHORT_URL_META_START]: (state) => ({ ...state, saving: true, error: false }),
[EDIT_SHORT_URL_META_ERROR]: (state) => ({ ...state, saving: false, error: true }),
[SHORT_URL_META_EDITED]: (state, { shortCode, meta }) => ({ shortCode, meta, saving: false, error: false }),
[SHORT_URL_META_EDITED]: (_, { payload }) => ({ ...payload, saving: false, error: false }),
[RESET_EDIT_SHORT_URL_META]: () => initialState,
}, initialState);
export const editShortUrlMeta = (buildShlinkApiClient) => (shortCode, domain, meta) => async (dispatch, getState) => {
export const editShortUrlMeta = (buildShlinkApiClient: ShlinkApiClientBuilder) => (
shortCode: string,
domain: string | null | undefined,
meta: ShortUrlMeta,
) => async (dispatch: Dispatch, getState: GetState) => {
dispatch({ type: EDIT_SHORT_URL_META_START });
const { updateShortUrlMeta } = buildShlinkApiClient(getState);
try {
await updateShortUrlMeta(shortCode, domain, meta);
dispatch({ shortCode, meta, domain, type: SHORT_URL_META_EDITED });
dispatch<Action<ShortUrlMetaEditedAction>>({
type: SHORT_URL_META_EDITED,
payload: { shortCode, meta, domain },
});
} catch (e) {
dispatch({ type: EDIT_SHORT_URL_META_ERROR });

View File

@@ -30,6 +30,7 @@ const initialState = {
error: false,
};
// TODO Make all actions fetch shortCode, domain and prop from payload
const setPropFromActionOnMatchingShortUrl = (prop) => (state, { shortCode, domain, [prop]: propValue }) => assocPath(
[ 'shortUrls', 'data' ],
state.shortUrls.data.map(
@@ -48,7 +49,13 @@ export default handleActions({
state,
),
[SHORT_URL_TAGS_EDITED]: setPropFromActionOnMatchingShortUrl('tags'),
[SHORT_URL_META_EDITED]: setPropFromActionOnMatchingShortUrl('meta'),
[SHORT_URL_META_EDITED]: (state, { payload: { shortCode, domain, meta } }) => assocPath(
[ 'shortUrls', 'data' ],
state.shortUrls.data.map(
(shortUrl) => shortUrlMatches(shortUrl, shortCode, domain) ? assoc('meta', meta, shortUrl) : shortUrl,
),
state,
),
[SHORT_URL_EDITED]: setPropFromActionOnMatchingShortUrl('longUrl'),
[CREATE_VISIT]: (state, { shortUrl: { shortCode, domain, visitsCount } }) => assocPath(
[ 'shortUrls', 'data' ],

View File

@@ -21,3 +21,7 @@ export const rangeOf = <T>(size: number, mappingFn: (value: number) => T, startA
export type Empty = null | undefined | '' | never[];
export const hasValue = <T>(value: T | Empty): value is T => !isNil(value) && !isEmpty(value);
export type Nullable<T> = {
[P in keyof T]: T[P] | null
};