Skip to content

RTC: Fix list sidebar reset during real-time collaboration#76025

Merged
alecgeatches merged 10 commits intoWordPress:trunkfrom
Automattic:fix/rtc-list-sidebar
Mar 17, 2026
Merged

RTC: Fix list sidebar reset during real-time collaboration#76025
alecgeatches merged 10 commits intoWordPress:trunkfrom
Automattic:fix/rtc-list-sidebar

Conversation

@alecgeatches
Copy link
Copy Markdown
Contributor

@alecgeatches alecgeatches commented Feb 27, 2026

What?

Closes #75647.

Why?

When real-time collaboration is enabled and two users are editing the same post, selecting a List block and viewing its items in the sidebar's "List View" tab clears out when the other user adds a list item:

Screen.Recording.2026-02-27.at.1.55.30.PM.mov

Reproducing the bug by editing a selected list

The root cause is that resetBlocks() is called whenever there's a real-time collaboration update, which dispatches RESET_BLOCKS. The openedListViewPanels reducer unconditionally clears all panel state on RESET_BLOCKS, collapsing the sidebar panel. Currently, there's no way to distinguish an RTC sync from a local reset, so the panel state gets wiped every time.

How?

This PR adds an isExternal flag to the resetBlocks action, set to true when the reset originates from an external data source (RTC sync, undo/redo via useBlockSync) rather than a local user action. When openedListViewPanels reducer receives isExternal as true, the reducer returns the existing state unchanged, preserving whatever panel state the user had open. Local resets still clear panel state as before. This allows the same local logic to work, but not interrupt RTC collaborators:

list-sidebar-fixed.mov

Fixed reproduction, selected list stays selected during edit

The flag is set in useBlockSync's setControlledBlocks(), which is always triggered by an external data source.

Testing Instructions

  1. Enable real-time collaboration and open a post in two separate browser tabs.
  2. In Tab 1, insert a List block with a couple of list items.
  3. In Tab 1, open the "Document Overview" panel and click on the List block. Switch to the "List View" tab in the right sidebar and see the list items.
  4. In Tab 2, add a new list item to the same List block.
  5. Go back to Tab 1. The sidebar should still show all list items (including the new one from Tab 2), and the panel should remain open.

Test Coverage

Tests were added for the openedListViewPanels reducer covering:

  • External RESET_BLOCKS (with isExternal: true) preserves all panel state
  • Local RESET_BLOCKS (without the flag) still clears the allOpen flag and individual panel entries as before

@alecgeatches alecgeatches self-assigned this Feb 27, 2026
@alecgeatches alecgeatches added the [Feature] Real-time Collaboration Phase 3 of the Gutenberg roadmap around real-time collaboration label Feb 27, 2026
@github-actions github-actions bot added the [Package] Block editor /packages/block-editor label Feb 27, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 2, 2026

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: alecgeatches <alecgeatches@git.wordpress.org>
Co-authored-by: ellatrix <ellatrix@git.wordpress.org>
Co-authored-by: ingeniumed <ingeniumed@git.wordpress.org>
Co-authored-by: jsnajdr <jsnajdr@git.wordpress.org>
Co-authored-by: chriszarate <czarate@git.wordpress.org>
Co-authored-by: maxschmeling <maxschmeling@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

// by the isOutgoing check above.
__unstableMarkNextChangeAsNotPersistent();
resetBlocks( controlledBlocks );
resetBlocks( controlledBlocks, { isExternal: true } );
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@ellatrix Do you think this isExternal flag for resetBlocks() is a reasonable idea?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The main point is when an RTC change comes through useBlockSync() we can use the flag to ignore local behavior that clears out the sidebar. There may be some other places we want to ignore the side effects of RESET_BLOCKS when the change is coming remotely, so this gives us an easy path to do so.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry for not replying to you before.

The openedListViewPanels reducer unconditionally clears all panel state on RESET_BLOCKS, collapsing the sidebar panel.

