diff --git a/packages/fields/README.md b/packages/fields/README.md
index 422d25f3d68bdc..b8d02a53adb798 100644
--- a/packages/fields/README.md
+++ b/packages/fields/README.md
@@ -14,6 +14,10 @@ npm install @wordpress/fields --save
+### deleteTemplate
+
+Undocumented declaration.
+
### duplicatePattern
Undocumented declaration.
@@ -54,6 +58,10 @@ Undocumented declaration.
Undocumented declaration.
+### resetTemplate
+
+Undocumented declaration.
+
### restorePost
Undocumented declaration.
diff --git a/packages/fields/package.json b/packages/fields/package.json
index 2e417c9f4de570..0ebc8f1bcd5d04 100644
--- a/packages/fields/package.json
+++ b/packages/fields/package.json
@@ -33,6 +33,7 @@
],
"dependencies": {
"@babel/runtime": "^7.16.0",
+ "@wordpress/api-fetch": "file:../api-fetch",
"@wordpress/blob": "file:../blob",
"@wordpress/blocks": "file:../blocks",
"@wordpress/components": "file:../components",
diff --git a/packages/fields/src/actions/index.ts b/packages/fields/src/actions/index.ts
index cf4fd6833f3fbe..b11ebbd5a1886b 100644
--- a/packages/fields/src/actions/index.ts
+++ b/packages/fields/src/actions/index.ts
@@ -1,3 +1,4 @@
export * from './base-post';
export * from './common';
export * from './pattern';
+export * from './template';
diff --git a/packages/fields/src/actions/pattern/delete-pattern.tsx b/packages/fields/src/actions/pattern/delete-pattern.tsx
new file mode 100644
index 00000000000000..de2ff1221aeff1
--- /dev/null
+++ b/packages/fields/src/actions/pattern/delete-pattern.tsx
@@ -0,0 +1,167 @@
+/**
+ * WordPress dependencies
+ */
+import { trash } from '@wordpress/icons';
+import { __, _n, sprintf } from '@wordpress/i18n';
+import { useState } from '@wordpress/element';
+import {
+ Button,
+ __experimentalText as Text,
+ __experimentalHStack as HStack,
+ __experimentalVStack as VStack,
+} from '@wordpress/components';
+import { privateApis as patternsPrivateApis } from '@wordpress/patterns';
+import type { Action } from '@wordpress/dataviews';
+
+/**
+ * Internal dependencies
+ */
+import { getItemTitle } from '../utils';
+import type { Pattern } from '../../types';
+import { decodeEntities } from '@wordpress/html-entities';
+import { unlock } from '../../lock-unlock';
+import type { Notice } from '../../mutation';
+import { deleteWithNotices } from '../../mutation';
+
+const { PATTERN_TYPES } = unlock( patternsPrivateApis );
+
+const deletePatternAction: Action< Pattern > = {
+ id: 'delete-pattern',
+ label: __( 'Delete' ),
+ isPrimary: true,
+ icon: trash,
+ isEligible( post ) {
+ return post.type === PATTERN_TYPES.user;
+ },
+ supportsBulk: true,
+ hideModalHeader: true,
+ RenderModal: ( { items, closeModal, onActionPerformed } ) => {
+ const [ isBusy, setIsBusy ] = useState( false );
+
+ return (
+
+
+ { items.length > 1
+ ? sprintf(
+ // translators: %d: number of items to delete.
+ _n(
+ 'Delete %d item?',
+ 'Delete %d items?',
+ items.length
+ ),
+ items.length
+ )
+ : sprintf(
+ // translators: %s: The template or template part's titles
+ __( 'Delete "%s"?' ),
+ getItemTitle( items[ 0 ] )
+ ) }
+
+
+
+
+
+
+ );
+ },
+};
+
+export default deletePatternAction;
diff --git a/packages/fields/src/actions/template/delete-template.tsx b/packages/fields/src/actions/template/delete-template.tsx
new file mode 100644
index 00000000000000..82a42636e4a210
--- /dev/null
+++ b/packages/fields/src/actions/template/delete-template.tsx
@@ -0,0 +1,219 @@
+/**
+ * WordPress dependencies
+ */
+import { trash } from '@wordpress/icons';
+import { __, _n, sprintf } from '@wordpress/i18n';
+import { useState } from '@wordpress/element';
+import {
+ Button,
+ __experimentalText as Text,
+ __experimentalHStack as HStack,
+ __experimentalVStack as VStack,
+} from '@wordpress/components';
+// @ts-ignore
+import { store as coreStore } from '@wordpress/core-data';
+import { store as noticesStore } from '@wordpress/notices';
+
+import type { Action } from '@wordpress/dataviews';
+
+/**
+ * Internal dependencies
+ */
+import { isTemplateRemovable, getItemTitle } from '../utils';
+import type { Template, TemplatePart } from '../../types';
+import { dispatch } from '@wordpress/data';
+import { decodeEntities } from '@wordpress/html-entities';
+
+export const removeTemplates = async (
+ items: ( Template | TemplatePart )[]
+) => {
+ const isResetting = items.every( ( item ) => item?.has_theme_file );
+
+ const promiseResult: any[] = await Promise.allSettled(
+ items.map( ( item ) =>
+ dispatch( coreStore ).deleteEntityRecord(
+ 'postType',
+ item.type,
+ item.id,
+ { force: true },
+ { throwOnError: true }
+ )
+ )
+ );
+
+ // If all the promises were fulfilled with sucess.
+ if ( promiseResult.every( ( { status } ) => status === 'fulfilled' ) ) {
+ let successMessage;
+
+ if ( items.length === 1 ) {
+ let title = '';
+ if ( typeof items[ 0 ].title === 'string' ) {
+ title = items[ 0 ].title;
+ } else if (
+ 'rendered' in items[ 0 ].title &&
+ typeof items[ 0 ].title.rendered === 'string'
+ ) {
+ title = items[ 0 ].title?.rendered;
+ } else if (
+ 'raw' in items[ 0 ].title &&
+ typeof items[ 0 ].title?.raw === 'string'
+ ) {
+ title = items[ 0 ].title?.raw;
+ }
+
+ successMessage = isResetting
+ ? sprintf(
+ /* translators: The template/part's name. */
+ __( '"%s" reset.' ),
+ decodeEntities( title )
+ )
+ : sprintf(
+ /* translators: The template/part's name. */
+ __( '"%s" deleted.' ),
+ decodeEntities( title )
+ );
+ } else {
+ successMessage = isResetting
+ ? __( 'Items reset.' )
+ : __( 'Items deleted.' );
+ }
+
+ dispatch( noticesStore ).createSuccessNotice( successMessage, {
+ type: 'snackbar',
+ id: 'editor-template-deleted-success',
+ } );
+ } else {
+ // If there was at lease one failure.
+ let errorMessage;
+ // If we were trying to delete a single template.
+ if ( promiseResult.length === 1 ) {
+ if ( promiseResult[ 0 ].reason?.message ) {
+ errorMessage = promiseResult[ 0 ].reason.message;
+ } else {
+ errorMessage = isResetting
+ ? __( 'An error occurred while reverting the item.' )
+ : __( 'An error occurred while deleting the item.' );
+ }
+ // If we were trying to delete a multiple templates
+ } else {
+ const errorMessages = new Set();
+ const failedPromises = promiseResult.filter(
+ ( { status } ) => status === 'rejected'
+ );
+ for ( const failedPromise of failedPromises ) {
+ if ( failedPromise.reason?.message ) {
+ errorMessages.add( failedPromise.reason.message );
+ }
+ }
+ if ( errorMessages.size === 0 ) {
+ errorMessage = __(
+ 'An error occurred while deleting the items.'
+ );
+ } else if ( errorMessages.size === 1 ) {
+ errorMessage = isResetting
+ ? sprintf(
+ /* translators: %s: an error message */
+ __(
+ 'An error occurred while reverting the items: %s'
+ ),
+ [ ...errorMessages ][ 0 ]
+ )
+ : sprintf(
+ /* translators: %s: an error message */
+ __(
+ 'An error occurred while deleting the items: %s'
+ ),
+ [ ...errorMessages ][ 0 ]
+ );
+ } else {
+ errorMessage = isResetting
+ ? sprintf(
+ /* translators: %s: a list of comma separated error messages */
+ __(
+ 'Some errors occurred while reverting the items: %s'
+ ),
+ [ ...errorMessages ].join( ',' )
+ )
+ : sprintf(
+ /* translators: %s: a list of comma separated error messages */
+ __(
+ 'Some errors occurred while deleting the items: %s'
+ ),
+ [ ...errorMessages ].join( ',' )
+ );
+ }
+ }
+ dispatch( noticesStore ).createErrorNotice( errorMessage, {
+ type: 'snackbar',
+ } );
+ }
+};
+
+// This action is used for templates, patterns and template parts.
+// Every other post type uses the similar `trashPostAction` which
+// moves the post to trash.
+const deleteTemplateAction: Action< Template | TemplatePart > = {
+ id: 'delete-post',
+ label: __( 'Delete' ),
+ isPrimary: true,
+ icon: trash,
+ isEligible( post ) {
+ return isTemplateRemovable( post );
+ },
+ supportsBulk: true,
+ hideModalHeader: true,
+ RenderModal: ( { items, closeModal, onActionPerformed } ) => {
+ const [ isBusy, setIsBusy ] = useState( false );
+
+ return (
+
+
+ { items.length > 1
+ ? sprintf(
+ // translators: %d: number of items to delete.
+ _n(
+ 'Delete %d item?',
+ 'Delete %d items?',
+ items.length
+ ),
+ items.length
+ )
+ : sprintf(
+ // translators: %s: The template or template part's titles
+ __( 'Delete "%s"?' ),
+ getItemTitle( items[ 0 ] )
+ ) }
+
+
+
+
+
+
+ );
+ },
+};
+
+export default deleteTemplateAction;
diff --git a/packages/fields/src/actions/template/index.ts b/packages/fields/src/actions/template/index.ts
new file mode 100644
index 00000000000000..34614942acae65
--- /dev/null
+++ b/packages/fields/src/actions/template/index.ts
@@ -0,0 +1,2 @@
+export { default as deleteTemplate } from './delete-template';
+export { default as resetTemplate } from './reset-template';
diff --git a/packages/fields/src/actions/template/reset-template.tsx b/packages/fields/src/actions/template/reset-template.tsx
new file mode 100644
index 00000000000000..2e672199cce2e5
--- /dev/null
+++ b/packages/fields/src/actions/template/reset-template.tsx
@@ -0,0 +1,300 @@
+/**
+ * WordPress dependencies
+ */
+import { backup } from '@wordpress/icons';
+import { dispatch, select, useDispatch } from '@wordpress/data';
+import { store as coreStore } from '@wordpress/core-data';
+import { __, sprintf } from '@wordpress/i18n';
+import { store as noticesStore } from '@wordpress/notices';
+import { useState } from '@wordpress/element';
+// @ts-ignore
+import { parse, __unstableSerializeAndClean } from '@wordpress/blocks';
+import {
+ Button,
+ __experimentalText as Text,
+ __experimentalHStack as HStack,
+ __experimentalVStack as VStack,
+} from '@wordpress/components';
+import type { Action } from '@wordpress/dataviews';
+
+/**
+ * Internal dependencies
+ */
+import {
+ getItemTitle,
+ isTemplateOrTemplatePart,
+ TEMPLATE_ORIGINS,
+ TEMPLATE_POST_TYPE,
+} from '../utils';
+import type { CoreDataError, Template, TemplatePart } from '../../types';
+import { addQueryArgs } from '@wordpress/url';
+import apiFetch from '@wordpress/api-fetch';
+
+const isTemplateRevertable = (
+ templateOrTemplatePart: Template | TemplatePart
+) => {
+ if ( ! templateOrTemplatePart ) {
+ return false;
+ }
+
+ return (
+ templateOrTemplatePart.source === TEMPLATE_ORIGINS.custom &&
+ ( Boolean( templateOrTemplatePart?.plugin ) ||
+ templateOrTemplatePart?.has_theme_file )
+ );
+};
+
+/**
+ * Copied - pasted from https://github.com/WordPress/gutenberg/blob/bf1462ad37d4637ebbf63270b9c244b23c69e2a8/packages/editor/src/store/private-actions.js#L233-L365
+ *
+ * @param {Object} template The template to revert.
+ * @param {Object} [options]
+ * @param {boolean} [options.allowUndo] Whether to allow the user to undo
+ * reverting the template. Default true.
+ */
+const revertTemplate = async (
+ template: TemplatePart | Template,
+ { allowUndo = true } = {}
+) => {
+ const noticeId = 'edit-site-template-reverted';
+ dispatch( noticesStore ).removeNotice( noticeId );
+ if ( ! isTemplateRevertable( template ) ) {
+ dispatch( noticesStore ).createErrorNotice(
+ __( 'This template is not revertable.' ),
+ {
+ type: 'snackbar',
+ }
+ );
+ return;
+ }
+
+ try {
+ const templateEntityConfig = select( coreStore ).getEntityConfig(
+ 'postType',
+ template.type
+ );
+
+ if ( ! templateEntityConfig ) {
+ dispatch( noticesStore ).createErrorNotice(
+ __(
+ 'The editor has encountered an unexpected error. Please reload.'
+ ),
+ { type: 'snackbar' }
+ );
+ return;
+ }
+
+ const fileTemplatePath = addQueryArgs(
+ `${ templateEntityConfig.baseURL }/${ template.id }`,
+ { context: 'edit', source: template.origin }
+ );
+
+ const fileTemplate = ( await apiFetch( {
+ path: fileTemplatePath,
+ } ) ) as any;
+ if ( ! fileTemplate ) {
+ dispatch( noticesStore ).createErrorNotice(
+ __(
+ 'The editor has encountered an unexpected error. Please reload.'
+ ),
+ { type: 'snackbar' }
+ );
+ return;
+ }
+
+ const serializeBlocks = ( { blocks: blocksForSerialization = [] } ) =>
+ __unstableSerializeAndClean( blocksForSerialization );
+
+ const edited = select( coreStore ).getEditedEntityRecord(
+ 'postType',
+ template.type,
+ template.id
+ ) as any;
+
+ // We are fixing up the undo level here to make sure we can undo
+ // the revert in the header toolbar correctly.
+ dispatch( coreStore ).editEntityRecord(
+ 'postType',
+ template.type,
+ template.id,
+ {
+ content: serializeBlocks, // Required to make the `undo` behave correctly.
+ blocks: edited.blocks, // Required to revert the blocks in the editor.
+ source: 'custom', // required to avoid turning the editor into a dirty state
+ },
+ {
+ undoIgnore: true, // Required to merge this edit with the last undo level.
+ }
+ );
+
+ const blocks = parse( fileTemplate?.content?.raw );
+
+ dispatch( coreStore ).editEntityRecord(
+ 'postType',
+ template.type,
+ fileTemplate.id,
+ {
+ content: serializeBlocks,
+ blocks,
+ source: 'theme',
+ }
+ );
+
+ if ( allowUndo ) {
+ const undoRevert = () => {
+ dispatch( coreStore ).editEntityRecord(
+ 'postType',
+ template.type,
+ edited.id,
+ {
+ content: serializeBlocks,
+ blocks: edited.blocks,
+ source: 'custom',
+ }
+ );
+ };
+
+ dispatch( noticesStore ).createSuccessNotice(
+ __( 'Template reset.' ),
+ {
+ type: 'snackbar',
+ id: noticeId,
+ actions: [
+ {
+ label: __( 'Undo' ),
+ onClick: undoRevert,
+ },
+ ],
+ }
+ );
+ }
+ } catch ( error: any ) {
+ const errorMessage =
+ error.message && error.code !== 'unknown_error'
+ ? error.message
+ : __( 'Template revert failed. Please reload.' );
+
+ dispatch( noticesStore ).createErrorNotice( errorMessage, {
+ type: 'snackbar',
+ } );
+ }
+};
+
+const resetTemplateAction: Action< Template | TemplatePart > = {
+ id: 'reset-template',
+ label: __( 'Reset' ),
+ isEligible: ( item ) => {
+ return (
+ isTemplateOrTemplatePart( item ) &&
+ item?.source === TEMPLATE_ORIGINS.custom &&
+ ( Boolean( item.type === 'wp_template' && item?.plugin ) ||
+ item?.has_theme_file )
+ );
+ },
+ icon: backup,
+ supportsBulk: true,
+ hideModalHeader: true,
+ RenderModal: ( { items, closeModal, onActionPerformed } ) => {
+ const [ isBusy, setIsBusy ] = useState( false );
+
+ const { saveEditedEntityRecord } = useDispatch( coreStore );
+ const { createSuccessNotice, createErrorNotice } =
+ useDispatch( noticesStore );
+ const onConfirm = async () => {
+ try {
+ for ( const template of items ) {
+ await revertTemplate( template, {
+ allowUndo: false,
+ } );
+ await saveEditedEntityRecord(
+ 'postType',
+ template.type,
+ template.id
+ );
+ }
+ createSuccessNotice(
+ items.length > 1
+ ? sprintf(
+ /* translators: The number of items. */
+ __( '%s items reset.' ),
+ items.length
+ )
+ : sprintf(
+ /* translators: The template/part's name. */
+ __( '"%s" reset.' ),
+ getItemTitle( items[ 0 ] )
+ ),
+ {
+ type: 'snackbar',
+ id: 'revert-template-action',
+ }
+ );
+ } catch ( error ) {
+ let fallbackErrorMessage;
+ if ( items[ 0 ].type === TEMPLATE_POST_TYPE ) {
+ fallbackErrorMessage =
+ items.length === 1
+ ? __(
+ 'An error occurred while reverting the template.'
+ )
+ : __(
+ 'An error occurred while reverting the templates.'
+ );
+ } else {
+ fallbackErrorMessage =
+ items.length === 1
+ ? __(
+ 'An error occurred while reverting the template part.'
+ )
+ : __(
+ 'An error occurred while reverting the template parts.'
+ );
+ }
+
+ const typedError = error as CoreDataError;
+ const errorMessage =
+ typedError.message && typedError.code !== 'unknown_error'
+ ? typedError.message
+ : fallbackErrorMessage;
+
+ createErrorNotice( errorMessage, { type: 'snackbar' } );
+ }
+ };
+ return (
+
+
+ { __( 'Reset to default and clear all customizations?' ) }
+
+
+
+
+
+
+ );
+ },
+};
+
+export default resetTemplateAction;
diff --git a/packages/fields/src/module.d.ts b/packages/fields/src/module.d.ts
new file mode 100644
index 00000000000000..f15f8771b3ca59
--- /dev/null
+++ b/packages/fields/src/module.d.ts
@@ -0,0 +1,2 @@
+declare module '@wordpress/editor';
+declare module '@wordpress/patterns';
diff --git a/packages/fields/src/mutation/index.ts b/packages/fields/src/mutation/index.ts
new file mode 100644
index 00000000000000..76d35b7b691b42
--- /dev/null
+++ b/packages/fields/src/mutation/index.ts
@@ -0,0 +1,205 @@
+/**
+ * WordPress dependencies
+ */
+import { store as noticesStore } from '@wordpress/notices';
+import { store as coreStore } from '@wordpress/core-data';
+
+/**
+ * Internal dependencies
+ */
+import type { CoreDataError, Post } from '../types';
+import { dispatch } from '@wordpress/data';
+
+export type Notice< T extends Post > = {
+ onSuccess: {
+ id?: string;
+ type?: string;
+ messages: {
+ getOneItemMessage: ( posts: T ) => string;
+ getMultipleItemMessage: ( posts: T[] ) => string;
+ };
+ };
+ onError: {
+ id?: string;
+ type?: string;
+ messages: {
+ getOneItemMessage: ( errors: Set< string > ) => string;
+ getMultipleItemMessage: ( errors: Set< string > ) => string;
+ };
+ };
+};
+
+export const deleteWithNotices = async < T extends Post >(
+ posts: T[],
+ notice: Notice< T >,
+ callbacks: {
+ onActionPerformed?: ( posts: T[] ) => void;
+ onActionError?: () => void;
+ }
+) => {
+ const { createSuccessNotice, createErrorNotice } = dispatch( noticesStore );
+ const { deleteEntityRecord } = dispatch( coreStore );
+ const promiseResult = await Promise.allSettled(
+ posts.map( ( post ) => {
+ return deleteEntityRecord(
+ 'postType',
+ post.type,
+ post.id,
+ { force: true },
+ { throwOnError: true }
+ );
+ } )
+ );
+ // If all the promises were fulfilled with success.
+ if ( promiseResult.every( ( { status } ) => status === 'fulfilled' ) ) {
+ let successMessage;
+ if ( promiseResult.length === 1 ) {
+ successMessage = notice.onSuccess.messages.getOneItemMessage(
+ posts[ 0 ]
+ );
+ } else {
+ successMessage =
+ notice.onSuccess.messages.getMultipleItemMessage( posts );
+ }
+ createSuccessNotice( successMessage, {
+ type: notice.onSuccess.type ?? 'snackbar',
+ id: notice.onSuccess.id,
+ } );
+ callbacks.onActionPerformed?.( posts );
+ } else {
+ const errorMessages = new Set< string >();
+ // If there was at lease one failure.
+ let errorMessage;
+ // If we were trying to permanently delete a single post.
+ if ( promiseResult.length === 1 ) {
+ const typedError = promiseResult[ 0 ] as {
+ reason?: CoreDataError;
+ };
+ if ( typedError.reason?.message ) {
+ errorMessage = typedError.reason.message;
+ errorMessages.add( typedError.reason.message );
+ } else {
+ errorMessage =
+ notice.onError.messages.getOneItemMessage( errorMessages );
+ }
+ // If we were trying to permanently delete multiple posts
+ } else {
+ const failedPromises = promiseResult.filter(
+ ( { status } ) => status === 'rejected'
+ );
+ for ( const failedPromise of failedPromises ) {
+ const typedError = failedPromise as {
+ reason?: CoreDataError;
+ };
+ if ( typedError.reason?.message ) {
+ errorMessages.add( typedError.reason.message );
+ }
+ }
+
+ errorMessage =
+ notice.onError.messages.getMultipleItemMessage( errorMessages );
+ }
+ createErrorNotice( errorMessage, {
+ type: notice.onError.type ?? 'snackbar',
+ id: notice.onError.id,
+ } );
+ callbacks.onActionError?.();
+ }
+};
+
+export const editWithNotices = async < T extends Post >(
+ postsWithUpdates: {
+ originalPost: T;
+ changes: Partial< T >;
+ }[],
+ notice: Notice< T >,
+ callbacks: {
+ onActionPerformed?: ( posts: T[] ) => void;
+ onActionError?: () => void;
+ }
+) => {
+ const { createSuccessNotice, createErrorNotice } = dispatch( noticesStore );
+ const { editEntityRecord, saveEditedEntityRecord } = dispatch( coreStore );
+ await Promise.allSettled(
+ postsWithUpdates.map( ( post ) => {
+ return editEntityRecord(
+ 'postType',
+ post.originalPost.type,
+ post.originalPost.id,
+ {
+ ...post.changes,
+ }
+ );
+ } )
+ );
+ const promiseResult = await Promise.allSettled(
+ postsWithUpdates.map( ( post ) => {
+ return saveEditedEntityRecord(
+ 'postType',
+ post.originalPost.type,
+ post.originalPost.id,
+ {
+ throwOnError: true,
+ }
+ );
+ } )
+ );
+ // If all the promises were fulfilled with success.
+ if ( promiseResult.every( ( { status } ) => status === 'fulfilled' ) ) {
+ let successMessage;
+ if ( promiseResult.length === 1 ) {
+ successMessage = notice.onSuccess.messages.getOneItemMessage(
+ postsWithUpdates[ 0 ].originalPost
+ );
+ } else {
+ successMessage = notice.onSuccess.messages.getMultipleItemMessage(
+ postsWithUpdates.map( ( post ) => post.originalPost )
+ );
+ }
+ createSuccessNotice( successMessage, {
+ type: notice.onSuccess.type ?? 'snackbar',
+ id: notice.onSuccess.id,
+ } );
+ callbacks.onActionPerformed?.(
+ postsWithUpdates.map( ( post ) => post.originalPost )
+ );
+ } else {
+ const errorMessages = new Set< string >();
+ // If there was at lease one failure.
+ let errorMessage;
+ // If we were trying to permanently delete a single post.
+ if ( promiseResult.length === 1 ) {
+ const typedError = promiseResult[ 0 ] as {
+ reason?: CoreDataError;
+ };
+ if ( typedError.reason?.message ) {
+ errorMessage = typedError.reason.message;
+ errorMessages.add( typedError.reason.message );
+ } else {
+ errorMessage =
+ notice.onError.messages.getOneItemMessage( errorMessages );
+ }
+ // If we were trying to permanently delete multiple posts
+ } else {
+ const failedPromises = promiseResult.filter(
+ ( { status } ) => status === 'rejected'
+ );
+ for ( const failedPromise of failedPromises ) {
+ const typedError = failedPromise as {
+ reason?: CoreDataError;
+ };
+ if ( typedError.reason?.message ) {
+ errorMessages.add( typedError.reason.message );
+ }
+ }
+
+ errorMessage =
+ notice.onError.messages.getMultipleItemMessage( errorMessages );
+ }
+ createErrorNotice( errorMessage, {
+ type: notice.onError.type ?? 'snackbar',
+ id: notice.onError.id,
+ } );
+ callbacks.onActionError?.();
+ }
+};
diff --git a/packages/fields/src/types.ts b/packages/fields/src/types.ts
index 664c2dd417201c..a5ed9596b07dfd 100644
--- a/packages/fields/src/types.ts
+++ b/packages/fields/src/types.ts
@@ -54,6 +54,7 @@ export interface TemplatePart extends CommonPost {
has_theme_file: boolean;
id: string;
area: string;
+ plugin?: string;
}
export interface Pattern extends CommonPost {
diff --git a/packages/fields/src/wordpress-editor.d.ts b/packages/fields/src/wordpress-editor.d.ts
deleted file mode 100644
index 915dacd5f05a9c..00000000000000
--- a/packages/fields/src/wordpress-editor.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-declare module '@wordpress/editor';
diff --git a/packages/fields/tsconfig.json b/packages/fields/tsconfig.json
index c55be59acf40f0..69dbd076d05747 100644
--- a/packages/fields/tsconfig.json
+++ b/packages/fields/tsconfig.json
@@ -7,6 +7,7 @@
"checkJs": false
},
"references": [
+ { "path": "../api-fetch" },
{ "path": "../components" },
{ "path": "../compose" },
{ "path": "../data" },
@@ -24,6 +25,5 @@
{ "path": "../hooks" },
{ "path": "../html-entities" }
],
- "include": [ "src" ],
- "exclude": [ "@wordpress/editor" ]
+ "include": [ "src" ]
}