UNPKG

@wordpress/editor

Version:
260 lines (248 loc) 7.83 kB
"use strict"; 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