Skip to content

feat: update error uploading receipt screen#82139

Merged
mjasikowski merged 37 commits into
Expensify:mainfrom
callstack-internal:update-uploading-receipt-screen
May 15, 2026
Merged

feat: update error uploading receipt screen#82139
mjasikowski merged 37 commits into
Expensify:mainfrom
callstack-internal:update-uploading-receipt-screen

Conversation

@OlimpiaZurek

@OlimpiaZurek OlimpiaZurek commented Feb 11, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

This PR redesigns the receipt upload error screen nad fixes expense creation errors from the receipt upload flow to show the correct message instead of the generic "Unexpected error creating this chat".

Fixed Issues

$ #80905
PROPOSAL:

Tests

  1. Go to the app and create a new expense with a receipt (e.g. scan a receipt or upload an image)
  2. Force the receipt upload to fail using one of the following methods:
  • Kill your network connection (airplane mode / disable Wi-Fi) before the upload completes
  • Background the app for an extended period during upload, then return
  • Use a poor/throttled network connection and background the app during upload
  1. Verify the error shows "Receipt upload failed. Save the receipt, or delete the expense and lose it." (NOT "Unexpected error creating this chat")
  2. Verify two buttons appear below the error: "Save receipt" and "Delete expense"
  3. Tap "Save receipt" — verify the receipt image downloads/opens
  4. Tap "Delete" — verify the failed expense is removed
  • Verify that no errors appear in the JS console

Offline tests

QA Steps

same as tests
// TODO: These must be filled out, or the issue title must include "[No QA]."

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that any callback methods that were added or modified are named for what the method does and never what callback they handle (i.e. toggleReport and not onIconClick)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text shown in the product is localized by adding it to src/languages/* files and using the translation method
      • If any non-english text was added/modified, I used JaimeGPT to get English > Spanish translation. I then posted it in #expensify-open-source and it was approved by an internal Expensify engineer. Link to Slack message:
    • I verified all numbers, amounts, dates and phone numbers shown in the product are using the localization methods
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
    • I verified proper file naming conventions were followed for any new files or renamed files. All non-platform specific files are named after what they export and are not named "index.js". All platform-specific files are named for the platform the code supports as outlined in the README.
    • I verified the JSDocs style guidelines (in STYLE.md) were followed
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • I verified all code is DRY (the PR doesn't include any logic written more than once, with the exception of tests)
  • I verified any variables that can be defined as constants (ie. in CONST.ts or at the top of the file that uses the constant) are defined as such
  • I verified that if a function's arguments changed that all usages have also been updated correctly
  • If any new file was added I verified that:
    • The file has a description of what it does and/or why is needed at the top of the file if the code is not self explanatory
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • If a new page is added, I verified it's using the ScrollView component to make it scrollable when more elements are added to the page.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native android
Android: mWeb Chrome
iOS: Native ios
iOS: mWeb Safari ios_web
MacOS: Chrome / Safari web_1 web_2

@melvin-bot

melvin-bot Bot commented Feb 11, 2026

Copy link
Copy Markdown

Hey, I noticed you changed src/languages/en.ts in a PR from a fork. For security reasons, translations are not generated automatically for PRs from forks.

If you want to automatically generate translations for other locales, an Expensify employee will have to:

  1. Look at the code and make sure there are no malicious changes.
  2. Run the Generate static translations GitHub workflow. If you have write access and the K2 extension, you can simply click: [this button]

Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running:

npx ts-node ./scripts/generateTranslations.ts --help

Typically, you'd want to translate only what you changed by running npx ts-node ./scripts/generateTranslations.ts --compare-ref main

@codecov

codecov Bot commented Feb 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.

Files with missing lines Coverage Δ
src/CONST/index.ts 92.30% <ø> (ø)
src/components/MessagesRow.tsx 92.85% <100.00%> (+0.54%) ⬆️
src/libs/ErrorUtils.ts 55.55% <71.42%> (+0.65%) ⬆️
src/components/ReceiptAudit.tsx 60.00% <57.14%> (+0.90%) ⬆️
.../libs/ReceiptUploadRetryHandler/handleFileRetry.ts 0.00% <0.00%> (ø)
src/pages/inbox/report/PureReportActionItem.tsx 79.43% <40.00%> (-0.40%) ⬇️
...nents/ReportActionItem/MoneyRequestReceiptView.tsx 68.12% <72.72%> (-0.57%) ⬇️
src/components/DotIndicatorMessage.tsx 64.70% <14.28%> (-17.80%) ⬇️
... and 8 files with indirect coverage changes

Comment thread src/components/DotIndicatorMessage.tsx
Comment thread src/hooks/useReceiptRetryParams.ts Outdated
@github-actions

Copy link
Copy Markdown
Contributor

🚧 @mjasikowski has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

This comment has been minimized.

@mjasikowski

Copy link
Copy Markdown
Contributor

This is still pending tests on my side

@OlimpiaZurek OlimpiaZurek force-pushed the update-uploading-receipt-screen branch from 7c1fa99 to 05b5f89 Compare March 24, 2026 15:06
@github-actions

Copy link
Copy Markdown
Contributor

🚧 @mjasikowski has triggered a test Expensify/App build. You can view the workflow run here.

@github-actions

github-actions Bot commented Apr 2, 2026

Copy link
Copy Markdown
Contributor

🚧 @mjasikowski has triggered a test Expensify/App build. You can view the workflow run here.

@mjasikowski mjasikowski requested a review from situchan April 3, 2026 08:05
@mjasikowski

Copy link
Copy Markdown
Contributor

@situchan can you test this one please?

@situchan

situchan commented May 8, 2026

Copy link
Copy Markdown
Contributor

@mjasikowski please generate new build

@github-actions

Copy link
Copy Markdown
Contributor

🚧 @mjasikowski has triggered a test Expensify/App build. You can view the workflow run here.

@situchan

Copy link
Copy Markdown
Contributor

Conflicts again. Doing final test now

@MelvinBot

Copy link
Copy Markdown
Contributor

Regression Analysis

I reviewed the full diff and traced the error flows through the codebase. Here are the potential regressions:

1. No confirmation before deleting expense (Medium Risk)

Old flow: User clicks dismiss (X) → onClose in PureReportActionItem calls showDismissReceiptErrorModal() → confirmation modal ("Are you sure?") → dismissError() deletes the expense.

New flow: User clicks "Delete expense" button → dismissError() fires immediately, which calls cleanUpMoneyRequest and deletes the expense with no confirmation.

The old confirmation guard (showDismissReceiptErrorModal at PureReportActionItem:342-354) is now unreachable for receipt errors because:

  • MessagesRow hides the dismiss (X) button when hasReceiptUploadError is true
  • getLatestErrorMessageField filters out receipt errors, so OfflineWithFeedback never shows an error row that would trigger onClose

This may be intentional (the explicit button labels replace the modal), but an accidental tap on "Delete expense" will permanently delete with no undo.

2. getLatestErrorMessageField filtering may hide co-occurring errors (Low Risk)

The PR filters all ReceiptError objects from getLatestErrorMessageField. If a transaction has both a receipt upload error AND another error (e.g., route error), only the non-receipt error surfaces through this utility. The receipt error renders through the separate DotIndicatorMessage button UI, so both should still be visible — but this dual-path rendering hasn't been explicitly tested for the co-occurrence case.

3. Dead code: old HTML receipt error message (Cleanup, No Risk)

DotIndicatorMessage no longer renders receiptFailureMessage (the <a href="retry"> / <a href="download"> HTML). The renderMessage function now returns null for receipt errors. The old handleRetryPress import and RenderHTML import are removed, but the receiptFailureMessage translation key still exists in all language files. Not a functional regression, just dead code.

Not a regression (verified safe)

  • ReceiptError.action / retryParams now optional: handleFileRetry.ts:13-16 has proper null guards.
  • Context menu for receipt errors: PureReportActionItem:461-464 correctly enables the context menu when only receipt errors exist (so users can still access "Delete expense" from long-press).
  • DotIndicatorMessage dismissError prop: All paths where receipt errors reach this component flow through MessagesRow, which always passes dismissError.

@situchan situchan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving since all reported bugs happening in production, not caused by our PR

@melvin-bot melvin-bot Bot requested a review from mjasikowski May 14, 2026 16:29
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

mjasikowski
mjasikowski previously approved these changes May 14, 2026
@mjasikowski

Copy link
Copy Markdown
Contributor

@OlimpiaZurek ping me when you resolve conflicts and I'll merge

@OlimpiaZurek

Copy link
Copy Markdown
Contributor Author

@mjasikowski done

@MelvinBot

Copy link
Copy Markdown
Contributor

Code Review

Overall this is a solid, well-scoped PR. The shift from inline HTML links to proper Button components is a clear UX improvement, and the fallbackReceiptError logic correctly addresses the generic "Unexpected error creating this chat" message. All CI checks pass and the code is clean. A few observations:

Minor Issues

  1. Duplicated hasOnlyReceiptErrors computation in PureReportActionItem.tsx — this identical expression is computed twice (once inside showPopover and once in the render body). Consider extracting it to a single variable above both usages.

  2. isStackedLayout readability in DotIndicatorMessage.tsx!(isInNarrowPaneModal && !isSmallScreenWidth) is a double negation that takes a moment to parse. A name flip like const isWideModalLayout = isInNarrowPaneModal && !isSmallScreenWidth with the condition inverted might read more naturally, but this is purely stylistic.

Things that look good

  • Null guard on isReceiptError — good defensive addition.
  • Filtering receipt errors from getLatestErrorMessageField — prevents receipt errors from leaking as generic text errors. The reviewer concern was already addressed.
  • Optional action/retryParams on ReceiptError type + the early return in handleFileRetry — clean handling of the fallback receipt error case where retry data isn't available.
  • Context menu still accessible for receipt-error-only actions in PureReportActionItem — users can still reach "Delete expense" via long-press.
  • setDeleteTransactionNavigateBackUrl before cleanUpMoneyRequest — ensures correct navigation after deletion.

No blocking issues found. 👍

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@mjasikowski mjasikowski merged commit 3ce43b1 into Expensify:main May 15, 2026
36 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🚧 @mjasikowski has triggered a test Expensify/App build. You can view the workflow run here.

@OSBotify

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/mjasikowski in version: 9.3.74-7 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

Bundle Size Analysis (Sentry):

@MelvinBot

Copy link
Copy Markdown
Contributor

Yes, the help site needs an update. PR #82139 introduces a new user-facing error screen for failed receipt uploads with Save receipt and Delete expense buttons — this isn't documented anywhere on the help site today.

I've created a draft PR: #90800

Changes made:

  • Added a new FAQ entry ("What should I do if my receipt upload fails?") to the Attach and edit receipts on expenses article documenting the two options
  • Added keywords for discoverability (receipt upload failed, receipt upload error, save receipt, delete expense)
  • Applied HelpDot label

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/Beamanator in version: 9.3.74-7 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

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.