fix(ui): download image opens in new tab (#5625)

* fix(ui): download image opens in new tab

In some environments, a simple `a` element cannot trigger a download of an image. Fetching the image directly can get around this and provide more reliable download functionality.

* use hook for imageUrlToBlob so token gets sent if needed

---------

Co-authored-by: Mary Hipp <maryhipp@Marys-MacBook-Air.local>
This commit is contained in:
psychedelicious 2024-02-02 07:25:01 +11:00 committed by GitHub
parent e9558f97c4
commit e675983e20
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 51 additions and 9 deletions

View File

@ -1376,6 +1376,7 @@
"problemCopyingCanvasDesc": "Unable to export base layer",
"problemCopyingImage": "Unable to Copy Image",
"problemCopyingImageLink": "Unable to Copy Image Link",
"problemDownloadingImage": "Unable to Download Image",
"problemDownloadingCanvas": "Problem Downloading Canvas",
"problemDownloadingCanvasDesc": "Unable to export base layer",
"problemImportingMask": "Problem Importing Mask",

View File

@ -0,0 +1,43 @@
import { useAppToaster } from 'app/components/Toaster';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { useImageUrlToBlob } from './useImageUrlToBlob';
export const useDownloadImage = () => {
const toaster = useAppToaster();
const { t } = useTranslation();
const imageUrlToBlob = useImageUrlToBlob();
const downloadImage = useCallback(
async (image_url: string, image_name: string) => {
try {
const blob = await imageUrlToBlob(image_url);
if (!blob) {
throw new Error('Unable to create Blob');
}
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = image_name;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
} catch (err) {
toaster({
title: t('toast.problemDownloadingImage'),
description: String(err),
status: 'error',
duration: 2500,
isClosable: true,
});
}
},
[t, toaster, imageUrlToBlob]
);
return { downloadImage };
};

View File

@ -4,6 +4,7 @@ import { useAppToaster } from 'app/components/Toaster';
import { $customStarUI } from 'app/store/nanostores/customStarUI';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
import { useDownloadImage } from 'common/hooks/useDownloadImage';
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
@ -47,7 +48,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
const toaster = useAppToaster();
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
const customStarUi = useStore($customStarUI);
const { downloadImage } = useDownloadImage();
const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata(imageDTO?.image_name);
const { getAndLoadEmbeddedWorkflow, getAndLoadEmbeddedWorkflowResult } = useGetAndLoadEmbeddedWorkflow({});
@ -143,6 +144,10 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
}
}, [unstarImages, imageDTO]);
const handleDownloadImage = useCallback(() => {
downloadImage(imageDTO.image_url, imageDTO.image_name);
}, [downloadImage, imageDTO.image_name, imageDTO.image_url]);
return (
<>
<MenuItem as="a" href={imageDTO.image_url} target="_blank" icon={<PiShareFatBold />}>
@ -153,14 +158,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
{t('parameters.copyImage')}
</MenuItem>
)}
<MenuItem
as="a"
download={true}
href={imageDTO.image_url}
target="_blank"
icon={<PiDownloadSimpleBold />}
w="100%"
>
<MenuItem icon={<PiDownloadSimpleBold />} onClickCapture={handleDownloadImage}>
{t('parameters.downloadImage')}
</MenuItem>
<MenuDivider />