Skip to content

fix(admin): refetch content after save/publish on non-i18n sites (#1557)#1568

Merged
ascorbic merged 1 commit into
emdash-cms:mainfrom
Emdash-Bug-Testing:fix/publish-button-locale-key-1557
Jun 26, 2026
Merged

fix(admin): refetch content after save/publish on non-i18n sites (#1557)#1568
ascorbic merged 1 commit into
emdash-cms:mainfrom
Emdash-Bug-Testing:fix/publish-button-locale-key-1557

Conversation

@marcusbellamyshaw-cell

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes the publish/save controls going stale after editing a published post, as reported in #1557. After saving a title change, the "Publish changes" button now appears immediately (previously it only showed after a hard refresh), and after publishing, the button immediately flips to "Unpublish" (previously it stayed on "Publish changes").

Root cause: the editor reads the content item under a React Query key scoped to the active locale — which is undefined when i18n is not configured (router.tsx, ContentEditPage). The save/publish/unpublish/discard/schedule mutations, however, invalidated a key scoped to rawItem.locale, which is the database default "en" even on non-i18n sites. React Query's partial matcher compares { locale: undefined } against { locale: "en" }, finds no match, and never refetches the item — so the draft-status pointers (liveRevisionId / draftRevisionId) the buttons key off stay stale until a manual refresh. This is why both reporters see it "since forever": it triggers on any site without i18n enabled.

Fix: invalidate by the ["content", collection, id] prefix (dropping the locale object) in all six content mutations. The id already uniquely identifies the row (translations are separate rows), so the locale object was redundant for uniqueness — and a prefix match correctly refetches the item regardless of which locale tab is active.

Closes #1557.

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable) — no new strings; messages.po not touched.
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion — n/a (bug fix)

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 4.8 (Claude Code)

Screenshots / test output

New test packages/admin/tests/publish-button-locale.test.tsx renders the real ContentEditor through the router with i18n off and covers both reported symptoms:

  • flips 'Publish changes' to 'Unpublish' after a successful publish
  • shows 'Publish changes' after editing the title and saving

Both fail on main (button never updates) and pass with this fix. Verified red→green by reverting only the invalidation-key change. Full admin suite: 1015/1016 pass (the one unrelated failure, a MediaPickerModal URL-probe timing test, passes in isolation and is not touched by this change).

…ash-cms#1557)

The content editor reads the item under a query key scoped to the active
locale (undefined when i18n is off), but the save/publish/unpublish/discard/
schedule mutations invalidated a key scoped to rawItem.locale (the DB default
"en"). React Query's partial matcher never matched, so the item was never
refetched and the "Publish changes" / "Unpublish" buttons stayed stale until a
hard refresh. Invalidate by (collection, id) prefix so it matches regardless
of locale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jun 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 18f6467

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
@emdash-cms/admin Patch
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

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

@pkg-pr-new

pkg-pr-new Bot commented Jun 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@1568

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@1568

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@1568

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@1568

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@1568

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@1568

emdash

npm i https://pkg.pr.new/emdash@1568

create-emdash

npm i https://pkg.pr.new/create-emdash@1568

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@1568

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@1568

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@1568

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@1568

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@1568

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@1568

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@1568

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@1568

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@1568

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@1568

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@1568

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@1568

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@1568

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@1568

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@1568

commit: 18f6467

@ascorbic ascorbic added bot:review Trigger an emdashbot code review on this PR and removed bot:review Trigger an emdashbot code review on this PR labels Jun 23, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a focused, well-explained bug fix with a sound approach and solid test coverage.

Approach. The root cause is correctly diagnosed: the editor reads the content item with a React Query key that includes { locale: activeLocale }, but on non-i18n sites activeLocale is undefined; the save/publish mutations invalidated with { locale: rawItem?.locale ?? activeLocale } (which falls back to the DB default "en"), so React Query never matched the active query and the buttons stayed stale. Changing the invalidation to the ["content", collection, id] prefix is the right fix: it matches the locale-scoped read query regardless of whether activeLocale is undefined or a real locale, and because translations are separate rows with separate IDs, the id is already unique enough that the locale object is unnecessary for invalidation precision.

What I checked.

  • ContentEditPage reads with queryKey: ["content", collection, id, { locale: activeLocale }] and the six mutations now invalidate with queryKey: ["content", collection, id]. React Query v5 (5.90.21) matches by prefix by default, so this invalidates the active query.
  • The prefix is not over-broad: the list query uses ["content", collection], which is shorter and therefore is not matched by the item prefix.
  • ContentEditor derives the publish button from getDraftStatus(item) (i.e., liveRevisionId vs draftRevisionId), so the test fixtures correctly model the two reported symptoms.
  • No other invalidateQueries in the admin source uses the old ["content", collection, id, { locale: ... }] shape, so the fix is complete for this bug.
  • The repro test renders the real router/editor (not a mocked ContentEditor), exercises both the publish and save flows, and deliberately returns a "clean" PUT response so the button only appears from the invalidated GET refetch.
  • AGENTS.md conventions are respected: no new UI strings; only existing Lingui messages are used; Tailwind/i18n/authorization/SQL concerns don't apply; a changeset is present and user-focused.

I don't see any blocking issues. The changeset is well-written, the tests target the exact cache-key mismatch, and the fix is minimal and consistent across all six content mutations.

@ascorbic ascorbic left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks!

@ascorbic ascorbic merged commit 43a70d4 into emdash-cms:main Jun 26, 2026
46 checks passed
@emdashbot emdashbot Bot mentioned this pull request Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/admin bot:review Trigger an emdashbot code review on this PR cla: signed review/needs-review No maintainer or bot review yet size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug with publish button when editing posts

2 participants