Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e34c8c2
Collab Sidebar: Replace AddCommentToolbarButton with CommentAvatarInd…
yashjawale Aug 20, 2025
1f2a1db
refactor: use threadParticipants and enhance avatar styling for origi…
yashjawale Aug 20, 2025
73400a9
refactor: simplify comment avatar styling and improve background colo…
yashjawale Aug 20, 2025
d8cf981
refactor: update comment avatar styles to use border colors instead o…
yashjawale Aug 21, 2025
2e82c39
refactor: update comment resolution logic and simplify unresolved cou…
yashjawale Aug 21, 2025
2520dfd
refactor: filter out trashed comments in thread participant retrieval
yashjawale Aug 21, 2025
b286b3f
fix: handle case of 100+ comments
yashjawale Aug 29, 2025
58e11a8
refactor: update method for retrieving total pages of comments
yashjawale Aug 29, 2025
db5c16c
fix: style overflow indicator for longer text
yashjawale Aug 29, 2025
d9bf2ae
fix: sentence comments end with period
yashjawale Aug 29, 2025
d46bbae
fix: handle case where main comment is not found in thread
yashjawale Aug 29, 2025
e9cc58e
refactor: remove unused AddCommentToolbarButton component
yashjawale Aug 29, 2025
970dda7
fix: remove title attribute from avatar
yashjawale Sep 11, 2025
ff766e4
fix: overflowText i18n
yashjawale Sep 11, 2025
9f5f11a
fix: overflowTitle i18n
yashjawale Sep 11, 2025
237d7aa
fix: use SCSS variables in styles
yashjawale Sep 11, 2025
0425317
fix: remove unused `all-resolved` class
yashjawale Sep 11, 2025
b9defa9
fix: comment-indicator-toolbar.js syntax
yashjawale Sep 11, 2025
342f655
style: fix style.scss formatting
yashjawale Sep 11, 2025
24edb96
refactor: style.scss use base variables
yashjawale Sep 12, 2025
43bb5a3
refactor: optimize comment thread handling in CollabSidebar
yashjawale Sep 12, 2025
22ab72b
Merge branch 'trunk' into feat/comment-toolbar-indicator
yashjawale Sep 12, 2025
b33eb7b
fix: comment 100+ indicator
yashjawale Sep 12, 2025
0f4d335
fix: unused postStatus variable
yashjawale Sep 12, 2025
65ea1fd
fix: irregular hover background on indicator
yashjawale Sep 12, 2025
c11a5c8
refactor: simplify post and comment data retrieval in CollabSidebar u…
yashjawale Sep 12, 2025
71df444
fix: exclude trashed block comments from query
yashjawale Sep 12, 2025
089d6fb
fix: allow multiple comment statuses in REST API requests and simplif…
yashjawale Sep 15, 2025
7d42184
fix: EOL whitespace in block-comments.php
yashjawale Sep 15, 2025
97877fd
Merge branch 'trunk' into feat/comment-toolbar-indicator
yashjawale Sep 15, 2025
7b86a9a
fix: remove function_exists check for gutenberg_filter_rest_comment_c…
yashjawale Sep 15, 2025
e48d5ad
fix: remove memoization from query arguments
yashjawale Sep 15, 2025
d42b3c4
Merge branch 'trunk' into feat/comment-toolbar-indicator
yashjawale Sep 15, 2025
6bff632
Merge branch 'trunk' into feat/comment-toolbar-indicator
yashjawale Sep 15, 2025
781e4d6
Refactor: Remove support for multiple comment statuses in REST API re…
yashjawale Sep 15, 2025
88f1954
Update packages/editor/src/components/collab-sidebar/comment-indicato…
t-hamano Sep 16, 2025
c2080f1
Update packages/editor/src/components/collab-sidebar/comment-indicato…
t-hamano Sep 16, 2025
a8acf34
Update packages/editor/src/components/collab-sidebar/comment-indicato…
t-hamano Sep 16, 2025
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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/**
* WordPress dependencies
*/
import { ToolbarButton } from '@wordpress/components';
import { _x } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import {
privateApis as blockEditorPrivateApis,
store as blockEditorStore,
} from '@wordpress/block-editor';
import { store as coreStore } from '@wordpress/core-data';

/**
* Internal dependencies
*/
import { unlock } from '../../lock-unlock';
import { store as editorStore } from '../../store';

const { CommentIconToolbarSlotFill } = unlock( blockEditorPrivateApis );

