Skip to content

Commit

Permalink
Gutenberg: Update Related Posts block to pull real time related posts (
Browse files Browse the repository at this point in the history
…#27659)

* Gutenberg: Fetch real time related posts

* Gutenberg: Use realtime related posts in block preview

* Gutenberg: No request duplication, refresh after save

* Use dates provided from the API

* Use human readable dates in example posts

* Fix image schema in example posts

* Handle related posts request failures
  • Loading branch information
tyxla authored Oct 17, 2018
1 parent 59ff2ef commit a5c963f
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 113 deletions.
18 changes: 12 additions & 6 deletions client/gutenberg/extensions/related-posts/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,26 @@ export const MAX_POSTS_TO_SHOW = 3;
export const DEFAULT_POSTS = [
{
title: 'Big iPhone/iPad Update Now Available',
image: 'https://wordpress.com/calypso/images/related-posts/cat-blog.png',
date: '2018-08-03',
img: {
src: 'https://wordpress.com/calypso/images/related-posts/cat-blog.png',
},
date: 'August 3, 2018',
context: 'In "Mobile"',
},
{
title: 'The WordPress for Android App Gets a Big Facelift',
image: 'https://wordpress.com/calypso/images/related-posts/devices.jpg',
date: '2018-08-02',
img: {
src: 'https://wordpress.com/calypso/images/related-posts/devices.jpg',
},
date: 'August 2, 2018',
context: 'In "Mobile"',
},
{
title: 'Upgrade Focus: VideoPress For Weddings',
image: 'https://wordpress.com/calypso/images/related-posts/mobile-wedding.jpg',
date: '2018-08-05',
img: {
src: 'https://wordpress.com/calypso/images/related-posts/mobile-wedding.jpg',
},
date: 'August 5, 2018',
context: 'In "Upgrade"',
},
];
269 changes: 162 additions & 107 deletions client/gutenberg/extensions/related-posts/edit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,122 +3,177 @@
/**
* External dependencies
*/
import apiFetch from '@wordpress/api-fetch';
import classNames from 'classnames';
import { __ } from '@wordpress/i18n';
import { Fragment } from '@wordpress/element';
import { Component, Fragment } from '@wordpress/element';
import { BlockAlignmentToolbar, BlockControls, InspectorControls } from '@wordpress/editor';
import { moment } from '@wordpress/date';
import { Button, PanelBody, RangeControl, ToggleControl, Toolbar } from '@wordpress/components';
import { withSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { ALIGNMENT_OPTIONS, DEFAULT_POSTS, MAX_POSTS_TO_SHOW } from './constants';

export default ( { attributes, className, setAttributes } ) => {
const {
align,
displayContext,
displayDate,
displayThumbnails,
postLayout,
postsToShow,
} = attributes;

const layoutControls = [
{
icon: 'grid-view',
title: __( 'Grid View', 'jetpack' ),
onClick: () => setAttributes( { postLayout: 'grid' } ),
isActive: postLayout === 'grid',
},
{
icon: 'list-view',
title: __( 'List View', 'jetpack' ),
onClick: () => setAttributes( { postLayout: 'list' } ),
isActive: postLayout === 'list',
},
];

const displayPosts = DEFAULT_POSTS.slice( 0, postsToShow );

return (
<Fragment>
<InspectorControls>
<PanelBody title={ __( 'Related Posts Settings', 'jetpack' ) }>
<ToggleControl
label={ __( 'Display thumbnails', 'jetpack' ) }
checked={ displayThumbnails }
onChange={ value => setAttributes( { displayThumbnails: value } ) }
/>
<ToggleControl
label={ __( 'Display date', 'jetpack' ) }
checked={ displayDate }
onChange={ value => setAttributes( { displayDate: value } ) }
/>
<ToggleControl
label={ __( 'Display context (category or tag)', 'jetpack' ) }
checked={ displayContext }
onChange={ value => setAttributes( { displayContext: value } ) }
/>
<RangeControl
label={ __( 'Number of posts', 'jetpack' ) }
value={ postsToShow }
onChange={ value =>
setAttributes( { postsToShow: Math.min( value, MAX_POSTS_TO_SHOW ) } )
}
min={ 1 }
max={ MAX_POSTS_TO_SHOW }
class RelatedPostsEdit extends Component {
state = {
posts: [],
fetchingPosts: false,
};

componentDidMount() {
this.fetchPosts();
}

componentDidUpdate( prevProps ) {
if ( prevProps.isSaving && ! this.props.isSaving ) {
this.fetchPosts();
}
}

fetchPosts() {
const { postId } = this.props;
const { fetchingPosts } = this.state;

if ( ! postId || fetchingPosts ) {
return;
}

this.setState( {
fetchingPosts: true,
} );

apiFetch( {
path: '/jetpack/v4/site/posts/related?http_envelope=1&post_id=' + postId,
} )
.then( response => {
this.setState( {
posts: response.posts,
fetchingPosts: false,
} );
} )
.catch( () => {
this.setState( {
fetchingPosts: false,
} );
} );
}

render() {
const { attributes, className, setAttributes } = this.props;
const { posts } = this.state;
const {
align,
displayContext,
displayDate,
displayThumbnails,
postLayout,
postsToShow,
} = attributes;

const layoutControls = [
{
icon: 'grid-view',
title: __( 'Grid View', 'jetpack' ),
onClick: () => setAttributes( { postLayout: 'grid' } ),
isActive: postLayout === 'grid',
},
{
icon: 'list-view',
title: __( 'List View', 'jetpack' ),
onClick: () => setAttributes( { postLayout: 'list' } ),
isActive: postLayout === 'list',
},
];

const postsToDisplay = posts.length ? posts : DEFAULT_POSTS;
const displayPosts = postsToDisplay.slice( 0, postsToShow );

return (
<Fragment>
<InspectorControls>
<PanelBody title={ __( 'Related Posts Settings', 'jetpack' ) }>
<ToggleControl
label={ __( 'Display thumbnails', 'jetpack' ) }
checked={ displayThumbnails }
onChange={ value => setAttributes( { displayThumbnails: value } ) }
/>
<ToggleControl
label={ __( 'Display date', 'jetpack' ) }
checked={ displayDate }
onChange={ value => setAttributes( { displayDate: value } ) }
/>
<ToggleControl
label={ __( 'Display context (category or tag)', 'jetpack' ) }
checked={ displayContext }
onChange={ value => setAttributes( { displayContext: value } ) }
/>
<RangeControl
label={ __( 'Number of posts', 'jetpack' ) }
value={ postsToShow }
onChange={ value =>
setAttributes( { postsToShow: Math.min( value, MAX_POSTS_TO_SHOW ) } )
}
min={ 1 }
max={ MAX_POSTS_TO_SHOW }
/>
</PanelBody>
</InspectorControls>

<BlockControls>
<BlockAlignmentToolbar
value={ align }
onChange={ nextAlign => {
setAttributes( { align: nextAlign } );
} }
controls={ ALIGNMENT_OPTIONS }
/>
</PanelBody>
</InspectorControls>

<BlockControls>
<BlockAlignmentToolbar
value={ align }
onChange={ nextAlign => {
setAttributes( { align: nextAlign } );
} }
controls={ ALIGNMENT_OPTIONS }
/>
<Toolbar controls={ layoutControls } />
</BlockControls>

<div
className={ classNames( `${ className }`, {
'is-grid': postLayout === 'grid',
[ `columns-${ postsToShow }` ]: postLayout === 'grid',
[ `align${ align }` ]: align,
} ) }
>
<div className={ `${ className }__preview-items` }>
{ displayPosts.map( ( post, i ) => (
<div className={ `${ className }__preview-post` } key={ i }>
{ displayThumbnails && (
<Button className={ `${ className }__preview-post-link` } isLink>
<img src={ post.image } alt={ post.title } />
</Button>
) }
<h4>
<Button className={ `${ className }__preview-post-link` } isLink>
{ post.title }
</Button>
</h4>
{ displayDate && (
<time
dateTime={ moment( post.date ).toISOString() }
className={ `${ className }__preview-post-date has-small-font-size` }
>
{ moment( post.date )
.local()
.format( 'MMMM DD, Y' ) }
</time>
) }
{ displayContext && <p>{ post.context }</p> }
</div>
) ) }
<Toolbar controls={ layoutControls } />
</BlockControls>

<div
className={ classNames( `${ className }`, {
'is-grid': postLayout === 'grid',
[ `columns-${ postsToShow }` ]: postLayout === 'grid',
[ `align${ align }` ]: align,
} ) }
>
<div className={ `${ className }__preview-items` }>
{ displayPosts.map( ( post, i ) => (
<div className={ `${ className }__preview-post` } key={ i }>
{ displayThumbnails &&
post.img &&
post.img.src && (
<Button className={ `${ className }__preview-post-link` } isLink>
<img src={ post.img.src } alt={ post.title } />
</Button>
) }
<h4>
<Button className={ `${ className }__preview-post-link` } isLink>
{ post.title }
</Button>
</h4>
{ displayDate && (
<span className={ `${ className }__preview-post-date has-small-font-size` }>
{ post.date }
</span>
) }
{ displayContext && <p>{ post.context }</p> }
</div>
) ) }
</div>
</div>
</div>
</Fragment>
);
};
</Fragment>
);
}
}

export default withSelect( select => {
const { getCurrentPostId, isSavingPost } = select( 'core/editor' );

return {
isSaving: !! isSavingPost(),
postId: getCurrentPostId(),
};
} )( RelatedPostsEdit );

0 comments on commit a5c963f

Please sign in to comment.