Skip to content

refactor(frontend): split GeneratePodcastDialog and fix token-count races - #1067

Merged
lfnovo merged 1 commit into
mainfrom
refactor/frontend-split-podcast-dialog
Jul 11, 2026
Merged

refactor(frontend): split GeneratePodcastDialog and fix token-count races#1067
lfnovo merged 1 commit into
mainfrom
refactor/frontend-split-podcast-dialog

Conversation

@lfnovo

@lfnovo lfnovo commented Jul 11, 2026

Copy link
Copy Markdown
Owner

Summary

GeneratePodcastDialog.tsx had grown to 983 lines and hid two real concurrency bugs. This PR splits the file and fixes the races. Structural moves are behavior-identical; the two behavior changes are called out below.

Structural (behavior-identical)

  • ContentSelectionPanel extracted to frontend/src/components/podcasts/ContentSelectionPanel.tsx. It now gets the query client via useQueryClient() instead of a raw queryClient prop, and the handler props are named onNotebookToggle / onSourceModeChange / onNoteToggle.
  • Shared selection types/helpers extracted to frontend/src/components/podcasts/generate-podcast-selection.ts (SourceMode, NotebookSelection, formatNumber, hasSelections, getSourceDefaultMode).
  • Duplicated sourcesConfig/notesConfig reduction deduplicated: the identical logic in the token-count effect and buildContentFromSelections is now one selectionsToContextConfigs(selections) helper.
  • Dead translation cache removed: the tr = {...} block referenced a Proxy-based t() that no longer exists (translations are plain react-i18next now); the panel calls t() directly.

Behavior fixes

  • Token/char counter race: the effect awaited chatApi.buildContext serially per notebook on every selections change with no debounce and no cancellation — rapid checkbox toggling produced overlapping request storms with last-write-wins on setTokenCount. Now debounced (400ms) with a generation-counter stale guard: a newer effect run invalidates any in-flight loop, so stale responses can neither accumulate into the totals nor overwrite fresher counts.
  • Dialog close timing: handleSubmit closed via setTimeout(..., 500) "to ensure refetch completes" — a sleep racing a network call. useGeneratePodcast's onSuccess already awaits refetchQueries, and mutateAsync only resolves after onSuccess settles, so the dialog now closes immediately after await mutateAsync(...) — deterministically after the refetch.

Verification

Inside frontend/:

  • npm run lint — 0 errors (8 pre-existing warnings, none introduced)
  • npm run test — 12 files, 85 tests passed
  • npm run build — succeeds

Review in cubic

…aces

Extract ContentSelectionPanel and shared selection helpers into their
own modules, deduplicate the sources/notes context-config reduction
into selectionsToContextConfigs, delete the obsolete translation
cache (plain react-i18next t() now), debounce the token/char counter
with a stale-response guard, and close the dialog when the episode
refetch resolves instead of after a fixed 500ms timer.

@cubic-dev-ai cubic-dev-ai 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.

No issues found across 4 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

You’re at about 90% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Re-trigger cubic

@lfnovo
lfnovo merged commit 2bd9211 into main Jul 11, 2026
8 checks passed
@lfnovo
lfnovo deleted the refactor/frontend-split-podcast-dialog branch September 2, 2026 22:03
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