-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Add a Latest Comments block #1931
Add a Latest Comments block #1931
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@westonruter @melchoyce This is ready for an initial review. Here's how it looks:
super( ...arguments ); | ||
this.toggleDisplayAvatar = this.toggleDisplayAvatar.bind( this ); | ||
this.toggleDisplayExcerpt = this.toggleDisplayExcerpt.bind( this ); | ||
this.toggleDisplayTimestamp = this.toggleDisplayTimestamp.bind( this ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice if there was some common abstraction for toggling boolean attributes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about this? 275b7d9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👌
/> | ||
|
||
<ToggleControl | ||
label={ __( 'Display timestamp' ) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be "Display timestamp" or "Display date"? It'd be nice to have language consistent with posts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be “Display publish date” both here and on the Latest Posts block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or yeah, just “Display date” would be fine I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be “Display publish date” both here and on the Latest Posts block
Should I change this in a follow-up PR?
<ul className={ this.props.className } key="latest-comments"> | ||
{ latestComments.map( ( comment, i ) => | ||
<li key={ i }> | ||
{ displayAvatar && comment.author_avatar_urls[ 96 ] && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this 96
a safe hard-coded assumption? Or, should we use the largest avatar size on the object?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's not safe because rest_avatar_sizes
can be filtered to cause different sizes to be returned in the response. So yeah, using the largest size I think is the right move.
{ displayAvatar && comment.author_avatar_urls[ 96 ] && | ||
<img className={ `${ this.props.className }__comment-avatar` } alt={ comment.author_name } src={ comment.author_avatar_urls[ 96 ] } /> | ||
} | ||
<a href={ comment.link } target="_blank">{ comment._embedded.up[ 0 ].title.rendered }</a> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should we display if there's no linked post, or the linked post doesn't have a title?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When there is no title, see 813af21.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in f3aa112f
<a href={ comment.link } target="_blank">{ comment._embedded.up[ 0 ].title.rendered }</a> | ||
{ displayTimestamp && comment.date_gmt && | ||
<span className={ `${ this.props.className }__comment-timestamp` }> | ||
{ moment( comment.date_gmt ).local().format( 'MMM DD h:mm A' ) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This timestamp format seems like it should be user-configurable. Maybe we need a JS implementation of human_time_diff()
with a filterable format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moment.js does support relative time: https://momentjs.com/docs/#/displaying/fromnow/
It could be a nice block attribute to decide whether the dates are shown in relative time or absolute time, according to the site's datetime format.
See also #1992 (comment) and #1992 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
according to the site's datetime format.
Should we get a new issue going about respecting the site's datetime format?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@danielbachhuber done: #2013
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it affects latest posts as well.
</span> | ||
} | ||
{ displayExcerpt && comment.content && | ||
<div className={ `${ this.props.className }__comment-excerpt` } dangerouslySetInnerHTML={ { __html: comment.content.rendered } } /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What algorithm would you like to use for truncating comments? WordPress doesn't currently implement a comment excerpt.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the dangerouslySetInnerHTML
here? Because a user may have markup in their comment?
I am not aware of an existing truncation solution, other than to use CSS.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the
dangerouslySetInnerHTML
here? Because a user may have markup in their excerpt?
Yes, HTML is permitted in comments (and WordPress includes some by default anyway)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mtias thoughts on whether raw HTML from comments should be embedded?
height: 48px; | ||
width: 48px; | ||
border-radius: 24px; | ||
margin-right: 5px; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it fine that these sizes are in px? If not, can you give me the requested values as em?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are 30 instances of em
units being used in block CSS, whereas there are 37 instances of px
units being used. So I'd say px
is safe. 👍
"displayTimestamp": true | ||
} | ||
} | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be nice to set up mock data so we can actually test component rendering.
Very cool — works as expected to me. Once the your code points are reviewed, let's commit and I'll see if I can figure out a follow-up PR to tweak the design/CSS. |
Does this need to be handled in this PR? |
Sure. Why not? Also, one other big piece needed in this PR is the server-side component for rendering the comments when the block is displayed on the frontend. See #2014 which just refactored where the PHP logic is located. |
Rebased with |
|
@melchoyce here's how the block is now rendering in the editor: And how it renders on the frontend: Last major piece remaining is how to handle comment excerpts in the editor. |
@westonruter Cool 👍 I have some visual tweaks I still want to make, but those can happen in a followup PR. Do you know if there's anything we can do to have the front-end view of the comments block inherit the theme's comment styles, or are we out of luck there? |
Yeah, I think it is possible. We'd essentially need to use the So that brings up another consideration. There's really two separate comment blocks. One block for the latest comments across the site (as built in this PR) and another block for the latest comments for this current post. This latter one I could see useful to show at the top of a longform post in a call-to-action to comment, or even in template building such a block could be used instead of |
/** | ||
* WordPress dependencies | ||
*/ | ||
import { Component } from 'element'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note with #2172, these need to be updated to prefix the dependencies with @wordpress/
. You will need to perform a rebase against the latest version of master and apply your changes:
git fetch origin
git rebase origin/master
Copied from Latest Posts block via 3d9b34a
Rebased and added dependency prefixes. Rewrote e34aeeb as e0e9088. |
Codecov Report
@@ Coverage Diff @@
## master #1931 +/- ##
==========================================
- Coverage 22.99% 22.79% -0.21%
==========================================
Files 141 143 +2
Lines 4370 4426 +56
Branches 738 748 +10
==========================================
+ Hits 1005 1009 +4
- Misses 2841 2883 +42
- Partials 524 534 +10
Continue to review full report at Codecov.
|
Working on the PHP rendering of this block has confirmed for me the painfulness of having to implement two separate rendering systems, in PHP and JS: the former using normal/ubiquitous WordPress template tags and PHP API function calls, and the latter in React with calls to the REST API. I would not be able to suggest developers look at this block for a recommended streamlined way to write a dynamic block, given the violation of DRY. Maintaining two separate rendering systems with an aim to have the editor be a faithful preview of the frontend will be tedious. |
Are we good with merging this as a first pass?
This is only repeating yourself if the editor is not offering editing capabilities for the block, in which case, by all means, people are free to server render it. If you have to write a native mobile block you will need to recreate the rendering as well, that's just the nature of optimizing for each experience. In the case of widgets, is there a reason why the already built rendering can't be used on the back-end? That means only writing the editor implementation. |
The only thing not implemented is truncating comment excerpts in the editor, and outputting the comment HTML in a safe way.
I guess it depends on what you mean by “editing capabilities”. Like is toggling whether or not the date is rendered such an edit?
This is exactly what I've been suggesting, I think. Using the built-in server-side PHP templating inside the editor block—the same logic that is used to render the dynamic block on the frontend—is what I think we should do in order to eliminate writing two separate renderers. In this way, the a dynamic widget block in the editor would be very similar to what was implemented for shortcode previews in the Shortcake plugin. It would also be very similar to how changes to server-rendered partials are previewed in the Customizer via selective refresh. |
This is not what I mean. I mean using the already built server-side implementation as the front-end rendering instead of redoing all that again, and only creating the editor one from scratch. Server rendering blocks in the editor context doesn't seem a good direction, only a shortcut that would worsen the editing experience in favor of the development experience of only writing once. |
@mtias: For the Recent Comments widget in core, its server-side rendering logic doesn't itself specifically have filters to modify its markup. But another “Comments for this Post” block (as described above) would indeed have filters since it would use So for the Latest Comments widget block, a client-side React implementation of the UI can indeed be implemented to mirror whatever rendering logic is developed on the server, but I fear this is not going to be scalable or possible in many cases. Mirroring implementations is re-work (not DRY). But if a theme could override a server-side If not, and in the case of a “Comments for this Post” block, the source of truth for the block's rendering is the server-side logic. There would not be a way to get a true preview of what the block will render without asking the server to render it. For static blocks, the source of truth is the client-side renderer, so they are kept DRY. For dynamic blocks, this isn't the case. So I don't see server-side rendering of blocks as being a shortcut. It's about not creating more work than is required. It's about honoring the source of truth for what a dynamic block renders, and it's about giving a faithful preview of what the block actually will render. If many of a dynamic block's settings are purely stylistic, there needn't be a worsening of the editing experience as the client and server in that case could just toggle classes on the wrapper. Otherwise, if an attribute change does require a re-rendering then this is not really a fundamental change from what we have with a client-side rendering because in the case of client-side rendering there is still often a need to refetch data from the API, and so there will often be latency in that case as well. I think using the server-side rendering of blocks in the editor will be key for developer adoption. Nobody wants to do work twice. It's also important for user adoption, because users want to preview what they're actually going to see rendered on the frontend. |
Just to throw an idea in because I haven't seen it discussed: what about a common templating implementation (e.g. Mustache) that can be used both server-side and client-side? |
Maybe of interest for consolidating/automating server-registered block attributes: #2529 |
@danielbachhuber The adoption of templating languages in Core has historically always been shied away from (e.g. Smarty, Twig, etc), the exception being Underscore templates in JS. WordPress uses “pure” PHP templates, for better or worse, and this is why JSX has been viewed so favorably so as to not be using any templating language. Adding a language-agnostic templating language would make the development experience worse on both the client and the server (lack of logic, lack of virtual DOM). I want to open a |
Closing this PR for now. Feel free to re-open when you're ready to proceed on it. |
Latest Posts Block v1 Support in Mobile
See #1792