Skip to content

Commit

Permalink
Patterns: Fix bug with pattern categories not saving sometimes (#54676)
Browse files Browse the repository at this point in the history

---------

Co-authored-by: Kai Hao <[email protected]>
  • Loading branch information
2 people authored and mikachan committed Sep 22, 2023
1 parent e530e3e commit 40c12b4
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 54 deletions.
52 changes: 7 additions & 45 deletions packages/patterns/src/components/category-selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import { __ } from '@wordpress/i18n';
import { useMemo, useState } from '@wordpress/element';
import { FormTokenField } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { useSelect } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';
import { useDebounce } from '@wordpress/compose';
import { decodeEntities } from '@wordpress/html-entities';
Expand All @@ -13,35 +13,26 @@ const unescapeString = ( arg ) => {
return decodeEntities( arg );
};

const unescapeTerm = ( term ) => {
return {
...term,
name: unescapeString( term.name ),
};
};

const EMPTY_ARRAY = [];
const MAX_TERMS_SUGGESTIONS = 20;
const DEFAULT_QUERY = {
per_page: MAX_TERMS_SUGGESTIONS,
_fields: 'id,name',
context: 'view',
};
const slug = 'wp_pattern_category';
export const CATEGORY_SLUG = 'wp_pattern_category';

export default function CategorySelector( { onCategorySelection } ) {
const [ values, setValues ] = useState( [] );
export default function CategorySelector( { values, onChange } ) {
const [ search, setSearch ] = useState( '' );
const debouncedSearch = useDebounce( setSearch, 500 );
const { invalidateResolution } = useDispatch( coreStore );

const { searchResults } = useSelect(
( select ) => {
const { getEntityRecords } = select( coreStore );

return {
searchResults: !! search
? getEntityRecords( 'taxonomy', slug, {
? getEntityRecords( 'taxonomy', CATEGORY_SLUG, {
...DEFAULT_QUERY,
search,
} )
Expand All @@ -57,28 +48,7 @@ export default function CategorySelector( { onCategorySelection } ) {
);
}, [ searchResults ] );

const { saveEntityRecord } = useDispatch( coreStore );

async function findOrCreateTerm( term ) {
try {
const newTerm = await saveEntityRecord( 'taxonomy', slug, term, {
throwOnError: true,
} );
invalidateResolution( 'getUserPatternCategories' );
return unescapeTerm( newTerm );
} catch ( error ) {
if ( error.code !== 'term_exists' ) {
throw error;
}

return {
id: error.data.term_id,
name: term.name,
};
}
}

function onChange( termNames ) {
function handleChange( termNames ) {
const uniqueTerms = termNames.reduce( ( terms, newTerm ) => {
if (
! terms.some(
Expand All @@ -90,15 +60,7 @@ export default function CategorySelector( { onCategorySelection } ) {
return terms;
}, [] );

setValues( uniqueTerms );

Promise.all(
uniqueTerms.map( ( termName ) =>
findOrCreateTerm( { name: termName } )
)
).then( ( newTerms ) => {
onCategorySelection( newTerms );
} );
onChange( uniqueTerms );
}

return (
Expand All @@ -107,7 +69,7 @@ export default function CategorySelector( { onCategorySelection } ) {
className="patterns-menu-items__convert-modal-categories"
value={ values }
suggestions={ suggestions }
onChange={ onChange }
onChange={ handleChange }
onInputChange={ debouncedSearch }
maxSuggestions={ MAX_TERMS_SUGGESTIONS }
label={ __( 'Categories' ) }
Expand Down
58 changes: 49 additions & 9 deletions packages/patterns/src/components/create-pattern-modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { __ } from '@wordpress/i18n';
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
Expand All @@ -23,7 +24,7 @@ import { PATTERN_DEFAULT_CATEGORY, PATTERN_SYNC_TYPES } from '../constants';
* Internal dependencies
*/
import { store as patternsStore } from '../store';
import CategorySelector from './category-selector';
import CategorySelector, { CATEGORY_SLUG } from './category-selector';
import { unlock } from '../lock-unlock';

export default function CreatePatternModal( {
Expand All @@ -34,13 +35,24 @@ export default function CreatePatternModal( {
className = 'patterns-menu-items__convert-modal',
} ) {
const [ syncType, setSyncType ] = useState( PATTERN_SYNC_TYPES.full );
const [ categories, setCategories ] = useState( [] );
const [ categoryTerms, setCategoryTerms ] = useState( [] );
const [ title, setTitle ] = useState( '' );
const [ isSaving, setIsSaving ] = useState( false );
const { createPattern } = unlock( useDispatch( patternsStore ) );

const { saveEntityRecord, invalidateResolution } = useDispatch( coreStore );
const { createErrorNotice } = useDispatch( noticesStore );

async function onCreate( patternTitle, sync ) {
if ( isSaving ) return;

try {
setIsSaving( true );
const categories = await Promise.all(
categoryTerms.map( ( termName ) =>
findOrCreateTerm( termName )
)
);

const newPattern = await createPattern(
patternTitle,
sync,
Expand All @@ -57,12 +69,35 @@ export default function CreatePatternModal( {
id: 'convert-to-pattern-error',
} );
onError();
} finally {
setIsSaving( false );
setCategoryTerms( [] );
setTitle( '' );
}
}

const handleCategorySelection = ( selectedCategories ) => {
setCategories( selectedCategories.map( ( cat ) => cat.id ) );
};
/**
* @param {string} term
* @return {Promise<number>} The pattern category id.
*/
async function findOrCreateTerm( term ) {
try {
const newTerm = await saveEntityRecord(
'taxonomy',
CATEGORY_SLUG,
{ name: term },
{ throwOnError: true }
);
invalidateResolution( 'getUserPatternCategories' );
return newTerm.id;
} catch ( error ) {
if ( error.code !== 'term_exists' ) {
throw error;
}

return error.data.term_id;
}
}

return (
<Modal
Expand All @@ -77,7 +112,6 @@ export default function CreatePatternModal( {
onSubmit={ ( event ) => {
event.preventDefault();
onCreate( title, syncType );
setTitle( '' );
} }
>
<VStack spacing="5">
Expand All @@ -90,7 +124,8 @@ export default function CreatePatternModal( {
className="patterns-create-modal__name-input"
/>
<CategorySelector
onCategorySelection={ handleCategorySelection }
values={ categoryTerms }
onChange={ setCategoryTerms }
/>
<ToggleControl
label={ __( 'Synced' ) }
Expand All @@ -117,7 +152,12 @@ export default function CreatePatternModal( {
{ __( 'Cancel' ) }
</Button>

<Button variant="primary" type="submit">
<Button
variant="primary"
type="submit"
aria-disabled={ isSaving }
isBusy={ isSaving }
>
{ __( 'Create' ) }
</Button>
</HStack>
Expand Down

0 comments on commit 40c12b4

Please sign in to comment.