Skip to content

fix(edit-content): refresh History, Comments and Reference Pages after save #36617 - #36901

Draft
adrianjm-dotCMS wants to merge 2 commits into
mainfrom
adrianjm-dotCMS/history-comments-side-panel-not-refreshing-when
Draft

fix(edit-content): refresh History, Comments and Reference Pages after save #36617#36901
adrianjm-dotCMS wants to merge 2 commits into
mainfrom
adrianjm-dotCMS/history-comments-side-panel-not-refreshing-when

Conversation

@adrianjm-dotCMS

@adrianjm-dotCMS adrianjm-dotCMS commented Aug 5, 2026

Copy link
Copy Markdown
Member

Fixes #36617

Proposed Changes

The sidebar's History, Comments and Reference Pages stayed stale after a save/publish and needed a manual page reload. There were two independent root causes.

  • The refresh effects lived on a component that gets destroyed. They sat on DotEditContentSidebarComponent, which the layout's @if destroys and recreates — taking the effects with it. Moved into store-level withHooks({ onInit }) in withActivities and withInformation, so they live as long as the store does. This matches the existing lock / workflow / history features.

  • withHistory did not recognise a newly minted version. Its memo only invalidated on ${identifier}:${languageId}, but a save mints a new inode under the same identifier and locale, so loadVersions never re-fired. The symptom differed per host, which is why it looked like two separate bugs:

Screen.Recording.2026-08-05.at.3.47.52.PM.mov
Full-screen Dialog
Navigates on save? Yes → runs initializeExistingContent No
Effect on the list Emptied (versions: []) Left untouched
What you saw "it went blank" "it didn't update"

Added two invalidation signals: a cleared list (status === INIT, ignored mid-reload so it cannot fetch the identifier being left behind) and a live-inode baseline that only advances when not viewing a historical version, so browsing versions — and returning from them — does not refetch.

  • Fixed signal granularity in both effects. They read store.uiState(), i.e. the whole slice. Every writer replaces that object wholesale, so the effects refetched on unrelated UI changes — including the view flip loadVersions performs internally, costing 3–4 redundant requests per save. Now they read the store.uiState.isSidebarOpen() leaf.

Checklist

  • Tests
  • Translations
  • Security Implications Contemplated (none — no new endpoints, inputs or permissions; only client-side refresh timing)

Additional Info

Tests: 2180 passing across 111 suites in edit-content; lint and typecheck clean. New coverage includes publish-in-dialog, publish-while-comparing, the mid-reload case, historical/compare round trips, and a regression test for the signal-granularity fix (verified it fails against the previous code).

One existing test changed expectationshould not reload anything when only the version inode changes asserted that moving only the inode does not reload, which was literally the bug. Rewritten as should reload versions but not push publish history when the live inode moves. This is safe because browsing versions always goes through loadVersionContent, which sets isViewingHistoricalVersion; a bare inode move only happens on save/publish. The browsing cases are now covered by dedicated tests that set the realistic state.

Known follow-ups, deliberately out of scope:

  • Push Publish History still does not reload on publish (kept the original per-identifier scoping).
  • clearVersions() / resetVersions() / clearPushPublishHistory() / resetPushPublishHistory() in history.feature.ts have no production call sites — dead code worth removing separately.

🤖 Generated with Claude Code

…r save #36617

The sidebar's History, Comments and Reference Pages stayed stale after a
save/publish, requiring a manual page reload. Two root causes:

- The refresh effects lived on DotEditContentSidebarComponent, which the
  layout's @if destroys and recreates, taking the effects with it. Moved
  them into store-level withHooks({onInit}) in withActivities and
  withInformation, matching the existing lock/workflow/history features.

- withHistory only invalidated its memo on `${identifier}:${languageId}`,
  but a save mints a new inode under the SAME identifier and locale, so
  loadVersions never re-fired. Full-screen rendered an empty list
  (initializeExistingContent had already cleared it); the dialog host,
  which never navigates, rendered a stale one. Added two invalidation
  signals: a cleared list (status INIT, ignored mid-reload so it cannot
  fetch the identifier being left behind) and a live-inode baseline that
  only advances when not viewing a historical version, so browsing
  versions and returning from them does not refetch.

Both effects now read the isSidebarOpen leaf instead of uiState(): every
writer replaces that slice wholesale, so depending on the object refetched
on unrelated UI changes, including the view flip loadVersions performs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @adrianjm-dotCMS's task in 6m 49s —— View job


Code Review — PR #36901

  • Read the diff and changed feature files
  • Analyze store hooks (activities, information, history)
  • Check signal granularity & invalidation logic
  • Post review

No issues found.

I traced every path the diff touches and the reasoning holds up:

withActivities / withInformation (store-level onInit effects) — Moving the refresh effects into withHooks({ onInit }) correctly ties their lifetime to the store rather than the @if-destroyed DotEditContentSidebarComponent. The effect() is created inside onInit, which runs in an injection context, matching the existing lock/workflow/history features. Reading the store.uiState.isSidebarOpen() leaf instead of store.uiState() is a valid deep-signal access and genuinely narrows the dependency so unrelated UI writes (including the internal view flip) no longer refetch.

No overlay-hang regression — I checked the interaction with isFullyLoaded (content.feature.ts:147). When the sidebar is closed during an in-place reload, the gated effects skip loading and information/activitiesStatus stay LOADING, but isFullyLoaded short-circuits to true on !isSidebarOpen, so the overlay still drops. Opening the sidebar later flips isSidebarOpen, re-fires the effect, and resolves the statuses. No stuck state.

withHistory invalidation (history.feature.ts:781-862) — The three-signal design is sound:

  • versionsCleared/pushPublishCleared are correctly suppressed while state === LOADING, so the mid-reload pass cannot fetch the outgoing identifier (the contentlet/state: LOADED patch lands together with statuses still INIT, so it fires on the next pass). Covered by "should not fetch with the outgoing identifier while a reload is in flight."
  • newLiveVersion is gated on !isViewingHistoricalVersion and a non-null baseline, and loadedLiveInode only advances when not viewing history — so the historical round-trip returns to a known inode and doesn't look "new," while a real post-publish inode move does reload. The publish-after-historical-round-trip test confirms the baseline bookkeeping.
  • Guarding the compare/historical patchState reset behind identityChanged && !isInitialLoad correctly preserves an active same-identity compare session across a refetch, which the compare-while-publish test exercises.

Test coverage — The rewritten "should reload versions but not push publish history when the live inode moves" expectation is justified: a bare inode move only happens on save/publish, and browsing always routes through loadVersionContent (which sets isViewingHistoricalVersion), now covered by dedicated historical/compare tests. The added suites cover the mid-reload, empty-list, compare, and round-trip cases, and the stated follow-ups (Push Publish History reload, dead clear*/reset* methods) are reasonable to defer.

The retained imports in the sidebar component (computed, inject, model, output) are all still used; the removed effect/untracked are no longer referenced there.
· adrianjm-dotCMS/history-comments-side-panel-not-refreshing-when

…+ e2e #36617

The feature specs verify the store effects against a synthetic store with no
component mounted, so they cannot catch the regression this fix addresses: the
effects used to live on DotEditContentSidebarComponent, which the layout's @if
destroys and recreates. Moving them back would leave every feature spec green.

Two layers close that gap, both parameterized over the full-screen and dialog
hosts since the bug had a different face in each:

- Integration (dot-edit-content.layout.component.spec.ts): real store mounted in
  the real layout with the sidebar as a MockComponent. Asserts the fetches happen
  while the sidebar is not rendered at all, that they survive the sidebar being
  destroyed and recreated, and that a save minting a new inode refreshes without
  any re-initialization (the dialog path). Verified these fail when the store
  hooks are removed.

- E2E (apps/dotcms-ui-e2e/.../sidebar/history-refresh.spec.ts): publishes and
  comments through the UI and asserts History/Comments update with no reload,
  in full-screen and in the dialog (reached via the relationship field's "New
  Content", which needs no page/template fixture). Verified against the pre-fix
  code: full-screen reproduced the empty list (expected 2, got 0) and the dialog
  reproduced the stale list (expected 2, got 1).

Dialog comments are deliberately not covered — the comment form is hidden for
content opened as 'new', which is the only mode that entry point offers. It
needs the UVE pencil flow and a page fixture; documented in the spec.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Frontend PR changes Angular/TypeScript frontend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

History & Comments side panel not refreshing when editing via UVE (works via content search)

1 participant