Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comment Template: Fix comment pagination with nested replies. #38187

Merged
merged 25 commits into from
Feb 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
cc8c199
REST: Set children attr of comments as embeddable
DAreRodz Jan 18, 2022
5b6c68c
REST: Expose some discussion settings
DAreRodz Jan 24, 2022
56e4c61
Add hook to generate comment query args
DAreRodz Jan 24, 2022
01e0c45
Fix how the default comments page index is computed
DAreRodz Jan 24, 2022
75239ad
Move REST API code to experimental
DAreRodz Jan 31, 2022
c3a0beb
Use experimental settings in Comment Template
DAreRodz Jan 31, 2022
7c7cb3d
Add `defaultPage` attribute to Comments Query Loop
DAreRodz Jan 31, 2022
73e1758
Keep other attributes when `inherit` changes
DAreRodz Feb 1, 2022
754f99d
Fix `comment_order` option usage
DAreRodz Feb 2, 2022
238e2bd
Replace `asc` with `ASC` inside `$comment_args`
DAreRodz Feb 2, 2022
fa0933c
Fix `array_reverse` and order in comment template
DAreRodz Feb 4, 2022
7f8b50e
Refactor `build_comment_query_vars` to use `inherit`
DAreRodz Feb 4, 2022
ab04e48
Simplify `build_comment_query_vars` function
DAreRodz Feb 8, 2022
817bd6c
Adapt Next and Numbers blocks to use `inherit`
DAreRodz Feb 8, 2022
02e28d8
Run `format-php` and fix lint errors
DAreRodz Feb 8, 2022
ef8bb94
Regenerate comment query loop fixtures
DAreRodz Feb 8, 2022
a1b3e4c
Update comment template PHP tests
DAreRodz Feb 8, 2022
07a2ce4
Write JSDocs and refactor Comment Template hooks
DAreRodz Feb 9, 2022
feacd2a
Use spread syntax to clone `topLevelComments`
DAreRodz Feb 13, 2022
ae28650
Simplify comments.
DAreRodz Feb 13, 2022
91e7a7a
Adapt some code after rebase
DAreRodz Feb 13, 2022
09f6402
Fix comment page index for oldest comments
DAreRodz Feb 14, 2022
d4483b4
Pass missing context to Comment Pagination blocks
DAreRodz Feb 14, 2022
a8e1477
Fix order when `inherit` is true
DAreRodz Feb 14, 2022
3cd373d
Add `paginationArrow` back to Pagination Next
DAreRodz Feb 16, 2022
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
2 changes: 1 addition & 1 deletion docs/reference-guides/core-blocks.md
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ An advanced block that allows displaying post comments based on different query
- **Name:** core/comments-query-loop - **Name:** core/comments-query-loop
- **Category:** theme - **Category:** theme
- **Supports:** align (full, wide), color (background, gradients, link, text), ~~html~~ - **Supports:** align (full, wide), color (background, gradients, link, text), ~~html~~
- **Attributes:** inherit, order, perPage, tagName - **Attributes:** defaultPage, inherit, order, perPage, tagName


## Cover ## Cover


