Skip to content

[$250] Chat - Focus in composer is lost after sending a message #83766

Description

@lanitochka17

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Version Number: 9.3.27-0
Reproducible in staging?: Yes
Reproducible in production?: No
If this was caught during regression testing, add the test name, ID and link from BrowserStack: https://test-management.browserstack.com/projects/2219752/folder/13176925/test-cases/41237591
Email or phone of affected tester (no customers): fischer9966+053122@gmail.com
Issue reported by: Applause Internal Team
Bug source: Regression TC Execution
Device used: MacOS 26.3 / Safari, Chrome
App Component: Chat Report View

Action Performed:

  1. Login to staging.new.expensify.com with any account
  2. Go to any chat
  3. Send message
  4. Verify that the composer focus is retained after sending the message.

Expected Result:

After sending the message, the composer field is cleared and the focus is set to the beginning of the line

Actual Result:

Focus in composer is lost after sending the message.

Workaround:

Unknown

Platforms:

  • Android: App
  • Android: mWeb Chrome
  • iOS: App
  • iOS: mWeb Safari
  • iOS: mWeb Chrome
  • Windows: Chrome
  • MacOS: Chrome / Safari

Screenshots/Videos

Bug7090428_1772229140450.Web-Chat-Focus-lost-after-post.mp4

View all open jobs on GitHub

Issue OwnerCurrent Issue Owner: @sobitneupane
Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~022027507279877883330
  • Upwork Job ID: 2027507279877883330
  • Last Price Increase: 2026-02-27

Activity

  1. added
    DeployBlockerCashThis issue or pull request should block deployment
    ExternalAdded to denote the issue can be worked on by a contributor
    BugSomething is broken. Auto assigns a BugZero manager.
    on Feb 27, 2026
  2. added
    Help WantedApply this label when an issue is open to proposals by contributors
    on Feb 27, 2026
  3. melvin-bot commented on Feb 27, 2026

    @melvin-bot

    Triggered auto assignment to Contributor-plus team member for initial proposal review - @sobitneupane (External)

  4. melvin-bot commented on Feb 27, 2026

    @melvin-bot

    Triggered auto assignment to @kadiealexander (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.

  5. changed the title [-]Chat - Focus in composer is lost after sending a message[/-] [+][$250] Chat - Focus in composer is lost after sending a message[/+] on Feb 27, 2026
  6. melvin-bot commented on Feb 27, 2026

    @melvin-bot

    Triggered auto assignment to @yuwenmemon (DeployBlockerCash), see https://stackoverflowteams.com/c/expensify/questions/9980/ for more details.

  7. melvin-bot commented on Feb 27, 2026

    @melvin-bot
  8. 6 remaining items

  9. MelvinBot commented on Feb 27, 2026

    @MelvinBot
    Contributor

    🔍 Issue Analysis

    Summary: After sending a message in chat on desktop web (Windows Chrome), the composer loses focus. This is a regression in staging v9.3.27-0, not present in production. A closely related deploy blocker — Expensify/App#83731 — reports the same symptom on mWeb (compose box closing after send).

    Root Cause: PR Expensify/App#81280 ("Report Actions Derived Value") introduces an async Onyx-derived value VISIBLE_REPORT_ACTIONS that causes a double re-render of ReportActionsView after each message send. The sequence is:

    1. User sends a message → optimistic REPORT_ACTIONS update fires → first re-render (action has pendingAction, so it bypasses the derived cache and is visible).
    2. The async VISIBLE_REPORT_ACTIONS derived value recomputes, but its Onyx.set() write is async, so the component receives the update after a delay.
    3. When the API success callback clears pendingAction: null, the action switches to the derived cache path. If the cache hasn't propagated yet, a timing guard (hasDerivedValueTimingIssue at src/pages/inbox/report/ReportActionsView.tsx:333) briefly replaces the entire ReportActionsList with a non-animated skeleton.
    4. This rapid DOM unmount/remount of the action list (a sibling of the composer) causes a layout reflow that triggers the browser to remove focus from the composer input.

    A contributing factor is PR Expensify/App#76104, which changed overflow: 'hidden' to overflow: 'clip' in src/styles/utils/chatContentScrollViewPlatformStyles/index.ts. overflow: clip does not create a block formatting context, making the layout less stable during rapid re-renders — this is especially impactful on mWeb Android Chrome (where the virtual keyboard dismisses), but also destabilizes desktop layout during the skeleton flash.

    Recommended Fix: Two options, in order of risk:

    1. Quickest fix (low risk): In ReportActionsView.tsx, when hasDerivedValueTimingIssue is true, use the previous visibleReportActions value as a fallback instead of rendering a skeleton. This prevents the DOM unmount/remount entirely and keeps the action list stable during the derived-value propagation delay.
    2. Alternative: Revert overflow: 'clip' back to overflow: 'hidden' in src/styles/utils/chatContentScrollViewPlatformStyles/index.ts (from PR fix: Allow multiple loads in useLoadReportActions hook #76104). This restores the block formatting context and stabilizes layout during re-renders. This is the fix proposed on the related issue [$250] mWeb – Compose box closes after sending messages #83731.

    Open Questions

    • Runtime confirmation would help determine whether the hasDerivedValueTimingIssue skeleton actually renders on desktop web after message send, or whether there is a separate mechanism causing focus loss on desktop Chrome specifically.
    • On medium-width desktops (< 1300px) with the side panel visible, isSidePanelHiddenOrLargeScreen is false, which blocks ALL focus restoration in the composer (ComposerWithSuggestions.tsx:790). If focus is lost under these conditions, no backup mechanism can recover it.
    Detailed Investigation

    Normal Focus Retention Flow (Before Regression)

    When a user sends a message, focus is retained through these mechanisms:

    • Enter key path: Keyboard events don't blur the input (ComposerWithSuggestions.tsx:520-523)
    • Send button click path: onMouseDown={(e) => e.preventDefault()} on the SendButton parent View (SendButton.tsx:39-40) prevents the browser from transferring focus to the button
    • The worklet clear (ComponentUtils/index.ts:11-18) calls input.clear() without triggering blur
    • Backup: focusComposerOnKeyPress listener (ComposerWithSuggestions.tsx:702-726) refocuses composer when any printable key is pressed

    These mechanisms are all still intact in the current code. The focus loss is caused by a new side effect (the derived value double re-render), not by a broken focus mechanism.

    Evidence Chain

    Step What Happens Evidence
    1 User sends message handleSendMessage in ReportActionCompose.tsx:469
    2 Optimistic action added with pendingAction: ADD Report/index.ts:743-755
    3 First re-render — action visible (bypasses cache) ReportActionsUtils.ts:1209 — if (reportAction.pendingAction)
    4 Derived value starts async recompute OnyxDerived/index.ts:80-83 — sync compute, async Onyx.set()
    5 API success clears pendingAction: null Report/index.ts:743-755 successData
    6 Second re-render — action goes through cache path ReportActionsUtils.ts:1213 — uses visibleReportActions cache
    7 If cache stale → hasDerivedValueTimingIssue triggers → skeleton ReportActionsView.tsx:333-336
    8 ReportActionsList unmounts/remounts → layout reflow → focus lost Browser behavior during significant DOM changes

    Why Desktop Web Is Affected

    • FocusTrap is inactive for the Report screen in wide layout (WIDE_LAYOUT_INACTIVE_SCREENS.ts:10), so it's not stealing focus
    • FocusTrap onActivate protects INPUT/TEXTAREA from blur (FocusTrapForScreen/index.web.tsx:46-48), even if it were active
    • The composer (in ReportFooter) is a sibling of ReportActionsView in ReportScreen.tsx:1032-1067, so the skeleton doesn't unmount the composer itself
    • However, the rapid layout reflow from the action list disappearing/reappearing can cause Chrome to drop focus from the active input element — this is browser-specific behavior

    PRs Involved

    PR Title Risk Level
    Expensify/App#81280 Report Actions Derived Value High — async double re-renders + skeleton guard causing DOM thrash
    Expensify/App#76104 Allow multiple loads in useLoadReportActions Medium — overflow: clip destabilizes layout during re-renders

    Relevant Code

    • src/pages/inbox/report/ReportActionsView.tsx:105 — subscribes to derived value
    • src/pages/inbox/report/ReportActionsView.tsx:220-242 — visibleReportActions useMemo with visibleReportActionsData dependency
    • src/pages/inbox/report/ReportActionsView.tsx:333-336 — hasDerivedValueTimingIssue skeleton guard
    • src/libs/ReportActionsUtils.ts:1206-1232 — isReportActionVisible with derived value fallbacks
    • src/libs/actions/OnyxDerived/configs/visibleReportActions.ts — derived value config
    • src/styles/utils/chatContentScrollViewPlatformStyles/index.ts — overflow: 'clip' change
    • src/pages/inbox/report/ReportActionCompose/SendButton.tsx:39-40 — focus prevention (still intact)
    • src/pages/inbox/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx:790 — isSidePanelHiddenOrLargeScreen focus guard

    Confidence: Medium-High

    The analysis of the async double re-render mechanism is well-supported by code evidence and aligns with the related mWeb issue (#83731). The specific mechanism by which Chrome drops focus during layout reflow on desktop is a browser-level behavior that would need runtime evidence (e.g., a Chrome DevTools focus monitor) to fully confirm.


    Note: This issue is closely related to deploy blocker Expensify/App#83731 (mWeb compose box closing after send), which has the same root cause. A fix for one should likely address both.

    Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, or @MelvinBot <your feedback> to refine this analysis.

  10. chassidysandraik-hash commented on Mar 1, 2026

    @chassidysandraik-hash

    I’m working on this now and will open a PR shortly.

  11. BartekObudzinski commented on Mar 2, 2026

    @BartekObudzinski
    Contributor

    Not reproducible on staging or dev as well

    Screen.Recording.2026-03-02.at.08.49.35.mov
  12. brunovjk commented on Mar 2, 2026

    @brunovjk
    Contributor

    @MobileMage do you believe this bug/regression is linked to our PR? Thank you.

  13. MobileMage commented on Mar 2, 2026

    @MobileMage
    Contributor

    No, I don't think so @brunovjk

  14. mkhutornyi commented on Mar 2, 2026

    @mkhutornyi
    Contributor

    This is regression from #76104 and already fixed by this revert

  15. ohrytskov commented on Mar 2, 2026

    @ohrytskov

    Proposal

    Please re-state the problem that we are trying to solve in this issue.

    On web, after sending a chat message, the composer input loses focus. This breaks the expected “type → send → keep typing” workflow and forces the user to click the composer again before sending the next message.

    What is the root cause of that problem?

    We currently rely on “nothing blurs the input” to keep focus after submit (e.g., preventDefault() on the send button click path and preventDefault() for Enter). In staging, the post-submit UI update (clearing the input + optimistic action render/layout changes) can still cause the browser to drop focus from the underlying <textarea>/<input>. Since we don’t explicitly restore focus after a successful submit, the composer stays blurred until the user manually refocuses it (and some existing fallback focus paths are intentionally gated by side-panel/layout checks, so they don’t help once focus is lost).

    What changes do you think we should make in order to solve the problem?

    Add an explicit “restore focus after send” step on web when the composer was focused at the moment the user sent the message.

    • Track whether the composer was focused when handleSendMessage() runs (e.g., via composerRef.current?.isFocused() in ReportActionCompose).
    • After the submit path completes (the onClear → submitForm() flow), if wasFocusedOnSend is true, call composerRef.current?.focus() to re-focus the input.
    • Keep it scoped to the “send message” path so we don’t steal focus during other re-renders, and avoid focusing when the user intentionally sent while not in the composer.

    What alternative solutions did you explore? (Optional)

    • Making the send button non-focusable: it’s already guarded (onMouseDown + focusable={false}), and the issue still reproduces.
    • Broadening existing “focus-on-keypress / focus-on-screen-focus” logic: those paths are meant for different scenarios and are gated by layout/state checks; using them here would either be ineffective or risk focusing at the wrong times.
  16. arosiclair commented on Mar 2, 2026

    @arosiclair
    Contributor

    Not reproducible in v9.3.27-5

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

Metadata

Metadata

Labels

BugSomething is broken. Auto assigns a BugZero manager.EngineeringExternalAdded to denote the issue can be worked on by a contributorHelp WantedApply this label when an issue is open to proposals by contributorsHourlyKSv2

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions