Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3786a56
feat: initial implementation - enable reopening comments
yashjawale Aug 19, 2025
1b36276
fix: update comment resolution status to 'hold' when reopening
yashjawale Aug 19, 2025
1a962d8
feat: update comment button to use undo icon for reopening approved c…
yashjawale Aug 19, 2025
34960f3
refactor: add onCommentReopen function to handle reopening comments i…
yashjawale Aug 20, 2025
eb94947
fix: restore translator comment to previous state
yashjawale Aug 20, 2025
e9e89ba
fix: prevent rendering of focused thread for approved comments
yashjawale Aug 20, 2025
0a88fc4
fix: move reopen approved comments option to the comment actions menu
yashjawale Aug 21, 2025
22a1421
fix: revert unrelated change in imports
yashjawale Aug 25, 2025
675905e
fix: update success message for reopening comments
yashjawale Aug 25, 2025
9ae8b8d
fix: remove redundant confirmation dialogs for reopening and resolvin…
yashjawale Aug 25, 2025
4a4d193
fix: comment resolution functionality after removing dialogs
yashjawale Aug 25, 2025
cc431a5
feat: add CommentQuickReply component for quick replies on resolved t…
yashjawale Aug 25, 2025
c8c5696
fix: remove translator comments without placeholders
yashjawale Sep 15, 2025
2d5cd92
Merge branch 'trunk' into feat/reopen-resolved-comments
yashjawale Sep 15, 2025
381bbae
fix: use resolve button instead of separate tooltip for resolved threads
yashjawale Sep 15, 2025
8ef98f1
refactor: remove CommentQuickReply component and use CommentForm instead
yashjawale Sep 15, 2025
b79517a
feat: add props to CommentForm for rows & placeholder
yashjawale Sep 15, 2025
96c9338
fix: button layout with large text
yashjawale Sep 15, 2025
90f88e5
Merge branch 'trunk' into feat/reopen-resolved-comments
yashjawale Sep 15, 2025
f961cf5
refactor: remove unnecessary button styles from comment dropdown menu
yashjawale Sep 15, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
import {
__experimentalHStack as HStack,
Button,
__experimentalInputControl as InputControl,
} from '@wordpress/components';
import { _x, __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { sanitizeCommentString } from './utils';

/**
* CommentQuickReply component.
* Displayed on resolved thread to allow quick reply.
* Also reopens the thread on submit.
*
* @param {Object} props - The component props.
* @param {Function} props.onSubmit - The function to call when updating the comment.
* @return {React.ReactNode} The CommentQuickReply component.
*/
function CommentQuickReply( { onSubmit } ) {
const [ inputComment, setInputComment ] = useState( '' );

const [ isFocused, setIsFocused ] = useState( false );

return (
<>
<InputControl
__next40pxDefaultSize
__nextHasNoMarginBottom
value={ inputComment ?? '' }
onChange={ setInputComment }
label={ __( 'Reopen the thread & reply' ) }
placeholder={ __( 'Replying will reopen this thread' ) }
onFocus={ () => setIsFocused( true ) }
hideLabelFromVision
/>
{ isFocused && (
<HStack alignment="left" spacing="3" justify="flex-start">
<Button
__next40pxDefaultSize
accessibleWhenDisabled
variant="primary"
onClick={ () => {
onSubmit( inputComment );
setInputComment( '' );
} }
disabled={
0 === sanitizeCommentString( inputComment ).length
}
text={ _x( 'Reply', 'Add reply comment' ) }
/>
<Button
__next40pxDefaultSize
variant="tertiary"
onClick={ () => {
setIsFocused( false );
setInputComment( '' );
} }
text={ _x( 'Cancel', 'Cancel comment button' ) }
/>
</HStack>
) }
</>
);
}

export default CommentQuickReply;
114 changes: 58 additions & 56 deletions packages/editor/src/components/collab-sidebar/comments.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { store as blockEditorStore } from '@wordpress/block-editor';
*/
import CommentAuthorInfo from './comment-author-info';
import CommentForm from './comment-form';
import CommentQuickReply from './comment-quick-reply';

/**
* Renders the Comments component.
Expand All @@ -35,6 +36,7 @@ import CommentForm from './comment-form';
* @param {Function} props.onAddReply - The function to add a reply to a comment.
* @param {Function} props.onCommentDelete - The function to delete a comment.
* @param {Function} props.onCommentResolve - The function to mark a comment as resolved.
* @param {Function} props.onCommentReopen - The function to reopen a resolved comment.
* @param {boolean} props.showCommentBoard - Whether to show the comment board.
* @param {Function} props.setShowCommentBoard - The function to set the comment board visibility.
* @return {React.ReactNode} The rendered Comments component.
Expand All @@ -45,6 +47,7 @@ export function Comments( {
onAddReply,
onCommentDelete,
onCommentResolve,
onCommentReopen,
showCommentBoard,
setShowCommentBoard,
} ) {
Expand Down Expand Up @@ -111,6 +114,7 @@ export function Comments( {
onAddReply={ onAddReply }
onCommentDelete={ onCommentDelete }
onCommentResolve={ onCommentResolve }
onCommentReopen={ onCommentReopen }
onEditComment={ onEditComment }
isFocused={ focusThread === thread.id }
clearThreadFocus={ clearThreadFocus }
Expand All @@ -127,6 +131,7 @@ function Thread( {
onAddReply,
onCommentDelete,
onCommentResolve,
onCommentReopen,
isFocused,
clearThreadFocus,
} ) {
Expand All @@ -135,6 +140,7 @@ function Thread( {
<CommentBoard
thread={ thread }
onResolve={ onCommentResolve }
onReopen={ onCommentReopen }
onEdit={ onEditComment }
onDelete={ onCommentDelete }
status={ thread.status }
Expand Down Expand Up @@ -176,6 +182,14 @@ function Thread( {
) ) }
</>
) }
{ 'approved' === thread.status && isFocused && (
<CommentQuickReply
onSubmit={ ( inputComment ) => {
onCommentReopen( thread.id );
onAddReply( inputComment, thread.id );
} }
/>
) }
{ 'approved' !== thread.status && isFocused && (
<VStack
className="editor-collab-sidebar-panel__child-thread"
Expand Down Expand Up @@ -208,7 +222,14 @@ function Thread( {
);
}

const CommentBoard = ( { thread, onResolve, onEdit, onDelete, status } ) => {
const CommentBoard = ( {
thread,
onResolve,
onReopen,
onEdit,
onDelete,
status,
} ) => {
const [ actionState, setActionState ] = useState( false );
const [ showConfirmDialog, setShowConfirmDialog ] = useState( false );

Expand All @@ -218,31 +239,33 @@ const CommentBoard = ( { thread, onResolve, onEdit, onDelete, status } ) => {
setShowConfirmDialog( false );
};

const handleConfirmResolve = () => {
onResolve( thread.id );
setActionState( false );
setShowConfirmDialog( false );
};

const handleCancel = () => {
setActionState( false );
setShowConfirmDialog( false );
};

const actions = [
onEdit && {
title: _x( 'Edit', 'Edit comment' ),
onClick: () => {
setActionState( 'edit' );
onEdit &&
status !== 'approved' && {
title: _x( 'Edit', 'Edit comment' ),
onClick: () => {
setActionState( 'edit' );
},
},
},
onDelete && {
title: _x( 'Delete', 'Delete comment' ),
onClick: () => {
setActionState( 'delete' );
setShowConfirmDialog( true );
},
},
onReopen &&
status === 'approved' && {
title: _x( 'Reopen', 'Reopen comment' ),
onClick: () => {
onReopen( thread.id );
},
},
];

const moreActions = actions.filter( ( item ) => item?.onClick );
Expand All @@ -256,13 +279,10 @@ const CommentBoard = ( { thread, onResolve, onEdit, onDelete, status } ) => {
date={ thread?.date }
/>
<span className="editor-collab-sidebar-panel__comment-status">
{ status !== 'approved' && (
<HStack
alignment="right"
justify="flex-end"
spacing="0"
>
{ 0 === thread?.parent && onResolve && (
<HStack alignment="right" justify="flex-end" spacing="0">
{ 0 === thread?.parent &&
status !== 'approved' &&
onResolve && (
<Button
label={ _x(
'Resolve',
Expand All @@ -271,31 +291,29 @@ const CommentBoard = ( { thread, onResolve, onEdit, onDelete, status } ) => {
__next40pxDefaultSize
icon={ published }
onClick={ () => {
setActionState( 'resolve' );
setShowConfirmDialog( true );
onResolve( thread.id );
} }
showTooltip
/>
) }
{ 0 < moreActions.length && (
<DropdownMenu
icon={ moreVertical }
label={ _x(
'Select an action',
'Select comment action'
) }
className="editor-collab-sidebar-panel__comment-dropdown-menu"
controls={ moreActions }
/>
) }
</HStack>
) }
{ status === 'approved' && (
// translators: tooltip for resolved comment
<Tooltip text={ __( 'Resolved' ) }>
<Icon icon={ check } />
</Tooltip>
) }
{ status === 'approved' && (
// translators: tooltip for resolved comment
<Tooltip text={ __( 'Resolved' ) }>
<Icon icon={ check } />
</Tooltip>
) }
{ 0 < moreActions.length && (
<DropdownMenu
icon={ moreVertical }
label={ _x(
'Select an action',
'Select comment action'
) }
className="editor-collab-sidebar-panel__comment-dropdown-menu"
controls={ moreActions }
/>
) }
</HStack>
</span>
</HStack>
<HStack
Expand Down Expand Up @@ -324,22 +342,6 @@ const CommentBoard = ( { thread, onResolve, onEdit, onDelete, status } ) => {
) }
</VStack>
</HStack>
{ 'resolve' === actionState && (
<ConfirmDialog
isOpen={ showConfirmDialog }
onConfirm={ handleConfirmResolve }
onCancel={ handleCancel }
confirmButtonText="Yes"
cancelButtonText="No"
>
{
// translators: message displayed when confirming an action
__(
'Are you sure you want to mark this comment as resolved?'
)
}
</ConfirmDialog>
) }
{ 'delete' === actionState && (
<ConfirmDialog
isOpen={ showConfirmDialog }
Expand Down
36 changes: 31 additions & 5 deletions packages/editor/src/components/collab-sidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,36 @@ function CollabSidebarContent( {
} );

if ( savedRecord ) {
// translators: Comment resolved successfully
createNotice( 'snackbar', __( 'Comment marked as resolved.' ), {
type: 'snackbar',
isDismissible: true,
} );
createNotice(
'snackbar',
// translators: Comment resolved successfully
__( 'Comment marked as resolved.' ),
{
type: 'snackbar',
isDismissible: true,
}
);
} else {
onError();
}
};

const onCommentReopen = async ( commentId ) => {
const savedRecord = await saveEntityRecord( 'root', 'comment', {
id: commentId,
status: 'hold',
} );

if ( savedRecord ) {
createNotice(
'snackbar',
// translators: Comment reopened successfully
__( 'Comment reopened.' ),
{
type: 'snackbar',
isDismissible: true,
}
);
} else {
onError();
}
Expand Down Expand Up @@ -207,6 +232,7 @@ function CollabSidebarContent( {
onAddReply={ addNewComment }
onCommentDelete={ onCommentDelete }
onCommentResolve={ onCommentResolve }
onCommentReopen={ onCommentReopen }
showCommentBoard={ showCommentBoard }
setShowCommentBoard={ setShowCommentBoard }
/>
Expand Down
Loading