From 320d630ee875f26c7c45f71eb61b7f40fff6aabb Mon Sep 17 00:00:00 2001 From: ramonjd Date: Mon, 23 May 2022 14:27:14 +1000 Subject: [PATCH] Pulls in the changes from https://github.com/WordPress/gutenberg/pull/35973 to check whether an image has been deleted from the attachments. If the image block was not replaced with an embed, and it's not an external image, and it's been deleted from the database, clear the attributes and trigger the placeholder. --- packages/block-library/src/image/edit.js | 60 +++++++++++++++++++++++ packages/block-library/src/image/utils.js | 23 +++++++++ 2 files changed, 83 insertions(+) diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index 3af2bfe7deffa..aa512b4b300a2 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -26,6 +26,7 @@ import { image as icon } from '@wordpress/icons'; * Internal dependencies */ import Image from './image'; +import { isMediaFileDeleted } from './utils'; /** * Module constants @@ -85,6 +86,20 @@ function hasDefaultSize( image, defaultSize ) { ); } +/** + * Checks if a media attachment object has been "destroyed", + * that is, removed from the media library. The core Media Library + * adds a `destroyed` property to a deleted attachment object in the media collection. + * + * @param {number} id The attachment id. + * + * @return {boolean} Whether the image has been destroyed. + */ +export function isMediaDestroyed( id ) { + const attachment = window?.wp?.media?.attachment( id ) || {}; + return attachment.destroyed; +} + export function ImageEdit( { attributes, setAttributes, @@ -125,6 +140,48 @@ export function ImageEdit( { return pick( getSettings(), [ 'imageDefaultSize', 'mediaUpload' ] ); }, [] ); + // A callback passed to MediaUpload, + // fired when the media modal closes. + function onCloseModal() { + if ( isMediaDestroyed( attributes?.id ) ) { + setAttributes( { + url: undefined, + id: undefined, + } ); + } + } + + /* + Runs an error callback if the image does not load. + If the error callback is triggered, we infer that that image + has been deleted. + */ + function onImageError( isReplaced = false ) { + noticeOperations.removeAllNotices(); + noticeOperations.createErrorNotice( + sprintf( + /* translators: %s url or missing image */ + __( 'Error loading image: %s' ), + url + ) + ); + + // If the image block was not replaced with an embed, + // and it's not an external image, + // and it's been deleted from the database, + // clear the attributes and trigger the placeholder. + if ( id && ! isReplaced && ! isExternalImage( id, url ) ) { + isMediaFileDeleted( id ).then( ( isFileDeleted ) => { + if ( isFileDeleted ) { + setAttributes( { + url: undefined, + id: undefined, + } ); + } + } ); + } + } + function onUploadError( message ) { noticeOperations.removeAllNotices(); noticeOperations.createErrorNotice( message ); @@ -323,6 +380,8 @@ export function ImageEdit( { containerRef={ ref } context={ context } clientId={ clientId } + onCloseModal={ onCloseModal } + onImageLoadError={ onImageError } /> ) } { ! url && ( @@ -339,6 +398,7 @@ export function ImageEdit( { onSelectURL={ onSelectURL } notices={ noticeUI } onError={ onUploadError } + onClose={ onCloseModal } accept="image/*" allowedTypes={ ALLOWED_MEDIA_TYPES } value={ { id, src } } diff --git a/packages/block-library/src/image/utils.js b/packages/block-library/src/image/utils.js index 9ac6e7fc38ceb..73b8211b1a120 100644 --- a/packages/block-library/src/image/utils.js +++ b/packages/block-library/src/image/utils.js @@ -3,6 +3,11 @@ */ import { isEmpty, each, get } from 'lodash'; +/** + * WordPress dependencies + */ +import apiFetch from '@wordpress/api-fetch'; + /** * Internal dependencies */ @@ -72,3 +77,21 @@ export function getImageSizeAttributes( image, size ) { return {}; } + +/** + * Performs a GET request on an image file to confirm whether it has been deleted from the database. + * + * @param {number=} mediaId The id of the image. + * @return {Promise} Media Object Promise. + */ +export async function isMediaFileDeleted( mediaId ) { + try { + const response = await apiFetch( { + path: `/wp/v2/media/${ mediaId }`, + } ); + const isMediaFileAvailable = response && response?.id === mediaId; + return ! isMediaFileAvailable; + } catch ( err ) { + return true; + } +}