@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
260 lines (248 loc) • 7.83 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = MaybeUploadMediaPanel;
var _components = require("@wordpress/components");
var _data = require("@wordpress/data");
var _i18n = require("@wordpress/i18n");
var _blockEditor = require("@wordpress/block-editor");
var _element = require("@wordpress/element");
var _blob = require("@wordpress/blob");
var _mediaUtil = require("./media-util");
var _jsxRuntime = require("react/jsx-runtime");
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
function flattenBlocks(blocks) {
const result = [];
blocks.forEach(block => {
result.push(block);
result.push(...flattenBlocks(block.innerBlocks));
});
return result;
}
/**
* Determine whether a block has external media.
*
* Different blocks use different attribute names (and potentially
* different logic as well) in determining whether the media is
* present, and whether it's external.
*
* @param {{name: string, attributes: Object}} block The block.
* @return {boolean?} Whether the block has external media
*/
function hasExternalMedia(block) {
if (block.name === 'core/image' || block.name === 'core/cover') {
return block.attributes.url && !block.attributes.id;
}
if (block.name === 'core/media-text') {
return block.attributes.mediaUrl && !block.attributes.mediaId;
}
return undefined;
}
/**
* Retrieve media info from a block.
*
* Different blocks use different attribute names, so we need this
* function to normalize things into a consistent naming scheme.
*
* @param {{name: string, attributes: Object}} block The block.
* @return {{url: ?string, alt: ?string, id: ?number}} The media info for the block.
*/
function getMediaInfo(block) {
if (block.name === 'core/image' || block.name === 'core/cover') {
const {
url,
alt,
id
} = block.attributes;
return {
url,
alt,
id
};
}
if (block.name === 'core/media-text') {
const {
mediaUrl: url,
mediaAlt: alt,
mediaId: id
} = block.attributes;
return {
url,
alt,
id
};
}
return {};
}
// Image component to represent a single image in the upload dialog.
function Image({
clientId,
alt,
url
}) {
const {
selectBlock
} = (0, _data.useDispatch)(_blockEditor.store);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__unstableMotion.img, {
tabIndex: 0,
role: "button",
"aria-label": (0, _i18n.__)('Select image block.'),
onClick: () => {
selectBlock(clientId);
},
onKeyDown: event => {
if (event.key === 'Enter' || event.key === ' ') {
selectBlock(clientId);
event.preventDefault();
}
},
alt: alt,
src: url,
animate: {
opacity: 1
},
exit: {
opacity: 0,
scale: 0
},
style: {
width: '36px',
height: '36px',
objectFit: 'cover',
borderRadius: '2px',
cursor: 'pointer'
},
whileHover: {
scale: 1.08
}
}, clientId);
}
function MaybeUploadMediaPanel() {
const [isUploading, setIsUploading] = (0, _element.useState)(false);
const [isAnimating, setIsAnimating] = (0, _element.useState)(false);
const [hadUploadError, setHadUploadError] = (0, _element.useState)(false);
const {
editorBlocks,
mediaUpload
} = (0, _data.useSelect)(select => ({
editorBlocks: select(_blockEditor.store).getBlocks(),
mediaUpload: select(_blockEditor.store).getSettings().mediaUpload
}), []);
// Get a list of blocks with external media.
const blocksWithExternalMedia = flattenBlocks(editorBlocks).filter(block => hasExternalMedia(block));
const {
updateBlockAttributes
} = (0, _data.useDispatch)(_blockEditor.store);
if (!mediaUpload || !blocksWithExternalMedia.length) {
return null;
}
const panelBodyTitle = [(0, _i18n.__)('Suggestion:'), /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
className: "editor-post-publish-panel__link",
children: (0, _i18n.__)('External media')
}, "label")];
/**
* Update an individual block to point to newly-added library media.
*
* Different blocks use different attribute names, so we need this
* function to ensure we modify the correct attributes for each type.
*
* @param {{name: string, attributes: Object}} block The block.
* @param {{id: number, url: string}} media Media library file info.
*/
function updateBlockWithUploadedMedia(block, media) {
if (block.name === 'core/image' || block.name === 'core/cover') {
updateBlockAttributes(block.clientId, {
id: media.id,
url: media.url
});
}
if (block.name === 'core/media-text') {
updateBlockAttributes(block.clientId, {
mediaId: media.id,
mediaUrl: media.url
});
}
}
// Handle fetching and uploading all external media in the post.
function uploadImages() {
setIsUploading(true);
setHadUploadError(false);
// Multiple blocks can be using the same URL, so we
// should ensure we only fetch and upload each of them once.
const mediaUrls = new Set(blocksWithExternalMedia.map(block => {
const {
url
} = getMediaInfo(block);
return url;
}));
// Create an upload promise for each URL, that we can wait for in all
// blocks that make use of that media.
const uploadPromises = Object.fromEntries(Object.entries((0, _mediaUtil.fetchMedia)([...mediaUrls])).map(([url, filePromise]) => {
const uploadPromise = filePromise.then(blob => new Promise((resolve, reject) => {
mediaUpload({
filesList: [blob],
onFileChange: ([media]) => {
if ((0, _blob.isBlobURL)(media.url)) {
return;
}
resolve(media);
},
onError() {
reject();
}
});
}));
return [url, uploadPromise];
}));
// Wait for all blocks to be updated with library media.
Promise.allSettled(blocksWithExternalMedia.map(block => {
const {
url
} = getMediaInfo(block);
return uploadPromises[url].then(media => updateBlockWithUploadedMedia(block, media)).then(() => setIsAnimating(true)).catch(() => setHadUploadError(true));
})).finally(() => {
setIsUploading(false);
});
}
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.PanelBody, {
initialOpen: true,
title: panelBodyTitle,
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("p", {
children: (0, _i18n.__)('Upload external images to the Media Library. Images from different domains may load slowly, display incorrectly, or be removed unexpectedly.')
}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
style: {
display: 'inline-flex',
flexWrap: 'wrap',
gap: '8px'
},
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__unstableAnimatePresence, {
onExitComplete: () => setIsAnimating(false),
children: blocksWithExternalMedia.map(block => {
const {
url,
alt
} = getMediaInfo(block);
return /*#__PURE__*/(0, _jsxRuntime.jsx)(Image, {
clientId: block.clientId,
url: url,
alt: alt
}, block.clientId);
})
}), isUploading || isAnimating ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Spinner, {}) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, {
__next40pxDefaultSize: true,
variant: "primary",
onClick: uploadImages,
children: (0, _i18n.__)('Upload')
})]
}), hadUploadError && /*#__PURE__*/(0, _jsxRuntime.jsx)("p", {
children: (0, _i18n.__)('Upload failed, try again.')
})]
});
}
//# sourceMappingURL=maybe-upload-media.js.map