const CommentAvatarIndicator = ( { onClick } ) => {
const { threadParticipants, hasUnresolved, hasMoreComments } = useSelect(
( select ) => {
const { getBlockAttributes, getSelectedBlockClientId } =
select( blockEditorStore );
const { getCurrentPostId } = select( editorStore );
const selectedBlock = getSelectedBlockClientId();
const selectedBlockAttributes = selectedBlock
? getBlockAttributes( selectedBlock )
: null;
const postId = getCurrentPostId();

// Get comment data for this block.
const blockCommentIdValue = selectedBlockAttributes?.blockCommentId;
const participantsMap = new Map();
let isResolved = false;
let moreCommentsExist = false;

if ( blockCommentIdValue && postId ) {
const queryArgs = {
post: postId,
type: 'block_comment',
status: 'any',
per_page: 100,
};

const comments = select( coreStore ).getEntityRecords(
'root',
'comment',
queryArgs
);

// Check if there are more pages available.
const totalPages = select(
coreStore
).getEntityRecordsTotalPages( 'root', 'comment', queryArgs );

// If we have more than 1 page, there are more comments
if ( totalPages && totalPages > 1 ) {
moreCommentsExist = true;
}

if ( comments ) {
// Get all comments in this thread.
// Main comment has id === blockCommentIdValue
// Replies have parent === blockCommentIdValue
const threadComments = comments.filter(
( comment ) =>
comment.status !== 'trash' &&
( comment.id === blockCommentIdValue ||
comment.parent === blockCommentIdValue )
);

// Sort by date to show participants in chronological order.
threadComments.sort(
( a, b ) => new Date( a.date ) - new Date( b.date )
);

// Find the main thread comment (first comment).
const mainComment = threadComments.find(
( comment ) => comment.id === blockCommentIdValue
);

// If no main comment is found, the thread doesn't exist.
if ( ! mainComment ) {
return {
threadParticipants: [],
hasUnresolved: false,
hasMoreComments: false,
};
}

// Thread is resolved if the main comment is approved.
isResolved = mainComment.status === 'approved';

threadComments.forEach( ( comment ) => {
// Track thread participants (original commenter + repliers)
if (
comment.author_name &&
comment.author_avatar_urls
) {
const authorKey = `${ comment.author }-${ comment.author_name }`;
if ( ! participantsMap.has( authorKey ) ) {
participantsMap.set( authorKey, {
name: comment.author_name,
avatar:
comment.author_avatar_urls?.[ '48' ] ||
Copy link
Member

@adamsilverstein adamsilverstein Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to add handling here to show a default avatar when the user has not set their avatar or is that already handled in the data?

I see that in useCommentAvatar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my understanding & testing...
We get the default grey avatar image from the API when user hasn't set the picture
& if the user email exists on gravatar then we get default gravatar image

Wondering why the hook handles it differently tho... 🤔
From checking its PR the only conversation I could find for avatar was discussion around allowing user to upload image in core instead of relying on gravatar.com

comment.author_avatar_urls?.[ '96' ],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where does this 96 come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

96 size is used as fallback when size 48 isn't available

image

From what I know, 96 is the default size for avatar images so they should be always available

The default Avatar size is 96x96 if you do not set the size using the size paramater.

Codex

isOriginalCommenter:
comment.id === blockCommentIdValue,
date: comment.date,
} );
}
}
} );
}
}

// Convert to array and maintain chronological order.
const participants = Array.from( participantsMap.values() );

return {
threadParticipants: participants,
hasUnresolved: ! isResolved,
hasMoreComments: moreCommentsExist,
};
},
[]
);

if ( ! threadParticipants.length ) {
return null;
}

// Show up to 3 avatars, with overflow indicator.
const maxAvatars = 3;
const visibleParticipants = threadParticipants.slice( 0, maxAvatars );
const overflowCount = Math.max( 0, threadParticipants.length - maxAvatars );

// If we hit the comment limit, show "100+" instead of exact overflow count.
const overflowText =
hasMoreComments && overflowCount > 0 ? '100+' : `+${ overflowCount }`;

const overflowTitle =
hasMoreComments && overflowCount > 0
? '100+ participants'
: `+${ overflowCount } more participants`;

return (
<CommentIconToolbarSlotFill.Fill>
<ToolbarButton
className={ `comment-avatar-indicator ${
hasUnresolved ? 'has-unresolved' : 'all-resolved'
}` }
label={ _x( 'View comments', 'View comment thread' ) }
onClick={ onClick }
showTooltip
>
<div className="comment-avatar-stack">
{ visibleParticipants.map( ( participant, index ) => (
<img
key={ participant.name + index }
src={ participant.avatar }
alt={ participant.name }
className="comment-avatar"
style={ { zIndex: maxAvatars - index } }
title={ participant.name }
/>
) ) }
{ overflowCount > 0 && (
<div
className="comment-avatar-overflow"
style={ { zIndex: 0 } }
title={ overflowTitle }
>
{ overflowText }
</div>
) }
</div>
</ToolbarButton>
</CommentIconToolbarSlotFill.Fill>
);
};

export default CommentAvatarIndicator;
18 changes: 9 additions & 9 deletions packages/editor/src/components/collab-sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { Comments } from './comments';
import { AddComment } from './add-comment';
import { store as editorStore } from '../../store';
import AddCommentButton from './comment-button';
import AddCommentToolbarButton from './comment-button-toolbar';
import CommentAvatarIndicator from './comment-indicator-toolbar';
import { useGlobalStylesContext } from '../global-styles-provider';
import { getCommentIdsFromBlocks } from './utils';

Expand Down Expand Up @@ -80,7 +80,7 @@ function CollabSidebarContent( {
comment_approved: 0,
};

// Create a new object, conditionally including the parent property
// Create a new object, conditionally including the parent property.
const updatedArgs = {
...args,
...( parentCommentId ? { parent: parentCommentId } : {} ),
Expand Down Expand Up @@ -264,28 +264,28 @@ export default function CollabSidebar() {
id: postId,
} );

// Process comments to build the tree structure
// Process comments to build the tree structure.
const { resultComments, sortedThreads } = useMemo( () => {
// Create a compare to store the references to all objects by id
// Create a compare to store the references to all objects by id.
const compare = {};
const result = [];

const filteredComments = ( threads ?? [] ).filter(
( comment ) => comment.status !== 'trash'
);

// Initialize each object with an empty `reply` array
// Initialize each object with an empty `reply` array.
filteredComments.forEach( ( item ) => {
compare[ item.id ] = { ...item, reply: [] };
} );

// Iterate over the data to build the tree structure
// Iterate over the data to build the tree structure.
filteredComments.forEach( ( item ) => {
if ( item.parent === 0 ) {
// If parent is 0, it's a root item, push it to the result array
// If parent is 0, it's a root item, push it to the result array.
result.push( compare[ item.id ] );
} else if ( compare[ item.parent ] ) {
// Otherwise, find its parent and push it to the parent's `reply` array
// Otherwise, find its parent and push it to the parent's `reply` array.
compare[ item.parent ].reply.push( compare[ item.id ] );
}
} );
Expand Down Expand Up @@ -332,7 +332,7 @@ export default function CollabSidebar() {
}

const AddCommentComponent = blockCommentId
? AddCommentToolbarButton
? CommentAvatarIndicator
: AddCommentButton;

return (
Expand Down
77 changes: 77 additions & 0 deletions packages/editor/src/components/collab-sidebar/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,80 @@
padding: 0;
}
}

// Comment avatar indicators
.comment-avatar-indicator {
position: relative;
padding: 4px !important;
min-width: auto !important;
background: transparent !important;
border: none !important;

&:hover {
background: rgba(0, 0, 0, 0.04) !important;
border-radius: 4px;
}

&.has-unresolved {
.comment-avatar-stack {
&::after {
content: "";
position: absolute;
top: -2px;
right: -2px;
width: 8px;
height: 8px;
background: #d63638;
border-radius: 50%;
border: 1px solid $white;
z-index: 10;
}
}
}
}

.comment-avatar-stack {
display: flex;
align-items: center;
position: relative;
height: 24px;
}

.comment-avatar {
width: 24px;
height: 24px;
border-radius: 50%;
border: 2px solid $white;
margin-left: -6px;
flex-shrink: 0;

&:first-child {
margin-left: 0;
border-color: #de6e55;
}

&:nth-child(2) {
border-color: #599637;
}

&:nth-child(3) {
border-color: #3858e9;
}
}

.comment-avatar-overflow {
width: fit-content;
height: 24px;
border-radius: 4rem;
padding: 0 4px;
background: #757575;
color: $white;
border: 2px solid $white;
margin-left: -6px;
display: flex;
align-items: center;
justify-content: center;
font-size: 10px;
font-weight: 600;
flex-shrink: 0;
}
Loading