Why does that happen in the first place? At first glance, that seems wrong to me. Maybe we should track down where that was introduced to understand the full picture better. Maybe we can remove this side effect and check for things other than RESET_BLOCKS

@alecgeatches alecgeatches added the [Type] Bug An existing feature does not function as intended label Mar 2, 2026
@ingeniumed
Copy link
Copy Markdown
Contributor

I have been encountering an intermittent bug with the way posts are rendered in the block editor for admin users with this fix. Note that this is with RTC off.

The rough steps are:

  • Open up a post
  • Edit the single post template from the post sidebar. Remove the featured image and make some other changes.
  • Hit save.
  • Go back to the wp-admin dashboard and load up a post.
  • You'll see that the post is rendered as if its being published.
  • Editing via the site editor also triggers the same issue
bug.mp4

I removed this fix, ran npm run distclean and then was finally able to load the post properly.

The reason why I said its intermittent is because I couldn't consistently replicate it with the above steps. I tried this 5 times, and could replicate it two times. The other times it just loaded as it should.

Note that it only happens with admins. Anyone lower than an admin doesn't see this behaviour.

@alecgeatches
Copy link
Copy Markdown
Contributor Author

alecgeatches commented Mar 12, 2026

@ingeniumed I took a look at your report above. I was unable to reproduce and I don't think it's related to this PR.

The only functional change affects openedListViewPanels(), which changes the behavior of the editor sidebar. Especially with RTC off and your reproduction, this seems to be unrelated. It almost looks like in your reproduction video that the left user has "Show Template" enabled through some means. npm run distclean fixing it makes me think it was some unusual build leftover, but I don't think it was this PR.

Feel free to try another reproduction!

@alecgeatches
Copy link
Copy Markdown
Contributor Author

@youknowriad @ellatrix, could you have a quick look at this PR and the comment above?

In short, we had added a isExternal flag to resetBlocks(), and then we use that to ignore the normal behavior to reset the sidebar if another user caused the resetBlocks() via RTC. I'm not very familiar with this part of the code and want to ensure it makes sense. Thanks!

@jsnajdr
Copy link
Copy Markdown
Member

jsnajdr commented Mar 16, 2026

The root cause is that resetBlocks() is called whenever there's a real-time collaboration update, which dispatches RESET_BLOCKS. The openedListViewPanels reducer unconditionally clears all panel state on RESET_BLOCKS, collapsing the sidebar panel.

Recently we're starting to see more and more instances of a very similar problem: when calling RESET_BLOCKS, do we want to completely reset the editor state and start from scratch, or do we want to just "edit the blocks by replacing" and preserve as much editor state as possible? It's increasingly clear that we want to do the latter.

Since #75458 we started preserving "controlled inner blocks" on reset. When the block IDs are mostly the same after reset, try to find the controlled subtrees and copy them over.

In #76529 and #76310 we identified that there is a lot of trouble when RESET_BLOCKS removes the blockEditingModes flags. In the end we'll probably start preserving them.

And now we see that also openedListViewPanels state ought to be preserved on RESET_BLOCKS instead of cleared.

It's always the same story: don't clear state on reset, preserve as much as possible. Instead of the isExternal flag, could we just preserve the openedListViewPanels state on reset, unconditionally? If any panels need to be closed, let's implement that some other way, triggered by a different condition.

@alecgeatches
Copy link
Copy Markdown
Contributor Author

alecgeatches commented Mar 16, 2026

Instead of the isExternal flag, could we just preserve the openedListViewPanels state on reset, unconditionally?

@jsnajdr Great idea! This PR has now been simplified to a -3 change and works the same.

