+ { url }
+
+ { __(
+ 'This might be due to a network error, or the image may have been deleted.'
+ ) }
+
+
+ {
+ onClear();
+ onSelect( media );
+ } }
+ onSelectURL={ ( selectedUrl ) => {
+ onClear();
+ onSelectURL( selectedUrl );
+ } }
+ onError={ onUploadError }
+ onCloseModal={ onCloseModal }
+ buttonVariant="primary"
+ />
+
+
+
+);
+
export function ImageEdit( {
attributes,
setAttributes,
@@ -108,6 +193,7 @@ export function ImageEdit( {
sizeSlug,
} = attributes;
const [ temporaryURL, setTemporaryURL ] = useState();
+ const [ hasImageLoadError, setHasImageLoadError ] = useState( false );
const altRef = useRef();
useEffect( () => {
@@ -125,6 +211,45 @@ 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 ) ) {
+ setHasImageLoadError( true );
+ }
+ }
+
+ function clearImageAttributes() {
+ setHasImageLoadError( false );
+ setAttributes( {
+ src: undefined,
+ id: undefined,
+ url: 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.
+ *
+ * @param {boolean} isReplaced Whether the image has been replaced.
+ */
+ function onImageError( isReplaced = false ) {
+ // 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 an error placeholder.
+ if ( id && ! isReplaced && ! isExternalImage( id, url ) ) {
+ isMediaFileDeleted( id ).then( ( isFileDeleted ) => {
+ if ( isFileDeleted ) {
+ noticeOperations.removeAllNotices();
+ setHasImageLoadError( true );
+ }
+ } );
+ }
+ }
+
function onUploadError( message ) {
noticeOperations.removeAllNotices();
noticeOperations.createErrorNotice( message );
@@ -309,7 +434,18 @@ export function ImageEdit( {
return (