[Glitch] Type Redux store and middleware

Port 6aeb162927 to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
Renaud Chaput
2023-05-09 16:56:26 +02:00
committed by Claire
parent a56c71faba
commit 5aa08826cf
9 changed files with 60 additions and 34 deletions

View File

@ -1,16 +0,0 @@
import { configureStore } from '@reduxjs/toolkit';
import thunk from 'redux-thunk';
import appReducer from '../reducers';
import loadingBarMiddleware from '../middleware/loading_bar';
import errorsMiddleware from '../middleware/errors';
import soundsMiddleware from '../middleware/sounds';
export const store = configureStore({
reducer: appReducer,
middleware: [
thunk,
loadingBarMiddleware({ promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'] }),
errorsMiddleware(),
soundsMiddleware(),
],
});

View File

@ -0,0 +1,23 @@
import { configureStore } from '@reduxjs/toolkit';
import { rootReducer } from '../reducers';
import { loadingBarMiddleware } from './middlewares/loading_bar';
import { errorsMiddleware } from './middlewares/errors';
import { soundsMiddleware } from './middlewares/sounds';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
export const store = configureStore({
reducer: rootReducer,
middleware: getDefaultMiddleware =>
getDefaultMiddleware().concat(
loadingBarMiddleware({ promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'] }))
.concat(errorsMiddleware)
.concat(soundsMiddleware()),
});
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof rootReducer>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

View File

@ -0,0 +1,18 @@
import { Middleware } from 'redux';
import { showAlertForError } from 'flavours/glitch/actions/alerts';
import { RootState } from '..';
const defaultFailSuffix = 'FAIL';
export const errorsMiddleware: Middleware<Record<string, never>, RootState> =
({ dispatch }) => next => action => {
if (action.type && !action.skipAlert) {
const isFail = new RegExp(`${defaultFailSuffix}$`, 'g');
if (action.type.match(isFail)) {
dispatch(showAlertForError(action.error, action.skipNotFound));
}
}
return next(action);
};

View File

@ -0,0 +1,31 @@
import { showLoading, hideLoading } from 'react-redux-loading-bar';
import { Middleware } from 'redux';
import { RootState } from '..';
interface Config {
promiseTypeSuffixes?: string[]
}
const defaultTypeSuffixes: Config['promiseTypeSuffixes'] = ['PENDING', 'FULFILLED', 'REJECTED'];
export const loadingBarMiddleware = (config: Config = {}): Middleware<Record<string, never>, RootState> => {
const promiseTypeSuffixes = config.promiseTypeSuffixes || defaultTypeSuffixes;
return ({ dispatch }) => next => (action) => {
if (action.type && !action.skipLoading) {
const [PENDING, FULFILLED, REJECTED] = promiseTypeSuffixes;
const isPending = new RegExp(`${PENDING}$`, 'g');
const isFulfilled = new RegExp(`${FULFILLED}$`, 'g');
const isRejected = new RegExp(`${REJECTED}$`, 'g');
if (action.type.match(isPending)) {
dispatch(showLoading());
} else if (action.type.match(isFulfilled) || action.type.match(isRejected)) {
dispatch(hideLoading());
}
}
return next(action);
};
};

View File

@ -0,0 +1,56 @@
import { Middleware, AnyAction } from 'redux';
import { RootState } from '..';
interface AudioSource {
src: string
type: string
}
const createAudio = (sources: AudioSource[]) => {
const audio = new Audio();
sources.forEach(({ type, src }) => {
const source = document.createElement('source');
source.type = type;
source.src = src;
audio.appendChild(source);
});
return audio;
};
const play = (audio: HTMLAudioElement) => {
if (!audio.paused) {
audio.pause();
if (typeof audio.fastSeek === 'function') {
audio.fastSeek(0);
} else {
audio.currentTime = 0;
}
}
audio.play();
};
export const soundsMiddleware = (): Middleware<Record<string, never>, RootState> => {
const soundCache: {[key: string]: HTMLAudioElement} = {
boop: createAudio([
{
src: '/sounds/boop.ogg',
type: 'audio/ogg',
},
{
src: '/sounds/boop.mp3',
type: 'audio/mpeg',
},
]),
};
return () => next => (action: AnyAction) => {
const sound = action?.meta?.sound;
if (sound && soundCache[sound]) {
play(soundCache[sound]);
}
return next(action);
};
};