Skip to content

Commit

Permalink
(wip) delete images along with board
Browse files Browse the repository at this point in the history
  • Loading branch information
Mary Hipp authored and psychedelicious committed Jun 29, 2023
1 parent 45935ca commit ba67e57
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 7 deletions.
2 changes: 2 additions & 0 deletions invokeai/frontend/web/src/app/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import DeleteImageModal from 'features/gallery/components/DeleteImageModal';
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
import UpdateImageBoardModal from '../../features/gallery/components/Boards/UpdateImageBoardModal';
import { useListModelsQuery } from 'services/api/endpoints/models';
import DeleteBoardImagesModal from '../../features/gallery/components/Boards/DeleteBoardImagesModal';

const DEFAULT_CONFIG = {};

Expand Down Expand Up @@ -158,6 +159,7 @@ const App = ({
</Grid>
<DeleteImageModal />
<UpdateImageBoardModal />
<DeleteBoardImagesModal />
<Toaster />
<GlobalHotkeys />
</>
Expand Down
13 changes: 8 additions & 5 deletions invokeai/frontend/web/src/app/components/InvokeAIUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
import UpdateImageBoardModal from '../../features/gallery/components/Boards/UpdateImageBoardModal';
import { AddImageToBoardContextProvider } from '../contexts/AddImageToBoardContext';
import { $authToken, $baseUrl } from 'services/api/client';
import { DeleteBoardImagesContextProvider } from '../contexts/DeleteBoardImagesContext';

const App = lazy(() => import('./App'));
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
Expand Down Expand Up @@ -86,11 +87,13 @@ const InvokeAIUI = ({
<ImageDndContext>
<DeleteImageContextProvider>
<AddImageToBoardContextProvider>
<App
config={config}
headerComponent={headerComponent}
setIsReady={setIsReady}
/>
<DeleteBoardImagesContextProvider>
<App
config={config}
headerComponent={headerComponent}
setIsReady={setIsReady}
/>
</DeleteBoardImagesContextProvider>
</AddImageToBoardContextProvider>
</DeleteImageContextProvider>
</ImageDndContext>
Expand Down
101 changes: 101 additions & 0 deletions invokeai/frontend/web/src/app/contexts/DeleteBoardImagesContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { useDisclosure } from '@chakra-ui/react';
import { PropsWithChildren, createContext, useCallback, useState } from 'react';
import { BoardDTO } from 'services/api/types';
import { useDeleteBoardMutation } from '../../services/api/endpoints/boards';

export type ImageUsage = {
isInitialImage: boolean;
isCanvasImage: boolean;
isNodesImage: boolean;
isControlNetImage: boolean;
};

type DeleteBoardImagesContextValue = {
/**
* Whether the move image dialog is open.
*/
isOpen: boolean;
/**
* Closes the move image dialog.
*/
onClose: () => void;
/**
* The image pending movement
*/
board?: BoardDTO;
onClickDeleteBoardImages: (board: BoardDTO) => void;
handleDeleteBoardImages: (boardId: string) => void;
handleDeleteBoardOnly: (boardId: string) => void;
};

export const DeleteBoardImagesContext =
createContext<DeleteBoardImagesContextValue>({
isOpen: false,
onClose: () => undefined,
onClickDeleteBoardImages: () => undefined,
handleDeleteBoardImages: () => undefined,
handleDeleteBoardOnly: () => undefined,
});

type Props = PropsWithChildren;

export const DeleteBoardImagesContextProvider = (props: Props) => {
const [boardToDelete, setBoardToDelete] = useState<BoardDTO>();
const { isOpen, onOpen, onClose } = useDisclosure();

const [deleteBoardAndImages] = useDeleteBoardAndImagesMutation();
const [deleteBoard] = useDeleteBoardMutation();

// Clean up after deleting or dismissing the modal
const closeAndClearBoardToDelete = useCallback(() => {
setBoardToDelete(undefined);
onClose();
}, [onClose]);

const onClickDeleteBoardImages = useCallback(
(board?: BoardDTO) => {
console.log({ board });
if (!board) {
return;
}
setBoardToDelete(board);
onOpen();
},
[setBoardToDelete, onOpen]
);

const handleDeleteBoardImages = useCallback(
(boardId: string) => {
if (boardToDelete) {
deleteBoardAndImages(boardId);
closeAndClearBoardToDelete();
}
},
[deleteBoardAndImages, closeAndClearBoardToDelete, boardToDelete]
);

const handleDeleteBoardOnly = useCallback(
(boardId: string) => {
if (boardToDelete) {
deleteBoard(boardId);
closeAndClearBoardToDelete();
}
},
[deleteBoard, closeAndClearBoardToDelete, boardToDelete]
);

return (
<DeleteBoardImagesContext.Provider
value={{
isOpen,
board: boardToDelete,
onClose: closeAndClearBoardToDelete,
onClickDeleteBoardImages,
handleDeleteBoardImages,
handleDeleteBoardOnly,
}}
>
{props.children}
</DeleteBoardImagesContext.Provider>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
AlertDialog,
AlertDialogBody,
AlertDialogContent,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogOverlay,
Divider,
Flex,
Text,
} from '@chakra-ui/react';
import IAIButton from 'common/components/IAIButton';
import { memo, useContext, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import { DeleteBoardImagesContext } from '../../../../app/contexts/DeleteBoardImagesContext';

const DeleteBoardImagesModal = () => {
const { t } = useTranslation();

const {
isOpen,
onClose,
board,
handleDeleteBoardImages,
handleDeleteBoardOnly,
} = useContext(DeleteBoardImagesContext);

const cancelRef = useRef<HTMLButtonElement>(null);

return (
<AlertDialog
isOpen={isOpen}
leastDestructiveRef={cancelRef}
onClose={onClose}
isCentered
>
<AlertDialogOverlay>
{board && (
<AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold">
Delete Board
</AlertDialogHeader>

<AlertDialogBody>
<Flex direction="column" gap={3}>
<Divider />
<Text>{t('common.areYouSure')}</Text>
<Text fontWeight="bold">
This board has {board.image_count} image(s) that will be
deleted.
</Text>
</Flex>
</AlertDialogBody>
<AlertDialogFooter gap={3}>
<IAIButton ref={cancelRef} onClick={onClose}>
Cancel
</IAIButton>
<IAIButton
colorScheme="warning"
onClick={() => handleDeleteBoardOnly(board.board_id)}
>
Delete Board Only
</IAIButton>
<IAIButton
colorScheme="error"
onClick={() => handleDeleteBoardImages(board.board_id)}
>
Delete Board and Images
</IAIButton>
</AlertDialogFooter>
</AlertDialogContent>
)}
</AlertDialogOverlay>
</AlertDialog>
);
};

export default memo(DeleteBoardImagesModal);
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '@chakra-ui/react';

import { useAppDispatch } from 'app/store/storeHooks';
import { memo, useCallback } from 'react';
import { memo, useCallback, useContext } from 'react';
import { FaFolder, FaTrash } from 'react-icons/fa';
import { ContextMenu } from 'chakra-ui-contextmenu';
import { BoardDTO, ImageDTO } from 'services/api/types';
Expand All @@ -29,6 +29,7 @@ import { useDroppable } from '@dnd-kit/core';
import { AnimatePresence } from 'framer-motion';
import IAIDropOverlay from 'common/components/IAIDropOverlay';
import { SelectedItemOverlay } from '../SelectedItemOverlay';
import { DeleteBoardImagesContext } from '../../../../app/contexts/DeleteBoardImagesContext';

interface HoverableBoardProps {
board: BoardDTO;
Expand All @@ -44,6 +45,8 @@ const HoverableBoard = memo(({ board, isSelected }: HoverableBoardProps) => {

const { board_name, board_id } = board;

const { onClickDeleteBoardImages } = useContext(DeleteBoardImagesContext);

const handleSelectBoard = useCallback(() => {
dispatch(boardIdSelected(board_id));
}, [board_id, dispatch]);
Expand All @@ -65,6 +68,11 @@ const HoverableBoard = memo(({ board, isSelected }: HoverableBoardProps) => {
deleteBoard(board_id);
}, [board_id, deleteBoard]);

const handleDeleteBoardAndImages = useCallback(() => {
console.log({ board });
onClickDeleteBoardImages(board);
}, [board, onClickDeleteBoardImages]);

const handleDrop = useCallback(
(droppedImage: ImageDTO) => {
if (droppedImage.board_id === board_id) {
Expand Down Expand Up @@ -92,6 +100,15 @@ const HoverableBoard = memo(({ board, isSelected }: HoverableBoardProps) => {
menuProps={{ size: 'sm', isLazy: true }}
renderMenu={() => (
<MenuList sx={{ visibility: 'visible !important' }}>
{board.image_count > 0 && (
<MenuItem
sx={{ color: 'error.300' }}
icon={<FaTrash />}
onClickCapture={handleDeleteBoardAndImages}
>
Delete Board and Images
</MenuItem>
)}
<MenuItem
sx={{ color: 'error.300' }}
icon={<FaTrash />}
Expand Down
6 changes: 5 additions & 1 deletion invokeai/frontend/web/src/services/api/endpoints/boards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,14 @@ export const boardsApi = api.injectEndpoints({
{ type: 'Board', id: arg.board_id },
],
}),

deleteBoard: build.mutation<void, string>({
query: (board_id) => ({ url: `boards/${board_id}`, method: 'DELETE' }),
invalidatesTags: (result, error, arg) => [{ type: 'Board', id: arg }],
}),
deleteBoardAndImages: build.mutation<void, string>({
query: (board_id) => ({ url: `boards/${board_id}`, method: 'DELETE', params: { include_images: true } }),
invalidatesTags: (result, error, arg) => [{ type: 'Board', id: arg }, { type: 'Image', id: LIST_TAG }],
}),
}),
});

Expand All @@ -96,4 +99,5 @@ export const {
useCreateBoardMutation,
useUpdateBoardMutation,
useDeleteBoardMutation,
useDeleteBoardAndImagesMutation
} = boardsApi;

0 comments on commit ba67e57

Please sign in to comment.