diff --git a/invokeai/frontend/web/src/app/contexts/AddImageToBoardContext.tsx b/invokeai/frontend/web/src/app/contexts/AddImageToBoardContext.tsx index cf541dca01..da3dcb2239 100644 --- a/invokeai/frontend/web/src/app/contexts/AddImageToBoardContext.tsx +++ b/invokeai/frontend/web/src/app/contexts/AddImageToBoardContext.tsx @@ -1,23 +1,7 @@ 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, - useEffect, - useState, -} from 'react'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { PropsWithChildren, createContext, useCallback, useState } from 'react'; import { ImageDTO } from 'services/api'; -import { RootState } from 'app/store/store'; -import { canvasSelector } from 'features/canvas/store/canvasSelectors'; -import { controlNetSelector } from 'features/controlNet/store/controlNetSlice'; -import { nodesSelecter } from 'features/nodes/store/nodesSlice'; -import { generationSelector } from 'features/parameters/store/generationSelectors'; -import { some } from 'lodash-es'; import { imageAddedToBoard } from '../../services/thunks/board'; export type ImageUsage = { @@ -27,48 +11,6 @@ export type ImageUsage = { isControlNetImage: boolean; }; -export const selectImageUsage = createSelector( - [ - generationSelector, - canvasSelector, - nodesSelecter, - controlNetSelector, - (state: RootState, image_name?: string) => image_name, - ], - (generation, canvas, nodes, controlNet, image_name) => { - const isInitialImage = generation.initialImage?.image_name === image_name; - - const isCanvasImage = canvas.layerState.objects.some( - (obj) => obj.kind === 'image' && obj.image.image_name === image_name - ); - - const isNodesImage = nodes.nodes.some((node) => { - return some( - node.data.inputs, - (input) => - input.type === 'image' && input.value?.image_name === image_name - ); - }); - - const isControlNetImage = some( - controlNet.controlNets, - (c) => - c.controlImage?.image_name === image_name || - c.processedControlImage?.image_name === image_name - ); - - const imageUsage: ImageUsage = { - isInitialImage, - isCanvasImage, - isNodesImage, - isControlNetImage, - }; - - return imageUsage; - }, - defaultSelectorOptions -); - type AddImageToBoardContextValue = { /** * Whether the move image dialog is open. diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList.tsx index 9e7d1ab960..1f84d3be0e 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList.tsx @@ -1,4 +1,13 @@ -import { Box, Grid, Input, Spacer } from '@chakra-ui/react'; +import { + Box, + Divider, + Grid, + Input, + InputGroup, + InputRightElement, + Spacer, + useDisclosure, +} from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; @@ -14,19 +23,25 @@ import AddBoardButton from './AddBoardButton'; import AllImagesBoard from './AllImagesBoard'; import { searchBoardsSelector } from '../../store/boardSelectors'; import { useSelector } from 'react-redux'; +import IAICollapse from '../../../../common/components/IAICollapse'; +import { CloseIcon } from '@chakra-ui/icons'; const selector = createSelector( [selectBoardsAll, boardsSelector], (boards, boardsState) => { - return { boards, selectedBoardId: boardsState.selectedBoardId }; + const selectedBoard = boards.find( + (board) => board.board_id === boardsState.selectedBoardId + ); + return { selectedBoard, searchText: boardsState.searchText }; }, defaultSelectorOptions ); const BoardsList = () => { const dispatch = useAppDispatch(); - const { selectedBoardId } = useAppSelector(selector); + const { selectedBoard, searchText } = useAppSelector(selector); const filteredBoards = useSelector(searchBoardsSelector); + const { isOpen, onToggle } = useDisclosure(); const [searchMode, setSearchMode] = useState(false); @@ -34,52 +49,68 @@ const BoardsList = () => { setSearchMode(searchTerm.length > 0); dispatch(setBoardSearchText(searchTerm)); }; + const clearBoardSearch = () => { + setSearchMode(false); + dispatch(setBoardSearchText('')); + }; return ( - - - { - handleBoardSearch(e.target.value); + + <> + + + { + handleBoardSearch(e.target.value); + }} + /> + {searchText && searchText.length && ( + + + + )} + + + - - - {!searchMode && ( - <> - - - - )} - {filteredBoards.map((board) => ( - - ))} - - + > + + {!searchMode && ( + <> + + + + )} + {filteredBoards.map((board) => ( + + ))} + + + + ); }; diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/HoverableBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/HoverableBoard.tsx index d368d4ab0b..6fd6ac41be 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/HoverableBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/HoverableBoard.tsx @@ -4,19 +4,34 @@ import { EditableInput, EditablePreview, Flex, - Icon, - Image, MenuItem, MenuList, } from '@chakra-ui/react'; -import { useAppDispatch } from 'app/store/storeHooks'; + +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { memo, useCallback } from 'react'; -import { FaFolder, FaTrash } from 'react-icons/fa'; +import { FaTrash } from 'react-icons/fa'; import { ContextMenu } from 'chakra-ui-contextmenu'; -import { BoardDTO } from 'services/api'; +import { BoardDTO, ImageDTO } from 'services/api'; import { IAIImageFallback } from 'common/components/IAIImageFallback'; import { boardIdSelected } from 'features/gallery/store/boardSlice'; -import { boardDeleted, boardUpdated } from '../../../../services/thunks/board'; +import { + boardDeleted, + boardUpdated, + imageAddedToBoard, +} from '../../../../services/thunks/board'; +import { selectImagesAll } from '../../store/imagesSlice'; +import IAIDndImage from '../../../../common/components/IAIDndImage'; +import { defaultSelectorOptions } from '../../../../app/store/util/defaultMemoizeOptions'; +import { createSelector } from '@reduxjs/toolkit'; + +const selector = createSelector( + [selectImagesAll], + (images) => { + return { images }; + }, + defaultSelectorOptions +); interface HoverableBoardProps { board: BoardDTO; @@ -25,6 +40,7 @@ interface HoverableBoardProps { const HoverableBoard = memo(({ board, isSelected }: HoverableBoardProps) => { const dispatch = useAppDispatch(); + const { images } = useAppSelector(selector); const { board_name, board_id, cover_image_url } = board; @@ -45,6 +61,23 @@ const HoverableBoard = memo(({ board, isSelected }: HoverableBoardProps) => { ); }; + const handleDrop = useCallback( + (droppedImage: ImageDTO) => { + if (droppedImage.board_id === board_id) { + return; + } + dispatch( + imageAddedToBoard({ + requestBody: { + board_id, + image_name: droppedImage.image_name, + }, + }) + ); + }, + [board_id, dispatch] + ); + return ( @@ -91,19 +124,12 @@ const HoverableBoard = memo(({ board, isSelected }: HoverableBoardProps) => { overflow: 'hidden', }} > - {cover_image_url ? ( - } - sx={{}} - /> - ) : ( - - )} + } + isUploadDisabled={true} + /> { const boards = useSelector(selectBoardsAll); - const [selectedBoard, setSelectedBoard] = useState(null); - const { isOpen, onClose, handleAddToBoard, image } = useContext( AddImageToBoardContext ); + const [selectedBoard, setSelectedBoard] = useState(); const cancelRef = useRef(null); @@ -50,10 +49,12 @@ const UpdateImageBoardModal = () => { - - Moving this image to a board will remove it from its existing - board. - + {currentBoard && ( + + Moving this image from{' '} + {currentBoard.board_name} to + + )} setSelectedBoard(v)} diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx index 647477ce39..c1c414ad8b 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx @@ -240,39 +240,10 @@ const ImageGalleryContent = () => { icon={} /> - {selectedBoard && ( - - {selectedBoard.board_name} - - )} + + {selectedBoard ? selectedBoard.board_name : 'All Images'} + - {/* } - /> - } - > - - setNewBoardName(e.target.value)} - /> - - Create - - - */}