UNPKG

@wordpress/core-data

Version:
614 lines (593 loc) 16.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.autosaves = autosaves; exports.blockPatternCategories = blockPatternCategories; exports.blockPatterns = blockPatterns; exports.currentGlobalStylesId = currentGlobalStylesId; exports.currentTheme = currentTheme; exports.currentUser = currentUser; exports.default = void 0; exports.defaultTemplates = defaultTemplates; exports.editsReference = editsReference; exports.embedPreviews = embedPreviews; exports.entities = void 0; exports.entitiesConfig = entitiesConfig; exports.navigationFallbackId = navigationFallbackId; exports.registeredPostMeta = registeredPostMeta; exports.templateAutoDraftId = templateAutoDraftId; exports.themeBaseGlobalStyles = themeBaseGlobalStyles; exports.themeGlobalStyleRevisions = themeGlobalStyleRevisions; exports.themeGlobalStyleVariations = themeGlobalStyleVariations; exports.undoManager = undoManager; exports.userPatternCategories = userPatternCategories; exports.userPermissions = userPermissions; exports.users = users; var _es = _interopRequireDefault(require("fast-deep-equal/es6")); var _compose = require("@wordpress/compose"); var _data = require("@wordpress/data"); var _undoManager = require("@wordpress/undo-manager"); var _utils = require("./utils"); var _queriedData = require("./queried-data"); var _entities = require("./entities"); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ /** @typedef {import('./types').AnyFunction} AnyFunction */ /** * Reducer managing authors state. Keyed by id. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ function users(state = { byId: {}, queries: {} }, action) { switch (action.type) { case 'RECEIVE_USER_QUERY': return { byId: { ...state.byId, // Key users by their ID. ...action.users.reduce((newUsers, user) => ({ ...newUsers, [user.id]: user }), {}) }, queries: { ...state.queries, [action.queryID]: action.users.map(user => user.id) } }; } return state; } /** * Reducer managing current user state. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ function currentUser(state = {}, action) { switch (action.type) { case 'RECEIVE_CURRENT_USER': return action.currentUser; } return state; } /** * Reducer managing the current theme. * * @param {string|undefined} state Current state. * @param {Object} action Dispatched action. * * @return {string|undefined} Updated state. */ function currentTheme(state = undefined, action) { switch (action.type) { case 'RECEIVE_CURRENT_THEME': return action.currentTheme.stylesheet; } return state; } /** * Reducer managing the current global styles id. * * @param {string|undefined} state Current state. * @param {Object} action Dispatched action. * * @return {string|undefined} Updated state. */ function currentGlobalStylesId(state = undefined, action) { switch (action.type) { case 'RECEIVE_CURRENT_GLOBAL_STYLES_ID': return action.id; } return state; } /** * Reducer managing the theme base global styles. * * @param {Record<string, object>} state Current state. * @param {Object} action Dispatched action. * * @return {Record<string, object>} Updated state. */ function themeBaseGlobalStyles(state = {}, action) { switch (action.type) { case 'RECEIVE_THEME_GLOBAL_STYLES': return { ...state, [action.stylesheet]: action.globalStyles }; } return state; } /** * Reducer managing the theme global styles variations. * * @param {Record<string, object>} state Current state. * @param {Object} action Dispatched action. * * @return {Record<string, object>} Updated state. */ function themeGlobalStyleVariations(state = {}, action) { switch (action.type) { case 'RECEIVE_THEME_GLOBAL_STYLE_VARIATIONS': return { ...state, [action.stylesheet]: action.variations }; } return state; } const withMultiEntityRecordEdits = reducer => (state, action) => { if (action.type === 'UNDO' || action.type === 'REDO') { const { record } = action; let newState = state; record.forEach(({ id: { kind, name, recordId }, changes }) => { newState = reducer(newState, { type: 'EDIT_ENTITY_RECORD', kind, name, recordId, edits: Object.entries(changes).reduce((acc, [key, value]) => { acc[key] = action.type === 'UNDO' ? value.from : value.to; return acc; }, {}) }); }); return newState; } return reducer(state, action); }; /** * Higher Order Reducer for a given entity config. It supports: * * - Fetching * - Editing * - Saving * * @param {Object} entityConfig Entity config. * * @return {AnyFunction} Reducer. */ function entity(entityConfig) { return (0, _compose.compose)([withMultiEntityRecordEdits, // Limit to matching action type so we don't attempt to replace action on // an unhandled action. (0, _utils.ifMatchingAction)(action => action.name && action.kind && action.name === entityConfig.name && action.kind === entityConfig.kind), // Inject the entity config into the action. (0, _utils.replaceAction)(action => { return { key: entityConfig.key || _entities.DEFAULT_ENTITY_KEY, ...action }; })])((0, _data.combineReducers)({ queriedData: _queriedData.reducer, edits: (state = {}, action) => { var _action$query$context; switch (action.type) { case 'RECEIVE_ITEMS': const context = (_action$query$context = action?.query?.context) !== null && _action$query$context !== void 0 ? _action$query$context : 'default'; if (context !== 'default') { return state; } const nextState = { ...state }; for (const record of action.items) { const recordId = record?.[action.key]; const edits = nextState[recordId]; if (!edits) { continue; } const nextEdits = Object.keys(edits).reduce((acc, key) => { var _record$key$raw; // If the edited value is still different to the persisted value, // keep the edited value in edits. if ( // Edits are the "raw" attribute values, but records may have // objects with more properties, so we use `get` here for the // comparison. !(0, _es.default)(edits[key], (_record$key$raw = record[key]?.raw) !== null && _record$key$raw !== void 0 ? _record$key$raw : record[key]) && ( // Sometimes the server alters the sent value which means // we need to also remove the edits before the api request. !action.persistedEdits || !(0, _es.default)(edits[key], action.persistedEdits[key]))) { acc[key] = edits[key]; } return acc; }, {}); if (Object.keys(nextEdits).length) { nextState[recordId] = nextEdits; } else { delete nextState[recordId]; } } return nextState; case 'EDIT_ENTITY_RECORD': const nextEdits = { ...state[action.recordId], ...action.edits }; Object.keys(nextEdits).forEach(key => { // Delete cleared edits so that the properties // are not considered dirty. if (nextEdits[key] === undefined) { delete nextEdits[key]; } }); return { ...state, [action.recordId]: nextEdits }; } return state; }, saving: (state = {}, action) => { switch (action.type) { case 'SAVE_ENTITY_RECORD_START': case 'SAVE_ENTITY_RECORD_FINISH': return { ...state, [action.recordId]: { pending: action.type === 'SAVE_ENTITY_RECORD_START', error: action.error, isAutosave: action.isAutosave } }; } return state; }, deleting: (state = {}, action) => { switch (action.type) { case 'DELETE_ENTITY_RECORD_START': case 'DELETE_ENTITY_RECORD_FINISH': return { ...state, [action.recordId]: { pending: action.type === 'DELETE_ENTITY_RECORD_START', error: action.error } }; } return state; }, revisions: (state = {}, action) => { // Use the same queriedDataReducer shape for revisions. if (action.type === 'RECEIVE_ITEM_REVISIONS') { const recordKey = action.recordKey; delete action.recordKey; const newState = (0, _queriedData.reducer)(state[recordKey], { ...action, type: 'RECEIVE_ITEMS' }); return { ...state, [recordKey]: newState }; } if (action.type === 'REMOVE_ITEMS') { return Object.fromEntries(Object.entries(state).filter(([id]) => !action.itemIds.some(itemId => { if (Number.isInteger(itemId)) { return itemId === +id; } return itemId === id; }))); } return state; } })); } /** * Reducer keeping track of the registered entities. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ function entitiesConfig(state = _entities.rootEntitiesConfig, action) { switch (action.type) { case 'ADD_ENTITIES': return [...state, ...action.entities]; } return state; } /** * Reducer keeping track of the registered entities config and data. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ const entities = (state = {}, action) => { const newConfig = entitiesConfig(state.config, action); // Generates a reducer for the entities nested by `kind` and `name`. // A config array with shape: // ``` // [ // { kind: 'taxonomy', name: 'category' }, // { kind: 'taxonomy', name: 'post_tag' }, // { kind: 'postType', name: 'post' }, // { kind: 'postType', name: 'page' }, // ] // ``` // generates a reducer for state tree with shape: // ``` // { // taxonomy: { // category, // post_tag, // }, // postType: { // post, // page, // }, // } // ``` let entitiesDataReducer = state.reducer; if (!entitiesDataReducer || newConfig !== state.config) { const entitiesByKind = newConfig.reduce((acc, record) => { const { kind } = record; if (!acc[kind]) { acc[kind] = []; } acc[kind].push(record); return acc; }, {}); entitiesDataReducer = (0, _data.combineReducers)(Object.fromEntries(Object.entries(entitiesByKind).map(([kind, subEntities]) => { const kindReducer = (0, _data.combineReducers)(Object.fromEntries(subEntities.map(entityConfig => [entityConfig.name, entity(entityConfig)]))); return [kind, kindReducer]; }))); } const newData = entitiesDataReducer(state.records, action); if (newData === state.records && newConfig === state.config && entitiesDataReducer === state.reducer) { return state; } return { reducer: entitiesDataReducer, records: newData, config: newConfig }; }; /** * @type {UndoManager} */ exports.entities = entities; function undoManager(state = (0, _undoManager.createUndoManager)()) { return state; } function editsReference(state = {}, action) { switch (action.type) { case 'EDIT_ENTITY_RECORD': case 'UNDO': case 'REDO': return {}; } return state; } /** * Reducer managing embed preview data. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ function embedPreviews(state = {}, action) { switch (action.type) { case 'RECEIVE_EMBED_PREVIEW': const { url, preview } = action; return { ...state, [url]: preview }; } return state; } /** * State which tracks whether the user can perform an action on a REST * resource. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ function userPermissions(state = {}, action) { switch (action.type) { case 'RECEIVE_USER_PERMISSION': return { ...state, [action.key]: action.isAllowed }; case 'RECEIVE_USER_PERMISSIONS': return { ...state, ...action.permissions }; } return state; } /** * Reducer returning autosaves keyed by their parent's post id. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ function autosaves(state = {}, action) { switch (action.type) { case 'RECEIVE_AUTOSAVES': const { postId, autosaves: autosavesData } = action; return { ...state, [postId]: autosavesData }; } return state; } function blockPatterns(state = [], action) { switch (action.type) { case 'RECEIVE_BLOCK_PATTERNS': return action.patterns; } return state; } function blockPatternCategories(state = [], action) { switch (action.type) { case 'RECEIVE_BLOCK_PATTERN_CATEGORIES': return action.categories; } return state; } function userPatternCategories(state = [], action) { switch (action.type) { case 'RECEIVE_USER_PATTERN_CATEGORIES': return action.patternCategories; } return state; } function navigationFallbackId(state = null, action) { switch (action.type) { case 'RECEIVE_NAVIGATION_FALLBACK_ID': return action.fallbackId; } return state; } /** * Reducer managing the theme global styles revisions. * * @param {Record<string, object>} state Current state. * @param {Object} action Dispatched action. * * @return {Record<string, object>} Updated state. */ function themeGlobalStyleRevisions(state = {}, action) { switch (action.type) { case 'RECEIVE_THEME_GLOBAL_STYLE_REVISIONS': return { ...state, [action.currentId]: action.revisions }; } return state; } /** * Reducer managing the template lookup per query. * * @param {Record<string, string>} state Current state. * @param {Object} action Dispatched action. * * @return {Record<string, string>} Updated state. */ function defaultTemplates(state = {}, action) { switch (action.type) { case 'RECEIVE_DEFAULT_TEMPLATE': return { ...state, [JSON.stringify(action.query)]: action.templateId }; } return state; } /** * Reducer returning an object of registered post meta. * * @param {Object} state Current state. * @param {Object} action Dispatched action. * * @return {Object} Updated state. */ function registeredPostMeta(state = {}, action) { switch (action.type) { case 'RECEIVE_REGISTERED_POST_META': return { ...state, [action.postType]: action.registeredPostMeta }; } return state; } function templateAutoDraftId(state = {}, action) { return action.type === 'RECEIVE_TEMPLATE_AUTO_DRAFT_ID' ? { ...state, [action.target]: action.id } : state; } var _default = exports.default = (0, _data.combineReducers)({ users, currentTheme, currentGlobalStylesId, currentUser, themeGlobalStyleVariations, themeBaseGlobalStyles, themeGlobalStyleRevisions, entities, editsReference, undoManager, embedPreviews, userPermissions, autosaves, blockPatterns, blockPatternCategories, userPatternCategories, navigationFallbackId, defaultTemplates, registeredPostMeta, templateAutoDraftId }); //# sourceMappingURL=reducer.js.map