Expand Down
62 changes: 47 additions & 15 deletions lib/compat/experimental/blocks.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function build_comment_query_vars_from_block( $block ) {


$comment_args = array( $comment_args = array(
'orderby' => 'comment_date_gmt', 'orderby' => 'comment_date_gmt',
'order' => 'ASC',
'status' => 'approve', 'status' => 'approve',
'no_found_rows' => false, 'no_found_rows' => false,
'update_comment_meta_cache' => false, // We lazy-load comment meta for performance. 'update_comment_meta_cache' => false, // We lazy-load comment meta for performance.
Expand All @@ -34,29 +35,36 @@ function build_comment_query_vars_from_block( $block ) {
$comment_args['hierarchical'] = false; $comment_args['hierarchical'] = false;
} }


$per_page = ! empty( $block->context['comments/perPage'] ) ? (int) $block->context['comments/perPage'] : 0; $inherit = ! empty( $block->context['comments/inherit'] );
if ( 0 === $per_page && get_option( 'page_comments' ) ) {
$per_page = (int) get_query_var( 'comments_per_page' ); $per_page = (int) get_option( 'comments_per_page' );
if ( 0 === $per_page ) { $query_per_page = (int) get_query_var( 'comments_per_page' );
$per_page = (int) get_option( 'comments_per_page' ); if ( 0 !== $query_per_page ) {
} $per_page = $query_per_page;
}
$block_per_page = ! empty( $block->context['comments/perPage'] ) ? (int) $block->context['comments/perPage'] : 0;
if ( ! $inherit && 0 !== $block_per_page ) {
$per_page = $block_per_page;
}

$default_page = get_option( 'default_comments_page' );
if ( ! $inherit && ! empty( $block->context['comments/defaultPage'] ) ) {
$default_page = $block->context['comments/defaultPage'];
} }

if ( $per_page > 0 ) { if ( $per_page > 0 ) {
$comment_args['number'] = $per_page; $comment_args['number'] = $per_page;
$page = (int) get_query_var( 'cpage' );


$page = (int) get_query_var( 'cpage' );
if ( $page ) { if ( $page ) {
$comment_args['offset'] = ( $page - 1 ) * $per_page; $comment_args['paged'] = $page;
} elseif ( 'oldest' === get_option( 'default_comments_page' ) ) { } elseif ( 'oldest' === $default_page ) {
$comment_args['offset'] = 0; $comment_args['paged'] = 1;
} elseif ( 'newest' === $default_page ) {
$comment_args['paged'] = ( new WP_Comment_Query( $comment_args ) )->max_num_pages;
} }
} }


$comment_args['order'] = ! empty( $block->context['comments/order'] ) ? $block->context['comments/order'] : null;
if ( empty( $comment_args['order'] ) && get_option( 'comment_order' ) ) {
$comment_args['order'] = get_option( 'comment_order' );
}

return $comment_args; return $comment_args;
} }
} }
Expand Down Expand Up @@ -128,3 +136,27 @@ function extend_block_editor_settings_with_discussion_settings( $settings ) {
} }
} }
add_filter( 'block_editor_settings_all', 'extend_block_editor_settings_with_discussion_settings' ); add_filter( 'block_editor_settings_all', 'extend_block_editor_settings_with_discussion_settings' );

if ( ! function_exists( 'gutenberg_rest_comment_set_children_as_embeddable' ) ) {
/**
* Mark the `children` attr of comments as embeddable so they can be included in
* REST API responses without additional requests.
*
* @return void
*/
function gutenberg_rest_comment_set_children_as_embeddable() {
add_filter(
'rest_prepare_comment',
function ( $response ) {
$links = $response->get_links();
if ( isset( $links['children'] ) ) {
$href = $links['children'][0]['href'];
$response->remove_link( 'children', $href );
$response->add_link( 'children', $href, array( 'embeddable' => true ) );
}
return $response;
}
);
}
}
add_action( 'rest_api_init', 'gutenberg_rest_comment_set_children_as_embeddable' );
8 changes: 7 additions & 1 deletion packages/block-library/src/comment-template/block.json
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@
"parent": [ "core/comments-query-loop" ], "parent": [ "core/comments-query-loop" ],
"description": "Contains the block elements used to render a comment, like the title, date, author, avatar and more.", "description": "Contains the block elements used to render a comment, like the title, date, author, avatar and more.",
"textdomain": "default", "textdomain": "default",
"usesContext": [ "comments/perPage", "postId", "comments/order" ], "usesContext": [
"comments/defaultPage",
"comments/inherit",
"comments/order",
"comments/perPage",
"postId"
],
"supports": { "supports": {
"reusable": false, "reusable": false,
"html": false, "html": false,
Expand Down
86 changes: 42 additions & 44 deletions packages/block-library/src/comment-template/edit.js
Original file line number Original file line Diff line number Diff line change
@@ -1,7 +1,7 @@
/** /**
* WordPress dependencies * WordPress dependencies
*/ */
import { useState, useMemo, memo } from '@wordpress/element'; import { useState, memo } from '@wordpress/element';
import { useSelect } from '@wordpress/data'; import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { import {
Expand All @@ -17,7 +17,7 @@ import { store as coreStore } from '@wordpress/core-data';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { convertToTree } from './util'; import { useCommentQueryArgs, useCommentTree } from './hooks';


const TEMPLATE = [ const TEMPLATE = [
[ 'core/comment-author-avatar' ], [ 'core/comment-author-avatar' ],
Expand Down Expand Up @@ -106,10 +106,10 @@ function CommentTemplateInnerBlocks( {
{ comment === ( activeComment || firstComment ) ? children : null } { comment === ( activeComment || firstComment ) ? children : null }


{ /* To avoid flicker when switching active block contexts, a preview { /* To avoid flicker when switching active block contexts, a preview
is ALWAYS rendered and the preview for the active block is hidden. is ALWAYS rendered and the preview for the active block is hidden.
This ensures that when switching the active block, the component is not This ensures that when switching the active block, the component is not
mounted again but rather it only toggles the `isHidden` prop. mounted again but rather it only toggles the `isHidden` prop.

The same strategy is used for preventing the flicker in the Post Template The same strategy is used for preventing the flicker in the Post Template
block. */ } block. */ }
<MemoizedCommentTemplatePreview <MemoizedCommentTemplatePreview
Expand Down Expand Up @@ -209,60 +209,58 @@ const CommentsList = ( {


export default function CommentTemplateEdit( { export default function CommentTemplateEdit( {
clientId, clientId,
context: { postId, 'comments/perPage': perPage, 'comments/order': order }, context: {
postId,
'comments/perPage': perPage,
'comments/order': order,
'comments/defaultPage': defaultPage,
'comments/inherit': inherit,
},
} ) { } ) {
const blockProps = useBlockProps(); const blockProps = useBlockProps();


const [ activeComment, setActiveComment ] = useState(); const [ activeComment, setActiveComment ] = useState();
const { const { commentOrder, threadCommentsDepth, threadComments } = useSelect(
commentOrder, ( select ) => {
commentsPerPage, const { getSettings } = select( blockEditorStore );
threadCommentsDepth, return getSettings().__experimentalDiscussionSettings;
threadComments, }
} = useSelect( ( select ) => { );
const { getSettings } = select( blockEditorStore );
return getSettings().__experimentalDiscussionSettings; const commentQuery = useCommentQueryArgs( {
postId,
perPage,
defaultPage,
inherit,
} ); } );


const { rawComments, blocks } = useSelect( const { topLevelComments, blocks } = useSelect(
( select ) => { ( select ) => {
const { getEntityRecords } = select( coreStore ); const { getEntityRecords } = select( coreStore );
const { getBlocks } = select( blockEditorStore ); const { getBlocks } = select( blockEditorStore );


const commentQuery = {
post: postId,
status: 'approve',
context: 'embed',
order: order || commentOrder,
};

if ( order ) {
commentQuery.order = order;
}
return { return {
rawComments: getEntityRecords( // Request only top-level comments. Replies are embedded.
'root', topLevelComments: commentQuery
'comment', ? getEntityRecords( 'root', 'comment', commentQuery )
commentQuery : null,
),
blocks: getBlocks( clientId ), blocks: getBlocks( clientId ),
}; };
}, },
[ postId, clientId, order ] [ clientId, commentQuery ]
); );
// TODO: Replicate the logic used on the server.
perPage = perPage || commentsPerPage;
// We convert the flat list of comments to tree.
// Then, we show only a maximum of `perPage` number of comments.
// This is because passing `per_page` to `getEntityRecords()` does not
// take into account nested comments.


let comments = useMemo( order = inherit || ! order ? commentOrder : order;
() => convertToTree( rawComments ).slice( 0, perPage ),
[ rawComments, perPage ] // Generate a tree structure of comment IDs.
let commentTree = useCommentTree(
// Reverse the order of top comments if needed.
order === 'desc' && topLevelComments
? [ ...topLevelComments ].reverse()
: topLevelComments
); );


if ( ! rawComments ) { if ( ! topLevelComments ) {
return ( return (
<p { ...blockProps }> <p { ...blockProps }>
<Spinner /> <Spinner />
Expand All @@ -271,20 +269,20 @@ export default function CommentTemplateEdit( {
} }


if ( ! postId ) { if ( ! postId ) {
comments = getCommentsPlaceholder( { commentTree = getCommentsPlaceholder( {
perPage, perPage,
threadComments, threadComments,
threadCommentsDepth, threadCommentsDepth,
} ); } );
} }


if ( ! comments.length ) { if ( ! commentTree.length ) {
return <p { ...blockProps }> { __( 'No results found.' ) }</p>; return <p { ...blockProps }> { __( 'No results found.' ) }</p>;
} }


return ( return (
<CommentsList <CommentsList
comments={ comments } comments={ commentTree }
blockProps={ blockProps } blockProps={ blockProps }
blocks={ blocks } blocks={ blocks }
activeComment={ activeComment } activeComment={ activeComment }
Expand Down
Loading