@wordpress/core-data
Version:
Access to and manipulation of core WordPress entities.
601 lines (600 loc) • 17 kB
JavaScript
// packages/core-data/src/actions.js
import fastDeepEqual from "fast-deep-equal/es6";
import { v4 as uuid } from "uuid";
import apiFetch from "@wordpress/api-fetch";
import { addQueryArgs } from "@wordpress/url";
import deprecated from "@wordpress/deprecated";
import { getNestedValue, setNestedValue } from "./utils";
import { receiveItems, removeItems, receiveQueriedItems } from "./queried-data";
import { DEFAULT_ENTITY_KEY } from "./entities";
import { createBatch } from "./batch";
import { STORE_NAME } from "./name";
import { LOCAL_EDITOR_ORIGIN, getSyncManager } from "./sync";
import logEntityDeprecation from "./utils/log-entity-deprecation";
function receiveUserQuery(queryID, users) {
return {
type: "RECEIVE_USER_QUERY",
users: Array.isArray(users) ? users : [users],
queryID
};
}
function receiveCurrentUser(currentUser) {
return {
type: "RECEIVE_CURRENT_USER",
currentUser
};
}
function addEntities(entities) {
return {
type: "ADD_ENTITIES",
entities
};
}
function receiveEntityRecords(kind, name, records, query = void 0, invalidateCache = false, edits = void 0, meta = void 0) {
if (kind === "postType") {
records = (Array.isArray(records) ? records : [records]).map(
(record) => record.status === "auto-draft" ? { ...record, title: "" } : record
);
}
let action;
if (query) {
action = receiveQueriedItems(records, query, edits, meta);
} else {
action = receiveItems(records, edits, meta);
}
return {
...action,
kind,
name,
invalidateCache
};
}
function receiveCurrentTheme(currentTheme) {
return {
type: "RECEIVE_CURRENT_THEME",
currentTheme
};
}
function __experimentalReceiveCurrentGlobalStylesId(currentGlobalStylesId) {
return {
type: "RECEIVE_CURRENT_GLOBAL_STYLES_ID",
id: currentGlobalStylesId
};
}
function __experimentalReceiveThemeBaseGlobalStyles(stylesheet, globalStyles) {
return {
type: "RECEIVE_THEME_GLOBAL_STYLES",
stylesheet,
globalStyles
};
}
function __experimentalReceiveThemeGlobalStyleVariations(stylesheet, variations) {
return {
type: "RECEIVE_THEME_GLOBAL_STYLE_VARIATIONS",
stylesheet,
variations
};
}
function receiveThemeSupports() {
deprecated("wp.data.dispatch( 'core' ).receiveThemeSupports", {
since: "5.9"
});
return {
type: "DO_NOTHING"
};
}
function receiveThemeGlobalStyleRevisions(currentId, revisions) {
deprecated(
"wp.data.dispatch( 'core' ).receiveThemeGlobalStyleRevisions()",
{
since: "6.5.0",
alternative: "wp.data.dispatch( 'core' ).receiveRevisions"
}
);
return {
type: "RECEIVE_THEME_GLOBAL_STYLE_REVISIONS",
currentId,
revisions
};
}
function receiveEmbedPreview(url, preview) {
return {
type: "RECEIVE_EMBED_PREVIEW",
url,
preview
};
}
var deleteEntityRecord = (kind, name, recordId, query, { __unstableFetch = apiFetch, throwOnError = false } = {}) => async ({ dispatch, resolveSelect }) => {
logEntityDeprecation(kind, name, "deleteEntityRecord");
const configs = await resolveSelect.getEntitiesConfig(kind);
const entityConfig = configs.find(
(config) => config.kind === kind && config.name === name
);
let error;
let deletedRecord = false;
if (!entityConfig) {
return;
}
const lock = await dispatch.__unstableAcquireStoreLock(
STORE_NAME,
["entities", "records", kind, name, recordId],
{ exclusive: true }
);
try {
dispatch({
type: "DELETE_ENTITY_RECORD_START",
kind,
name,
recordId
});
let hasError = false;
let { baseURL } = entityConfig;
if (kind === "postType" && name === "wp_template" && (recordId && typeof recordId === "string" && !/^\d+$/.test(recordId) || !window?.__experimentalTemplateActivate)) {
baseURL = baseURL.slice(0, baseURL.lastIndexOf("/")) + "/templates";
}
try {
let path = `${baseURL}/${recordId}`;
if (query) {
path = addQueryArgs(path, query);
}
deletedRecord = await __unstableFetch({
path,
method: "DELETE"
});
await dispatch(removeItems(kind, name, recordId, true));
} catch (_error) {
hasError = true;
error = _error;
}
dispatch({
type: "DELETE_ENTITY_RECORD_FINISH",
kind,
name,
recordId,
error
});
if (hasError && throwOnError) {
throw error;
}
return deletedRecord;
} finally {
dispatch.__unstableReleaseStoreLock(lock);
}
};
var editEntityRecord = (kind, name, recordId, edits, options = {}) => ({ select, dispatch }) => {
logEntityDeprecation(kind, name, "editEntityRecord");
const entityConfig = select.getEntityConfig(kind, name);
if (!entityConfig) {
throw new Error(
`The entity being edited (${kind}, ${name}) does not have a loaded config.`
);
}
const { mergedEdits = {} } = entityConfig;
const record = select.getRawEntityRecord(kind, name, recordId);
const editedRecord = select.getEditedEntityRecord(
kind,
name,
recordId
);
const edit = {
kind,
name,
recordId,
// Clear edits when they are equal to their persisted counterparts
// so that the property is not considered dirty.
edits: Object.keys(edits).reduce((acc, key) => {
const recordValue = record[key];
const editedRecordValue = editedRecord[key];
const value = mergedEdits[key] ? { ...editedRecordValue, ...edits[key] } : edits[key];
acc[key] = fastDeepEqual(recordValue, value) ? void 0 : value;
return acc;
}, {})
};
if (window.__experimentalEnableSync && entityConfig.syncConfig) {
if (globalThis.IS_GUTENBERG_PLUGIN) {
const objectType = `${kind}/${name}`;
const objectId = recordId;
getSyncManager()?.update(
objectType,
objectId,
edit.edits,
LOCAL_EDITOR_ORIGIN
);
}
}
if (!options.undoIgnore) {
select.getUndoManager().addRecord(
[
{
id: { kind, name, recordId },
changes: Object.keys(edits).reduce((acc, key) => {
acc[key] = {
from: editedRecord[key],
to: edits[key]
};
return acc;
}, {})
}
],
options.isCached
);
}
dispatch({
type: "EDIT_ENTITY_RECORD",
...edit
});
};
var undo = () => ({ select, dispatch }) => {
const undoRecord = select.getUndoManager().undo();
if (!undoRecord) {
return;
}
dispatch({
type: "UNDO",
record: undoRecord
});
};
var redo = () => ({ select, dispatch }) => {
const redoRecord = select.getUndoManager().redo();
if (!redoRecord) {
return;
}
dispatch({
type: "REDO",
record: redoRecord
});
};
var __unstableCreateUndoLevel = () => ({ select }) => {
select.getUndoManager().addRecord();
};
var saveEntityRecord = (kind, name, record, {
isAutosave = false,
__unstableFetch = apiFetch,
throwOnError = false
} = {}) => async ({ select, resolveSelect, dispatch }) => {
logEntityDeprecation(kind, name, "saveEntityRecord");
const configs = await resolveSelect.getEntitiesConfig(kind);
const entityConfig = configs.find(
(config) => config.kind === kind && config.name === name
);
if (!entityConfig) {
return;
}
const entityIdKey = entityConfig.key ?? DEFAULT_ENTITY_KEY;
const recordId = record[entityIdKey];
const isNewRecord = !!entityIdKey && !recordId;
const lock = await dispatch.__unstableAcquireStoreLock(
STORE_NAME,
["entities", "records", kind, name, recordId || uuid()],
{ exclusive: true }
);
try {
for (const [key, value] of Object.entries(record)) {
if (typeof value === "function") {
const evaluatedValue = value(
select.getEditedEntityRecord(kind, name, recordId)
);
dispatch.editEntityRecord(
kind,
name,
recordId,
{
[key]: evaluatedValue
},
{ undoIgnore: true }
);
record[key] = evaluatedValue;
}
}
dispatch({
type: "SAVE_ENTITY_RECORD_START",
kind,
name,
recordId,
isAutosave
});
let updatedRecord;
let error;
let hasError = false;
let { baseURL } = entityConfig;
if (kind === "postType" && name === "wp_template" && (recordId && typeof recordId === "string" && !/^\d+$/.test(recordId) || !window?.__experimentalTemplateActivate)) {
baseURL = baseURL.slice(0, baseURL.lastIndexOf("/")) + "/templates";
}
try {
const path = `${baseURL}${recordId ? "/" + recordId : ""}`;
const persistedRecord = !isNewRecord ? select.getRawEntityRecord(kind, name, recordId) : {};
if (isAutosave) {
const currentUser = select.getCurrentUser();
const currentUserId = currentUser ? currentUser.id : void 0;
const autosavePost = await resolveSelect.getAutosave(
persistedRecord.type,
persistedRecord.id,
currentUserId
);
let data = {
...persistedRecord,
...autosavePost,
...record
};
data = Object.keys(data).reduce(
(acc, key) => {
if ([
"title",
"excerpt",
"content",
"meta"
].includes(key)) {
acc[key] = data[key];
}
return acc;
},
{
// Do not update the `status` if we have edited it when auto saving.
// It's very important to let the user explicitly save this change,
// because it can lead to unexpected results. An example would be to
// have a draft post and change the status to publish.
status: data.status === "auto-draft" ? "draft" : void 0
}
);
updatedRecord = await __unstableFetch({
path: `${path}/autosaves`,
method: "POST",
data
});
if (persistedRecord.id === updatedRecord.id) {
let newRecord = {
...persistedRecord,
...data,
...updatedRecord
};
newRecord = Object.keys(newRecord).reduce(
(acc, key) => {
if (["title", "excerpt", "content"].includes(
key
)) {
acc[key] = newRecord[key];
} else if (key === "status") {
acc[key] = persistedRecord.status === "auto-draft" && newRecord.status === "draft" ? newRecord.status : persistedRecord.status;
} else {
acc[key] = persistedRecord[key];
}
return acc;
},
{}
);
dispatch.receiveEntityRecords(
kind,
name,
newRecord,
void 0,
true
);
} else {
dispatch.receiveAutosaves(
persistedRecord.id,
updatedRecord
);
}
} else {
let edits = record;
if (entityConfig.__unstablePrePersist) {
edits = {
...edits,
...entityConfig.__unstablePrePersist(
persistedRecord,
edits
)
};
}
updatedRecord = await __unstableFetch({
path,
method: recordId ? "PUT" : "POST",
data: edits
});
dispatch.receiveEntityRecords(
kind,
name,
updatedRecord,
void 0,
true,
edits
);
}
} catch (_error) {
hasError = true;
error = _error;
}
dispatch({
type: "SAVE_ENTITY_RECORD_FINISH",
kind,
name,
recordId,
error,
isAutosave
});
if (hasError && throwOnError) {
throw error;
}
return updatedRecord;
} finally {
dispatch.__unstableReleaseStoreLock(lock);
}
};
var __experimentalBatch = (requests) => async ({ dispatch }) => {
const batch = createBatch();
const api = {
saveEntityRecord(kind, name, record, options) {
return batch.add(
(add) => dispatch.saveEntityRecord(kind, name, record, {
...options,
__unstableFetch: add
})
);
},
saveEditedEntityRecord(kind, name, recordId, options) {
return batch.add(
(add) => dispatch.saveEditedEntityRecord(kind, name, recordId, {
...options,
__unstableFetch: add
})
);
},
deleteEntityRecord(kind, name, recordId, query, options) {
return batch.add(
(add) => dispatch.deleteEntityRecord(kind, name, recordId, query, {
...options,
__unstableFetch: add
})
);
}
};
const resultPromises = requests.map((request) => request(api));
const [, ...results] = await Promise.all([
batch.run(),
...resultPromises
]);
return results;
};
var saveEditedEntityRecord = (kind, name, recordId, options) => async ({ select, dispatch, resolveSelect }) => {
logEntityDeprecation(kind, name, "saveEditedEntityRecord");
if (!select.hasEditsForEntityRecord(kind, name, recordId)) {
return;
}
const configs = await resolveSelect.getEntitiesConfig(kind);
const entityConfig = configs.find(
(config) => config.kind === kind && config.name === name
);
if (!entityConfig) {
return;
}
const entityIdKey = entityConfig.key || DEFAULT_ENTITY_KEY;
const edits = select.getEntityRecordNonTransientEdits(
kind,
name,
recordId
);
const record = { [entityIdKey]: recordId, ...edits };
return await dispatch.saveEntityRecord(kind, name, record, options);
};
var __experimentalSaveSpecifiedEntityEdits = (kind, name, recordId, itemsToSave, options) => async ({ select, dispatch, resolveSelect }) => {
logEntityDeprecation(
kind,
name,
"__experimentalSaveSpecifiedEntityEdits"
);
if (!select.hasEditsForEntityRecord(kind, name, recordId)) {
return;
}
const edits = select.getEntityRecordNonTransientEdits(
kind,
name,
recordId
);
const editsToSave = {};
for (const item of itemsToSave) {
setNestedValue(editsToSave, item, getNestedValue(edits, item));
}
const configs = await resolveSelect.getEntitiesConfig(kind);
const entityConfig = configs.find(
(config) => config.kind === kind && config.name === name
);
const entityIdKey = entityConfig?.key || DEFAULT_ENTITY_KEY;
if (recordId) {
editsToSave[entityIdKey] = recordId;
}
return await dispatch.saveEntityRecord(
kind,
name,
editsToSave,
options
);
};
function receiveUploadPermissions(hasUploadPermissions) {
deprecated("wp.data.dispatch( 'core' ).receiveUploadPermissions", {
since: "5.9",
alternative: "receiveUserPermission"
});
return receiveUserPermission("create/media", hasUploadPermissions);
}
function receiveUserPermission(key, isAllowed) {
return {
type: "RECEIVE_USER_PERMISSION",
key,
isAllowed
};
}
function receiveUserPermissions(permissions) {
return {
type: "RECEIVE_USER_PERMISSIONS",
permissions
};
}
function receiveAutosaves(postId, autosaves) {
return {
type: "RECEIVE_AUTOSAVES",
postId,
autosaves: Array.isArray(autosaves) ? autosaves : [autosaves]
};
}
function receiveNavigationFallbackId(fallbackId) {
return {
type: "RECEIVE_NAVIGATION_FALLBACK_ID",
fallbackId
};
}
function receiveDefaultTemplateId(query, templateId) {
return {
type: "RECEIVE_DEFAULT_TEMPLATE",
query,
templateId
};
}
var receiveRevisions = (kind, name, recordKey, records, query, invalidateCache = false, meta) => async ({ dispatch, resolveSelect }) => {
logEntityDeprecation(kind, name, "receiveRevisions");
const configs = await resolveSelect.getEntitiesConfig(kind);
const entityConfig = configs.find(
(config) => config.kind === kind && config.name === name
);
const key = entityConfig && entityConfig?.revisionKey ? entityConfig.revisionKey : DEFAULT_ENTITY_KEY;
dispatch({
type: "RECEIVE_ITEM_REVISIONS",
key,
items: Array.isArray(records) ? records : [records],
recordKey,
meta,
query,
kind,
name,
invalidateCache
});
};
export {
__experimentalBatch,
__experimentalReceiveCurrentGlobalStylesId,
__experimentalReceiveThemeBaseGlobalStyles,
__experimentalReceiveThemeGlobalStyleVariations,
__experimentalSaveSpecifiedEntityEdits,
__unstableCreateUndoLevel,
addEntities,
deleteEntityRecord,
editEntityRecord,
receiveAutosaves,
receiveCurrentTheme,
receiveCurrentUser,
receiveDefaultTemplateId,
receiveEmbedPreview,
receiveEntityRecords,
receiveNavigationFallbackId,
receiveRevisions,
receiveThemeGlobalStyleRevisions,
receiveThemeSupports,
receiveUploadPermissions,
receiveUserPermission,
receiveUserPermissions,
receiveUserQuery,
redo,
saveEditedEntityRecord,
saveEntityRecord,
undo
};
//# sourceMappingURL=actions.js.map