Repository navigation
fix(admin): stop the editor saving over the newer version during a save conflict - #3118
Conversation
…ve conflict Recovering from a save conflict refreshes the entry's revision token so that "Save anyway" can write, and the editor holds autosave back while the conflict notice is up. Every other write could still use that token. Publishing, scheduling, unscheduling, unpublishing and a publication date change save the pending edits first without checking the conflict, and a save that was queued behind the refused one ran after recovery. Each wrote the editor's copy over the other version, and publishing then put that copy live. These actions now wait for the writer's decision. The publishing controls are disabled while the notice is up, including items in a menu that was already open, and a publication date change is refused with a message in its dialog. While a conflict is recorded, the save queue sends only a save the writer starts during it, so a save that was already waiting when the conflict arrived is not sent. "Save anyway" and discarding clear the conflict as before, and so does a successful author or SEO save.
🦋 Changeset detectedLatest commit: c2faa00 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-test
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-loader
@emdash-cms/registry-moderation
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
This is the right change, solving the right problem in a way that fits EmDash. The bug — publishing/schedule/unpublish/date-change paths flushing pending edits and writing over a newer version while a save conflict is showing — is real and serious (it can publish stale content). The approach is sound: keep the existing serialize-and-flush behavior, but gate the actions on a conflict ref that is readable before the next render, and drop queued saves that would run after the conflict unless the user explicitly chooses Save anyway. The UI is disabled consistently across the sidebar, mobile header, and distraction-free overlay, including already-open menu items.
I read the diff and full changed files, traced the call sites for PublishActions / SettingsActionBar, checked the PublicationDateDialog error handling, and verified the recovery ref wiring in router.tsx. The conflict guard is applied in the right places (handlePublish, runScheduleChange, and serializeEditorSave), and the explicitSave flag correctly lets Save anyway through. New UI strings are Lingui-wrapped, the Tailwind layout is unchanged, and the changeset accurately describes the user-facing fix for @emdash-cms/admin.
No blockers. The code is clean.
Overlapping PRsThis PR modifies files that are also changed by other open PRs:
This may cause merge conflicts or duplicated work. A maintainer will coordinate. |
…hange The notice was cleared by any successful save, including the author and SEO writes the settings panel sends. Those carry the token recovered from the conflict, so the server accepts them, and once the notice was gone the editor's autosave was free to send the writer's older copy with a token the server also accepts, overwriting the other version with no further warning. Only a save the writer started clears it now. An author or SEO change still saves, so nothing the writer typed is dropped, but the entry stays in conflict until they choose.
…ice is up An author or SEO save during a conflict invalidates the entry, and the read that follows wrote the newer version into the form, so the copy the notice promises is still there was gone from the editor while the notice still said it was there. A read that lands during a conflict no longer replaces the form. The schedule dialog is the one publishing surface a writer can still reach once the conflict arrives, because the menu item behind it is disabled only after it is open, so it gets the case the publication date dialog already had.
There was a problem hiding this comment.
This is a re-review of a tightly scoped bug fix, and the change still looks correct and complete.
The PR addresses a real, serious problem: while the content editor is showing a save-conflict notice, publishing/schedule/unpublish/date-change paths were flushing pending edits first and writing over the newer version, and a save already queued behind a refused autosave could do the same. The fix gates those actions on a conflict ref that is writable before the next render, drops queued saves that would run after the conflict unless the user explicitly chooses Save anyway, and disables the publishing controls so the existing UI cannot be driven into the bad state.
I re-read the full diff and the changed source files, checked the call sites for PublishActions and SettingsActionBar, traced the serializeEditorSave queue logic in router.tsx, and verified the schedule/date-dialog refusal path. The wiring is consistent across the sidebar, mobile header, and distraction-free overlay, including already-open menus. New UI strings are Lingui-wrapped, no raw Tailwind directional classes were introduced, and the patch changeset accurately describes the user-facing fix for @emdash-cms/admin. The new test cases specifically exercise the race and refusal behaviors rather than asserting implementation details back at themselves.
No blockers. The code is clean.
What does this PR do?
Prevents the content editor from saving over another writer's newer version while the conflict notice is up.
Conflict recovery (#2902) fetches the current revision token so Save anyway can write, and pauses only autosave. Publishing, scheduling, removing a schedule, unpublishing and date changes (#2972) save pending edits first, so they wrote over the other version with that token, and publishing put that copy live. A save already queued behind the refused one did the same.
While the notice is up the publishing controls are disabled, open menus included; a date or schedule change is refused in its dialog; a save queued before the conflict no longer runs after it; an author or SEO save no longer clears the notice; and a read no longer replaces what the writer typed. Save anyway, discarding, and everything outside a conflict are unchanged. An author or SEO save still writes with the token recovered from the conflict. Withholding that token from those saves would change #2902's recovery, so it is left for a separate change.
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain.AI-generated code disclosure
Screenshots / test output
The notice in the content editor after another writer saved the same entry. Publish changes is disabled until the writer decides.
A publication date change during the conflict is refused in its dialog, and nothing is written.
The same refusal in the Arabic locale. The dialog follows the right-to-left direction, and the new message stays English until the catalogs are translated.
Nine cases join
publish-autosave-race.test.tsx. Against the unmodified source the first eight fail, and the ninth, which publishes after Save anyway, passes on both:With the change,
packages/adminis green:In
demos/simpleagainst the real server, Save date and Publish wrote over the other version before the change and do not after it.