|
|
|
import {
|
|
|
|
isAsyncThunkAction,
|
|
|
|
isPending as isThunkActionPending,
|
|
|
|
isFulfilled as isThunkActionFulfilled,
|
|
|
|
isRejected as isThunkActionRejected,
|
|
|
|
isAction,
|
|
|
|
} from '@reduxjs/toolkit';
|
|
|
|
import type { Middleware, UnknownAction } from '@reduxjs/toolkit';
|
|
|
|
|
|
|
|
import { showLoading, hideLoading } from 'react-redux-loading-bar';
|
|
|
|
|
|
|
|
import type { RootState } from '..';
|
|
|
|
|
|
|
|
interface Config {
|
|
|
|
promiseTypeSuffixes?: string[];
|
|
|
|
}
|
|
|
|
|
|
|
|
const defaultTypeSuffixes: Config['promiseTypeSuffixes'] = [
|
|
|
|
'PENDING',
|
|
|
|
'FULFILLED',
|
|
|
|
'REJECTED',
|
|
|
|
];
|
|
|
|
|
|
|
|
interface ActionWithSkipLoading extends UnknownAction {
|
|
|
|
skipLoading: boolean;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isActionWithSkipLoading(
|
|
|
|
action: unknown,
|
|
|
|
): action is ActionWithSkipLoading {
|
|
|
|
return (
|
|
|
|
isAction(action) &&
|
|
|
|
'skipLoading' in action &&
|
|
|
|
typeof action.skipLoading === 'boolean'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
export const loadingBarMiddleware = (
|
|
|
|
config: Config = {},
|
|
|
|
): Middleware<{ skipLoading?: boolean }, RootState> => {
|
|
|
|
const promiseTypeSuffixes = config.promiseTypeSuffixes ?? defaultTypeSuffixes;
|
|
|
|
|
|
|
|
return ({ dispatch }) =>
|
|
|
|
(next) =>
|
|
|
|
(action) => {
|
|
|
|
let isPending = false;
|
|
|
|
let isFulfilled = false;
|
|
|
|
let isRejected = false;
|
|
|
|
|
|
|
|
if (
|
|
|
|
isAsyncThunkAction(action)
|
|
|
|
// TODO: once we get the first use-case for it, add a check for skipLoading
|
|
|
|
) {
|
|
|
|
if (isThunkActionPending(action)) isPending = true;
|
|
|
|
else if (isThunkActionFulfilled(action)) isFulfilled = true;
|
|
|
|
else if (isThunkActionRejected(action)) isRejected = true;
|
|
|
|
} else if (
|
|
|
|
isActionWithSkipLoading(action) &&
|
|
|
|
!action.skipLoading &&
|
|
|
|
typeof action.type === 'string'
|
|
|
|
) {
|
|
|
|
const [PENDING, FULFILLED, REJECTED] = promiseTypeSuffixes;
|
|
|
|
|
|
|
|
const isPendingRegexp = new RegExp(`${PENDING}$`, 'g');
|
|
|
|
const isFulfilledRegexp = new RegExp(`${FULFILLED}$`, 'g');
|
|
|
|
const isRejectedRegexp = new RegExp(`${REJECTED}$`, 'g');
|
|
|
|
|
|
|
|
if (action.type.match(isPendingRegexp)) {
|
|
|
|
isPending = true;
|
|
|
|
} else if (action.type.match(isFulfilledRegexp)) {
|
|
|
|
isFulfilled = true;
|
|
|
|
} else if (action.type.match(isRejectedRegexp)) {
|
|
|
|
isRejected = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isPending) {
|
|
|
|
dispatch(showLoading());
|
|
|
|
} else if (isFulfilled || isRejected) {
|
|
|
|
dispatch(hideLoading());
|
|
|
|
}
|
|
|
|
|
|
|
|
return next(action);
|
|
|
|
};
|
|
|
|
};
|