Skip to content

[CP Staging] Fix broken Android upload thumbnail by adding file:// scheme to cached local source - #99546

Merged
Gonals merged 3 commits into
mainfrom
claude-fixAndroidUploadThumbnailScheme
Aug 26, 2026
Merged

[CP Staging] Fix broken Android upload thumbnail by adding file:// scheme to cached local source#99546
Gonals merged 3 commits into
mainfrom
claude-fixAndroidUploadThumbnailScheme

Conversation

@MelvinBot

@MelvinBot MelvinBot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

On Android, sending an image to a chat showed a broken thumbnail while the attachment uploaded (deploy blocker, regressed in 9.4.59 → 9.4.60).

The optimistic upload preview resolves its source through getCachedAttachment, which returns the cached local file path stored in Onyx. That path is stored schemeless (e.g. /data/user/0/…/cache/attachments/<id>.jpg) so RNFS file operations (exists/unlink) accept it. On Android, React Native's <Image> only loads a local file when it carries a file:// scheme — a bare absolute path renders as a broken image (iOS tolerates schemeless paths, which is why this was Android-only).

#96531 didn't add the schemeless path, but it made the cache write reliably succeed (added RNFS.mkdir before the copy and moved the dir to Caches), so the preview now consistently receives the schemeless cached path where it previously fell back to the original file:// picked URI.

Fix: add the file:// scheme to the value returned to the image renderer, while keeping the stored path schemeless so the RNFS operations still work. This is a minimal roll-forward that leaves the #96531 privacy hardening and its migration intact.

Fixed Issues

$ #99490
PROPOSAL: #99490 (comment)

Tests

  1. Launch the Expensify app (Android / iOS native).
  2. Open any chat.
  3. Send an image to the chat.
  4. Verify the image thumbnail loads instantly — it renders the actual image (not a broken-image placeholder/icon) while the attachment is uploading.
  5. Verify the image continues to display correctly after the upload completes.
  • Verify that no errors appear in the JS console

Offline tests

Same as Tests.

QA Steps

  1. Launch the Expensify app (Android / iOS native).
  2. Open any chat.
  3. Send an image to the chat.
  4. Verify the image thumbnail loads instantly — it renders the actual image (not a broken-image placeholder/icon) while the attachment is uploading.
  5. Verify the image continues to display correctly after the upload completes.
  6. Regression check: confirm attachment upload previews still render correctly on all other platforms (Android: mWeb Chrome, iOS: Native, iOS: mWeb Safari, MacOS: Chrome / Safari).
  • 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 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 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)
  • 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)
  • 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.
  • 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: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

MelvinBot and others added 2 commits August 26, 2026 09:39
…d local source

Co-authored-by: Linh Vo <linhvovan29546@users.noreply.github.com>
Co-authored-by: Linh Vo <linhvovan29546@users.noreply.github.com>
@melvin-bot melvin-bot Bot added Melvin-Test-Android Melvin-Test-Web Triggers Melvin to run the testing steps of the PR on web labels Aug 26, 2026
@MelvinBot
MelvinBot requested a review from a team August 26, 2026 09:44
@MelvinBot

Copy link
Copy Markdown
Contributor Author

I created this PR from an upstream branch since you don't have push access to a fork of Expensify/App.

To take ownership of this branch and be able to push updates, run:

git remote add upstream https://github.com/Expensify/App.git
git fetch upstream claude-fixAndroidUploadThumbnailScheme
git checkout -b claude-fixAndroidUploadThumbnailScheme upstream/claude-fixAndroidUploadThumbnailScheme
git push -u origin claude-fixAndroidUploadThumbnailScheme

Then you can close this PR and open a new one from your fork, or just push directly here if you have write access.

…ess')

Co-authored-by: Linh Vo <linhvovan29546@users.noreply.github.com>
@MelvinBot

Copy link
Copy Markdown
Contributor Author

Fixed the failing spellcheck check and pushed to the branch.

Root cause: cspell flagged the word schemeless (not in the dictionary) in comments added by this PR — 1 occurrence in src/libs/actions/Attachment/index.native.ts:91 and 2 in tests/actions/AttachmentTest.ts.

Fix: Reworded the three comments to avoid the non-dictionary word (e.g. "stored without a scheme") — comment-only changes, no logic touched. npm run spell-changed now passes on both files.

The other red check, Check independent approval, is expected on a bot-authored PR and isn't something a code change resolves.

Web smoke test (attachment upload)

Result: fail (blocked, not a defect). The attach-and-send flow couldn't be driven on web because file uploads are explicitly out of scope for the web testing tooling (the native file-picker can't be interacted with). Worth noting the actual fix here lives in index.native.ts, an Android-native code path — web uses a different implementation, so this fix can't be verified on web anyway.

Step Status Observation
Open a chat/report pass Landed in Concierge chat with a visible composer
Open attachment picker via composer actions menu pass "Add attachment" invoked the native file picker
Select an image and confirm thumbnail renders while/after uploading fail No supported web verb to interact with the native file-picker dialog (uploads out of scope); flow couldn't be initiated
Evidence (1)

Concierge chat composer after the Add attachment file-picker step, showing no attachment staged

Concierge chat composer after the Add attachment file-picker step, showing no attachment staged

view run

@MelvinBot

Copy link
Copy Markdown
Contributor Author

No new code fix needed — the previously-failing spellcheck check was already resolved by my earlier commit (d8b3841) and is now passing.

The only check still showing red is Check independent approval, which is expected on a bot-authored PR and isn't something a code change resolves — it clears once a reviewer approves. The perf tests (baseline-perf-tests / branch-perf-tests) are still running, not failing.


view run

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Ran the testing steps on Android (standalone NewDot, emulator)✅ Pass. Attached and sent images to a 1:1 Concierge chat; the thumbnail rendered the actual image content immediately (never a broken-image icon) both while the attachment was still uploading and after it completed, and the full-resolution image displayed correctly. No JS console errors related to sending the image.

Step Status Observation
Attach/send an image from the device in a 1:1 chat ✅ pass Started a 1:1 chat with Concierge, used the + composer > Add attachment > Choose from gallery to attach and send test images (small solid-color PNGs and a large 4000×4000 noise PNG to slow the upload).
Thumbnail preview renders actual image while uploading (not a broken-image icon/placeholder) ✅ pass Captured immediately after sending the large image: the message bubble already showed the real gray-noise image content with no broken-image icon or blank placeholder while the send/response cycle was still fresh.
Image displays correctly after upload completes ✅ pass Sent images rendered correctly both inline in the chat and in the full-screen attachment viewer.
No JS console errors related to sending the image ✅ pass adb logcat filtered for ReactNativeJS errors/exceptions around the attachment-send timestamps showed no errors tied to attachment/image sending.
Screenshots

Large image thumbnail rendering actual image content immediately after send/while upload was still in flight

Large image thumbnail rendering actual image content immediately after send/while upload was still in flight

First test image sent and rendered correctly in Concierge 1:1 chat

First test image sent and rendered correctly in Concierge 1:1 chat

Sent image displayed correctly in full-screen attachment viewer after upload completed

Sent image displayed correctly in full-screen attachment viewer after upload completed

view run

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Attempted the testing steps on Web⚠️ Blocked (tooling limitation, not an App defect).

I couldn't complete the image-upload verification on web because the test harness (agent-device --platform web) has no supported way to select/upload a file into the native file-chooser that "Add attachment" opens — file uploads are explicitly out of scope for its web command surface. So the attach → upload → thumbnail flow could never be started. I did reach a 1:1 chat and confirm the "Add attachment" button opens cleanly (no error/crash), but no upload was initiated, so the key moment couldn't be observed.

Note: the regression this PR fixes is Android-only (iOS/web already tolerate the schemeless local path), so web is a regression-check rather than the primary repro. The Android run for this PR passed all steps. Verifying the web upload preview here would need someone to run it manually (or the harness to expose native file-chooser support, e.g. Playwright setInputFiles).

Step Status Observation
Open a 1:1 chat ✅ pass Reached the Expensify Concierge 1:1 chat.
Attach/send an image ❌ blocked Composer "Add attachment" opened, but agent-device web has no verb to supply a file to the native file-chooser; uploads are explicitly out of scope.
Thumbnail preview renders actual image while uploading (not broken-image icon) ❌ blocked Not reachable — no upload was ever initiated.
Image displays correctly after upload completes ❌ blocked Not reachable — no upload was ever initiated.
No JS console errors related to sending the image ❌ blocked Not observable — no send occurred and no console capture is available on the web harness.
Screenshots

Concierge 1:1 chat with composer visible before opening actions menu

Concierge 1:1 chat with composer visible before opening actions menu

Chat composer after clicking Add attachment; no file could be selected via agent-device

Chat composer after clicking Add attachment; no file could be selected via agent-device

view run

@Gonals
Gonals self-requested a review August 26, 2026 10:38
@linhvovan29546

Copy link
Copy Markdown
Contributor

@MelvinBot Please add the test step and QA steps for Android/iOS app

  • Launch Expensify app.
  • Open any chat.
  • Send an image to the chat.
  • Verify the Image thumbnail will load instantly when uploading attachment.

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Added the Tests and QA Steps for Android/iOS native to the PR description, based on the steps you provided:

  1. Launch the Expensify app (Android / iOS native).
  2. Open any chat.
  3. Send an image to the chat.
  4. Verify the image thumbnail loads instantly — it renders the actual image (not a broken-image placeholder/icon) while the attachment is uploading.
  5. Verify the image continues to display correctly after the upload completes.

The QA Steps mirror these and add a regression check that upload previews still render correctly on the remaining platforms (Android: mWeb Chrome, iOS: mWeb Safari, MacOS: Chrome / Safari). Offline tests are set to "Same as Tests". Please review and adjust the wording if you'd like anything different.


view run

@linhvovan29546

Copy link
Copy Markdown
Contributor

@MelvinBot Please make PR ready for review

@MelvinBot
MelvinBot marked this pull request as ready for review August 26, 2026 11:06
@MelvinBot
MelvinBot requested a review from a team as a code owner August 26, 2026 11:06
@melvin-bot
melvin-bot Bot requested review from NicolasBonet, linhvovan29546 and luacmartins and removed request for a team August 26, 2026 11:06
@melvin-bot

melvin-bot Bot commented Aug 26, 2026

Copy link
Copy Markdown

@linhvovan29546 @NicolasBonet @luacmartins One of you needs to 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]

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 Done — marked this PR as Ready for review.


view run

@melvin-bot
melvin-bot Bot removed the request for review from a team August 26, 2026 11:07
@linhvovan29546

linhvovan29546 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible 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 checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (see Reviewing the code)
    • 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 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)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • 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 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.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this 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.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
telegram-cloud-document-5-6086875545071528333.mp4
Android: mWeb Chrome

N/A. The PR only applies changes to the native Android/iOS code.

iOS: HybridApp
Screen.Recording.2026-08-26.at.18.11.12.mov
iOS: mWeb Safari

N/A. The PR only applies changes to the native Android/iOS code.

MacOS: Chrome / Safari

N/A. The PR only applies changes to the native Android/iOS code.

@Gonals
Gonals merged commit 3e1ee85 into main Aug 26, 2026
47 of 49 checks passed
@Gonals
Gonals deleted the claude-fixAndroidUploadThumbnailScheme branch August 26, 2026 13:06
@Gonals Gonals changed the title Fix broken Android upload thumbnail by adding file:// scheme to cached local source [CP Staging] Fix broken Android upload thumbnail by adding file:// scheme to cached local source Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🚧 Gonals 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 pushed a commit that referenced this pull request Aug 26, 2026
…nailScheme

Fix broken Android upload thumbnail by adding file:// scheme to cached local source

(cherry picked from commit 3e1ee85)

(cherry-picked to staging by lakchote)
@OSBotify OSBotify added the CP Staging marks PRs that have been CP'd to staging label Aug 26, 2026
@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Cherry-picked to staging by https://github.com/lakchote in version: 9.4.61-2 🚀

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

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 No help site changes are required for this PR, so I did not create a docs draft PR.

This is a platform-specific bug fix: it prefixes the cached local attachment path with a file:// scheme so React Native's native <Image> on Android can load it, fixing the broken thumbnail shown while an image uploads (src/libs/actions/Attachment/index.native.ts). It restores previously-expected behavior and introduces no new feature, setting, tab, button, or user-facing workflow.

The help-site articles under App/docs/articles document how to use features (e.g. sending chat messages, attaching receipts) — none describe internal image-rendering behavior, and none become inaccurate as a result of this change. There is nothing to add or update.


view run

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/AndrewGable in version: 9.4.61-4 🚀

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

Bundle Size Analysis (Sentry):

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Cherry-picked to staging by https://github.com/lakchote in version: 9.4.62-1 🚀

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

@MelvinBot

Copy link
Copy Markdown
Contributor Author

🤖 No help site changes are required for this PR, so I did not create a docs PR.

This PR is a purely internal rendering bug fix: it adds a file:// scheme to the cached local image path returned by getCachedAttachment so React Native's <Image> on Android loads the local file instead of showing a broken thumbnail while an attachment uploads.

It introduces no new feature, setting, tab, button, or workflow, and doesn't change any user-facing behavior that Expensify's help site (App/docs/articles) documents — the help site describes how to use features, not the internal handling of upload preview thumbnails. There is nothing to add or correct in the articles as a result of this change.


view run

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/AndrewGable in version: 9.4.62-4 🚀

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

Bundle Size Analysis (Sentry):

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

Labels

CP Staging marks PRs that have been CP'd to staging Melvin-Test-Android Melvin-Test-Web Triggers Melvin to run the testing steps of the PR on web

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants