Skip to content

[Due for payment 2026-10-13] [$175] New message indicator does not pop up on 1st message after going back online #102094

Closed

Description

@applause-bot

If you haven’t already, check out our contributing guidelines for onboarding. To join our Slack channel, fill out this form.


Version Number: 9.4.92-5
Reproducible in staging?: Yes
Reproducible in production?: Yes
If this was caught during regression testing, add the test name, ID and link from BrowserStack: #100033
Email or phone of affected tester (no customers): emady.syed123+nonadmin1@gmail.com
Issue reported by: Applause Internal Team
Bug source: Pull Request QA execution
Device used: All
App Component: Chat Report View

Action Performed:

  1. Open App
  2. Go to any expense with many comments
  3. Scroll to top
  4. Receive a message from other user
  5. Tap on new message indicator
  6. Go to troubleshoot and go offline
  7. Go back to same expense from step 2 and add a comment
  8. Go online
  9. Go back to same expense and scroll to top
  10. Receive One message and wait few moments
  11. Receive another message

Expected Result:

New message indicator should pop up on 1st message after going back online

Actual Result:

New message indicator does not pop up on 1st message after going back online

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

Bug7267304_1790170396992.ScreenRecording_09-23-2026_18-25-40_1.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Julia Ponikarchuk
Bug ID: 7267304

Issue OwnerCurrent Issue Owner: @mallenexpensify
Upwork Automation - Do Not Edit

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Sep 23, 2026
  2. applause-bot commented on Sep 23, 2026

    @applause-bot
    Author

    While Applause doesn't embed attachments for this bug, here are the links for quick view:
    https://platform.applause.com/services/links/v1/external/5a6a30fdd536af712859185fc7e6980ecdeff2af93bf3b5bf21bfde4cf227195

  3. MelvinBot commented on Sep 23, 2026

    @MelvinBot
    Contributor

    Proposal

    ⚠️ Medium confidence, from static analysis only — I could not drive NewDot in a browser on this run, so nothing below is runtime-verified.

    What is the root cause of that problem?

    In the expense (money request) report view, every new action that arrives over Pusher force-hides the floating new-message pill, including messages from other people.

    src/components/MoneyRequestReportView/useMoneyRequestReportScroll.ts:224-232 calls setIsFloatingMessageCounterVisible(false) on line 227 — before the isFromCurrentUser guard on line 229. So an incoming message from another user hides the indicator instead of leaving it alone.

    Meanwhile the indicator is turned on by a separate path: when unreadMarkerReportActionIndex flips from -1, an effect re-runs the viewability check and shows the pill (useReportUnreadMessageScrollTracking.ts:179-185). Those two run in the same beat when a message lands, and the Pusher hide is deferred through TransitionTracker.runAfterTransitions, so it tends to land last and win — no indicator for that message.

    The chat list does not have this problem: its equivalent handler returns early for other people's actions at useReportActionsNewActionLiveTail.ts:103, before it ever touches the counter.

    This also explains why message 2 works. The list is oldest-first, so appending a message does not change the index of the already-marked message. Nothing re-shows the pill until FlashList emits a fresh viewability callback, which the next message's layout change produces.

    Note the report says this reproduces in production, so this is pre-existing and not caused by #100033 — that PR moved this code but kept the ordering.

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

    In useMoneyRequestReportScroll.ts, move setIsFloatingMessageCounterVisible(false) below the isFromCurrentUser / ADD_COMMENT guard, so the pill is only dismissed when the user's own comment is about to scroll the list to the bottom. That matches what the chat list already does.

    Add a regression test covering: scrolled away from the bottom, receive one action from another user, assert the counter stays visible.

    Supporting detail and a second, separate defect

    Why the offline steps matter (steps 6-8). They are most likely just how the tester reached the state the bug needs: scrolled up, report freshly re-read, then a live message arriving. Sending offline leaves lastReadTime behind lastVisibleActionCreated, so reopening the report at step 9 marks it read and clears the marker — a clean slate before message 1 lands.

    Second defect, worth fixing while in here (may not cause this bug). useUnreadMarker.ts:110-117 computes earliestReceivedOfflineMessageIndex over sortedReportActions, but shouldDisplayNewMarkerOnReportAction.ts:205-226 uses it as an index into visibleReportActions. For this view those two arrays are in opposite orders: reportActions is newest-first, while visibleReportActions is oldest-first (useMoneyRequestReportVisibleActions.ts:93). Whenever a message does arrive while the user is offline, the marker scan is bounded by, and anchors "earliest offline message" to, the wrong row. The index should be computed in the visibleReportActions domain.

    What to check to confirm or kill the main hypothesis. Log unreadMarkerReportActionID, unreadMarkerReportActionIndex, and isFloatingMessageCounterVisible on each render while running steps 9-11. If the marker ID is set for message 1 but the counter is false, the Pusher hide is the cause. If the marker ID stays null for message 1, the cause is upstream in useUnreadMarker instead and this proposal is wrong.


    Next Steps for Contributor+ team:
    To accept: @MelvinBot implement [this](https://github.com/Expensify/App/issues/102094) to create a draft PR.
    To refine: @MelvinBot <your feedback>
    To reject: Explain why you are rejecting Melvin's proposal.


    view run

  4. added
    ExternalAdded to denote the issue can be worked on by a contributor
    on Sep 23, 2026
  5. melvin-bot commented on Sep 23, 2026

    @melvin-bot

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

  6. melvin-bot commented on Sep 23, 2026

    @melvin-bot

    Unable to auto-create job on Upwork. The BZ team member should create it manually for this issue.

  7. ashusatyarthi-wq commented on Sep 26, 2026

    @ashusatyarthi-wq
    Contributor details Your Expensify account email: ashu.satyarthi.business@gmail.com Upwork Profile Link: https://www.upwork.com/freelancers/~01b8ab177d61dc225c

    Proposal

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

    In an expense report (MoneyRequestReportView), when scrolled up and receiving a new message from another user after going back online, the floating new message indicator pill does not pop up on the first incoming message.

    What is the root cause of that problem?

    In src/components/MoneyRequestReportView/useMoneyRequestReportScroll.ts:224-232:

    const onNewActionEvent = useEffectEvent((isFromCurrentUser: boolean, reportAction?: OnyxTypes.ReportAction) => {
        TransitionTracker.runAfterTransitions({
            callback: () => {
                setIsFloatingMessageCounterVisible(false);
                // If a new comment is added from the current user, scroll to the bottom, otherwise leave the user position unchanged
                if (!isFromCurrentUser || reportAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT) {
                    return;
                }

    setIsFloatingMessageCounterVisible(false) is called unconditionally at line 227 — before the check determining whether the action is from the current user or another user.

    When a message from another user arrives via Pusher:

    1. useReportUnreadMessageScrollTracking.ts detects the new unread message and attempts to display the floating new message indicator pill.
    2. Simultaneously, onNewActionEvent runs its deferred transition callback and calls setIsFloatingMessageCounterVisible(false).
    3. Because the transition callback runs asynchronously after rendering, setIsFloatingMessageCounterVisible(false) overwrites the visible state and suppresses the floating pill for the incoming message.

    In contrast, the chat report equivalent (useReportActionsNewActionLiveTail.ts:101) guards against actions not from the current user (if (!isFromCurrentUser) return;) before performing any scroll or pill state changes.

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

    In src/components/MoneyRequestReportView/useMoneyRequestReportScroll.ts:
    Move setIsFloatingMessageCounterVisible(false) inside the current-user comment guard so that the counter is only dismissed when the current user's own comment is scrolling the view to the bottom:

    const onNewActionEvent = useEffectEvent((isFromCurrentUser: boolean, reportAction?: OnyxTypes.ReportAction) => {
        TransitionTracker.runAfterTransitions({
            callback: () => {
                // If a new comment is added from the current user, scroll to the bottom, otherwise leave the user position unchanged
                if (!isFromCurrentUser || reportAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT) {
                    return;
                }
    
                setIsFloatingMessageCounterVisible(false);
    
                // Delay so the just-sent item has landed in the data before we jump
                const index = visibleReportActions.findIndex((item) => item.reportActionID === reportAction?.reportActionID);
                // ...

    What alternative solutions did you explore? (Optional)

    Gating setIsFloatingMessageCounterVisible(false) on viewability callbacks was considered, but FlashList viewability events already handle showing and hiding the floating indicator based on scroll position (useReportUnreadMessageScrollTracking.ts). onNewActionEvent should only ever dismiss the floating counter when the user submits their own comment and triggers an programmatic auto-scroll to the bottom.

  8. melvin-bot commented on Sep 28, 2026

    @melvin-bot

    @Pujan92 Eep! 4 days overdue now. Issues have feelings too...

  9. melvin-bot commented on Sep 30, 2026

    @melvin-bot

    @Pujan92 Still overdue 6 days?! Let's take care of this!

  10. Pujan92 commented on Oct 1, 2026

    @Pujan92
    Contributor

    @MelvinBot proposal looks good, move setIsFloatingMessageCounterVisible(false) below the isFromCurrentUser guard.

    🎀👀🎀 C+ reviewed

  11. 15 remaining items

  12. changed the title [-][$175] New message indicator does not pop up on 1st message after going back online[/-] [+][Due for payment 2026-10-13] [$175] New message indicator does not pop up on 1st message after going back online[/+] on Oct 6, 2026
  13. melvin-bot commented on Oct 6, 2026

    @melvin-bot

    @Pujan92

    The solution for this issue has been 🚀 deployed to production 🚀 in version 9.5.3-4 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

    If no regressions arise, payment will be issued on 2026-10-13. 🎊

    The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the Contributor+ Checklist from here into a new comment on this GH and complete it. If you have the K2 extension, you can simply click: [this button]. If no checklist is needed for this issue, you can click: [no checklist button]

  14. melvin-bot commented on Oct 12, 2026

    @melvin-bot

    Triggered auto assignment to @mallenexpensify (Awaiting Payment)

  15. melvin-bot commented on Oct 12, 2026

    @melvin-bot

    Payment Summary

    Resolving PRs:

    BugZero Checklist (@mallenexpensify)

    • I have confirmed assignees, roles, and Upwork contracts look correct
    • I have paid out Upwork contracts / manual NewDot requests
    • [BugZero Assignee] I have created a GH issue for creating/updating the regression test once above steps have been agreed upon
  16. Pujan92 commented on Oct 13, 2026

    @Pujan92
    Contributor

    BugZero Checklist:

    • [Contributor] Classify the bug:
    Bug classification

    Source of bug:

    • 1a. Result of the original design (eg. a case wasn't considered)
    • 1b. Mistake during implementation
    • 1c. Backend bug
    • 1z. Other:

    Where bug was reported:

    • 2a. Reported on production
    • 2b. Reported on staging (deploy blocker)
    • 2c. Reported on both staging and production
    • 2d. Reported on a PR
    • 2z. Other:

    Who reported the bug:

    • 3a. Expensify user
    • 3b. Expensify employee
    • 3c. Contributor
    • 3d. QA
    • 3z. Other:
    • [Contributor] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake.

      Link to comment: https://github.com/Expensify/App/pull/59664/changes#r4260152255

    • [Contributor] If the regression was CRITICAL (e.g. interrupts a core flow) A discussion in #expensify-open-source has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner.

      Link to discussion: N/A

    • [Contributor] If it was decided to create a regression test for the bug, please propose the regression test steps using the template below to ensure the same bug will not reach production again.

    • [BugZero Assignee] Create a GH issue for creating/updating the regression test once above steps have been agreed upon.

      Link to issue: https://github.com/Expensify/Expensify/issues/691943

    Regression Test Proposal

    Precondition: User A and User B are both members of an expense report that has enough comments to scroll.

    1. As User A, open the expense report and scroll to the top.
    2. As User B, add a comment to the same expense report.
    3. As User A, verify the new-message indicator appears.
    Zapier Logs Run ID: 00040eee-4941-a1b9-3c45-6dfe88182774
  17. mallenexpensify commented on Oct 15, 2026

    @mallenexpensify
    Contributor

    Payment Summary

    Contributor+: @Pujan92 due $175 via NewDot

  18. twisterdotcom commented on Oct 15, 2026

    @twisterdotcom
    Contributor

    $175 approved for @Pujan92 in Report ID: R00KriUZon70.

    Zapier Logs Run ID: 00040eee-5f73-adb1-afec-0647568d11a4

    SO: https://stackoverflowteams.com/c/expensify/questions/7582

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

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionBugSomething is broken. Auto assigns a BugZero manager.DailyKSv2ExternalAdded to denote the issue can be worked on by a contributor

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions