"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.ImageEdit = ImageEdit;
exports.pickRelevantMediaFiles = exports.isExternalImage = exports.default = void 0;
var _clsx = _interopRequireDefault(require("clsx"));
var _blob = require("@wordpress/blob");
var _blocks = require("@wordpress/blocks");
var _components = require("@wordpress/components");
var _data = require("@wordpress/data");
var _blockEditor = require("@wordpress/block-editor");
var _element = require("@wordpress/element");
var _i18n = require("@wordpress/i18n");
var _icons = require("@wordpress/icons");
var _notices = require("@wordpress/notices");
var _compose = require("@wordpress/compose");
var _hooks = require("../utils/hooks");
var _image = _interopRequireDefault(require("./image"));
var _utils = require("./utils");
var _useMaxWidthObserver = require("./use-max-width-observer");
var _constants = require("./constants");
var _jsxRuntime = require("react/jsx-runtime");
/**
 * External dependencies
 */

/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

/**
 * Module constants
 */

const pickRelevantMediaFiles = (image, size) => {
  const imageProps = Object.fromEntries(Object.entries(image !== null && image !== void 0 ? image : {}).filter(([key]) => ['alt', 'id', 'link', 'caption'].includes(key)));
  imageProps.url = image?.sizes?.[size]?.url || image?.media_details?.sizes?.[size]?.source_url || image.url;
  return imageProps;
};

/**
 * Is the url for the image hosted externally. An externally hosted image has no
 * id and is not a blob url.
 *
 * @param {number=} id  The id of the image.
 * @param {string=} url The url of the image.
 *
 * @return {boolean} Is the url an externally hosted url?
 */
exports.pickRelevantMediaFiles = pickRelevantMediaFiles;
const isExternalImage = (id, url) => url && !id && !(0, _blob.isBlobURL)(url);

/**
 * Checks if WP generated the specified image size. Size generation is skipped
 * when the image is smaller than the said size.
 *
 * @param {Object} image
 * @param {string} size
 *
 * @return {boolean} Whether or not it has default image size.
 */
