1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | 6× 5× 15× 5× 28× 7× 21× 21× 7× 2× 2× 2× 2× 2× 4× 4× 12× 3× 3× 3× 3× 3× 3× 3× 3× 3× 3× 2× 2× 2× 2× 2× 2× 2× 2× 2× 2× 9× 2× 2× 3× 3× 2× 2× 2× 2× 1× 1× 9× 9× 9× | import get from 'lodash/get'; import v4 from 'uuid/v4'; import { normalize } from 'normalizr'; import { batchActions } from 'redux-batched-actions'; import { getEntitiesSlice } from './selectors'; import { arrayFrom } from './utils'; // UTILS {{{ function normalizeData(schema, data) { const dataWithIds = arrayFrom(data).map((e) => e.id ? e : { ...e, id: v4() }); return normalize(dataWithIds, [ schema ]); } function extractEntities(entitiesAndKey) { return Object.keys(entitiesAndKey.entities).map((id) => ({ entity: { ...entitiesAndKey.entities[id], id: entitiesAndKey.entities[id].id ? entitiesAndKey.entities[id].id : id, }, key: entitiesAndKey.key, })); } // We try to dispatch the actions of the orignal entities // (the ones which schema was passed) first function sortMainFirst(main) { return (entity1, entity2) => { if (entity1.key === main.key) { return -1; } else Iif (entity2.key === main.key) { return 1; } return 0; }; } function getFromState(state, key, id) { return get(getEntitiesSlice(state), [key, id]); } // }}} function createCreateEntityActions(action) { const dataPath = get(action, 'meta.dataPath'); const schema = get(action, 'meta.schema'); const skipNormalization = get(action, 'meta.skipNormalization'); const data = skipNormalization ? get(action, dataPath) : normalizeData(schema, get(action, dataPath)); return Object.keys(data.entities) .map((key) => ({ entities: data.entities[key], key })) .reduce((memo, entitiesAndKey) => [ ...memo, ...extractEntities(entitiesAndKey) ], []) .sort(sortMainFirst(schema)) .map((payload) => ({ type: `@@entman/CREATE_ENTITY_${payload.key.toUpperCase()}`, payload, })); } function createUpdateEntityActions(action, getState) { const dataPath = get(action, 'meta.dataPath'); const schema = get(action, 'meta.schema'); const ids = get(action, 'meta.ids'); const useDefault = get(action, 'meta.useDefault'); const data = normalizeData(schema, ids.map((id) => ({ ...get(action, dataPath), id }))); return Object.keys(data.entities) .map((key) => ({ entities: data.entities[key], key })) .reduce((memo, entitiesAndKey) => [ ...memo, ...extractEntities(entitiesAndKey) ], []) .map((entity) => ({ ...entity, oldEntity: getFromState(getState(), entity.key, entity.entity.id) })) .sort(sortMainFirst(schema)) .map((payload) => ({ type: `@@entman/UPDATE_ENTITY_${payload.key.toUpperCase()}`, payload: { ...payload, useDefault }, })); } function createUpdateEntityIdActions(action, getState) { const schema = get(action, 'meta.schema'); const oldId = get(action, 'meta.oldId'); const newId = get(action, 'meta.newId'); return [{ type: `@@entman/UPDATE_ENTITY_ID_${schema.key.toUpperCase()}`, payload: { oldId, newId, oldEntity: getFromState(getState(), schema.key, oldId) }, }]; } function createDeleteEntityActions(action, getState) { const schema = get(action, 'meta.schema'); const ids = get(action, 'meta.ids'); // Do we cascade delete? return ids .map((id) => ({ id, key: schema.key })) .map((info) => ({ entity: getFromState(getState(), info.key, info.id), key: info.key })) .map((payload) => ({ type: `@@entman/DELETE_ENTITY_${schema.key.toUpperCase()}`, payload, })); } function processEntmanAction(store, next, action, enableBatching) { switch (action.meta.type) { case 'CREATE_ENTITIES': { Eif (enableBatching) { return next(batchActions(createCreateEntityActions(action))); } return createCreateEntityActions(action).forEach(next); } case 'UPDATE_ENTITIES': { Eif (enableBatching) { return next(batchActions(createUpdateEntityActions(action, store.getState))); } return createUpdateEntityActions(action, store.getState).forEach(next); } case 'UPDATE_ENTITY_ID': { Eif (enableBatching) { return next(batchActions(createUpdateEntityIdActions(action, store.getState))); } return createUpdateEntityIdActions(action, store.getState).forEach(next); } case 'DELETE_ENTITIES': { Eif (enableBatching) { return next(batchActions(createDeleteEntityActions(action, store.getState))); } return createDeleteEntityActions(action, store.getState).forEach(next); } default: { console.warn(`[ENTMAN] Unknown action type found ${action.meta.type}`); return next(action); } } } export default function entman(config={}) { const { enableBatching } = config; return (store) => (next) => (action) => { Iif ( ! get(action, 'meta.isEntmanAction', false)) { return next(action); } next(action); return processEntmanAction(store, next, action, enableBatching); }; } |