Skip to content

[AI Chapters] Show indicator until fingerprinting settles#5460

Open
sztomek wants to merge 2 commits into
mainfrom
feat/chapter-fingerprint-sync
Open

[AI Chapters] Show indicator until fingerprinting settles#5460
sztomek wants to merge 2 commits into
mainfrom
feat/chapter-fingerprint-sync

Conversation

@sztomek

@sztomek sztomek commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Description

This is a follow-up of #5445
Now we show an indicator on the top of the chapters page until fingerprinting settles.

Fixes PCDROID-616 https://linear.app/a8c/issue/PCDROID-616/show-indicator-while-fingerprinting-is-in-progress

Testing Instructions

  1. Find a podcast episode with generated chapters (e.g. The Daily episodes)
  2. Open an episode
  3. Select the Chapters tab
  4. Hit a chapter to play
  5. Notice the indicator
  6. Wait a bit until the fingerprinting finishes
  7. Notice the indicator disappearing

Screenshots or Screencast

Screenshot_20260622_160601

Checklist

  • If this is a user-facing change, I have added an entry in CHANGELOG.md
  • Ensure the linter passes (./gradlew spotlessApply to automatically apply formatting/linting)
  • I have considered whether it makes sense to add tests for my changes
  • All strings that need to be localized are in modules/services/localization/src/main/res/values/strings.xml
  • Any jetpack compose components I added or changed are covered by compose previews
  • I have updated (or requested that someone edit) the Event Horizon schema to reflect any new or changed analytics.

@sztomek sztomek added this to the 8.15 milestone Jun 22, 2026
@sztomek sztomek requested a review from a team as a code owner June 22, 2026 14:27
@sztomek sztomek requested review from Copilot and geekygecko and removed request for a team June 22, 2026 14:27
@sztomek sztomek added [Type] Enhancement Improve an existing feature. [Area] Chapters Episode chapters labels Jun 22, 2026

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@geekygecko geekygecko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for making this change. I have asked the designers for feedback here p1782174481298809-slack-C05RR9P9RAT

@wpmobilebot wpmobilebot modified the milestones: 8.15, 8.16 Jun 23, 2026
@wpmobilebot

Copy link
Copy Markdown
Collaborator

Version 8.15 has now entered code-freeze, so the milestone of this PR has been updated to 8.16.

@sztomek sztomek force-pushed the feat/chapter-fingerprint-sync branch from 547c986 to c0d95f5 Compare July 3, 2026 10:38
@claude

claude Bot commented Jul 3, 2026

Copy link
Copy Markdown

Claude finished @sztomek's task in 2m 18s —— View job


PR Review — AI Chapters fingerprinting indicator

I reviewed the diff, the touched files, and FingerprintTimingManager for context. Overall this is a clean, well-tested change — the state derivation is correct for the common path, the feature is properly gated behind SYNCED_TRANSCRIPTS + AppPlatform.Phone, and the four new ViewModel tests cover the meaningful branches (preparing/generated, embedded chapters, other episode, non-phone). Nice work. A few notes below.

🟡 Suggestions

1. Missing Compose preview (inline)
AligningChaptersRow has no @Preview, while ChapterRow and ChaptersHeader in the same package each ship a full themed set. The checklist ticks "compose components… covered by compose previews," so this seems like an oversight. It also reads LocalChaptersTheme.current, so a preview would validate the theme wiring.

2. activeEpisodeUuid read is not reactive (inline)
activeEpisodeUuid is a @Volatile var read inside map on stateFlow, so the derived flag only recomputes on a stateFlow emission. It works today because prepareForEpisode() sets activeEpisodeUuid before flipping the state to Preparing, but that ordering is an implicit contract worth a comment (or derive the active episode from a flow). Not blocking.

🔵 Questions / minor

  • "Preparing" ≠ "aligning chapters" in every case. The indicator shows for any State.Preparing on an episode with generated chapters, but the full-timeline chapter alignment only runs when shouldRunEagerPass is true (hasGeneratedChapters && (isDownloaded || unmetered)). For a streaming episode on a metered connection, preparation is playhead-following (transcript highlighting) rather than a full chapter re-align, yet the copy still reads "Aligning chapters…". Is showing it there intended, or should the indicator track the eager pass specifically?
  • Never-settling case. If a stream never reaches MINIMUM_COVERAGE_FOR_ACTIVE (and never fails/goes unavailable), Preparing persists and so does the spinner. Probably acceptable, just flagging the UX edge.
  • Accessibility (nit): the CircularProgressIndicator has no semantics/contentDescription. The adjacent TextP50 label carries the meaning, so this is low priority.
  • Material2 CircularProgressIndicator/Divider usage is consistent with the rest of this (not-yet-migrated) module, so that's fine per the guidelines.

None of these are blockers — (1) and (2) are the ones I'd address before merge.

Comment on lines +108 to +129
@Composable
private fun AligningChaptersRow(
modifier: Modifier = Modifier,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 20.dp, vertical = 8.dp),
) {
CircularProgressIndicator(
color = LocalChaptersTheme.current.headerTitle,
strokeWidth = 2.dp,
modifier = Modifier.size(16.dp),
)
TextP50(
text = stringResource(LR.string.chapters_aligning),
color = LocalChaptersTheme.current.headerTitle,
)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Every other composable in this package (ChapterRow, ChaptersHeader) ships a full set of themed @Previews, and the PR checklist claims "Any jetpack compose components I added or changed are covered by compose previews." AligningChaptersRow has none. Since it reads LocalChaptersTheme.current, a preview would also catch the theme wiring. Consider adding one (matching the existing Light/Dark/etc. set) so the indicator is visually verifiable.

Comment on lines +95 to +100
fingerprintTimingManager.stateFlow
.map { state ->
state is FingerprintTimingManager.State.Preparing &&
fingerprintTimingManager.activeEpisodeUuid == episodeId
}
.distinctUntilChanged()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

activeEpisodeUuid is a plain @Volatile var, not part of a flow, but it's read inside map on stateFlow. This means the derived boolean only recomputes when stateFlow emits — a change to activeEpisodeUuid alone will not re-trigger it.

This happens to work today because FingerprintTimingManager.prepareForEpisode() sets activeEpisodeUuid = episodeUuid (line 396) before setting _stateFlow.value = State.Preparing (line 422). But it's fragile: if the manager ever switches episodes while remaining in Preparing (e.g. resetState() nulls activeEpisodeUuid without changing the state, then a new prepare sets Preparing again — which MutableStateFlow dedups, emitting nothing), an already-subscribed consumer wouldn't observe the episode change. Worth a comment noting the ordering dependency, or better, deriving the active episode from the flow itself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Area] Chapters Episode chapters [Type] Enhancement Improve an existing feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants