"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.Inserter = void 0;
var _reactNative = require("react-native");
var _i18n = require("@wordpress/i18n");
var _components = require("@wordpress/components");
var _element = require("@wordpress/element");
var _data = require("@wordpress/data");
var _compose = require("@wordpress/compose");
var _blocks = require("@wordpress/blocks");
var _icons = require("@wordpress/icons");
var _reactNativeBridge = require("@wordpress/react-native-bridge");
var _style = _interopRequireDefault(require("./style.scss"));
var _menu = _interopRequireDefault(require("./menu"));
var _insertionPoint = _interopRequireDefault(require("../block-list/insertion-point"));
var _store = require("../../store");
var _jsxRuntime = require("react/jsx-runtime");
/**
 * External dependencies
 */

/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

const VOICE_OVER_ANNOUNCEMENT_DELAY = 1000;
const defaultRenderToggle = ({
  onToggle,
  disabled,
  iconStyle,
  buttonStyle,
  onLongPress
}) => {
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.ToolbarButton, {
    title: (0, _i18n._x)('Add block', 'Generic label for block inserter button'),
    icon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_icons.Icon, {
      icon: _icons.plus,
      style: iconStyle
    }),
    onClick: onToggle,
    extraProps: {
      hint: (0, _i18n.__)('Double tap to add a block'),
      // testID is present to disambiguate this element for native UI tests. It's not
      // usually required for components. See: https://github.com/WordPress/gutenberg/pull/18832#issuecomment-561411389.
      testID: 'add-block-button',
      onLongPress,
      hitSlop: {
        top: 10,
        bottom: 10,
        left: 10,
        right: 10
      }
    },
    isDisabled: disabled,
    customContainerStyles: buttonStyle,
    fixedRatio: false
  });
};
class Inserter extends _element.Component {
  announcementTimeout;
  constructor() {
    super(...arguments);
    this.onToggle = this.onToggle.bind(this);
    this.renderInserterToggle = this.renderInserterToggle.bind(this);
    this.renderContent = this.renderContent.bind(this);
  }
  componentWillUnmount() {
    clearTimeout(this.announcementTimeout);
  }
  getInsertionOptions() {
    const addBeforeOption = {
      value: 'before',
      label: (0, _i18n.__)('Add Block Before'),
      icon: _icons.plusCircle
    };
    const replaceCurrentOption = {
      value: 'replace',
      label: (0, _i18n.__)('Replace Current Block'),
      icon: _icons.plusCircleFilled
    };
    const addAfterOption = {
      value: 'after',
      label: (0, _i18n.__)('Add Block After'),
      icon: _icons.plusCircle
    };
    const addToBeginningOption = {
      value: 'start',
      label: (0, _i18n.__)('Add To Beginning'),
      icon: _icons.insertBefore
    };
    const addToEndOption = {
      value: 'end',
      label: (0, _i18n.__)('Add To End'),
      icon: _icons.insertAfter
    };
    const {
      isAnyBlockSelected,
      isSelectedBlockReplaceable
    } = this.props;
    if (isAnyBlockSelected) {
      if (isSelectedBlockReplaceable) {
        return [addToBeginningOption, addBeforeOption, replaceCurrentOption, addAfterOption, addToEndOption];
      }
      return [addToBeginningOption, addBeforeOption, addAfterOption, addToEndOption];
    }
    return [addToBeginningOption, addToEndOption];
  }
  getInsertionIndex(insertionType) {
    const {
      insertionIndexDefault,
      insertionIndexStart,
      insertionIndexBefore,
      insertionIndexAfter,
      insertionIndexEnd
    } = this.props;
    if (insertionType === 'start') {
      return insertionIndexStart;
    }
    if (insertionType === 'before' || insertionType === 'replace') {
      return insertionIndexBefore;
    }
    if (insertionType === 'after') {
      return insertionIndexAfter;
    }
    if (insertionType === 'end') {
      return insertionIndexEnd;
    }
    return insertionIndexDefault;
  }
  shouldReplaceBlock(insertionType) {
    const {
      isSelectedBlockReplaceable
    } = this.props;
    if (insertionType === 'replace') {
      return true;
    }
    if (insertionType === 'default' && isSelectedBlockReplaceable) {
      return true;
    }
    return false;
  }
  onToggle(isOpen) {
    const {
      blockTypeImpressions,
      onToggle,
      updateSettings
    } = this.props;
    if (!isOpen) {
      const impressionsRemain = Object.values(blockTypeImpressions).some(count => count > 0);
      if (impressionsRemain) {
        const decrementedImpressions = Object.entries(blockTypeImpressions).reduce((acc, [blockName, count]) => ({
          ...acc,
          [blockName]: Math.max(count - 1, 0)
        }), {});

        // Persist block type impression to JavaScript store.
        updateSettings({
          impressions: decrementedImpressions
        });

        // Persist block type impression count to native app store.
        (0, _reactNativeBridge.setBlockTypeImpressions)(decrementedImpressions);
      }
    }

    // Surface toggle callback to parent component.
    if (onToggle) {
      onToggle(isOpen);
    }
    this.onInserterToggledAnnouncement(isOpen);
  }
  onInserterToggledAnnouncement(isOpen) {
    _reactNative.AccessibilityInfo.isScreenReaderEnabled().then(isEnabled => {
      if (isEnabled) {
        const isIOS = _reactNative.Platform.OS === 'ios';
        const announcement = isOpen ? (0, _i18n.__)('Scrollable block menu opened. Select a block.') : (0, _i18n.__)('Scrollable block menu closed.');
        this.announcementTimeout = setTimeout(() => _reactNative.AccessibilityInfo.announceForAccessibility(announcement), isIOS ? VOICE_OVER_ANNOUNCEMENT_DELAY : 0);
      }
    });
  }

