Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c8072d4
Font Library: add wp_font_face post type and scaffold font face REST …
creativecoder Jan 9, 2024
61da35c
Font Library: create font faces through the REST API (#57702)
creativecoder Jan 11, 2024
42565a3
Refactor Font Family Controller (#57785)
creativecoder Jan 12, 2024
afacdf2
Font Family and Font Face REST API endpoints: better data handling an…
creativecoder Jan 15, 2024
7c82cb9
Font Families REST API endpoint: ensure unique font family slugs (#57…
creativecoder Jan 16, 2024
f84cc6d
Font Library: delete child font faces and font assets when deleting p…
creativecoder Jan 16, 2024
492a3ee
Font Library: refactor client side install functions to work with rev…
jffng Jan 17, 2024
9ebc25f
Cleanup/font library view error handling (#57926)
pbking Jan 17, 2024
92ff955
Fix unique key prop warning when opening modal
mikachan Jan 17, 2024
5b57f3e
Add key props to FontsGrid children
mikachan Jan 17, 2024
8a5efd4
Font Faces endpoint: prevent creating font faces with duplicate setti…
creativecoder Jan 17, 2024
3a739bc
Font Library: Update uninstall/delete on client side (#57932)
mikachan Jan 18, 2024
22253e5
Add slug/id back to FontCollection
mikachan Jan 18, 2024
c646e3d
Change tabsFromCollections inline with Font Collections PR
mikachan Jan 18, 2024
2d3abce
Use child.key for key prop in FontsGrid
mikachan Jan 18, 2024
c6e0fbb
Update packages/edit-site/src/components/global-styles/font-library-m…
mikachan Jan 18, 2024
74be330
Merge branch 'trunk' into try/font-library-refactor
mikachan Jan 18, 2024
a666bb5
Font Library: address JS feedback in #57688 (#57961)
mikachan Jan 18, 2024
32689e1
Font Library REST API endpoints: address initial feedback from featur…
creativecoder Jan 19, 2024
2aa9c64
Font Library: font collection refactor to use the new schema (#57884)
matiasbenedetto Jan 19, 2024
fe2c0ae
Merge branch 'try/font-library-refactor' into font-library/fix-react-…
mikachan Jan 19, 2024
ab889a7
Merge branch 'try/font-library-refactor' into font-library/fix-react-…
mikachan Jan 23, 2024
75c27e5
Remove old WP_REST_Autosave_Fonts_Controller class
mikachan Jan 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Font Library: refactor client side install functions to work with rev…
…ised API (#57844)

* Add batchInstallFontFaces function and related plumbing.

* Fix resolver name.

* Add embedding and rebuild theme.json settings for fontFamily.

* Handle responses directly, add to collection before activating. Remove unused test.

* Remove getIntersectingFontFaces.

* Check for existing font family before installing.

* Reference src, not uploadedFile key.

Co-authored-by: Matias Benedetto <[email protected]>

* Check for existing font family using GET /font-families?slug=.

* Filter already installed font faces (determined by matching fontWeight AND fontStyle)

---------

Co-authored-by: Matias Benedetto <[email protected]>
Co-authored-by: Jason Crist <[email protected]>
  • Loading branch information
3 people authored Jan 17, 2024
commit 492a3eef6783ca80d9c6bb4a35ee6c674c7d4ae4
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import {
* Internal dependencies
*/
import {
fetchInstallFont,
fetchGetFontFamilyBySlug,
fetchInstallFontFamily,
fetchUninstallFonts,
fetchFontCollections,
fetchFontCollection,
Expand All @@ -26,10 +27,11 @@ import {
mergeFontFamilies,
loadFontFaceInBrowser,
getDisplaySrcFromFontFace,
makeFormDataFromFontFamily,
makeFontFacesFormData,
makeFontFamilyFormData,
batchInstallFontFaces,
} from './utils';
import { toggleFont } from './utils/toggleFont';
import getIntersectingFontFaces from './utils/get-intersecting-font-faces';

export const FontLibraryContext = createContext( {} );

Expand Down Expand Up @@ -60,12 +62,19 @@ function FontLibraryProvider( { children } ) {
records: libraryPosts = [],
isResolving: isResolvingLibrary,
hasResolved: hasResolvedLibrary,
} = useEntityRecords( 'postType', 'wp_font_family', { refreshKey } );
} = useEntityRecords( 'postType', 'wp_font_family', {
refreshKey,
_embed: true,
} );

const libraryFonts =
( libraryPosts || [] ).map( ( post ) =>
JSON.parse( post.content.raw )
) || [];
( libraryPosts || [] ).map( ( post ) => {
post.font_family_settings.fontFace =
post?._embedded?.font_faces.map(
( face ) => face.font_face_settings
) || [];
return post.font_family_settings;
} ) || [];

// Global Styles (settings) font families
const [ fontFamilies, setFontFamilies ] = useGlobalSetting(
Expand Down Expand Up @@ -195,32 +204,108 @@ function FontLibraryProvider( { children } ) {
async function installFont( font ) {
setIsInstalling( true );
try {
// Prepare formData to install.
const formData = makeFormDataFromFontFamily( font );
// Get the ID of the font family post, if it is already installed.
let installedFontFamily = await fetchGetFontFamilyBySlug(
font.slug
)
.then( ( response ) => {
if ( ! response || response.length === 0 ) {
return null;
}
const fontFamilyPost = response[ 0 ];
return {
id: fontFamilyPost.id,
...fontFamilyPost.font_family_settings,
fontFace:
fontFamilyPost?._embedded?.font_faces.map(
( face ) => face.font_face_settings
) || [],
};
} )
.catch( ( e ) => {
// eslint-disable-next-line no-console
console.error( e );
return null;
} );

// Otherwise, install it.
if ( ! installedFontFamily ) {
const fontFamilyFormData = makeFontFamilyFormData( font );
// Prepare font family form data to install.
installedFontFamily = await fetchInstallFontFamily(
fontFamilyFormData
)
.then( ( response ) => {
return {
id: response.id,
...response.font_face_settings,
fontFace: [],
};
} )
.catch( ( e ) => {
throw Error( e.message );
} );
}

// Filter Font Faces that have already been installed
// We determine that by comparing the fontWeight and fontStyle
font.fontFace = font.fontFace.filter( ( fontFaceToInstall ) => {
return (
-1 ===
installedFontFamily.fontFace.findIndex(
( installedFontFace ) => {
return (
installedFontFace.fontWeight ===
fontFaceToInstall.fontWeight &&
installedFontFace.fontStyle ===
fontFaceToInstall.fontStyle
);
}
)
);
} );

if ( font.fontFace.length === 0 ) {
// Looks like we're only trying to install fonts that are already installed.
// Let's not do that.
// TODO: Exit with an error message?
return {
errors: [ 'All font faces are already installed' ],
};
}

// Prepare font faces form data to install.
const fontFacesFormData = makeFontFacesFormData( font );

// Install the fonts (upload the font files to the server and create the post in the database).
const response = await fetchInstallFont( formData );
const fontsInstalled = response?.successes || [];
// Get intersecting font faces between the fonts we tried to installed and the fonts that were installed
// (to avoid activating a non installed font).
const fontToBeActivated = getIntersectingFontFaces(
fontsInstalled,
[ font ]
const response = await batchInstallFontFaces(
installedFontFamily.id,
fontFacesFormData
);
// Activate the font families (add the font families to the global styles).
activateCustomFontFamilies( fontToBeActivated );

const fontFacesInstalled = response?.successes || [];

// Rebuild fontFace settings
font.fontFace =
fontFacesInstalled.map( ( face ) => {
return face.font_face_settings;
} ) || [];

// Activate the font family (add the font family to the global styles).
activateCustomFontFamilies( [ font ] );
// Save the global styles to the database.
saveSpecifiedEntityEdits( 'root', 'globalStyles', globalStylesId, [
'settings.typography.fontFamilies',
] );
refreshLibrary();
setIsInstalling( false );

return response;
} catch ( error ) {
setIsInstalling( false );
return {
errors: [ error ],
};
} finally {
setIsInstalling( false );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/
import apiFetch from '@wordpress/api-fetch';

export async function fetchInstallFont( data ) {
export async function fetchInstallFontFamily( data ) {
const config = {
path: '/wp/v2/font-families',
method: 'POST',
Expand All @@ -16,6 +16,23 @@ export async function fetchInstallFont( data ) {
return apiFetch( config );
}

export async function fetchInstallFontFace( fontFamilyId, data ) {
const config = {
path: `/wp/v2/font-families/${ fontFamilyId }/font-faces`,
method: 'POST',
body: data,
};
return apiFetch( config );
}

export async function fetchGetFontFamilyBySlug( slug ) {
const config = {
path: `/wp/v2/font-families?slug=${ slug }&_embed=true`,
method: 'GET',
};
return apiFetch( config );
}

export async function fetchUninstallFonts( fonts ) {
const data = {
font_families: fonts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { privateApis as componentsPrivateApis } from '@wordpress/components';
*/
import { FONT_WEIGHTS, FONT_STYLES } from './constants';
import { unlock } from '../../../../lock-unlock';
import { fetchInstallFontFace } from '../resolvers';

/**
* Browser dependencies
Expand Down Expand Up @@ -135,39 +136,84 @@ export function getDisplaySrcFromFontFace( input, urlPrefix ) {
return src;
}

export function makeFormDataFromFontFamily( fontFamily ) {
export function makeFontFamilyFormData( fontFamily ) {
const formData = new FormData();
const { kebabCase } = unlock( componentsPrivateApis );

const newFontFamily = {
...fontFamily,
const { fontFace, category, ...familyWithValidParameters } = fontFamily;
const fontFamilySettings = {
...familyWithValidParameters,
slug: kebabCase( fontFamily.slug ),
};

if ( newFontFamily?.fontFace ) {
const newFontFaces = newFontFamily.fontFace.map(
( face, faceIndex ) => {
if ( face.file ) {
// Slugified file name because the it might contain spaces or characters treated differently on the server.
const fileId = `file-${ faceIndex }`;
// Add the files to the formData
formData.append( fileId, face.file, face.file.name );
// remove the file object from the face object the file is referenced by the uploadedFile key
const { file, ...faceWithoutFileProperty } = face;
const newFace = {
...faceWithoutFileProperty,
uploadedFile: fileId,
};
return newFace;
}
return face;
formData.append(
'font_family_settings',
JSON.stringify( fontFamilySettings )
);
return formData;
}

export function makeFontFacesFormData( font ) {
if ( font?.fontFace ) {
const fontFacesFormData = font.fontFace.map( ( face, faceIndex ) => {
const formData = new FormData();
if ( face.file ) {
// Slugified file name because the it might contain spaces or characters treated differently on the server.
const fileId = `file-${ faceIndex }`;
// Add the files to the formData
formData.append( fileId, face.file, face.file.name );
// remove the file object from the face object the file is referenced in src
const { file, ...faceWithoutFileProperty } = face;
const fontFaceSettings = {
...faceWithoutFileProperty,
src: fileId,
};
formData.append(
'font_face_settings',
JSON.stringify( fontFaceSettings )
);
} else {
formData.append( 'font_face_settings', JSON.stringify( face ) );
}
);
newFontFamily.fontFace = newFontFaces;
return formData;
} );

return fontFacesFormData;
}
}

formData.append( 'font_family_settings', JSON.stringify( newFontFamily ) );
return formData;
export async function batchInstallFontFaces( fontFamilyId, fontFacesData ) {
const promises = fontFacesData.map( ( faceData ) =>
fetchInstallFontFace( fontFamilyId, faceData )
);
const responses = await Promise.allSettled( promises );

const results = {
errors: [],
successes: [],
};

responses.forEach( ( result, index ) => {
if ( result.status === 'fulfilled' ) {
const response = result.value;
if ( response.id ) {
results.successes.push( response );
} else {
results.errors.push( {
data: fontFacesData[ index ],
message: `Error: ${ response.message }`,
} );
}
} else {
// Handle network errors or other fetch-related errors
results.errors.push( {
data: fontFacesData[ index ],
error: `Fetch error: ${ result.reason }`,
} );
}
} );

return results;
}

/*
Expand Down

This file was deleted.