refactor: monorepo setup with lerna (#243)
This commit is contained in:
committed by
GitHub
parent
dac29fbf3c
commit
504d95c34f
11
packages/core/src/store/hooks.ts
Normal file
11
packages/core/src/store/hooks.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import type { RootState } from './index';
|
||||
import type { TypedUseSelectorHook } from 'react-redux';
|
||||
import type { AnyAction, ThunkDispatch } from '@reduxjs/toolkit';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type AppDispatch = ThunkDispatch<RootState, any, AnyAction>;
|
||||
|
||||
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
17
packages/core/src/store/index.ts
Normal file
17
packages/core/src/store/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
|
||||
import createRootReducer from '../reducers/combinedReducer';
|
||||
import { waitUntilAction } from './middleware/waitUntilAction';
|
||||
|
||||
const store = configureStore({
|
||||
reducer: createRootReducer(),
|
||||
middleware: getDefaultMiddleware =>
|
||||
getDefaultMiddleware({
|
||||
immutableCheck: false,
|
||||
serializableCheck: false,
|
||||
}).concat(waitUntilAction),
|
||||
});
|
||||
|
||||
export { store };
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppDispatch = typeof store.dispatch;
|
64
packages/core/src/store/middleware/waitUntilAction.ts
Normal file
64
packages/core/src/store/middleware/waitUntilAction.ts
Normal file
@ -0,0 +1,64 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// Based on wait-service by Mozilla:
|
||||
// https://github.com/mozilla/gecko-dev/blob/main/devtools/client/shared/redux/middleware/wait-service.js
|
||||
|
||||
/**
|
||||
* A middleware that provides the ability for actions to install a
|
||||
* function to be run once when a specific condition is met by an
|
||||
* action coming through the system. Think of it as a thunk that
|
||||
* blocks until the condition is met.
|
||||
*/
|
||||
import type { AnyAction, Dispatch, Middleware, MiddlewareAPI } from '@reduxjs/toolkit';
|
||||
|
||||
export const WAIT_UNTIL_ACTION = 'WAIT_UNTIL_ACTION';
|
||||
|
||||
export interface WaitActionArgs {
|
||||
predicate: (action: AnyAction) => boolean;
|
||||
run: (dispatch: Dispatch, getState: () => any, action: AnyAction) => void;
|
||||
}
|
||||
|
||||
interface WaitAction extends WaitActionArgs {
|
||||
type: typeof WAIT_UNTIL_ACTION;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line func-style
|
||||
export const waitUntilAction: Middleware<{}, any, Dispatch> = ({
|
||||
dispatch,
|
||||
getState,
|
||||
}: MiddlewareAPI<Dispatch, any>) => {
|
||||
let pending: WaitAction[] = [];
|
||||
|
||||
function checkPending(action: AnyAction): void {
|
||||
const readyRequests = [];
|
||||
const stillPending = [];
|
||||
|
||||
// Find the pending requests whose predicates are satisfied with
|
||||
// this action. Wait to run the requests until after we update the
|
||||
// pending queue because the request handler may synchronously
|
||||
// dispatch again and run this service (that use case is
|
||||
// completely valid).
|
||||
for (const request of pending) {
|
||||
if (request.predicate(action)) {
|
||||
readyRequests.push(request);
|
||||
} else {
|
||||
stillPending.push(request);
|
||||
}
|
||||
}
|
||||
|
||||
pending = stillPending;
|
||||
for (const request of readyRequests) {
|
||||
request.run(dispatch, getState, action);
|
||||
}
|
||||
}
|
||||
|
||||
return (next: Dispatch<AnyAction>) =>
|
||||
(action: AnyAction): null | AnyAction => {
|
||||
if (action.type === WAIT_UNTIL_ACTION) {
|
||||
pending.push(action as WaitAction);
|
||||
return null;
|
||||
}
|
||||
const result = next(action);
|
||||
checkPending(action);
|
||||
return result;
|
||||
};
|
||||
};
|
51
packages/core/src/store/slices/snackbars.ts
Normal file
51
packages/core/src/store/slices/snackbars.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import type { RootState } from '..';
|
||||
|
||||
type MessageType = 'error' | 'warning' | 'info' | 'success';
|
||||
|
||||
export interface SnackbarMessage {
|
||||
id: string;
|
||||
type: MessageType;
|
||||
message:
|
||||
| string
|
||||
| {
|
||||
key: string;
|
||||
options?: Record<string, unknown>;
|
||||
};
|
||||
}
|
||||
|
||||
// Define a type for the slice state
|
||||
export interface SnackbarState {
|
||||
messages: SnackbarMessage[];
|
||||
}
|
||||
|
||||
// Define the initial state using that type
|
||||
const initialState: SnackbarState = {
|
||||
messages: [],
|
||||
};
|
||||
|
||||
export const SnackbarSlice = createSlice({
|
||||
name: 'snackbar',
|
||||
// `createSlice` will infer the state type from the `initialState` argument
|
||||
initialState,
|
||||
reducers: {
|
||||
addSnackbar: (state, action: PayloadAction<Omit<SnackbarMessage, 'id'>>) => {
|
||||
state.messages.push({
|
||||
id: uuid(),
|
||||
...action.payload,
|
||||
});
|
||||
},
|
||||
removeSnackbarById: (state, action: PayloadAction<string>) => {
|
||||
state.messages = state.messages.filter(message => message.id !== action.payload);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { addSnackbar, removeSnackbarById } = SnackbarSlice.actions;
|
||||
|
||||
export const selectSnackbars = (state: RootState) => state.snackbar.messages;
|
||||
|
||||
export default SnackbarSlice.reducer;
|
Reference in New Issue
Block a user