mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
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:
parent
e9558f97c4
commit
e675983e20
@ -1376,6 +1376,7 @@
|
|||||||
"problemCopyingCanvasDesc": "Unable to export base layer",
|
"problemCopyingCanvasDesc": "Unable to export base layer",
|
||||||
"problemCopyingImage": "Unable to Copy Image",
|
"problemCopyingImage": "Unable to Copy Image",
|
||||||
"problemCopyingImageLink": "Unable to Copy Image Link",
|
"problemCopyingImageLink": "Unable to Copy Image Link",
|
||||||
|
"problemDownloadingImage": "Unable to Download Image",
|
||||||
"problemDownloadingCanvas": "Problem Downloading Canvas",
|
"problemDownloadingCanvas": "Problem Downloading Canvas",
|
||||||
"problemDownloadingCanvasDesc": "Unable to export base layer",
|
"problemDownloadingCanvasDesc": "Unable to export base layer",
|
||||||
"problemImportingMask": "Problem Importing Mask",
|
"problemImportingMask": "Problem Importing Mask",
|
||||||
|
43
invokeai/frontend/web/src/common/hooks/useDownloadImage.ts
Normal file
43
invokeai/frontend/web/src/common/hooks/useDownloadImage.ts
Normal 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 };
|
||||||
|
};
|
@ -4,6 +4,7 @@ import { useAppToaster } from 'app/components/Toaster';
|
|||||||
import { $customStarUI } from 'app/store/nanostores/customStarUI';
|
import { $customStarUI } from 'app/store/nanostores/customStarUI';
|
||||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||||
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
|
import { useCopyImageToClipboard } from 'common/hooks/useCopyImageToClipboard';
|
||||||
|
import { useDownloadImage } from 'common/hooks/useDownloadImage';
|
||||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||||
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
|
import { imagesToChangeSelected, isModalOpenChanged } from 'features/changeBoardModal/store/slice';
|
||||||
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
import { imagesToDeleteSelected } from 'features/deleteImageModal/store/slice';
|
||||||
@ -47,7 +48,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
|||||||
const toaster = useAppToaster();
|
const toaster = useAppToaster();
|
||||||
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
|
const isCanvasEnabled = useFeatureStatus('unifiedCanvas').isFeatureEnabled;
|
||||||
const customStarUi = useStore($customStarUI);
|
const customStarUi = useStore($customStarUI);
|
||||||
|
const { downloadImage } = useDownloadImage();
|
||||||
const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata(imageDTO?.image_name);
|
const { metadata, isLoading: isLoadingMetadata } = useDebouncedMetadata(imageDTO?.image_name);
|
||||||
|
|
||||||
const { getAndLoadEmbeddedWorkflow, getAndLoadEmbeddedWorkflowResult } = useGetAndLoadEmbeddedWorkflow({});
|
const { getAndLoadEmbeddedWorkflow, getAndLoadEmbeddedWorkflowResult } = useGetAndLoadEmbeddedWorkflow({});
|
||||||
@ -143,6 +144,10 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
|||||||
}
|
}
|
||||||
}, [unstarImages, imageDTO]);
|
}, [unstarImages, imageDTO]);
|
||||||
|
|
||||||
|
const handleDownloadImage = useCallback(() => {
|
||||||
|
downloadImage(imageDTO.image_url, imageDTO.image_name);
|
||||||
|
}, [downloadImage, imageDTO.image_name, imageDTO.image_url]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MenuItem as="a" href={imageDTO.image_url} target="_blank" icon={<PiShareFatBold />}>
|
<MenuItem as="a" href={imageDTO.image_url} target="_blank" icon={<PiShareFatBold />}>
|
||||||
@ -153,14 +158,7 @@ const SingleSelectionMenuItems = (props: SingleSelectionMenuItemsProps) => {
|
|||||||
{t('parameters.copyImage')}
|
{t('parameters.copyImage')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
<MenuItem
|
<MenuItem icon={<PiDownloadSimpleBold />} onClickCapture={handleDownloadImage}>
|
||||||
as="a"
|
|
||||||
download={true}
|
|
||||||
href={imageDTO.image_url}
|
|
||||||
target="_blank"
|
|
||||||
icon={<PiDownloadSimpleBold />}
|
|
||||||
w="100%"
|
|
||||||
>
|
|
||||||
{t('parameters.downloadImage')}
|
{t('parameters.downloadImage')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user