[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:
@ -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(),
|
||||
],
|
||||
});
|
23
app/javascript/flavours/glitch/store/index.ts
Normal file
23
app/javascript/flavours/glitch/store/index.ts
Normal 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;
|
18
app/javascript/flavours/glitch/store/middlewares/errors.ts
Normal file
18
app/javascript/flavours/glitch/store/middlewares/errors.ts
Normal 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);
|
||||
};
|
@ -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);
|
||||
};
|
||||
};
|
56
app/javascript/flavours/glitch/store/middlewares/sounds.ts
Normal file
56
app/javascript/flavours/glitch/store/middlewares/sounds.ts
Normal 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);
|
||||
};
|
||||
};
|
Reference in New Issue
Block a user