Skip to content

Commit

Permalink
Patterns: Fix pattern categories on import (#58926)
Browse files Browse the repository at this point in the history
Co-authored-by: ntsekouras <[email protected]>
Co-authored-by: aaronrobertshaw <[email protected]>
Co-authored-by: glendaviesnz <[email protected]>
Co-authored-by: bph <[email protected]>
Co-authored-by: hanneslsm <[email protected]>
Co-authored-by: colorful-tones <[email protected]>
Co-authored-by: annezazu <[email protected]>
  • Loading branch information
8 people authored and youknowriad committed Feb 20, 2024
1 parent 51c5b29 commit 8f7e6a6
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 90 deletions.
38 changes: 27 additions & 11 deletions packages/edit-site/src/components/add-new-pattern/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ import {
PATTERN_DEFAULT_CATEGORY,
TEMPLATE_PART_POST_TYPE,
} from '../../utils/constants';
import usePatternCategories from '../sidebar-navigation-screen-patterns/use-pattern-categories';

const { useHistory, useLocation } = unlock( routerPrivateApis );
const { CreatePatternModal } = unlock( editPatternsPrivateApis );
const { CreatePatternModal, useAddPatternCategory } = unlock(
editPatternsPrivateApis
);

export default function AddNewPattern() {
const history = useHistory();
Expand All @@ -43,7 +44,6 @@ export default function AddNewPattern() {
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
const patternUploadInputRef = useRef();
const { patternCategories } = usePatternCategories();

function handleCreatePattern( { pattern, categoryId } ) {
setShowPatternModal( false );
Expand Down Expand Up @@ -97,6 +97,7 @@ export default function AddNewPattern() {
title: __( 'Import pattern from JSON' ),
} );

const { categoryMap, findOrCreateTerm } = useAddPatternCategory();
return (
<>
<DropdownMenu
Expand Down Expand Up @@ -132,12 +133,23 @@ export default function AddNewPattern() {
const file = event.target.files?.[ 0 ];
if ( ! file ) return;
try {
const currentCategoryId =
params.categoryType !== TEMPLATE_PART_POST_TYPE &&
patternCategories.find(
( category ) =>
category.name === params.categoryId
)?.id;
let currentCategoryId;
// When we're not handling template parts, we should
// add or create the proper pattern category.
if ( params.categoryType !== TEMPLATE_PART_POST_TYPE ) {
const currentCategory = categoryMap
.values()
.find(
( term ) => term.name === params.categoryId
);
if ( !! currentCategory ) {
currentCategoryId =
currentCategory.id ||
( await findOrCreateTerm(
currentCategory.label
) );
}
}
const pattern = await createPatternFromFile(
file,
currentCategoryId
Expand All @@ -146,8 +158,12 @@ export default function AddNewPattern() {
);

// Navigate to the All patterns category for the newly created pattern
// if we're not on that page already.
if ( ! currentCategoryId ) {
// if we're not on that page already and if we're not in the `my-patterns`
// category.
if (
! currentCategoryId &&
params.categoryId !== 'my-patterns'
) {
history.push( {
path: `/patterns`,
categoryType: PATTERN_TYPES.theme,
Expand Down
84 changes: 5 additions & 79 deletions packages/patterns/src/components/create-pattern-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,17 @@ import {
ToggleControl,
} from '@wordpress/components';
import { __, _x } from '@wordpress/i18n';
import { useState, useMemo } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { useState } from '@wordpress/element';
import { useDispatch } from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';
import { store as coreStore } from '@wordpress/core-data';

/**
* Internal dependencies
*/
import { PATTERN_DEFAULT_CATEGORY, PATTERN_SYNC_TYPES } from '../constants';

/**
* Internal dependencies
*/
import { store as patternsStore } from '../store';
import CategorySelector, { CATEGORY_SLUG } from './category-selector';
import CategorySelector from './category-selector';
import { useAddPatternCategory } from '../private-hooks';
import { unlock } from '../lock-unlock';

export default function CreatePatternModal( {
Expand Down Expand Up @@ -59,47 +55,9 @@ export function CreatePatternModalContents( {

const [ isSaving, setIsSaving ] = useState( false );
const { createPattern } = unlock( useDispatch( patternsStore ) );
const { saveEntityRecord, invalidateResolution } = useDispatch( coreStore );
const { createErrorNotice } = useDispatch( noticesStore );

const { corePatternCategories, userPatternCategories } = useSelect(
( select ) => {
const { getUserPatternCategories, getBlockPatternCategories } =
select( coreStore );

return {
corePatternCategories: getBlockPatternCategories(),
userPatternCategories: getUserPatternCategories(),
};
}
);

const categoryMap = useMemo( () => {
// Merge the user and core pattern categories and remove any duplicates.
const uniqueCategories = new Map();
userPatternCategories.forEach( ( category ) => {
uniqueCategories.set( category.label.toLowerCase(), {
label: category.label,
name: category.name,
id: category.id,
} );
} );

corePatternCategories.forEach( ( category ) => {
if (
! uniqueCategories.has( category.label.toLowerCase() ) &&
// There are two core categories with `Post` label so explicitly remove the one with
// the `query` slug to avoid any confusion.
category.name !== 'query'
) {
uniqueCategories.set( category.label.toLowerCase(), {
label: category.label,
name: category.name,
} );
}
} );
return uniqueCategories;
}, [ userPatternCategories, corePatternCategories ] );
const { categoryMap, findOrCreateTerm } = useAddPatternCategory();

async function onCreate( patternTitle, sync ) {
if ( ! title || isSaving ) {
Expand Down Expand Up @@ -137,38 +95,6 @@ export function CreatePatternModalContents( {
}
}

/**
* @param {string} term
* @return {Promise<number>} The pattern category id.
*/
async function findOrCreateTerm( term ) {
try {
const existingTerm = categoryMap.get( term.toLowerCase() );
if ( existingTerm && existingTerm.id ) {
return existingTerm.id;
}
// If we have an existing core category we need to match the new user category to the
// correct slug rather than autogenerating it to prevent duplicates, eg. the core `Headers`
// category uses the singular `header` as the slug.
const termData = existingTerm
? { name: existingTerm.label, slug: existingTerm.name }
: { name: term };
const newTerm = await saveEntityRecord(
'taxonomy',
CATEGORY_SLUG,
termData,
{ throwOnError: true }
);
invalidateResolution( 'getUserPatternCategories' );
return newTerm.id;
} catch ( error ) {
if ( error.code !== 'term_exists' ) {
throw error;
}

return error.data.term_id;
}
}
return (
<form
onSubmit={ ( event ) => {
Expand Down
2 changes: 2 additions & 0 deletions packages/patterns/src/private-apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import PatternsMenuItems from './components';
import RenamePatternCategoryModal from './components/rename-pattern-category-modal';
import PartialSyncingControls from './components/partial-syncing-controls';
import ResetOverridesControl from './components/reset-overrides-control';
import { useAddPatternCategory } from './private-hooks';
import {
PATTERN_TYPES,
PATTERN_DEFAULT_CATEGORY,
Expand All @@ -35,6 +36,7 @@ lock( privateApis, {
RenamePatternCategoryModal,
PartialSyncingControls,
ResetOverridesControl,
useAddPatternCategory,
PATTERN_TYPES,
PATTERN_DEFAULT_CATEGORY,
PATTERN_USER_CATEGORY,
Expand Down
91 changes: 91 additions & 0 deletions packages/patterns/src/private-hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**
* WordPress dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { useMemo } from '@wordpress/element';

/**
* Internal dependencies
*/
import { CATEGORY_SLUG } from './components/category-selector';

/**
* Helper hook that creates a Map with the core and user patterns categories
* and removes any duplicates. It's used when we need to create new user
* categories when creating or importing patterns.
* This hook also provides a function to find or create a pattern category.
*
* @return {Object} The merged categories map and the callback function to find or create a category.
*/
export function useAddPatternCategory() {
const { saveEntityRecord, invalidateResolution } = useDispatch( coreStore );
const { corePatternCategories, userPatternCategories } = useSelect(
( select ) => {
const { getUserPatternCategories, getBlockPatternCategories } =
select( coreStore );

return {
corePatternCategories: getBlockPatternCategories(),
userPatternCategories: getUserPatternCategories(),
};
},
[]
);
const categoryMap = useMemo( () => {
// Merge the user and core pattern categories and remove any duplicates.
const uniqueCategories = new Map();
userPatternCategories.forEach( ( category ) => {
uniqueCategories.set( category.label.toLowerCase(), {
label: category.label,
name: category.name,
id: category.id,
} );
} );

corePatternCategories.forEach( ( category ) => {
if (
! uniqueCategories.has( category.label.toLowerCase() ) &&
// There are two core categories with `Post` label so explicitly remove the one with
// the `query` slug to avoid any confusion.
category.name !== 'query'
) {
uniqueCategories.set( category.label.toLowerCase(), {
label: category.label,
name: category.name,
} );
}
} );
return uniqueCategories;
}, [ userPatternCategories, corePatternCategories ] );

async function findOrCreateTerm( term ) {
try {
const existingTerm = categoryMap.get( term.toLowerCase() );
if ( existingTerm?.id ) {
return existingTerm.id;
}
// If we have an existing core category we need to match the new user category to the
// correct slug rather than autogenerating it to prevent duplicates, eg. the core `Headers`
// category uses the singular `header` as the slug.
const termData = existingTerm
? { name: existingTerm.label, slug: existingTerm.name }
: { name: term };
const newTerm = await saveEntityRecord(
'taxonomy',
CATEGORY_SLUG,
termData,
{ throwOnError: true }
);
invalidateResolution( 'getUserPatternCategories' );
return newTerm.id;
} catch ( error ) {
if ( error.code !== 'term_exists' ) {
throw error;
}
return error.data.term_id;
}
}

return { categoryMap, findOrCreateTerm };
}

0 comments on commit 8f7e6a6

Please sign in to comment.