mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-09 17:57:26 +03:00
Migrated first reducer to typescript, adding also type for the shared app state
This commit is contained in:
parent
e193a692e8
commit
87e64e5899
8 changed files with 117 additions and 59 deletions
6
package-lock.json
generated
6
package-lock.json
generated
|
@ -3441,6 +3441,12 @@
|
||||||
"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,6 +86,7 @@
|
||||||
"@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 +1,25 @@
|
||||||
|
import { MercureInfo } from '../mercure/reducers/mercureInfo';
|
||||||
|
|
||||||
export type ConnectDecorator = (props: string[], actions?: string[]) => any;
|
export type ConnectDecorator = (props: string[], actions?: string[]) => any;
|
||||||
|
|
||||||
|
export interface ShlinkState {
|
||||||
|
servers: any;
|
||||||
|
selectedServer: any;
|
||||||
|
shortUrlsList: any;
|
||||||
|
shortUrlsListParams: any;
|
||||||
|
shortUrlCreationResult: any;
|
||||||
|
shortUrlDeletion: any;
|
||||||
|
shortUrlTags: any;
|
||||||
|
shortUrlMeta: any;
|
||||||
|
shortUrlEdition: any;
|
||||||
|
shortUrlVisits: any;
|
||||||
|
tagVisits: any;
|
||||||
|
shortUrlDetail: any;
|
||||||
|
tagsList: any;
|
||||||
|
tagDelete: any;
|
||||||
|
tagEdit: any;
|
||||||
|
mercureInfo: MercureInfo;
|
||||||
|
settings: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GetState = () => ShlinkState;
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
import { handleActions } from 'redux-actions';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
|
||||||
export const GET_MERCURE_INFO_START = 'shlink/mercure/GET_MERCURE_INFO_START';
|
|
||||||
export const GET_MERCURE_INFO_ERROR = 'shlink/mercure/GET_MERCURE_INFO_ERROR';
|
|
||||||
export const GET_MERCURE_INFO = 'shlink/mercure/GET_MERCURE_INFO';
|
|
||||||
/* eslint-enable padding-line-between-statements */
|
|
||||||
|
|
||||||
export const MercureInfoType = PropTypes.shape({
|
|
||||||
token: PropTypes.string,
|
|
||||||
mercureHubUrl: PropTypes.string,
|
|
||||||
loading: PropTypes.bool,
|
|
||||||
error: PropTypes.bool,
|
|
||||||
});
|
|
||||||
|
|
||||||
const initialState = {
|
|
||||||
token: undefined,
|
|
||||||
mercureHubUrl: undefined,
|
|
||||||
loading: true,
|
|
||||||
error: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default handleActions({
|
|
||||||
[GET_MERCURE_INFO_START]: (state) => ({ ...state, loading: true, error: false }),
|
|
||||||
[GET_MERCURE_INFO_ERROR]: (state) => ({ ...state, loading: false, error: true }),
|
|
||||||
[GET_MERCURE_INFO]: (state, { token, mercureHubUrl }) => ({ token, mercureHubUrl, loading: false, error: false }),
|
|
||||||
}, initialState);
|
|
||||||
|
|
||||||
export const loadMercureInfo = (buildShlinkApiClient) => () => async (dispatch, getState) => {
|
|
||||||
dispatch({ type: GET_MERCURE_INFO_START });
|
|
||||||
|
|
||||||
const { settings } = getState();
|
|
||||||
const { mercureInfo } = buildShlinkApiClient(getState);
|
|
||||||
|
|
||||||
if (!settings.realTimeUpdates.enabled) {
|
|
||||||
dispatch({ type: GET_MERCURE_INFO_ERROR });
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await mercureInfo();
|
|
||||||
|
|
||||||
dispatch({ type: GET_MERCURE_INFO, ...result });
|
|
||||||
} catch (e) {
|
|
||||||
dispatch({ type: GET_MERCURE_INFO_ERROR });
|
|
||||||
}
|
|
||||||
};
|
|
59
src/mercure/reducers/mercureInfo.ts
Normal file
59
src/mercure/reducers/mercureInfo.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { handleActions } from 'redux-actions';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
import { ShlinkApiClientBuilder, ShlinkMercureInfo } from '../../utils/services/types';
|
||||||
|
import { GetState } from '../../container/types';
|
||||||
|
|
||||||
|
/* eslint-disable padding-line-between-statements */
|
||||||
|
export const GET_MERCURE_INFO_START = 'shlink/mercure/GET_MERCURE_INFO_START';
|
||||||
|
export const GET_MERCURE_INFO_ERROR = 'shlink/mercure/GET_MERCURE_INFO_ERROR';
|
||||||
|
export const GET_MERCURE_INFO = 'shlink/mercure/GET_MERCURE_INFO';
|
||||||
|
/* eslint-enable padding-line-between-statements */
|
||||||
|
|
||||||
|
/** @deprecated Use MercureInfo interface */
|
||||||
|
export const MercureInfoType = PropTypes.shape({
|
||||||
|
token: PropTypes.string,
|
||||||
|
mercureHubUrl: PropTypes.string,
|
||||||
|
loading: PropTypes.bool,
|
||||||
|
error: PropTypes.bool,
|
||||||
|
});
|
||||||
|
|
||||||
|
export interface MercureInfo {
|
||||||
|
token?: string;
|
||||||
|
mercureHubUrl?: string;
|
||||||
|
loading: boolean;
|
||||||
|
error: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: MercureInfo = {
|
||||||
|
loading: true,
|
||||||
|
error: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default handleActions<MercureInfo, ShlinkMercureInfo>({
|
||||||
|
[GET_MERCURE_INFO_START]: (state) => ({ ...state, loading: true, error: false }),
|
||||||
|
[GET_MERCURE_INFO_ERROR]: (state) => ({ ...state, loading: false, error: true }),
|
||||||
|
[GET_MERCURE_INFO]: (_, { payload }) => ({ ...payload, loading: false, error: false }),
|
||||||
|
}, initialState);
|
||||||
|
|
||||||
|
export const loadMercureInfo = (buildShlinkApiClient: ShlinkApiClientBuilder) =>
|
||||||
|
() => async (dispatch: Dispatch, getState: GetState) => {
|
||||||
|
dispatch({ type: GET_MERCURE_INFO_START });
|
||||||
|
|
||||||
|
const { settings } = getState();
|
||||||
|
const { mercureInfo } = buildShlinkApiClient(getState);
|
||||||
|
|
||||||
|
if (!settings.realTimeUpdates.enabled) {
|
||||||
|
dispatch({ type: GET_MERCURE_INFO_ERROR });
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = await mercureInfo();
|
||||||
|
|
||||||
|
dispatch({ type: GET_MERCURE_INFO, payload });
|
||||||
|
} catch (e) {
|
||||||
|
dispatch({ type: GET_MERCURE_INFO_ERROR });
|
||||||
|
}
|
||||||
|
};
|
|
@ -16,8 +16,9 @@ import tagDeleteReducer from '../tags/reducers/tagDelete';
|
||||||
import tagEditReducer from '../tags/reducers/tagEdit';
|
import tagEditReducer from '../tags/reducers/tagEdit';
|
||||||
import mercureInfoReducer from '../mercure/reducers/mercureInfo';
|
import mercureInfoReducer from '../mercure/reducers/mercureInfo';
|
||||||
import settingsReducer from '../settings/reducers/settings';
|
import settingsReducer from '../settings/reducers/settings';
|
||||||
|
import { ShlinkState } from '../container/types';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers<ShlinkState>({
|
||||||
servers: serversReducer,
|
servers: serversReducer,
|
||||||
selectedServer: selectedServerReducer,
|
selectedServer: selectedServerReducer,
|
||||||
shortUrlsList: shortUrlsListReducer,
|
shortUrlsList: shortUrlsListReducer,
|
||||||
|
|
11
src/utils/services/types.ts
Normal file
11
src/utils/services/types.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import { RegularServer } from '../../servers/data';
|
||||||
|
import { GetState } from '../../container/types';
|
||||||
|
import ShlinkApiClient from './ShlinkApiClient';
|
||||||
|
|
||||||
|
// FIXME Move to ShlinkApiClientBuilder
|
||||||
|
export type ShlinkApiClientBuilder = (getStateOrSelectedServer: RegularServer | GetState) => ShlinkApiClient;
|
||||||
|
|
||||||
|
export interface ShlinkMercureInfo {
|
||||||
|
token: string;
|
||||||
|
mercureHubUrl: string;
|
||||||
|
}
|
|
@ -1,9 +1,14 @@
|
||||||
|
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,
|
||||||
} from '../../../src/mercure/reducers/mercureInfo.js';
|
} from '../../../src/mercure/reducers/mercureInfo';
|
||||||
|
import { ShlinkMercureInfo } from '../../../src/utils/services/types';
|
||||||
|
import ShlinkApiClient from '../../../src/utils/services/ShlinkApiClient';
|
||||||
|
import { GetState } from '../../../src/container/types';
|
||||||
|
|
||||||
describe('mercureInfoReducer', () => {
|
describe('mercureInfoReducer', () => {
|
||||||
const mercureInfo = {
|
const mercureInfo = {
|
||||||
|
@ -13,21 +18,21 @@ describe('mercureInfoReducer', () => {
|
||||||
|
|
||||||
describe('reducer', () => {
|
describe('reducer', () => {
|
||||||
it('returns loading on GET_MERCURE_INFO_START', () => {
|
it('returns loading on GET_MERCURE_INFO_START', () => {
|
||||||
expect(reducer({}, { type: GET_MERCURE_INFO_START })).toEqual({
|
expect(reducer(undefined, { type: GET_MERCURE_INFO_START } as Action<ShlinkMercureInfo>)).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({}, { type: GET_MERCURE_INFO_ERROR })).toEqual({
|
expect(reducer(undefined, { type: GET_MERCURE_INFO_ERROR } as Action<ShlinkMercureInfo>)).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({}, { type: GET_MERCURE_INFO, ...mercureInfo })).toEqual({
|
expect(reducer(undefined, { type: GET_MERCURE_INFO, payload: mercureInfo })).toEqual({
|
||||||
...mercureInfo,
|
...mercureInfo,
|
||||||
loading: false,
|
loading: false,
|
||||||
error: false,
|
error: false,
|
||||||
|
@ -36,15 +41,15 @@ describe('mercureInfoReducer', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('loadMercureInfo', () => {
|
describe('loadMercureInfo', () => {
|
||||||
const createApiClientMock = (result) => ({
|
const createApiClientMock = (result: Promise<ShlinkMercureInfo>) => Mock.of<ShlinkApiClient>({
|
||||||
mercureInfo: jest.fn(() => result),
|
mercureInfo: jest.fn().mockReturnValue(result),
|
||||||
});
|
});
|
||||||
const dispatch = jest.fn();
|
const dispatch = jest.fn();
|
||||||
const createGetStateMock = (enabled) => jest.fn(() => ({
|
const createGetStateMock = (enabled: boolean): GetState => jest.fn().mockReturnValue({
|
||||||
settings: {
|
settings: {
|
||||||
realTimeUpdates: { enabled },
|
realTimeUpdates: { enabled },
|
||||||
},
|
},
|
||||||
}));
|
});
|
||||||
|
|
||||||
afterEach(jest.resetAllMocks);
|
afterEach(jest.resetAllMocks);
|
||||||
|
|
||||||
|
@ -69,7 +74,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, ...mercureInfo });
|
expect(dispatch).toHaveBeenNthCalledWith(2, { type: GET_MERCURE_INFO, payload: mercureInfo });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('throws error on failure', async () => {
|
it('throws error on failure', async () => {
|
Loading…
Reference in a new issue