Skip to content

CMM-2133: Expandable referrers with children in new Stats#23066

Merged
adalpari merged 14 commits into
trunkfrom
feature/cmm-2133-stats-referrer-children
Jul 3, 2026
Merged

CMM-2133: Expandable referrers with children in new Stats#23066
adalpari merged 14 commits into
trunkfrom
feature/cmm-2133-stats-referrer-children

Conversation

@adalpari

@adalpari adalpari commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Description

Adds expand/collapse support for child referrers in the new Stats "Referrers" card and its detail screen, and makes the detail screen show the full referrer list.

Referrer groups returned by the API can contain a list of child referrers (e.g. the "Search Engines" group nests "Google Search", etc.). Previously these were discarded and every group rendered as a flat row, and the detail screen only showed the same ~10 items as the card.

Changes:

  • Thread a children list (name, url, views) from the datasource → repository → UI state → ViewModel. Groups with children now render an expand/collapse dropdown (following the UTM card pattern).
  • Child rows open their URL in a Chrome Custom Tab (ActivityNavigator.openInCustomTab).
  • Only rows that actually navigate/open something show a chevron: the decorative right-arrow was removed from the shared Most Viewed rows (which affects Posts & Pages, Clicks, Search Terms, Video Plays, File Downloads too), and the right-chevron is kept only on the tappable URL child rows.
  • The card requests a bounded set of referrers (max = 10), while the referrers detail screen re-fetches the full list itself (max = 0, which the server treats as "all"). The detail screen now self-fetches via a new MostViewedDetailViewModel (with Loading/Error/Loaded states) instead of receiving the whole item list through the launching Intent — this keeps the card request small and avoids passing a large parcelable list across the process boundary (which risked TransactionTooLargeException on high-traffic sites). This matches Posts & Pages in that the card still shows the first CARD_MAX_ITEMS entries.
  • The detail LazyColumn is keyed by index rather than by item id. Referrer ids are derived from name.hashCode(), which is not guaranteed unique (empty or duplicate names collide), so keying by id could crash the list with a duplicate-key error — more likely now that the detail lists the full referrer set.
  • The duplicated expand/collapse composables across the card and detail screens were extracted into a shared MostViewedCommonComposables.kt (reusing the existing StatsListRowContainer), and both NewStatsActivity and MostViewedDetailActivity inject ActivityNavigator rather than constructing it in a composable.
  • In the detail screen (a LazyColumn), the per-row expanded state uses rememberSaveable so an expanded group stays expanded when it scrolls off-screen and back.

Testing instructions

Referrers card — expandable children:

  1. Open the new Stats screen on a site whose referrers include a group with children (e.g. "Search Engines").
  • Verify groups with children show an up/down dropdown arrow (before the views count) and expand/collapse when tapped, revealing indented child rows.
  • Verify groups without children have no chevron and are not tappable.
  • Verify tapping a child row opens its URL in a Chrome Custom Tab.
  1. Tap Show all to open the Referrers detail screen.
  • Verify a brief loading indicator shows, then the detail lists more than 10 referrers (the full set), while the card still shows the first 10.
  • Verify the same expand/collapse and child-tap behavior works, and expanded state survives scrolling (expand a group near the top, scroll far down and back — it should still be expanded).
  • Rotate the device on the detail screen and verify it does not re-fetch/flicker and keeps its data.
  • (Optional) With no network, tap Show all and verify an error state with a Retry button that re-fetches.

Regression — shared Most Viewed cards:

  1. Open the Posts & Pages, Clicks, Search Terms, Video Plays and File Downloads cards.
  • Verify the rows look unchanged and no longer show a decorative right-arrow.
  • Tap Show all on each and verify the detail screens still open and render the passed items correctly

adalpari and others added 6 commits July 3, 2026 12:43
Referrer groups can contain child referrers (e.g. "Search Engines"),
but the new stats Referrers card discarded them and rendered a flat
list. Thread a children list (name, url, views) from the datasource
through the repository, UI state and ViewModel, and render referrer
groups with children as expand/collapse rows in both the card and the
detail screen, following the UTM card pattern. Tapping a child opens
its URL in a Chrome Custom Tab.

CMM-2133

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Remove the decorative right arrow from Most Viewed rows (shared by
posts, referrers, clicks, search terms, video plays and file downloads)
since it did not navigate anywhere. Move the expand/collapse dropdown
arrow before the views count on referrer groups with children, and show
a right arrow only on clickable URL child rows to signal they open a
link.

CMM-2133

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Expanded child rows sat flush against their parent row in the referrers
and UTM cards and detail screens. Add a small top margin so children are
visually separated when a group is expanded.

