diff --git a/src/libs/tryResolveUrlFromApiRoot.ts b/src/libs/tryResolveUrlFromApiRoot.ts
index 69609cc2e933..eeaab85b0a7f 100644
--- a/src/libs/tryResolveUrlFromApiRoot.ts
+++ b/src/libs/tryResolveUrlFromApiRoot.ts
@@ -1,6 +1,7 @@
import type {ImageSourcePropType} from 'react-native';
import Config from '@src/CONFIG';
import type {ReceiptSource} from '@src/types/onyx/Transaction';
+import proxyConfig from '../../config/proxyConfig';
import {getApiRoot} from './ApiUtils';
// Absolute URLs (`/` or `//`) should be resolved from API ROOT
@@ -29,6 +30,13 @@ function tryResolveUrlFromApiRoot(url: string | ImageSourcePropType | ReceiptSou
return url;
}
const apiRoot = getApiRoot({shouldUseSecure: false});
+
+ // When using the staging web proxy, the URL might already include the `/staging/` proxy path.
+ // In that case, do not replace the leading `/` again, as it would create `/staging/staging/...`.
+ if (apiRoot === proxyConfig.STAGING && url.startsWith(apiRoot)) {
+ return url;
+ }
+
return url.replace(ORIGIN_PATTERN, apiRoot);
}
diff --git a/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/AttachmentStateContextProvider.tsx b/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/AttachmentStateContextProvider.tsx
index 1a1dbaf47465..e18b5f2349bc 100644
--- a/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/AttachmentStateContextProvider.tsx
+++ b/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/AttachmentStateContextProvider.tsx
@@ -47,7 +47,7 @@ function AttachmentStateContextProvider({children}: Props) {
setAttachmentLoadedState({});
}, []);
- const isAttachmentLoaded = useCallback((key: AttachmentSource) => attachmentLoaded?.[convertSourceToString(key)] !== false, [attachmentLoaded]);
+ const isAttachmentLoaded = useCallback((key: AttachmentSource) => attachmentLoaded?.[convertSourceToString(key)] === true, [attachmentLoaded]);
const value = useMemo(() => ({setAttachmentLoaded, clearAttachmentLoaded, isAttachmentLoaded}), [setAttachmentLoaded, clearAttachmentLoaded, isAttachmentLoaded]);
return {children};
}
diff --git a/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/index.tsx b/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/index.tsx
index 8fa1914a8808..f3d98dad539e 100644
--- a/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/index.tsx
+++ b/src/pages/media/AttachmentModalScreen/AttachmentModalBaseContent/index.tsx
@@ -6,7 +6,7 @@ import ActivityIndicator from '@components/ActivityIndicator';
import AttachmentCarousel from '@components/Attachments/AttachmentCarousel';
import {AttachmentCarouselPagerActionsContext, AttachmentCarouselPagerStateContext} from '@components/Attachments/AttachmentCarousel/Pager/AttachmentCarouselPagerContext';
import type {AttachmentCarouselPagerActionsContextType, AttachmentCarouselPagerStateContextType} from '@components/Attachments/AttachmentCarousel/Pager/types';
-import AttachmentView from '@components/Attachments/AttachmentView';
+import AttachmentView, {checkIsFileImage} from '@components/Attachments/AttachmentView';
import useAttachmentErrors from '@components/Attachments/AttachmentView/useAttachmentErrors';
import type {Attachment} from '@components/Attachments/types';
import BlockingView from '@components/BlockingViews/BlockingView';
@@ -99,6 +99,7 @@ function AttachmentModalBaseContent({
const [transactionFromOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION}${getNonEmptyStringOnyxID(transactionID)}`);
const transaction = transactionProp ?? transactionFromOnyx;
const [currentAttachmentLink, setCurrentAttachmentLink] = useState(attachmentLink);
+ const [currentPreviewSource, setCurrentPreviewSource] = useState();
const bottomSafeAreaPaddingStyle = useBottomSafeSafeAreaPaddingStyle({
addBottomSafeAreaPadding: true,
addOfflineIndicatorBottomSafeAreaPadding: true,
@@ -135,6 +136,7 @@ function AttachmentModalBaseContent({
(attachment: Attachment) => {
setSource(attachment.source);
setFile(attachment.file);
+ setCurrentPreviewSource(attachment.previewSource);
setIsAuthTokenRequiredState(attachment.isAuthTokenRequired ?? false);
onCarouselAttachmentChange(attachment);
setCurrentAttachmentLink(attachment?.attachmentLink ?? '');
@@ -188,19 +190,22 @@ function AttachmentModalBaseContent({
const {isAttachmentLoaded} = useContext(AttachmentStateContext);
const isEReceipt = transaction && !hasReceiptSource(transaction) && hasEReceipt(transaction);
+ const isFileImage = typeof source !== 'function' && checkIsFileImage(source, fileToDisplay?.name);
+ const isSourceLoaded = isAttachmentLoaded?.(source) || (!!currentPreviewSource && isAttachmentLoaded?.(currentPreviewSource));
const shouldShowDownloadButton = useMemo(() => {
const isValidContext = !isEmptyObject(report) || type === CONST.ATTACHMENT_TYPE.SEARCH || shouldAllowDownloadOutsideReportContext;
if (!isValidContext || isErrorInAttachment(source) || isEReceipt) {
return false;
}
- return !!onDownloadAttachment && isDownloadButtonReadyToBeShown && !shouldShowNotFoundPage && !isOffline && !isLocalSource && isAttachmentLoaded?.(source);
+ return !!onDownloadAttachment && isDownloadButtonReadyToBeShown && !shouldShowNotFoundPage && !isOffline && !isLocalSource && (!isFileImage || isSourceLoaded);
}, [
- isAttachmentLoaded,
isDownloadButtonReadyToBeShown,
isErrorInAttachment,
+ isFileImage,
isLocalSource,
isOffline,
+ isSourceLoaded,
onDownloadAttachment,
report,
shouldAllowDownloadOutsideReportContext,