From 60d9dbc29da680f728181d48210c8e69be69f105 Mon Sep 17 00:00:00 2001
From: Miguel Fonseca <150562+mcsf@users.noreply.github.com>
Date: Wed, 12 Jul 2023 16:07:34 +0100
Subject: [PATCH] (Prelude) Factor out function updateFootnotes
---
packages/core-data/src/entity-provider.js | 127 +--------------------
packages/core-data/src/footnotes/index.js | 128 ++++++++++++++++++++++
2 files changed, 130 insertions(+), 125 deletions(-)
create mode 100644 packages/core-data/src/footnotes/index.js
diff --git a/packages/core-data/src/entity-provider.js b/packages/core-data/src/entity-provider.js
index 6cc1e021841b4a..16d15679c0c44d 100644
--- a/packages/core-data/src/entity-provider.js
+++ b/packages/core-data/src/entity-provider.js
@@ -9,20 +9,17 @@ import {
} from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { parse, __unstableSerializeAndClean } from '@wordpress/blocks';
-import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
/**
* Internal dependencies
*/
import { STORE_NAME } from './name';
-import { unlock } from './private-apis';
+import { updateFootnotesFromMeta } from './footnotes';
/** @typedef {import('@wordpress/blocks').WPBlock} WPBlock */
const EMPTY_ARRAY = [];
-let oldFootnotes = {};
-
/**
* Internal dependencies
*/
@@ -190,127 +187,7 @@ export function useEntityBlockEditor( kind, name, { id: _id } = {} ) {
}, [ content ] );
const updateFootnotes = useCallback(
- ( _blocks ) => {
- const output = { blocks: _blocks };
- if ( ! meta ) return output;
- // If meta.footnotes is empty, it means the meta is not registered.
- if ( meta.footnotes === undefined ) return output;
-
- const { getRichTextValues } = unlock( blockEditorPrivateApis );
- const _content = getRichTextValues( _blocks ).join( '' ) || '';
- const newOrder = [];
-
- // This can be avoided when
- // https://github.com/WordPress/gutenberg/pull/43204 lands. We can then
- // get the order directly from the rich text values.
- if ( _content.indexOf( 'data-fn' ) !== -1 ) {
- const regex = /data-fn="([^"]+)"/g;
- let match;
- while ( ( match = regex.exec( _content ) ) !== null ) {
- newOrder.push( match[ 1 ] );
- }
- }
-
- const footnotes = meta.footnotes
- ? JSON.parse( meta.footnotes )
- : [];
- const currentOrder = footnotes.map( ( fn ) => fn.id );
-
- if ( currentOrder.join( '' ) === newOrder.join( '' ) )
- return output;
-
- const newFootnotes = newOrder.map(
- ( fnId ) =>
- footnotes.find( ( fn ) => fn.id === fnId ) ||
- oldFootnotes[ fnId ] || {
- id: fnId,
- content: '',
- }
- );
-
- function updateAttributes( attributes ) {
- attributes = { ...attributes };
-
- for ( const key in attributes ) {
- const value = attributes[ key ];
-
- if ( Array.isArray( value ) ) {
- attributes[ key ] = value.map( updateAttributes );
- continue;
- }
-
- if ( typeof value !== 'string' ) {
- continue;
- }
-
- if ( value.indexOf( 'data-fn' ) === -1 ) {
- continue;
- }
-
- // When we store rich text values, this would no longer
- // require a regex.
- const regex =
- /(]+data-fn="([^"]+)"[^>]*>]*>)[\d*]*<\/a><\/sup>/g;
-
- attributes[ key ] = value.replace(
- regex,
- ( match, opening, fnId ) => {
- const index = newOrder.indexOf( fnId );
- return `${ opening }${ index + 1 }`;
- }
- );
-
- const compatRegex =
- /]+data-fn="([^"]+)"[^>]*>\*<\/a>/g;
-
- attributes[ key ] = attributes[ key ].replace(
- compatRegex,
- ( match, fnId ) => {
- const index = newOrder.indexOf( fnId );
- return `${
- index + 1
- }`;
- }
- );
- }
-
- return attributes;
- }
-
- function updateBlocksAttributes( __blocks ) {
- return __blocks.map( ( block ) => {
- return {
- ...block,
- attributes: updateAttributes( block.attributes ),
- innerBlocks: updateBlocksAttributes(
- block.innerBlocks
- ),
- };
- } );
- }
-
- // We need to go through all block attributs deeply and update the
- // footnote anchor numbering (textContent) to match the new order.
- const newBlocks = updateBlocksAttributes( _blocks );
-
- oldFootnotes = {
- ...oldFootnotes,
- ...footnotes.reduce( ( acc, fn ) => {
- if ( ! newOrder.includes( fn.id ) ) {
- acc[ fn.id ] = fn;
- }
- return acc;
- }, {} ),
- };
-
- return {
- meta: {
- ...meta,
- footnotes: JSON.stringify( newFootnotes ),
- },
- blocks: newBlocks,
- };
- },
+ ( _blocks ) => updateFootnotesFromMeta( _blocks, meta ),
[ meta ]
);
diff --git a/packages/core-data/src/footnotes/index.js b/packages/core-data/src/footnotes/index.js
new file mode 100644
index 00000000000000..211755f5b42aa7
--- /dev/null
+++ b/packages/core-data/src/footnotes/index.js
@@ -0,0 +1,128 @@
+/**
+ * WordPress dependencies
+ */
+import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
+
+/**
+ * Internal dependencies
+ */
+import { unlock } from '../private-apis';
+
+const { getRichTextValues } = unlock( blockEditorPrivateApis );
+
+let oldFootnotes = {};
+
+export function updateFootnotesFromMeta( blocks, meta ) {
+ const output = { blocks };
+ if ( ! meta ) return output;
+ // If meta.footnotes is empty, it means the meta is not registered.
+ if ( meta.footnotes === undefined ) return output;
+
+ const _content = getRichTextValues( blocks ).join( '' ) || '';
+ const newOrder = [];
+
+ // This can be avoided when
+ // https://github.com/WordPress/gutenberg/pull/43204 lands. We can then
+ // get the order directly from the rich text values.
+ if ( _content.indexOf( 'data-fn' ) !== -1 ) {
+ const regex = /data-fn="([^"]+)"/g;
+ let match;
+ while ( ( match = regex.exec( _content ) ) !== null ) {
+ newOrder.push( match[ 1 ] );
+ }
+ }
+
+ const footnotes = meta.footnotes ? JSON.parse( meta.footnotes ) : [];
+ const currentOrder = footnotes.map( ( fn ) => fn.id );
+
+ if ( currentOrder.join( '' ) === newOrder.join( '' ) ) return output;
+
+ const newFootnotes = newOrder.map(
+ ( fnId ) =>
+ footnotes.find( ( fn ) => fn.id === fnId ) ||
+ oldFootnotes[ fnId ] || {
+ id: fnId,
+ content: '',
+ }
+ );
+
+ function updateAttributes( attributes ) {
+ attributes = { ...attributes };
+
+ for ( const key in attributes ) {
+ const value = attributes[ key ];
+
+ if ( Array.isArray( value ) ) {
+ attributes[ key ] = value.map( updateAttributes );
+ continue;
+ }
+
+ if ( typeof value !== 'string' ) {
+ continue;
+ }
+
+ if ( value.indexOf( 'data-fn' ) === -1 ) {
+ continue;
+ }
+
+ // When we store rich text values, this would no longer
+ // require a regex.
+ const regex =
+ /(]+data-fn="([^"]+)"[^>]*>]*>)[\d*]*<\/a><\/sup>/g;
+
+ attributes[ key ] = value.replace(
+ regex,
+ ( match, opening, fnId ) => {
+ const index = newOrder.indexOf( fnId );
+ return `${ opening }${ index + 1 }`;
+ }
+ );
+
+ const compatRegex = /]+data-fn="([^"]+)"[^>]*>\*<\/a>/g;
+
+ attributes[ key ] = attributes[ key ].replace(
+ compatRegex,
+ ( match, fnId ) => {
+ const index = newOrder.indexOf( fnId );
+ return `${
+ index + 1
+ }`;
+ }
+ );
+ }
+
+ return attributes;
+ }
+
+ function updateBlocksAttributes( __blocks ) {
+ return __blocks.map( ( block ) => {
+ return {
+ ...block,
+ attributes: updateAttributes( block.attributes ),
+ innerBlocks: updateBlocksAttributes( block.innerBlocks ),
+ };
+ } );
+ }
+
+ // We need to go through all block attributs deeply and update the
+ // footnote anchor numbering (textContent) to match the new order.
+ const newBlocks = updateBlocksAttributes( blocks );
+
+ oldFootnotes = {
+ ...oldFootnotes,
+ ...footnotes.reduce( ( acc, fn ) => {
+ if ( ! newOrder.includes( fn.id ) ) {
+ acc[ fn.id ] = fn;
+ }
+ return acc;
+ }, {} ),
+ };
+
+ return {
+ meta: {
+ ...meta,
+ footnotes: JSON.stringify( newFootnotes ),
+ },
+ blocks: newBlocks,
+ };
+}