mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): refactor image deletion
Add `DeleteImageContext`: - provide a single function to delete an image - opens the modal or immediately deletes, if confirm is off
This commit is contained in:
@ -21,6 +21,7 @@ import { ReactNode, memo, useCallback, useEffect, useState } from 'react';
|
||||
import { APP_HEIGHT, APP_WIDTH } from 'theme/util/constants';
|
||||
import GlobalHotkeys from './GlobalHotkeys';
|
||||
import Toaster from './Toaster';
|
||||
import DeleteModal from 'features/gallery/components/DeleteModal';
|
||||
|
||||
const DEFAULT_CONFIG = {};
|
||||
|
||||
@ -133,6 +134,7 @@ const App = ({
|
||||
<FloatingGalleryButton />
|
||||
</Portal>
|
||||
</Grid>
|
||||
<DeleteModal />
|
||||
<Toaster />
|
||||
<GlobalHotkeys />
|
||||
</>
|
||||
|
@ -17,6 +17,10 @@ import '../../i18n';
|
||||
import { socketMiddleware } from 'services/events/middleware';
|
||||
import { Middleware } from '@reduxjs/toolkit';
|
||||
import ImageDndContext from './ImageDnd/ImageDndContext';
|
||||
import {
|
||||
DeleteImageContext,
|
||||
DeleteImageContextProvider,
|
||||
} from 'app/contexts/DeleteImageContext';
|
||||
|
||||
const App = lazy(() => import('./App'));
|
||||
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
|
||||
@ -71,11 +75,13 @@ const InvokeAIUI = ({
|
||||
<React.Suspense fallback={<Loading />}>
|
||||
<ThemeLocaleProvider>
|
||||
<ImageDndContext>
|
||||
<App
|
||||
config={config}
|
||||
headerComponent={headerComponent}
|
||||
setIsReady={setIsReady}
|
||||
/>
|
||||
<DeleteImageContextProvider>
|
||||
<App
|
||||
config={config}
|
||||
headerComponent={headerComponent}
|
||||
setIsReady={setIsReady}
|
||||
/>
|
||||
</DeleteImageContextProvider>
|
||||
</ImageDndContext>
|
||||
</ThemeLocaleProvider>
|
||||
</React.Suspense>
|
||||
|
107
invokeai/frontend/web/src/app/contexts/DeleteImageContext.tsx
Normal file
107
invokeai/frontend/web/src/app/contexts/DeleteImageContext.tsx
Normal file
@ -0,0 +1,107 @@
|
||||
import { useDisclosure } from '@chakra-ui/react';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
|
||||
import { requestedImageDeletion } from 'features/gallery/store/actions';
|
||||
import { systemSelector } from 'features/system/store/systemSelectors';
|
||||
import { PropsWithChildren, createContext, useCallback, useState } from 'react';
|
||||
import { ImageDTO } from 'services/api';
|
||||
|
||||
type DeleteImageContextValue = {
|
||||
/**
|
||||
* Whether the delete image dialog is open.
|
||||
*/
|
||||
isOpen: boolean;
|
||||
/**
|
||||
* Closes the delete image dialog.
|
||||
*/
|
||||
onClose: () => void;
|
||||
/**
|
||||
* Immediately deletes an image.
|
||||
*
|
||||
* You probably don't want to use this - use `onDelete` instead.
|
||||
*/
|
||||
onImmediatelyDelete: () => void;
|
||||
/**
|
||||
* Opens the delete image dialog and handles all deletion-related checks.
|
||||
*/
|
||||
onDelete: (image?: ImageDTO) => void;
|
||||
};
|
||||
|
||||
export const DeleteImageContext = createContext<DeleteImageContextValue>({
|
||||
isOpen: false,
|
||||
onClose: () => undefined,
|
||||
onImmediatelyDelete: () => undefined,
|
||||
onDelete: () => undefined,
|
||||
});
|
||||
|
||||
const selector = createSelector(
|
||||
[systemSelector],
|
||||
(system) => {
|
||||
const { isProcessing, isConnected, shouldConfirmOnDelete } = system;
|
||||
|
||||
return {
|
||||
canDeleteImage: isConnected && !isProcessing,
|
||||
shouldConfirmOnDelete,
|
||||
isProcessing,
|
||||
isConnected,
|
||||
};
|
||||
},
|
||||
defaultSelectorOptions
|
||||
);
|
||||
|
||||
type Props = PropsWithChildren;
|
||||
|
||||
export const DeleteImageContextProvider = (props: Props) => {
|
||||
const { canDeleteImage, shouldConfirmOnDelete } = useAppSelector(selector);
|
||||
const [imageToDelete, setImageToDelete] = useState<ImageDTO>();
|
||||
const dispatch = useAppDispatch();
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
const closeAndClearImageToDelete = useCallback(() => {
|
||||
setImageToDelete(undefined);
|
||||
onClose();
|
||||
}, [onClose]);
|
||||
|
||||
const onImmediatelyDelete = useCallback(() => {
|
||||
if (canDeleteImage && imageToDelete) {
|
||||
dispatch(requestedImageDeletion(imageToDelete));
|
||||
}
|
||||
closeAndClearImageToDelete();
|
||||
}, [canDeleteImage, imageToDelete, closeAndClearImageToDelete, dispatch]);
|
||||
|
||||
const handleDelete = useCallback(
|
||||
(image: ImageDTO) => {
|
||||
if (shouldConfirmOnDelete) {
|
||||
onOpen();
|
||||
} else {
|
||||
dispatch(requestedImageDeletion(image));
|
||||
}
|
||||
},
|
||||
[shouldConfirmOnDelete, onOpen, dispatch]
|
||||
);
|
||||
|
||||
const onDelete = useCallback(
|
||||
(image?: ImageDTO) => {
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
setImageToDelete(image);
|
||||
handleDelete(image);
|
||||
},
|
||||
[handleDelete]
|
||||
);
|
||||
|
||||
return (
|
||||
<DeleteImageContext.Provider
|
||||
value={{
|
||||
isOpen,
|
||||
onClose: closeAndClearImageToDelete,
|
||||
onDelete,
|
||||
onImmediatelyDelete,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</DeleteImageContext.Provider>
|
||||
);
|
||||
};
|
Reference in New Issue
Block a user