CMM-2133

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
When a group had more than one child, the child rows were rendered flush
against each other. Space them evenly in the referrers and UTM cards and
detail screens.

CMM-2133

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The referrers endpoint returns only 10 items when max is unset. Send an
explicit max=0 (which the server treats as unlimited) so it returns the
full referrer list, matching how Posts & Pages behaves: the card still
shows the first CARD_MAX_ITEMS entries while the detail screen shows all
of them.

CMM-2133

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Extract the duplicated chevron, child row and expandable-children block
from MostViewedCard and MostViewedDetailActivity into a shared
MostViewedCommonComposables file, reusing StatsListRowContainer for the
child row bar. Inject ActivityNavigator into NewStatsActivity and thread
the child-click callback down instead of constructing the navigator in a
composable and casting the context to Activity. Compute maxChildViews
only when a group is expanded, and document the referrers max=0
semantics.

CMM-2133

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@dangermattic

dangermattic commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator
2 Warnings
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
⚠️ PR is not assigned to a milestone.

Generated by 🚫 Danger

@wpmobilebot

wpmobilebot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

App Icon📲 You can test the changes from this Pull Request in Jetpack Android by scanning the QR code below to install the corresponding build.

App NameJetpack Android
Build TypeDebug
Versionpr23066-4f5ef54
Build Number1498
Application IDcom.jetpack.android.prealpha
Commit4f5ef54
Installation URL7h66v1gcg94n8
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

wpmobilebot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

App Icon📲 You can test the changes from this Pull Request in WordPress Android by scanning the QR code below to install the corresponding build.

App NameWordPress Android
Build TypeDebug
Versionpr23066-4f5ef54
Build Number1498
Application IDorg.wordpress.android.prealpha
Commit4f5ef54
Installation URL7op352rfhufs8
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

The detail screen renders rows in a LazyColumn, so the per-row expanded
state held in a plain remember was discarded when a row scrolled off
screen and reset to collapsed when it returned. Use rememberSaveable and
key the list by item id so an expanded group stays expanded across
scrolling.

CMM-2133

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@codecov

codecov Bot commented Jul 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 27.12418% with 223 lines in your changes missing coverage. Please review.
✅ Project coverage is 37.44%. Comparing base (ae87e14) to head (4f5ef54).
⚠️ Report is 1 commits behind head on trunk.

Files with missing lines Patch % Lines
...newstats/mostviewed/MostViewedCommonComposables.kt 0.00% 105 Missing ⚠️
...ui/newstats/mostviewed/MostViewedDetailActivity.kt 0.00% 72 Missing ⚠️
.../wordpress/android/ui/newstats/NewStatsActivity.kt 0.00% 13 Missing ⚠️
...roid/ui/newstats/datasource/StatsDataSourceImpl.kt 0.00% 13 Missing ⚠️
...s/android/ui/newstats/mostviewed/MostViewedCard.kt 0.00% 12 Missing ⚠️
...i/newstats/mostviewed/MostViewedDetailViewModel.kt 93.33% 0 Missing and 3 partials ⚠️
...ss/android/ui/newstats/utm/UtmCommonComposables.kt 0.00% 3 Missing ⚠️
...id/ui/newstats/mostviewed/MostViewedCardUiState.kt 88.88% 1 Missing ⚠️
...roid/ui/newstats/mostviewed/MostViewedViewModel.kt 90.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            trunk   #23066    +/-   ##
========================================
  Coverage   37.43%   37.44%            
========================================
  Files        2335     2337     +2     
  Lines      126326   126454   +128     
  Branches    17398    17422    +24     
========================================
+ Hits        47296    47354    +58     
- Misses      75156    75223    +67     
- Partials     3874     3877     +3     

☔ 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.

The referrers card fetched the full (max=0) list and passed it to the
detail screen through the launching Intent, and the detail LazyColumn was
keyed by item.id (derived from name.hashCode()). On sites with many
referrers this risked a TransactionTooLargeException, and empty/duplicate
referrer names could collide into duplicate keys and crash the list.

Bound the card request to REFERRERS_CARD_MAX and add fetchReferrersDetail
(max=0) so the detail screen self-fetches its own unbounded list via a new
MostViewedDetailViewModel (Loading/Error/Loaded), passing only the period
through the Intent. Key the detail list by index instead of item.id.

CMM-2133

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
adalpari and others added 3 commits July 3, 2026 13:58
Reset the expanded state when a row is reused for a different referrer, and
add unit tests covering MostViewedDetailViewModel load/error/retry behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
The referrers detail ViewModel surfaced raw exception messages to users;
log the exception and show the localized stats_error_unknown string
instead, and rethrow CancellationException. Make the referrers card's
expand/collapse state survive configuration changes by keying each row
on its item id and holding the expanded flag in rememberSaveable, so an
expanded group stays expanded after rotation (matching the detail screen)
while still resetting when the list reloads for a different referrer.

CMM-2133

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
StatsRepositoryTest tripped detekt's LargeClass rule after the referrer
tests were added. Extract them into a dedicated test class instead of
baselining, keeping the referrer tests grouped in one place.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@adalpari adalpari marked this pull request as ready for review July 3, 2026 12:40
@adalpari adalpari requested a review from nbradbury July 3, 2026 12:55
@nbradbury

Copy link
Copy Markdown
Contributor

@adalpari I've not tested this yet, but Claude had a couple of suggestions that I think are worth looking into.

Screenshot 2026-07-03 at 9 25 02 AM

@nbradbury

Copy link
Copy Markdown
Contributor

I believe this is unrelated to this PR, but I'm confused by what the percentages mean in referrers. I don't see them on the web or old stats.

stats

@nbradbury

Copy link
Copy Markdown
Contributor

Verify tapping a child row opens its URL in a Chrome Custom Tab.

This works but I think the icon could be replaced by one of the standard "open" icons.

open_in_browser_24dp_1F1F1F_FILL0_wght400_GRAD0_opsz24 open_in_new_24dp_1F1F1F_FILL0_wght400_GRAD0_opsz24

@adalpari

adalpari commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

I believe this is unrelated to this PR, but I'm confused by what the percentages mean in referrers. I don't see them on the web or old stats.

stats

The bar is just a visual indicator for the ranking. So the top number of hits is 100% (bar filled) and every other entru goes from there. It looks odd when there's just one or two entries, though.
Screenshot 2026-07-03 at 16 28 04

@adalpari

adalpari commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Verify tapping a child row opens its URL in a Chrome Custom Tab.

This works but I think the icon could be replaced by one of the standard "open" icons.

open_in_browser_24dp_1F1F1F_FILL0_wght400_GRAD0_opsz24 open_in_new_24dp_1F1F1F_FILL0_wght400_GRAD0_opsz24

I'm showing the icons coming in the JSON response, so I'm not sure there's a good way to correlate them... Anyway, can you show me a screenshot? I'm not actually seeing icons in these responses 🤔

Referrer ids are derived from name.hashCode(), which can collide on
empty/duplicate names. Two colliding rows sharing a key() could
misattribute the per-row rememberSaveable expanded state. Add the index
to the key to guarantee uniqueness while keeping the item id so the
saved state still resets when the referrer at a position changes after a
period reload.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@nbradbury

Copy link
Copy Markdown
Contributor

I'm showing the icons coming in the JSON response, so I'm not sure there's a good way to correlate them...

Ah, got it.

Anyway, can you show me a screenshot? I'm not actually seeing icons in these responses

Screenshot_1783089127

@adalpari

adalpari commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

I'm showing the icons coming in the JSON response, so I'm not sure there's a good way to correlate them...

Ah, got it.

Anyway, can you show me a screenshot? I'm not actually seeing icons in these responses

Screenshot_1783089127

Ahh, you mean those arrow icons! I thought you meant the referrer entry icon, which is out of the client's control.
Let me take a deeper look! :)

The card (MostViewedItemRow) and detail screen (DetailItemRow) each
carried a near-identical ~80-line expandable-row body that differed only
by the leading position number and the child indent. Consolidate them
into a single MostViewedExpandableRow in MostViewedCommonComposables.kt,
built on StatsListRowContainer and parameterized with position/child
indent (mirroring the UtmExpandableRow pattern), so future changes to
expand/collapse behavior live in one place.

Also swap the clickable child row's ChevronRight for the standard
AutoMirrored OpenInNew icon (with an "Open link" content description) to
make it clearer the row opens an external URL.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@adalpari

adalpari commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

@nbradbury I've made some changes from Claude's suggestions and also used a more appropriate icon. Could you take another look?

Screenshot 2026-07-03 at 16 58 53

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

Looks good! :shipit:

@adalpari adalpari enabled auto-merge (squash) July 3, 2026 15:29
@adalpari adalpari merged commit 0217de3 into trunk Jul 3, 2026
21 of 23 checks passed
@adalpari adalpari deleted the feature/cmm-2133-stats-referrer-children branch July 3, 2026 15:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants