Fix receipt thumbnail disappearing after rotate/crop#87091
Conversation
When replaceReceipt runs, it sets localSource: null in the optimistic data to clear the stale local URI (correct Onyx semantics - explicit null is required to clear a key on merge). However, ReportActionItemImage computed effectiveImage using a strict !== undefined check, so null was treated as a defined local source and used directly, producing a blank image during the optimistic update: // null !== undefined → true → effectiveImage = null (bug) const effectiveImage = localSource !== undefined && typeof image === 'string' ? localSource : image; Fixed by using loose != null, which treats both null and undefined as "no local source", matching the intended semantic. Also updates localSource type to string | null to reflect that it can be explicitly cleared via Onyx merge. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@shubham1206agra Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
🚧 @Beamanator has triggered a test Expensify/App build. You can view the workflow run here. |
This comment has been minimized.
This comment has been minimized.
| const effectiveIsLocalFile = isLocalFile || !!localSource; | ||
| const effectiveThumbnail = localSource ?? thumbnail; | ||
| const effectiveImage = localSource !== undefined && typeof image === 'string' ? localSource : image; | ||
| const effectiveImage = localSource != null && typeof image === 'string' ? localSource : image; |
There was a problem hiding this comment.
@MelvinBot find any regressions caused by this change.
Regression AnalysisVerdict: No production regressions found. The three changes are well-scoped and safe. EvidenceEvery production consumer of
The Onyx merge behavior: Setting One minor note (non-blocking)
|
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppAndroid: mWeb ChromeiOS: HybridAppios.moviOS: mWeb SafariMacOS: Chrome / Safariweb1.movweb2.mov |
|
@carlosmiceli Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
Code ReviewVerdict: LGTM. The changes are minimal, well-targeted, and the PR description is excellent. Change-by-change
No issues found
|
|
Codex Review: Didn't find any major issues. 🎉 ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
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". |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
…eipt-replace Fix receipt thumbnail disappearing after rotate/crop (cherry picked from commit ec32a1a) (cherry-picked to staging by jasperhuangg)
|
🚧 @NikkiWines has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Cherry-picked to staging by https://github.com/jasperhuangg in version: 9.3.52-3 🚀
Bundle Size Analysis (Sentry): |
|
No help site changes are required for this PR. Why: This PR is a pure bug fix — it corrects stale The existing help article
All of that documentation remains correct after this fix. |
|
🚀 Deployed to production by https://github.com/roryabraham in version: 9.3.52-9 🚀
|
|
🚀 Cherry-picked to staging by https://github.com/jasperhuangg in version: 9.3.53-0 🚀
Bundle Size Analysis (Sentry): |
|
No help site changes are required for this PR. The changes are internal bug fixes to receipt thumbnail rendering ( |
|
🚀 Cherry-picked to staging by https://github.com/jasperhuangg in version: 9.3.53-0 🚀
Bundle Size Analysis (Sentry): |
|
🚀 Deployed to production by https://github.com/roryabraham in version: 9.3.53-7 🚀
|


Explanation of Change
PR #86512 introduced
localSourceontransaction.receiptto preserve the local file URI during SmartScan, preventing the server's remote URL from causing an unnecessary image reload.ReportActionItemImageuseslocalSourcein priority overthumbnail/imagewhen rendering.However,
replaceReceipt(used by both the Replace button and the Rotate/Crop flow) only mergessource,state, andfilenameintoreceiptOptimistic— Onyx merge doesn't remove keys absent from the update, so the oldlocalSourcepersists.ReportActionItemImagethen renders the stale local URI instead of the new receipt. The full-receipt modal readsreceipt.sourcedirectly, which is why the correct image shows there but not in expense list/detail thumbnail views.This PR has two fixes:
1.
IOU/index.ts— clearlocalSourceon replace (localSource: null)Adding
localSource: nulltoreceiptOptimisticexplicitly clears the stale URI via Onyx merge. This fixes both deploy blockers: the old receipt no longer persists after a Replace, and thumbnails update correctly after Rotate/Crop.2.
ReportActionItemImage.tsx— fix theeffectiveImagenull guardWithout this fix, setting
localSource: nullintroduces a new regression: the image goes blank during the optimistic update. The existing guard uses!== undefined:nullpasses this strict check, soeffectiveImageis assignednullinstead of the correctimagevalue, producing an empty image source until the server responds.!= null(loose equality) treats bothnullandundefinedas "no local source".3.
Transaction.ts— widenlocalSourcetype tostring | nullReflects that
localSourcecan be explicitly cleared.Fixed Issues
$ #87029
$ #87051
PROPOSAL:
Tests
Scenario A — Replace receipt ($ #87029: old receipt persists after replace)
Scenario B — Rotate/Crop receipt ($ #87051: thumbnail not updated after rotate/crop)
Scenario C — No blank image during optimistic update (fixes Codex-identified regression)
Offline tests
QA Steps
Deploy blocker #87029 — Replace receipt:
Deploy blocker #87051 — Rotate/Crop thumbnail:
Regression check — no blank image:
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari