Skip to content

Commit

Permalink
Editor: Add wordcount and reading time info in post card (#60672)
Browse files Browse the repository at this point in the history
Co-authored-by: ntsekouras <ntsekouras@git.wordpress.org>
Co-authored-by: mcsf <mcsf@git.wordpress.org>
Co-authored-by: swissspidy <swissspidy@git.wordpress.org>
Co-authored-by: jameskoster <jameskoster@git.wordpress.org>
Co-authored-by: tobifjellner <tobifjellner@git.wordpress.org>
Co-authored-by: NekoJonez <nekojonez@git.wordpress.org>
  • Loading branch information
7 people authored Apr 15, 2024
1 parent 26e35c7 commit 3a830e2
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 31 deletions.
2 changes: 1 addition & 1 deletion packages/editor/src/components/post-actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export default function PostActions( { onActionPerformed } ) {
<DropdownMenu
trigger={
<Button
size="compact"
size="small"
icon={ moreVertical }
label={ __( 'Actions' ) }
disabled={
Expand Down
122 changes: 92 additions & 30 deletions packages/editor/src/components/post-card-panel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,57 @@ import {
} from '@wordpress/components';
import { store as coreStore } from '@wordpress/core-data';
import { useSelect } from '@wordpress/data';
import { __, sprintf } from '@wordpress/i18n';
import { __, _x, _n, sprintf } from '@wordpress/i18n';
import { humanTimeDiff } from '@wordpress/date';
import { decodeEntities } from '@wordpress/html-entities';
import { count as wordCount } from '@wordpress/wordcount';

/**
* Internal dependencies
*/
import { store as editorStore } from '../../store';
import { TEMPLATE_POST_TYPE } from '../../store/constants';
import {
TEMPLATE_POST_TYPE,
TEMPLATE_PART_POST_TYPE,
} from '../../store/constants';
import { unlock } from '../../lock-unlock';
import TemplateAreas from '../template-areas';

export default function PostCardPanel( { className, actions } ) {
const { modified, title, templateInfo, icon, postType } = useSelect(
( select ) => {
const {
getEditedPostAttribute,
getCurrentPostType,
getCurrentPostId,
__experimentalGetTemplateInfo,
} = select( editorStore );
const { getEditedEntityRecord } = select( coreStore );
const _type = getCurrentPostType();
const _id = getCurrentPostId();
const _record = getEditedEntityRecord( 'postType', _type, _id );
const _templateInfo = __experimentalGetTemplateInfo( _record );
return {
title:
_templateInfo?.title || getEditedPostAttribute( 'title' ),
modified: getEditedPostAttribute( 'modified' ),
id: _id,
postType: _type,
templateInfo: _templateInfo,
icon: unlock( select( editorStore ) ).getPostIcon( _type, {
area: _record?.area,
} ),
};
}
);
const {
modified,
title,
templateInfo,
icon,
postType,
postContent,
isPostsPage,
} = useSelect( ( select ) => {
const {
getEditedPostAttribute,
getCurrentPostType,
getCurrentPostId,
__experimentalGetTemplateInfo,
} = select( editorStore );
const { getEditedEntityRecord, getEntityRecord } = select( coreStore );
const siteSettings = getEntityRecord( 'root', 'site' );
const _type = getCurrentPostType();
const _id = getCurrentPostId();
const _record = getEditedEntityRecord( 'postType', _type, _id );
const _templateInfo = __experimentalGetTemplateInfo( _record );
return {
title: _templateInfo?.title || getEditedPostAttribute( 'title' ),
modified: getEditedPostAttribute( 'modified' ),
id: _id,
postType: _type,
templateInfo: _templateInfo,
icon: unlock( select( editorStore ) ).getPostIcon( _type, {
area: _record?.area,
} ),
isPostsPage: +_id === siteSettings?.page_for_posts,
postContent: getEditedPostAttribute( 'content' ),
};
}, [] );
const description = templateInfo?.description;
const lastEditedText =
modified &&
Expand All @@ -62,7 +74,9 @@ export default function PostCardPanel( { className, actions } ) {
__( 'Last edited %s.' ),
humanTimeDiff( modified )
);

const showPostContentInfo =
! isPostsPage &&
! [ TEMPLATE_POST_TYPE, TEMPLATE_PART_POST_TYPE ].includes( postType );
return (
<PanelBody>
<div
Expand All @@ -89,12 +103,17 @@ export default function PostCardPanel( { className, actions } ) {
{ actions }
</HStack>
<VStack className="editor-post-card-panel__content">
{ ( description || lastEditedText ) && (
{ ( description ||
lastEditedText ||
showPostContentInfo ) && (
<VStack
className="editor-post-card-panel__description"
spacing={ 2 }
>
{ description && <Text>{ description }</Text> }
{ showPostContentInfo && (
<PostContentInfo postContent={ postContent } />
) }
{ lastEditedText && (
<Text>{ lastEditedText }</Text>
) }
Expand All @@ -106,3 +125,46 @@ export default function PostCardPanel( { className, actions } ) {
</PanelBody>
);
}

// Taken from packages/editor/src/components/time-to-read/index.js.
const AVERAGE_READING_RATE = 189;

// This component renders the wordcount and reading time for the post.
function PostContentInfo( { postContent } ) {
/*
* translators: If your word count is based on single characters (e.g. East Asian characters),
* enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'.
* Do not translate into your own language.
*/
const wordCountType = _x( 'words', 'Word count type. Do not translate!' );
const wordsCounted = postContent
? wordCount( postContent, wordCountType )
: 0;
if ( ! wordsCounted ) {
return null;
}
const readingTime = Math.round( wordsCounted / AVERAGE_READING_RATE );
const wordsCountText = sprintf(
// translators: %s: the number of words in the post.
_n( '%s word', '%s words', wordsCounted ),
wordsCounted.toLocaleString()
);
const minutesText =
readingTime <= 1
? __( '1 minute' )
: sprintf(
// translators: %s: the number of minutes to read the post.
_n( '%s minute', '%s minutes', readingTime ),
readingTime.toLocaleString()
);
return (
<Text>
{ sprintf(
/* translators: 1: How many words a post has. 2: the number of minutes to read the post (e.g. 130 words, 2 minutes read time.) */
__( '%1$s, %2$s read time.' ),
wordsCountText,
minutesText
) }
</Text>
);
}

0 comments on commit 3a830e2

Please sign in to comment.