From 2990fa23fe6d9ca481438d38c273133ced6c5b36 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Mon, 10 Jul 2023 23:33:21 +1000 Subject: [PATCH] wip --- .../middleware/listenerMiddleware/index.ts | 3 +- .../IAIErrorLoadingImageFallback.tsx | 42 ++++++++ .../src/common/components/IAIFillSkeleton.tsx | 30 ++++++ .../features/batch/components/BatchImage.tsx | 23 ++-- .../features/gallery/components/BatchGrid.tsx | 3 +- .../features/gallery/components/BoardGrid.tsx | 102 ++++++++++++++++++ .../gallery/components/GalleryImage.tsx | 44 ++++---- .../gallery/components/ImageContextMenu.tsx | 4 +- .../components/ImageGalleryContent.tsx | 9 +- .../gallery/components/ImageGalleryGrid.tsx | 2 +- 10 files changed, 227 insertions(+), 35 deletions(-) create mode 100644 invokeai/frontend/web/src/common/components/IAIErrorLoadingImageFallback.tsx create mode 100644 invokeai/frontend/web/src/common/components/IAIFillSkeleton.tsx create mode 100644 invokeai/frontend/web/src/features/gallery/components/BoardGrid.tsx diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts index b48f602c8e..2467cf6009 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/index.ts @@ -47,7 +47,6 @@ import { addInitialImageSelectedListener } from './listeners/initialImageSelecte import { addModelSelectedListener } from './listeners/modelSelected'; import { addReceivedOpenAPISchemaListener } from './listeners/receivedOpenAPISchema'; import { addReceivedPageOfImagesListener } from './listeners/receivedPageOfImages'; -import { addSelectionAddedToBatchListener } from './listeners/selectionAddedToBatch'; import { addSessionCanceledFulfilledListener, addSessionCanceledPendingListener, @@ -201,7 +200,7 @@ addBoardIdSelectedListener(); addReceivedOpenAPISchemaListener(); // Batches -addSelectionAddedToBatchListener(); +// addSelectionAddedToBatchListener(); addAddBoardToBatchListener(); // DND diff --git a/invokeai/frontend/web/src/common/components/IAIErrorLoadingImageFallback.tsx b/invokeai/frontend/web/src/common/components/IAIErrorLoadingImageFallback.tsx new file mode 100644 index 0000000000..2136acc3c3 --- /dev/null +++ b/invokeai/frontend/web/src/common/components/IAIErrorLoadingImageFallback.tsx @@ -0,0 +1,42 @@ +import { Box, Flex, Icon } from '@chakra-ui/react'; +import { FaExclamation } from 'react-icons/fa'; + +const IAIErrorLoadingImageFallback = () => { + return ( + + + + + + ); +}; + +export default IAIErrorLoadingImageFallback; diff --git a/invokeai/frontend/web/src/common/components/IAIFillSkeleton.tsx b/invokeai/frontend/web/src/common/components/IAIFillSkeleton.tsx new file mode 100644 index 0000000000..a3c83cb734 --- /dev/null +++ b/invokeai/frontend/web/src/common/components/IAIFillSkeleton.tsx @@ -0,0 +1,30 @@ +import { Box, Skeleton } from '@chakra-ui/react'; + +const IAIFillSkeleton = () => { + return ( + + + + ); +}; + +export default IAIFillSkeleton; diff --git a/invokeai/frontend/web/src/features/batch/components/BatchImage.tsx b/invokeai/frontend/web/src/features/batch/components/BatchImage.tsx index 14d6e83efc..833e0b12d2 100644 --- a/invokeai/frontend/web/src/features/batch/components/BatchImage.tsx +++ b/invokeai/frontend/web/src/features/batch/components/BatchImage.tsx @@ -1,10 +1,12 @@ -import { Box, Spinner } from '@chakra-ui/react'; +import { Box } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { TypesafeDraggableData } from 'app/components/ImageDnd/typesafeDnd'; import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAIDndImage from 'common/components/IAIDndImage'; +import IAIErrorLoadingImageFallback from 'common/components/IAIErrorLoadingImageFallback'; +import IAIFillSkeleton from 'common/components/IAIFillSkeleton'; import { batchImageRangeEndSelected, batchImageSelected, @@ -32,11 +34,13 @@ type BatchImageProps = { const BatchImage = (props: BatchImageProps) => { const dispatch = useAppDispatch(); - const { imageName } = props; - - const { currentData: imageDTO } = useGetImageDTOQuery(imageName); - + const { + currentData: imageDTO, + isLoading, + isError, + isSuccess, + } = useGetImageDTOQuery(imageName); const selector = useMemo(() => makeSelector(imageName), [imageName]); const { isSelected, selectionCount, selection } = useAppSelector(selector); @@ -76,8 +80,12 @@ const BatchImage = (props: BatchImageProps) => { } }, [imageDTO, selection, selectionCount]); - if (!imageDTO) { - return ; + if (isLoading) { + return ; + } + + if (isError || !imageDTO) { + return ; } return ( @@ -108,6 +116,7 @@ const BatchImage = (props: BatchImageProps) => { isUploadDisabled={true} resetTooltip="Remove from batch" withResetIcon + thumbnail /> )} diff --git a/invokeai/frontend/web/src/features/gallery/components/BatchGrid.tsx b/invokeai/frontend/web/src/features/gallery/components/BatchGrid.tsx index 350f4c18c1..7a733221ee 100644 --- a/invokeai/frontend/web/src/features/gallery/components/BatchGrid.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/BatchGrid.tsx @@ -1,5 +1,5 @@ import { Box } from '@chakra-ui/react'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { useAppSelector } from 'app/store/storeHooks'; import { useOverlayScrollbars } from 'overlayscrollbars-react'; import { memo, useEffect, useRef, useState } from 'react'; @@ -26,7 +26,6 @@ const selector = createSelector( ); const BatchGrid = () => { - const dispatch = useAppDispatch(); const { t } = useTranslation(); const rootRef = useRef(null); const [scroller, setScroller] = useState(null); diff --git a/invokeai/frontend/web/src/features/gallery/components/BoardGrid.tsx b/invokeai/frontend/web/src/features/gallery/components/BoardGrid.tsx new file mode 100644 index 0000000000..7e9f0a56a1 --- /dev/null +++ b/invokeai/frontend/web/src/features/gallery/components/BoardGrid.tsx @@ -0,0 +1,102 @@ +import { Box, Spinner } from '@chakra-ui/react'; +import { useOverlayScrollbars } from 'overlayscrollbars-react'; + +import { memo, useEffect, useRef, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { FaExclamation, FaImage } from 'react-icons/fa'; + +import { createSelector } from '@reduxjs/toolkit'; +import { stateSelector } from 'app/store/store'; +import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; +import { IAINoContentFallback } from 'common/components/IAIImageFallback'; +import BatchImage from 'features/batch/components/BatchImage'; +import { VirtuosoGrid } from 'react-virtuoso'; +import { useGetAllBoardImagesForBoardQuery } from 'services/api/endpoints/boardImages'; +import ItemContainer from './ItemContainer'; +import ListContainer from './ListContainer'; + +const selector = createSelector( + [stateSelector], + (state) => { + return { + imageNames: state.batch.imageNames, + }; + }, + defaultSelectorOptions +); + +type BoardGridProps = { + board_id: string; +}; + +const BoardGrid = (props: BoardGridProps) => { + const { board_id } = props; + const { data, isLoading, isError, isSuccess } = + useGetAllBoardImagesForBoardQuery({ + board_id, + }); + const { t } = useTranslation(); + const rootRef = useRef(null); + const [scroller, setScroller] = useState(null); + const [initialize, osInstance] = useOverlayScrollbars({ + defer: true, + options: { + scrollbars: { + visibility: 'auto', + autoHide: 'leave', + autoHideDelay: 1300, + theme: 'os-theme-dark', + }, + overflow: { x: 'hidden' }, + }, + }); + + useEffect(() => { + const { current: root } = rootRef; + if (scroller && root) { + initialize({ + target: root, + elements: { + viewport: scroller, + }, + }); + } + return () => osInstance()?.destroy(); + }, [scroller, initialize, osInstance]); + + if (isLoading) { + return ; + } + + if (isError) { + return ; + } + + if (isSuccess && data.image_names) { + return ( + + ( + + )} + /> + + ); + } + + return ( + + ); +}; + +export default memo(BoardGrid); diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryImage.tsx b/invokeai/frontend/web/src/features/gallery/components/GalleryImage.tsx index cba52cfba3..0e7ae6cd79 100644 --- a/invokeai/frontend/web/src/features/gallery/components/GalleryImage.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/GalleryImage.tsx @@ -1,4 +1,4 @@ -import { Box } from '@chakra-ui/react'; +import { Box, Spinner } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { TypesafeDraggableData } from 'app/components/ImageDnd/typesafeDnd'; import { stateSelector } from 'app/store/store'; @@ -7,8 +7,12 @@ import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAIDndImage from 'common/components/IAIDndImage'; import { imageToDeleteSelected } from 'features/imageDeletion/store/imageDeletionSlice'; import { MouseEvent, memo, useCallback, useMemo } from 'react'; -import { ImageDTO } from 'services/api/types'; -import { imageSelected } from '../store/gallerySlice'; +import { useGetImageDTOQuery } from 'services/api/endpoints/images'; +import { + imageRangeEndSelected, + imageSelected, + imageSelectionToggled, +} from '../store/gallerySlice'; import ImageContextMenu from './ImageContextMenu'; export const makeSelector = (image_name: string) => @@ -23,33 +27,29 @@ export const makeSelector = (image_name: string) => ); interface HoverableImageProps { - imageDTO: ImageDTO; + imageName: string; } const GalleryImage = (props: HoverableImageProps) => { const dispatch = useAppDispatch(); - - const { imageDTO } = props; - const { image_name } = imageDTO; - - const localSelector = useMemo(() => makeSelector(image_name), [image_name]); + const { imageName } = props; + const { currentData: imageDTO } = useGetImageDTOQuery(imageName); + const localSelector = useMemo(() => makeSelector(imageName), [imageName]); const { isSelected, selectionCount, selection } = useAppSelector(localSelector); const handleClick = useCallback( (e: MouseEvent) => { - // multiselect disabled for now - // if (e.shiftKey) { - // dispatch(imageRangeEndSelected(props.imageDTO.image_name)); - // } else if (e.ctrlKey || e.metaKey) { - // dispatch(imageSelectionToggled(props.imageDTO.image_name)); - // } else { - // dispatch(imageSelected(props.imageDTO.image_name)); - // } - dispatch(imageSelected(props.imageDTO.image_name)); + if (e.shiftKey) { + dispatch(imageRangeEndSelected(imageName)); + } else if (e.ctrlKey || e.metaKey) { + dispatch(imageSelectionToggled(imageName)); + } else { + dispatch(imageSelected(imageName)); + } }, - [dispatch, props.imageDTO.image_name] + [dispatch, imageName] ); const handleDelete = useCallback( @@ -81,13 +81,17 @@ const GalleryImage = (props: HoverableImageProps) => { } }, [imageDTO, selection, selectionCount]); + if (!imageDTO) { + return ; + } + return ( {(ref) => ( { }, [imageDTO.image_url]); const handleAddSelectionToBatch = useCallback(() => { - dispatch(selectionAddedToBatch({ images_names: selection })); + dispatch(imagesAddedToBatch(selection)); }, [dispatch, selection]); const handleAddToBatch = useCallback(() => { diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx index 8168b397fb..cdde89379e 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryContent.tsx @@ -38,6 +38,7 @@ import { import { useListAllBoardsQuery } from 'services/api/endpoints/boards'; import { mode } from 'theme/util/mode'; import BatchGrid from './BatchGrid'; +import BoardGrid from './BoardGrid'; import BoardsList from './Boards/BoardsList'; import ImageGalleryGrid from './ImageGalleryGrid'; @@ -228,7 +229,13 @@ const ImageGalleryContent = () => { - {selectedBoardId === 'batch' ? : } + {selectedBoardId === 'batch' ? ( + + ) : selectedBoardId ? ( + + ) : ( + + )} ); diff --git a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryGrid.tsx b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryGrid.tsx index b6240f78b9..aeae5cd4e3 100644 --- a/invokeai/frontend/web/src/features/gallery/components/ImageGalleryGrid.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/ImageGalleryGrid.tsx @@ -160,7 +160,7 @@ const ImageGalleryGrid = () => { ) : ( ) }