Skip to content

[HOLD] Decompose ReportActionComposer#86899

Draft
adhorodyski wants to merge 48 commits intoExpensify:mainfrom
callstack-internal:decompose/report-action-composer-v2
Draft

[HOLD] Decompose ReportActionComposer#86899
adhorodyski wants to merge 48 commits intoExpensify:mainfrom
callstack-internal:decompose/report-action-composer-v2

Conversation

@adhorodyski
Copy link
Copy Markdown
Contributor

@adhorodyski adhorodyski commented Apr 1, 2026

@rlinoz

Explanation of Change

Decomposes the 808-LOC ReportActionCompose monolith into a compound component architecture with optimized render performance.

Compound components:
The orchestrator is now 55 LOC — pure composition with Composer.Box, Composer.DropZone, Composer.LocalTime, Composer.Footer, Composer.ActionMenu, Composer.Input, Composer.EmojiPicker, and Composer.SendButton. Each component owns its behavior and self-subscribes to Onyx data per CLEAN-REACT-PATTERNS-2.

6 contexts organized by change frequency:

  • Text (hot) — composer value, changes every keystroke
  • State (warm) — isFocused, isMenuVisible, isFullComposerAvailable
  • Actions (frozen) — stable setters and ref-closing callbacks, never triggers re-renders
  • SendState (warm) — isSendDisabled, exceededMaxLength, isBlockedFromConcierge — all primitives, no function refs
  • SendActions (infrequent) — handleSendMessage, onValueChange
  • Meta (frozen) — refs only, never triggers re-renders

Hook split for render isolation:

  • Split useAttachmentUploadValidation into useAttachmentPicker (lightweight, no Onyx subs) and useReceiptDrop (heavy, ~10 Onyx subs). Each consumer calls its hook directly instead of reading from context.
  • Relocated useComposerSubmit from provider to ComposerInputWrapper (its sole consumer), removing ~12 Onyx subscriptions from the provider render path.
  • Extracted useComposerFocus — owns focus state internally.

Activity API for hidden subtrees:

  • Wrapped PopoverMenu in AttachmentPickerWithMenuItems with <Activity mode> — React skips reconciliation of the entire menu subtree when closed.
  • Wrapped EmojiPicker popover with <Activity mode> — same optimization for the global emoji picker.

Performance (React Profiler, averaged across 3-4 runs):

Scenario Main Branch Delta
Open menu + close (duration) 301ms 208ms -31%
Open menu + close (commits) 34 26 -24%
Type + send message (duration) 444ms 388ms -13%
Screenshot 2026-04-03 at 16 23 56 Screenshot 2026-04-03 at 16 24 07

Fixed Issues

$ #87097
PROPOSAL:

Tests

  1. Open any chat and verify the composer renders correctly (input, + button, send button, emoji picker)
  2. Type a message and send — verify it posts with no errors
  3. Open the action menu (+) — verify all options appear, close with Escape
  4. Attach a file via the action menu — verify preview modal appears and file sends
  5. Open the emoji picker — verify selecting an emoji inserts it
  6. Drag and drop a file onto the composer — verify it's accepted
  7. Navigate between chat rooms — verify composer state resets per room
  8. Test offline mode — verify composer works and queues messages
  9. Verify no JS console errors throughout

adhorodyski and others added 28 commits April 1, 2026 12:11
…tate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ents

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ds without minHeight hack

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@adhorodyski adhorodyski changed the title Decompose ReportActionCompose into compound Composer architecture Decompose ReportActionCompose with compound components Apr 1, 2026
adhorodyski and others added 19 commits April 2, 2026 17:35
…ubmit from provider

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@adhorodyski adhorodyski changed the title Decompose ReportActionCompose with compound components [HOLD] Decompose ReportActionComposer Apr 3, 2026
@cristipaval
Copy link
Copy Markdown
Contributor

cristipaval commented Apr 6, 2026

FYI: I've just added #87097 to the Fixed issues section in the PR description

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.

2 participants