"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
var _exportNames = {
  setCurrentTemplateId: true,
  createTemplate: true,
  showBlockTypes: true,
  hideBlockTypes: true,
  saveDirtyEntities: true,
  revertTemplate: true,
  removeTemplates: true
};
exports.saveDirtyEntities = exports.revertTemplate = exports.removeTemplates = exports.hideBlockTypes = exports.createTemplate = void 0;
exports.setCurrentTemplateId = setCurrentTemplateId;
exports.showBlockTypes = void 0;
var _coreData = require("@wordpress/core-data");
var _i18n = require("@wordpress/i18n");
var _notices = require("@wordpress/notices");
var _blockEditor = require("@wordpress/block-editor");
var _preferences = require("@wordpress/preferences");
var _url = require("@wordpress/url");
var _apiFetch = _interopRequireDefault(require("@wordpress/api-fetch"));
var _blocks = require("@wordpress/blocks");
var _htmlEntities = require("@wordpress/html-entities");
var _isTemplateRevertable = _interopRequireDefault(require("./utils/is-template-revertable"));
var _privateActions = require("../dataviews/store/private-actions");
Object.keys(_privateActions).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  if (key in exports && exports[key] === _privateActions[key]) return;
  Object.defineProperty(exports, key, {
    enumerable: true,
    get: function () {
      return _privateActions[key];
    }
  });
});
/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

/**
 * Returns an action object used to set which template is currently being used/edited.
 *
 * @param {string} id Template Id.
 *
 * @return {Object} Action object.
 */
function setCurrentTemplateId(id) {
  return {
    type: 'SET_CURRENT_TEMPLATE_ID',
    id
  };
}

/**
 * Create a block based template.
 *
 * @param {Object?} template Template to create and assign.
 */
const createTemplate = template => async ({
  select,
  dispatch,
  registry
}) => {
  const savedTemplate = await registry.dispatch(_coreData.store).saveEntityRecord('postType', 'wp_template', template);
  registry.dispatch(_coreData.store).editEntityRecord('postType', select.getCurrentPostType(), select.getCurrentPostId(), {
    template: savedTemplate.slug
  });
  registry.dispatch(_notices.store).createSuccessNotice((0, _i18n.__)("Custom template created. You're in template mode now."), {
    type: 'snackbar',
    actions: [{
      label: (0, _i18n.__)('Go back'),
      onClick: () => dispatch.setRenderingMode(select.getEditorSettings().defaultRenderingMode)
    }]
  });
  return savedTemplate;
};

/**
 * Update the provided block types to be visible.
 *
 * @param {string[]} blockNames Names of block types to show.
 */
exports.createTemplate = createTemplate;
const showBlockTypes = blockNames => ({
  registry
}) => {
  var _registry$select$get;
  const existingBlockNames = (_registry$select$get = registry.select(_preferences.store).get('core', 'hiddenBlockTypes')) !== null && _registry$select$get !== void 0 ? _registry$select$get : [];
  const newBlockNames = existingBlockNames.filter(type => !(Array.isArray(blockNames) ? blockNames : [blockNames]).includes(type));
  registry.dispatch(_preferences.store).set('core', 'hiddenBlockTypes', newBlockNames);
};

/**
 * Update the provided block types to be hidden.
 *
 * @param {string[]} blockNames Names of block types to hide.
 */
exports.showBlockTypes = showBlockTypes;
const hideBlockTypes = blockNames => ({
  registry
}) => {
  var _registry$select$get2;
  const existingBlockNames = (_registry$select$get2 = registry.select(_preferences.store).get('core', 'hiddenBlockTypes')) !== null && _registry$select$get2 !== void 0 ? _registry$select$get2 : [];
  const mergedBlockNames = new Set([...existingBlockNames, ...(Array.isArray(blockNames) ? blockNames : [blockNames])]);
  registry.dispatch(_preferences.store).set('core', 'hiddenBlockTypes', [...mergedBlockNames]);
};

/**
 * Save entity records marked as dirty.
 *
 * @param {Object}   options                      Options for the action.
 * @param {Function} [options.onSave]             Callback when saving happens.
 * @param {object[]} [options.dirtyEntityRecords] Array of dirty entities.
 * @param {object[]} [options.entitiesToSkip]     Array of entities to skip saving.
 * @param {Function} [options.close]              Callback when the actions is called. It should be consolidated with `onSave`.
 */