  /**
   * Render callback to display Dropdown toggle element.
   *
   * @param {Object}   options
   * @param {Function} options.onToggle Callback to invoke when toggle is
   *                                    pressed.
   * @param {boolean}  options.isOpen   Whether dropdown is currently open.
   *
   * @return {Element} Dropdown toggle element.
   */
  renderInserterToggle({
    onToggle,
    isOpen
  }) {
    const {
      disabled,
      renderToggle = defaultRenderToggle,
      getStylesFromColorScheme,
      showSeparator
    } = this.props;
    if (showSeparator && isOpen) {
      return /*#__PURE__*/(0, _jsxRuntime.jsx)(_insertionPoint.default, {});
    }
    const buttonStyle = getStylesFromColorScheme(_style.default['inserter-menu__add-block-button'], _style.default['inserter-menu__add-block-button--dark']);
    const iconStyle = getStylesFromColorScheme(_style.default['inserter-menu__add-block-button-icon'], _style.default['inserter-menu__add-block-button-icon--dark']);
    const onPress = () => {
      this.setState({
        destinationRootClientId: this.props.destinationRootClientId,
        shouldReplaceBlock: this.shouldReplaceBlock('default'),
        insertionIndex: this.getInsertionIndex('default')
      }, onToggle);
    };
    const onLongPress = () => {
      if (this.picker) {
        this.picker.presentPicker();
      }
    };
    const onPickerSelect = insertionType => {
      this.setState({
        destinationRootClientId: this.props.destinationRootClientId,
        shouldReplaceBlock: this.shouldReplaceBlock(insertionType),
        insertionIndex: this.getInsertionIndex(insertionType)
      }, onToggle);
    };
    return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
      children: [renderToggle({
        onToggle: onPress,
        isOpen,
        disabled,
        iconStyle,
        buttonStyle,
        onLongPress
      }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Picker, {
        ref: instance => this.picker = instance,
        options: this.getInsertionOptions(),
        onChange: onPickerSelect,
        hideCancelButton: true
      })]
    });
  }

  /**
   * Render callback to display Dropdown content element.
   *
   * @param {Object}   options
   * @param {Function} options.onClose Callback to invoke when dropdown is
   *                                   closed.
   * @param {boolean}  options.isOpen  Whether dropdown is currently open.
   *
   * @return {Element} Dropdown content element.
   */
  renderContent({
    onClose,
    isOpen
  }) {
    const {
      clientId,
      isAppender
    } = this.props;
    const {
      destinationRootClientId,
      shouldReplaceBlock,
      insertionIndex
    } = this.state;
    return /*#__PURE__*/(0, _jsxRuntime.jsx)(_menu.default, {
      isOpen: isOpen,
      onSelect: onClose,
      onDismiss: onClose,
      rootClientId: destinationRootClientId,
      clientId: clientId,
      isAppender: isAppender,
      shouldReplaceBlock: shouldReplaceBlock,
      insertionIndex: insertionIndex
    });
  }
  render() {
    return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Dropdown, {
      onToggle: this.onToggle,
      headerTitle: (0, _i18n.__)('Add a block'),
      renderToggle: this.renderInserterToggle,
      renderContent: this.renderContent
    });
  }
}
exports.Inserter = Inserter;
var _default = exports.default = (0, _compose.compose)([(0, _data.withDispatch)(dispatch => {
  const {
    updateSettings
  } = dispatch(_store.store);
  return {
    updateSettings
  };
}), (0, _data.withSelect)((select, {
  clientId,
  isAppender,
  rootClientId
}) => {
  const {
    getBlockRootClientId,
    getBlockSelectionEnd,
    getBlockOrder,
    getBlockIndex,
    getBlock,
    getSettings: getBlockEditorSettings
  } = select(_store.store);
  const end = getBlockSelectionEnd();
  // `end` argument (id) can refer to the component which is removed
  // due to pressing `undo` button, that's why we need to check
  // if `getBlock( end) is valid, otherwise `null` is passed
  const isAnyBlockSelected = !isAppender && end && getBlock(end);
  const destinationRootClientId = isAnyBlockSelected ? getBlockRootClientId(end) : rootClientId;
  const selectedBlockIndex = getBlockIndex(end);
  const endOfRootIndex = getBlockOrder(rootClientId).length;
  const isSelectedUnmodifiedDefaultBlock = isAnyBlockSelected ? (0, _blocks.isUnmodifiedDefaultBlock)(getBlock(end)) : undefined;
  function getDefaultInsertionIndex() {
    const {
      __experimentalShouldInsertAtTheTop: shouldInsertAtTheTop
    } = getBlockEditorSettings();

    // If post title is selected insert as first block.
    if (shouldInsertAtTheTop) {
      return 0;
    }

    // If the clientId is defined, we insert at the position of the block.
    if (clientId) {
      return getBlockIndex(clientId);
    }

    // If there is a selected block,
    if (isAnyBlockSelected) {
      // And the last selected block is unmodified (empty), it will be replaced.
      if (isSelectedUnmodifiedDefaultBlock) {
        return selectedBlockIndex;
      }

      // We insert after the selected block.
      return selectedBlockIndex + 1;
    }

    // Otherwise, we insert at the end of the current rootClientId.
    return endOfRootIndex;
  }
  const insertionIndexStart = 0;
  const insertionIndexBefore = isAnyBlockSelected ? selectedBlockIndex : insertionIndexStart;
  const insertionIndexAfter = isAnyBlockSelected ? selectedBlockIndex + 1 : endOfRootIndex;
  const insertionIndexEnd = endOfRootIndex;
  return {
    blockTypeImpressions: getBlockEditorSettings().impressions,
    destinationRootClientId,
    insertionIndexDefault: getDefaultInsertionIndex(),
    insertionIndexBefore,
    insertionIndexAfter,
    insertionIndexStart,
    insertionIndexEnd,
    isAnyBlockSelected: !!isAnyBlockSelected,
    isSelectedBlockReplaceable: isSelectedUnmodifiedDefaultBlock
  };
}), _compose.withPreferredColorScheme])(Inserter);
//# sourceMappingURL=index.native.js.map