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:
psychedelicious
2023-06-06 00:40:40 +10:00
parent fa338ddb6a
commit 3d249c4fa3
9 changed files with 191 additions and 216 deletions

View File

@ -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 />
</>

View File

@ -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>

View 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>
);
};