exports.hideBlockTypes = hideBlockTypes;
const saveDirtyEntities = ({
  onSave,
  dirtyEntityRecords = [],
  entitiesToSkip = [],
  close
} = {}) => ({
  registry
}) => {
  const PUBLISH_ON_SAVE_ENTITIES = [{
    kind: 'postType',
    name: 'wp_navigation'
  }];
  const saveNoticeId = 'site-editor-save-success';
  const homeUrl = registry.select(_coreData.store).getEntityRecord('root', '__unstableBase')?.home;
  registry.dispatch(_notices.store).removeNotice(saveNoticeId);
  const entitiesToSave = dirtyEntityRecords.filter(({
    kind,
    name,
    key,
    property
  }) => {
    return !entitiesToSkip.some(elt => elt.kind === kind && elt.name === name && elt.key === key && elt.property === property);
  });
  close?.(entitiesToSave);
  const siteItemsToSave = [];
  const pendingSavedRecords = [];
  entitiesToSave.forEach(({
    kind,
    name,
    key,
    property
  }) => {
    if ('root' === kind && 'site' === name) {
      siteItemsToSave.push(property);
    } else {
      if (PUBLISH_ON_SAVE_ENTITIES.some(typeToPublish => typeToPublish.kind === kind && typeToPublish.name === name)) {
        registry.dispatch(_coreData.store).editEntityRecord(kind, name, key, {
          status: 'publish'
        });
      }
      pendingSavedRecords.push(registry.dispatch(_coreData.store).saveEditedEntityRecord(kind, name, key));
    }
  });
  if (siteItemsToSave.length) {
    pendingSavedRecords.push(registry.dispatch(_coreData.store).__experimentalSaveSpecifiedEntityEdits('root', 'site', undefined, siteItemsToSave));
  }
  registry.dispatch(_blockEditor.store).__unstableMarkLastChangeAsPersistent();
  Promise.all(pendingSavedRecords).then(values => {
    return onSave ? onSave(values) : values;
  }).then(values => {
    if (values.some(value => typeof value === 'undefined')) {
      registry.dispatch(_notices.store).createErrorNotice((0, _i18n.__)('Saving failed.'));
    } else {
      registry.dispatch(_notices.store).createSuccessNotice((0, _i18n.__)('Site updated.'), {
        type: 'snackbar',
        id: saveNoticeId,
        actions: [{
          label: (0, _i18n.__)('View site'),
          url: homeUrl
        }]
      });
    }
  }).catch(error => registry.dispatch(_notices.store).createErrorNotice(`${(0, _i18n.__)('Saving failed.')} ${error}`));
};

/**
 * Reverts a template to its original theme-provided file.
 *
 * @param {Object}  template            The template to revert.
 * @param {Object}  [options]
 * @param {boolean} [options.allowUndo] Whether to allow the user to undo
 *                                      reverting the template. Default true.
 */
exports.saveDirtyEntities = saveDirtyEntities;
const revertTemplate = (template, {
  allowUndo = true
} = {}) => async ({
  registry
}) => {
  const noticeId = 'edit-site-template-reverted';
  registry.dispatch(_notices.store).removeNotice(noticeId);
  if (!(0, _isTemplateRevertable.default)(template)) {
    registry.dispatch(_notices.store).createErrorNotice((0, _i18n.__)('This template is not revertable.'), {
      type: 'snackbar'
    });
    return;
  }
  try {
    const templateEntityConfig = registry.select(_coreData.store).getEntityConfig('postType', template.type);
    if (!templateEntityConfig) {
      registry.dispatch(_notices.store).createErrorNotice((0, _i18n.__)('The editor has encountered an unexpected error. Please reload.'), {
        type: 'snackbar'
      });
      return;
    }
    const fileTemplatePath = (0, _url.addQueryArgs)(`${templateEntityConfig.baseURL}/${template.id}`, {
      context: 'edit',
      source: template.origin
    });
    const fileTemplate = await (0, _apiFetch.default)({
      path: fileTemplatePath
    });
    if (!fileTemplate) {
      registry.dispatch(_notices.store).createErrorNotice((0, _i18n.__)('The editor has encountered an unexpected error. Please reload.'), {
        type: 'snackbar'
      });
      return;
    }
    const serializeBlocks = ({
      blocks: blocksForSerialization = []
    }) => (0, _blocks.__unstableSerializeAndClean)(blocksForSerialization);
    const edited = registry.select(_coreData.store).getEditedEntityRecord('postType', template.type, template.id);

    // We are fixing up the undo level here to make sure we can undo
    // the revert in the header toolbar correctly.
    registry.dispatch(_coreData.store).editEntityRecord('postType', template.type, template.id, {
      content: serializeBlocks,
      // Required to make the `undo` behave correctly.
      blocks: edited.blocks,
      // Required to revert the blocks in the editor.
      source: 'custom' // required to avoid turning the editor into a dirty state
    }, {
      undoIgnore: true // Required to merge this edit with the last undo level.
    });
    const blocks = (0, _blocks.parse)(fileTemplate?.content?.raw);
    registry.dispatch(_coreData.store).editEntityRecord('postType', template.type, fileTemplate.id, {
      content: serializeBlocks,
      blocks,
      source: 'theme'
    });
    if (allowUndo) {
      const undoRevert = () => {
        registry.dispatch(_coreData.store).editEntityRecord('postType', template.type, edited.id, {
          content: serializeBlocks,
          blocks: edited.blocks,
          source: 'custom'
        });
      };
      registry.dispatch(_notices.store).createSuccessNotice((0, _i18n.__)('Template reset.'), {
        type: 'snackbar',
        id: noticeId,
        actions: [{
          label: (0, _i18n.__)('Undo'),
          onClick: undoRevert
        }]
      });
    }
  } catch (error) {
    const errorMessage = error.message && error.code !== 'unknown_error' ? error.message : (0, _i18n.__)('Template revert failed. Please reload.');
    registry.dispatch(_notices.store).createErrorNotice(errorMessage, {
      type: 'snackbar'
    });
  }
};

/**
 * Action that removes an array of templates, template parts or patterns.
 *
 * @param {Array} items An array of template,template part or pattern objects to remove.
 */
exports.revertTemplate = revertTemplate;
const removeTemplates = items => async ({
  registry
}) => {
  const isResetting = items.every(item => item?.has_theme_file);
  const promiseResult = await Promise.allSettled(items.map(item => {
    return registry.dispatch(_coreData.store).deleteEntityRecord('postType', item.type, item.id, {
      force: true
    }, {
      throwOnError: true
    });
  }));

  // If all the promises were fulfilled with sucess.
  if (promiseResult.every(({
    status
  }) => status === 'fulfilled')) {
    let successMessage;
    if (items.length === 1) {
      // Depending on how the entity was retrieved its title might be
      // an object or simple string.
      let title;
      if (typeof items[0].title === 'string') {
        title = items[0].title;
      } else if (typeof items[0].title?.rendered === 'string') {
        title = items[0].title?.rendered;
      } else if (typeof items[0].title?.raw === 'string') {
        title = items[0].title?.raw;
      }
      successMessage = isResetting ? (0, _i18n.sprintf)( /* translators: The template/part's name. */
      (0, _i18n.__)('"%s" reset.'), (0, _htmlEntities.decodeEntities)(title)) : (0, _i18n.sprintf)( /* translators: The template/part's name. */
      (0, _i18n.__)('"%s" deleted.'), (0, _htmlEntities.decodeEntities)(title));
    } else {
      successMessage = isResetting ? (0, _i18n.__)('Items reset.') : (0, _i18n.__)('Items deleted.');
    }
    registry.dispatch(_notices.store).createSuccessNotice(successMessage, {
      type: 'snackbar',
      id: 'editor-template-deleted-success'
    });
  } else {
    // If there was at lease one failure.
    let errorMessage;
    // If we were trying to delete a single template.
    if (promiseResult.length === 1) {
      if (promiseResult[0].reason?.message) {
        errorMessage = promiseResult[0].reason.message;
      } else {
        errorMessage = isResetting ? (0, _i18n.__)('An error occurred while reverting the item.') : (0, _i18n.__)('An error occurred while deleting the item.');
      }
      // If we were trying to delete a multiple templates
    } else {
      const errorMessages = new Set();
      const failedPromises = promiseResult.filter(({
        status
      }) => status === 'rejected');
      for (const failedPromise of failedPromises) {
        if (failedPromise.reason?.message) {
          errorMessages.add(failedPromise.reason.message);
        }
      }
      if (errorMessages.size === 0) {
        errorMessage = (0, _i18n.__)('An error occurred while deleting the items.');
      } else if (errorMessages.size === 1) {
        errorMessage = isResetting ? (0, _i18n.sprintf)( /* translators: %s: an error message */
        (0, _i18n.__)('An error occurred while reverting the items: %s'), [...errorMessages][0]) : (0, _i18n.sprintf)( /* translators: %s: an error message */
        (0, _i18n.__)('An error occurred while deleting the items: %s'), [...errorMessages][0]);
      } else {
        errorMessage = isResetting ? (0, _i18n.sprintf)( /* translators: %s: a list of comma separated error messages */
        (0, _i18n.__)('Some errors occurred while reverting the items: %s'), [...errorMessages].join(',')) : (0, _i18n.sprintf)( /* translators: %s: a list of comma separated error messages */
        (0, _i18n.__)('Some errors occurred while deleting the items: %s'), [...errorMessages].join(','));
      }
    }
    registry.dispatch(_notices.store).createErrorNotice(errorMessage, {
      type: 'snackbar'
    });
  }
};
exports.removeTemplates = removeTemplates;
//# sourceMappingURL=private-actions.js.map