@wordpress/core-data
Version:
Access to and manipulation of core WordPress entities.
614 lines (593 loc) • 16.4 kB
JavaScript
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
;