2022-12-04 14:58:13 -08:00
|
|
|
import produce, { enableMapSet } from 'immer';
|
|
|
|
|
2022-12-04 15:26:28 -08:00
|
|
|
import {
|
|
|
|
ENTITIES_IMPORT,
|
2023-03-14 12:13:49 -07:00
|
|
|
ENTITIES_DELETE,
|
2023-03-22 12:34:10 -07:00
|
|
|
ENTITIES_DISMISS,
|
2022-12-04 15:26:28 -08:00
|
|
|
ENTITIES_FETCH_REQUEST,
|
|
|
|
ENTITIES_FETCH_SUCCESS,
|
|
|
|
ENTITIES_FETCH_FAIL,
|
|
|
|
EntityAction,
|
|
|
|
} from './actions';
|
2022-12-04 14:58:13 -08:00
|
|
|
import { createCache, createList, updateStore, updateList } from './utils';
|
|
|
|
|
2023-03-15 14:52:09 -07:00
|
|
|
import type { DeleteEntitiesOpts } from './actions';
|
2022-12-04 15:53:56 -08:00
|
|
|
import type { Entity, EntityCache, EntityListState } from './types';
|
2022-12-04 14:58:13 -08:00
|
|
|
|
|
|
|
enableMapSet();
|
|
|
|
|
|
|
|
/** Entity reducer state. */
|
2022-12-04 16:54:54 -08:00
|
|
|
interface State {
|
|
|
|
[entityType: string]: EntityCache | undefined
|
|
|
|
}
|
2022-12-04 14:58:13 -08:00
|
|
|
|
|
|
|
/** Import entities into the cache. */
|
|
|
|
const importEntities = (
|
2022-12-04 16:54:54 -08:00
|
|
|
state: State,
|
2022-12-04 14:58:13 -08:00
|
|
|
entityType: string,
|
|
|
|
entities: Entity[],
|
|
|
|
listKey?: string,
|
2022-12-04 15:53:56 -08:00
|
|
|
newState?: EntityListState,
|
2022-12-04 14:58:13 -08:00
|
|
|
): State => {
|
|
|
|
return produce(state, draft => {
|
2022-12-04 16:54:54 -08:00
|
|
|
const cache = draft[entityType] ?? createCache();
|
2022-12-04 14:58:13 -08:00
|
|
|
cache.store = updateStore(cache.store, entities);
|
|
|
|
|
2022-12-04 16:54:54 -08:00
|
|
|
if (typeof listKey === 'string') {
|
|
|
|
let list = { ...(cache.lists[listKey] ?? createList()) };
|
2022-12-04 15:53:56 -08:00
|
|
|
list = updateList(list, entities);
|
|
|
|
if (newState) {
|
|
|
|
list.state = newState;
|
|
|
|
}
|
2022-12-04 16:54:54 -08:00
|
|
|
cache.lists[listKey] = list;
|
2022-12-04 14:58:13 -08:00
|
|
|
}
|
|
|
|
|
2022-12-04 16:54:54 -08:00
|
|
|
draft[entityType] = cache;
|
2022-12-04 14:58:13 -08:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2023-03-14 12:13:49 -07:00
|
|
|
const deleteEntities = (
|
|
|
|
state: State,
|
|
|
|
entityType: string,
|
|
|
|
ids: Iterable<string>,
|
2023-03-15 14:52:09 -07:00
|
|
|
opts: DeleteEntitiesOpts,
|
2023-03-14 12:13:49 -07:00
|
|
|
) => {
|
|
|
|
return produce(state, draft => {
|
|
|
|
const cache = draft[entityType] ?? createCache();
|
|
|
|
|
|
|
|
for (const id of ids) {
|
|
|
|
delete cache.store[id];
|
|
|
|
|
2023-03-15 14:52:09 -07:00
|
|
|
if (!opts?.preserveLists) {
|
|
|
|
for (const list of Object.values(cache.lists)) {
|
|
|
|
list?.ids.delete(id);
|
|
|
|
}
|
2023-03-14 12:13:49 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
draft[entityType] = cache;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2023-03-22 12:34:10 -07:00
|
|
|
const dismissEntities = (
|
|
|
|
state: State,
|
|
|
|
entityType: string,
|
|
|
|
ids: Iterable<string>,
|
|
|
|
listKey: string,
|
|
|
|
) => {
|
|
|
|
return produce(state, draft => {
|
|
|
|
const cache = draft[entityType] ?? createCache();
|
|
|
|
const list = cache.lists[listKey];
|
|
|
|
|
|
|
|
if (list) {
|
|
|
|
for (const id of ids) {
|
|
|
|
list.ids.delete(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
draft[entityType] = cache;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-12-04 15:26:28 -08:00
|
|
|
const setFetching = (
|
|
|
|
state: State,
|
|
|
|
entityType: string,
|
|
|
|
listKey: string | undefined,
|
|
|
|
isFetching: boolean,
|
2023-03-13 16:34:42 -07:00
|
|
|
error?: any,
|
2022-12-04 15:26:28 -08:00
|
|
|
) => {
|
|
|
|
return produce(state, draft => {
|
2022-12-04 16:54:54 -08:00
|
|
|
const cache = draft[entityType] ?? createCache();
|
2022-12-04 15:26:28 -08:00
|
|
|
|
2022-12-04 16:54:54 -08:00
|
|
|
if (typeof listKey === 'string') {
|
|
|
|
const list = cache.lists[listKey] ?? createList();
|
2022-12-04 15:26:28 -08:00
|
|
|
list.state.fetching = isFetching;
|
2023-03-13 16:34:42 -07:00
|
|
|
list.state.error = error;
|
2022-12-04 16:54:54 -08:00
|
|
|
cache.lists[listKey] = list;
|
2022-12-04 15:26:28 -08:00
|
|
|
}
|
|
|
|
|
2022-12-04 16:54:54 -08:00
|
|
|
draft[entityType] = cache;
|
2022-12-04 15:26:28 -08:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-12-04 14:58:13 -08:00
|
|
|
/** Stores various entity data and lists in a one reducer. */
|
2022-12-04 16:54:54 -08:00
|
|
|
function reducer(state: Readonly<State> = {}, action: EntityAction): State {
|
2022-12-04 14:58:13 -08:00
|
|
|
switch (action.type) {
|
|
|
|
case ENTITIES_IMPORT:
|
|
|
|
return importEntities(state, action.entityType, action.entities, action.listKey);
|
2023-03-14 12:13:49 -07:00
|
|
|
case ENTITIES_DELETE:
|
2023-03-15 14:52:09 -07:00
|
|
|
return deleteEntities(state, action.entityType, action.ids, action.opts);
|
2023-03-22 12:34:10 -07:00
|
|
|
case ENTITIES_DISMISS:
|
|
|
|
return dismissEntities(state, action.entityType, action.ids, action.listKey);
|
2022-12-04 15:53:56 -08:00
|
|
|
case ENTITIES_FETCH_SUCCESS:
|
|
|
|
return importEntities(state, action.entityType, action.entities, action.listKey, action.newState);
|
2022-12-04 15:26:28 -08:00
|
|
|
case ENTITIES_FETCH_REQUEST:
|
|
|
|
return setFetching(state, action.entityType, action.listKey, true);
|
|
|
|
case ENTITIES_FETCH_FAIL:
|
2023-03-13 16:34:42 -07:00
|
|
|
return setFetching(state, action.entityType, action.listKey, false, action.error);
|
2022-12-04 14:58:13 -08:00
|
|
|
default:
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-14 12:13:49 -07:00
|
|
|
export default reducer;
|
|
|
|
export type { State };
|