Skip to content

[Due for payment 2026-07-17] iOS-Scan-Report preview is blank after dismissing the "Enjoying New Expensify" pop up #95721

Closed

Description

@applause-bot

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: v9.4.32-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/test-runs/63957128/folder/13176933/41237697/1554723359?sort=-identifier
Email or phone of affected tester (no customers): lekima20+65107@gmail.com
Issue reported by: Applause Internal Team
Bug source: Exploratory - Significant User Experience Deterioration
Device used: iPhone 16 pro/ 26.5
App Component: Money Requests

Action Performed:

Precondition:

  • On the main device reinstall app, log in with a new account that doesn't have workspace, call user A
  • Log in another account on a different device and invite user A to the workspace.
  1. Open app login with user A
  2. Tap on the scan icon
  3. Take photo a receipt and create scan expense to the workspace chat
  4. After scanning completed, dismiss the "Enjoying New Expensify" pop up

Expected Result:

After dismissing the "Enjoying New Expensify" pop up, expense preview should display normally

Actual Result:

After dismissing the "Enjoying New Expensify" pop up, expense preview is blank until tap on it

Workaround:

Unknown

Platforms:

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

Screenshots/Videos

Bug7203864_1783608045761.Wxjg0941_1_.mp4

View all open jobs on GitHub

Applause Internal Information
Exported by: Mita Rachim
Bug ID: 7203864

Issue OwnerCurrent Issue Owner: @abzokhattab

Activity

  1. added
    BugSomething is broken. Auto assigns a BugZero manager.
    on Jul 9, 2026
  2. applause-bot commented on Jul 9, 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/a2c8b133df60028278c2f0f5f8b28334ee050c893edee1ca79c5b703ad76ccbf

  3. melvin-bot commented on Jul 9, 2026

    @melvin-bot

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

  4. melvin-bot commented on Jul 9, 2026

    @melvin-bot

    💬 A slack conversation has been started in #expensify-open-source

  5. github-actions commented on Jul 9, 2026

    @github-actions
    Contributor

    👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:

    1. Identify the pull request that introduced this issue and revert it.
    2. Find someone who can quickly fix the issue.
    3. Fix the issue yourself.
  6. mitarachim commented on Jul 9, 2026

    @mitarachim

    I cannot reproduce issue while Use Staging Server is disable/OFF

    ScreenRecording_07-09-2026.23.MP4
  7. MelvinBot commented on Jul 9, 2026

    @MelvinBot
    Contributor

    🔍 Investigation Summary

    Classification: Frontend bug
    Causing PR: #94681 - "[NOQA] perf: add MoneyRequestReportPreview layout components" by @OlimpiaZurek (High confidence)
    Related Issues: None identified

    Recommendation: REVERT

    This PR decomposed the entire MoneyRequestReportPreview render path and moved its width-measurement + transition-pending gating into a new provider/nested-FlashList structure that fails to render on iOS narrow layout after a modal dismissal until a manual re-layout is forced (tap). It is on staging but not in production, matching the staging-only repro. A clean revert restores the previously-working single-component render.

    Assigned: @OlimpiaZurek (PR author), @abzokhattab, @cristipaval, @mountiny (approving reviewers)
    Labels: No change. Kept DeployBlockerCash (an App PR caused the App-deploy-blocking bug). No DeployBlocker label was present.

    📋 Detailed Analysis

    Evidence

    • The "Enjoying New Expensify?" popup is ProactiveAppReviewModalManager (en.ts key proactiveAppReview.title). Its files were not changed in this release (last substantive change 2026-05-12), so the popup itself is not the culprit — it is only the trigger that leaves a modal-dismissal transition in flight.
    • The blank element is the scan expense's MoneyRequestReportPreview. That component and its whole subtree were rewritten by [NOQA] perf: add MoneyRequestReportPreview layout components #94681 (14 files, +1315/−590), merged 2026-07-06:
    • Staging-vs-production check confirms this is staging-only:
      • compare production...<94681 merge sha> → behind (not an ancestor of production → not in production)
      • compare <94681 merge sha>...staging → ahead (is an ancestor of staging → on staging)
      • This exactly matches "Reproducible in staging: Yes / production: No."
    • "Use Staging Server ON" requirement: the defect is timing-sensitive on the scan/report API response. Against the staging backend the preview mounts under the condition that exposes the render gate; against production timing it does not. The rendering defect itself is entirely client-side in the App.

    Verification

    Affected files (all rewritten by #94681):

    • src/components/ReportActionItem/MoneyRequestReportPreview/index.tsx
    • src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewProvider.tsx
    • src/components/ReportActionItem/MoneyRequestReportPreview/MoneyRequestReportPreviewBody.tsx
    • src/components/ReportActionItem/MoneyRequestReportPreview/TransactionReportCarousel.tsx

    Confirmed the PR modifies these files via gh pr view 94681 --json files.

    Root Cause

    The preview's rendered width is derived from onLayout-measured state, and the carousel is gated behind a loading state:

    • index.tsx initializes width state to {currentWidth: 0, currentWrapperWidth: 0} and only populates it from onCarouselLayout / onWrapperLayout (index.tsx:72-102).
    • MoneyRequestReportPreviewProvider.tsx:149: shouldShowPreviewLoading = isTransitionPending || shouldShowLoading || shouldShowLoadingDeferred || (!currentWidth && !shouldShowPreviewPlaceholder).
    • isTransitionPending initializes to true whenever there is a pending DISMISS_MODAL_AND_OPEN_REPORT follow-up action for this report (MoneyRequestReportPreviewProvider.tsx:104-107) — precisely the state left behind by dismissing the "Enjoying New Expensify" modal after a scan. It is only cleared by useFocusEffect + TransitionTracker.runAfterTransitions (:115-126).
    • While shouldShowPreviewLoading is true (transition pending, or currentWidth === 0), TransactionReportCarousel.tsx:28-44 renders only an ActivityIndicator — the transaction content does not render.
    • The nested horizontal FlashList (TransactionReportCarousel.tsx:56-79) even keys items on the measured width (keyExtractor uses reportPreviewStyles.transactionPreviewCarouselStyle.width). On iOS narrow layout, getMoneyRequestReportPreviewStyle computes transactionPreviewWidth = currentWidth - padding - peek, which is 0/negative when currentWidth is the initial 0 (the ?? 256 fallback does not apply because 0 is passed explicitly, not undefined).

    When the modal covers the list on mount, the report-action onLayout/transition-tracker path does not deliver a usable width or clear isTransitionPending, so the preview stays in the loading/zero-width state — blank. Tapping navigates/forces a re-layout, which fires onLayout and/or clears the pending transition, and the preview finally renders. Before #94681 this render path was a single component without this provider + nested-FlashList measurement pipeline, so the regression is scoped to this PR.

    @OlimpiaZurek since you authored the decomposition, please confirm — reverting #94681 is the safest path to unblock the App deploy.

  8. 4 remaining items

  9. added and removed
    DeployBlockerCashThis issue or pull request should block deployment
    on Jul 9, 2026
  10. melvin-bot commented on Jul 10, 2026

    @melvin-bot

    The solution for this issue has been 🚀 deployed to production 🚀 in version 9.4.32-3 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-07-17. 🎊

    For reference, here are some details about the assignees on this issue:

  11. changed the title [-]iOS-Scan-Report preview is blank after dismissing the "Enjoying New Expensify" pop up[/-] [+][Due for payment 2026-07-17] iOS-Scan-Report preview is blank after dismissing the "Enjoying New Expensify" pop up[/+] on Jul 10, 2026
  12. melvin-bot commented on Jul 10, 2026

    @melvin-bot

    @abzokhattab The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero 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]

  13. melvin-bot commented on Jul 14, 2026

    @melvin-bot

    @abzokhattab The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed. Please copy/paste the BugZero 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. fabioh8010 commented on Jul 22, 2026

    @fabioh8010
    Contributor

    Updates:

    All five blockers share one root cause — the coalescing defers the derived write to a macrotask, past React's render, so within that window consumers observe raw-updated-but-derived-stale state (a stale frame for #95696/#95698/#95700/#95721; a stale read that trips openReport's destructive fallback for #95753). The fix is to flush the coalesced recompute on a microtask (queueMicrotask) instead of a macrotask: the flush drains at the end of the current task — before paint and before React's next render — so derived data is consistent when consumers read it.

    Measurements

    [fabio] dependency changed / compute counts the derived recomputes; ManualSendMessage is the send-path span; the OnyxDerivedCompute_reportAttributes rows are the recompute count + total time for the busiest derived key.

    Metric main (sync) macrotask microtask
    recomputes (per 51 dep-changes) 51 20 20
    send latency (mean of 5) 177.2 ms 82.0 ms 84.2 ms
    reportAttributes computes 11 (~295 ms) 5 (~242 ms) 5 (~238 ms)

    Microtask matches macrotask on coalescing (51 → 20), send latency (~84 vs ~82 ms; ~53% faster than main), and reportAttributes recompute count (11 → 5). There is no measurable perf cost to using microtask over macrotask, and it removes the stale-frame/stale-read window.

    Here's the v2 PR: #96785

  15. mountiny commented on Jul 23, 2026

    @mountiny
    Contributor

    Really great investigation, thanks for the fix!

  16. lanitochka17 commented on Jul 28, 2026

    @lanitochka17

    Deploy Blocker #97250 was identified to be related to the PR #96785 that attempted to fix this issue.

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.EngineeringReviewingHas a PR in reviewWeeklyKSv2

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions