From b5ef4d4a9965c27a63d1b6e7a4d20979b6a040e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Wed, 14 Oct 2020 17:53:02 +0300 Subject: [PATCH 1/2] Latest posts: use hooks + API v2 --- .../block-library/src/latest-posts/edit.js | 819 +++++++++--------- 1 file changed, 404 insertions(+), 415 deletions(-) diff --git a/packages/block-library/src/latest-posts/edit.js b/packages/block-library/src/latest-posts/edit.js index 50a0e1d05808c..199e047a3fcd9 100644 --- a/packages/block-library/src/latest-posts/edit.js +++ b/packages/block-library/src/latest-posts/edit.js @@ -7,7 +7,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { Component, RawHTML } from '@wordpress/element'; +import { useState, RawHTML, useEffect, useRef } from '@wordpress/element'; import { BaseControl, PanelBody, @@ -28,6 +28,7 @@ import { BlockAlignmentToolbar, BlockControls, __experimentalImageSizeControl as ImageSizeControl, + useBlockProps, } from '@wordpress/block-editor'; import { withSelect } from '@wordpress/data'; import { pin, list, grid } from '@wordpress/icons'; @@ -51,475 +52,463 @@ const USERS_LIST_QUERY = { per_page: -1, }; -class LatestPostsEdit extends Component { - constructor() { - super( ...arguments ); - this.state = { - categoriesList: [], - authorList: [], - }; - } +function LatestPostsEdit( { + attributes, + setAttributes, + imageSizeOptions, + latestPosts, + defaultImageWidth, + defaultImageHeight, +} ) { + const [ categoriesList, setCategoriesList ] = useState( [] ); + const [ authorList, setAuthorList ] = useState( [] ); + const { + displayFeaturedImage, + displayPostContentRadio, + displayPostContent, + displayPostDate, + displayAuthor, + postLayout, + columns, + order, + orderBy, + categories, + selectedAuthor, + postsToShow, + excerptLength, + featuredImageAlign, + featuredImageSizeSlug, + featuredImageSizeWidth, + featuredImageSizeHeight, + addLinkToFeaturedImage, + } = attributes; + const categorySuggestions = categoriesList.reduce( + ( accumulator, category ) => ( { + ...accumulator, + [ category.name ]: category, + } ), + {} + ); + const selectCategories = ( tokens ) => { + const hasNoSuggestion = tokens.some( + ( token ) => + typeof token === 'string' && ! categorySuggestions[ token ] + ); + if ( hasNoSuggestion ) { + return; + } + // Categories that are already will be objects, while new additions will be strings (the name). + // allCategories nomalizes the array so that they are all objects. + const allCategories = tokens.map( ( token ) => { + return typeof token === 'string' + ? categorySuggestions[ token ] + : token; + } ); + // We do nothing if the category is not selected + // from suggestions. + if ( includes( allCategories, null ) ) { + return false; + } + setAttributes( { categories: allCategories } ); + }; - componentDidMount() { - this.isStillMounted = true; - this.fetchRequest = apiFetch( { + const isStillMounted = useRef(); + + useEffect( () => { + isStillMounted.current = true; + + apiFetch( { path: addQueryArgs( `/wp/v2/categories`, CATEGORIES_LIST_QUERY ), } ) - .then( ( categoriesList ) => { - if ( this.isStillMounted ) { - this.setState( { categoriesList } ); + .then( ( data ) => { + if ( isStillMounted.current ) { + setCategoriesList( data ); } } ) .catch( () => { - if ( this.isStillMounted ) { - this.setState( { categoriesList: [] } ); + if ( isStillMounted.current ) { + setCategoriesList( [] ); } } ); - this.fetchRequest = apiFetch( { + apiFetch( { path: addQueryArgs( `/wp/v2/users`, USERS_LIST_QUERY ), } ) - .then( ( authorList ) => { - if ( this.isStillMounted ) { - this.setState( { authorList } ); + .then( ( data ) => { + if ( isStillMounted.current ) { + setAuthorList( data ); } } ) .catch( () => { - if ( this.isStillMounted ) { - this.setState( { authorList: [] } ); + if ( isStillMounted.current ) { + setAuthorList( [] ); } } ); - } - componentWillUnmount() { - this.isStillMounted = false; - } - - render() { - const { - attributes, - setAttributes, - imageSizeOptions, - latestPosts, - defaultImageWidth, - defaultImageHeight, - } = this.props; - const { categoriesList, authorList } = this.state; - const { - displayFeaturedImage, - displayPostContentRadio, - displayPostContent, - displayPostDate, - displayAuthor, - postLayout, - columns, - order, - orderBy, - categories, - selectedAuthor, - postsToShow, - excerptLength, - featuredImageAlign, - featuredImageSizeSlug, - featuredImageSizeWidth, - featuredImageSizeHeight, - addLinkToFeaturedImage, - } = attributes; - const categorySuggestions = categoriesList.reduce( - ( accumulator, category ) => ( { - ...accumulator, - [ category.name ]: category, - } ), - {} - ); - const selectCategories = ( tokens ) => { - const hasNoSuggestion = tokens.some( - ( token ) => - typeof token === 'string' && ! categorySuggestions[ token ] - ); - if ( hasNoSuggestion ) { - return; - } - // Categories that are already will be objects, while new additions will be strings (the name). - // allCategories nomalizes the array so that they are all objects. - const allCategories = tokens.map( ( token ) => { - return typeof token === 'string' - ? categorySuggestions[ token ] - : token; - } ); - // We do nothing if the category is not selected - // from suggestions. - if ( includes( allCategories, null ) ) { - return false; - } - setAttributes( { categories: allCategories } ); + return () => { + isStillMounted.current = false; }; + }, [] ); - const inspectorControls = ( - - - + + + setAttributes( { displayPostContent: value } ) + } + /> + { displayPostContent && ( + - setAttributes( { displayPostContent: value } ) + setAttributes( { + displayPostContentRadio: value, + } ) } /> - { displayPostContent && ( - - setAttributes( { - displayPostContentRadio: value, - } ) + setAttributes( { excerptLength: value } ) } + min={ MIN_EXCERPT_LENGTH } + max={ MAX_EXCERPT_LENGTH } /> ) } - { displayPostContent && - displayPostContentRadio === 'excerpt' && ( - - setAttributes( { excerptLength: value } ) - } - min={ MIN_EXCERPT_LENGTH } - max={ MAX_EXCERPT_LENGTH } - /> - ) } - + - - - setAttributes( { displayAuthor: value } ) - } - /> - - setAttributes( { displayPostDate: value } ) - } - /> - + + + setAttributes( { displayAuthor: value } ) + } + /> + + setAttributes( { displayPostDate: value } ) + } + /> + - - - setAttributes( { displayFeaturedImage: value } ) - } - /> - { displayFeaturedImage && ( - <> - { - const newAttrs = {}; - if ( value.hasOwnProperty( 'width' ) ) { - newAttrs.featuredImageSizeWidth = - value.width; - } - if ( value.hasOwnProperty( 'height' ) ) { - newAttrs.featuredImageSizeHeight = - value.height; - } - setAttributes( newAttrs ); - } } - slug={ featuredImageSizeSlug } - width={ featuredImageSizeWidth } - height={ featuredImageSizeHeight } - imageWidth={ defaultImageWidth } - imageHeight={ defaultImageHeight } - imageSizeOptions={ imageSizeOptions } - onChangeImage={ ( value ) => - setAttributes( { - featuredImageSizeSlug: value, - featuredImageSizeWidth: undefined, - featuredImageSizeHeight: undefined, - } ) + + + setAttributes( { displayFeaturedImage: value } ) + } + /> + { displayFeaturedImage && ( + <> + { + const newAttrs = {}; + if ( value.hasOwnProperty( 'width' ) ) { + newAttrs.featuredImageSizeWidth = + value.width; } - /> - - - { __( 'Image alignment' ) } - - - setAttributes( { - featuredImageAlign: value, - } ) - } - controls={ [ 'left', 'center', 'right' ] } - isCollapsed={ false } - /> - - + setAttributes( { + featuredImageSizeSlug: value, + featuredImageSizeWidth: undefined, + featuredImageSizeHeight: undefined, + } ) + } + /> + + + { __( 'Image alignment' ) } + + setAttributes( { - addLinkToFeaturedImage: value, + featuredImageAlign: value, } ) } + controls={ [ 'left', 'center', 'right' ] } + isCollapsed={ false } /> - - ) } - + + + setAttributes( { + addLinkToFeaturedImage: value, + } ) + } + /> + + ) } + - - - setAttributes( { order: value } ) - } - onOrderByChange={ ( value ) => - setAttributes( { orderBy: value } ) + + + setAttributes( { order: value } ) + } + onOrderByChange={ ( value ) => + setAttributes( { orderBy: value } ) + } + onNumberOfItemsChange={ ( value ) => + setAttributes( { postsToShow: value } ) + } + categorySuggestions={ categorySuggestions } + onCategoryChange={ selectCategories } + selectedCategories={ categories } + onAuthorChange={ ( value ) => + setAttributes( { + selectedAuthor: + '' !== value ? Number( value ) : undefined, + } ) + } + authorList={ authorList } + selectedAuthorId={ selectedAuthor } + /> + + { postLayout === 'grid' && ( + + setAttributes( { columns: value } ) } - onNumberOfItemsChange={ ( value ) => - setAttributes( { postsToShow: value } ) + min={ 2 } + max={ + ! hasPosts + ? MAX_POSTS_COLUMNS + : Math.min( + MAX_POSTS_COLUMNS, + latestPosts.length + ) } - categorySuggestions={ categorySuggestions } - onCategoryChange={ selectCategories } - selectedCategories={ categories } - onAuthorChange={ ( value ) => - setAttributes( { - selectedAuthor: - '' !== value ? Number( value ) : undefined, - } ) - } - authorList={ authorList } - selectedAuthorId={ selectedAuthor } + required /> + ) } + + + ); - { postLayout === 'grid' && ( - - setAttributes( { columns: value } ) - } - min={ 2 } - max={ - ! hasPosts - ? MAX_POSTS_COLUMNS - : Math.min( - MAX_POSTS_COLUMNS, - latestPosts.length - ) - } - required - /> + const blockProps = useBlockProps(); + + const hasPosts = Array.isArray( latestPosts ) && latestPosts.length; + if ( ! hasPosts ) { + return ( +
+ { inspectorControls } + + { ! Array.isArray( latestPosts ) ? ( + + ) : ( + __( 'No posts found.' ) ) } - - + +
); + } - const hasPosts = Array.isArray( latestPosts ) && latestPosts.length; - if ( ! hasPosts ) { - return ( - <> - { inspectorControls } - - { ! Array.isArray( latestPosts ) ? ( - - ) : ( - __( 'No posts found.' ) - ) } - - - ); - } - - // Removing posts from display should be instant. - const displayPosts = - latestPosts.length > postsToShow - ? latestPosts.slice( 0, postsToShow ) - : latestPosts; + // Removing posts from display should be instant. + const displayPosts = + latestPosts.length > postsToShow + ? latestPosts.slice( 0, postsToShow ) + : latestPosts; - const layoutControls = [ - { - icon: list, - title: __( 'List view' ), - onClick: () => setAttributes( { postLayout: 'list' } ), - isActive: postLayout === 'list', - }, - { - icon: grid, - title: __( 'Grid view' ), - onClick: () => setAttributes( { postLayout: 'grid' } ), - isActive: postLayout === 'grid', - }, - ]; + const layoutControls = [ + { + icon: list, + title: __( 'List view' ), + onClick: () => setAttributes( { postLayout: 'list' } ), + isActive: postLayout === 'list', + }, + { + icon: grid, + title: __( 'Grid view' ), + onClick: () => setAttributes( { postLayout: 'grid' } ), + isActive: postLayout === 'grid', + }, + ]; - const dateFormat = __experimentalGetSettings().formats.date; + const dateFormat = __experimentalGetSettings().formats.date; - return ( - <> - { inspectorControls } - - - - + + ); } export default withSelect( ( select, props ) => { From f7cef455d4e236ec3aaba3c6f17950413bed4995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Thu, 15 Oct 2020 10:53:22 +0300 Subject: [PATCH 2/2] Use useSelect --- .../block-library/src/latest-posts/edit.js | 183 +++++++++--------- 1 file changed, 94 insertions(+), 89 deletions(-) diff --git a/packages/block-library/src/latest-posts/edit.js b/packages/block-library/src/latest-posts/edit.js index 199e047a3fcd9..c5f012764676a 100644 --- a/packages/block-library/src/latest-posts/edit.js +++ b/packages/block-library/src/latest-posts/edit.js @@ -30,7 +30,7 @@ import { __experimentalImageSizeControl as ImageSizeControl, useBlockProps, } from '@wordpress/block-editor'; -import { withSelect } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { pin, list, grid } from '@wordpress/icons'; /** @@ -52,17 +52,13 @@ const USERS_LIST_QUERY = { per_page: -1, }; -function LatestPostsEdit( { - attributes, - setAttributes, - imageSizeOptions, - latestPosts, - defaultImageWidth, - defaultImageHeight, -} ) { - const [ categoriesList, setCategoriesList ] = useState( [] ); - const [ authorList, setAuthorList ] = useState( [] ); +export default function LatestPostsEdit( { attributes, setAttributes } ) { const { + postsToShow, + order, + orderBy, + categories, + selectedAuthor, displayFeaturedImage, displayPostContentRadio, displayPostContent, @@ -70,11 +66,6 @@ function LatestPostsEdit( { displayAuthor, postLayout, columns, - order, - orderBy, - categories, - selectedAuthor, - postsToShow, excerptLength, featuredImageAlign, featuredImageSizeSlug, @@ -82,6 +73,93 @@ function LatestPostsEdit( { featuredImageSizeHeight, addLinkToFeaturedImage, } = attributes; + const { + imageSizeOptions, + latestPosts, + defaultImageWidth, + defaultImageHeight, + } = useSelect( + ( select ) => { + const { getEntityRecords, getMedia } = select( 'core' ); + const { getSettings } = select( 'core/block-editor' ); + const { imageSizes, imageDimensions } = getSettings(); + const catIds = + categories && categories.length > 0 + ? categories.map( ( cat ) => cat.id ) + : []; + const latestPostsQuery = pickBy( + { + categories: catIds, + author: selectedAuthor, + order, + orderby: orderBy, + per_page: postsToShow, + }, + ( value ) => ! isUndefined( value ) + ); + + const posts = getEntityRecords( + 'postType', + 'post', + latestPostsQuery + ); + + return { + defaultImageWidth: get( + imageDimensions, + [ featuredImageSizeSlug, 'width' ], + 0 + ), + defaultImageHeight: get( + imageDimensions, + [ featuredImageSizeSlug, 'height' ], + 0 + ), + imageSizeOptions: imageSizes + .filter( ( { slug } ) => slug !== 'full' ) + .map( ( { name, slug } ) => ( { + value: slug, + label: name, + } ) ), + latestPosts: ! Array.isArray( posts ) + ? posts + : posts.map( ( post ) => { + if ( ! post.featured_media ) return post; + + const image = getMedia( post.featured_media ); + let url = get( + image, + [ + 'media_details', + 'sizes', + featuredImageSizeSlug, + 'source_url', + ], + null + ); + if ( ! url ) { + url = get( image, 'source_url', null ); + } + const featuredImageInfo = { + url, + // eslint-disable-next-line camelcase + alt: image?.alt_text, + }; + return { ...post, featuredImageInfo }; + } ), + }; + }, + [ + featuredImageSizeSlug, + postsToShow, + order, + orderBy, + categories, + selectedAuthor, + ] + ); + const [ categoriesList, setCategoriesList ] = useState( [] ); + const [ authorList, setAuthorList ] = useState( [] ); const categorySuggestions = categoriesList.reduce( ( accumulator, category ) => ( { ...accumulator, @@ -510,76 +588,3 @@ function LatestPostsEdit( { ); } - -export default withSelect( ( select, props ) => { - const { - featuredImageSizeSlug, - postsToShow, - order, - orderBy, - categories, - selectedAuthor, - } = props.attributes; - const { getEntityRecords, getMedia } = select( 'core' ); - const { getSettings } = select( 'core/block-editor' ); - const { imageSizes, imageDimensions } = getSettings(); - const catIds = - categories && categories.length > 0 - ? categories.map( ( cat ) => cat.id ) - : []; - const latestPostsQuery = pickBy( - { - categories: catIds, - author: selectedAuthor, - order, - orderby: orderBy, - per_page: postsToShow, - }, - ( value ) => ! isUndefined( value ) - ); - - const posts = getEntityRecords( 'postType', 'post', latestPostsQuery ); - const imageSizeOptions = imageSizes - .filter( ( { slug } ) => slug !== 'full' ) - .map( ( { name, slug } ) => ( { value: slug, label: name } ) ); - - return { - defaultImageWidth: get( - imageDimensions, - [ featuredImageSizeSlug, 'width' ], - 0 - ), - defaultImageHeight: get( - imageDimensions, - [ featuredImageSizeSlug, 'height' ], - 0 - ), - imageSizeOptions, - latestPosts: ! Array.isArray( posts ) - ? posts - : posts.map( ( post ) => { - if ( ! post.featured_media ) return post; - - const image = getMedia( post.featured_media ); - let url = get( - image, - [ - 'media_details', - 'sizes', - featuredImageSizeSlug, - 'source_url', - ], - null - ); - if ( ! url ) { - url = get( image, 'source_url', null ); - } - const featuredImageInfo = { - url, - // eslint-disable-next-line camelcase - alt: image?.alt_text, - }; - return { ...post, featuredImageInfo }; - } ), - }; -} )( LatestPostsEdit );