From ee07bbf5081ace0597d7558fc37de066257024a2 Mon Sep 17 00:00:00 2001 From: Ramon Date: Wed, 15 Dec 2021 09:29:04 +1100 Subject: [PATCH] Image: Reflect media deletion in the editor (#35973) * initial commit. listening out for changes to the attachments collection and passing to onRemove prop. * Removing console.log * Reverting component changes * Adding a store to media utils * Rolling back historical experimental commits. Now relying on the `onClose` event of the media upload, and the media object to test if the media has been "destroyed." * Testing setting a temporary media object so we can keep track of uploaded media * Winding this back to check if an image exists when the editor loads. This removes the `isSelected` check, but also removes a bunch of code as well. We could run the same check on `isSelected`... TBC * Removed the onerror listener and relying on the error handler already added to the Image props * Fixed grammatical mistake. * Check if the media has been destroyed when the image is selected * Only delete image source URL and ID when detecting a destroyed image. That way the caption, title and alt values persist in case the user deletes, then replaces. --- .../src/components/media-placeholder/index.js | 2 + .../components/media-replace-flow/index.js | 2 + packages/block-library/src/image/edit.js | 44 +++++++++++++++++++ packages/block-library/src/image/image.js | 17 +++++-- 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/media-placeholder/index.js b/packages/block-editor/src/components/media-placeholder/index.js index f13206870f815..123c8c5b0070f 100644 --- a/packages/block-editor/src/components/media-placeholder/index.js +++ b/packages/block-editor/src/components/media-placeholder/index.js @@ -75,6 +75,7 @@ export function MediaPlaceholder( { onDoubleClick, onFilesPreUpload = noop, onHTMLDrop = noop, + onClose = noop, children, mediaLibraryButton, placeholder, @@ -328,6 +329,7 @@ export function MediaPlaceholder( { gallery={ multiple && onlyAllowsImages() } multiple={ multiple } onSelect={ onSelect } + onClose={ onClose } allowedTypes={ allowedTypes } value={ Array.isArray( value ) diff --git a/packages/block-editor/src/components/media-replace-flow/index.js b/packages/block-editor/src/components/media-replace-flow/index.js index 3cd8e0f1755ae..160019ea52999 100644 --- a/packages/block-editor/src/components/media-replace-flow/index.js +++ b/packages/block-editor/src/components/media-replace-flow/index.js @@ -39,6 +39,7 @@ const MediaReplaceFlow = ( { onSelect, onSelectURL, onFilesUpload = noop, + onCloseModal = noop, name = __( 'Replace' ), createNotice, removeNotice, @@ -136,6 +137,7 @@ const MediaReplaceFlow = ( { value={ mediaId } onSelect={ ( media ) => selectMedia( media ) } allowedTypes={ allowedTypes } + onClose={ onCloseModal } render={ ( { open } ) => ( { __( 'Open Media Library' ) } diff --git a/packages/block-library/src/image/edit.js b/packages/block-library/src/image/edit.js index 7f29a8d1078c5..bcef8caefe829 100644 --- a/packages/block-library/src/image/edit.js +++ b/packages/block-library/src/image/edit.js @@ -87,6 +87,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 = wp?.media?.attachment( id ) || {}; + return attachment.destroyed; +} + export function ImageEdit( { attributes, setAttributes, @@ -127,6 +141,33 @@ 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 ) { + // If the image block was not replaced with an embed, + // clear the attributes and trigger the placeholder. + if ( ! isReplaced ) { + setAttributes( { + url: undefined, + id: undefined, + } ); + } + } + function onUploadError( message ) { noticeOperations.removeAllNotices(); noticeOperations.createErrorNotice( message ); @@ -324,6 +365,8 @@ export function ImageEdit( { containerRef={ ref } context={ context } clientId={ clientId } + onCloseModal={ onCloseModal } + onImageLoadError={ onImageError } /> ) } { ! url && ( @@ -340,6 +383,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/image.js b/packages/block-library/src/image/image.js index 8c49687685d87..455743c6ebee0 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -43,7 +43,7 @@ import { store as coreStore } from '@wordpress/core-data'; */ import { createUpgradedEmbedBlock } from '../embed/util'; import useClientWidth from './use-client-width'; -import { isExternalImage } from './edit'; +import { isExternalImage, isMediaDestroyed } from './edit'; /** * Module constants @@ -72,12 +72,14 @@ export default function Image( { isSelected, insertBlocksAfter, onReplace, + onCloseModal, onSelectImage, onSelectURL, onUploadError, containerRef, context, clientId, + onImageLoadError, } ) { const imageRef = useRef(); const captionRef = useRef(); @@ -213,11 +215,16 @@ export default function Image( { } function onImageError() { - // Check if there's an embed block that handles this URL. + // Check if there's an embed block that handles this URL, e.g., instagram URL. + // See: https://github.com/WordPress/gutenberg/pull/11472 const embedBlock = createUpgradedEmbedBlock( { attributes: { url } } ); - if ( undefined !== embedBlock ) { + const shouldReplace = undefined !== embedBlock; + + if ( shouldReplace ) { onReplace( embedBlock ); } + + onImageLoadError( shouldReplace ); } function onSetHref( props ) { @@ -289,6 +296,9 @@ export default function Image( { if ( ! isSelected ) { setIsEditingImage( false ); } + if ( isSelected && isMediaDestroyed( id ) ) { + onImageLoadError(); + } }, [ isSelected ] ); const canEditImage = id && naturalWidth && naturalHeight && imageEditing; @@ -352,6 +362,7 @@ export default function Image( { onSelect={ onSelectImage } onSelectURL={ onSelectURL } onError={ onUploadError } + onCloseModal={ onCloseModal } /> ) }