mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-02-12 11:44:57 -05:00
feat(ui): migrate all clipboard stuff to useClipboard
This commit is contained in:
@@ -1,12 +1,11 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { withResultAsync } from 'common/util/result';
|
||||
import { useClipboard } from 'common/hooks/useClipboard';
|
||||
import { useCanvasManager } from 'features/controlLayers/contexts/CanvasManagerProviderGate';
|
||||
import type { CanvasEntityAdapterControlLayer } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterControlLayer';
|
||||
import type { CanvasEntityAdapterInpaintMask } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterInpaintMask';
|
||||
import type { CanvasEntityAdapterRasterLayer } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRasterLayer';
|
||||
import type { CanvasEntityAdapterRegionalGuidance } from 'features/controlLayers/konva/CanvasEntity/CanvasEntityAdapterRegionalGuidance';
|
||||
import { canvasToBlob } from 'features/controlLayers/konva/util';
|
||||
import { copyBlobToClipboard } from 'features/system/util/copyBlobToClipboard';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { startCase } from 'lodash-es';
|
||||
import { useCallback } from 'react';
|
||||
@@ -17,6 +16,7 @@ const log = logger('canvas');
|
||||
|
||||
export const useCopyLayerToClipboard = () => {
|
||||
const { t } = useTranslation();
|
||||
const clipboard = useClipboard();
|
||||
const copyLayerToCipboard = useCallback(
|
||||
async (
|
||||
adapter:
|
||||
@@ -30,27 +30,25 @@ export const useCopyLayerToClipboard = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await withResultAsync(async () => {
|
||||
try {
|
||||
const canvas = adapter.getCanvas();
|
||||
const blob = await canvasToBlob(canvas);
|
||||
copyBlobToClipboard(blob);
|
||||
});
|
||||
|
||||
if (result.isOk()) {
|
||||
log.trace('Layer copied to clipboard');
|
||||
toast({
|
||||
status: 'info',
|
||||
title: t('toast.layerCopiedToClipboard'),
|
||||
clipboard.writeImage(blob, () => {
|
||||
log.trace('Layer copied to clipboard');
|
||||
toast({
|
||||
status: 'info',
|
||||
title: t('toast.layerCopiedToClipboard'),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
log.error({ error: serializeError(result.error) }, 'Problem copying layer to clipboard');
|
||||
} catch (error) {
|
||||
log.error({ error: serializeError(error) }, 'Problem copying layer to clipboard');
|
||||
toast({
|
||||
status: 'error',
|
||||
title: t('toast.problemCopyingLayer'),
|
||||
});
|
||||
}
|
||||
},
|
||||
[t]
|
||||
[clipboard, t]
|
||||
);
|
||||
|
||||
return copyLayerToCipboard;
|
||||
@@ -58,6 +56,7 @@ export const useCopyLayerToClipboard = () => {
|
||||
|
||||
export const useCopyCanvasToClipboard = (region: 'canvas' | 'bbox') => {
|
||||
const { t } = useTranslation();
|
||||
const clipboard = useClipboard();
|
||||
const canvasManager = useCanvasManager();
|
||||
const copyCanvasToClipboard = useCallback(async () => {
|
||||
const rect =
|
||||
@@ -74,20 +73,19 @@ export const useCopyCanvasToClipboard = (region: 'canvas' | 'bbox') => {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await withResultAsync(async () => {
|
||||
try {
|
||||
const rasterAdapters = canvasManager.compositor.getVisibleAdaptersOfType('raster_layer');
|
||||
const canvasElement = canvasManager.compositor.getCompositeCanvas(rasterAdapters, rect);
|
||||
const blob = await canvasToBlob(canvasElement);
|
||||
copyBlobToClipboard(blob);
|
||||
});
|
||||
|
||||
if (result.isOk()) {
|
||||
toast({ title: t('controlLayers.regionCopiedToClipboard', { region: startCase(region) }) });
|
||||
} else {
|
||||
log.error({ error: serializeError(result.error) }, 'Failed to save canvas to gallery');
|
||||
clipboard.writeImage(blob, () => {
|
||||
log.trace('Region copied to clipboard');
|
||||
toast({ title: t('controlLayers.regionCopiedToClipboard', { region: startCase(region) }) });
|
||||
});
|
||||
} catch (error) {
|
||||
log.error({ error: serializeError(error) }, 'Failed to save canvas to gallery');
|
||||
toast({ title: t('controlLayers.copyRegionError', { region: startCase(region) }), status: 'error' });
|
||||
}
|
||||
}, [canvasManager.compositor, canvasManager.stateApi, region, t]);
|
||||
}, [canvasManager.compositor, canvasManager.stateApi, clipboard, region, t]);
|
||||
|
||||
return copyCanvasToClipboard;
|
||||
};
|
||||
|
||||
@@ -8,16 +8,12 @@ import { PiCopyBold } from 'react-icons/pi';
|
||||
export const ImageMenuItemCopy = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const imageDTO = useImageDTOContext();
|
||||
const { isClipboardAPIAvailable, copyImageToClipboard } = useCopyImageToClipboard();
|
||||
const copyImageToClipboard = useCopyImageToClipboard();
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
copyImageToClipboard(imageDTO.image_url);
|
||||
}, [copyImageToClipboard, imageDTO.image_url]);
|
||||
|
||||
if (!isClipboardAPIAvailable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<IconMenuItem
|
||||
icon={<PiCopyBold />}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Box, Flex, IconButton, Tooltip, useShiftModifier } from '@invoke-ai/ui-library';
|
||||
import { getOverlayScrollbarsParams } from 'common/components/OverlayScrollbars/constants';
|
||||
import { useClipboard } from 'common/hooks/useClipboard';
|
||||
import { Formatter } from 'fracturedjsonjs';
|
||||
import { isString } from 'lodash-es';
|
||||
import { OverlayScrollbarsComponent } from 'overlayscrollbars-react';
|
||||
@@ -25,9 +26,10 @@ const DataViewer = (props: Props) => {
|
||||
const { label, data, fileName, withDownload = true, withCopy = true, extraCopyActions } = props;
|
||||
const dataString = useMemo(() => (isString(data) ? data : formatter.Serialize(data)) ?? '', [data]);
|
||||
const shift = useShiftModifier();
|
||||
const clipboard = useClipboard();
|
||||
const handleCopy = useCallback(() => {
|
||||
navigator.clipboard.writeText(dataString);
|
||||
}, [dataString]);
|
||||
clipboard.writeText(dataString);
|
||||
}, [clipboard, dataString]);
|
||||
|
||||
const handleDownload = useCallback(() => {
|
||||
const blob = new Blob([dataString]);
|
||||
@@ -94,9 +96,10 @@ type ExtraCopyActionProps = {
|
||||
};
|
||||
const ExtraCopyAction = ({ label, data, getData }: ExtraCopyActionProps) => {
|
||||
const { t } = useTranslation();
|
||||
const clipboard = useClipboard();
|
||||
const handleCopy = useCallback(() => {
|
||||
navigator.clipboard.writeText(JSON.stringify(getData(data), null, 2));
|
||||
}, [data, getData]);
|
||||
clipboard.writeText(JSON.stringify(getData(data), null, 2));
|
||||
}, [clipboard, data, getData]);
|
||||
|
||||
return (
|
||||
<Tooltip label={`${t('gallery.copy')} ${label} JSON`}>
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { $projectUrl } from 'app/store/nanostores/projectId';
|
||||
import { useAssertSingleton } from 'common/hooks/useAssertSingleton';
|
||||
import { useClipboard } from 'common/hooks/useClipboard';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import { atom } from 'nanostores';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
@@ -38,7 +39,7 @@ export const ShareWorkflowModal = () => {
|
||||
const workflowToShare = useStore($workflowToShare);
|
||||
const projectUrl = useStore($projectUrl);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const clipboard = useClipboard();
|
||||
const workflowLink = useMemo(() => {
|
||||
if (!workflowToShare || !projectUrl) {
|
||||
return null;
|
||||
@@ -50,13 +51,14 @@ export const ShareWorkflowModal = () => {
|
||||
if (!workflowLink) {
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(workflowLink);
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('toast.linkCopied'),
|
||||
clipboard.writeText(workflowLink, () => {
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('toast.linkCopied'),
|
||||
});
|
||||
});
|
||||
$workflowToShare.set(null);
|
||||
}, [workflowLink, t]);
|
||||
}, [workflowLink, clipboard, t]);
|
||||
|
||||
return (
|
||||
<Modal isOpen={workflowToShare !== null} onClose={clearWorkflowToShare} isCentered size="lg" useInert={false}>
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
useDisclosure,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
|
||||
import { useClipboard } from 'common/hooks/useClipboard';
|
||||
import { discordLink, githubLink, websiteLink } from 'features/system/store/constants';
|
||||
import { map } from 'lodash-es';
|
||||
import InvokeLogoYellow from 'public/assets/images/invoke-tag-lrg.svg';
|
||||
@@ -36,6 +37,7 @@ type AboutModalProps = {
|
||||
const AboutModal = ({ children }: AboutModalProps) => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { t } = useTranslation();
|
||||
const clipboard = useClipboard();
|
||||
const { depsArray, depsObject } = useGetAppDepsQuery(undefined, {
|
||||
selectFromResult: ({ data }) => ({
|
||||
depsObject: data,
|
||||
@@ -45,8 +47,8 @@ const AboutModal = ({ children }: AboutModalProps) => {
|
||||
const { data: appVersion } = useGetAppVersionQuery();
|
||||
|
||||
const handleCopy = useCallback(() => {
|
||||
navigator.clipboard.writeText(JSON.stringify(depsObject, null, 2));
|
||||
}, [depsObject]);
|
||||
clipboard.writeText(JSON.stringify(depsObject, null, 2));
|
||||
}, [clipboard, depsObject]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Copies a blob to the clipboard by calling navigator.clipboard.write().
|
||||
*/
|
||||
export const copyBlobToClipboard = (blob: Promise<Blob> | Blob, type = 'image/png') => {
|
||||
navigator.clipboard.write([
|
||||
new ClipboardItem({
|
||||
[type]: blob,
|
||||
}),
|
||||
]);
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Flex, IconButton, Text } from '@invoke-ai/ui-library';
|
||||
import { useClipboard } from 'common/hooks/useClipboard';
|
||||
import { ExternalLink } from 'features/gallery/components/ImageViewer/NoContentForViewer';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
@@ -18,7 +19,7 @@ export const ErrorToastTitle = ({ errorType }: Props) => {
|
||||
|
||||
export default function ErrorToastDescription({ errorType, isLocal, sessionId, errorMessage }: Props) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const clipboard = useClipboard();
|
||||
const description = useMemo(() => {
|
||||
if (errorType === 'OutOfMemoryError') {
|
||||
if (isLocal) {
|
||||
@@ -38,7 +39,7 @@ export default function ErrorToastDescription({ errorType, isLocal, sessionId, e
|
||||
}
|
||||
}, [errorMessage, errorType, isLocal, t]);
|
||||
|
||||
const copySessionId = useCallback(() => navigator.clipboard.writeText(sessionId), [sessionId]);
|
||||
const copySessionId = useCallback(() => clipboard.writeText(sessionId), [clipboard, sessionId]);
|
||||
|
||||
return (
|
||||
<Flex flexDir="column">
|
||||
|
||||
Reference in New Issue
Block a user