perf(mobile): composer draft no longer re-renders the thread screen when typing#21261

merged1
perf/mobile-render-9
perf/mobile-render-10
opened12d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 10d ago

Each keystroke in a thread's composer re-rendered the whole thread screen: the route, header, feed, floating controls and, on Android, the Home screen behind it.

Every keystroke replaces the composer drafts map, and four readers subscribed to the whole map: the thread composer state hook (feeding ThreadRouteContent), the selected-thread request hook (for question attachments), Home's pending tasks, and the attachment upload worker.

Now each reader subscribes to only what it shows:

  • ThreadComposer reads its own draft through a per-key composerDraftAtom, so the draft text and attachments are no longer route props.
  • useThreadComposerState reads only the draft's model, runtime and interaction mode (useComposerDraftSettings).
  • The request hook reads the question drafts under its thread's prefix. Pending tasks read only new-task drafts. The upload worker reads only attachments and project. All three projections compare their entries by reference, so they stay equal while only text changes.
  • setComposerDraftText keeps the draft's attachment array when no attachment was dropped. It used to rebuild the array on every edit, which defeated the upload worker's comparison.

Renders on the device

Pixel 9 API 36 emulator, dev client, median of 3 runs, same base (layer 9) and device. In the typing scenario, the app opens a thread, focuses the composer and types five words with adb input text, recorded with the render counter from measure-renders.mts.

ScenarioRenders per commit beforeAfterCommits (before / after)
Typing in the composer1034633 / 35
The open thread streams414039 / 36
Another thread streams while on Home1037512 / 12
Component, typing scenario (median)BeforeAfter
View1751858
ThreadRouteContent110
ThreadHeader, ThreadDetailScreen, ThreadFeed11 each0
HomeScreen / HomeRouteScreen10 each0
ThreadOutboxDrainWorker101
Container2 (feed list cells)2700

The composer itself still renders on every keystroke, as it should.

On Android I typed a draft, left the thread and came back to the same draft, cleared it, opened the attachment menu, sent a turn, and queued a follow-up while it streamed. The queued message was delivered ("from queue").

Claude Opus 5.5 via Claude Code.

🤖 Generated with Claude Code

Checks

20 successful · 13 skipped
All checks passed
15789f1
Synced from GitHub

Approvability

Verdict: Needs Human Approval

Macroscope's review found this PR requires human approval — This PR substantially restructures shared mobile composer state and its subscribers, changing update propagation across the thread screen, Home, question attachments, and upload handling. The intended performance improvement is focused, but the cross-cutting selector and equality infrastructure warrants human review.

No code changes detected at 15789f1. Prior analysis still applies.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Team
  • Run ID: 2d1a825f-8b33-46f1-8cb8-85c34919ac32
📥 Commits

Reviewing files that changed from the base of the PR and between c244cad4ef4786186535500271ee6af4e5f95d37 and f094f0930fd97c6eceb4e46d54bf0f0b19564f89.

📒 Files selected for processing: 2
  • apps/mobile/src/features/threads/ThreadComposer.tsx
  • apps/mobile/src/state/composer-attachment-uploads.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 5 remain after this review.

📝 Walkthrough

Composer drafts now use keyed and derived atoms. Thread state, upload and task hooks, and composer screens read the draft data they need without passing draft text and attachments through screen props.

Changes: Mobile composer draft state

Layer / File(s)Summary
Keyed draft selectors
apps/mobile/src/state/use-composer-drafts.ts
Derived atoms provide drafts by key prefix, new-task use, upload sources, and per-draft settings. useComposerDraft subscribes to a per-key atom.
Draft data consumers
apps/mobile/src/state/composer-attachment-uploads.ts, apps/mobile/src/state/use-pending-new-tasks.ts, apps/mobile/src/state/use-selected-thread-requests.ts, apps/mobile/src/state/use-selected-thread-requests.test.tsx
Upload and pending-task hooks use specialized draft atoms. Selected-thread requests read question-attachment drafts scoped to the selected thread.
Thread composer integration
apps/mobile/src/state/use-thread-composer-state.ts, apps/mobile/src/features/threads/ThreadComposer.tsx, apps/mobile/src/features/threads/ThreadDetailScreen.tsx, apps/mobile/src/features/threads/ThreadRouteScreen.tsx, apps/mobile/src/state/thread-task-permissions.test.ts
Thread composer state reads draft settings. ThreadComposer reads text and attachments through useComposerDraft; screen props no longer carry those values. Artifact templates read text from a keyed draft snapshot.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Refactor

Suggested reviewers: t3dotgg, maria-rcks

Merge Risk: ⚪ Minimal · up to f094f

Typing can update the composer without unnecessarily rerendering unrelated thread screens, while attachments, pending tasks, request inputs, and queued-edit drafts remain associated with the correct draft. No material merge risk remains.

Pre-merge checks: Passed 4
Check nameStatusExplanation
Linked Issues checkPassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes checkPassedCheck skipped because no linked issues were found for this pull request.
Title checkPassedThe title clearly and concisely describes the primary performance improvement: composer typing no longer re-renders the thread screen.
Description checkPassedThe description clearly explains the problem, implementation, measured results, and manual verification. It does not include explicit scope or approval details, and it does not provide the requested U…
Full details: Description check

Explanation

The description clearly explains the problem, implementation, measured results, and manual verification. It does not include explicit scope or approval details, and it does not provide the requested UI screenshots, but the core description is complete and focused.

✨ Finishing Touches: 💡 1
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

Synced from GitHub

Thread transfer impact

⚠️ The latest CI run did not produce a thread transfer result for 15789f1.

This comment will update automatically after the next completed run.

Added 1 commit

  • perf(mobile): typing in the composer no longer re-renders the thread screen15789f1