"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: '32px',
      height: '32px',
      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, {
        size: "compact",
        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