@wordpress/block-library
Version:
Block library for the WordPress editor.
180 lines (158 loc) • 4.88 kB
JavaScript
/**
* WordPress dependencies
*/
import { useRegistry, useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { useState, useCallback } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import menuItemsToBlocks from '../menu-items-to-blocks';
export const CLASSIC_MENU_CONVERSION_SUCCESS = 'success';
export const CLASSIC_MENU_CONVERSION_ERROR = 'error';
export const CLASSIC_MENU_CONVERSION_PENDING = 'pending';
export const CLASSIC_MENU_CONVERSION_IDLE = 'idle';
// This is needed to ensure that multiple components using this hook
// do not import the same classic menu twice.
let classicMenuBeingConvertedId = null;
function useConvertClassicToBlockMenu(
createNavigationMenu,
{ throwOnError = false } = {}
) {
const registry = useRegistry();
const { editEntityRecord } = useDispatch( coreStore );
const [ status, setStatus ] = useState( CLASSIC_MENU_CONVERSION_IDLE );
const [ error, setError ] = useState( null );
const convertClassicMenuToBlockMenu = useCallback(
async ( menuId, menuName, postStatus = 'publish' ) => {
let navigationMenu;
let classicMenuItems;
// 1. Fetch the classic Menu items.
try {
classicMenuItems = await registry
.resolveSelect( coreStore )
.getMenuItems( {
menus: menuId,
per_page: -1,
context: 'view',
} );
} catch ( err ) {
throw new Error(
sprintf(
// translators: %s: The name of a menu (e.g. Header menu).
__( `Unable to fetch classic menu "%s" from API.` ),
menuName
),
{
cause: err,
}
);
}
// Handle offline response which resolves to `null`.
if ( classicMenuItems === null ) {
throw new Error(
sprintf(
// translators: %s: The name of a menu (e.g. Header menu).
__( `Unable to fetch classic menu "%s" from API.` ),
menuName
)
);
}
// 2. Convert the classic items into blocks.
const { innerBlocks } = menuItemsToBlocks( classicMenuItems );
// 3. Create the `wp_navigation` Post with the blocks.
try {
navigationMenu = await createNavigationMenu(
menuName,
innerBlocks,
postStatus
);
/**
* Immediately trigger editEntityRecord to change the wp_navigation post status to 'publish'.
* This status change causes the menu to be displayed on the front of the site and sets the post state to be "dirty".
* The problem being solved is if saveEditedEntityRecord was used here, the menu would be updated on the frontend and the editor _automatically_,
* without user interaction.
* If the user abandons the site editor without saving, there would still be a wp_navigation post created as draft.
*/
await editEntityRecord(
'postType',
'wp_navigation',
navigationMenu.id,
{
status: 'publish',
},
{ throwOnError: true }
);
} catch ( err ) {
throw new Error(
sprintf(
// translators: %s: The name of a menu (e.g. Header menu).
__( `Unable to create Navigation Menu "%s".` ),
menuName
),
{
cause: err,
}
);
}
return navigationMenu;
},
[ createNavigationMenu, editEntityRecord, registry ]
);
const convert = useCallback(
async ( menuId, menuName, postStatus ) => {
// Check whether this classic menu is being imported already.
if ( classicMenuBeingConvertedId === menuId ) {
return;
}
// Set the ID for the currently importing classic menu.
classicMenuBeingConvertedId = menuId;
if ( ! menuId || ! menuName ) {
setError( 'Unable to convert menu. Missing menu details.' );
setStatus( CLASSIC_MENU_CONVERSION_ERROR );
return;
}
setStatus( CLASSIC_MENU_CONVERSION_PENDING );
setError( null );
return await convertClassicMenuToBlockMenu(
menuId,
menuName,
postStatus
)
.then( ( navigationMenu ) => {
setStatus( CLASSIC_MENU_CONVERSION_SUCCESS );
// Reset the ID for the currently importing classic menu.
classicMenuBeingConvertedId = null;
return navigationMenu;
} )
.catch( ( err ) => {
setError( err?.message );
// Reset the ID for the currently importing classic menu.
setStatus( CLASSIC_MENU_CONVERSION_ERROR );
// Reset the ID for the currently importing classic menu.
classicMenuBeingConvertedId = null;
// Rethrow error for debugging.
if ( throwOnError ) {
throw new Error(
sprintf(
// translators: %s: The name of a menu (e.g. Header menu).
__( `Unable to create Navigation Menu "%s".` ),
menuName
),
{
cause: err,
}
);
}
} );
},
[ convertClassicMenuToBlockMenu, throwOnError ]
);
return {
convert,
status,
error,
};
}
export default useConvertClassicToBlockMenu;