Skip to content

Commit

Permalink
Avoid overriding custom settings on font library save (WordPress#60438)
Browse files Browse the repository at this point in the history
Co-authored-by: matiasbenedetto <[email protected]>
Co-authored-by: youknowriad <[email protected]>
Co-authored-by: mikachan <[email protected]>
Co-authored-by: creativecoder <[email protected]>
Co-authored-by: desrosj <[email protected]>
Co-authored-by: estelaris <[email protected]>
Co-authored-by: YanCol <[email protected]>
  • Loading branch information
8 people authored and cbravobernal committed Apr 9, 2024
1 parent 9d13567 commit 6d78358
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ import {
checkFontFaceInstalled,
} from './utils';
import { toggleFont } from './utils/toggleFont';
import setNestedValue from '../../../utils/set-nested-value';

export const FontLibraryContext = createContext( {} );

function FontLibraryProvider( { children } ) {
const { __experimentalSaveSpecifiedEntityEdits: saveSpecifiedEntityEdits } =
useDispatch( coreStore );
const { saveEntityRecord } = useDispatch( coreStore );
const { globalStylesId } = useSelect( ( select ) => {
const { __experimentalGetCurrentGlobalStylesId } = select( coreStore );
return { globalStylesId: __experimentalGetCurrentGlobalStylesId() };
Expand Down Expand Up @@ -94,11 +94,30 @@ function FontLibraryProvider( { children } ) {
'base'
);

// Save font families to the global styles post in the database.
const saveFontFamilies = () => {
saveSpecifiedEntityEdits( 'root', 'globalStyles', globalStylesId, [
'settings.typography.fontFamilies',
] );
/*
* Save the font families to the database.
* This function is called when the user activates or deactivates a font family.
* It only updates the global styles post content in the database for new font families.
* This avoids saving other styles/settings changed by the user using other parts of the editor.
*
* It uses the font families from the param to avoid using the font families from an outdated state.
*
* @param {Array} fonts - The font families that will be saved to the database.
*/
const saveFontFamilies = async ( fonts ) => {
// Gets the global styles database post content.
const updatedGlobalStyles = globalStyles.record;

// Updates the database version of global styles with the edited font families in the client.
setNestedValue(
updatedGlobalStyles,
[ 'settings', 'typography', 'fontFamilies' ],
fonts
);

// Saves a new version of the global styles in the database.
await saveEntityRecord( 'root', 'globalStyles', updatedGlobalStyles );
};

// Library Fonts
Expand Down Expand Up @@ -322,15 +341,11 @@ function FontLibraryProvider( { children } ) {

if ( fontFamiliesToActivate.length > 0 ) {
// Activate the font family (add the font family to the global styles).
activateCustomFontFamilies( fontFamiliesToActivate );

// Save the global styles to the database.
await saveSpecifiedEntityEdits(
'root',
'globalStyles',
globalStylesId,
[ 'settings.typography.fontFamilies' ]
const activeFonts = activateCustomFontFamilies(
fontFamiliesToActivate
);
// Save the global styles to the database.
await saveFontFamilies( activeFonts );

refreshLibrary();
}
Expand Down Expand Up @@ -360,14 +375,11 @@ function FontLibraryProvider( { children } ) {
// Deactivate the font family if delete request is successful
// (Removes the font family from the global styles).
if ( uninstalledFontFamily.deleted ) {
deactivateFontFamily( fontFamilyToUninstall );
// Save the global styles to the database.
await saveSpecifiedEntityEdits(
'root',
'globalStyles',
globalStylesId,
[ 'settings.typography.fontFamilies' ]
const activeFonts = deactivateFontFamily(
fontFamilyToUninstall
);
// Save the global styles to the database.
await saveFontFamilies( activeFonts );
}

// Refresh the library (the library font families from database).
Expand All @@ -391,42 +403,54 @@ function FontLibraryProvider( { children } ) {
const newCustomFonts = initialCustomFonts.filter(
( f ) => f.slug !== font.slug
);
setFontFamilies( {
const activeFonts = {
...fontFamilies,
[ font.source ]: newCustomFonts,
} );
};
setFontFamilies( activeFonts );

if ( font.fontFace ) {
font.fontFace.forEach( ( face ) => {
unloadFontFaceInBrowser( face, 'all' );
} );
}
return activeFonts;
};

const activateCustomFontFamilies = ( fontsToAdd ) => {
// Removes the id from the families and faces to avoid saving that to global styles post content.
const fontsToActivate = fontsToAdd.map(
( { id: _familyDbId, fontFace, ...font } ) => ( {
...font,
...( fontFace && fontFace.length > 0
? {
fontFace: fontFace.map(
( { id: _faceDbId, ...face } ) => face
),
}
: {} ),
} )
);
const fontsToActivate = cleanFontsForSave( fontsToAdd );

// Activate the fonts by set the new custom fonts array.
setFontFamilies( {
const activeFonts = {
...fontFamilies,
// Merge the existing custom fonts with the new fonts.
custom: mergeFontFamilies( fontFamilies?.custom, fontsToActivate ),
} );
};

// Activate the fonts by set the new custom fonts array.
setFontFamilies( activeFonts );

loadFontsInBrowser( fontsToActivate );

return activeFonts;
};

// Removes the id from the families and faces to avoid saving that to global styles post content.
const cleanFontsForSave = ( fonts ) => {
return fonts.map( ( { id: _familyDbId, fontFace, ...font } ) => ( {
...font,
...( fontFace && fontFace.length > 0
? {
fontFace: fontFace.map(
( { id: _faceDbId, ...face } ) => face
),
}
: {} ),
} ) );
};

const loadFontsInBrowser = ( fonts ) => {
// Add custom fonts to the browser.
fontsToActivate.forEach( ( font ) => {
fonts.forEach( ( font ) => {
if ( font.fontFace ) {
font.fontFace.forEach( ( face ) => {
// Load font faces just in the iframe because they already are in the document.
Expand Down Expand Up @@ -518,6 +542,7 @@ function FontLibraryProvider( { children } ) {
value={ {
libraryFontSelected,
handleSetLibraryFontSelected,
fontFamilies,
themeFonts,
baseThemeFonts,
customFonts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ function InstalledFonts() {
fontFamiliesHasChanges,
notice,
setNotice,
fontFamilies,
} = useContext( FontLibraryContext );
const [ isConfirmDeleteOpen, setIsConfirmDeleteOpen ] = useState( false );
const customFontFamilyId =
Expand Down Expand Up @@ -262,7 +263,9 @@ function InstalledFonts() {
) }
<Button
variant="primary"
onClick={ saveFontFamilies }
onClick={ () => {
saveFontFamilies( fontFamilies );
} }
disabled={ ! fontFamiliesHasChanges }
__experimentalIsFocusable
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { store as coreStore } from '@wordpress/core-data';
import { useSupportedStyles } from '../../components/global-styles/hooks';
import { unlock } from '../../lock-unlock';
import cloneDeep from '../../utils/clone-deep';
import setNestedValue from '../../utils/set-nested-value';

const { cleanEmptyObject, GlobalStylesContext } = unlock(
blockEditorPrivateApis
Expand Down Expand Up @@ -236,46 +237,6 @@ function useChangesToPush( name, attributes, userConfig ) {
}, [ supports, attributes, blockUserConfig ] );
}

/**
* Sets the value at path of object.
* If a portion of path doesn’t exist, it’s created.
* Arrays are created for missing index properties while objects are created
* for all other missing properties.
*
* This function intentionally mutates the input object.
*
* Inspired by _.set().
*
* @see https://lodash.com/docs/4.17.15#set
*
* @todo Needs to be deduplicated with its copy in `@wordpress/core-data`.
*
* @param {Object} object Object to modify
* @param {Array} path Path of the property to set.
* @param {*} value Value to set.
*/
function setNestedValue( object, path, value ) {
if ( ! object || typeof object !== 'object' ) {
return object;
}

path.reduce( ( acc, key, idx ) => {
if ( acc[ key ] === undefined ) {
if ( Number.isInteger( path[ idx + 1 ] ) ) {
acc[ key ] = [];
} else {
acc[ key ] = {};
}
}
if ( idx === path.length - 1 ) {
acc[ key ] = value;
}
return acc[ key ];
}, object );

return object;
}

function PushChangesToGlobalStylesControl( {
name,
attributes,
Expand Down
39 changes: 39 additions & 0 deletions packages/edit-site/src/utils/set-nested-value.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Sets the value at path of object.
* If a portion of path doesn’t exist, it’s created.
* Arrays are created for missing index properties while objects are created
* for all other missing properties.
*
* This function intentionally mutates the input object.
*
* Inspired by _.set().
*
* @see https://lodash.com/docs/4.17.15#set
*
* @todo Needs to be deduplicated with its copy in `@wordpress/core-data`.
*
* @param {Object} object Object to modify
* @param {Array} path Path of the property to set.
* @param {*} value Value to set.
*/
export default function setNestedValue( object, path, value ) {
if ( ! object || typeof object !== 'object' ) {
return object;
}

path.reduce( ( acc, key, idx ) => {
if ( acc[ key ] === undefined ) {
if ( Number.isInteger( path[ idx + 1 ] ) ) {
acc[ key ] = [];
} else {
acc[ key ] = {};
}
}
if ( idx === path.length - 1 ) {
acc[ key ] = value;
}
return acc[ key ];
}, object );

return object;
}

0 comments on commit 6d78358

Please sign in to comment.