Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Popup with options to remove metadata within the block #331

Merged
merged 18 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 4 additions & 38 deletions assets/editor/extensions/block-id.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,50 +8,17 @@ import shorthash from 'shorthash';
*/
import { addFilter } from '@wordpress/hooks';
import { useEffect, useRef, useCallback } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { getBlockType } from '@wordpress/blocks';
import { createHigherOrderComponent, useThrottle } from '@wordpress/compose';

import useAllBlocks from '../../hooks/use-all-blocks';

function useBlockID(props) {
const { setAttributes, attributes, clientId, name } = props;
const blockSettings = getBlockType(name);
const didMountRef = useRef(false);

const { getBlocks } = useSelect((select) => {
return select('core/block-editor');
});

/**
* Get recursive all blocks of the current page
*
* @param {boolean} blocks - block list
*
* @return {Array} block list
*/
const getAllBlocks = useCallback(
(blocks = false) => {
let result = [];

if (!blocks) {
blocks = getBlocks();
}

if (!blocks) {
return result;
}

blocks.forEach((data) => {
result.push(data);

if (data.innerBlocks && data.innerBlocks.length) {
result = [...result, ...getAllBlocks(data.innerBlocks)];
}
});

return result;
},
[getBlocks]
);
const allBlocks = useAllBlocks();

const onUpdate = useCallback(
(checkDuplicates) => {
Expand All @@ -62,7 +29,6 @@ function useBlockID(props) {

// prevent unique ID duplication after block duplicated.
if (checkDuplicates) {
const allBlocks = getAllBlocks();
allBlocks.forEach((data) => {
if (
data.clientId &&
Expand Down Expand Up @@ -112,7 +78,7 @@ function useBlockID(props) {
}
}
},
[attributes, clientId, getAllBlocks, name, setAttributes]
[attributes, clientId, allBlocks, name, setAttributes]
);

const onUpdateThrottle = useThrottle(onUpdate, 60);
Expand Down
1 change: 1 addition & 0 deletions assets/editor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ import './store';
import './blocks/free';
import './blocks/main';
import './extensions/block-id';
import './plugins';
6 changes: 6 additions & 0 deletions assets/editor/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { registerPlugin } from '@wordpress/plugins';
import RemoveBlockWithSavedMeta from './remove-block-with-saved-post-meta';

registerPlugin('lazy-blocks-remove-block-with-saved-meta', {
render: RemoveBlockWithSavedMeta,
});
185 changes: 185 additions & 0 deletions assets/editor/plugins/remove-block-with-saved-post-meta/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import './index.scss';
// eslint-disable-next-line import/no-extraneous-dependencies
import { differenceWith } from 'lodash';
import { useSelect } from '@wordpress/data';
import { useEffect, useState, useMemo, useCallback } from '@wordpress/element';
import { useEntityProp } from '@wordpress/core-data';
import { Button, ToggleControl } from '@wordpress/components';
import { usePrevious } from '@wordpress/compose';
import { __ } from '@wordpress/i18n';

import Modal from '../../../components/modal';
import useAllBlocks from '../../../hooks/use-all-blocks';

let options = window.lazyblocksGutenberg;
if (!options?.blocks?.length) {
options = {
post_type: 'post',
blocks: [],
controls: {},
};
}

function RemoveBlockWithSavedMeta() {
const [isModalOpen, setIsModalOpen] = useState(false);
const [savedMetaNames, setMetaNames] = useState([]);

const allBlocks = useAllBlocks();
const prevAllBlocks = usePrevious(allBlocks);

const postType = useSelect(
(select) => select('core/editor')?.getCurrentPostType?.(),
[]
);

const [meta, setMeta] = useEntityProp('postType', postType, 'meta');

// Memoize blocks with meta controls
const blocksWithMetaControls = useMemo(() => {
return options.blocks.filter((block) =>
Object.values(block.controls).some(
(control) => control.save_in_meta === 'true'
)
);
}, []);

const handleMetaToggle = useCallback((index, value) => {
setMetaNames((prev) =>
prev.map((prevMeta, i) =>
i === index ? { ...prevMeta, checked: value } : prevMeta
)
);
}, []);

const handleConfirmDelete = useCallback(() => {
const metaToDelete = savedMetaNames.reduce((acc, savedMeta) => {
if (savedMeta.checked) {
acc[savedMeta.metaName] = null;
}

return acc;
}, {});

if (Object.keys(metaToDelete).length) {
setMeta(metaToDelete);
}

setIsModalOpen(false);
}, [savedMetaNames, setMeta]);

const handleCancel = useCallback(() => {
setIsModalOpen(false);
}, []);

useEffect(() => {
if (!prevAllBlocks) {
return;
}

const removedBlocks = differenceWith(
prevAllBlocks,
allBlocks,
(el1, el2) => el1.clientId === el2.clientId
);

if (!removedBlocks.length) {
return;
}

const savedSlugs = blocksWithMetaControls.map((block) => block.slug);

const processRemovedBlocks = () => {
const uniqueMetaNames = new Set();

const metaNames = removedBlocks
.filter((block) => savedSlugs.includes(block.name))
.flatMap((block) => {
const savedBlock = blocksWithMetaControls.find(
(saved) => saved.slug === block.name
);

return Object.values(savedBlock.controls)
.filter((control) => control.save_in_meta === 'true')
.map((control) => ({
metaName: control.save_in_meta_name || control.name,
label: control.label,
default: control.default,
checked: false,
}))
.filter((control) => {
if (uniqueMetaNames.has(control.metaName)) {
return false;
}

uniqueMetaNames.add(control.metaName);

return true;
});
})
.filter((m) => {
const metaValue = meta[m.metaName];

return (
metaValue !== m.default &&
metaValue !== undefined &&
metaValue !== null &&
metaValue !== ''
);
});

if (metaNames?.length) {
setMetaNames(metaNames);
setIsModalOpen(true);
}
};

processRemovedBlocks();
}, [meta, allBlocks, prevAllBlocks, blocksWithMetaControls]);

if (!isModalOpen || !savedMetaNames.length) {
return null;
}

return (
<Modal
title={__('Remove post meta used by this block?', 'lazy-blocks')}
onRequestClose={handleCancel}
size="medium"
>
<p style={{ marginTop: 0 }}>
{__(
'This block created metadata that is still saved in your post.',
'lazy-blocks'
)}
<br />
{__(
'Would you like to remove any of these meta fields?',
'lazy-blocks'
)}
</p>
<p>{__('Select post meta to remove:', 'lazy-blocks')}</p>
{savedMetaNames.map((currentMeta, index) => (
<ToggleControl
key={currentMeta.metaName}
label={currentMeta.label}
checked={currentMeta.checked}
onChange={(value) => handleMetaToggle(index, value)}
/>
))}
<div className="lzb-gutenberg-remove-post-meta-modal-buttons">
<Button
variant="primary"
onClick={handleConfirmDelete}
disabled={!savedMetaNames.some((m) => m.checked)}
>
{__('Remove selected Meta', 'lazy-blocks')}
</Button>
<Button variant="link" onClick={handleCancel}>
{__('Cancel', 'lazy-blocks')}
</Button>
</div>
</Modal>
);
}

export default RemoveBlockWithSavedMeta;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.lzb-gutenberg-remove-post-meta-modal-buttons {
display: flex;
gap: 16px;
margin-top: 26px;
}
39 changes: 39 additions & 0 deletions assets/hooks/use-all-blocks/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { useCallback, useMemo } from '@wordpress/element';
import { useSelect } from '@wordpress/data';

/**
* Custom hook to get all blocks recursively.
*
* @return {Function} Function to get all blocks.
*/
function useAllBlocks() {
const { allBlocks } = useSelect((select) => {
const { getBlocks } = select('core/block-editor');

return {
allBlocks: getBlocks(),
};
}, []);

const flattenBlocks = useCallback((blocks) => {
if (!blocks?.length) {
return [];
}

const result = [];

blocks.forEach((data) => {
result.push(data);

if (data.innerBlocks?.length) {
result.push(...flattenBlocks(data.innerBlocks));
}
});

return result;
}, []);

return useMemo(() => flattenBlocks(allBlocks), [allBlocks, flattenBlocks]);
}

export default useAllBlocks;
2 changes: 1 addition & 1 deletion build/control-file.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('react', 'react-dom', 'wp-block-editor', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '5ad81efea32da8e6a67c');
<?php return array('dependencies' => array('react', 'react-dom', 'wp-block-editor', 'wp-components', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => 'b71835124bf0464c824e');
2 changes: 1 addition & 1 deletion build/control-file.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/editor-constructor.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => 'edbc2c4cddea46341962');
<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => 'c9355c9ad4d453a56d82');
2 changes: 1 addition & 1 deletion build/editor-constructor.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions build/editor-rtl.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion build/editor.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n'), 'version' => '61d7c5ab7c461a8c5833');
<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-api-fetch', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-compose', 'wp-core-data', 'wp-data', 'wp-element', 'wp-hooks', 'wp-i18n', 'wp-plugins'), 'version' => 'b410994159c603f84602');
1 change: 1 addition & 0 deletions build/editor.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions build/editor.js

Large diffs are not rendered by default.

Loading