import mapValues from 'lodash/mapValues';
import entityReducer from './entityReducer';
import {
REMOVE_ENTITIES,
ADD_ENTITIES_OF_TYPE,
UPDATE_ENTITIES,
SET_ENTITY,
REMOVE_ENTIRE_ENTITY,
} from '../../actions/entities/entityActions';
/**
* Reducer for a list of entities of a single entity type. The state is structured
* as a map with ids as key and calls the entityReducer for the value.
*
* {
* [entity id]: entityReducer(),
* ...
* }
*/
const entityTypeReducer = (state = {}, action, strictIdCheck = false) => {
if (action.meta && typeof action.meta.entityId !== 'undefined') {
if (strictIdCheck && action.type === SET_ENTITY) {
const dataId = action?.payload.data.id;
if (dataId && dataId !== action.meta.entityId) {
throw new Error(
`Strict id check failed: key "${action.meta.entityId}" of entity does not match id "${dataId}"`,
);
}
}
return {
...state,
[action.meta.entityId]: entityReducer(state[action.meta.entityId], action, strictIdCheck),
};
}
switch (action.type) {
case ADD_ENTITIES_OF_TYPE:
if (strictIdCheck) {
Object.keys(action.payload).forEach(id => {
if (action.payload[id].id !== id) {
throw new Error(
`Strict id check failed: key "${id}" of entity does not match id "${action.payload[id].id}"`,
);
}
});
}
return {
...state,
...action.payload,
};
case REMOVE_ENTITIES:
return Object.keys(state)
.filter(key => !action.payload.includes(key))
.reduce((newState, key) => {
newState[key] = state[key]; // eslint-disable-line no-param-reassign
return newState;
}, {});
case REMOVE_ENTIRE_ENTITY:
return Object.keys(state)
.filter(key => !action.payload.includes(key))
.reduce((newState, key) => {
newState[key] = state[key]; // eslint-disable-line no-param-reassign
return null;
}, {});
case UPDATE_ENTITIES:
return mapValues(state, entityState => {
const { predicate } = action.meta;
if (
!predicate ||
Object.keys(predicate).every(key => entityState[key] === predicate[key])
) {
return entityReducer(entityState, action);
}
return entityState;
});
default:
return state;
}
};
export default entityTypeReducer;