Skip to content

Fixed Top Posts stuck loading or hiding data when web analytics is off#29103

Merged
9larsons merged 5 commits into
mainfrom
slars/pensive-mayer-965eed
Jul 4, 2026
Merged

Fixed Top Posts stuck loading or hiding data when web analytics is off#29103
9larsons merged 5 commits into
mainfrom
slars/pensive-mayer-965eed

Conversation

@9larsons

@9larsons 9larsons commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • createQuery/createPaginatedQuery/createInfiniteQuery in admin-x-framework now derive isLoading from fetchStatus !== 'idle' instead of passing React Query's raw isLoading through. A disabled query that never fetches otherwise reports isLoading: true forever, since status only leaves 'loading' on an actual fetch dispatch.
  • Removed enabled: webAnalyticsEnabled from useTopPostsViews in the Stats Overview page. getTopPostsViews already returns real DB-backed data (posts, authors, sent/opened counts, member attribution, clicks) when Tinybird isn't configured — gating the fetch discarded all of that, not just view counts. The views metric stays hidden via the existing showWebAnalytics check in TopPosts.
  • Skipped a DB lookup in getTopPostsViews that ran unconditionally but was always discarded when there was no views data to enrich.

Test plan

  • apps/admin-x-framework unit tests pass (161 tests)
  • apps/stats unit tests pass (234 tests)
  • ghost/core test/unit/server/services/stats/posts.test.js passes unmodified (46 tests)
  • eslint clean on all changed files