@alecgeatches alecgeatches requested a review from jsnajdr March 16, 2026 18:37
@chriszarate chriszarate added the Backport to WP 7.0 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Mar 17, 2026
@github-project-automation github-project-automation bot moved this to 🔎 Needs Review in WordPress 7.0 Editor Tasks Mar 17, 2026
@alecgeatches alecgeatches merged commit ee6261f into WordPress:trunk Mar 17, 2026
46 checks passed
@github-project-automation github-project-automation bot moved this from 🔎 Needs Review to ✅ Done in WordPress 7.0 Editor Tasks Mar 17, 2026
@alecgeatches alecgeatches deleted the fix/rtc-list-sidebar branch March 17, 2026 17:23
@github-actions github-actions bot added this to the Gutenberg 22.8 milestone Mar 17, 2026
@github-actions github-actions bot removed the Backport to WP 7.0 Beta/RC Pull request that needs to be backported to the WordPress major release that's currently in beta label Mar 17, 2026
gutenbergplugin pushed a commit that referenced this pull request Mar 17, 2026
* Add test to demonstrate selection change in nested blocks

* Fix selection change when nested block changes

* Add isExternal flag to RESET_BLOCKS and ignore panel state changes when a change is RTC-initiated

* Remove resetBlocks selection fix, as restoreSelection() handles this case

* Fix tests not updated with isExternal: true callback

* Fix default parameter in test

* Remove `isExternal` from RESET_BLOCKS, don't affect panel state

* Revert test changes and export

Co-authored-by: alecgeatches <alecgeatches@git.wordpress.org>
Co-authored-by: ellatrix <ellatrix@git.wordpress.org>
Co-authored-by: ingeniumed <ingeniumed@git.wordpress.org>
Co-authored-by: jsnajdr <jsnajdr@git.wordpress.org>
Co-authored-by: chriszarate <czarate@git.wordpress.org>
Co-authored-by: maxschmeling <maxschmeling@git.wordpress.org>
@github-actions github-actions bot added the Backported to WP Core Pull request that has been successfully merged into WP Core label Mar 17, 2026
@github-actions
Copy link
Copy Markdown

I just cherry-picked this PR to the wp/7.0 branch to get it included in the next release: 7ccb55b

pento pushed a commit to WordPress/wordpress-develop that referenced this pull request Mar 19, 2026
This updates the pinned hash from the `gutenberg` from `8c78d87453509661a9f28f978ba2c242d515563b` to `487a096a9782ba6110a7686d7b4b2d0c55ed1b06`.

The following changes are included:

- Disables anchor support for the Page Break block. (WordPress/gutenberg#76434)
- WP Admin: Update Connectors screen footer text for consistency. (WordPress/gutenberg#76382)
- E2E Tests: Add coverage for AI plugin callout banner on Connectors page (WordPress/gutenberg#76432)
- Update sync docs (WordPress/gutenberg#75972)
- RTC: Add preference for collaborator notifications (WordPress/gutenberg#76460)
- Fix "should undo bold" flaky test (WordPress/gutenberg#76464)
- TimePicker: Clamp month day to valid day for month (WordPress/gutenberg#76400)
- RTC: Fix error when entity record doesn't have 'meta' property (WordPress/gutenberg#76311)
- Navigation: Update close button size. (WordPress/gutenberg#76482)
- TemplateContentPanel: fix useSelect warning (WordPress/gutenberg#76421)
- DataViews: Add spinner in `DataViewsLayout` in initial load of data (WordPress/gutenberg#76486) (WordPress/gutenberg#76490)
- RTC: Fix TypeError in areEditorStatesEqual when selection is undefined (WordPress/gutenberg#76163)
- Page/Post Content Focus Mode: Fix insertion into Post Content block (WordPress/gutenberg#76477)
- Revisions: use useSubRegistry={false} to fix global store selectors (WordPress/gutenberg#76152) (WordPress/gutenberg#76522)
- Fix RTL styling on Connectors, Font Library, and boot-based admin pages (WordPress/gutenberg#76496)
- RTC: Auto-register custom taxonomy rest_base values for CRDT sync (WordPress/gutenberg#75983)
- RTC: Add a limit for the default provider (WordPress/gutenberg#76437)
- Fix RTL styling on AI plugin callout banner (WordPress/gutenberg#76497)
- Add command palette trigger button to admin bar (WordPress/gutenberg#75757)
- Block Bindings: Remove source items constrained by enums (WordPress/gutenberg#76200)
- HTML Block: Remove "unsaved changes" check (WordPress/gutenberg#76086)
- CI: Don't build release notes during plugin build workflow for WP Core sync (WordPress/gutenberg#76398) (WordPress/gutenberg#76578)
- CI: Simplify strategy matrix in Build Gutenberg Plugin Zip workflow (WordPress/gutenberg#76435) (WordPress/gutenberg#76538)
- Media: Add hooks and extension points for client-side media processing (WordPress/gutenberg#74913)
- RTC: Fix list sidebar reset during real-time collaboration (WordPress/gutenberg#76025)
- RTC: Fix CRDT serialization of nested RichText attributes (WordPress/gutenberg#76597)
- RTC: Remove post list lock icon and replace user-specific lock text (WordPress/gutenberg#76322)
- Fix HEIC upload error handling and sub-size format (WordPress/gutenberg#76514)
- RTC: Fix cursor index sync with rich text formatting (WordPress/gutenberg#76418)
- RTC: Allow filtering of `SyncConnectionModal` (WordPress/gutenberg#76554)
- RTC: Implement front-end peer limits (WordPress/gutenberg#76565)
- Navigation overlay close button may be displayed twice (WordPress/gutenberg#76585)
- Site Editor > Templates: fix author filter (WordPress/gutenberg#76625)
- Revisions: Show changed block attributes in inspector sidebar (WordPress/gutenberg#76550)
- Fix IS_GUTENBERG_PLUGIN env var override in build config  (WordPress/gutenberg#76605)
- Real Time Collaboration: Introduce filters for the polling intervals. (WordPress/gutenberg#76518)
- RTC: Fix RichTextData deserialization (WordPress/gutenberg#76607)
- Cross Origin Isolation: Remove `img` from the list of elements that get mutated (WordPress/gutenberg#76618)
- RTC: Scroll to collaborator on click (WordPress/gutenberg#76561)
- Update changelog link for pull request 11276 (WordPress/gutenberg#76638)
- Fix backport changelog filename (WordPress/gutenberg#76651)
- Build: Skip non-minified build for WASM-inlined workers (WordPress/gutenberg#76615)
- RTC: Change RTC option name (WordPress/gutenberg#76643)
- BlockListBlock: fix crash when selectedProps are null (WordPress/gutenberg#75826)
- Build: Fix vips worker 404 when SCRIPT_DEBUG is true (WordPress/gutenberg#76657)
- useMediaQuery: support in-iframe queries via new `WindowContext` (WordPress/gutenberg#76446) (WordPress/gutenberg#76660)
- Refactor admin-ui Page component to use @wordpress/theme tokens and @wordpress/ui layout primitive (WordPress/gutenberg#75963)
- Connectors: Improve accessibility (WordPress/gutenberg#76456)
- Build: Remove unused JXL WASM module from vips worker (WordPress/gutenberg#76639)
- Connectors: fix button size (WordPress/gutenberg#76582)
- Compose: Implement useCopyToClipboard and useCopyOnClick with native clipboard API (WordPress/gutenberg#75723) (WordPress/gutenberg#76663)
- Connectors: Fetch specific plugin instead of all plugins (WordPress/gutenberg#76594)
- Revisions: Add Meta fields diff panel to document sidebar (WordPress/gutenberg#76341)
- Loosen client-side media processing requirements (WordPress/gutenberg#76616)
- Reduce the added halo for selected block. (WordPress/gutenberg#76619)
- Connectors: Add unregisterConnector and upsert support (WordPress/gutenberg#76541)

A full list of changes can be found on GitHub: https://github.com/WordPress/gutenberg/compare/8c78d87453509661a9f28f978ba2c242d515563b…487a096a9782ba6110a7686d7b4b2d0c55ed1b06.

Log created with:

git log --reverse --format="- %s" 8c78d87453509661a9f28f978ba2c242d515563b..487a096a9782ba6110a7686d7b4b2d0c55ed1b06 | sed 's|#\([0-9][0-9]*\)|https://github.com/WordPress/gutenberg/pull/\1|g; /github\.com\/WordPress\/gutenberg\/pull/!d' | pbcopy

See #64595.

git-svn-id: https://develop.svn.wordpress.org/trunk@62063 602fd350-edb4-49c9-b593-d223f7449a82
markjaquith pushed a commit to markjaquith/WordPress that referenced this pull request Mar 19, 2026
This updates the pinned hash from the `gutenberg` from `8c78d87453509661a9f28f978ba2c242d515563b` to `487a096a9782ba6110a7686d7b4b2d0c55ed1b06`.

The following changes are included:

- Disables anchor support for the Page Break block. (WordPress/gutenberg#76434)
- WP Admin: Update Connectors screen footer text for consistency. (WordPress/gutenberg#76382)
- E2E Tests: Add coverage for AI plugin callout banner on Connectors page (WordPress/gutenberg#76432)
- Update sync docs (WordPress/gutenberg#75972)
- RTC: Add preference for collaborator notifications (WordPress/gutenberg#76460)
- Fix "should undo bold" flaky test (WordPress/gutenberg#76464)
- TimePicker: Clamp month day to valid day for month (WordPress/gutenberg#76400)
- RTC: Fix error when entity record doesn't have 'meta' property (WordPress/gutenberg#76311)
- Navigation: Update close button size. (WordPress/gutenberg#76482)
- TemplateContentPanel: fix useSelect warning (WordPress/gutenberg#76421)
- DataViews: Add spinner in `DataViewsLayout` in initial load of data (WordPress/gutenberg#76486) (WordPress/gutenberg#76490)
- RTC: Fix TypeError in areEditorStatesEqual when selection is undefined (WordPress/gutenberg#76163)
- Page/Post Content Focus Mode: Fix insertion into Post Content block (WordPress/gutenberg#76477)
- Revisions: use useSubRegistry={false} to fix global store selectors (WordPress/gutenberg#76152) (WordPress/gutenberg#76522)
- Fix RTL styling on Connectors, Font Library, and boot-based admin pages (WordPress/gutenberg#76496)
- RTC: Auto-register custom taxonomy rest_base values for CRDT sync (WordPress/gutenberg#75983)
- RTC: Add a limit for the default provider (WordPress/gutenberg#76437)
- Fix RTL styling on AI plugin callout banner (WordPress/gutenberg#76497)
- Add command palette trigger button to admin bar (WordPress/gutenberg#75757)
- Block Bindings: Remove source items constrained by enums (WordPress/gutenberg#76200)
- HTML Block: Remove "unsaved changes" check (WordPress/gutenberg#76086)
- CI: Don't build release notes during plugin build workflow for WP Core sync (WordPress/gutenberg#76398) (WordPress/gutenberg#76578)
- CI: Simplify strategy matrix in Build Gutenberg Plugin Zip workflow (WordPress/gutenberg#76435) (WordPress/gutenberg#76538)
- Media: Add hooks and extension points for client-side media processing (WordPress/gutenberg#74913)
- RTC: Fix list sidebar reset during real-time collaboration (WordPress/gutenberg#76025)
- RTC: Fix CRDT serialization of nested RichText attributes (WordPress/gutenberg#76597)
- RTC: Remove post list lock icon and replace user-specific lock text (WordPress/gutenberg#76322)
- Fix HEIC upload error handling and sub-size format (WordPress/gutenberg#76514)
- RTC: Fix cursor index sync with rich text formatting (WordPress/gutenberg#76418)
- RTC: Allow filtering of `SyncConnectionModal` (WordPress/gutenberg#76554)
- RTC: Implement front-end peer limits (WordPress/gutenberg#76565)
- Navigation overlay close button may be displayed twice (WordPress/gutenberg#76585)
- Site Editor > Templates: fix author filter (WordPress/gutenberg#76625)
- Revisions: Show changed block attributes in inspector sidebar (WordPress/gutenberg#76550)
- Fix IS_GUTENBERG_PLUGIN env var override in build config  (WordPress/gutenberg#76605)
- Real Time Collaboration: Introduce filters for the polling intervals. (WordPress/gutenberg#76518)
- RTC: Fix RichTextData deserialization (WordPress/gutenberg#76607)
- Cross Origin Isolation: Remove `img` from the list of elements that get mutated (WordPress/gutenberg#76618)
- RTC: Scroll to collaborator on click (WordPress/gutenberg#76561)
- Update changelog link for pull request 11276 (WordPress/gutenberg#76638)
- Fix backport changelog filename (WordPress/gutenberg#76651)
- Build: Skip non-minified build for WASM-inlined workers (WordPress/gutenberg#76615)
- RTC: Change RTC option name (WordPress/gutenberg#76643)
- BlockListBlock: fix crash when selectedProps are null (WordPress/gutenberg#75826)
- Build: Fix vips worker 404 when SCRIPT_DEBUG is true (WordPress/gutenberg#76657)
- useMediaQuery: support in-iframe queries via new `WindowContext` (WordPress/gutenberg#76446) (WordPress/gutenberg#76660)
- Refactor admin-ui Page component to use @wordpress/theme tokens and @wordpress/ui layout primitive (WordPress/gutenberg#75963)
- Connectors: Improve accessibility (WordPress/gutenberg#76456)
- Build: Remove unused JXL WASM module from vips worker (WordPress/gutenberg#76639)
- Connectors: fix button size (WordPress/gutenberg#76582)
- Compose: Implement useCopyToClipboard and useCopyOnClick with native clipboard API (WordPress/gutenberg#75723) (WordPress/gutenberg#76663)
- Connectors: Fetch specific plugin instead of all plugins (WordPress/gutenberg#76594)
- Revisions: Add Meta fields diff panel to document sidebar (WordPress/gutenberg#76341)
- Loosen client-side media processing requirements (WordPress/gutenberg#76616)
- Reduce the added halo for selected block. (WordPress/gutenberg#76619)
- Connectors: Add unregisterConnector and upsert support (WordPress/gutenberg#76541)

A full list of changes can be found on GitHub: https://github.com/WordPress/gutenberg/compare/8c78d87453509661a9f28f978ba2c242d515563b…487a096a9782ba6110a7686d7b4b2d0c55ed1b06.

Log created with:

git log --reverse --format="- %s" 8c78d87453509661a9f28f978ba2c242d515563b..487a096a9782ba6110a7686d7b4b2d0c55ed1b06 | sed 's|#\([0-9][0-9]*\)|https://github.com/WordPress/gutenberg/pull/\1|g; /github\.com\/WordPress\/gutenberg\/pull/!d' | pbcopy

See #64595.
Built from https://develop.svn.wordpress.org/trunk@62063


git-svn-id: http://core.svn.wordpress.org/trunk@61345 1a063a9b-81f0-0310-95a4-ce76da25c4cd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backported to WP Core Pull request that has been successfully merged into WP Core [Feature] Real-time Collaboration Phase 3 of the Gutenberg roadmap around real-time collaboration [Package] Block editor /packages/block-editor [Type] Bug An existing feature does not function as intended

Projects

Development

Successfully merging this pull request may close these issues.

RTC: [Interface] List updates clear out the list items in the block inspector sidebar

7 participants