exports.isExternalImage = isExternalImage;
function hasSize(image, size) {
  var _image$sizes$size, _image$media_details$;
  return 'url' in ((_image$sizes$size = image?.sizes?.[size]) !== null && _image$sizes$size !== void 0 ? _image$sizes$size : {}) || 'source_url' in ((_image$media_details$ = image?.media_details?.sizes?.[size]) !== null && _image$media_details$ !== void 0 ? _image$media_details$ : {});
}
function ImageEdit({
  attributes,
  setAttributes,
  isSelected: isSingleSelected,
  className,
  insertBlocksAfter,
  onReplace,
  context,
  clientId,
  __unstableParentLayout: parentLayout
}) {
  const {
    url = '',
    alt,
    caption,
    id,
    width,
    height,
    sizeSlug,
    aspectRatio,
    scale,
    align,
    metadata
  } = attributes;
  const [temporaryURL, setTemporaryURL] = (0, _element.useState)(attributes.blob);
  const containerRef = (0, _element.useRef)();
  // Only observe the max width from the parent container when the parent layout is not flex nor grid.
  // This won't work for them because the container width changes with the image.
  // TODO: Find a way to observe the container width for flex and grid layouts.
  const isMaxWidthContainerWidth = !parentLayout || parentLayout.type !== 'flex' && parentLayout.type !== 'grid';
  const [maxWidthObserver, maxContentWidth] = (0, _useMaxWidthObserver.useMaxWidthObserver)();
  const [placeholderResizeListener, {
    width: placeholderWidth
  }] = (0, _compose.useResizeObserver)();
  const isSmallContainer = placeholderWidth && placeholderWidth < 160;
  const altRef = (0, _element.useRef)();
  (0, _element.useEffect)(() => {
    altRef.current = alt;
  }, [alt]);
  const captionRef = (0, _element.useRef)();
  (0, _element.useEffect)(() => {
    captionRef.current = caption;
  }, [caption]);
  const {
    __unstableMarkNextChangeAsNotPersistent,
    replaceBlock
  } = (0, _data.useDispatch)(_blockEditor.store);
  (0, _element.useEffect)(() => {
    if (['wide', 'full'].includes(align)) {
      __unstableMarkNextChangeAsNotPersistent();
      setAttributes({
        width: undefined,
        height: undefined,
        aspectRatio: undefined,
        scale: undefined
      });
    }
  }, [__unstableMarkNextChangeAsNotPersistent, align, setAttributes]);
  const {
    getSettings,
    getBlockRootClientId,
    getBlockName,
    canInsertBlockType
  } = (0, _data.useSelect)(_blockEditor.store);
  const blockEditingMode = (0, _blockEditor.useBlockEditingMode)();
  const {
    createErrorNotice
  } = (0, _data.useDispatch)(_notices.store);
  function onUploadError(message) {
    createErrorNotice(message, {
      type: 'snackbar'
    });
    setAttributes({
      src: undefined,
      id: undefined,
      url: undefined,
      blob: undefined
    });
  }
  function onSelectImagesList(images) {
    const win = containerRef.current?.ownerDocument.defaultView;
    if (images.every(file => file instanceof win.File)) {
      /** @type {File[]} */
      const files = images;
      const rootClientId = getBlockRootClientId(clientId);
      if (files.some(file => !(0, _utils.isValidFileType)(file))) {
        // Copied from the same notice in the gallery block.
        createErrorNotice((0, _i18n.__)('If uploading to a gallery all files need to be image formats'), {
          id: 'gallery-upload-invalid-file',
          type: 'snackbar'
        });
      }
      const imageBlocks = files.filter(file => (0, _utils.isValidFileType)(file)).map(file => (0, _blocks.createBlock)('core/image', {
        blob: (0, _blob.createBlobURL)(file)
      }));
      if (getBlockName(rootClientId) === 'core/gallery') {
        replaceBlock(clientId, imageBlocks);
      } else if (canInsertBlockType('core/gallery', rootClientId)) {
        const galleryBlock = (0, _blocks.createBlock)('core/gallery', {}, imageBlocks);
        replaceBlock(clientId, galleryBlock);
      }
    }
  }
  function onSelectImage(media) {
    if (Array.isArray(media)) {
      onSelectImagesList(media);
      return;
    }
    if (!media || !media.url) {
      setAttributes({
        url: undefined,
        alt: undefined,
        id: undefined,
        title: undefined,
        caption: undefined,
        blob: undefined
      });
      setTemporaryURL();
      return;
    }
    if ((0, _blob.isBlobURL)(media.url)) {
      setTemporaryURL(media.url);
      return;
    }
    const {
      imageDefaultSize
    } = getSettings();

    // Try to use the previous selected image size if its available
    // otherwise try the default image size or fallback to "full"
    let newSize = 'full';
    if (sizeSlug && hasSize(media, sizeSlug)) {
      newSize = sizeSlug;
    } else if (hasSize(media, imageDefaultSize)) {
      newSize = imageDefaultSize;
    }
    let mediaAttributes = pickRelevantMediaFiles(media, newSize);

    // If a caption text was meanwhile written by the user,
    // make sure the text is not overwritten by empty captions.
    if (captionRef.current && !mediaAttributes.caption) {
      const {
        caption: omittedCaption,
        ...restMediaAttributes
      } = mediaAttributes;
      mediaAttributes = restMediaAttributes;
    }
    let additionalAttributes;
    // Reset the dimension attributes if changing to a different image.
    if (!media.id || media.id !== id) {
      additionalAttributes = {
        sizeSlug: newSize
      };
    } else {
      // Keep the same url when selecting the same file, so "Resolution"
      // option is not changed.
      additionalAttributes = {
        url
      };
    }

    // Check if default link setting should be used.
    let linkDestination = attributes.linkDestination;
    if (!linkDestination) {
      // Use the WordPress option to determine the proper default.
      // The constants used in Gutenberg do not match WP options so a little more complicated than ideal.
      // TODO: fix this in a follow up PR, requires updating media-text and ui component.
      switch (window?.wp?.media?.view?.settings?.defaultProps?.link || _constants.LINK_DESTINATION_NONE) {
        case 'file':
        case _constants.LINK_DESTINATION_MEDIA:
          linkDestination = _constants.LINK_DESTINATION_MEDIA;
          break;
        case 'post':
        case _constants.LINK_DESTINATION_ATTACHMENT:
          linkDestination = _constants.LINK_DESTINATION_ATTACHMENT;
          break;
        case _constants.LINK_DESTINATION_CUSTOM:
          linkDestination = _constants.LINK_DESTINATION_CUSTOM;
          break;
        case _constants.LINK_DESTINATION_NONE:
          linkDestination = _constants.LINK_DESTINATION_NONE;
          break;
      }
    }

    // Check if the image is linked to it's media.
    let href;
    switch (linkDestination) {
      case _constants.LINK_DESTINATION_MEDIA:
        href = media.url;
        break;
      case _constants.LINK_DESTINATION_ATTACHMENT:
        href = media.link;
        break;
    }
    mediaAttributes.href = href;
    setAttributes({
      blob: undefined,
      ...mediaAttributes,
      ...additionalAttributes,
      linkDestination
    });
    setTemporaryURL();
  }
  function onSelectURL(newURL) {
    if (newURL !== url) {
      setAttributes({
        blob: undefined,
        url: newURL,
        id: undefined,
        sizeSlug: getSettings().imageDefaultSize
      });
      setTemporaryURL();
    }
  }
  (0, _hooks.useUploadMediaFromBlobURL)({
    url: temporaryURL,
    allowedTypes: _constants.ALLOWED_MEDIA_TYPES,
    onChange: onSelectImage,
    onError: onUploadError
  });
  const isExternal = isExternalImage(id, url);
  const src = isExternal ? url : undefined;
  const mediaPreview = !!url && /*#__PURE__*/(0, _jsxRuntime.jsx)("img", {
    alt: (0, _i18n.__)('Edit image'),
    title: (0, _i18n.__)('Edit image'),
    className: "edit-image-preview",
    src: url
  });
  const borderProps = (0, _blockEditor.__experimentalUseBorderProps)(attributes);
  const shadowProps = (0, _blockEditor.__experimentalGetShadowClassesAndStyles)(attributes);
  const classes = (0, _clsx.default)(className, {
    'is-transient': !!temporaryURL,
    'is-resized': !!width || !!height,
    [`size-${sizeSlug}`]: sizeSlug,
    'has-custom-border': !!borderProps.className || borderProps.style && Object.keys(borderProps.style).length > 0
  });
  const blockProps = (0, _blockEditor.useBlockProps)({
    ref: containerRef,
    className: classes
  });

  // Much of this description is duplicated from MediaPlaceholder.
  const {
    lockUrlControls = false,
    lockUrlControlsMessage
  } = (0, _data.useSelect)(select => {
    if (!isSingleSelected) {
      return {};
    }
    const blockBindingsSource = (0, _blocks.getBlockBindingsSource)(metadata?.bindings?.url?.source);
    return {
      lockUrlControls: !!metadata?.bindings?.url && !blockBindingsSource?.canUserEditValue?.({
        select,
        context,
        args: metadata?.bindings?.url?.args
      }),
      lockUrlControlsMessage: blockBindingsSource?.label ? (0, _i18n.sprintf)( /* translators: %s: Label of the bindings source. */
      (0, _i18n.__)('Connected to %s'), blockBindingsSource.label) : (0, _i18n.__)('Connected to dynamic data')
    };
  }, [context, isSingleSelected, metadata?.bindings?.url]);
  const placeholder = content => {
    return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Placeholder, {
      className: (0, _clsx.default)('block-editor-media-placeholder', {
        [borderProps.className]: !!borderProps.className && !isSingleSelected
      }),
      icon: !isSmallContainer && (lockUrlControls ? _icons.plugins : _icons.image),
      withIllustration: !isSingleSelected || isSmallContainer,
      label: !isSmallContainer && (0, _i18n.__)('Image'),
      instructions: !lockUrlControls && !isSmallContainer && (0, _i18n.__)('Upload or drag an image file here, or pick one from your library.'),
      style: {
        aspectRatio: !(width && height) && aspectRatio ? aspectRatio : undefined,
        width: height && aspectRatio ? '100%' : width,
        height: width && aspectRatio ? '100%' : height,
        objectFit: scale,
        ...borderProps.style,
        ...shadowProps.style
      },
      children: [lockUrlControls && !isSmallContainer && lockUrlControlsMessage, !lockUrlControls && !isSmallContainer && content, placeholderResizeListener]
    });
  };
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
    children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("figure", {
      ...blockProps,
      children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_image.default, {
        temporaryURL: temporaryURL,
        attributes: attributes,
        setAttributes: setAttributes,
        isSingleSelected: isSingleSelected,
        insertBlocksAfter: insertBlocksAfter,
        onReplace: onReplace,
        onSelectImage: onSelectImage,
        onSelectURL: onSelectURL,
        onUploadError: onUploadError,
        context: context,
        clientId: clientId,
        blockEditingMode: blockEditingMode,
        parentLayoutType: parentLayout?.type,
        maxContentWidth: maxContentWidth
      }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.MediaPlaceholder, {
        icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.BlockIcon, {
          icon: _icons.image
        }),
        onSelect: onSelectImage,
        onSelectURL: onSelectURL,
        onError: onUploadError,
        placeholder: placeholder,
        accept: "image/*",
        allowedTypes: _constants.ALLOWED_MEDIA_TYPES,
        handleUpload: files => files.length === 1,
        value: {
          id,
          src
        },
        mediaPreview: mediaPreview,
        disableMediaButtons: temporaryURL || url
      })]
    }),
    // The listener cannot be placed as the first element as it will break the in-between inserter.
    // See https://github.com/WordPress/gutenberg/blob/71134165868298fc15e22896d0c28b41b3755ff7/packages/block-editor/src/components/block-list/use-in-between-inserter.js#L120
    isSingleSelected && isMaxWidthContainerWidth && maxWidthObserver]
  });
}
var _default = exports.default = ImageEdit;
//# sourceMappingURL=edit.js.map