- useTopPostsViews is gated with `enabled: webAnalyticsEnabled` (#28713) to
  stop it polling Ghost when analytics is off, but the raw `isLoading` it
  returns was fed straight into the card's loading state
- React Query v4 keeps `isLoading` true forever for a disabled query that
  never fetched, so with web analytics off the card never left its skeleton
  loading state
- only report loading while the query is actually allowed to run, matching
  the pattern already used by useTinybirdQuery for the same enabled/disabled
  split
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for this team, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Changes

The admin-x-framework query hook factories now gate isLoading on both React Query loading state and fetchStatus !== 'idle'. In stats overview, the top-posts views query no longer depends on the web analytics setting. On the server, getTopPostsViews now skips the enrichment pipeline when Tinybird returns no viewsData.

Possibly related PRs

  • TryGhost/Ghost#28713: Related change to the same useTopPostsViews enabled gating in apps/stats/src/views/Stats/Overview/overview.tsx.

Suggested reviewers: weylandswart

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly describes the main change: fixing Top Posts loading and data visibility when web analytics is off.
Description check ✅ Passed The description directly matches the changeset and explains the loading fix, fetch gating removal, and backend optimization.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch slars/pensive-mayer-965eed

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Jul 4, 2026

Copy link
Copy Markdown

🤖 Nx Cloud AI Fix

Ensure the fix-ci command is configured to always run in your CI pipeline to get automatic fixes in future runs. For more information, please see https://nx.dev/ci/features/self-healing-ci


View your CI Pipeline Execution ↗ for commit c38d8f5

Command Status Duration Result
nx run @tryghost/admin-x-settings:test:acceptance ✅ Succeeded 10m 41s View ↗
nx run-many --target=build --projects=tag:publi... ✅ Succeeded 1s View ↗
nx run ghost:test:ci:integration ✅ Succeeded 2m 33s View ↗
nx run-many -t test:unit -p @tryghost/admin-x-f... ✅ Succeeded 6m 49s View ↗
nx run ghost:test:integration ✅ Succeeded 2m 55s View ↗
nx run @tryghost/admin:build ✅ Succeeded 4m 16s View ↗
nx run ghost:test:legacy ✅ Succeeded 3m 1s View ↗
nx run ghost:test:e2e ✅ Succeeded 2m 32s View ↗
Additional runs (5) ✅ Succeeded ... View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-07-04 19:39:14 UTC

…ries

- the previous commit worked around React Query v4's stuck-`isLoading`
  behavior only at the Top Posts call site, but every hook built on
  createQuery/createPaginatedQuery/createInfiniteQuery in admin-x-framework
  has the same issue for any `enabled: false` query that never fetches
- fixed it once in the three shared factories instead: `isLoading` is now
  gated on `fetchStatus !== 'idle'`, so a disabled query correctly reports
  not-loading instead of loading-forever
- reverted the local workaround in the Stats Overview page now that the
  underlying hook behaves correctly
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for this team, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@9larsons 9larsons changed the title Fixed Top Posts card stuck loading when web analytics is disabled Fixed disabled queries getting stuck in a loading state forever Jul 4, 2026

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
apps/stats/src/views/Stats/Overview/overview.tsx (1)

78-78: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use isInitialLoading for the top-posts skeleton
useTopPostsViews already exposes isInitialLoading; using that here avoids the manual webAnalyticsEnabled && gate while keeping the disabled-query loading state correct.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/stats/src/views/Stats/Overview/overview.tsx` at line 78, The top-posts
skeleton loading state in Overview is gating on a manual webAnalyticsEnabled
check instead of using the query’s built-in initial loading state. Update the
logic around useTopPostsViews in Overview so it relies on isInitialLoading from
the hook, and remove the extra manual enabled-condition check for the skeleton
visibility.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/stats/src/views/Stats/Overview/overview.tsx`:
- Line 78: The top-posts skeleton loading state in Overview is gating on a
manual webAnalyticsEnabled check instead of using the query’s built-in initial
loading state. Update the logic around useTopPostsViews in Overview so it relies
on isInitialLoading from the hook, and remove the extra manual enabled-condition
check for the skeleton visibility.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b2c369ae-aae8-4edf-931d-c4ba6d5b0098

📥 Commits

Reviewing files that changed from the base of the PR and between 71a4b0d and 139ad6a.

📒 Files selected for processing (1)
  • apps/stats/src/views/Stats/Overview/overview.tsx

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
apps/admin-x-framework/src/utils/api/hooks.ts (1)

62-68: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting the duplicated fetch-gate logic.

The same comment and one-line computation is repeated verbatim across all three factories. A small shared helper (e.g. isQueryLoading(result)) would avoid drift if this logic needs to change again.

♻️ Suggested helper
+const isQueryLoading = (result: {isLoading: boolean; fetchStatus: string}) =>
+    result.isLoading && result.fetchStatus !== 'idle';
+
 export const createQuery = ...
     return {
         ...result,
         data,
-        isLoading: result.isLoading && result.fetchStatus !== 'idle'
+        isLoading: isQueryLoading(result)
     };

Also applies to: 108-115, 152-158

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/admin-x-framework/src/utils/api/hooks.ts` around lines 62 - 68, The
fetch-status gating logic for `isLoading` is duplicated across the query
factories, so extract it into a shared helper such as `isQueryLoading(result)`
and use that helper in each factory that returns `{ ...result, data, isLoading
}`. Keep the current behavior exactly the same by centralizing the
`result.isLoading && result.fetchStatus !== 'idle'` check in one place, so the
implementations in the affected hook factories stay consistent and easier to
update.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/admin-x-framework/src/utils/api/hooks.ts`:
- Around line 62-68: The fetch-status gating logic for `isLoading` is duplicated
across the query factories, so extract it into a shared helper such as
`isQueryLoading(result)` and use that helper in each factory that returns `{
...result, data, isLoading }`. Keep the current behavior exactly the same by
centralizing the `result.isLoading && result.fetchStatus !== 'idle'` check in
one place, so the implementations in the affected hook factories stay consistent
and easier to update.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ea6768fe-8ce5-44d9-999b-cc7c03567ac4

📥 Commits

Reviewing files that changed from the base of the PR and between 139ad6a and 41f68c6.

📒 Files selected for processing (1)
  • apps/admin-x-framework/src/utils/api/hooks.ts

- useTopPostsViews was gated behind `enabled: webAnalyticsEnabled`, so the
  whole request (and all the Ghost-DB-backed data it returns: authors, sent/
  opened counts, member attribution, clicks) was skipped whenever web
  analytics was disabled or unconfigured, not just the view counts
- getTopPostsViews already degrades gracefully without a tinybirdClient
  (posts-stats-service.js) - it still returns recently published posts from
  Ghost's own DB, it just can't attach real view numbers
- the browser never talks to Tinybird directly for this endpoint, so gating
  the fetch bought no protection against Tinybird load; it only threw away
  data Ghost could serve on its own
- removed the gate so the query always runs; the views metric itself is
  still correctly hidden via showWebAnalytics in the TopPosts component
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for this team, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@9larsons 9larsons changed the title Fixed disabled queries getting stuck in a loading state forever Fixed Top Posts getting stuck loading (or hiding real data) when web analytics is off Jul 4, 2026
no ref

- postsWithViews is built by mapping over viewsData, so whenever viewsData is
  empty (no tinybirdClient, or Tinybird returned nothing for the range) the
  posts/authors/attribution/click queries feeding that lookup were run for
  nothing - the result was always going to be []
- skip that block entirely when viewsData is empty and go straight to the
  recency-based backfill query, which already covers this case
- no behavior change: same tests (posts.test.js) pass unmodified
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for this team, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

no ref

- trimmed comments that restated what the diff already makes clear
@cursor

cursor Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bugbot is not enabled for this team, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@9larsons 9larsons changed the title Fixed Top Posts getting stuck loading (or hiding real data) when web analytics is off Fixed Top Posts stuck loading or hiding data when web analytics is off Jul 4, 2026
@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 77 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.30%. Comparing base (aee6d24) to head (cfec00d).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
.../core/server/services/stats/posts-stats-service.js 0.00% 77 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #29103      +/-   ##
==========================================
- Coverage   74.30%   74.30%   -0.01%     
==========================================
  Files        1565     1565              
  Lines      135712   135716       +4     
  Branches    16463    16461       -2     
==========================================
- Hits       100841   100838       -3     
- Misses      33842    33848       +6     
- Partials     1029     1030       +1     
Flag Coverage Δ
admin-tests 55.16% <ø> (-0.02%) ⬇️
e2e-tests 76.45% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@9larsons 9larsons enabled auto-merge (squash) July 4, 2026 19:37
@9larsons 9larsons merged commit 99b1411 into main Jul 4, 2026
46 checks passed
@9larsons 9larsons deleted the slars/pensive-mayer-965eed branch July 4, 2026 19:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant