@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
423 lines (413 loc) • 14.7 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.ExperimentalEditorProvider = void 0;
var _reactNative = require("react-native");
var _memize = _interopRequireDefault(require("memize"));
var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
var _reactNativeBridge = _interopRequireWildcard(require("@wordpress/react-native-bridge"));
var _element = require("@wordpress/element");
var _wordcount = require("@wordpress/wordcount");
var _blocks = require("@wordpress/blocks");
var _data = require("@wordpress/data");
var _compose = require("@wordpress/compose");
var _hooks = require("@wordpress/hooks");
var _blockEditor = require("@wordpress/block-editor");
var _blockLibrary = require("@wordpress/block-library");
var _i18n = require("@wordpress/i18n");
var _editor = require("@wordpress/editor");
var _notices = require("@wordpress/notices");
var _coreData = require("@wordpress/core-data");
var _index = _interopRequireDefault(require("./index.js"));
var _postTitle = require("../post-title");
var _jsxRuntime = require("react/jsx-runtime");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
* External dependencies
*/
/**
* WordPress dependencies
*/
const postTypeEntities = [{
name: 'post',
baseURL: '/wp/v2/posts'
}, {
name: 'page',
baseURL: '/wp/v2/pages'
}, {
name: 'attachment',
baseURL: '/wp/v2/media'
}, {
name: 'wp_block',
baseURL: '/wp/v2/blocks'
}].map(postTypeEntity => ({
kind: 'postType',
...postTypeEntity,
transientEdits: {
blocks: true,
selection: true
},
mergedEdits: {
meta: true
},
rawAttributes: ['title', 'excerpt', 'content']
}));
/**
* Internal dependencies
*/
class NativeEditorProvider extends _element.Component {
constructor() {
super(...arguments);
// Keep a local reference to `post` to detect changes.
this.post = this.props.post;
this.props.addEntities(postTypeEntities);
this.props.receiveEntityRecords('postType', this.post.type, this.post);
this.onHardwareBackPress = this.onHardwareBackPress.bind(this);
this.onContentUpdate = this.onContentUpdate.bind(this);
this.getEditorSettings = (0, _memize.default)((settings, capabilities) => ({
...settings,
capabilities
}), {
maxSize: 1
});
this.state = {
isHelpVisible: false
};
}
componentDidMount() {
const {
capabilities,
createErrorNotice,
locale,
hostAppNamespace,
updateEditorSettings,
updateBlockEditorSettings
} = this.props;
updateEditorSettings({
capabilities,
...this.getThemeColors(this.props),
locale,
hostAppNamespace
});
this.subscriptionParentGetHtml = (0, _reactNativeBridge.subscribeParentGetHtml)(() => {
this.serializeToNativeAction();
});
this.subscriptionParentToggleHTMLMode = (0, _reactNativeBridge.subscribeParentToggleHTMLMode)(() => {
this.toggleMode();
});
this.subscriptionParentSetTitle = (0, _reactNativeBridge.subscribeSetTitle)(payload => {
this.props.editTitle(payload.title);
});
this.subscriptionParentUpdateHtml = (0, _reactNativeBridge.subscribeUpdateHtml)(payload => {
this.updateHtmlAction(payload.html);
});
this.subscriptionParentReplaceBlock = (0, _reactNativeBridge.subscribeReplaceBlock)(payload => {
this.replaceBlockAction(payload.html, payload.clientId);
});
this.subscriptionParentMediaAppend = (0, _reactNativeBridge.subscribeMediaAppend)(payload => {
const blockName = 'core/' + payload.mediaType;
const blockType = (0, _blocks.getBlockType)(blockName);
if (blockType && blockType?.name) {
const newBlock = (0, _blocks.createBlock)(blockType.name, {
id: payload.mediaId,
[payload.mediaType === 'image' ? 'url' : 'src']: payload.mediaUrl
});
const indexAfterSelected = this.props.selectedBlockIndex + 1;
const insertionIndex = indexAfterSelected || this.props.blockCount;
this.props.insertBlock(newBlock, insertionIndex);
} else {
createErrorNotice((0, _i18n.__)('File type not supported as a media file.'));
}
});
this.subscriptionParentUpdateEditorSettings = (0, _reactNativeBridge.subscribeUpdateEditorSettings)(({
...editorSettings
}) => {
updateEditorSettings(this.getThemeColors(editorSettings));
});
this.subscriptionParentUpdateCapabilities = (0, _reactNativeBridge.subscribeUpdateCapabilities)(payload => {
this.updateCapabilitiesAction(payload);
});
this.subscriptionParentShowNotice = (0, _reactNativeBridge.subscribeShowNotice)(payload => {
this.props.createSuccessNotice(payload.message);
});
this.subscriptionParentShowEditorHelp = (0, _reactNativeBridge.subscribeShowEditorHelp)(() => {
this.setState({
isHelpVisible: true
});
});
this.hardwareBackPressListener = _reactNative.BackHandler.addEventListener('hardwareBackPress', this.onHardwareBackPress);
this.subscriptionOnContentUpdate = (0, _reactNativeBridge.subscribeToContentUpdate)(data => {
this.onContentUpdate(data);
});
// Request current block impressions from native app.
(0, _reactNativeBridge.requestBlockTypeImpressions)(storedImpressions => {
const impressions = {
..._blockLibrary.NEW_BLOCK_TYPES,
...storedImpressions
};
// Persist impressions to JavaScript store.
updateBlockEditorSettings({
impressions
});
// Persist impressions to native store if they do not include latest
// `NEW_BLOCK_TYPES` configuration.
const storedImpressionKeys = Object.keys(storedImpressions);
const storedImpressionsCurrent = Object.keys(_blockLibrary.NEW_BLOCK_TYPES).every(newKey => storedImpressionKeys.includes(newKey));
if (!storedImpressionsCurrent) {
(0, _reactNativeBridge.setBlockTypeImpressions)(impressions);
}
});
}
componentWillUnmount() {
if (this.subscriptionParentGetHtml) {
this.subscriptionParentGetHtml.remove();
}
if (this.subscriptionParentToggleHTMLMode) {
this.subscriptionParentToggleHTMLMode.remove();
}
if (this.subscriptionParentSetTitle) {
this.subscriptionParentSetTitle.remove();
}
if (this.subscriptionParentUpdateHtml) {
this.subscriptionParentUpdateHtml.remove();
}
if (this.subscriptionParentReplaceBlock) {
this.subscriptionParentReplaceBlock.remove();
}
if (this.subscriptionParentMediaAppend) {
this.subscriptionParentMediaAppend.remove();
}
if (this.subscriptionParentUpdateEditorSettings) {
this.subscriptionParentUpdateEditorSettings.remove();
}
if (this.subscriptionParentUpdateCapabilities) {
this.subscriptionParentUpdateCapabilities.remove();
}
if (this.subscriptionParentShowNotice) {
this.subscriptionParentShowNotice.remove();
}
if (this.subscriptionParentShowEditorHelp) {
this.subscriptionParentShowEditorHelp.remove();
}
if (this.hardwareBackPressListener) {
this.hardwareBackPressListener.remove();
}
if (this.subscriptionOnContentUpdate) {
this.subscriptionOnContentUpdate.remove();
}
}
getThemeColors({
rawStyles,
rawFeatures
}) {
const {
defaultEditorColors,
defaultEditorGradients
} = this.props;
if (rawStyles && rawFeatures) {
return (0, _blockEditor.getGlobalStyles)(rawStyles, rawFeatures);
}
return (0, _blockEditor.getColorsAndGradients)(defaultEditorColors, defaultEditorGradients, rawFeatures);
}
componentDidUpdate(prevProps) {
if (!prevProps.isReady && this.props.isReady) {
const blocks = this.props.blocks;
const isUnsupportedBlock = ({
name
}) => name === (0, _blocks.getUnregisteredTypeHandlerName)();
const unsupportedBlockNames = blocks.filter(isUnsupportedBlock).map(block => block.attributes.originalName);
_reactNativeBridge.default.editorDidMount(unsupportedBlockNames);
}
}
onHardwareBackPress() {
const {
clearSelectedBlock,
selectedBlockIndex
} = this.props;
if (selectedBlockIndex !== -1) {
clearSelectedBlock();
return true;
}
return false;
}
onContentUpdate({
content: rawContent
}) {
const {
editTitle,
onClearPostTitleSelection,
onInsertBlockAfter: onInsertBlocks,
title
} = this.props;
const content = (0, _blocks.pasteHandler)({
plainText: rawContent
});
(0, _postTitle.insertContentWithTitle)(title, content, editTitle, onInsertBlocks);
onClearPostTitleSelection();
}
serializeToNativeAction() {
const title = this.props.title;
let html;
if (this.props.mode === 'text') {
// The HTMLTextInput component does not update the store when user is doing changes
// Let's request the HTML from the component's state directly.
html = (0, _hooks.applyFilters)('native.persist-html');
} else {
html = (0, _blocks.serialize)(this.props.blocks);
}
const hasChanges = title !== this.post.title.raw || html !== this.post.content.raw;
// Variable to store the content structure metrics.
const contentInfo = {};
contentInfo.characterCount = (0, _wordcount.count)(html, 'characters_including_spaces');
contentInfo.wordCount = (0, _wordcount.count)(html, 'words');
contentInfo.paragraphCount = this.props.paragraphCount;
contentInfo.blockCount = this.props.blockCount;
_reactNativeBridge.default.provideToNative_Html(html, title, hasChanges, contentInfo);
if (hasChanges) {
this.post.title.raw = title;
this.post.content.raw = html;
}
}
updateHtmlAction(html) {
const parsed = (0, _blocks.parse)(html);
this.props.resetEditorBlocksWithoutUndoLevel(parsed);
}
replaceBlockAction(html, blockClientId) {
const parsed = (0, _blocks.parse)(html);
this.props.replaceBlock(blockClientId, parsed);
}
toggleMode() {
const {
mode,
switchMode
} = this.props;
// Refresh html content first.
this.serializeToNativeAction();
switchMode(mode === 'visual' ? 'text' : 'visual');
}
updateCapabilitiesAction(capabilities) {
this.props.updateEditorSettings({
capabilities
});
}
render() {
const {
children,
post,
capabilities,
settings,
...props
} = this.props;
const editorSettings = this.getEditorSettings(settings, capabilities);
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_index.default, {
post: this.post,
settings: editorSettings,
...props,
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSafeAreaContext.SafeAreaProvider, {
children: children
})
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_editor.EditorHelpTopics, {
isVisible: this.state.isHelpVisible,
onClose: () => this.setState({
isHelpVisible: false
}),
close: () => this.setState({
isHelpVisible: false
}),
showSupport: capabilities?.supportSection === true
})]
});
}
}
const ComposedNativeProvider = exports.ExperimentalEditorProvider = (0, _compose.compose)([(0, _data.withSelect)(select => {
var _settings$colors, _settings$gradients;
const {
__unstableIsEditorReady: isEditorReady,
getEditorBlocks,
getEditedPostAttribute,
getEditedPostContent,
getEditorSettings,
getEditorMode
} = select(_editor.store);
const {
getBlockIndex,
getSelectedBlockClientId,
getGlobalBlockCount
} = select(_blockEditor.store);
const settings = getEditorSettings();
const defaultEditorColors = (_settings$colors = settings?.colors) !== null && _settings$colors !== void 0 ? _settings$colors : [];
const defaultEditorGradients = (_settings$gradients = settings?.gradients) !== null && _settings$gradients !== void 0 ? _settings$gradients : [];
const selectedBlockClientId = getSelectedBlockClientId();
return {
mode: getEditorMode(),
isReady: isEditorReady(),
blocks: getEditorBlocks(),
title: getEditedPostAttribute('title'),
getEditedPostContent,
defaultEditorColors,
defaultEditorGradients,
selectedBlockIndex: getBlockIndex(selectedBlockClientId),
blockCount: getGlobalBlockCount(),
paragraphCount: getGlobalBlockCount('core/paragraph')
};
}), (0, _data.withDispatch)(dispatch => {
const {
editPost,
resetEditorBlocks,
updateEditorSettings,
switchEditorMode,
togglePostTitleSelection
} = dispatch(_editor.store);
const {
clearSelectedBlock,
updateSettings,
insertBlock,
insertBlocks,
replaceBlock
} = dispatch(_blockEditor.store);
const {
addEntities,
receiveEntityRecords
} = dispatch(_coreData.store);
const {
createSuccessNotice,
createErrorNotice
} = dispatch(_notices.store);
return {
updateBlockEditorSettings: updateSettings,
updateEditorSettings,
addEntities,
insertBlock,
insertBlocks,
createSuccessNotice,
createErrorNotice,
clearSelectedBlock,
editTitle(title) {
editPost({
title
});
},
receiveEntityRecords,
resetEditorBlocksWithoutUndoLevel(blocks) {
resetEditorBlocks(blocks, {
__unstableShouldCreateUndoLevel: false
});
},
switchMode(mode) {
switchEditorMode(mode);
},
onInsertBlockAfter(blocks) {
insertBlocks(blocks, undefined, undefined, false);
},
onClearPostTitleSelection() {
togglePostTitleSelection(false);
},
replaceBlock
};
})])(NativeEditorProvider);
var _default = exports.default = ComposedNativeProvider;
//# sourceMappingURL=index.native.js.map