Skip to content

Stats post detail: prefetch email tab availability and seed it from the email pages - #113931

Open
dognose24 wants to merge 2 commits into
trunkfrom
fix/stats-464-email-tabs-prefetch
Open

Stats post detail: prefetch email tab availability and seed it from the email pages#113931
dognose24 wants to merge 2 commits into
trunkfrom
fix/stats-464-email-tabs-prefetch

Conversation

@dognose24

@dognose24 dognose24 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Part of STATS-464.

Follow-up to #113843 (merged); rebased onto trunk.

Proposed Changes

  • Extract the email tab availability query into a shared postEmailStatsAvailabilityQueryOptions factory and prefetch it from the post() route controller. The gate skips the warm-up only when subscriptions is provably inactive: at route time the Jetpack module state is usually not loaded yet (QueryJetpackModules runs after mount and /me/sites does not request active_modules), so an unknown state still prefetches, while the page's own enabled check stays strict. The /rate request now runs in parallel with the post detail chunk download instead of after mount.
  • When clicking Post traffic from the Email opens/clicks pages, seed the availability cache first (seedPostEmailStatsAvailability): being on an email tab already proves the post has email stats, so that navigation renders the tab strip immediately.

Why are these changes being made?

  • After Stats post detail: show email tabs based on actual email stats #113843 the Post traffic / Email opens / Email clicks tab strip is driven by an async /rate query that is deliberately not persisted, so on a fresh visit the strip appears only once the request resolves. Review of Stats post detail: show email tabs based on actual email stats #113843 measured a ~760ms gap when navigating from the Emails page (a pre-existing behavior on trunk, ~640ms).
  • Prefetching at the route level hides the request latency behind the chunk download on all entry paths; seeding on the Emails → Post traffic navigation removes the flash entirely on the one path where the answer is already known. A stale seeded entry cannot mislead: it is only written when email stats provably exist, and real responses overwrite it.

Testing Instructions

  1. On a site with newsletter posts, open Stats → Traffic and click a post that was sent as an email. In DevTools' Network tab, observe the .../stats/opens/emails/<post>/rate request starting together with the async-load-...-stats-post-detail chunk rather than after it.
  2. Open the same post's Email opens page, then click Post traffic: the tab strip should be present as soon as the page renders, with no /rate request needed for it to appear.
  3. Regression checks: a post that was never emailed still shows no tabs; the home page entry (post ID 0) shows no tabs and issues no /rate request; sites without email stats support (no subscriptions module) issue no /rate request from the controller.

Pre-merge Checklist

  • Has the general commit checklist been followed? (PCYsg-hS-p2)
  • Have you written new tests for your changes?
  • Have you tested the feature in Simple (P9HQHe-k8-p2), Atomic (P9HQHe-jW-p2), and self-hosted Jetpack sites (PCYsg-g6b-p2)? (Simple and Atomic verified on local dev: prefetch runs in parallel with the chunk — on a full reload of the post detail page the tabs are present on first paint, where before they popped in late; Emails → Post traffic shows the tabs immediately with no /rate request. Self-hosted Jetpack / Odyssey not tested yet.)
  • Have you checked for TypeScript, React or other console errors?
  • For UI changes, have you tested the affected components in dark mode?
  • Have you tested accessibility for your changes? Ensure the feature remains usable with various user agents (e.g., browsers), interfaces (e.g., keyboard navigation), and assistive technologies (e.g., screen readers) (PCYsg-S3g-p2).
  • Have you used memoizing on expensive computations? More info in Memoizing with create-selector and Using memoizing selectors and Our Approach to Data
  • Have we added the "[Status] String Freeze" label as soon as any new strings were ready for translation (p4TIVU-5Jq-p2)?
    • For UI changes, have we tested the change in various languages (for example, ES, PT, FR, or DE)? The length of text and words vary significantly between languages.
  • For changes affecting Jetpack: Have we added the "[Status] Needs Privacy Updates" label if this pull request changes what data or activity we track or use (p4TIVU-aUh-p2)?

@dognose24
dognose24 changed the base branch from trunk to fix/stats-456-post-detail-email-tabs August 31, 2026 03:14
@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown

Here is how your PR affects size of JS and CSS bundles shipped to the user's browser:

Sections (~394 bytes added 📈 [gzipped])

Details
Name Parsed Gzip
stats +1.48 kB +0.1% +394 B +0.1%

Sections contain code specific for a given set of routes. Is downloaded and parsed only when a particular route is navigated to.

Async-loaded Components (~141 bytes removed 📉 [gzipped])

Details
Name Parsed Gzip
async-load-calypso-my-sites-stats-stats-post-detail -534 B -1.6% -141 B -1.4%

React components that are loaded lazily, when a certain part of UI is displayed for the first time.

Legend

What is parsed and gzip size?

Parsed Size: Uncompressed size of the JS and CSS files. This much code needs to be parsed and stored in memory.
Gzip Size: Compressed size of the JS and CSS files. This much data needs to be downloaded over network.

Base automatically changed from fix/stats-456-post-detail-email-tabs to trunk August 31, 2026 04:00
dognose24 and others added 2 commits August 31, 2026 12:49
…he email pages

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5G7Fg8tpLg8Qe9EaSs79t
…s provably inactive

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C5G7Fg8tpLg8Qe9EaSs79t
@dognose24
dognose24 force-pushed the fix/stats-464-email-tabs-prefetch branch from cc68906 to 51db32c Compare August 31, 2026 04:51
@dognose24
dognose24 marked this pull request as ready for review August 31, 2026 14:24
@dognose24
dognose24 requested a review from kangzj August 31, 2026 14:25
@matticbot matticbot added the [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. label Aug 31, 2026
@dognose24
dognose24 requested review from a team, Nikschavan and chihsuan August 31, 2026 14:25
@dognose24 dognose24 self-assigned this Aug 31, 2026
@dognose24
dognose24 requested a lite review from Copilot August 31, 2026 14:26

Copilot AI 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.

Pull request overview

This PR improves the Stats post-detail “Email opens/clicks” tab-strip UX by warming the /rate-based availability query earlier and by seeding the cache on navigations where email availability is already known (Email → Post traffic), reducing or eliminating the “tabs pop in late” flash.

Changes:

  • Extracts the email-tab availability react-query options into a shared postEmailStatsAvailabilityQueryOptions factory and prefetches it from the stats post() route controller.
  • Seeds the availability cache when navigating from Email opens/clicks back to Post traffic to render the tab strip immediately.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
client/my-sites/stats/stats-details-navigation/index.tsx Seeds the email availability cache when navigating from email tabs to Post traffic.
client/my-sites/stats/hooks/use-post-email-stats-availability-query.ts Extracts shared query options and adds a cache seeding helper for known-positive navigations.
client/my-sites/stats/controller.jsx Prefetches the email availability query during route handling to overlap with chunk loading.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +44 to +47
const { queryKey } = postEmailStatsAvailabilityQueryOptions( siteId, postId );
if ( queryClient.getQueryData( queryKey ) === undefined ) {
queryClient.setQueryData( queryKey, { total_sends: 1 } );
}
Comment on lines +429 to +432
const canHaveEmailStats =
!! supportsEmailStats &&
( isSimpleSite( state, siteId ) ||
isJetpackModuleActive( state, siteId, 'subscriptions', true ) !== false );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants