You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Due for payment 2025-12-26] [$250] Download icon/option not show in image preview (for large file size image) #74128
Version Number: 9.2.42-11 Reproducible in staging?: Y Reproducible in production?: Y If this was caught during regression testing, add the test name, ID and link from TestRail: Email or phone of affected tester (no customers): Logs:https://stackoverflow.com/c/expensify/questions/4856 Expensify/Expensify Issue URL: Issue reported by:@mallenexpensify Slack conversation (hyperlinked to channel name): #Expensify Bugs
Action Performed:
Send any lengthy/large attachment to User B
Login in as User B
Open the attachment and observe
Expected Result:
To be able to download the image
Actual Result:
No download option showing in upper right hand corner
Workaround:
Unknown
Platforms:
Select the officially supported platforms where the issue was reproduced:
Android: App
Android: mWeb Chrome
iOS: App
iOS: mWeb Safari
iOS: mWeb Chrome
Windows: Chrome
MacOS: Chrome / Safari
MacOS: Desktop
Platforms Tested:
On which of our officially supported platforms was this issue tested:
What changes do you think we should make in order to solve the problem?
We should prevent the BaseImage component from setting the attachment loaded state to false if it's already true. So, when a large image loads quickly from the main Image component, the fallback Image component won't reset it back to false
const{setAttachmentLoaded, isAttachmentLoaded}=useContext(AttachmentStateContext);useEffect(()=>{// Only set to false if it's not already loadedif(!isAttachmentLoaded?.(sourceasAttachmentSource)){setAttachmentLoaded?.(sourceasAttachmentSource,false);}// eslint-disable-next-line react-compiler/react-compiler// eslint-disable-next-line react-hooks/exhaustive-deps},[]);
changed the title [-]Download icon/option not show in image preview (for large file size image)[/-][+][$250] Download icon/option not show in image preview (for large file size image)[/+]on Nov 4, 2025
For high-resolution images, the imageSource includes the 1024.jpg variant.
Later, when isAttachmentLoaded is checked in AttachmentModalBaseContent, it uses a different source value (without 1024.jpg), so the comparison fails and isAttachmentLoaded returns false. This happens because we use the source, not the preview source.
As a result, the download button does not appear even though the image is loaded.
What changes do you think we should make in order to solve the problem?
To resolve this issue, we should update the source to check isAttachmentLoaded in the case of high-resolution images as well. Update the logic to ensure that for high-resolution images, we check the correct image source (previewSource) when determining whether the attachment has loaded
What alternative solutions did you explore? (Optional)
Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.
Please re-state the problem that we are trying to solve in this issue.
The download icon/option is not showing in the image preview for large file size images. When a user opens a large attachment, the download button in the upper right-hand corner of the modal does not appear, preventing users from downloading the image.
What is the root cause of that problem?
The AttachmentModal component does not verify if the attachment has finished loading before rendering the download button.
The current logic only checks for:
allowDownload flag
isDownloadButtonReadyToBeShown (carousel state)
Other UI conditions like offline mode, local source, etc.
For large images, the button visibility logic runs before the image finishes loading, and since there’s no check for the attachment’s load state, the button never appears.
The newer AttachmentModalBaseContent component already has load-state tracking implemented via AttachmentStateContext, but AttachmentModal (standalone modal) does not.
What changes do you think we should make in order to solve the problem?
Add attachment load state tracking to AttachmentModal by:
Wrapping modal content with AttachmentStateContextProvider.
Adding a check for isAttachmentLoaded before showing the download button.
Moving the button visibility logic into an inner component that can access the context.
Implementation Strategy
Add Attachment Load State Tracking
Import and use AttachmentStateContextProvider and AttachmentStateContext from the attachment modal screen components.
Wrap the modal content with the provider to track when attachments finish loading.
Add Load State Check
Update shouldShowDownloadButton logic to include a check for whether the image has fully loaded using isAttachmentLoaded?.(sourceState).
Create Inner Component
Create a new inner component AttachmentModalContent inside AttachmentModal.tsx.
Use useContext(AttachmentStateContext) inside it to access isAttachmentLoaded.
Move the button rendering logic into this component so it re-renders when load state updates.
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.2.84-8 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:
@huult / @truph01@joekaufmanexpensify 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]
[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:
[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:
[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:
Regression Test Proposal
Precondition:
Test:
Open the Expensify app.
Open any chat and send a large or lengthy attachment. Example: Attachment
Open the attachment.
Verify that the download icon is visible and that you are able to download the image.
It seems like the latest PR uses the same logic as I originally proposed (which wasn't selected at the time). Even if it's not identical, both check if the attachment is loaded before setting it to false, just written with different syntax (negative check vs early return)
Since the final solution implements this logic, am I eligible for partial payment/compensation?
My initial thought is no. It's not unusual for there to be some overlap between solutions for a bug, and I am not aware of such overlap in and of itself being grounds for partial compensation. cc @pecanoro in case you feel differently.
Activity
melvin-bot commentedon Nov 3, 2025
Triggered auto assignment to @joekaufmanexpensify (
Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.nyomanjyotisa commentedon Nov 4, 2025
Proposal
Please re-state the problem that we are trying to solve in this issue.
Download icon/option not show in image preview (for large file size image)
What is the root cause of that problem?
When an image is rendered, this
useEffectsets the attachment loaded state tofalseon mountApp/src/components/Image/BaseImage.tsx
Lines 10 to 14 in 28ede48
But the problem occurs because the
Lightboxcomponent renders twoImagecomponents simultaneously:MultiGestureCanvasApp/src/components/Lightbox/index.tsx
Lines 234 to 251 in 28ede48
App/src/components/Lightbox/index.tsx
Lines 257 to 269 in 28ede48
For large images that load instantly from cache:
Imagecomponent mounts and sets loaded state tofalsetrueImagecomponent (fallback) mounts and sets it back tofalseisAttachmentLoaded?.(source)) flickers and disappearsApp/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/index.tsx
Lines 183 to 191 in 28ede48
What changes do you think we should make in order to solve the problem?
We should prevent the
BaseImagecomponent from setting the attachment loaded state tofalseif it's alreadytrue. So, when a large image loads quickly from the main Image component, the fallback Image component won't reset it back tofalseApp/src/components/Image/BaseImage.tsx
Lines 9 to 14 in 28ede48
Apply the same change to
BaseImage.native.tsxWhat alternative solutions did you explore? (Optional)
None
[-]Download icon/option not show in image preview (for large file size image)[/-][+][$250] Download icon/option not show in image preview (for large file size image)[/+]melvin-bot commentedon Nov 4, 2025
Job added to Upwork: https://www.upwork.com/jobs/~021985737100474543832
melvin-bot commentedon Nov 4, 2025
Triggered auto assignment to Contributor-plus team member for initial proposal review - @jayeshmangwani (
External)huult commentedon Nov 5, 2025
Proposal
Please re-state the problem that we are trying to solve in this issue.
Download icon/option not show in image preview (for large file size image)
What is the root cause of that problem?
In high-resolution cases, we use the
imageSourceprop here:App/src/components/Attachments/AttachmentView/index.tsx
Line 317 in 5279e0b
This
imageSourceis passed to theBaseImagecomponent, whereisAttachmentLoadedis set totrueonce the image is loaded:App/src/components/Image/BaseImage.tsx
Line 17 in 8789440
For high-resolution images, the
imageSourceincludes the1024.jpgvariant.Later, when
isAttachmentLoadedis checked inAttachmentModalBaseContent, it uses adifferent source value(without1024.jpg), so the comparison fails andisAttachmentLoadedreturnsfalse. This happens because we use the source, not the preview source.As a result, the download button does not appear even though the image is loaded.
App/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/index.tsx
Line 190 in 13da20b
What changes do you think we should make in order to solve the problem?
To resolve this issue, we should update the source to check
isAttachmentLoadedin the case of high-resolution images as well. Update the logic to ensure that for high-resolution images, we check the correct image source (previewSource) when determining whether the attachment has loadedApp/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/index.tsx
Lines 184 to 191 in 13da20b
update to
const shouldShowDownloadButton = useMemo(() => { const isValidContext = !isEmptyObject(report) || type === CONST.ATTACHMENT_TYPE.SEARCH; if (!isValidContext || isErrorInAttachment(source)) { return false; } // When isHighResolution is true, check if previewSource is loaded instead of the main source let sourceToCheck: AvatarSource = source; if (isHighResolution && previewSource) { const previewSourceString = SafeString(previewSource); sourceToCheck = previewSourceString || source; } return !!onDownloadAttachment && isDownloadButtonReadyToBeShown && !shouldShowNotFoundPage && !isOffline && !isLocalSource && isAttachmentLoaded?.(sourceToCheck); }, [ isAttachmentLoaded, isDownloadButtonReadyToBeShown, isErrorInAttachment, isLocalSource, isOffline, onDownloadAttachment, report, shouldShowNotFoundPage, source, type, isHighResolution, previewSource, ]);Test branch
Screen.Recording.2025-11-05.at.15.58.23.mov
What alternative solutions did you explore? (Optional)
Reminder: Please use plain English, be brief and avoid jargon. Feel free to use images, charts or pseudo-code if necessary. Do not post large multi-line diffs or write walls of text. Do not create PRs unless you have been hired for this job.
IjaazA commentedon Nov 5, 2025
Proposal
Please re-state the problem that we are trying to solve in this issue.
The download icon/option is not showing in the image preview for large file size images. When a user opens a large attachment, the download button in the upper right-hand corner of the modal does not appear, preventing users from downloading the image.
What is the root cause of that problem?
The AttachmentModal component does not verify if the attachment has finished loading before rendering the download button.
The current logic only checks for:
allowDownload flag
isDownloadButtonReadyToBeShown (carousel state)
Other UI conditions like offline mode, local source, etc.
For large images, the button visibility logic runs before the image finishes loading, and since there’s no check for the attachment’s load state, the button never appears.
The newer AttachmentModalBaseContent component already has load-state tracking implemented via AttachmentStateContext, but AttachmentModal (standalone modal) does not.
What changes do you think we should make in order to solve the problem?
Add attachment load state tracking to AttachmentModal by:
Wrapping modal content with AttachmentStateContextProvider.
Adding a check for isAttachmentLoaded before showing the download button.
Moving the button visibility logic into an inner component that can access the context.
Implementation Strategy
Add Attachment Load State Tracking
Import and use AttachmentStateContextProvider and AttachmentStateContext from the attachment modal screen components.
Wrap the modal content with the provider to track when attachments finish loading.
Add Load State Check
Update shouldShowDownloadButton logic to include a check for whether the image has fully loaded using isAttachmentLoaded?.(sourceState).
Create Inner Component
Create a new inner component AttachmentModalContent inside AttachmentModal.tsx.
Use useContext(AttachmentStateContext) inside it to access isAttachmentLoaded.
Move the button rendering logic into this component so it re-renders when load state updates.
Update Button Visibility Logic
Before:
After:
What alternative solutions did you explore? (Optional)
Timeout-based approach
Wait for a fixed timeout before showing the download button.
joekaufmanexpensify commentedon Nov 6, 2025
Pending @jayeshmangwani review
43 remaining items
melvin-bot commentedon Dec 19, 2025
The solution for this issue has been 🚀 deployed to production 🚀 in version 9.2.84-8 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 2025-12-26. 🎊
For reference, here are some details about the assignees on this issue:
melvin-bot commentedon Dec 19, 2025
@huult / @truph01 @joekaufmanexpensify 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]
joekaufmanexpensify commentedon Dec 22, 2025
@truph01 could you please handle the checklist so we can prep for payment?
truph01 commentedon Dec 22, 2025
BugZero Checklist:
[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:
[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:
[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:
Regression Test Proposal
Precondition:
Test:
Open the Expensify app.
Open any chat and send a large or lengthy attachment. Example:
Attachment
Open the attachment.
Verify that the download icon is visible and that you are able to download the image.
Do we agree 👍 or 👎
nyomanjyotisa commentedon Dec 22, 2025
It seems like the latest PR uses the same logic as I originally proposed (which wasn't selected at the time). Even if it's not identical, both check if the attachment is loaded before setting it to false, just written with different syntax (negative check vs early return)
Since the final solution implements this logic, am I eligible for partial payment/compensation?
Thanks!
cc @joekaufmanexpensify @truph01
joekaufmanexpensify commentedon Dec 23, 2025
My initial thought is no. It's not unusual for there to be some overlap between solutions for a bug, and I am not aware of such overlap in and of itself being grounds for partial compensation. cc @pecanoro in case you feel differently.
joekaufmanexpensify commentedon Dec 23, 2025
@truph01 thanks for doing checklist.
joekaufmanexpensify commentedon Dec 26, 2025
Regression issue
joekaufmanexpensify commentedon Dec 26, 2025
All set to pay 👍
joekaufmanexpensify commentedon Dec 26, 2025
Payment Summary
joekaufmanexpensify commentedon Dec 26, 2025
@huult @truph01 I sent $250 to both of you via Upwork and ended your contracts
joekaufmanexpensify commentedon Dec 26, 2025
Upwork job closed
joekaufmanexpensify commentedon Dec 26, 2025
All set, thanks everyone!