Notes: Add emoji reactions - stored as custom comment type#76767
Notes: Add emoji reactions - stored as custom comment type#76767adamsilverstein wants to merge 84 commits intotrunkfrom
Conversation
Introduce a new component that displays a horizontal row of emoji buttons for adding reactions to notes. Features include: - Curated emoji set: 👍 👎 ❤️ 🎉 😄 😕 👀 🚀 - Keyboard navigation with arrow keys, Home, and End - Accessible with role="listbox" and role="option" Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduce a component that displays current reactions with counts as pill-shaped buttons. Features include: - Shows reaction counts for each emoji - Highlights user's own reactions with distinct styling - Click to toggle (add/remove) reaction - "+" button opens emoji picker dropdown Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduce a popover component that shows who reacted and when. Features include: - Displays reactions grouped by emoji - Shows user avatars and names - Uses humanTimeDiff() for relative timestamps (e.g., "3 days ago") - Fetches user data for all reactors Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Extend the useBlockCommentsActions hook with three new functions:
- onAddReaction: Add a reaction to a comment
- onRemoveReaction: Remove user's reaction from a comment
- onToggleReaction: Toggle reaction (add if not present, remove if present)
Reactions are stored in comment meta._wp_reactions with structure:
{ emoji: [{ userId, timestamp }] }
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Connect the reaction components to the Notes sidebar: - Import and render ReactionDisplay in CommentBoard - Add ReactionDetailsPopover for viewing reaction details - Add "See emoji reaction details" menu action - Pass onToggleReaction through component hierarchy - Get current user ID for highlighting own reactions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add SCSS styles for: - Reactions container with flexbox layout - Pill-shaped reaction buttons with active state - Add reaction button with dashed border - Emoji picker dropdown - Reaction details popover with user avatars Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive E2E tests for the emoji reactions feature: - can add an emoji reaction to a note - can remove own emoji reaction by clicking it - can see emoji reaction details - reaction buttons are keyboard accessible - can add multiple different reactions to same note Also adds addReactionToComment helper to BlockCommentUtils. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Resolve merge conflicts with trunk's selectedNote editor state changes (#75177) while preserving emoji reaction features. Fix all 5 emoji reaction E2E tests that were timing out because the Dropdown popover was stealing focus from the thread, triggering the onBlur handler which collapsed the note and unmounted the emoji picker. - Add focusOnMount: false to the reaction Dropdown popoverProps - Add popover focus check to the thread onBlur handler - Update addReactionToComment E2E helper to wait for the emoji picker Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…yling Replace the plus icon with a Google Docs-inspired smiley face SVG, change focusOnMount to 'firstElement' so the emoji picker captures focus and prevents the note from collapsing, and restyle the button to be perfectly round with a clean white background that appears on hover. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move the "Add reaction" smiley button from below the note content to the upper-right header area alongside the resolve and actions buttons. Also register _wp_reactions as comment meta in PHP so the REST API accepts it, and fix the 500 error caused by spreading all comment meta (including potentially invalid _wp_note_status) when saving reactions. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
Add variant="tertiary" to the reaction pill Button components so the WordPress default dark button styling doesn't override the custom light gray background. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>
The Button component automatically adds the is-pressed class when aria-pressed is true, which sets a dark background (#1E1E1E). Override with matching specificity to keep the light blue active state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Increase height to 32px, use equal padding on all sides, and reduce gap between emoji and count to bring the aspect ratio closer to 1:1. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The reactions meta registration is a new feature targeting WordPress 7.0, not a 6.9 backport. Move it to its own file in lib/compat/wordpress-7.0/ per reviewer feedback.
The editor assumes users are logged in by default, so these guards are redundant. Matches the pattern used by other collab sidebar actions.
Reactions update comment metadata without adding, removing, or resizing comments, so reflowing is unnecessary.
Replace individual getUser() calls with a single getUsers() request using include, context: view, and _fields to reduce API calls and support low-capability users.
Negative reactions are better expressed as comments in a collaborative editing context. The thinking emoji provides a constructive "I need to consider this" signal instead.
The horizontal layout caused emojis to overflow and get cut off in the sidebar popover.
Start conservatively with ❤️ 🎉 😄 👀 🚀 to avoid skin-tone concerns and keep the picker compact. More reactions can be added later.
|
Flaky tests detected in ea19200. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/25089941102
|
- Fix children link to query for 'reaction' type when parent is a note - Add unmount cleanup flag to useReactionEmojis to prevent state updates after component unmounts - Wrap onToggleReaction in useCallback with reactionsMap dependency to prevent stale closure
The OPTIONS dispatch on a collection route may not include the schema in the expected structure. Test the controller's schema directly.
|
Regarding unbounded requests & embedding reactions: If embedding all the reactions on a given note is too much, we could think about caching/retrieving only the reaction count (grouped by emoji). This way we can quickly display the different reactions on a note and lazy load the individual reactions (and who gave them) after that. |
Add a computed reaction_summary field to note comment REST responses that returns grouped reaction counts, whether the current user reacted, and the user's reaction ID for each emoji. This eliminates the need for clients to fetch all individual reaction records separately. Includes batch pre-fetching in get_items() to avoid N+1 queries when returning multiple notes.
Replace the separate unbounded useEntityRecords call for reactions with the reaction_summary field returned on each note. This eliminates a separate API request and the per_page:-1 fetch of all individual reaction records, addressing unbounded request and performance concerns. Also removes the eager user data fetch for tooltips, using count-based labels instead.
Test schema structure, populated summary with current user's reaction, summary for a different user, and empty summary when no reactions exist.
Add the smiley face SVG to the icons library and import it in reaction-display.js instead of defining it inline.
Fetch individual reaction details (who reacted) only when the user hovers or focuses a reaction pill button, with a module-level cache to avoid repeated requests. This restores the GitHub-style tooltip showing user names without eagerly fetching all user data on page load.
Split the single GROUP_CONCAT-based query into two simpler queries: one for aggregated counts and one for the current user's reaction IDs. This avoids MySQL's group_concat_max_len truncation on notes with many reactions.
…omment-type Reconcile emoji reactions with trunk's notes architecture refactor. Drop PR's old positioning API (reflowComments, boardOffsets, calculatedOffset, setHeights, setBlockRef) in favor of trunk's floating prop backed by useFloatingBoard. Preserve the reactions data flow (reactionsMap, onToggleReaction) on top of the new architecture. Drop require statements for icons registry files removed in trunk (#76455).
…omment-type Resolve conflicts from trunk's collab-sidebar refactor (#77614): - Adopt new component structure (notes.js, note.js, note-thread.js) - Rename useBlockCommentsActions → useNoteActions; keep reactionsMap - Thread onToggleReaction and reactionsMap through Notes → NoteThread → Note - Render AddReactionButton + ReactionDisplay inside Note when isSelected - Delete obsolete comments.js - Replace experimental HStack with Stack from @wordpress/ui - Add missing useCallback import in hooks.js
|
Two open threads carried over from #75549 worth noting:
All other feedback from #75549 has been addressed — see my comparison for details. |
Add a docblock to lib/compat/wordpress-7.1/block-comments.php explaining why reactions use a custom 'reaction' comment type instead of comment meta: free authorship/timestamps from comment APIs, generic type that can attach to other resources, and per-row writes to avoid serialized-meta races. Links to the discussion in PRs #75549 and #75148.
Trunk's collab-sidebar refactor renamed the test fixture and helper: - blockCommentUtils → blockNoteUtils - addBlockWithComment → addBlockWithNote Update the Emoji Reactions describe block to use the current names so Playwright stops failing with 'Test has unknown parameter blockCommentUtils'.
|
@swissspidy this is what's shipping now - in case you want to double-check it matches what you had in mind:
The unbounded |
The Lint Styles check enforces using the design system cursor token for interactive non-link controls (rule added in trunk). Replace 'cursor: pointer' on .editor-collab-sidebar-panel__add-reaction-button to satisfy the rule.
…omment_types(). Mirrors the change being proposed for core in WordPress/wordpress-develop#10930. Introduces a single helper that returns the list of internal comment types ('note', 'reaction'), filterable so future additions only need to update one place. Apply it across all the existing 'note'/'reaction' guards in the 7.1 compat layer: * gutenberg_update_get_avatar_comment_type_7_1() — avatar-eligible types. * gutenberg_exclude_block_comments_from_admin_7_1() — admin query exclude. * gutenberg_filter_comment_count_query_exclude_block_comments_7_1() — pending-count SQL guard. * gutenberg_hide_note_from_comment_list_table_7_1() — list table args. * gutenberg_exclude_notes_from_comment_count_7_1() — approved-count SQL. * Gutenberg_REST_Comment_Controller_7_1::is_note_or_reaction() — REST controller's per-request type check (used by permissions, prepare, validation, and links). Props westonruter for the suggestion to centralize this list.
Summary
Closes #75144
Switches emoji reaction storage from
_wp_note_reactionscomment meta to individualreactioncomment records, as suggested by @swissspidy.Each reaction is stored as its own comment with:
comment_type = 'reaction'comment_parent = <note ID>comment_content = <emoji slug>(e.g.heart,rocket)user_id = <reacting user>This approach is more flexible, aligns with WordPress data patterns (similar to the React plugin), and could eventually extend to regular comments.
Related
OPTIONS /wp/v2/commentsschema with agutenberg_note_reaction_emojisfilter, implementing the approach suggested by @MamadukaRemaining feedback to address
From the review on #75549:
useEntityRecordsfor reactions has noper_pagelimit. Consider lazy-loading reactions only when a note thread is opened. Comment@wordpress/icons(@ellatrix) — Low priority, fine for now. CommentAlready addressed
wordpress-7.1compat dir (@Mamaduka, @t-hamano) — Donereaction(@swissspidy) — DoneTest plan
npm run test:e2e -- test/e2e/specs/editor/various/block-comments.spec.jsnpm run test:unit packages/editor/src/components/collab-sidebar/npm run test:unit:php:base -- --filter=Gutenberg_REST_Comments