fix: Prevent draft autosave state loss and editor overwriting#283
Conversation
Revision-backed autosaves were updating the draft revision but returning the base content row, so the API response could immediately disagree with the saved draft state. Reload the current draft revision after a successful save and merge its data and draft slug into the returned item. Add a regression test covering skipRevision updates on a revision-backed collection. Co-authored-by: Codex <codex@openai.com>
Update the admin autosave path so a PUT response is only applied back into the edit-page queries when the local editor state still matches the snapshot that was sent. This keeps autosave from triggering an extra GET for the same item, but avoids overwriting newer local edits with a stale response that arrives after the user keeps typing. Add focused regressions for the query-cache update helper and the ContentEditor snapshot guard. Co-authored-by: Codex <codex@openai.com>
Reconcile PortableTextEditor with post-mount value updates from parent state, while avoiding unnecessary resets for equivalent content. Add browser regressions covering external value replacement without onChange noise and preserving clean history for equivalent updates. Co-authored-by: Codex <codex@openai.com>
|
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
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. |
|
Hey, could you resolve the conflicts and add a changeset? |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@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-forms
@emdash-cms/plugin-webhook-notifier
commit: |
|
Thanks for the thorough work here. Since this was opened, #302 has been merged, which fixes the admin-side form reset (replacing invalidateQueries with setQueryData cache patching). However, this PR contains two fixes that #302 doesn't address and that are still worth landing:
Would you be up for extracting those into one or two smaller PRs? Both would be good to land, but the server-side fix in particular is self-contained and should be straightforward to land on its own. |
|
This PR has been inactive for 14 days. It will be closed automatically in 7 days if there is no further activity. If you're still working on this, please push an update or leave a comment. |
There was a problem hiding this comment.
This is a well-targeted, sound bug-fix PR. The approach—fixing the server-side autosave response for revision-backed collections, eliminating the redundant post-autosave refetch, guarding against stale-response overwrites with a snapshot comparison, and making the rich-text editor reconcile post-mount value changes—is exactly what the three compounding issues call for.
I traced the full flow in ContentEditPage → ContentEditor → PortableTextEditor and in the runtime handleContentUpdate path. The server-side merge of draft revision data into the response item is done correctly and _rev remains valid because encodeRev only uses version and updatedAt, which are untouched. The client-side snapshot guard (currentDataRef.current === autosaveSnapshot) works because serializeEditorState returns a string and === compares primitive values. The cache update helper applyAutosaveResultToQueryCache updates both the content item and the draft revision query with matching keys, so the merged item memo stays consistent without a full invalidation. TipTap reconciliation uses a stable Portable-Text signature (stripping auto-generated _keys) to avoid false-positive setContent calls, and emitUpdate: false prevents onChange loops.
Tests cover all three fixes. I found no logic bugs, security issues, or regressions in the changed code.
The only remaining issue is a missing changeset. Both packages/core and packages/admin are published packages, and AGENTS.md requires a changeset for any behavior change in a published package. The PR checklist even leaves this box unchecked.
| bylines: bodyWithoutRev.bylines, | ||
| }); | ||
|
|
||
| if (result.success && result.data && usesDraftRevisions && result.data.item.draftRevisionId) { |
There was a problem hiding this comment.
[needs fixing] This PR changes the behavior of the published emdash package (the autosave response now returns merged draft-revision data) and of @emdash-cms/admin (new autosave cache-updating logic and editor reconciliation). AGENTS.md requires a changeset for any published-package behavior change:
If your change affects a published package's behavior, add a changeset. Without one, the change won't trigger a package release.
Please add a changeset with pnpm changeset, edit it to cover both emdash (patch) and @emdash-cms/admin (patch), and describe the user-facing fixes.
What does this PR do?
Fixes a cluster of related draft/autosave bugs in the admin editor for revision-enabled content.
This started from a verified repro on a published post:
PUTresponse returned stale contentGETrequest also returned stale base-row dataRoot cause turned out to be three separate compounding issues:
packages/corePUTresponse was built from the stale base content row instead of the active draft revisionpackages/adminGETpackages/adminPortableTextEditortreated its initial value as mount-only stateWhat this PR changes:
GETcall from the client (as the necessary data is in the autosave response).Relates to #272, but extends beyond that admin-only reset loop to also fix the stale revision-backed autosave response, the redundant post-autosave refetch, and the in-flight autosave overwrite edge case.
Type of change
Checklist
pnpm typecheckpassespnpm --silent lint:json | jq '.diagnostics | length'returns 0pnpm testpasses (or targeted tests for my change)pnpm formathas been runAI-generated code disclosure
But with a lot of prior verification, including with Playwright, and a lot of discussion on the solution