mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-10 02:07:26 +03:00
Removed dependency on redux-actions for all reducers already migrated to typescript
This commit is contained in:
parent
d8f3952920
commit
f04aece7df
15 changed files with 99 additions and 74 deletions
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -3441,12 +3441,6 @@
|
||||||
"popper.js": "^1.14.1"
|
"popper.js": "^1.14.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/redux-actions": {
|
|
||||||
"version": "2.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/redux-actions/-/redux-actions-2.6.1.tgz",
|
|
||||||
"integrity": "sha512-zKgK+ATp3sswXs6sOYo1tk8xdXTy4CTaeeYrVQlClCjeOpag5vzPo0ASWiiBJ7vsiQRAdb3VkuFLnDoBimF67g==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/stack-utils": {
|
"@types/stack-utils": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
|
||||||
|
|
|
@ -86,7 +86,6 @@
|
||||||
"@types/react-redux": "^7.1.9",
|
"@types/react-redux": "^7.1.9",
|
||||||
"@types/react-router-dom": "^5.1.5",
|
"@types/react-router-dom": "^5.1.5",
|
||||||
"@types/reactstrap": "^8.5.1",
|
"@types/reactstrap": "^8.5.1",
|
||||||
"@types/redux-actions": "^2.6.1",
|
|
||||||
"@types/uuid": "^8.3.0",
|
"@types/uuid": "^8.3.0",
|
||||||
"adm-zip": "^0.4.13",
|
"adm-zip": "^0.4.13",
|
||||||
"autoprefixer": "^9.6.3",
|
"autoprefixer": "^9.6.3",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import { Action, handleActions } from 'redux-actions';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Dispatch } from 'redux';
|
import { Action, Dispatch } from 'redux';
|
||||||
import { ShlinkApiClientBuilder, ShlinkMercureInfo } from '../../utils/services/types';
|
import { ShlinkApiClientBuilder, ShlinkMercureInfo } from '../../utils/services/types';
|
||||||
import { GetState } from '../../container/types';
|
import { GetState } from '../../container/types';
|
||||||
|
import { buildReducer } from '../../utils/helpers/redux';
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const GET_MERCURE_INFO_START = 'shlink/mercure/GET_MERCURE_INFO_START';
|
export const GET_MERCURE_INFO_START = 'shlink/mercure/GET_MERCURE_INFO_START';
|
||||||
|
@ -25,15 +25,17 @@ export interface MercureInfo {
|
||||||
error: boolean;
|
error: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type GetMercureInfoAction = Action<string> & ShlinkMercureInfo;
|
||||||
|
|
||||||
const initialState: MercureInfo = {
|
const initialState: MercureInfo = {
|
||||||
loading: true,
|
loading: true,
|
||||||
error: false,
|
error: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handleActions<MercureInfo, ShlinkMercureInfo>({
|
export default buildReducer<MercureInfo, GetMercureInfoAction>({
|
||||||
[GET_MERCURE_INFO_START]: (state) => ({ ...state, loading: true, error: false }),
|
[GET_MERCURE_INFO_START]: (state) => ({ ...state, loading: true, error: false }),
|
||||||
[GET_MERCURE_INFO_ERROR]: (state) => ({ ...state, loading: false, error: true }),
|
[GET_MERCURE_INFO_ERROR]: (state) => ({ ...state, loading: false, error: true }),
|
||||||
[GET_MERCURE_INFO]: (_, { payload }) => ({ ...payload, loading: false, error: false }),
|
[GET_MERCURE_INFO]: (_, { token, mercureHubUrl }) => ({ token, mercureHubUrl, loading: false, error: false }),
|
||||||
}, initialState);
|
}, initialState);
|
||||||
|
|
||||||
export const loadMercureInfo = (buildShlinkApiClient: ShlinkApiClientBuilder) =>
|
export const loadMercureInfo = (buildShlinkApiClient: ShlinkApiClientBuilder) =>
|
||||||
|
@ -50,9 +52,9 @@ export const loadMercureInfo = (buildShlinkApiClient: ShlinkApiClientBuilder) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = await mercureInfo();
|
const result = await mercureInfo();
|
||||||
|
|
||||||
dispatch<Action<ShlinkMercureInfo>>({ type: GET_MERCURE_INFO, payload });
|
dispatch<Action<ShlinkMercureInfo>>({ type: GET_MERCURE_INFO, ...result });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dispatch({ type: GET_MERCURE_INFO_ERROR });
|
dispatch({ type: GET_MERCURE_INFO_ERROR });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { createAction, handleActions } from 'redux-actions';
|
|
||||||
import { identity, memoizeWith, pipe } from 'ramda';
|
import { identity, memoizeWith, pipe } from 'ramda';
|
||||||
import { Action, Dispatch } from 'redux';
|
import { Action, Dispatch } from 'redux';
|
||||||
import { resetShortUrlParams } from '../../short-urls/reducers/shortUrlsListParams';
|
import { resetShortUrlParams } from '../../short-urls/reducers/shortUrlsListParams';
|
||||||
import { versionToPrintable, versionToSemVer as toSemVer } from '../../utils/helpers/version';
|
import { versionToPrintable, versionToSemVer as toSemVer } from '../../utils/helpers/version';
|
||||||
import { NonReachableServer, NotFoundServer, ReachableServer, SelectedServer } from '../data';
|
import { SelectedServer } from '../data';
|
||||||
import { GetState } from '../../container/types';
|
import { GetState } from '../../container/types';
|
||||||
import { ShlinkApiClientBuilder, ShlinkHealth } from '../../utils/services/types';
|
import { ShlinkApiClientBuilder, ShlinkHealth } from '../../utils/services/types';
|
||||||
|
import { buildActionCreator, buildReducer } from '../../utils/helpers/redux';
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const SELECT_SERVER = 'shlink/selectedServer/SELECT_SERVER';
|
export const SELECT_SERVER = 'shlink/selectedServer/SELECT_SERVER';
|
||||||
|
@ -16,7 +16,10 @@ export const MAX_FALLBACK_VERSION = '999.999.999';
|
||||||
export const LATEST_VERSION_CONSTRAINT = 'latest';
|
export const LATEST_VERSION_CONSTRAINT = 'latest';
|
||||||
/* eslint-enable padding-line-between-statements */
|
/* eslint-enable padding-line-between-statements */
|
||||||
|
|
||||||
const initialState: SelectedServer = null;
|
export interface SelectServerAction extends Action<string> {
|
||||||
|
selectedServer: SelectedServer;
|
||||||
|
}
|
||||||
|
|
||||||
const versionToSemVer = pipe(
|
const versionToSemVer = pipe(
|
||||||
(version: string) => version === LATEST_VERSION_CONSTRAINT ? MAX_FALLBACK_VERSION : version,
|
(version: string) => version === LATEST_VERSION_CONSTRAINT ? MAX_FALLBACK_VERSION : version,
|
||||||
toSemVer(MIN_FALLBACK_VERSION),
|
toSemVer(MIN_FALLBACK_VERSION),
|
||||||
|
@ -30,7 +33,14 @@ const getServerVersion = memoizeWith(
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
|
|
||||||
export const resetSelectedServer = createAction(RESET_SELECTED_SERVER);
|
const initialState: SelectedServer = null;
|
||||||
|
|
||||||
|
export default buildReducer<SelectedServer, SelectServerAction>({
|
||||||
|
[RESET_SELECTED_SERVER]: () => initialState,
|
||||||
|
[SELECT_SERVER]: (_, { selectedServer }) => selectedServer,
|
||||||
|
}, initialState);
|
||||||
|
|
||||||
|
export const resetSelectedServer = buildActionCreator(RESET_SELECTED_SERVER);
|
||||||
|
|
||||||
export const selectServer = (
|
export const selectServer = (
|
||||||
buildShlinkApiClient: ShlinkApiClientBuilder,
|
buildShlinkApiClient: ShlinkApiClientBuilder,
|
||||||
|
@ -48,7 +58,7 @@ export const selectServer = (
|
||||||
const selectedServer = servers[serverId];
|
const selectedServer = servers[serverId];
|
||||||
|
|
||||||
if (!selectedServer) {
|
if (!selectedServer) {
|
||||||
dispatch<Action & { selectedServer: NotFoundServer }>({
|
dispatch<SelectServerAction>({
|
||||||
type: SELECT_SERVER,
|
type: SELECT_SERVER,
|
||||||
selectedServer: { serverNotFound: true },
|
selectedServer: { serverNotFound: true },
|
||||||
});
|
});
|
||||||
|
@ -60,7 +70,7 @@ export const selectServer = (
|
||||||
const { health } = buildShlinkApiClient(selectedServer);
|
const { health } = buildShlinkApiClient(selectedServer);
|
||||||
const { version, printableVersion } = await getServerVersion(serverId, health);
|
const { version, printableVersion } = await getServerVersion(serverId, health);
|
||||||
|
|
||||||
dispatch<Action & { selectedServer: ReachableServer }>({
|
dispatch<SelectServerAction>({
|
||||||
type: SELECT_SERVER,
|
type: SELECT_SERVER,
|
||||||
selectedServer: {
|
selectedServer: {
|
||||||
...selectedServer,
|
...selectedServer,
|
||||||
|
@ -70,14 +80,9 @@ export const selectServer = (
|
||||||
});
|
});
|
||||||
dispatch(loadMercureInfo());
|
dispatch(loadMercureInfo());
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dispatch<Action & { selectedServer: NonReachableServer }>({
|
dispatch<SelectServerAction>({
|
||||||
type: SELECT_SERVER,
|
type: SELECT_SERVER,
|
||||||
selectedServer: { ...selectedServer, serverNotReachable: true },
|
selectedServer: { ...selectedServer, serverNotReachable: true },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handleActions<SelectedServer, any>({
|
|
||||||
[RESET_SELECTED_SERVER]: () => initialState,
|
|
||||||
[SELECT_SERVER]: (_, { selectedServer }: any) => selectedServer,
|
|
||||||
}, initialState);
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { handleActions } from 'redux-actions';
|
|
||||||
import { pipe, assoc, map, reduce, dissoc } from 'ramda';
|
import { pipe, assoc, map, reduce, dissoc } from 'ramda';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import { Action } from 'redux';
|
||||||
import { ServerData, ServerWithId } from '../data';
|
import { ServerData, ServerWithId } from '../data';
|
||||||
|
import { buildReducer } from '../../utils/helpers/redux';
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const EDIT_SERVER = 'shlink/servers/EDIT_SERVER';
|
export const EDIT_SERVER = 'shlink/servers/EDIT_SERVER';
|
||||||
|
@ -11,6 +12,10 @@ export const CREATE_SERVERS = 'shlink/servers/CREATE_SERVERS';
|
||||||
|
|
||||||
export type ServersMap = Record<string, ServerWithId>;
|
export type ServersMap = Record<string, ServerWithId>;
|
||||||
|
|
||||||
|
export interface CreateServersAction extends Action<string> {
|
||||||
|
newServers: ServersMap;
|
||||||
|
}
|
||||||
|
|
||||||
const initialState: ServersMap = {};
|
const initialState: ServersMap = {};
|
||||||
|
|
||||||
const serverWithId = (server: ServerWithId | ServerData): ServerWithId => {
|
const serverWithId = (server: ServerWithId | ServerData): ServerWithId => {
|
||||||
|
@ -21,8 +26,8 @@ const serverWithId = (server: ServerWithId | ServerData): ServerWithId => {
|
||||||
return assoc('id', uuid(), server);
|
return assoc('id', uuid(), server);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handleActions<ServersMap, any>({
|
export default buildReducer<ServersMap, CreateServersAction>({
|
||||||
[CREATE_SERVERS]: (state, { newServers }: any) => ({ ...state, ...newServers }),
|
[CREATE_SERVERS]: (state, { newServers }) => ({ ...state, ...newServers }),
|
||||||
[DELETE_SERVER]: (state, { serverId }: any) => dissoc(serverId, state),
|
[DELETE_SERVER]: (state, { serverId }: any) => dissoc(serverId, state),
|
||||||
[EDIT_SERVER]: (state, { serverId, serverData }: any) => !state[serverId]
|
[EDIT_SERVER]: (state, { serverId, serverData }: any) => !state[serverId]
|
||||||
? state
|
? state
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { handleActions } from 'redux-actions';
|
|
||||||
import { Action } from 'redux';
|
import { Action } from 'redux';
|
||||||
|
import { buildReducer } from '../../utils/helpers/redux';
|
||||||
|
|
||||||
export const SET_REAL_TIME_UPDATES = 'shlink/realTimeUpdates/SET_REAL_TIME_UPDATES';
|
export const SET_REAL_TIME_UPDATES = 'shlink/realTimeUpdates/SET_REAL_TIME_UPDATES';
|
||||||
|
|
||||||
|
@ -17,11 +17,13 @@ const initialState: Settings = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handleActions<Settings, any>({
|
type SettingsAction = Action & Settings;
|
||||||
[SET_REAL_TIME_UPDATES]: (state, { realTimeUpdates }: any) => ({ ...state, realTimeUpdates }),
|
|
||||||
|
export default buildReducer<Settings, SettingsAction>({
|
||||||
|
[SET_REAL_TIME_UPDATES]: (state, { realTimeUpdates }) => ({ ...state, realTimeUpdates }),
|
||||||
}, initialState);
|
}, initialState);
|
||||||
|
|
||||||
export const setRealTimeUpdates = (enabled: boolean): Action & Settings => ({
|
export const setRealTimeUpdates = (enabled: boolean): SettingsAction => ({
|
||||||
type: SET_REAL_TIME_UPDATES,
|
type: SET_REAL_TIME_UPDATES,
|
||||||
realTimeUpdates: { enabled },
|
realTimeUpdates: { enabled },
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { createAction, handleActions } from 'redux-actions';
|
|
||||||
import { Action, Dispatch } from 'redux';
|
import { Action, Dispatch } from 'redux';
|
||||||
import { ShlinkApiClientBuilder } from '../../utils/services/types';
|
import { ShlinkApiClientBuilder } from '../../utils/services/types';
|
||||||
import { GetState } from '../../container/types';
|
import { GetState } from '../../container/types';
|
||||||
import { ShortUrl, ShortUrlData } from '../data';
|
import { ShortUrl, ShortUrlData } from '../data';
|
||||||
|
import { buildReducer, buildActionCreator } from '../../utils/helpers/redux';
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const CREATE_SHORT_URL_START = 'shlink/createShortUrl/CREATE_SHORT_URL_START';
|
export const CREATE_SHORT_URL_START = 'shlink/createShortUrl/CREATE_SHORT_URL_START';
|
||||||
|
@ -27,16 +27,20 @@ export interface ShortUrlCreation {
|
||||||
error: boolean;
|
error: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CreateShortUrlAction extends Action<string> {
|
||||||
|
result: ShortUrl;
|
||||||
|
}
|
||||||
|
|
||||||
const initialState: ShortUrlCreation = {
|
const initialState: ShortUrlCreation = {
|
||||||
result: null,
|
result: null,
|
||||||
saving: false,
|
saving: false,
|
||||||
error: false,
|
error: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handleActions<ShortUrlCreation, any>({
|
export default buildReducer<ShortUrlCreation, CreateShortUrlAction>({
|
||||||
[CREATE_SHORT_URL_START]: (state) => ({ ...state, saving: true, error: false }),
|
[CREATE_SHORT_URL_START]: (state) => ({ ...state, saving: true, error: false }),
|
||||||
[CREATE_SHORT_URL_ERROR]: (state) => ({ ...state, saving: false, error: true }),
|
[CREATE_SHORT_URL_ERROR]: (state) => ({ ...state, saving: false, error: true }),
|
||||||
[CREATE_SHORT_URL]: (_, { result }: any) => ({ result, saving: false, error: false }),
|
[CREATE_SHORT_URL]: (_, { result }) => ({ result, saving: false, error: false }),
|
||||||
[RESET_CREATE_SHORT_URL]: () => initialState,
|
[RESET_CREATE_SHORT_URL]: () => initialState,
|
||||||
}, initialState);
|
}, initialState);
|
||||||
|
|
||||||
|
@ -50,7 +54,7 @@ export const createShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) =>
|
||||||
try {
|
try {
|
||||||
const result = await createShortUrl(data);
|
const result = await createShortUrl(data);
|
||||||
|
|
||||||
dispatch<Action & { result: ShortUrl }>({ type: CREATE_SHORT_URL, result });
|
dispatch<CreateShortUrlAction>({ type: CREATE_SHORT_URL, result });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dispatch({ type: CREATE_SHORT_URL_ERROR });
|
dispatch({ type: CREATE_SHORT_URL_ERROR });
|
||||||
|
|
||||||
|
@ -58,4 +62,4 @@ export const createShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) =>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resetCreateShortUrl = createAction(RESET_CREATE_SHORT_URL);
|
export const resetCreateShortUrl = buildActionCreator(RESET_CREATE_SHORT_URL);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { createAction, handleActions, Action } from 'redux-actions';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch, Action } from 'redux';
|
||||||
import { ShortUrlMeta } from '../data';
|
import { ShortUrlMeta } from '../data';
|
||||||
import { ShlinkApiClientBuilder } from '../../utils/services/types';
|
import { ShlinkApiClientBuilder } from '../../utils/services/types';
|
||||||
import { GetState } from '../../container/types';
|
import { GetState } from '../../container/types';
|
||||||
|
import { buildActionCreator, buildReducer } from '../../utils/helpers/redux';
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
/* eslint-disable padding-line-between-statements */
|
||||||
export const EDIT_SHORT_URL_META_START = 'shlink/shortUrlMeta/EDIT_SHORT_URL_META_START';
|
export const EDIT_SHORT_URL_META_START = 'shlink/shortUrlMeta/EDIT_SHORT_URL_META_START';
|
||||||
|
@ -34,7 +34,7 @@ export interface ShortUrlMetaEdition {
|
||||||
error: boolean;
|
error: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ShortUrlMetaEditedAction {
|
interface ShortUrlMetaEditedAction extends Action<string> {
|
||||||
shortCode: string;
|
shortCode: string;
|
||||||
domain?: string | null;
|
domain?: string | null;
|
||||||
meta: ShortUrlMeta;
|
meta: ShortUrlMeta;
|
||||||
|
@ -47,10 +47,10 @@ const initialState: ShortUrlMetaEdition = {
|
||||||
error: false,
|
error: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default handleActions<ShortUrlMetaEdition, ShortUrlMetaEditedAction>({
|
export default buildReducer<ShortUrlMetaEdition, ShortUrlMetaEditedAction>({
|
||||||
[EDIT_SHORT_URL_META_START]: (state) => ({ ...state, saving: true, error: false }),
|
[EDIT_SHORT_URL_META_START]: (state) => ({ ...state, saving: true, error: false }),
|
||||||
[EDIT_SHORT_URL_META_ERROR]: (state) => ({ ...state, saving: false, error: true }),
|
[EDIT_SHORT_URL_META_ERROR]: (state) => ({ ...state, saving: false, error: true }),
|
||||||
[SHORT_URL_META_EDITED]: (_, { payload }) => ({ ...payload, saving: false, error: false }),
|
[SHORT_URL_META_EDITED]: (_, { shortCode, meta }) => ({ shortCode, meta, saving: false, error: false }),
|
||||||
[RESET_EDIT_SHORT_URL_META]: () => initialState,
|
[RESET_EDIT_SHORT_URL_META]: () => initialState,
|
||||||
}, initialState);
|
}, initialState);
|
||||||
|
|
||||||
|
@ -64,10 +64,7 @@ export const editShortUrlMeta = (buildShlinkApiClient: ShlinkApiClientBuilder) =
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateShortUrlMeta(shortCode, domain, meta);
|
await updateShortUrlMeta(shortCode, domain, meta);
|
||||||
dispatch<Action<ShortUrlMetaEditedAction>>({
|
dispatch<ShortUrlMetaEditedAction>({ shortCode, meta, domain, type: SHORT_URL_META_EDITED });
|
||||||
type: SHORT_URL_META_EDITED,
|
|
||||||
payload: { shortCode, meta, domain },
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
dispatch({ type: EDIT_SHORT_URL_META_ERROR });
|
dispatch({ type: EDIT_SHORT_URL_META_ERROR });
|
||||||
|
|
||||||
|
@ -75,4 +72,4 @@ export const editShortUrlMeta = (buildShlinkApiClient: ShlinkApiClientBuilder) =
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resetShortUrlMeta = createAction(RESET_EDIT_SHORT_URL_META);
|
export const resetShortUrlMeta = buildActionCreator(RESET_EDIT_SHORT_URL_META);
|
||||||
|
|
|
@ -49,13 +49,7 @@ export default handleActions({
|
||||||
state,
|
state,
|
||||||
),
|
),
|
||||||
[SHORT_URL_TAGS_EDITED]: setPropFromActionOnMatchingShortUrl('tags'),
|
[SHORT_URL_TAGS_EDITED]: setPropFromActionOnMatchingShortUrl('tags'),
|
||||||
[SHORT_URL_META_EDITED]: (state, { payload: { shortCode, domain, meta } }) => assocPath(
|
[SHORT_URL_META_EDITED]: setPropFromActionOnMatchingShortUrl('meta'),
|
||||||
[ 'shortUrls', 'data' ],
|
|
||||||
state.shortUrls.data.map(
|
|
||||||
(shortUrl) => shortUrlMatches(shortUrl, shortCode, domain) ? assoc('meta', meta, shortUrl) : shortUrl,
|
|
||||||
),
|
|
||||||
state,
|
|
||||||
),
|
|
||||||
[SHORT_URL_EDITED]: setPropFromActionOnMatchingShortUrl('longUrl'),
|
[SHORT_URL_EDITED]: setPropFromActionOnMatchingShortUrl('longUrl'),
|
||||||
[CREATE_VISIT]: (state, { shortUrl: { shortCode, domain, visitsCount } }) => assocPath(
|
[CREATE_VISIT]: (state, { shortUrl: { shortCode, domain, visitsCount } }) => assocPath(
|
||||||
[ 'shortUrls', 'data' ],
|
[ 'shortUrls', 'data' ],
|
||||||
|
|
17
src/utils/helpers/redux.ts
Normal file
17
src/utils/helpers/redux.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
import { Action } from 'redux';
|
||||||
|
|
||||||
|
type ActionDispatcher<State, AT> = (currentState: State, action: AT) => State;
|
||||||
|
type ActionDispatcherMap<State, AT> = Record<string, ActionDispatcher<State, AT>>;
|
||||||
|
|
||||||
|
export const buildReducer = <State, AT extends Action>(map: ActionDispatcherMap<State, AT>, initialState: State) => (
|
||||||
|
state: State | undefined,
|
||||||
|
action: AT,
|
||||||
|
): State => {
|
||||||
|
const { type } = action;
|
||||||
|
const actionDispatcher = map[type];
|
||||||
|
const currentState = state ?? initialState;
|
||||||
|
|
||||||
|
return actionDispatcher ? actionDispatcher(currentState, action) : currentState;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const buildActionCreator = <T extends string>(type: T) => (): Action<T> => ({ type });
|
|
@ -1,10 +1,10 @@
|
||||||
import { Mock } from 'ts-mockery';
|
import { Mock } from 'ts-mockery';
|
||||||
import { Action } from 'redux-actions';
|
|
||||||
import reducer, {
|
import reducer, {
|
||||||
GET_MERCURE_INFO_START,
|
GET_MERCURE_INFO_START,
|
||||||
GET_MERCURE_INFO_ERROR,
|
GET_MERCURE_INFO_ERROR,
|
||||||
GET_MERCURE_INFO,
|
GET_MERCURE_INFO,
|
||||||
loadMercureInfo,
|
loadMercureInfo,
|
||||||
|
GetMercureInfoAction,
|
||||||
} from '../../../src/mercure/reducers/mercureInfo';
|
} from '../../../src/mercure/reducers/mercureInfo';
|
||||||
import { ShlinkMercureInfo } from '../../../src/utils/services/types';
|
import { ShlinkMercureInfo } from '../../../src/utils/services/types';
|
||||||
import ShlinkApiClient from '../../../src/utils/services/ShlinkApiClient';
|
import ShlinkApiClient from '../../../src/utils/services/ShlinkApiClient';
|
||||||
|
@ -17,22 +17,26 @@ describe('mercureInfoReducer', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('reducer', () => {
|
describe('reducer', () => {
|
||||||
|
const action = (type: string, args: Partial<ShlinkMercureInfo> = {}) => Mock.of<GetMercureInfoAction>(
|
||||||
|
{ type, ...args },
|
||||||
|
);
|
||||||
|
|
||||||
it('returns loading on GET_MERCURE_INFO_START', () => {
|
it('returns loading on GET_MERCURE_INFO_START', () => {
|
||||||
expect(reducer(undefined, { type: GET_MERCURE_INFO_START } as Action<ShlinkMercureInfo>)).toEqual({
|
expect(reducer(undefined, action(GET_MERCURE_INFO_START))).toEqual({
|
||||||
loading: true,
|
loading: true,
|
||||||
error: false,
|
error: false,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error on GET_MERCURE_INFO_ERROR', () => {
|
it('returns error on GET_MERCURE_INFO_ERROR', () => {
|
||||||
expect(reducer(undefined, { type: GET_MERCURE_INFO_ERROR } as Action<ShlinkMercureInfo>)).toEqual({
|
expect(reducer(undefined, action(GET_MERCURE_INFO_ERROR))).toEqual({
|
||||||
loading: false,
|
loading: false,
|
||||||
error: true,
|
error: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns mercure info on GET_MERCURE_INFO', () => {
|
it('returns mercure info on GET_MERCURE_INFO', () => {
|
||||||
expect(reducer(undefined, { type: GET_MERCURE_INFO, payload: mercureInfo })).toEqual({
|
expect(reducer(undefined, { type: GET_MERCURE_INFO, ...mercureInfo })).toEqual({
|
||||||
...mercureInfo,
|
...mercureInfo,
|
||||||
loading: false,
|
loading: false,
|
||||||
error: false,
|
error: false,
|
||||||
|
@ -74,7 +78,7 @@ describe('mercureInfoReducer', () => {
|
||||||
expect(apiClientMock.mercureInfo).toHaveBeenCalledTimes(1);
|
expect(apiClientMock.mercureInfo).toHaveBeenCalledTimes(1);
|
||||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(1, { type: GET_MERCURE_INFO_START });
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: GET_MERCURE_INFO_START });
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(2, { type: GET_MERCURE_INFO, payload: mercureInfo });
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: GET_MERCURE_INFO, ...mercureInfo });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws error on failure', async () => {
|
it('throws error on failure', async () => {
|
||||||
|
|
|
@ -10,17 +10,17 @@ import reducer, {
|
||||||
} from '../../../src/servers/reducers/selectedServer';
|
} from '../../../src/servers/reducers/selectedServer';
|
||||||
import { RESET_SHORT_URL_PARAMS } from '../../../src/short-urls/reducers/shortUrlsListParams';
|
import { RESET_SHORT_URL_PARAMS } from '../../../src/short-urls/reducers/shortUrlsListParams';
|
||||||
import { ShlinkState } from '../../../src/container/types';
|
import { ShlinkState } from '../../../src/container/types';
|
||||||
import { NonReachableServer, NotFoundServer } from '../../../src/servers/data';
|
import { NonReachableServer, NotFoundServer, RegularServer } from '../../../src/servers/data';
|
||||||
|
|
||||||
describe('selectedServerReducer', () => {
|
describe('selectedServerReducer', () => {
|
||||||
describe('reducer', () => {
|
describe('reducer', () => {
|
||||||
it('returns default when action is RESET_SELECTED_SERVER', () =>
|
it('returns default when action is RESET_SELECTED_SERVER', () =>
|
||||||
expect(reducer(null, { type: RESET_SELECTED_SERVER } as any)).toEqual(null));
|
expect(reducer(null, { type: RESET_SELECTED_SERVER, selectedServer: null })).toEqual(null));
|
||||||
|
|
||||||
it('returns selected server when action is SELECT_SERVER', () => {
|
it('returns selected server when action is SELECT_SERVER', () => {
|
||||||
const selectedServer = { id: 'abc123' };
|
const selectedServer = Mock.of<RegularServer>({ id: 'abc123' });
|
||||||
|
|
||||||
expect(reducer(null, { type: SELECT_SERVER, selectedServer } as any)).toEqual(selectedServer);
|
expect(reducer(null, { type: SELECT_SERVER, selectedServer })).toEqual(selectedServer);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ describe('settingsReducer', () => {
|
||||||
|
|
||||||
describe('reducer', () => {
|
describe('reducer', () => {
|
||||||
it('returns realTimeUpdates when action is SET_REAL_TIME_UPDATES', () => {
|
it('returns realTimeUpdates when action is SET_REAL_TIME_UPDATES', () => {
|
||||||
expect(reducer(undefined, { type: SET_REAL_TIME_UPDATES, realTimeUpdates } as any)).toEqual({ realTimeUpdates });
|
expect(reducer(undefined, { type: SET_REAL_TIME_UPDATES, realTimeUpdates })).toEqual({ realTimeUpdates });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import reducer, {
|
||||||
RESET_CREATE_SHORT_URL,
|
RESET_CREATE_SHORT_URL,
|
||||||
createShortUrl,
|
createShortUrl,
|
||||||
resetCreateShortUrl,
|
resetCreateShortUrl,
|
||||||
|
CreateShortUrlAction,
|
||||||
} from '../../../src/short-urls/reducers/shortUrlCreation';
|
} from '../../../src/short-urls/reducers/shortUrlCreation';
|
||||||
import { ShortUrl } from '../../../src/short-urls/data';
|
import { ShortUrl } from '../../../src/short-urls/data';
|
||||||
import ShlinkApiClient from '../../../src/utils/services/ShlinkApiClient';
|
import ShlinkApiClient from '../../../src/utils/services/ShlinkApiClient';
|
||||||
|
@ -15,8 +16,12 @@ describe('shortUrlCreationReducer', () => {
|
||||||
const shortUrl = Mock.all<ShortUrl>();
|
const shortUrl = Mock.all<ShortUrl>();
|
||||||
|
|
||||||
describe('reducer', () => {
|
describe('reducer', () => {
|
||||||
|
const action = (type: string, args: Partial<CreateShortUrlAction> = {}) => Mock.of<CreateShortUrlAction>(
|
||||||
|
{ type, ...args },
|
||||||
|
);
|
||||||
|
|
||||||
it('returns loading on CREATE_SHORT_URL_START', () => {
|
it('returns loading on CREATE_SHORT_URL_START', () => {
|
||||||
expect(reducer(undefined, { type: CREATE_SHORT_URL_START } as any)).toEqual({
|
expect(reducer(undefined, action(CREATE_SHORT_URL_START))).toEqual({
|
||||||
result: null,
|
result: null,
|
||||||
saving: true,
|
saving: true,
|
||||||
error: false,
|
error: false,
|
||||||
|
@ -24,7 +29,7 @@ describe('shortUrlCreationReducer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns error on CREATE_SHORT_URL_ERROR', () => {
|
it('returns error on CREATE_SHORT_URL_ERROR', () => {
|
||||||
expect(reducer(undefined, { type: CREATE_SHORT_URL_ERROR } as any)).toEqual({
|
expect(reducer(undefined, action(CREATE_SHORT_URL_ERROR))).toEqual({
|
||||||
result: null,
|
result: null,
|
||||||
saving: false,
|
saving: false,
|
||||||
error: true,
|
error: true,
|
||||||
|
@ -32,7 +37,7 @@ describe('shortUrlCreationReducer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns result on CREATE_SHORT_URL', () => {
|
it('returns result on CREATE_SHORT_URL', () => {
|
||||||
expect(reducer(undefined, { type: CREATE_SHORT_URL, result: shortUrl } as any)).toEqual({
|
expect(reducer(undefined, action(CREATE_SHORT_URL, { result: shortUrl }))).toEqual({
|
||||||
result: shortUrl,
|
result: shortUrl,
|
||||||
saving: false,
|
saving: false,
|
||||||
error: false,
|
error: false,
|
||||||
|
@ -40,7 +45,7 @@ describe('shortUrlCreationReducer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns default state on RESET_CREATE_SHORT_URL', () => {
|
it('returns default state on RESET_CREATE_SHORT_URL', () => {
|
||||||
expect(reducer(undefined, { type: RESET_CREATE_SHORT_URL } as any)).toEqual({
|
expect(reducer(undefined, action(RESET_CREATE_SHORT_URL))).toEqual({
|
||||||
result: null,
|
result: null,
|
||||||
saving: false,
|
saving: false,
|
||||||
error: false,
|
error: false,
|
||||||
|
@ -49,8 +54,7 @@ describe('shortUrlCreationReducer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('resetCreateShortUrl', () => {
|
describe('resetCreateShortUrl', () => {
|
||||||
it('returns proper action', () =>
|
it('returns proper action', () => expect(resetCreateShortUrl()).toEqual({ type: RESET_CREATE_SHORT_URL }));
|
||||||
expect(resetCreateShortUrl()).toEqual({ type: RESET_CREATE_SHORT_URL }));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('createShortUrl', () => {
|
describe('createShortUrl', () => {
|
||||||
|
|
|
@ -37,7 +37,7 @@ describe('shortUrlMetaReducer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns provided tags and shortCode on SHORT_URL_META_EDITED', () => {
|
it('returns provided tags and shortCode on SHORT_URL_META_EDITED', () => {
|
||||||
expect(reducer(undefined, { type: SHORT_URL_META_EDITED, payload: { meta, shortCode } })).toEqual({
|
expect(reducer(undefined, { type: SHORT_URL_META_EDITED, meta, shortCode })).toEqual({
|
||||||
meta,
|
meta,
|
||||||
shortCode,
|
shortCode,
|
||||||
saving: false,
|
saving: false,
|
||||||
|
@ -64,8 +64,6 @@ describe('shortUrlMetaReducer', () => {
|
||||||
afterEach(jest.clearAllMocks);
|
afterEach(jest.clearAllMocks);
|
||||||
|
|
||||||
it.each([[ undefined ], [ null ], [ 'example.com' ]])('dispatches metadata on success', async (domain) => {
|
it.each([[ undefined ], [ null ], [ 'example.com' ]])('dispatches metadata on success', async (domain) => {
|
||||||
const payload = { meta, shortCode, domain };
|
|
||||||
|
|
||||||
await editShortUrlMeta(buildShlinkApiClient)(shortCode, domain, meta)(dispatch, getState);
|
await editShortUrlMeta(buildShlinkApiClient)(shortCode, domain, meta)(dispatch, getState);
|
||||||
|
|
||||||
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
|
expect(buildShlinkApiClient).toHaveBeenCalledTimes(1);
|
||||||
|
@ -73,7 +71,7 @@ describe('shortUrlMetaReducer', () => {
|
||||||
expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, domain, meta);
|
expect(updateShortUrlMeta).toHaveBeenCalledWith(shortCode, domain, meta);
|
||||||
expect(dispatch).toHaveBeenCalledTimes(2);
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_META_START });
|
expect(dispatch).toHaveBeenNthCalledWith(1, { type: EDIT_SHORT_URL_META_START });
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_META_EDITED, payload });
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SHORT_URL_META_EDITED, meta, shortCode, domain });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('dispatches error on failure', async () => {
|
it('dispatches error on failure', async () => {
|
||||||
|
|
Loading…
Reference in a new issue